mirror of
https://github.com/techno-tim/k3s-ansible.git
synced 2026-08-08 23:13:19 +02:00
c82f2e0415
* chore(deps): apply dependency updates in one combined change - Bump ansible-core to 2.19.11 and jmespath to 1.1.0 in requirements.in - Regenerate the Python 3.11 pip-compile lock in requirements.txt - Bump molecule-plugins to 23.6.0 while keeping molecule on the stable 6.x series (avoids the molecule-plugins 26 major jump that broke vagrant module resolution in CI) - Bump ruamel-yaml-clib to 0.2.15 - Bump the zgosalvez/github-actions-ensure-sha-pinned-actions action to 5.0.6 (SHA-pinned) in lint.yml * fix(server): make log_destination conditional boolean for ansible-core 2.19 - The always block's 'Save logs of k3s-init.service' task used when: log_destination where log_destination is a path string derived from an env var - ansible-core 2.19 rejects string-derived conditionals; evaluate the path as a real boolean (non-empty) check so the conditional is a true boolean - Required to keep the k3s_server role working with ansible-core 2.19.11 (the dependency bump in this change) * fix(verify): coerce regex_search assertions to bool for ansible-core 2.19 - ansible-core 2.19 requires assert conditionals to be boolean; regex_search returns a string, which is now rejected - Wrap all regex_search results used in assert.that with | bool so the calico, cilium, metallb, and kube-vip image-tag checks produce boolean results * fix(verify): use boolean is regex_search test instead of | bool - | bool on a regex_search result coerces a tag string like v0.16.0 to False in ansible-core 2.19, failing the image-tag assertions - Use the is regex_search test which returns a real boolean without string coercion for the calico, cilium, metallb, and kube-vip image assertions * fix(verify): use is not none for regex_search assertions - ansible-core 2.19 has no "is regex_search" test and rejects bool string coercion, so use the regex_search filter with an "is not none" comparison, which yields a real boolean for the image-tag assertions - Applies to calico, cilium, metallb, and kube-vip image checks * fix(metallb): retry transient apiserver resets in config tests - The Layer 2 and BGP final configuration checks ran a kubectl get per resource with no retry, so a transient connection refused from the kube API could abort converge - Mirror the download_retries/download_delay retry pattern used by the 'Wait for MetalLB resources' task so these checks survive api server resets on slow runners
246 lines
8.4 KiB
YAML
246 lines
8.4 KiB
YAML
---
|
|
- name: Stop k3s-init
|
|
ansible.builtin.systemd:
|
|
name: k3s-init
|
|
state: stopped
|
|
failed_when: false
|
|
|
|
# k3s-init won't work if the port is already in use
|
|
- name: Stop k3s
|
|
ansible.builtin.systemd:
|
|
name: k3s
|
|
state: stopped
|
|
failed_when: false
|
|
|
|
- name: Clean previous runs of k3s-init # noqa command-instead-of-module
|
|
# The systemd module does not support "reset-failed", so we need to resort to command.
|
|
ansible.builtin.command: systemctl reset-failed k3s-init
|
|
failed_when: false
|
|
changed_when: false
|
|
|
|
- name: Deploy K3s http_proxy conf
|
|
ansible.builtin.include_tasks: http_proxy.yml
|
|
when: proxy_env is defined
|
|
|
|
- name: Deploy vip manifest
|
|
ansible.builtin.include_tasks: vip.yml
|
|
- name: Deploy metallb manifest
|
|
ansible.builtin.include_tasks: metallb.yml
|
|
tags: metallb
|
|
# Deploy MetalLB unless kube-vip owns the load balancer IP range, or Cilium
|
|
# BGP is enabled (Cilium then provides its own load balancing). The cilium_bgp
|
|
# default keeps this safe when Cilium variables are not in scope at all (#644)
|
|
# while still deploying MetalLB when a non-BGP Cilium CNI is in use.
|
|
when: kube_vip_lb_ip_range is not defined and not (cilium_bgp | default(false) | bool)
|
|
|
|
- name: Deploy kube-vip manifest
|
|
ansible.builtin.include_tasks: kube-vip.yml
|
|
tags: kubevip
|
|
when: kube_vip_lb_ip_range is defined
|
|
|
|
- name: Initialize and verify the K3s control plane
|
|
any_errors_fatal: true
|
|
when: not ansible_check_mode
|
|
block:
|
|
- name: Materialize per-host server initialization arguments
|
|
ansible.builtin.set_fact:
|
|
k3s_server_init_args: "{{ server_init_args }}"
|
|
no_log: true
|
|
when: groups[group_name_master | default('master')] | length > 1
|
|
|
|
- name: Orchestrate control-plane initialization from the first master
|
|
when: inventory_hostname == groups[group_name_master | default('master')][0]
|
|
block:
|
|
- name: Init the first master inside the transient k3s-init service
|
|
ansible.builtin.command:
|
|
cmd: >-
|
|
systemd-run -p RestartSec=2 -p Restart=on-failure
|
|
-p Delegate=yes -p TasksMax=infinity -p KillMode=process
|
|
-p LimitNOFILE=1048576 -p LimitNPROC=infinity -p LimitCORE=infinity
|
|
--unit=k3s-init k3s server {{ server_init_args }}
|
|
creates: "{{ systemd_dir }}/k3s-init.service"
|
|
no_log: true
|
|
|
|
- name: Wait for the first master Kubernetes API
|
|
ansible.builtin.command:
|
|
cmd: "{{ k3s_kubectl_binary | default('k3s kubectl') }} get --raw=/readyz"
|
|
register: first_master_api
|
|
until: first_master_api.rc == 0
|
|
retries: "{{ retry_count | default(20) }}"
|
|
delay: 2
|
|
changed_when: false
|
|
|
|
- name: Verify the first master API listener on its node address
|
|
ansible.builtin.wait_for:
|
|
host: >-
|
|
{{ hostvars[groups[group_name_master | default('master')][0]].k3s_node_ip
|
|
| split(',') | first }}
|
|
port: 6443
|
|
connect_timeout: 2
|
|
timeout: "{{ retry_count | default(20) * 2 }}"
|
|
|
|
- name: Join additional masters one at a time
|
|
ansible.builtin.include_tasks: join_master.yml
|
|
loop: "{{ groups[group_name_master | default('master')][1:] }}"
|
|
loop_control:
|
|
loop_var: joining_master
|
|
when: groups[group_name_master | default('master')] | length > 1
|
|
|
|
- name: Verify that all expected control-plane nodes registered
|
|
ansible.builtin.command:
|
|
cmd: >-
|
|
{{ k3s_kubectl_binary | default('k3s kubectl') }} get nodes
|
|
-o=jsonpath='{.items[*].metadata.name}'
|
|
register: nodes
|
|
until:
|
|
- nodes.rc == 0
|
|
- expected_control_plane_names | difference(nodes.stdout.split()) | length == 0
|
|
retries: "{{ retry_count | default(20) }}"
|
|
delay: 2
|
|
changed_when: false
|
|
vars:
|
|
expected_control_plane_names: >-
|
|
{{
|
|
groups[group_name_master | default('master')]
|
|
| map('extract', hostvars, 'ansible_hostname')
|
|
| list
|
|
}}
|
|
|
|
- name: Wait for K3s bootstrap CRDs to become established
|
|
ansible.builtin.command:
|
|
cmd: >-
|
|
{{ k3s_kubectl_binary | default('k3s kubectl') }} wait
|
|
--for=condition=Established --timeout=5s
|
|
crd/addons.k3s.cattle.io
|
|
crd/helmcharts.helm.cattle.io
|
|
crd/helmchartconfigs.helm.cattle.io
|
|
register: bootstrap_crds
|
|
until: bootstrap_crds.rc == 0
|
|
retries: "{{ retry_count | default(20) }}"
|
|
delay: 2
|
|
changed_when: false
|
|
always:
|
|
- name: Save logs of k3s-init.service
|
|
ansible.builtin.include_tasks: fetch_k3s_init_logs.yml
|
|
# ANSIBLE_K3S_LOG_DIR is a path string when set; evaluate it as a boolean
|
|
# so the conditional is a real boolean (ansible-core 2.19 rejects string
|
|
# conditionals derived from env vars).
|
|
when: log_destination | default('') != ''
|
|
vars:
|
|
log_destination: >-
|
|
{{ lookup('ansible.builtin.env', 'ANSIBLE_K3S_LOG_DIR', default=False) }}
|
|
- name: Kill the temporary service used for initialization
|
|
ansible.builtin.systemd:
|
|
name: k3s-init
|
|
state: stopped
|
|
failed_when: false
|
|
|
|
- name: Copy K3s service file
|
|
register: k3s_service
|
|
ansible.builtin.template:
|
|
src: k3s.service.j2
|
|
dest: "{{ systemd_dir }}/k3s.service"
|
|
owner: root
|
|
group: root
|
|
mode: "0644"
|
|
|
|
- name: Enable and check K3s service
|
|
ansible.builtin.systemd:
|
|
name: k3s
|
|
daemon_reload: true
|
|
state: restarted
|
|
enabled: true
|
|
|
|
- name: Wait for node-token
|
|
ansible.builtin.wait_for:
|
|
path: /var/lib/rancher/k3s/server/node-token
|
|
|
|
- name: Register node-token file access mode
|
|
ansible.builtin.stat:
|
|
path: /var/lib/rancher/k3s/server
|
|
register: p
|
|
|
|
- name: Change file access node-token
|
|
ansible.builtin.file:
|
|
path: /var/lib/rancher/k3s/server
|
|
mode: g+rx,o+rx
|
|
|
|
- name: Read node-token from master
|
|
ansible.builtin.slurp:
|
|
src: /var/lib/rancher/k3s/server/node-token
|
|
register: node_token
|
|
|
|
- name: Store Master node-token
|
|
ansible.builtin.set_fact:
|
|
token: "{{ node_token.content | b64decode | regex_replace('\n', '') }}"
|
|
|
|
- name: Restore node-token file access
|
|
ansible.builtin.file:
|
|
path: /var/lib/rancher/k3s/server
|
|
mode: "{{ p.stat.mode }}"
|
|
|
|
- name: Create directory .kube
|
|
ansible.builtin.file:
|
|
path: "{{ ansible_user_dir }}/.kube"
|
|
state: directory
|
|
owner: "{{ ansible_user_id }}"
|
|
mode: u=rwx,g=rx,o=
|
|
|
|
- name: Copy config file to user home directory
|
|
ansible.builtin.copy:
|
|
src: /etc/rancher/k3s/k3s.yaml
|
|
dest: "{{ ansible_user_dir }}/.kube/config"
|
|
remote_src: true
|
|
owner: "{{ ansible_user_id }}"
|
|
mode: u=rw,g=,o=
|
|
|
|
- name: Configure kubectl cluster to {{ endpoint_url }}
|
|
ansible.builtin.command: >-
|
|
{{ k3s_kubectl_binary | default('k3s kubectl') }} config set-cluster default
|
|
--server={{ endpoint_url }}
|
|
--kubeconfig {{ ansible_user_dir }}/.kube/config
|
|
changed_when: true
|
|
vars:
|
|
endpoint_url: >-
|
|
https://{{ apiserver_endpoint | ansible.utils.ipwrap }}:6443
|
|
# Deactivated linter rules:
|
|
# - jinja[invalid]: As of version 6.6.0, ansible-lint complains that the input to ipwrap
|
|
# would be undefined. This will not be the case during playbook execution.
|
|
# noqa jinja[invalid]
|
|
|
|
- name: Create kubectl symlink
|
|
ansible.builtin.file:
|
|
src: /usr/local/bin/k3s
|
|
dest: /usr/local/bin/kubectl
|
|
state: link
|
|
when: k3s_create_kubectl_symlink | default(true) | bool
|
|
|
|
- name: Create crictl symlink
|
|
ansible.builtin.file:
|
|
src: /usr/local/bin/k3s
|
|
dest: /usr/local/bin/crictl
|
|
state: link
|
|
when: k3s_create_crictl_symlink | default(true) | bool
|
|
|
|
- name: Get contents of manifests folder
|
|
ansible.builtin.find:
|
|
paths: /var/lib/rancher/k3s/server/manifests
|
|
file_type: file
|
|
register: k3s_server_manifests
|
|
|
|
- name: Get sub dirs of manifests folder
|
|
ansible.builtin.find:
|
|
paths: /var/lib/rancher/k3s/server/manifests
|
|
file_type: directory
|
|
register: k3s_server_manifests_directories
|
|
|
|
- name: Remove manifests and folders that are only needed for bootstrapping cluster so k3s doesn't auto apply on start
|
|
ansible.builtin.file:
|
|
path: "{{ item.path }}"
|
|
state: absent
|
|
with_items:
|
|
- "{{ k3s_server_manifests.files }}"
|
|
- "{{ k3s_server_manifests_directories.files }}"
|
|
loop_control:
|
|
label: "{{ item.path }}"
|