一、概述
1.1 背景介绍
Kubernetes已经成为容器编排的事实标准,但搭建一个生产可用的K8s集群并不简单。
我第一次搭建K8s集群的时候,照着官方文档用kubeadm init,看起来很顺利,结果上线后各种问题:证书过期集群挂了、etcd数据丢了恢复不了、网络插件选错了性能很差、master单点导致业务中断...
后来陆陆续续踩了无数坑,才算搞明白企业级K8s集群应该怎么搭。这篇文章就是把这些经验整理出来,让你少走弯路。
1.2 技术特点
企业级K8s集群的要求:
-
• 高可用:Master节点多副本,任何单点故障不影响集群
-
• 可扩展:能方便地添加Node节点
-
• 安全:RBAC权限控制、网络隔离、镜像安全扫描
-
• 可观测:完善的监控、日志、告警
-
• 灾备:etcd备份恢复、多集群容灾
架构设计:
┌─────────────────┐
│ LoadBalancer │
│ (VIP: 10.0.0.100)│
└────────┬────────┘
│
┌───────────────────────┼───────────────────────┐
│ │ │
┌────▼────┐ ┌────▼────┐ ┌────▼────┐
│ Master-1 │ │ Master-2 │ │ Master-3 │
│ API+ETCD │ │ API+ETCD │ │ API+ETCD │
└─────────┘ └─────────┘ └─────────┘
┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐
│ Node-1 │ │ Node-2 │ │ Node-3 │ │ Node-N │
│ Kubelet │ │ Kubelet │ │ Kubelet │ │ Kubelet │
│ CNI │ │ CNI │ │ CNI │ │ CNI │
└─────────┘ └─────────┘ └─────────┘ └─────────┘
1.3 适用场景
-
• 场景一:从零搭建生产环境K8s集群
-
• 场景二:将测试集群升级为生产级别
-
• 场景三:学习K8s集群架构原理
1.4 环境要求
| 组件 | 版本要求 | 说明 |
|---|---|---|
| 操作系统 | CentOS 7.9 / Ubuntu 20.04+ | 推荐Ubuntu |
| Kubernetes | 1.28+ | 本文使用1.28 |
| Container Runtime | containerd 1.7+ | 不再使用Docker |
| 网络插件 | Calico 3.26+ | 推荐Calico |
| 服务器配置 | 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 | 负载均衡 |
二、详细步骤
2.1 准备工作
以下操作在所有节点执行。
◆ 2.1.1 系统配置
# 设置主机名(每台机器执行对应的)
hostnamectl set-hostname k8s-master-1
# 配置hosts
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
# 关闭防火墙(生产环境建议配置规则而非关闭)
systemctl stop firewalld
systemctl disable firewalld
# 关闭SELinux
setenforce 0
sed -i 's/^SELINUX=enforcing$/SELINUX=disabled/' /etc/selinux/config
# 关闭swap(K8s要求)
swapoff -a
sed -i '/ swap / s/^\(.*\)$/#\1/g' /etc/fstab
# 确认关闭
free -h # Swap应该显示0
◆ 2.1.2 内核参数
# 加载必要模块
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
◆ 2.1.3 安装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
◆ 2.1.4 安装kubeadm、kubelet、kubectl
# 添加阿里云K8s仓库
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
# 安装指定版本
yum install -y kubelet-1.28.0 kubeadm-1.28.0 kubectl-1.28.0
# 设置开机启动(先不启动)
systemctl enable kubelet
2.2 高可用负载均衡
在初始化集群之前,需要先配置API Server的高可用。
◆ 2.2.1 方案选择
| 方案 | 优点 | 缺点 |
|---|---|---|
| HAProxy + Keepalived | 成熟稳定,自建可控 | 需要额外维护 |
| 云厂商SLB | 省心,高可用 | 依赖云厂商 |
| kube-vip | K8s原生,轻量 | 相对较新 |
这里用HAProxy + Keepalived方案,在Master-1和Master-2上部署:
# 安装
yum install -y haproxy keepalived
◆ 2.2.2 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
◆ 2.2.3 Keepalived配置
Master-1(MASTER):
# /etc/keepalived/keepalived.conf
global_defs {
router_id LVS_K8S
}
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
#!/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 初始化集群
◆ 2.3.1 准备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
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
◆ 2.3.2 初始化第一个Master
# 预拉取镜像
kubeadm config images pull --config kubeadm-config.yaml
# 初始化
kubeadm init --config=kubeadm-config.yaml --upload-certs
# 记住输出的join命令!类似:
# 加入Master:
# kubeadm join 10.0.0.100:6443 --token xxx --discovery-token-ca-cert-hash sha256:xxx --control-plane --certificate-key xxx
# 加入Node:
# kubeadm join 10.0.0.100:6443 --token xxx --discovery-token-ca-cert-hash sha256:xxx
# 配置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
◆ 2.3.3 加入其他Master节点
在Master-2和Master-3上执行:
kubeadm join 10.0.0.100:6443 \
--token <token> \
--discovery-token-ca-cert-hash sha256:<hash> \
--control-plane \
--certificate-key <certificate-key>
配置kubectl:
mkdir -p $HOME/.kube
cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
◆ 2.3.4 加入Worker节点
在所有Node上执行:
kubeadm join 10.0.0.100:6443 \
--token <token> \
--discovery-token-ca-cert-hash sha256:<hash>
2.4 安装网络插件
集群初始化后节点是NotReady状态,需要安装CNI网络插件:
# 安装Calico
kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.26.1/manifests/calico.yaml
# 如果使用其他Pod CIDR,需要修改
kubectl setenv 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 # 可能显示Unhealthy,这是已知问题,不影响使用
# 查看系统Pod
kubectl get pods -n kube-system
# 创建测试Pod
kubectl run nginx --image=nginx:alpine
kubectl get pods
# 测试服务
kubectl expose pod nginx --port=80 --type=NodePort
kubectl get svc
# 访问 http://<NodeIP>:<NodePort>
# 清理测试
kubectl delete pod nginx
kubectl delete svc nginx
三、示例代码和配置
3.1 证书过期处理(坑1)
K8s证书默认1年过期,过期后集群不可用。
查看证书有效期:
kubeadm certs check-expiration
更新证书:
# 备份旧证书
cp -r /etc/kubernetes/pki /etc/kubernetes/pki.bak
# 更新所有证书
kubeadm certs renew all
# 重启控制平面组件
crictl pods --namespace kube-system | grep -E 'kube-apiserver|kube-controller|kube-scheduler|etcd' | awk '{print $1}' | xargs crictl rmp -f
# 更新kubeconfig
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存储了所有集群数据,必须定期备份。
备份脚本:
#!/bin/bash
# /opt/scripts/etcd_backup.sh
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
mv /etc/kubernetes/manifests/kube-apiserver.yaml /tmp/
# 2. 在所有Master执行恢复
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. 替换数据目录
mv /var/lib/etcd /var/lib/etcd.bak
mv /var/lib/etcd-restore /var/lib/etcd
# 4. 恢复kube-apiserver
mv /tmp/kube-apiserver.yaml /etc/kubernetes/manifests/
3.3 常见问题排查脚本
#!/bin/bash
# k8s_health_check.sh
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 我踩过的10个大坑
| 坑 | 问题描述 | 解决方案 |
|---|---|---|
| 1 | 证书1年过期,集群挂了 | 设置自动更新cron任务 |
| 2 | etcd数据丢失,集群完蛋 | 每天备份etcd |
| 3 | Master单点故障 | 3节点高可用 |
| 4 | flannel性能差 | 改用Calico |
| 5 | 网络插件装错,Pod无法通信 | 确认podSubnet匹配 |
| 6 | containerd没配SystemdCgroup | 节点NotReady |
| 7 | swap没关 | kubelet起不来 |
| 8 | 国内拉不到镜像 | 改用阿里云镜像源 |
| 9 | 证书SAN没加全 | 初始化时配置certSANs |
| 10 | 升级版本跨度太大 | 逐版本升级 |
四、最佳实践和注意事项
4.1 最佳实践
◆ 4.1.1 节点规划
| 集群规模 | Master节点 | Worker节点 | etcd |
|---|---|---|---|
| 小型(<50 Pod) | 3 | 3+ | 内置 |
| 中型(50-500 Pod) | 3 | 10+ | 内置 |
| 大型(>500 Pod) | 3 | 50+ | 外置3-5节点 |
◆ 4.1.2 资源配置建议
Master节点:
小型集群:4C8G
中型集群:8C16G
大型集群:16C32G
Worker节点:
通用型:8C16G
计算密集:16C32G
内存密集:8C64G
◆ 4.1.3 安全加固
# 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安全标准
kubectl label namespace default pod-security.kubernetes.io/enforce=restricted
4.2 注意事项
◆ 4.2.1 版本升级
# 查看可用版本
yum list --showduplicates kubeadm
# 升级步骤(先Master后Node)
# 1. 升级kubeadm
yum install -y kubeadm-1.29.0
# 2. 验证升级计划
kubeadm upgrade plan
# 3. 执行升级(第一个Master)
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
◆ 4.2.2 常见错误
| 错误 | 原因 | 解决 |
|---|---|---|
| NodeNotReady | CNI未安装或kubelet问题 | 检查kubelet日志 |
| ImagePullBackOff | 镜像拉取失败 | 检查镜像地址和网络 |
| CrashLoopBackOff | 容器启动失败 | 查看Pod日志 |
| Pending | 资源不足或调度问题 | describe pod查看Events |
五、故障排查和监控
5.1 常用排查命令
# 查看节点详情
kubectl describe node <node-name>
# 查看Pod详情
kubectl describe pod <pod-name>
# 查看Pod日志
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
5.2 安装监控
# 安装metrics-server
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/download/v0.6.4/components.yaml
# 修改配置(如果证书有问题)
kubectl edit deployment metrics-server -n kube-system
# 添加 --kubelet-insecure-tls
# 验证
kubectl top nodes
kubectl top pods
六、总结
6.1 技术要点回顾
-
• 高可用是基础:3 Master + VIP负载均衡
-
• etcd备份是保险:每天备份,定期验证
-
• 证书管理要重视:设置自动更新
-
• 网络插件要选对:生产推荐Calico
-
• 升级要谨慎:逐版本升级,先测试后生产
6.2 进阶学习方向
-
- GitOps:ArgoCD/Flux持续部署
-
- 服务网格:Istio流量管理
-
- 多集群:Federation/Liqo
-
- Operator开发:自定义资源管理
6.3 参考资料
-
• Kubernetes官方文档
-
• kubeadm文档
-
• Calico文档
附录
A. 命令速查表
# 集群管理
kubeadm init # 初始化集群
kubeadm join# 加入集群
kubeadm reset # 重置节点
kubeadm token create --print-join-command # 生成join命令
# kubectl常用
kubectl get nodes # 查看节点
kubectl get pods -A # 查看所有Pod
kubectl describe pod <name> # Pod详情
kubectl logs <pod> # 查看日志
kubectl exec -it <pod> -- sh # 进入容器
# 故障排查
kubectl get events # 查看事件
journalctl -u kubelet # kubelet日志
crictl ps # 查看容器
crictl logs <container-id> # 容器日志
B. 端口列表
| 组件 | 端口 | 说明 |
|---|---|---|
| kube-apiserver | 6443 | API Server |
| etcd | 2379/2380 | etcd client/peer |
| kubelet | 10250 | Kubelet API |
| kube-scheduler | 10259 | Scheduler |
| kube-controller | 10257 | Controller Manager |
C. 术语表
| 术语 | 说明 |
|---|---|
| Master | 控制平面节点 |
| Node/Worker | 工作节点 |
| etcd | 分布式键值存储 |
| CNI | 容器网络接口 |
| Pod | 最小调度单元 |
| Service | 服务抽象 |
| Deployment | 无状态应用管理 |