Skip to main content

Lightweight Kubernetes Cluster - K3s: Upgrade K3s Cluster to a New Release

1403 words·
Kubernetes Kubernetes Cluster K3s Debian Kubectl Helm

Overview
#

I’m using the following Debian 12 based nodes in this tutorial:

192.168.30.20 # Controller Node
192.168.30.21 # Worker Node
192.168.30.22 # Worker Node
192.168.30.23 # Worker Node



Create K3s Cluster
#

Install Prerequisites
#

Install the following dependencies on the controller and the worker nodes:

# Install prerequisites
sudo apt update && sudo apt upgrade -y &&
sudo apt install -y curl iptables jq

List Available K3s Versions
#

Available K3s versions: https://github.com/k3s-io/k3s/tags

# List available K3s versions: Stable only
curl -s https://api.github.com/repos/k3s-io/k3s/releases | jq -r '.[] | select(.prerelease == false) | .tag_name' | grep 'k3s' | sort -V

# Shell output:
v1.29.13+k3s1
v1.29.14+k3s1
v1.30.9+k3s1
v1.30.10+k3s1
v1.31.4+k3s1
v1.31.5+k3s1
v1.31.6+k3s1
v1.32.0+k3s1
v1.32.1+k3s1
v1.32.2+k3s1
  • v1.32.2 Kubernetes upstream version that K3s is based on

  • +k3s1 K3s-specific build, including optimizations and customizations


Install Controller Node
#

Install Controller Node
#

Install and enable K3s on the controller node:

# Install K3s: With Traefik Ingress
export K3S_VERSION="v1.30.10+k3s1"
curl -sfL https://get.k3s.io | INSTALL_K3S_VERSION="$K3S_VERSION" sh -

Adapt Permissions
#

The following steps create a new group called “kubectl” to allows other users then root to controll the cluster via Kubectl:

# Create a kubectl group
sudo groupadd kubectl

# Add the current user to the group
sudo usermod -aG kubectl $USER

# Change the group owner of the K3s config file
sudo chown root:kubectl /etc/rancher/k3s/k3s.yaml

# Adapt the permissions
sudo chmod 640 /etc/rancher/k3s/k3s.yaml
# Verify permissions
ls -l /etc/rancher/k3s/k3s.yaml

# Shell output:
-rw-r----- 1 root kubectl 2961 Mar  8 10:26 /etc/rancher/k3s/k3s.yaml
# Export env variable: Permanent
echo 'export KUBECONFIG=/etc/rancher/k3s/k3s.yaml' >> ~/.bashrc

# Apply changes
source ~/.bashrc

Verify the K3s Cluster
#

Verify the K3s cluster:

# List cluster nodes:
kubectl get nodes -o wide

# Shell output:
NAME        STATUS   ROLES                  AGE   VERSION         INTERNAL-IP     EXTERNAL-IP   OS-IMAGE                         KERNEL-VERSION   CONTAINER-RUNTIME
debian-01   Ready    control-plane,master   10m   v1.30.10+k3s1   192.168.30.20   <none>        Debian GNU/Linux 12 (bookworm)   6.1.0-31-amd64   containerd://1.7.23-k3s2

Copy the Token
#

Get the token from the controller node that can be used to join the worker nodes to the cluster:

# Extract the token from the master node
sudo cat /var/lib/rancher/k3s/server/node-token

# Shell output:
K109dae8560dc61d3eb9e8113974b7d177d522bc257e375df13204621c7379cee75::server:ebb8279a2c0d4b91ed8c22e35a4e9b30



Install Worker Nodes
#

Join Worker Nodes
#

Run the following command on the worker node, to export the K3s version, Controller node token and IP as env variables:

# Add Worker Nodes
export K3S_VERSION="v1.30.10+k3s1"
export K3S_TOKEN="K109dae8560dc61d3eb9e8113974b7d177d522bc257e375df13204621c7379cee75::server:ebb8279a2c0d4b91ed8c22e35a4e9b30"
export SERVER_IP="192.168.30.20"

curl -sfL https://get.k3s.io | INSTALL_K3S_VERSION="$K3S_VERSION" \
INSTALL_K3S_EXEC="agent" \
K3S_TOKEN="$K3S_TOKEN" \
K3S_URL="https://$SERVER_IP:6443" sh -

Label the Worker Nodes
#

# Label the worker nodes
kubectl label nodes debian-02 kubernetes.io/role=worker &&
kubectl label nodes debian-03 kubernetes.io/role=worker &&
kubectl label nodes debian-04 kubernetes.io/role=worker

Verify the K3s cluster
#

# List cluster nodes:
kubectl get nodes -o wide

# Shell output:
NAME        STATUS   ROLES                  AGE     VERSION         INTERNAL-IP     EXTERNAL-IP   OS-IMAGE                         KERNEL-VERSION   CONTAINER-RUNTIME
debian-01   Ready    control-plane,master   20m     v1.30.10+k3s1   192.168.30.20   <none>        Debian GNU/Linux 12 (bookworm)   6.1.0-31-amd64   containerd://1.7.23-k3s2
debian-02   Ready    worker                 4m28s   v1.30.10+k3s1   192.168.30.21   <none>        Debian GNU/Linux 12 (bookworm)   6.1.0-31-amd64   containerd://1.7.23-k3s2
debian-03   Ready    worker                 3m59s   v1.30.10+k3s1   192.168.30.22   <none>        Debian GNU/Linux 12 (bookworm)   6.1.0-31-amd64   containerd://1.7.23-k3s2
debian-04   Ready    worker                 3m56s   v1.30.10+k3s1   192.168.30.23   <none>        Debian GNU/Linux 12 (bookworm)   6.1.0-31-amd64   containerd://1.7.23-k3s2



Taint the Controller Node
#

Prevent regular workloads from being scheduled on the control plane node:

# Taint the master node
kubectl taint nodes debian-01 key=value:NoSchedule
# Verify node taints
kubectl describe node debian-01 | grep Taints

# Shell output:
Taints:             key=value:NoSchedule

Install Helm
#

# Install Helm with script
curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 &&
chmod +x get_helm.sh &&
./get_helm.sh

Example Deployment
#

Create an example deployment with NodePort service, so that the cluster runs a workload:

  • example-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: example-deployment
  labels:
    app: example-deployment
spec:
  replicas: 5
  selector:
    matchLabels:
      app: example-container
  template:
    metadata:
      labels:
        app: example-container
    spec:
      containers:
      - name: my-container
        image: jueklu/container-2
        ports:
        - containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
  name: my-container-service
spec:
  type: NodePort
  selector:
    app: example-container
  ports:
  - protocol: TCP
    port: 8080
    targetPort: 8080
    nodePort: 30080
# Apply the configuration
kubectl apply -f example-deployment.yaml

Verify the deployment:

# Verify the deployment
kubectl get pod -o wide

# Shell output:
NAME                                 READY   STATUS    RESTARTS   AGE    IP          NODE        NOMINATED NODE   READINESS GATES
example-deployment-747b9cbf6-69kqt   1/1     Running   0          47s    10.42.1.3   debian-02   <none>           <none>
example-deployment-747b9cbf6-cb7d9   1/1     Running   0          47s    10.42.3.4   debian-04   <none>           <none>
example-deployment-747b9cbf6-gmkzd   1/1     Running   0          47s    10.42.2.3   debian-03   <none>           <none>
example-deployment-747b9cbf6-gmp25   1/1     Running   0          47s    10.42.1.4   debian-02   <none>           <none>
example-deployment-747b9cbf6-lmv7w   1/1     Running   0          102s   10.42.3.3   debian-04   <none>           <none>



Upgrade the K3s Cluster
#

Upgrade the K3s cluster to a new version:

  • Upgrade the Controller node first

  • Drain, upgrade and uncordon each worker node in sequence


Backup SQLite DB
#

# Install SQLite client
sudo apt install sqlite3 -y

# Verify the installation
sqlite3 --version
# Create a backup directory
sudo mkdir -p /k3s-backup

# Use SQLite dump to create a backup of the DB
sudo sqlite3 /var/lib/rancher/k3s/server/db/state.db ".backup '/k3s-backup/v1.30.10+k3s1_backup.db'"

Upgrade Controller Node
#

# Stop the K3s service
systemctl stop k3s
# Upgrade the controller node
export K3S_VERSION="v1.31.6+k3s1"
curl -sfL https://get.k3s.io | INSTALL_K3S_VERSION="$K3S_VERSION" sh -
# Start the K3s service
systemctl start k3s
# Verify the current version
k3s --version

# Shell output:
k3s version v1.31.6+k3s1 (6ab750f9)
go version go1.22.12
# Verify the cluster
kubectl get nodes -o wide

# Shell output:
NAME        STATUS   ROLES                  AGE   VERSION         INTERNAL-IP     EXTERNAL-IP   OS-IMAGE                         KERNEL-VERSION   CONTAINER-RUNTIME
debian-01   Ready    control-plane,master   51m   v1.31.6+k3s1    192.168.30.20   <none>        Debian GNU/Linux 12 (bookworm)   6.1.0-31-amd64   containerd://2.0.2-k3s2
debian-02   Ready    worker                 34m   v1.30.10+k3s1   192.168.30.21   <none>        Debian GNU/Linux 12 (bookworm)   6.1.0-31-amd64   containerd://1.7.23-k3s2
debian-03   Ready    worker                 34m   v1.30.10+k3s1   192.168.30.22   <none>        Debian GNU/Linux 12 (bookworm)   6.1.0-31-amd64   containerd://1.7.23-k3s2
debian-04   Ready    worker                 34m   v1.30.10+k3s1   192.168.30.23   <none>        Debian GNU/Linux 12 (bookworm)   6.1.0-31-amd64   containerd://1.7.23-k3s2
# Verify the pods
kubectl get pods

# Shell output:
NAME                                 READY   STATUS    RESTARTS   AGE
example-deployment-747b9cbf6-69kqt   1/1     Running   0          5m48s
example-deployment-747b9cbf6-cb7d9   1/1     Running   0          5m48s
example-deployment-747b9cbf6-gmkzd   1/1     Running   0          5m48s
example-deployment-747b9cbf6-gmp25   1/1     Running   0          5m48s
example-deployment-747b9cbf6-lmv7w   1/1     Running   0          6m43s

Upgrade Worker Nodes: Node 1
#

# Drain the worker node
kubectl drain debian-02 --ignore-daemonsets

# Shell output:
Warning: ignoring DaemonSet-managed Pods: kube-system/svclb-traefik-55bec918-qxjcq
evicting pod default/example-deployment-747b9cbf6-gmp25
evicting pod default/example-deployment-747b9cbf6-69kqt
pod/example-deployment-747b9cbf6-69kqt evicted
pod/example-deployment-747b9cbf6-gmp25 evicted
node/debian-02 drained
# Upgrade the worker node
export K3S_VERSION="v1.31.6+k3s1"
export K3S_TOKEN="K109dae8560dc61d3eb9e8113974b7d177d522bc257e375df13204621c7379cee75::server:ebb8279a2c0d4b91ed8c22e35a4e9b30"
export SERVER_IP="192.168.30.20"

curl -sfL https://get.k3s.io | INSTALL_K3S_VERSION="$K3S_VERSION" \
INSTALL_K3S_EXEC="agent" \
K3S_TOKEN="$K3S_TOKEN" \
K3S_URL="https://$SERVER_IP:6443" sh -
# Uncordon the worker node
kubectl uncordon debian-02
# Verify the cluster
kubectl get nodes -o wide

# Shell output:
NAME        STATUS   ROLES                  AGE   VERSION         INTERNAL-IP     EXTERNAL-IP   OS-IMAGE                         KERNEL-VERSION   CONTAINER-RUNTIME
debian-01   Ready    control-plane,master   51m   v1.31.6+k3s1    192.168.30.20   <none>        Debian GNU/Linux 12 (bookworm)   6.1.0-31-amd64   containerd://2.0.2-k3s2
debian-02   Ready    worker                 34m   v1.30.10+k3s1   192.168.30.21   <none>        Debian GNU/Linux 12 (bookworm)   6.1.0-31-amd64   containerd://1.7.23-k3s2
debian-03   Ready    worker                 34m   v1.30.10+k3s1   192.168.30.22   <none>        Debian GNU/Linux 12 (bookworm)   6.1.0-31-amd64   containerd://1.7.23-k3s2
debian-04   Ready    worker                 34m   v1.30.10+k3s1   192.168.30.23   <none>        Debian GNU/Linux 12 (bookworm)   6.1.0-31-amd64   containerd://1.7.23-k3s2

Upgrade Worker Nodes: Node 2
#

# Drain the worker node
kubectl drain debian-03 --ignore-daemonsets
# Upgrade the worker node
export K3S_VERSION="v1.31.6+k3s1"
export K3S_TOKEN="K109dae8560dc61d3eb9e8113974b7d177d522bc257e375df13204621c7379cee75::server:ebb8279a2c0d4b91ed8c22e35a4e9b30"
export SERVER_IP="192.168.30.20"

curl -sfL https://get.k3s.io | INSTALL_K3S_VERSION="$K3S_VERSION" \
INSTALL_K3S_EXEC="agent" \
K3S_TOKEN="$K3S_TOKEN" \
K3S_URL="https://$SERVER_IP:6443" sh -
# Uncordon the worker node
kubectl uncordon debian-03
# Verify the cluster
kubectl get nodes -o wide

# Shell output:
NAME        STATUS   ROLES                  AGE   VERSION         INTERNAL-IP     EXTERNAL-IP   OS-IMAGE                         KERNEL-VERSION   CONTAINER-RUNTIME
debian-01   Ready    control-plane,master   64m   v1.31.6+k3s1    192.168.30.20   <none>        Debian GNU/Linux 12 (bookworm)   6.1.0-31-amd64   containerd://2.0.2-k3s2
debian-02   Ready    worker                 47m   v1.31.6+k3s1    192.168.30.21   <none>        Debian GNU/Linux 12 (bookworm)   6.1.0-31-amd64   containerd://2.0.2-k3s2
debian-03   Ready    worker                 47m   v1.31.6+k3s1    192.168.30.22   <none>        Debian GNU/Linux 12 (bookworm)   6.1.0-31-amd64   containerd://2.0.2-k3s2
debian-04   Ready    worker                 47m   v1.30.10+k3s1   192.168.30.23   <none>        Debian GNU/Linux 12 (bookworm)   6.1.0-31-amd64   containerd://1.7.23-k3s2

Upgrade Worker Nodes: Node 3
#

# Drain the worker node
kubectl drain debian-04 --ignore-daemonsets
# Upgrade the worker node
export K3S_VERSION="v1.31.6+k3s1"
export K3S_TOKEN="K109dae8560dc61d3eb9e8113974b7d177d522bc257e375df13204621c7379cee75::server:ebb8279a2c0d4b91ed8c22e35a4e9b30"
export SERVER_IP="192.168.30.20"

curl -sfL https://get.k3s.io | INSTALL_K3S_VERSION="$K3S_VERSION" \
INSTALL_K3S_EXEC="agent" \
K3S_TOKEN="$K3S_TOKEN" \
K3S_URL="https://$SERVER_IP:6443" sh -
# Uncordon the worker node
kubectl uncordon debian-04
# Verify the cluster
kubectl get nodes -o wide

# Shell output:
NAME        STATUS   ROLES                  AGE   VERSION        INTERNAL-IP     EXTERNAL-IP   OS-IMAGE                         KERNEL-VERSION   CONTAINER-RUNTIME
debian-01   Ready    control-plane,master   65m   v1.31.6+k3s1   192.168.30.20   <none>        Debian GNU/Linux 12 (bookworm)   6.1.0-31-amd64   containerd://2.0.2-k3s2
debian-02   Ready    worker                 49m   v1.31.6+k3s1   192.168.30.21   <none>        Debian GNU/Linux 12 (bookworm)   6.1.0-31-amd64   containerd://2.0.2-k3s2
debian-03   Ready    worker                 48m   v1.31.6+k3s1   192.168.30.22   <none>        Debian GNU/Linux 12 (bookworm)   6.1.0-31-amd64   containerd://2.0.2-k3s2
debian-04   Ready    worker                 48m   v1.31.6+k3s1   192.168.30.23   <none>        Debian GNU/Linux 12 (bookworm)   6.1.0-31-amd64   containerd://2.0.2-k3s2
# Verify the pods
kubectl get pods -o wide

# Shell output:
NAME                                 READY   STATUS    RESTARTS   AGE     IP          NODE        NOMINATED NODE   READINESS GATES
example-deployment-747b9cbf6-4rpnj   1/1     Running   0          3m53s   10.42.1.6   debian-02   <none>           <none>
example-deployment-747b9cbf6-4znrw   1/1     Running   0          67s     10.42.2.7   debian-03   <none>           <none>
example-deployment-747b9cbf6-64bc5   1/1     Running   0          3m53s   10.42.1.5   debian-02   <none>           <none>
example-deployment-747b9cbf6-pck5s   1/1     Running   0          67s     10.42.2.8   debian-03   <none>           <none>
example-deployment-747b9cbf6-rq75q   1/1     Running   0          67s     10.42.2.6   debian-03   <none>           <none>