incus-contrib/ansible/playbooks/showcase/k3s-control.yml

143 lines
4.5 KiB
YAML

---
# showcase/k3s-control.yml — K3s server (control plane) setup
#
# Installs K3s in server mode. The join token is stored at
# /var/lib/rancher/k3s/server/token for workers to read
# via the Incus API.
#
# Note: The Aether blueprint profile should include:
# security.nesting: "true"
# for K3s to manage pods and containers properly.
- name: Generate K3s server setup script
ansible.builtin.set_fact:
k3s_server_script: |
#!/bin/bash
set -e
echo "=== K3s server setup starting ==="
export DEBIAN_FRONTEND=noninteractive
# Container workaround: create /dev/kmsg if missing
[ -e /dev/kmsg ] || ln -s /dev/console /dev/kmsg
# Install prerequisites
apt-get install -y -qq curl 2>&1 | tail -1
# Install K3s server
echo "Installing K3s server (this takes 1-2 minutes)..."
curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="\
--write-kubeconfig-mode=644 \
--disable=traefik \
--snapshotter=native \
--kubelet-arg=feature-gates=KubeletInUserNamespace=true \
--node-ip={{ ffsdn_instance_ip | default('127.0.0.1') }} \
--tls-san={{ ffsdn_instance_ip | default('127.0.0.1') }}" sh -
echo "[OK] K3s installed"
# Wait for K3s to be ready
echo "Waiting for K3s node to be ready..."
export KUBECONFIG=/etc/rancher/k3s/k3s.yaml
for i in $(seq 1 60); do
if kubectl get nodes 2>/dev/null | grep -q ' Ready'; then
echo "[OK] K3s node ready"
break
fi
sleep 5
done
# Show node status
kubectl get nodes -o wide 2>/dev/null || true
# Verify token file exists
if [ -f /var/lib/rancher/k3s/server/node-token ]; then
echo "[OK] Join token stored at /var/lib/rancher/k3s/server/node-token"
else
echo "[ERROR] Join token not found"
exit 1
fi
echo "=== K3s server setup complete ==="
- name: Push K3s server setup script
ansible.builtin.uri:
url: "{{ incus_api }}/1.0/instances/{{ ffsdn_instance_name }}/files?path=/tmp/k3s-server-setup.sh"
method: POST
client_cert: "{{ incus_cert }}"
client_key: "{{ incus_key }}"
validate_certs: false
body: "{{ k3s_server_script }}"
headers:
Content-Type: "application/octet-stream"
X-Incus-type: "file"
X-Incus-mode: "0755"
status_code: [200]
- name: Execute K3s server setup script
ansible.builtin.uri:
url: "{{ incus_api }}/1.0/instances/{{ ffsdn_instance_name }}/exec"
method: POST
client_cert: "{{ incus_cert }}"
client_key: "{{ incus_key }}"
validate_certs: false
body_format: json
body:
command: ["/bin/bash", "/tmp/k3s-server-setup.sh"]
record-output: true
interactive: false
wait-for-websocket: false
environment:
DEBIAN_FRONTEND: "noninteractive"
status_code: [202]
return_content: true
register: k3s_server_exec
- name: Wait for K3s server setup to complete (up to 10 minutes)
ansible.builtin.uri:
url: "{{ incus_api }}{{ k3s_server_exec.json.operation }}/wait?timeout=600"
method: GET
client_cert: "{{ incus_cert }}"
client_key: "{{ incus_key }}"
validate_certs: false
return_content: true
timeout: 620
register: k3s_server_wait
failed_when: k3s_server_wait.json.metadata.status != "Success"
- name: Verify K3s server setup exit code
ansible.builtin.assert:
that:
- k3s_server_wait.json.metadata.metadata.return | int == 0
fail_msg: "K3s server setup exited with code {{ k3s_server_wait.json.metadata.metadata.return }}"
success_msg: "K3s server setup completed — control plane ready"
- name: Verify join token is accessible
ansible.builtin.uri:
url: "{{ incus_api }}/1.0/instances/{{ ffsdn_instance_name }}/files?path=/var/lib/rancher/k3s/server/token"
method: GET
client_cert: "{{ incus_cert }}"
client_key: "{{ incus_key }}"
validate_certs: false
return_content: true
register: token_check
- name: Display K3s server info
ansible.builtin.debug:
msg: "K3s control plane ready at {{ ffsdn_instance_ip | default('unknown') }}:6443 — token available for workers"
- name: Clean up setup script
ansible.builtin.uri:
url: "{{ incus_api }}/1.0/instances/{{ ffsdn_instance_name }}/exec"
method: POST
client_cert: "{{ incus_cert }}"
client_key: "{{ incus_key }}"
validate_certs: false
body_format: json
body:
command: ["/bin/rm", "-f", "/tmp/k3s-server-setup.sh"]
record-output: false
interactive: false
wait-for-websocket: false
status_code: [202]
ignore_errors: true