Skip to content

Commit 8457b46

Browse files
committed
fix: apply k3s version bumps declaratively through site.yml
Re-running site.yml with a raised k3s_version replaced the on-disk binary but left agents and joined servers running the old runtime, because the install script skips the service start and the roles only restarted the service on a config change. A version-only bump was therefore not applied until the services were restarted by hand. Always restart the k3s service in the server and agent roles on a site.yml run, so the cluster reliably picks up a new config or runtime without any logic to detect whether the binary changed. On a multi-server cluster, run the playbook with --forks=1 so the servers restart one at a time and the etcd quorum is preserved; document this in the README. Keep the dedicated upgrade.yml integration test and add a second upgrade through site.yml, asserting the running version the kubelet reports rather than the on-disk binary, so the tests fail if the restart regresses. Co-authored-by: Dominik Wombacher <dominik@wombacher.cc> Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Aleksei Sviridkin <f@lex.la>
1 parent 6688377 commit 8457b46

4 files changed

Lines changed: 95 additions & 2 deletions

File tree

.github/workflows/integration.yml

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ jobs:
1919
env:
2020
STARTING_K3S_VERSION: v1.34.3+k3s1
2121
UPGRADE_K3S_VERSION: v1.35.1+k3s1
22+
SITE_UPGRADE_K3S_VERSION: v1.35.6+k3s1
2223
# K3s requires privileged containers to run inside Docker and access to cgrougs.
2324
steps:
2425
- name: Set container OS based on service manager
@@ -138,6 +139,37 @@ jobs:
138139
- name: Verify K3s upgraded on Agent
139140
run: docker exec agent-node k3s --version | grep ${UPGRADE_K3S_VERSION}
140141

142+
# Second upgrade, this time re-running site.yml with a bumped version: it
143+
# must upgrade the running runtime on both the server and the agent, not
144+
# just replace the on-disk binary.
145+
- name: Modify the k3s_version in inventory for a site.yml upgrade
146+
run: |
147+
sed -i "s/k3s_version: .*/k3s_version: ${SITE_UPGRADE_K3S_VERSION}/" tests/${{ matrix.inventory }}.yml
148+
149+
- name: Run site Playbook to upgrade via a version bump
150+
run: ansible-playbook playbooks/site.yml -i tests/${{ matrix.inventory }}.yml
151+
152+
# The install script replaces the on-disk binary regardless of a restart,
153+
# so assert the running version the kubelet reports to the API: it only
154+
# advances once the service was actually restarted onto the new build.
155+
# This is what fails if the version-bump restart regresses.
156+
- name: Verify the running K3s version upgraded on Server and Agent
157+
run: |
158+
for node in server-node agent-node; do
159+
for attempt in 1 2 3 4 5 6; do
160+
running=$(docker exec server-node k3s kubectl get node "$node" -o jsonpath='{.status.nodeInfo.kubeletVersion}' 2>&1)
161+
echo "$node running version: $running"
162+
if echo "$running" | grep -q "${SITE_UPGRADE_K3S_VERSION}"; then
163+
break
164+
fi
165+
if [ "$attempt" -eq 6 ]; then
166+
echo "$node did not reach ${SITE_UPGRADE_K3S_VERSION}"
167+
exit 1
168+
fi
169+
sleep 10
170+
done
171+
done
172+
141173
- name: Wait for all deployments to be ready
142174
run: |
143175
for attempt in 1 2 3 4 5 6; do
@@ -324,6 +356,51 @@ jobs:
324356
done
325357
exit 1
326358
359+
# Re-running site.yml with a bumped version must upgrade every etcd
360+
# server. --forks=1 makes Ansible restart them one at a time so the etcd
361+
# quorum is never lost.
362+
- name: Modify the k3s_version in inventory for a site.yml upgrade
363+
run: |
364+
sed -i 's/k3s_version: v1.34.1+k3s1/k3s_version: v1.34.3+k3s1/' tests/ha.yml
365+
366+
- name: Run site Playbook to upgrade the servers via a version bump
367+
run: ansible-playbook playbooks/site.yml -i tests/ha.yml --forks=1
368+
369+
# Assert the running (kubelet-reported) version, not the on-disk binary:
370+
# without a rolling restart the joined servers stay on the old runtime.
371+
- name: Verify the running K3s version upgraded on all servers via site.yml
372+
run: |
373+
docker exec server-node1 k3s kubectl get nodes -o wide
374+
for SERVER in $SERVERS; do
375+
for attempt in 1 2 3 4 5 6; do
376+
running=$(docker exec server-node1 k3s kubectl get node "$SERVER" -o jsonpath='{.status.nodeInfo.kubeletVersion}' 2>&1)
377+
echo "$SERVER running version: $running"
378+
if echo "$running" | grep -q v1.34.3; then
379+
break
380+
fi
381+
if [ "$attempt" -eq 6 ]; then
382+
echo "$SERVER did not reach v1.34.3"
383+
exit 1
384+
fi
385+
sleep 10
386+
done
387+
done
388+
389+
- name: Wait for all deployments to be ready after the site.yml upgrade
390+
run: |
391+
for attempt in 1 2 3 4 5 6; do
392+
echo "Attempt $attempt: checking deployments"
393+
output=$(docker exec server-node1 k3s kubectl get deployments -n kube-system -o jsonpath='{range .items[*]}{.metadata.name}={.status.readyReplicas}/{.spec.replicas}{"\n"}{end}' 2>&1)
394+
echo "$output"
395+
if ! echo "$output" | grep -q "<no value>" && echo "$output" | awk -F '[=/]' '{if ($2 != $3) exit 1}' ; then
396+
exit 0
397+
fi
398+
if [ "$attempt" -lt 6 ]; then
399+
sleep 15
400+
fi
401+
done
402+
exit 1
403+
327404
- name: Debug nodes/pods on failure
328405
if: failure()
329406
run: |

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,14 @@ ansible-playbook k3s.orchestration.upgrade -i inventory.yml
161161
ansible-playbook playbooks/upgrade.yml -i inventory.yml
162162
```
163163

164+
Re-running the `site.yml` playbook after bumping `k3s_version` performs the same upgrade declaratively: it restarts the k3s services so the cluster picks up the new runtime. On a multi-server (HA) cluster, add `--forks=1` so Ansible restarts the servers one at a time and the etcd quorum is never lost:
165+
166+
```bash
167+
ansible-playbook playbooks/site.yml -i inventory.yml --forks=1
168+
```
169+
170+
The dedicated `upgrade.yml` playbook remains available and unchanged.
171+
164172
## Airgap Install
165173

166174
Airgap installation is supported via the `airgap_dir` variable. This variable should be set to the path of a directory containing the K3s binary and images. The release artifacts can be downloaded from the [K3s Releases](https://github.com/k3s-io/k3s/releases). You must download the appropriate images for you architecture (any of the compression formats will work). Additionally, you must run the `airgap` role to set up the airgapped environment.

roles/k3s_agent/tasks/main.yml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,12 @@
9595
loop:
9696
- "K3S_TOKEN={{ token }}"
9797

98+
# Always restart so a re-run of site.yml — whether it changed the agent config
99+
# or bumped k3s_version — reliably brings the agent onto the new state. The
100+
# install script leaves the service stopped (INSTALL_K3S_SKIP_START), so a plain
101+
# restart also covers the first install.
98102
- name: Enable and start K3s agent
99103
ansible.builtin.service:
100104
name: k3s-agent
101-
state: "{{ 'restarted' if _agent_config_result.changed else 'started' }}"
105+
state: restarted
102106
enabled: true

roles/k3s_server/tasks/main.yml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -294,11 +294,15 @@
294294
ansible.builtin.systemd:
295295
daemon_reload: true
296296

297+
# Always restart a joined server so a re-run of site.yml — whether it only
298+
# bumped k3s_version or changed config — brings it onto the new runtime, the
299+
# same way the first server above always restarts. On a multi-server cluster
300+
# run the playbook with --forks=1 so the servers restart one at a time and
301+
# the etcd quorum is preserved (see README).
297302
- name: Restart K3s service
298303
when:
299304
- ansible_facts.services['k3s.service'] is defined
300305
- ansible_facts.services['k3s.service'].state == 'running'
301-
- k3s_server_config_result.changed
302306
ansible.builtin.service:
303307
name: k3s
304308
state: restarted

0 commit comments

Comments
 (0)