从零搭建企业级K8s集群

一、概述

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 进阶学习方向

    1. GitOps:ArgoCD/Flux持续部署
    1. 服务网格:Istio流量管理
    1. 多集群:Federation/Liqo
    1. 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 无状态应用管理
相关推荐
Elastic 中国社区官方博客4 小时前
使用 Elastic Cloud Serverless 扩展批量索引
大数据·运维·数据库·elasticsearch·搜索引擎·云原生·serverless
超龄超能程序猿4 小时前
Docker GPU插件(NVIDIA Container Toolkit)安装
运维·docker·容器
岳来5 小时前
docker 从 Path 值看容器启动命令
运维·docker·容器
南宫乘风5 小时前
Kubernetes 本地存储实战:Open-Local 从部署到生产级应用
云原生·容器·kubernetes
IT利刃出鞘8 小时前
Docker Compose--解决容器时间不正确的问题
运维·docker·容器
eight *11 小时前
docker部署elk+filebeat日志收集分析系统
elk·docker·容器
小股虫12 小时前
分布式事务:在增长中台,我们如何做到“发出去的内容”和“记录的数据”不打架?
分布式·微服务·云原生·架构·团队建设·方法论
自己的九又四分之三站台12 小时前
docker安装pgvector、age和postgis
运维·docker·容器
忧郁蓝调2613 小时前
Redis不停机数据迁移:基于 redis-shake 的跨实例 / 跨集群同步方案
运维·数据库·redis·阿里云·缓存·云原生·paas
java1234_小锋13 小时前
ZooKeeper集群中服务器之间是怎样通信的?
分布式·zookeeper·云原生