Kubernetes已经成为容器编排的事实标准,但搭建一个生产可用的K8s集群并非易事。本文将基于实战经验,从零开始构建一个高可用的企业级K8s集群,并总结在部署过程中最常见的10个“坑”及其解决方案。
一、概述
1.1 背景介绍
初次搭建K8s集群时,很多开发者可能仅通过kubeadm init命令便宣告成功,但真正投入生产环境后,各种问题会接踵而至:证书过期导致集群不可用、etcd数据丢失难以恢复、网络插件选择不当影响性能、Master节点单点故障造成业务中断等。本文将系统梳理这些经验,帮助读者构建一个稳定、可靠的生产级集群。
1.2 技术特点
企业级K8s集群的核心要求:
- 高可用:Master节点采用多副本部署,消除单点故障风险。
- 可扩展:能够便捷地横向扩展Worker节点。
- 安全:实施RBAC权限控制、网络策略隔离及镜像安全扫描。
- 可观测:集成完善的监控、日志收集与告警体系。
- 灾备:建立定时的etcd备份恢复机制与多集群容灾方案。
架构设计:
采用三节点Master高可用架构,通过负载均衡器(VIP)对外暴露API Server。
┌─────────────────┐
│ LoadBalancer │
│ (VIP: 10.0.0.100)│
└────────┬────────┘
│
┌────────▼────────┐ ┌────────▼────────┐ ┌────────▼────────┐
│ Master-1 │ │ Master-2 │ │ Master-3 │
│ (API+ETCD) │ │ (API+ETCD) │ │ (API+ETCD) │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│ │ │
┌────────▼────────┐ ┌────────▼────────┐ ┌────────▼────────┐
│ Node-1 │ │ Node-2 │ │ Node-N │
│ (Kubelet+CNI) │ │ (Kubelet+CNI) │ │ (Kubelet+CNI) │
└─────────────────┘ └─────────────────┘ └─────────────────┘
1.3 适用场景
- 从零开始搭建生产环境的K8s集群。
- 将测试或开发集群升级至生产可用标准。
- 深入理解K8s高可用集群的架构原理与部署细节。
1.4 环境要求
| 组件 |
版本要求 |
说明 |
| 操作系统 |
CentOS 7.9 / Ubuntu 20.04+ |
推荐使用Ubuntu系统 |
| Kubernetes |
1.28+ |
本文以1.28版本为例 |
| Container Runtime |
containerd 1.7+ |
K8s 1.24后不再默认支持Docker |
| 网络插件 |
Calico 3.26+ |
生产环境推荐 |
| 服务器配置 |
4C8G+ |
Master节点最低配置 |
| 服务器规划: |
角色 |
主机名 |
IP |
配置 |
| Master-1 |
k8s-master-1 |
10.0.0.11 |
4C8G 100G |
| Master-2 |
k8s-master-2 |
10.0.0.12 |
4C8G 100G |
| Master-3 |
k8s-master-3 |
10.0.0.13 |
4C8G 100G |
| Node-1 |
k8s-node-1 |
10.0.0.21 |
8C16G 200G |
| Node-2 |
k8s-node-2 |
10.0.0.22 |
8C16G 200G |
| Node-3 |
k8s-node-3 |
10.0.0.23 |
8C16G 200G |
| VIP |
- |
10.0.0.100 |
负载均衡虚拟IP |
二、详细部署步骤
2.1 准备工作
以下操作需在所有节点上执行。
系统基础配置:
# 设置主机名(每台机器执行对应的命令)
hostnamectl set-hostname k8s-master-1
# 配置主机名解析
cat >> /etc/hosts << 'EOF'
10.0.0.11 k8s-master-1
10.0.0.12 k8s-master-2
10.0.0.13 k8s-master-3
10.0.0.21 k8s-node-1
10.0.0.22 k8s-node-2
10.0.0.23 k8s-node-3
10.0.0.100 k8s-api # VIP
EOF
# 关闭防火墙与SELinux(生产环境建议配置具体规则)
systemctl stop firewalld && systemctl disable firewalld
setenforce 0
sed -i 's/^SELINUX=enforcing$/SELINUX=disabled/' /etc/selinux/config
# 关闭swap(Kubernetes强制要求)
swapoff -a
sed -i '/ swap / s/^\(.*\)$/#\1/g' /etc/fstab
# 验证
free -h # Swap一行应显示为0
内核参数优化:
# 加载内核模块
cat > /etc/modules-load.d/k8s.conf << 'EOF'
overlay
br_netfilter
EOF
modprobe overlay
modprobe br_netfilter
# 配置内核参数
cat > /etc/sysctl.d/k8s.conf << 'EOF'
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
# 性能优化参数
net.ipv4.tcp_max_syn_backlog = 65535
net.core.somaxconn = 65535
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 30
fs.file-max = 655360
fs.inotify.max_user_watches = 524288
EOF
sysctl --system
安装容器运行时containerd:
从K8s 1.24开始,不再内置对Docker的支持,推荐使用containerd。
# 安装依赖
yum install -y yum-utils device-mapper-persistent-data lvm2
# 添加Docker仓库(内含containerd)
yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
# 安装containerd
yum install -y containerd.io
# 生成并修改默认配置
containerd config default > /etc/containerd/config.toml
sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml
# 为国内用户修改sandbox镜像地址
sed -i 's|registry.k8s.io/pause:3.8|registry.aliyuncs.com/google_containers/pause:3.9|' /etc/containerd/config.toml
# 启动并设置开机自启
systemctl enable containerd
systemctl start containerd
# 验证安装
ctr version
安装kubeadm、kubelet、kubectl:
# 配置阿里云Kubernetes Yum源
cat > /etc/yum.repos.d/kubernetes.repo << 'EOF'
[kubernetes]
name=Kubernetes
baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64/
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg
EOF
# 安装指定版本(此处以1.28.0为例)
yum install -y kubelet-1.28.0 kubeadm-1.28.0 kubectl-1.28.0
systemctl enable kubelet # 先启用,暂不启动
2.2 配置高可用负载均衡
在初始化集群前,需先建立API Server的高可用访问点。这里采用成熟的HAProxy + Keepalived方案。
安装软件包(在计划部署负载均衡的Master节点上执行,例如Master-1和Master-2):
yum install -y haproxy keepalived
配置HAProxy (/etc/haproxy/haproxy.cfg):
global
log /dev/log local0
log /dev/log local1 notice
daemon
defaults
mode tcp
log global
option tcplog
option dontlognull
timeout connect 5000
timeout client 50000
timeout server 50000
frontend kubernetes-apiserver
bind *:6443
mode tcp
option tcplog
default_backend kubernetes-apiserver
backend kubernetes-apiserver
mode tcp
option tcp-check
balance roundrobin
server k8s-master-1 10.0.0.11:6443 check fall 3 rise 2
server k8s-master-2 10.0.0.12:6443 check fall 3 rise 2
server k8s-master-3 10.0.0.13:6443 check fall 3 rise 2
listen stats
bind *:8080
mode http
stats enable
stats uri /stats
stats auth admin:admin123
配置Keepalived:
vrrp_script check_haproxy {
script "/etc/keepalived/check_haproxy.sh"
interval 3
weight -20
}
vrrp_instance VI_1 {
state MASTER
interface eth0 # 请根据实际情况修改网卡名
virtual_router_id 51
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass K8SHA_KA
}
virtual_ipaddress {
10.0.0.100/24 # VIP
}
track_script {
check_haproxy
}
}
- **Master-2 (BACKUP角色)**:配置文件与Master-1类似,主要修改`state BACKUP`和`priority 90`。
**健康检查脚本** (`/etc/keepalived/check_haproxy.sh`):
```bash
#!/bin/bash
if ! pidof haproxy > /dev/null; then
systemctl restart haproxy
sleep 3
if ! pidof haproxy > /dev/null; then
exit 1
fi
fi
exit 0
赋予脚本执行权限并启动服务:
chmod +x /etc/keepalived/check_haproxy.sh
systemctl enable haproxy keepalived
systemctl start haproxy keepalived
# 验证VIP是否成功绑定
ip addr show | grep 10.0.0.100
2.3 初始化Kubernetes集群
准备kubeadm配置文件 (在Master-1上执行):
创建文件 kubeadm-config.yaml:
apiVersion: kubeadm.k8s.io/v1beta3
kind: ClusterConfiguration
kubernetesVersion: v1.28.0
controlPlaneEndpoint: "10.0.0.100:6443" # 指向VIP
imageRepository: registry.aliyuncs.com/google_containers # 国内镜像源
networking:
dnsDomain: cluster.local
serviceSubnet: "10.96.0.0/12"
podSubnet: "10.244.0.0/16" # 需与后续Calico配置匹配
etcd:
local:
dataDir: /var/lib/etcd
apiServer:
certSANs: # 证书包含所有可能的访问地址
- "10.0.0.100"
- "10.0.0.11"
- "10.0.0.12"
- "10.0.0.13"
- "k8s-api"
- "k8s-master-1"
- "k8s-master-2"
- "k8s-master-3"
extraArgs:
audit-log-path: /var/log/kubernetes/audit.log
audit-log-maxage: "30"
audit-log-maxbackup: "3"
audit-log-maxsize: "100"
controllerManager:
extraArgs:
bind-address: 0.0.0.0
scheduler:
extraArgs:
bind-address: 0.0.0.0
---
apiVersion: kubeadm.k8s.io/v1beta3
kind: InitConfiguration
localAPIEndpoint:
advertiseAddress: 10.0.0.11 # 当前Master节点的IP
bindPort: 6443
nodeRegistration:
criSocket: unix:///var/run/containerd/containerd.sock
taints:
- effect: NoSchedule
key: node-role.kubernetes.io/control-plane
初始化第一个Master节点:
# 预拉取所需镜像
kubeadm config images pull --config kubeadm-config.yaml
# 执行初始化
kubeadm init --config=kubeadm-config.yaml --upload-certs
初始化成功后,会输出用于加入集群的命令,请务必妥善保存。输出内容通常包含两种命令:一种用于加入其他控制平面节点(--control-plane),另一种用于加入工作节点。
配置kubectl访问:
mkdir -p $HOME/.kube
cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
chown $(id -u):$(id -g) $HOME/.kube/config
# 验证
kubectl get nodes
加入其他Master节点:
在Master-2和Master-3上,使用初始化输出中提供的--control-plane命令加入集群。加入后,同样需要复制admin.conf配置文件到本地。
加入Worker节点:
在所有Node节点上,使用初始化输出中提供的(不带--control-plane参数的)join命令加入集群。
2.4 安装网络插件
集群初始化后,节点状态为NotReady,因为尚未安装容器网络接口(CNI)插件。生产环境推荐使用Calico。
# 安装Calico
kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.26.1/manifests/calico.yaml
# 如果Pod子网与默认值(192.168.0.0/16)不同,需设置环境变量
# kubectl set env daemonset/calico-node -n kube-system CALICO_IPV4POOL_CIDR="10.244.0.0/16"
# 等待所有系统Pod就绪
kubectl get pods -n kube-system -w
# 验证节点状态
kubectl get nodes # 所有节点应变为Ready状态
2.5 集群功能验证
# 查看集群节点详情
kubectl get nodes -o wide
# 查看核心组件状态
kubectl get cs
# 查看kube-system命名空间下的Pod
kubectl get pods -n kube-system
# 运行一个测试Pod
kubectl run nginx --image=nginx:alpine
kubectl get pods
# 创建NodePort Service暴露测试Pod
kubectl expose pod nginx --port=80 --type=NodePort
kubectl get svc
# 通过 http://<任意Node的IP>:<NodePort> 访问测试页面
# 清理测试资源
kubectl delete pod nginx
kubectl delete svc nginx
三、关键配置与避坑指南
3.1 证书过期处理(坑1)
K8s集群证书默认有效期为1年,过期将导致集群不可用。
查看证书有效期:
kubeadm certs check-expiration
手动更新所有证书:
# 1. 备份现有证书
cp -r /etc/kubernetes/pki /etc/kubernetes/pki.bak
# 2. 更新证书
kubeadm certs renew all
# 3. 重启控制平面静态Pod(API Server等)
# 获取Pod ID并重启
crictl pods --namespace kube-system | grep -E 'kube-apiserver|kube-controller|kube-scheduler|etcd' | awk '{print $1}' | xargs crictl rmp -f
# 系统会自动重建Pod
# 4. 更新kubectl使用的配置文件
cp /etc/kubernetes/admin.conf $HOME/.kube/config
配置自动更新(推荐):
创建每月执行的定时任务 /etc/cron.monthly/k8s-cert-renew:
#!/bin/bash
kubeadm certs renew all
crictl pods --namespace kube-system | grep -E 'kube-apiserver|kube-controller|kube-scheduler' | awk '{print $1}' | xargs crictl rmp -f
记得赋予脚本执行权限。
3.2 etcd备份与恢复(坑2)
etcd存储了集群的所有状态数据,定期备份是保障集群安全的重中之重,属于必须建立的数据库备份实践。
备份脚本示例 (/opt/scripts/etcd_backup.sh):
#!/bin/bash
BACKUP_DIR="/data/etcd-backup"
DATE=$(date +%Y%m%d_%H%M%S)
ETCDCTL_API=3
mkdir -p ${BACKUP_DIR}
etcdctl snapshot save ${BACKUP_DIR}/etcd-snapshot-${DATE}.db \
--endpoints=https://127.0.0.1:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key
# 验证备份文件
etcdctl snapshot status ${BACKUP_DIR}/etcd-snapshot-${DATE}.db
# 清理旧备份,保留最近7天
find ${BACKUP_DIR} -name "*.db" -mtime +7 -delete
echo "Backup completed: ${BACKUP_DIR}/etcd-snapshot-${DATE}.db"
配置定时备份(每天凌晨2点):
0 2 * * * /opt/scripts/etcd_backup.sh >> /var/log/etcd-backup.log 2>&1
恢复流程(灾难恢复时):
# 1. 停止所有Master节点的kube-apiserver(通过移动manifest文件)
mv /etc/kubernetes/manifests/kube-apiserver.yaml /tmp/
# 2. 在其中一个Master节点执行恢复(以Master-1为例)
ETCDCTL_API=3 etcdctl snapshot restore /data/etcd-backup/etcd-snapshot-xxx.db \
--data-dir=/var/lib/etcd-restore \
--name=k8s-master-1 \
--initial-cluster="k8s-master-1=https://10.0.0.11:2380,k8s-master-2=https://10.0.0.12:2380,k8s-master-3=https://10.0.0.13:2380" \
--initial-advertise-peer-urls=https://10.0.0.11:2380
# 3. 替换etcd数据目录
mv /var/lib/etcd /var/lib/etcd.bak
mv /var/lib/etcd-restore /var/lib/etcd
# 在其他Master节点上重复此步骤
# 4. 恢复kube-apiserver
mv /tmp/kube-apiserver.yaml /etc/kubernetes/manifests/
3.3 集群健康检查脚本
创建一个综合检查脚本 (k8s_health_check.sh) 便于日常运维:
#!/bin/bash
echo "========================================"
echo "K8s集群健康检查 $(date)"
echo "========================================"
echo ""
echo "=== 节点状态 ==="
kubectl get nodes -o wide
echo ""
echo "=== 系统Pod状态 ==="
kubectl get pods -n kube-system -o wide | grep -v Running
echo ""
echo "=== 异常Pod ==="
kubectl get pods -A | grep -v Running | grep -v Completed
echo ""
echo "=== etcd健康状态 ==="
kubectl exec -n kube-system etcd-k8s-master-1 -- etcdctl \
--endpoints=https://127.0.0.1:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key \
endpoint health
echo ""
echo "=== 证书有效期 ==="
kubeadm certs check-expiration 2>/dev/null
echo ""
echo "=== 资源使用 ==="
kubectl top nodes 2>/dev/null || echo "需要安装metrics-server"
echo "========================================"
3.4 十大常见“坑”与解决方案总结
| 坑 |
问题描述 |
解决方案 |
| 1 |
证书1年过期,集群突然不可用 |
设置cron任务自动更新证书 |
| 2 |
etcd数据丢失,集群彻底崩溃 |
严格执行每日定时备份etcd |
| 3 |
Master单点故障导致业务中断 |
部署至少3节点的Master高可用集群 |
| 4 |
默认网络插件flannel性能不佳 |
生产环境改用Calico或Cilium |
| 5 |
网络插件Pod子网配置不匹配 |
确保kubeadm配置与CNI配置的podSubnet一致 |
| 6 |
containerd未配置SystemdCgroup |
修改containerd配置,设置SystemdCgroup = true |
| 7 |
未关闭swap,kubelet无法启动 |
在所有节点上永久关闭swap分区 |
| 8 |
国内环境无法拉取k8s.gcr.io镜像 |
使用阿里云、华为云等国内镜像仓库源 |
| 9 |
证书SAN列表不全,外部访问失败 |
初始化时在certSANs中列出所有IP和域名 |
| 10 |
跨大版本升级导致兼容性问题 |
遵循官方建议,逐次小版本升级,先测试后生产 |
四、生产环境最佳实践
4.1 节点规划建议
| 集群规模 |
Master节点 |
Worker节点 |
etcd部署模式 |
| 小型 (< 50 Pod) |
3 |
3+ |
内置(本文方案) |
| 中型 (50 - 500 Pod) |
3 |
10+ |
内置 |
| 大型 (> 500 Pod) |
3 |
50+ |
考虑外置独立集群(3-5节点) |
资源配置参考:
- Master节点:小型集群4C8G,中型8C16G,大型16C32G。
- Worker节点:通用型8C16G,计算密集型16C32G,内存密集型8C64G。
4.2 安全加固
# 1. 应用最小权限RBAC原则
kubectl create clusterrolebinding ops-admin \
--clusterrole=admin \
--user=ops@example.com
# 2. 实施默认拒绝的网络策略
cat <<EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: default
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
EOF
# 3. 启用Pod安全标准(Restricted模式)
kubectl label namespace default pod-security.kubernetes.io/enforce=restricted
4.3 版本升级注意事项
升级前务必备份etcd并查看官方发布说明。
# 查看可升级版本
yum list --showduplicates kubeadm
# 标准升级流程(先Master后Node)
# 1. 升级首个Master节点的kubeadm
yum install -y kubeadm-1.29.0
# 2. 查看升级计划
kubeadm upgrade plan
# 3. 执行升级
kubeadm upgrade apply v1.29.0
# 4. 升级其他Master节点
kubeadm upgrade node
# 5. 升级所有节点的kubelet和kubectl
yum install -y kubelet-1.29.0 kubectl-1.29.0
systemctl daemon-reload
systemctl restart kubelet
五、故障排查与监控
常用排查命令:
# 查看资源详情
kubectl describe node <node-name>
kubectl describe pod <pod-name>
# 查看日志
kubectl logs <pod-name> [-c <container-name>]
# 进入容器
kubectl exec -it <pod-name> -- /bin/sh
# 查看集群事件
kubectl get events --sort-by='.lastTimestamp'
# 查看kubelet日志
journalctl -u kubelet -f
安装基础监控metrics-server:
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/download/v0.6.4/components.yaml
# 如果节点证书不被认可,可编辑部署添加`--kubelet-insecure-tls`参数
# kubectl edit deployment metrics-server -n kube-system
# 验证
kubectl top nodes
kubectl top pods
六、总结
构建一个稳定的企业级Kubernetes集群,高可用是基石(3 Master + 负载均衡),etcd备份是生命线,证书管理不可忽视。网络插件选择(如Calico)、逐版本谨慎升级也是保障生产环境稳定的关键。
完成基础集群搭建后,可以进一步探索云原生生态,例如采用GitOps工作流(如ArgoCD)实现持续部署,或集成服务网格(如Istio)进行细粒度的流量管理。
| 运维端口参考: |
组件 |
端口 |
说明 |
| kube-apiserver |
6443 |
API Server |
| etcd |
2379/2380 |
客户端/对等通信 |
| kubelet |
10250 |
Kubelet API |
| kube-scheduler |
10259 |
Scheduler |
| kube-controller-manager |
10257 |
Controller Manager |
常用命令速查:
# 集群管理
kubeadm init
kubeadm join
kubeadm reset
kubeadm token create --print-join-command
# 资源操作
kubectl get nodes/pods/svc
kubectl describe <resource> <name>
kubectl logs <pod>
kubectl exec -it <pod> -- sh
# 运维调试
kubectl get events
journalctl -u kubelet
crictl ps
crictl logs <container-id>