Kubernetes 集群搭建实战:从 kubeadm 到生产级高可用部署 原创

温馨提示:
本文最后更新于 2026-07-02,已超过 70 天没有更新。 若文章内的图片失效(无法正常加载),请留言反馈或直接 联系我

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 集群。回顾整个搭建过程,我们完成了以下关键步骤:

  1. 架构设计:规划了三节点控制平面高可用架构,前置 HAProxy 负载均衡器
  2. 基础环境:在所有节点上完成了系统优化、容器运行时安装和 Kubernetes 组件安装
  3. 集群初始化:使用 kubeadm 配置文件精确控制集群参数,完成第一个控制平面节点初始化
  4. 高可用配置:成功加入其余控制平面节点,形成 etcd 集群
  5. 网络部署:安装 Calico CNI 插件,实现 Pod 跨节点通信
  6. 节点扩展:Worker 节点顺利加入集群,承载工作负载
  7. 管理工具:部署 Dashboard 提供可视化管理和监控
  8. 流量入口:配置 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 运维实战、应用迁移、服务网格等系列文章,敬请期待。