如何从单节点 K3s 迁移到高可用

Posted by Ksd on April 10, 2022

前言

K3s 支持单节点安装和高可用安装两种方式,单节点安装就是 k3s server 内嵌了一个 SQLite 作为数据库,而且这个数据库不支持扩展,所以生产环境不建议使用单节点方式安装 k3s。

单节点 k3s server 的架构:

K3s 高可用安装分为 “使用外部数据库实现高可用安装” 和 “嵌入式 etcd 的高可用” 两种方式:

  • 使用外部数据库实现高可用安装:可以将 k3s 服务和数据库解耦,在启动 k3s 服务时通过对应数据库的参数来连接已经创建好的数据库,支持:PostgreSQL、MySQL、MariaDB、etcd。
  • 嵌入式 etcd 的高可用:从 v1.19.5+k3s1 版本开始,k3s 已添加了对嵌入式 etcd 的支持,每个 k3s master 节点内置了一个 etcd 数据库,要在这种模式下运行 k3s,你必须有奇数的 server 节点。我们建议从三个节点开始。

虽然单节点 k3s server 集群可以满足各种用例,但是对于需要稳定运行的生产环境,推荐安装高可用的 k3s server。如果你已经在生产上部署了单节点的 k3s 集群,现在想将你的集群转换为高可用集群,你之前可能会选择重新搭建高可用的 k3s 集群。

本文将要介绍如何将单节点嵌入式 SQLite 数据库 k3s 集群直接转换为内置 etcd 高可用 k3s 集群,并且不影响业务集群的数据。

注意: k3s v1.22.2 及更新版本才支持从单节点 k3s 集群转换为内置 etcd 集群

单节点 k3s 集群转换为内置 etcd 高可用集群

环境准备

首先你需要有一个单节点的 k3s 集群,本例使用 1 master 节点、1 worker 节点的 k3s 集群。安装方法可以参考 k3s 官网快速入门指南章节。

root@ip-172-31-13-233:~# kubectl get nodes
NAME               STATUS   ROLES                  AGE   VERSION
ip-172-31-13-233   Ready    control-plane,master   86s   v1.23.6+k3s1
ip-172-31-4-125    Ready    <none>                 42s   v1.23.6+k3s1

创建测试 deployment 和 service,以便转换为高可用集群后确认数据是否丢失:

root@ip-172-31-13-233:~# kubectl apply -f https://raw.githubusercontent.com/kingsd041/tmp/master/nginx.yml
deployment.apps/nginx-deployment created
service/nginx created

root@ip-172-31-13-233:~# kubectl get deployment,svc
NAME                               READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/nginx-deployment   3/3     3            3           36s

NAME                 TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
service/kubernetes   ClusterIP   10.43.0.1       <none>        443/TCP        5m47s
service/nginx        NodePort    10.43.254.106   <none>        80:30007/TCP   36s

将单节点 K3s 集群转换为内置 etcd 高可用集群

首先,先停止 k3s 服务:

root@ip-172-31-13-233:~# systemctl stop k3s

然后,通过使用 --cluster-init 标志重新启动你的 k3s server 来将其转换为 etcd 集群:

root@ip-172-31-13-233:~# curl -sfL https://get.k3s.io | sh -s - --cluster-init

[INFO]  Finding release for channel stable
[INFO]  Using v1.23.6+k3s1 as release
[INFO]  Downloading hash https://github.com/k3s-io/k3s/releases/download/v1.23.6+k3s1/sha256sum-amd64.txt
[INFO]  Skipping binary downloaded, installed k3s matches hash
[INFO]  Skipping installation of SELinux RPM
[INFO]  Skipping /usr/local/bin/kubectl symlink to k3s, already exists
[INFO]  Skipping /usr/local/bin/crictl symlink to k3s, already exists
[INFO]  Skipping /usr/local/bin/ctr symlink to k3s, already exists
[INFO]  Creating killall script /usr/local/bin/k3s-killall.sh
[INFO]  Creating uninstall script /usr/local/bin/k3s-uninstall.sh
[INFO]  env: Creating environment file /etc/systemd/system/k3s.service.env
[INFO]  systemd: Creating service file /etc/systemd/system/k3s.service
[INFO]  systemd: Enabling k3s unit
Created symlink /etc/systemd/system/multi-user.target.wants/k3s.service → /etc/systemd/system/k3s.service.
[INFO]  systemd: Starting k3s

查看 master 节点的角色,来确认是否转换成功:

root@ip-172-31-13-233:~# kubectl get nodes
NAME               STATUS   ROLES                       AGE     VERSION
ip-172-31-13-233   Ready    control-plane,etcd,master   9m39s   v1.23.6+k3s1
ip-172-31-4-125    Ready    <none>                      8m55s   v1.23.6+k3s1

从上面 ROLES 列可以看到,master 节点的角色增加了 etcd,证明已经通过内置 etcd 数据库重新启动了 k3s 集群。

扩容 K3s master 节点

当通过 --cluster-init 标志将默认嵌入式 SQLite 数据库的现有集群转换为 etcd 集群之后,我们也可以参考嵌入式 DB 的高可用组成多个 master 节点的集群,操作步骤和普通的嵌入式 etcd 的高可用集群的方式没有任何区别。

本例将扩容到 3 个 k3s master 节点:

root@ip-172-31-13-233:~# kubectl get nodes
NAME               STATUS   ROLES                       AGE     VERSION
ip-172-31-1-114    Ready    control-plane,etcd,master   30s     v1.23.6+k3s1
ip-172-31-13-233   Ready    control-plane,etcd,master   24m     v1.23.6+k3s1
ip-172-31-4-125    Ready    <none>                      23m     v1.23.6+k3s1
ip-172-31-9-12     Ready    control-plane,etcd,master   2m42s   v1.23.6+k3s1

当然,你也可以继续向集群中添加 k3s worker 节点:

root@ip-172-31-13-233:~# kubectl get nodes
NAME               STATUS   ROLES                       AGE     VERSION
ip-172-31-1-114    Ready    control-plane,etcd,master   89s     v1.23.6+k3s1
ip-172-31-13-233   Ready    control-plane,etcd,master   25m     v1.23.6+k3s1
ip-172-31-4-125    Ready    <none>                      24m     v1.23.6+k3s1
ip-172-31-8-211    Ready    <none>                      7s      v1.23.6+k3s1
ip-172-31-9-12     Ready    control-plane,etcd,master   3m41s   v1.23.6+k3s1

验证业务数据是否有变化

最后,再次确认之前创建的测试 deployment、service 是否有变化,来验证转换后数据是否有影响:

root@ip-172-31-13-233:~# kubectl get deployment,svc
NAME                               READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/nginx-deployment   3/3     3            3           6m40s

NAME                 TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
service/kubernetes   ClusterIP   10.43.0.1       <none>        443/TCP        11m
service/nginx        NodePort    10.43.254.106   <none>        80:30007/TCP   6m40s

从以上结果来看,转换为内置 etcd 数据库之后,业务数据并没有变化,不会影响你生产或测试环境。而且这些转换过程由 k3s 自动完成,不需要我们过多操作。

迁移失败后如何恢复数据

通过使用 --cluster-init 标志执行从 sqlite 迁移到 etcd 时,K3s 首先会将 sqlite 默认数据文件(/var/lib/rancher/k3s/server/db/state.db)备份为 /var/lib/rancher/k3s/server/db/state.db.migrated。然后创建 etcd 数据目录(/var/lib/rancher/k3s/server/db/etcd),并把转换后的 etcd 数据生成到该目录下。最后,通过 --cluster-init 标志告知 K3s 应该使用 etcd 启动该集群。

root@ip-172-31-8-83:~# ls /var/lib/rancher/k3s/server/db/
etcd  state.db-shm  state.db-wal  state.db.migrated

所以,当我们迁移到 etcd 数据库失败时,只需要将 state.db.migrated 还原为 state.db,然后删除掉 etcd 文件夹,并且去掉 --cluster-init 标志重新启动 K3s 即可:

root@ip-172-31-8-83:~# service k3s stop
root@ip-172-31-8-83:~# mv /var/lib/rancher/k3s/server/db/state.db.migrated /var/lib/rancher/k3s/server/db/state.db
root@ip-172-31-8-83:~# rm -rf /var/lib/rancher/k3s/server/db/etcd
root@ip-172-31-8-83:~# ls /var/lib/rancher/k3s/server/db/
state.db  state.db-shm  state.db-wal
root@ip-172-31-8-83:~# curl -sfL https://get.k3s.io | sh -
[INFO]  Finding release for channel stable
[INFO]  Using v1.23.6+k3s1 as release
[INFO]  Downloading hash https://github.com/k3s-io/k3s/releases/download/v1.23.6+k3s1/sha256sum-amd64.txt
[INFO]  Skipping binary downloaded, installed k3s matches hash
[INFO]  Skipping installation of SELinux RPM
[INFO]  Skipping /usr/local/bin/kubectl symlink to k3s, already exists
[INFO]  Skipping /usr/local/bin/crictl symlink to k3s, already exists
[INFO]  Skipping /usr/local/bin/ctr symlink to k3s, already exists
[INFO]  Creating killall script /usr/local/bin/k3s-killall.sh
[INFO]  Creating uninstall script /usr/local/bin/k3s-uninstall.sh
[INFO]  env: Creating environment file /etc/systemd/system/k3s.service.env
[INFO]  systemd: Creating service file /etc/systemd/system/k3s.service
[INFO]  systemd: Enabling k3s unit
Created symlink /etc/systemd/system/multi-user.target.wants/k3s.service → /etc/systemd/system/k3s.service.
[INFO]  systemd: Starting k3s
root@ip-172-31-8-83:~# kubectl get nodes
NAME             STATUS   ROLES                  AGE     VERSION
ip-172-31-2-70   Ready    <none>                 7m21s   v1.23.6+k3s1
ip-172-31-8-83   Ready    control-plane,master   7m48s   v1.23.6+k3s1