Kubernetes 集群搭建实战:从 kubeadm 到生产级高可用部署 原创
Kubernetes 集群搭建实战:从 kubeadm 到生产级高可用部署
在云原生技术席卷全球的今天,Kubernetes 已经成为容器编排领域的事实标准。无论是初创公司还是大型企业,都在积极拥抱 Kubernetes 来管理其容器化应用。然而,对于很多运维人员和开发者来说,从零搭建一个生产可用的 Kubernetes 集群仍然是一项具有挑战性的任务。
本文将带你从零开始,使用 kubeadm 工具逐步搭建一个具备高可用控制平面的 Kubernetes 集群。我们会涵盖环境准备、kubeadm 初始化、控制平面高可用配置、Worker 节点加入、CNI 网络插件部署、Dashboard 安装以及 Ingress 控制器配置等核心环节。读完本文,你将拥有一个真正可用于生产环境的 Kubernetes 集群。
一、环境规划与准备工作
1.1 集群架构设计
生产级 Kubernetes 集群的核心要求是高可用。控制平面(Control Plane)作为集群的大脑,必须消除单点故障。我们采用以下架构:
- 控制平面节点(Master):3 台,组成高可用集群,通过 HAProxy + Keepalived 或云平台负载均衡器暴露统一的 API Server 端点
- Worker 节点:至少 2 台,运行实际的工作负载
- etcd 集群:与控制平面节点共存(堆叠模式),或独立部署(外部 etcd 模式)
- 负载均衡器:前置在控制平面之前,分发 API Server 请求
本文采用堆叠 etcd 模式(控制平面与 etcd 同节点),搭配 HAProxy 作为负载均衡器。这种方式在节点数量有限时最为高效。
1.2 节点规格要求
以下是推荐的节点配置:
| 角色 | 数量 | CPU | 内存 | 磁盘 | 操作系统 |
|---|---|---|---|---|---|
| 控制平面 | 3 | 4 核 | 8 GB | 50 GB SSD | Ubuntu 22.04 / Rocky Linux 9 |
| Worker | 2+ | 8 核 | 16 GB | 100 GB SSD | Ubuntu 22.04 / Rocky Linux 9 |
| 负载均衡器 | 1 | 2 核 | 4 GB | 20 GB | Ubuntu 22.04 |
本文示例使用 Ubuntu 22.04 LTS,所有节点均需配置静态 IP 和主机名解析。
1.3 网络规划
Kubernetes 集群涉及多个网络平面,需要提前规划:
- 节点网络:节点间互通,例如 192.168.1.0/24
- Pod 网络:Pod 使用的 CIDR,例如 10.244.0.0/16(Flannel 默认)或 10.100.0.0/16(Calico 默认)
- Service 网络:Service 使用的 CIDR,例如 10.96.0.0/12
- API Server 虚拟 IP:负载均衡器暴露的 VIP,例如 192.168.1.100
Pod 网络和 Service 网络不能与节点网络重叠,且两者之间也不能重叠。
1.4 主机名与 hosts 解析
在所有节点上编辑 /etc/hosts,添加以下内容:
192.168.1.10 k8s-master-1
192.168.1.11 k8s-master-2
192.168.1.12 k8s-master-3
192.168.1.20 k8s-worker-1
192.168.1.21 k8s-worker-2
192.168.1.100 k8s-api.vip
确保每个节点的主机名设置正确:
sudo hostnamectl set-hostname k8s-master-1 # 每个节点设置对应的名称
二、基础环境配置(所有节点)
2.1 系统优化与依赖安装
以下操作需要在所有节点(控制平面 + Worker)上执行。首先关闭 swap,Kubernetes 要求 swap 必须关闭:
sudo swapoff -a
sudo sed -i '/ swap / s/^/#/' /etc/fstab
加载必要的内核模块:
cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF
sudo modprobe overlay
sudo modprobe br_netfilter
配置内核参数,确保 iptables 能正确处理桥接流量:
cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
EOF
sudo sysctl --system
验证配置生效:
lsmod | grep br_netfilter
lsmod | grep overlay
sysctl net.bridge.bridge-nf-call-iptables
2.2 容器运行时安装(Containerd)
Kubernetes 从 1.24 版本开始移除了对 Docker 作为容器运行时的内置支持,推荐使用 containerd。我们安装最新稳定版:
# 安装依赖
sudo apt-get update
sudo apt-get install -y ca-certificates curl gnupg
# 添加 Docker 官方 GPG 密钥和仓库
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt-get install -y containerd.io
配置 containerd 使用 systemd cgroup 驱动,这是 Kubernetes 推荐的配置:
sudo mkdir -p /etc/containerd
containerd config default | sudo tee /etc/containerd/config.toml
# 修改配置,启用 systemd cgroup 驱动
sudo sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml
# 同时修改 sandbox 镜像地址为国内镜像(可选,加速拉取)
sudo sed -i 's|sandbox_image = "registry.k8s.io/pause:.*"|sandbox_image = "registry.aliyuncs.com/google_containers/pause:3.9"|' /etc/containerd/config.toml
sudo systemctl restart containerd
sudo systemctl enable containerd
验证 containerd 运行正常:
sudo ctr version
sudo systemctl status containerd
2.3 安装 kubeadm、kubelet 和 kubectl
在所有节点上安装 Kubernetes 组件。这里使用阿里云镜像源加速国内下载:
# 添加 Kubernetes apt 仓库
sudo apt-get install -y apt-transport-https ca-certificates curl
curl -fsSL https://mirrors.aliyun.com/kubernetes-new/core/stable/v1.30/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
echo "deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://mirrors.aliyun.com/kubernetes-new/core/stable/v1.30/deb/ /" | sudo tee /etc/apt/sources.list.d/kubernetes.list
sudo apt-get update
sudo apt-get install -y kubelet kubeadm kubectl
sudo apt-mark hold kubelet kubeadm kubectl
apt-mark hold 可以防止这些组件被意外升级,因为 Kubernetes 版本升级需要谨慎规划。
验证安装:
kubeadm version
kubelet --version
kubectl version --client
三、配置负载均衡器
在负载均衡器节点(本文使用 k8s-master-1 兼任,生产环境建议独立部署)上安装 HAProxy:
sudo apt-get install -y haproxy
编辑 HAProxy 配置文件 /etc/haproxy/haproxy.cfg:
global
log /dev/log local0
log /dev/log local1 notice
chroot /var/lib/haproxy
stats socket /run/haproxy/admin.sock mode 660 level admin expose-fd listeners
stats timeout 30s
user haproxy
group haproxy
daemon
defaults
log global
mode tcp
option tcplog
option dontlognull
timeout connect 5000
timeout client 50000
timeout server 50000
frontend k8s-api
bind 0.0.0.0:6443
mode tcp
option tcplog
default_backend k8s-masters
backend k8s-masters
mode tcp
balance roundrobin
option tcp-check
server master-1 192.168.1.10:6443 check fall 3 rise 2
server master-2 192.168.1.11:6443 check fall 3 rise 2
server master-3 192.168.1.12:6443 check fall 3 rise 2
重启 HAProxy 并验证:
sudo systemctl restart haproxy
sudo systemctl enable haproxy
sudo systemctl status haproxy
验证负载均衡器正常工作:
curl -k https://192.168.1.100:6443/version
如果返回 JSON 格式的版本信息,说明负载均衡器配置成功。
四、初始化第一个控制平面节点
在第一个控制平面节点(k8s-master-1)上执行初始化。首先创建一个 kubeadm 配置文件:
cat <<EOF | sudo tee /etc/kubernetes/kubeadm-config.yaml
apiVersion: kubeadm.k8s.io/v1beta3
kind: InitConfiguration
localAPIEndpoint:
advertiseAddress: "192.168.1.10"
bindPort: 6443
nodeRegistration:
criSocket: unix:///var/run/containerd/containerd.sock
name: k8s-master-1
---
apiVersion: kubeadm.k8s.io/v1beta3
kind: ClusterConfiguration
kubernetesVersion: "v1.30.0"
controlPlaneEndpoint: "192.168.1.100:6443"
apiServer:
certSANs:
- "192.168.1.100"
- "k8s-api.vip"
- "127.0.0.1"
controllerManager:
extraArgs:
bind-address: "0.0.0.0"
scheduler:
extraArgs:
bind-address: "0.0.0.0"
networking:
serviceSubnet: "10.96.0.0/12"
podSubnet: "10.100.0.0/16"
dnsDomain: "cluster.local"
---
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
cgroupDriver: systemd
EOF
配置要点说明:
- controlPlaneEndpoint:指向负载均衡器的 VIP 和端口,这是高可用的关键
- certSANs:将 VIP 和域名加入 API Server 证书的 SAN 列表,确保通过 VIP 访问时证书验证通过
- podSubnet:根据后续 CNI 插件的要求设置,Calico 默认使用 10.100.0.0/16
- cgroupDriver:与 containerd 配置保持一致,使用 systemd
执行初始化:
sudo kubeadm init --config=/etc/kubernetes/kubeadm-config.yaml --upload-certs
--upload-certs 参数会将控制平面证书上传到集群,方便后续控制平面节点加入时自动获取。
初始化成功后,会输出类似以下的重要信息:
Your Kubernetes control-plane has been initialized successfully!
To start using your cluster, you need to run the following as a regular user:
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
You can now join any number of control-plane nodes by running the following command on each as root:
kubeadm join 192.168.1.100:6443 --token xxxxxx \
--discovery-token-ca-cert-hash sha256:xxxxxx \
--control-plane --certificate-key xxxxxx
Please note that the certificate-key gives access to cluster sensitive data, keep it secret!
Then you can join any number of worker nodes by running the following on each as root:
kubeadm join 192.168.1.100:6443 --token xxxxxx \
--discovery-token-ca-cert-hash sha256:xxxxxx
务必保存输出的 token、ca-cert-hash 和 certificate-key,后续加入节点时需要用到。
配置 kubectl 访问集群:
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
# 验证集群状态
kubectl get nodes
kubectl get pods -n kube-system
此时节点状态应为 NotReady,因为还没有安装 CNI 网络插件。这是正常现象。
五、加入其余控制平面节点
在 k8s-master-2 和 k8s-master-3 上执行加入命令。如果 token 过期,可以在第一个控制平面节点上重新生成:
# 重新生成 token(如果过期)
kubeadm token create
# 获取 CA 证书哈希
openssl x509 -pubkey -in /etc/kubernetes/pki/ca.crt | openssl rsa -pubin -outform der 2>/dev/null | openssl dgst -sha256 -hex | sed 's/^.* //'
# 获取证书密钥(用于控制平面节点加入)
sudo kubeadm init phase upload-certs --upload-certs
在 k8s-master-2 上执行:
sudo kubeadm join 192.168.1.100:6443 --token <token> \
--discovery-token-ca-cert-hash sha256:<hash> \
--control-plane --certificate-key <key>
在 k8s-master-3 上同样执行上述命令。加入成功后,在第一个控制平面节点上验证:
kubectl get nodes
应该看到三个 master 节点,状态均为 NotReady(等待 CNI)。
验证 etcd 集群状态:
kubectl get pods -n kube-system | grep etcd
三个 etcd 实例都应该正常运行,形成 etcd 集群。
六、安装 CNI 网络插件
CNI(Container Network Interface)插件是 Kubernetes 集群网络的基石。我们选择 Calico,它是目前生产环境中最广泛使用的 CNI 插件之一,支持网络策略、BGP 路由和丰富的安全功能。
6.1 安装 Calico
在第一个控制平面节点上执行:
# 下载 Calico 清单文件
curl -O https://raw.githubusercontent.com/projectcalico/calico/v3.28.0/manifests/calico.yaml
# 如果 Pod CIDR 不是默认的 192.168.0.0/16,需要修改配置
# 编辑 calico.yaml,找到 CALICO_IPV4POOL_CIDR 环境变量,设置为我们规划的 Pod 网络
# - name: CALICO_IPV4POOL_CIDR
# value: "10.100.0.0/16"
# 应用 Calico
kubectl apply -f calico.yaml
等待 Calico 组件启动:
kubectl get pods -n kube-system -w | grep calico
所有 calico-node pod 都进入 Running 状态后,检查节点状态:
kubectl get nodes
此时所有节点应该变为 Ready 状态。
6.2 验证网络通信
创建一个测试 Pod 验证跨节点通信:
kubectl run test-pod --image=busybox -- sleep 3600
kubectl exec test-pod -- ping 10.100.0.1 # 测试到集群网络的连通性
再验证 DNS 解析:
kubectl exec test-pod -- nslookup kubernetes.default.svc.cluster.local
如果 DNS 解析正常,说明 CoreDNS 和网络插件协同工作良好。
七、加入 Worker 节点
在 Worker 节点上执行加入命令。如果 token 已过期,重新生成:
# 在控制平面节点上
kubeadm token create --print-join-command
在 k8s-worker-1 上执行:
sudo kubeadm join 192.168.1.100:6443 --token <token> \
--discovery-token-ca-cert-hash sha256:<hash>
在 k8s-worker-2 上同样执行。加入后在控制平面节点验证:
kubectl get nodes -o wide
所有节点应该都处于 Ready 状态,并显示各自的 IP 地址和 Kubernetes 版本。
查看节点角色标签:
kubectl get nodes --show-labels
Worker 节点默认没有 node-role.kubernetes.io/control-plane 标签,但也没有 node-role.kubernetes.io/worker 标签。可以手动添加:
kubectl label node k8s-worker-1 node-role.kubernetes.io/worker=worker
kubectl label node k8s-worker-2 node-role.kubernetes.io/worker=worker
八、部署 Kubernetes Dashboard
Dashboard 是 Kubernetes 的官方 Web UI,方便可视化管理和监控集群。
8.1 安装 Dashboard
# 安装最新版 Dashboard
kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v3.0.0/charts/kubernetes-dashboard.yaml
验证 Dashboard Pod 运行:
kubectl get pods -n kubernetes-dashboard
kubectl get svc -n kubernetes-dashboard
8.2 创建管理员用户
创建一个具有集群管理员权限的 ServiceAccount 和 ClusterRoleBinding:
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: ServiceAccount
metadata:
name: admin-user
namespace: kubernetes-dashboard
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: admin-user
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- kind: ServiceAccount
name: admin-user
namespace: kubernetes-dashboard
EOF
获取登录 Token:
kubectl -n kubernetes-dashboard create token admin-user
8.3 访问 Dashboard
默认情况下 Dashboard Service 类型为 ClusterIP,可以通过 kubectl proxy 访问:
kubectl proxy
然后访问:http://localhost:8001/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/
也可以将 Service 类型改为 NodePort 以便外部访问:
kubectl patch svc kubernetes-dashboard -n kubernetes-dashboard -p '{"spec":{"type":"NodePort"}}'
kubectl get svc -n kubernetes-dashboard
获取 NodePort 端口后,通过 https://<任意节点IP>:<NodePort> 访问,使用上面生成的 Token 登录。
九、配置 Ingress 控制器
Ingress 是 Kubernetes 中管理外部访问集群服务的 API 对象,而 Ingress Controller 是实现这一功能的实际组件。我们选择 NGINX Ingress Controller,它是社区最成熟的选择。
9.1 安装 NGINX Ingress Controller
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.10.0/deploy/static/provider/baremetal/deploy.yaml
验证安装:
kubectl get pods -n ingress-nginx
kubectl get svc -n ingress-nginx
9.2 配置 Ingress 示例
创建一个示例应用来验证 Ingress 功能:
# 创建示例 Deployment 和 Service
cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: demo-app
spec:
replicas: 3
selector:
matchLabels:
app: demo
template:
metadata:
labels:
app: demo
spec:
containers:
- name: nginx
image: nginx:alpine
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: demo-svc
spec:
selector:
app: demo
ports:
- port: 80
targetPort: 80
EOF
创建 Ingress 规则:
cat <<EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: demo-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
ingressClassName: nginx
rules:
- host: demo.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: demo-svc
port:
number: 80
EOF
测试 Ingress 访问:
# 获取 Ingress Controller 的 NodePort
kubectl get svc -n ingress-nginx
# 使用 curl 测试(替换 NodePort)
curl -H "Host: demo.example.com" http://<节点IP>:<NodePort>
如果返回 nginx 欢迎页面,说明 Ingress 配置成功。
9.3 生产环境 Ingress 最佳实践
在生产环境中,建议对 Ingress Controller 做以下优化:
- 使用 LoadBalancer 类型:如果集群运行在云平台,将 Service 类型改为 LoadBalancer 以获取云负载均衡器
- 配置 HPA:为 Ingress Controller Pod 配置水平自动伸缩
- 调整资源限制:根据流量规模设置合适的 CPU/内存 requests 和 limits
- 启用 SSL 终止:配置 TLS 证书,实现 HTTPS 访问
- 开启监控:启用 Prometheus 指标暴露,对接监控系统
十、集群验证与生产化配置
10.1 核心功能验证
完成上述步骤后,执行全面的集群功能验证:
# 1. 节点状态
kubectl get nodes -o wide
# 2. 核心组件状态
kubectl get pods -n kube-system -o wide
# 3. 存储类(如果配置了)
kubectl get storageclass
# 4. API Server 高可用验证
# 分别停止各个 master 节点的 kubelet,观察集群是否仍然可用
# 注意:不要在验证期间在生产环境操作
10.2 节点污点管理
默认情况下,控制平面节点带有 node-role.kubernetes.io/control-plane:NoSchedule 污点,阻止普通 Pod 调度到控制平面节点上。如果 Worker 节点资源紧张,可以移除污点允许控制平面节点运行工作负载(不推荐生产环境这样做):
kubectl taint nodes --all node-role.kubernetes.io/control-plane-
10.3 配置自动补全与别名
为 kubectl 配置自动补全和常用别名,大幅提升操作效率:
# 在控制平面节点上
echo 'source <(kubectl completion bash)' >> ~/.bashrc
echo 'alias k=kubectl' >> ~/.bashrc
echo 'complete -o default -F __start_kubectl k' >> ~/.bashrc
source ~/.bashrc
10.4 节点资源预留
生产环境中,需要为系统守护进程和 kubelet 预留资源,避免节点资源被 Pod 完全占用导致系统不稳定。在 kubelet 配置中设置:
# 编辑 /var/lib/kubelet/config.yaml
# 添加以下配置
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
...
systemReserved:
cpu: "500m"
memory: "1Gi"
kubeReserved:
cpu: "500m"
memory: "1Gi"
evictionHard:
memory.available: "500Mi"
nodefs.available: "10%"
重启 kubelet 使配置生效:
sudo systemctl restart kubelet
十一、常见问题与排错指南
11.1 节点 NotReady
最常见的原因是 CNI 网络插件未安装或配置错误。排查步骤:
# 查看节点详情
kubectl describe node <node-name>
# 查看 kubelet 日志
sudo journalctl -u kubelet -f
# 检查 containerd 状态
sudo ctr namespace ls
sudo ctr -n k8s.io container ls
11.2 证书问题
Kubernetes 证书默认有效期为一年。可以使用 kubeadm certs check-expiration 检查证书过期时间:
sudo kubeadm certs check-expiration
证书更新:
sudo kubeadm certs renew all
sudo systemctl restart kubelet
11.3 Pod 处于 Pending 状态
通常是因为资源不足或节点有污点:
kubectl describe pod <pod-name>
kubectl get events --sort-by='.lastTimestamp'
11.4 CoreDNS 解析失败
检查 CoreDNS Pod 日志和配置:
kubectl logs -n kube-system -l k8s-app=kube-dns
kubectl get configmap coredns -n kube-system -o yaml
十二、总结与后续规划
至此,我们已经使用 kubeadm 从零搭建了一个具备生产级高可用能力的 Kubernetes 集群。回顾整个搭建过程,我们完成了以下关键步骤:
- 架构设计:规划了三节点控制平面高可用架构,前置 HAProxy 负载均衡器
- 基础环境:在所有节点上完成了系统优化、容器运行时安装和 Kubernetes 组件安装
- 集群初始化:使用 kubeadm 配置文件精确控制集群参数,完成第一个控制平面节点初始化
- 高可用配置:成功加入其余控制平面节点,形成 etcd 集群
- 网络部署:安装 Calico CNI 插件,实现 Pod 跨节点通信
- 节点扩展:Worker 节点顺利加入集群,承载工作负载
- 管理工具:部署 Dashboard 提供可视化管理和监控
- 流量入口:配置 NGINX Ingress Controller,实现外部流量接入
这个集群已经具备了生产运行的基本能力,但要真正投入生产,还需要考虑以下方面:
- 持久化存储:部署 Rook/Ceph、Longhorn 或使用云平台存储类,为有状态应用提供持久化存储
- 监控告警:部署 Prometheus + Grafana 监控栈,配置告警规则
- 日志收集:部署 Loki 或 Elasticsearch + Fluentd + Kibana(EFK)日志收集系统
- CI/CD 集成:对接 GitLab CI、Jenkins 或 ArgoCD 实现应用持续交付
- 安全加固:配置 RBAC 权限、网络策略、Pod 安全策略、镜像扫描
- 备份恢复:定期备份 etcd 数据,制定灾难恢复预案
- 集群升级:制定 Kubernetes 版本升级策略,保持集群版本在支持周期内
Kubernetes 的学习曲线虽然陡峭,但一旦掌握了核心概念和搭建流程,你会发现它带来的部署效率、资源利用率和运维自动化程度是传统方式无法比拟的。希望本文能帮助你在 Kubernetes 的实践之路上迈出坚实的第一步。
如果你在搭建过程中遇到任何问题,欢迎在评论区留言讨论。后续我们还会推出 Kubernetes 运维实战、应用迁移、服务网格等系列文章,敬请期待。