【Kubernetes02】—— 使用 kubeadm 从零搭建 K8s 集群(实操避坑版)

【Kubernetes】------ 使用 kubeadm 从零搭建 K8s 集群(实操避坑版)

上一篇讲了 K8s 的核心原理,这篇直接上手------用 kubeadm 搭一个真正能跑的集群。

不是 Minikube 那种单节点玩具,是一个 1 Master + 2 Worker 的三节点集群,装完就能部署应用。踩过的坑我都标出来了,照着做基本不会翻车。


一、环境说明与前置条件

本文以 Ubuntu 22.04 LTS 为例。如果你用 CentOS/RHEL,大部分步骤一样,但包管理命令和防火墙配置需要自己调整。

组件 版本 用途 备注
操作系统 Ubuntu 22.04 LTS 三台虚拟机 至少 2 核 CPU / 2G 内存
Kubernetes 1.28+ 集群版本 本文以 1.28 为例
containerd 1.7+ 容器运行时 K8s 1.24 起不再直接支持 Docker
Calico 3.26+ CNI 网络插件 生产环境推荐

节点规划:

主机名 IP 角色 配置
k8s-master 192.168.1.100 Control Plane 2C/2G
k8s-node1 192.168.1.101 Worker Node 2C/2G
k8s-node2 192.168.1.102 Worker Node 2C/2G

⚠️ 注意: 以下所有步骤,除非特别说明,三台机器都要执行


二、系统基础配置

Step 1:关闭 swap 和配置主机名

K8s 要求关闭 swap,原因很简单:kubelet 需要精确控制内存分配,swap 会干扰它对节点资源的判断。

bash 复制代码
# 关闭 swap(临时)
sudo swapoff -a

# 永久关闭:注释掉 /etc/fstab 中的 swap 行
sudo sed -i '/ swap / s/^\(.*\)$/#\1/g' /etc/fstab

设置主机名(每台机器分别执行):

bash 复制代码
# 在 master 上执行
sudo hostnamectl set-hostname k8s-master

# 在 node1 上执行
sudo hostnamectl set-hostname k8s-node1

# 在 node2 上执行
sudo hostnamectl set-hostname k8s-node2

配置 hosts 文件,三台机器都加:

bash 复制代码
cat >> /etc/hosts << EOF
192.168.1.100 k8s-master
192.168.1.101 k8s-node1
192.168.1.102 k8s-node2
EOF

成功标志: 执行 free -h 看不到 swap 分区,hostname 返回正确的主机名。

Step 2:加载内核模块和配置网络参数

这一步很容易忘,但漏了会导致后面 Pod 网络出问题。

bash 复制代码
# 加载必需的内核模块
cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF

sudo modprobe overlay
sudo modprobe br_netfilter

# 配置网络参数
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 有输出,sysctl net.bridge.bridge-nf-call-iptables 返回 1。


三、安装 containerd

K8s 1.24 起移除了对 dockershim 的支持。别纠结,直接用 containerd,它本来就是 Docker 底层的运行时。

bash 复制代码
# 安装依赖
sudo apt-get update
sudo apt-get install -y ca-certificates curl gnupg

# 添加 Docker 官方 GPG 密钥(containerd 包在 Docker 仓库里)
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

# 添加 Docker 仓库
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

# 安装 containerd
sudo apt-get update
sudo apt-get install -y containerd.io

配置 containerd 使用 systemd 作为 cgroup driver(这一步是关键):

bash 复制代码
# 生成默认配置
sudo containerd config default | sudo tee /etc/containerd/config.toml > /dev/null

# 修改 SystemdCgroup = true
sudo sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml

# 重启 containerd
sudo systemctl restart containerd
sudo systemctl enable containerd

🔴 重点: SystemdCgroup = true 这一行必须改。不改的话,kubelet 和 containerd 的 cgroup 管理方式不一致,Pod 会莫名其妙 OOMKilled。别问我怎么知道的。

成功标志: sudo systemctl status containerd 显示 active (running)。


四、安装 kubeadm、kubelet、kubectl

bash 复制代码
# 添加 Kubernetes apt 仓库
sudo apt-get update
sudo apt-get install -y apt-transport-https ca-certificates curl gpg

curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.28/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://pkgs.k8s.io/core:/stable:/v1.28/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 很重要。生产环境里 K8s 组件自动升级可能导致集群不可用。我们团队之前踩过这个坑------周末 kubelet 自动升级了小版本,结果跟 API Server 版本不兼容,周一来了集群就挂了。

成功标志: kubeadm version 返回版本号。


五、初始化 Control Plane

只在 master 节点 执行:

bash 复制代码
sudo kubeadm init \
  --apiserver-advertise-address=192.168.1.100 \
  --pod-network-cidr=10.244.0.0/16 \
  --service-cidr=10.96.0.0/16 \
  --kubernetes-version=v1.28.0

参数说明:

参数 说明
--apiserver-advertise-address master 的 IP API Server 监听地址
--pod-network-cidr 10.244.0.0/16 Pod 网络地址段,Calico 默认用这个
--service-cidr 10.96.0.0/16 Service 网络地址段
--kubernetes-version v1.28.0 指定版本,避免拉取最新版

初始化成功后,输出末尾会有类似这样的信息:

复制代码
Your Kubernetes control plane has been successfully initialized!

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

Then you can join any number of control-plane nodes by running:

  kubeadm join 192.168.1.100:6443 --token <token> \
    --discovery-token-ca-cert-hash sha256:<hash>

Then you can join any number of worker nodes by running the following on each:

  kubeadm join 192.168.1.100:6443 --token <token> \
    --discovery-token-ca-cert-hash sha256:<hash>

🔴 重点: 把最后那个 kubeadm join 命令抄下来保存好。等下 Worker 节点加入集群要用。如果忘了,可以用这个命令重新生成:

bash 复制代码
kubeadm token create --print-join-command

配置 kubectl(在 master 上执行):

bash 复制代码
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 能看到 master 节点,状态是 NotReady(这是正常的,还没装网络插件)。


六、安装网络插件(Calico)

集群现在还不能跑 Pod,因为没有网络插件。Pod 之间互相访问、Service 转发流量都靠它。

bash 复制代码
kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.26.1/manifests/calico.yaml

等一两分钟,让 Calico 的 Pod 全部启动:

bash 复制代码
# 监控 Pod 启动状态
kubectl get pods -n kube-system -w

成功标志: kubectl get nodes 显示 master 状态变为 Readykubectl get pods -n kube-system 中所有 Pod 都是 Running

⚠️ 注意: 如果 Calico Pod 卡在 ContainerCreatingImagePullBackOff,大概率是网络问题。检查 master 是否能访问外网(Calico 镜像在 Docker Hub 上)。如果拉不下来,可以提前在所有节点上 crictl pull calico/node:v3.26.1


七、Worker 节点加入集群

每台 Worker 节点 上执行之前保存的 join 命令:

bash 复制代码
sudo kubeadm join 192.168.1.100:6443 --token <token> \
  --discovery-token-ca-cert-hash sha256:<hash>

回到 master 验证:

bash 复制代码
kubectl get nodes

预期输出:

复制代码
NAME         STATUS   ROLES           AGE   VERSION
k8s-master   Ready    control-plane   10m   v1.28.0
k8s-node1    Ready    <none>          2m    v1.28.0
k8s-node2    Ready    <none>          1m    v1.28.0

成功标志: 三个节点都是 Ready 状态。如果 Worker 节点是 NotReady,等一两分钟再看------Calico 需要在新节点上也部署完成。


八、验证集群:部署一个 Nginx

集群搭好了,跑个应用验证一下:

bash 复制代码
# 创建 Deployment
kubectl create deployment nginx-test --image=nginx:1.25 --replicas=3

# 暴露 Service
kubectl expose deployment nginx-test --port=80 --type=NodePort

# 查看 Pod 分布
kubectl get pods -o wide

# 查看 Service
kubectl get svc nginx-test

验证 Pod 分散在不同节点上(说明调度正常):

bash 复制代码
kubectl get pods -o wide

预期输出类似:

复制代码
NAME                           READY   STATUS    RESTARTS   AGE   IP           NODE
nginx-test-7d6dd5c955-abc12   1/1     Running   0          30s   10.244.1.5   k8s-node1
nginx-test-7d6dd5c955-def34   1/1     Running   0          30s   10.244.2.5   k8s-node2
nginx-test-7d6dd5c955-ghi56   1/1     Running   0          30s   10.244.2.6   k8s-node2

访问测试:

bash 复制代码
# 获取 NodePort
NODE_PORT=$(kubectl get svc nginx-test -o jsonpath='{.spec.ports[0].nodePort}')

# 通过任意节点的 IP 访问
curl http://192.168.1.101:$NODE_PORT

如果返回 Nginx 的欢迎页面,集群就搭好了。

测试完清理资源:

bash 复制代码
kubectl delete deployment nginx-test
kubectl delete svc nginx-test

九、常见问题排查

问题一:kubeadm init 卡在 [wait-control-plane]

原因: 大概率是 containerd 没正常运行,或者 CRI socket 路径不对。

排查:

bash 复制代码
sudo systemctl status containerd
sudo crictl info

解决: 如果 containerd 没启动,sudo systemctl start containerd。如果 crictl 报错连接不上,检查 /etc/containerd/config.toml 里的 SystemdCgroup 是否改成了 true

问题二:Worker 节点 join 后一直是 NotReady

原因: Calico 还没部署到新节点,或者节点间网络不通。

排查:

bash 复制代码
# 在 NotReady 的节点上执行
sudo journalctl -u kubelet -f

解决: 等待 2-3 分钟。如果超过 5 分钟还是 NotReady,检查节点间能否互相 ping 通,以及防火墙是否放行了 6443、10250、179(BGP)等端口。

问题三:Pod 跨节点访问不通

原因: CNI 插件没装好,或者节点间防火墙阻断了 Pod 网络。

排查:

bash 复制代码
# 检查 Calico 状态
kubectl get pods -n kube-system -l k8s-app=calico-node
calicoctl node status

解决: 确认 Calico Pod 全部 Running。如果 Calico 用的是 BGP 模式,确保节点间 TCP 179 端口没被防火墙拦截。

问题四:token 过期了,Worker 节点加不进来

原因: kubeadm 生成的 token 默认 24 小时过期。

解决:

bash 复制代码
# 重新生成 join 命令
kubeadm token create --print-join-command

十、总结:你搭完集群后要记住的事

  1. kubeadm 只管集群初始化。它不管机器 provisioning、不管监控、不管日志,这些要自己配。
  2. containerd 的 SystemdCgroup 必须改成 true。这是最容易忘的一步,也是最容易导致诡异问题的一步。
  3. token 会过期 。Worker 节点要在 24 小时内加入,或者用 kubeadm token create 重新生成。
  4. 网络插件是必需的。没装 CNI 插件的集群就是一个空壳,Pod 之间互相访问不了。
  5. 生产环境要加 --control-plane-endpoint。单 master 的话没问题,但如果以后要加高可用,初始化时就要配好负载均衡器地址。

参考资源

相关推荐
人工智能培训1 小时前
从GPT到开源大模型
人工智能·gpt·深度学习·机器学习·容器·知识图谱
张小凡vip2 小时前
Kubernetes--secret的简介和使用
云原生·容器·kubernetes
IT策士2 小时前
Docker 常见面试问题
docker·容器·面试
预测模型的开发与应用研究2 小时前
双Docker Oracle XE 跨库查询操作文档
docker·oracle·容器
逻极3 小时前
Docker容器化实战:从镜像构建到微服务编排与避坑指南
docker·容器·镜像·devops
张忠琳3 小时前
【client-go v0.36.1】(Reflector Part 3) Reflector 超深度分析 — watchList 流式初始化
云原生·kubernetes·informer·client-go·reflector
IT策士3 小时前
k8s 常见面试问题
容器·面试·kubernetes
鹤落晴春4 小时前
【K8s】资源配额与访问控制
docker·容器·kubernetes
蘋天纬地4 小时前
k8s中的工作负载是什么,都有哪几种类型的工作负载
云原生·容器·kubernetes