适用环境
- Ubuntu Server 22.04 / 24.04
- 1 台 Control Plane,若干 Worker
- containerd 作为容器运行时
- Calico v3.32.1 作为 CNI
- IPv4 单栈
- 所有操作使用
root,普通用户请在命令前加 sudo
以下为单控制平面方案。生产环境建议至少部署 3 个控制平面节点,并在 API Server 前配置负载均衡器。
一、节点规划
示例:
| 主机名 |
IP |
角色 |
| k8s-master01 |
192.168.10.10 |
Control Plane |
| k8s-worker01 |
192.168.10.11 |
Worker |
| k8s-worker02 |
192.168.10.12 |
Worker |
每台节点至少需要 2 GiB 内存;控制平面至少 2 核 CPU,各节点网络必须互通。(kubernetes.io)
Pod 网段规划:
1 2
| Pod CIDR: 192.168.0.0/16 Service CIDR: 10.96.0.0/12
|
确保 Pod、Service 网段不与宿主机网络、VPN 网络重叠。
二、配置主机名
分别执行:
1 2 3 4 5 6 7 8
| hostnamectl set-hostname k8s-master01
hostnamectl set-hostname k8s-worker01
hostnamectl set-hostname k8s-worker02
|
所有节点配置 /etc/hosts:
1 2 3 4 5
| cat >> /etc/hosts <<'EOF' 192.168.10.10 k8s-master01 192.168.10.11 k8s-worker01 192.168.10.12 k8s-worker02 EOF
|
验证:
1 2 3
| ping -c 2 k8s-master01 ping -c 2 k8s-worker01 ping -c 2 k8s-worker02
|
三、开放防火墙端口
控制平面至少开放:
1 2 3 4 5
| TCP 6443 TCP 2379-2380 TCP 10250 TCP 10257 TCP 10259
|
Worker 至少开放:
1 2 3
| TCP 10250 TCP 10256 TCP/UDP 30000-32767 # 使用 NodePort 时
|
另外,需要允许集群节点之间的 Calico 网络流量。最简单的方式是在安全组或内部防火墙上允许集群节点网段互访。
测试环境可以临时关闭 UFW:
生产环境不要直接关闭防火墙,应按照 Kubernetes 和 CNI 的要求精确放行。官方要求节点间必须开放 Kubernetes 组件通信端口。(kubernetes.io)
四、所有节点执行系统初始化
1. 更新时间
1 2 3 4 5
| apt-get update apt-get install -y chrony curl ca-certificates gpg apt-transport-https
systemctl enable --now chrony chronyc sources
|
2. 关闭 Swap
1 2 3 4 5
| swapoff -a
sed -ri '/\sswap\s/s/^#?/#/' /etc/fstab
free -h
|
确认 Swap 为 0。
默认情况下,kubelet 检测到 Swap 会拒绝启动,因此需要关闭 Swap,或者单独修改 kubelet 的 Swap 配置。(kubernetes.io)
3. 加载内核模块
1 2 3 4 5 6 7
| cat > /etc/modules-load.d/k8s.conf <<'EOF' overlay br_netfilter EOF
modprobe overlay modprobe br_netfilter
|
确认:
1
| lsmod | grep -E 'overlay|br_netfilter'
|
4. 配置内核参数
1 2 3 4 5 6 7
| cat > /etc/sysctl.d/99-kubernetes-cri.conf <<'EOF' net.bridge.bridge-nf-call-iptables = 1 net.bridge.bridge-nf-call-ip6tables = 1 net.ipv4.ip_forward = 1 EOF
sysctl --system
|
验证:
1 2 3
| sysctl net.bridge.bridge-nf-call-iptables sysctl net.bridge.bridge-nf-call-ip6tables sysctl net.ipv4.ip_forward
|
结果应均为 1。
五、所有节点安装 containerd
1. 安装
1 2
| apt-get update apt-get install -y containerd
|
2. 生成默认配置
1 2 3
| mkdir -p /etc/containerd
containerd config default > /etc/containerd/config.toml
|
3. 使用 systemd cgroup
1 2
| sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' \ /etc/containerd/config.toml
|
确认配置:
1
| grep -n 'SystemdCgroup' /etc/containerd/config.toml
|
应看到:
同时确认 disabled_plugins 中没有 cri:
1
| grep -n 'disabled_plugins' /etc/containerd/config.toml
|
Kubernetes 官方推荐 containerd 与 kubelet 使用一致的 systemd cgroup 驱动,并要求 containerd 启用 CRI 插件。(kubernetes.io)
4. 启动 containerd
1 2 3 4 5 6
| systemctl daemon-reload systemctl enable --now containerd systemctl restart containerd
systemctl status containerd --no-pager
|
验证:
六、所有节点安装 kubeadm、kubelet、kubectl
Kubernetes 从 1.24 开始为每个次版本提供独立软件源。v1.36 应使用 pkgs.k8s.io/core:/stable:/v1.36,旧的 apt.kubernetes.io 已被冻结。(kubernetes.io)
1. 添加签名密钥
1 2 3 4 5
| mkdir -p -m 755 /etc/apt/keyrings
curl -fsSL \ https://pkgs.k8s.io/core:/stable:/v1.36/deb/Release.key \ | gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
|
2. 添加 Kubernetes 软件源
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| cat > /etc/apt/sources.list.d/kubernetes.list <<'EOF' deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.36/deb/ / EOF
cat <<EOF | sudo tee /etc/yum.repos.d/kubernetes.repo [kubernetes] name=Kubernetes baseurl=https://pkgs.k8s.io/core:/stable:/v1.36/rpm/ enabled=1 gpgcheck=1 gpgkey=https://pkgs.k8s.io/core:/stable:/v1.36/rpm/repodata/repomd.xml.key exclude=kubelet kubeadm kubectl cri-tools kubernetes-cni EOF
|
3. 安装组件
1 2 3 4 5 6 7 8 9 10 11 12
| apt-get update
apt-get install -y kubelet kubeadm kubectl
apt-mark hold kubelet kubeadm kubectl
systemctl enable --now kubelet
sudo yum install -y kubelet kubeadm kubectl --disableexcludes=kubernetes sudo systemctl enable --now kubelet
|
此时 kubelet 反复重启属于正常现象,因为集群还没有初始化。
4. 检查版本
1 2 3
| kubeadm version -o short kubelet kubectl version
|
预期:
1 2 3
| v1.36.2 Kubernetes v1.36.2 Client Version: v1.36.2
|
如果软件源后续发布了新的 v1.36 补丁版本,安装到的版本可能高于 v1.36.2。
七、初始化控制平面
只在 k8s-master01 执行。
1. 确认节点 IP
1 2
| ip route show hostname -I
|
假设控制平面 IP 为:
1 2 3
| CONTROL_PLANE_IP=192.168.10.10
CONTROL_PLANE_IP=192.168.122.9
|
2. 检查初始化条件
1 2
| kubeadm config images list \ --kubernetes-version "$(kubeadm version -o short)"
|
预拉取镜像:
1 2 3
| kubeadm config images pull \ --kubernetes-version "$(kubeadm version -o short)" \ --cri-socket unix:///run/containerd/containerd.sock
|
3. 初始化集群
1 2 3 4 5 6
| kubeadm init \ --kubernetes-version "$(kubeadm version -o short)" \ --apiserver-advertise-address="${CONTROL_PLANE_IP}" \ --pod-network-cidr=10.10.0.0/16 \ --service-cidr=10.96.0.0/12 \ --cri-socket unix:///run/containerd/containerd.sock
|
初始化成功后,终端会输出类似:
1 2 3
| kubeadm join 192.168.10.10:6443 \ --token xxxxxx.xxxxxxxxxxxxxxxx \ --discovery-token-ca-cert-hash sha256:xxxxxxxx
|
保存该命令,不要公开 Token。
4. 配置 kubectl
普通用户执行:
1 2 3 4 5
| mkdir -p $HOME/.kube
cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
chown "$(id -u):$(id -g)" $HOME/.kube/config
|
如果直接使用 root:
1 2 3
| export KUBECONFIG=/etc/kubernetes/admin.conf
echo 'export KUBECONFIG=/etc/kubernetes/admin.conf' >> ~/.bashrc
|
验证:
1 2
| kubectl get nodes kubectl get pods -A
|
此时节点显示 NotReady、CoreDNS 显示 Pending 是正常现象,因为还没有安装 CNI。Kubernetes 要求集群安装一个且只能安装一个 Pod 网络插件。(kubernetes.io)
八、安装 Calico CNI
下面使用 Calico v3.32.1 Operator。Calico 官方推荐新集群使用 Operator 管理安装和升级。(docs.tigera.io)
只在控制平面执行:
1 2 3 4 5
| kubectl create -f \ https://raw.githubusercontent.com/projectcalico/calico/v3.32.1/manifests/v1_crd_projectcalico_org.yaml
kubectl create -f \ https://raw.githubusercontent.com/projectcalico/calico/v3.32.1/manifests/tigera-operator.yaml
|
下载默认配置:
1 2
| curl -O \ https://raw.githubusercontent.com/projectcalico/calico/v3.32.1/manifests/custom-resources.yaml
|
确认 Calico IP 池:
1
| grep -A5 -B5 'cidr:' custom-resources.yaml
|
应该使用:
如果不是,请将其修改为与 kubeadm init --pod-network-cidr 相同的网段。
安装:
1
| kubectl create -f custom-resources.yaml
|
观察安装状态:
1
| watch kubectl get tigerastatus
|
也可以检查 Pod:
等待所有 Calico 状态变为:
1 2 3
| AVAILABLE=True PROGRESSING=False DEGRADED=False
|
然后检查:
1 2
| kubectl get nodes -o wide kubectl get pods -A
|
控制平面节点应变为 Ready,CoreDNS 应变为 Running。
九、Worker 加入集群
在每台 Worker 节点执行初始化时输出的命令:
1 2 3 4
| kubeadm join 192.168.10.10:6443 \ --token xxxxxx.xxxxxxxxxxxxxxxx \ --discovery-token-ca-cert-hash sha256:xxxxxxxx \ --cri-socket unix:
|
然后在控制平面检查:
1
| kubectl get nodes -o wide
|
预期:
1 2 3 4
| NAME STATUS ROLES VERSION k8s-master01 Ready control-plane v1.36.2 k8s-worker01 Ready <none> v1.36.2 k8s-worker02 Ready <none> v1.36.2
|
如果 Join 命令丢失,在控制平面重新生成:
1
| kubeadm token create --print-join-command
|
执行时追加 containerd 参数:
1
| --cri-socket unix:///run/containerd/containerd.sock
|
十、验证集群
1. 检查集群状态
1 2 3 4
| kubectl cluster-info kubectl get nodes kubectl get pods -A kubectl get componentstatuses
|
更推荐使用:
1 2
| kubectl get --raw='/readyz?verbose' kubectl get --raw='/livez?verbose'
|
2. 部署测试应用
1 2 3 4 5 6
| kubectl create deployment nginx-test --image=nginx:alpine
kubectl expose deployment nginx-test \ --name=nginx-test \ --port=80 \ --type=NodePort
|
等待部署完成:
1 2 3
| kubectl rollout status deployment/nginx-test kubectl get pods -o wide kubectl get service nginx-test
|
获取 NodePort:
1 2 3 4
| NODE_PORT=$(kubectl get service nginx-test \ -o jsonpath='{.spec.ports[0].nodePort}')
echo "$NODE_PORT"
|
访问 Worker 节点:
1
| curl "http://192.168.10.11:${NODE_PORT}"
|
成功返回 Nginx 页面表示以下链路均正常:
- Pod 调度
- 容器运行时
- Calico 网络
- kube-proxy
- Service/NodePort
清理测试应用:
1 2
| kubectl delete service nginx-test kubectl delete deployment nginx-test
|
十一、单节点集群可选配置
如果只有一台控制平面节点,需要允许业务 Pod 调度到控制平面:
1
| kubectl taint nodes --all node-role.kubernetes.io/control-plane-
|
多节点生产集群不建议执行。(kubernetes.io)
十二、常见故障排查
节点一直 NotReady
1 2 3
| kubectl describe node k8s-worker01 kubectl get pods -A journalctl -u kubelet -n 200 --no-pager
|
重点检查:
- Calico 是否正常
- Pod CIDR 是否一致
- 节点间防火墙是否放行
net.ipv4.ip_forward 是否为 1
containerd/CRI 错误
1 2 3
| systemctl status containerd --no-pager journalctl -u containerd -n 200 --no-pager crictl info
|
配置 crictl:
1 2 3 4 5 6
| cat > /etc/crictl.yaml <<'EOF' runtime-endpoint: unix:///run/containerd/containerd.sock image-endpoint: unix:///run/containerd/containerd.sock timeout: 10 debug: false EOF
|
kubelet 启动失败
1 2
| systemctl status kubelet journalctl -xeu kubelet
|
检查:
1 2
| swapon --show grep SystemdCgroup /etc/containerd/config.toml
|
重置失败节点
在需要重新安装的节点执行:
1 2 3 4
| kubeadm reset -f \ --cri-socket unix:///run/containerd/containerd.sock
systemctl restart containerd
|
然后重新执行 kubeadm join。注意,kubeadm reset 不会自动清理全部 iptables/IPVS 规则。(kubernetes.io)