Kubernetes 是一个开源的容器编排引擎,可用来对容器化应用进行自动化部署、扩缩和管理。本文将会描述如何搭建一个可用于生产环境的高可用集群。
若第一次接触 k8s,建议看完 k8s - 快速入门 再查看此文。
一、节点准备(每台都要执行)
准备 3 台同网段的服务器,另预留一个空闲 IP 做 VIP
- VIP: 192.168.5.50(网段里没有被使用的ip即可)
- Server node1: 192.168.5.101
- Server node2: 192.168.5.102
- Server node3: 192.168.5.103
1. 关闭 swap(可选,生产建议)
k3s 会把 kubelet 的
failSwapOn设为false,不关 swap 也能正常启动;但生产环境建议关闭:避免系统进程换出拖慢 kubelet 心跳和 etcd 选举,也让内存压力信号更准确。
若保留 swap,Pod 默认也不会使用(kubelet 默认swapBehavior: NoSwap),只有系统进程会用。
1 | sudo cp /etc/fstab /etc/fstab.bak |
回滚:
sudo cp /etc/fstab.bak /etc/fstab后执行sudo swapon -a即可恢复。
2. 确认网卡和 IP
1 | $ ip -br a |
获得网卡 ens2 和 IP 192.168.5.101
3. 安装 keepalived(VIP 高可用)
1 | sudo apt install -y keepalived |
配置文件如下 /etc/keepalived/keepalived.conf(第 1 台):
1 | # 健康检查:返回 0 表示健康,非 0 则表示本机服务挂了,按 weight 降低本机优先级 |
顺便一提:当你想把 VIP 用于监测其他服务时,修改 script 即可
1 | script "/usr/bin/curl -sf http://127.0.0.1:80/healthz" # 例如实现 Nginx 高可用 |
4. 其他节点
第 2/3 台需要修改 ⚠️ 部分包括 priority(分别改为99和98)、unicast_src_ip(节点自己的 IP)、unicast_peer(不含自己的另外两台);
其他配置则保持一致,特别是 virtual_router_id、auth_pass、virtual_ipaddress 。
5. 确认
启动并确认:
1 | sudo systemctl enable --now keepalived |
三台都写
state BACKUP,靠priority选主;不想让恢复的节点抢回 VIP(减少连接抖动)就在vrrp_instance里加nopreempt。
防火墙要放行 VRRP(IP 协议号 112),只开端口没用。
健康检查必须查本机服务,而不是去 ping 其他节点或检查 VIP:查别人只能证明别人活着,跟本机能不能对外服务无关。
二、安装 Server
1. 编写 config.yaml
只需要改下面三个变量,其余不用动:
1 | # ⚠️ 分别修改为本机 IP、网卡和 keepalived 的 VIP |
并直接执行命令,命令会将配置写入到 /etc/rancher/k3s/config.yaml
1 | sudo mkdir -p /etc/rancher/k3s && sudo tee /etc/rancher/k3s/config.yaml > /dev/null <<EOF |
写完查看配置内容,确保如 node-ip 等参数为真实参数而非变量占位符
1 | sudo cat /etc/rancher/k3s/config.yaml |
2. 安装命令
执行命令安装 K3s Server
1 | curl -sfL https://rancher-mirror.rancher.cn/k3s/k3s-install.sh | INSTALL_K3S_MIRROR=cn INSTALL_K3S_VERSION=v1.37.1+k3s1 sh - |
装完确认 apiserver 和 VIP 都能访问:
1 | sudo kubectl get nodes |
三、安装其他 Server
etcd 需要奇数台 server 才能保证多数派:3 台挂 1 台仍可读写,挂 2 台就不能写了。第 1 台已经初始化好集群,其余节点只要拿到 token 并指向 VIP。
1. 获取 token
在第一台 Server 执行命令查看 token
1 | sudo cat /var/lib/rancher/k3s/server/token |
2. 编写 config.yaml
同样修改变量为节点自己的
1 | export NODE_IP=192.168.5.102 \ |
然后执行命令写入配置
1 | sudo mkdir -p /etc/rancher/k3s && sudo tee /etc/rancher/k3s/config.yaml > /dev/null <<EOF |
cluster-cidr、service-cidr、cluster-dns、disable、secrets-encryption必须在所有 server 上一致,否则 join 会失败。
3. 安装
执行命令将 server-node2 加入集群
1 | curl -sfL https://rancher-mirror.rancher.cn/k3s/k3s-install.sh | INSTALL_K3S_MIRROR=cn INSTALL_K3S_VERSION=v1.37.1+k3s1 sh - |
建议等第 2 台
Ready之后再装第 3 台。
4. 验证
最终确认三台都是 Ready 且 ROLES 带 etcd 说明成功,此时挂掉任意一台,集群仍可以工作。
1 | $ sudo kubectl get nodes |
5. 注意事项
装错要先清理:节点数据目录里一旦有 etcd 数据,
server:参数会被直接忽略,改成单机模式就再也 join 不进去。要重来必须先执行sudo /usr/local/bin/k3s-uninstall.sh。固定注册地址:本文用 keepalived 的 VIP(
192.168.5.50)作为server:地址,第 1 台挂了新节点照样能加入;如果写第 1 台的真实 IP,那台一挂就加不进新节点了。业务节点:k3s 的 server 默认可调度,不加 agent 也能跑业务;要单独跑业务节点,用
sh -s - agent安装,配置文件里只保留server、token、node-ip、flannel-iface、kubelet-arg这类 agent 侧参数。
四、安装 Agent
server 默认就能跑业务,加 Agent 的目的是把业务和 etcd 隔开:Agent 不存 etcd 数据、不参与选举,加多少台都不影响 etcd 的奇数多数派,挂掉也不影响集群读写。示例新增一台业务节点 192.168.5.111。
1. 确认能连到集群
Agent 只需要能访问 apiserver,不需要装 keepalived:
1 | curl -k https://192.168.5.50:6443/healthz |
返回 ok 即可,报 x509 说明 VIP 没写进 server 的 tls-san。
2. 生成临时 token(在任意 server 上)
前文加 server 用的是 /var/lib/rancher/k3s/server/token 里的静态 token,但它是集群主密钥:能用来加入 server、创建后改不了,泄露等于失守。只加业务节点用临时 token 即可。
1 | sudo k3s token create --ttl 2h |
3. 编写 config.yaml
别把 server 那份配置复制过来:
cluster-init、cluster-cidr、service-cidr、cluster-dns、disable、etcd-*、secrets-encryption都是 server 侧参数,由 server 下发。Agent 写了不生效,日志里只会出现WARN Unknown flag --xxx found in config.yaml, skipping,看着「配置了」其实没配。
同样只改变量:
1 | export NODE_IP=192.168.5.111 \ |
1 | sudo mkdir -p /etc/rancher/k3s && sudo tee /etc/rancher/k3s/config.yaml > /dev/null <<EOF |
需要打标签或污点时,也在这里加
node-label: [disk=ssd]、node-taint: [gpu=true:NoSchedule],安装时一并生效。
4. 安装
1 | curl -sfL https://rancher-mirror.rancher.cn/k3s/k3s-install.sh | INSTALL_K3S_MIRROR=cn INSTALL_K3S_VERSION=v1.37.1+k3s1 sh -s - agent |
注意结尾是 sh -s - agent,不是 server;写错会把业务节点装成第 4 台 server,etcd 变成偶数台(4 台挂 2 台就不可用)。
5. 验证
在任意 server 上查看:
1 | $ sudo kubectl get nodes -o wide |
ROLES 显示 <none> 是正常的,Agent 本来就没有 control-plane/etcd 角色;INTERNAL-IP 应该是 node-ip 填的地址,不对说明网卡或 IP 选错了。
在 Agent 本机确认服务状态:
1 | sudo systemctl status k3s-agent --no-pager |
五、让 server 不再跑业务(可选)
加 Agent 不会自动让 server 停止调度,server 上仍然会继续创建 Pod。要让业务只落在 Agent 上,需要给 server 打污点,例如在每台 server 的 config.yaml 里加:
1 | node-taint: |
然后 sudo systemctl restart k3s 重启生效,再把 server 上残留的 Pod 清掉:
1 | sudo kubectl drain node1 --ignore-daemonsets --delete-emptydir-data |
NoSchedule只拦新调度,不会动已经在跑的 Pod,所以要 drain 一次;drain 会顺手把节点标成不可调度,得再 uncordon 回来(污点已经在拦新 Pod,不需要留着 cordon)。操作前确认业务有多副本,单副本服务会中断。
六、注意事项
- Agent 也能持 VIP:本文 VIP 只服务 apiserver,健康检查查的是 6443。如果希望 ingress 也通过 VIP 对外,需要在跑 ingress 的节点上装 keepalived,并把
vrrp_script的检查命令改成对应端口。 - VIP 变了要改 kubeconfig:
/etc/rancher/k3s/k3s.yaml里写的是https://192.168.5.50:6443,VIP 换地址或不再使用 keepalived 时,所有kubectl客户端(包括拷到本地的~/.kube/config)都要同步改,否则直接连不上。
七、卸载 Agent
在 server 上摘掉节点,再在业务节点上执行卸载脚本:
1 | # server 上 |
1 | # node4 上 |
顺序不能反。先卸载再删 Node,集群里会留一个永远
NotReady的节点,补一句kubectl delete node node4即可清掉。
八、卸载 Server
1 | # 存活的 server 上 |
1 | # node3 上 |
卸载不可逆:脚本会停掉 k3s 和所有 Pod,删掉本机 etcd 数据和节点配置。动手前先
sudo k3s etcd-snapshot save备份,快照在/var/lib/rancher/k3s/server/db/snapshots/。3 台卸掉 1 台,剩 2 台仍可读写;但不能一台台卸成 1 台,剩下那台凑不齐 etcd 多数派,缩容要用
k3s server --cluster-reset。卸载后按需清理残留:
sudo ip link delete cni0、sudo ip link delete flannel.1,以及不再需要的 keepalived(sudo systemctl disable --now keepalived)。
