Skip to content

kubeadm 单主集群

kubeadm 适合搭建结构清楚、便于理解组件关系的 Kubernetes 集群。单控制平面适合实验、测试和内部验证;生产环境则需要多控制平面加 API Server 高可用入口。

环境规划

实验环境用一台控制平面和两台工作节点:

主机名IP角色
k8s-master01192.168.10.11控制平面
k8s-node01192.168.10.12工作节点
k8s-node02192.168.10.13工作节点

网段提前定好,初始化后改起来成本很高:

项目示例说明
Node 网段192.168.10.0/24服务器真实网段
Pod 网段10.244.0.0/16Pod IP 池,不能和内网、VPN、Docker 网段冲突
Service 网段10.96.0.0/12ClusterIP 地址池
API Server192.168.10.11:6443单主环境直接用控制平面 IP

版本用变量管理,避免后面命令里写死版本号:

bash
K8S_VERSION="1.36.0"       # kubeadm/kubelet/kubectl 尽量同一补丁版本
K8S_MINOR="v1.36"          # 官方仓库按 minor 版本组织
POD_CIDR="10.244.0.0/16"   # 和 CNI 插件配置保持一致
SVC_CIDR="10.96.0.0/12"    # Service 网段初始化后不适合改

系统初始化——所有节点都执行

主机名和解析:

bash
hostnamectl set-hostname k8s-master01  # 每台按实际角色改

cat >> /etc/hosts <<'EOF'
192.168.10.11 k8s-master01
192.168.10.12 k8s-node01
192.168.10.13 k8s-node02
EOF

关闭 swap——kubelet 要求 swap 关闭,否则 Pod 性能和行为会异常:

bash
swapoff -a
sed -ri '/\sswap\s/s/^/#/' /etc/fstab  # 防止重启后 swap 自动挂回来

内核模块和参数。br_netfilter 让 bridge 流量经过 iptables,这是 kube-proxy 和 CNI 策略生效的前提:

bash
cat > /etc/modules-load.d/k8s.conf <<'EOF'
overlay
br_netfilter
EOF

modprobe overlay
modprobe br_netfilter

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

时间同步。节点时间漂移会影响证书校验、Token 有效期、etcd 一致性,看起来像认证失败的问题底层可能只是时间不准:

bash
systemctl enable --now chronyd
chronyc sources -v

安装 containerd

RHEL / Rocky / CentOS:

bash
yum install -y yum-utils
yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
yum install -y containerd.io cri-tools

Ubuntu:

bash
apt-get update
apt-get install -y ca-certificates curl gnupg

install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg \
  -o /etc/apt/keyrings/docker.asc
chmod a+r /etc/apt/keyrings/docker.asc

echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo \"${UBUNTU_CODENAME:-$VERSION_CODENAME}\") stable" \
  > /etc/apt/sources.list.d/docker.list

apt-get update
apt-get install -y containerd.io cri-tools

生成配置。kubelet 使用 systemd 管理 cgroup,containerd 的 cgroup 驱动也要改成 systemd——两边不一致会导致容器资源统计异常:

bash
mkdir -p /etc/containerd
containerd config default > /etc/containerd/config.toml
sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml

systemctl enable --now containerd

配置 crictl,让它知道容器运行时 socket 在哪:

bash
cat > /etc/crictl.yaml <<'EOF'
runtime-endpoint: unix:///run/containerd/containerd.sock
image-endpoint: unix:///run/containerd/containerd.sock
timeout: 10
debug: false
EOF

crictl info | head  # 能返回运行时信息,CRI 通道就正常

安装 kubeadm、kubelet、kubectl

RHEL 系列用官方 rpm 仓库:

bash
cat > /etc/yum.repos.d/kubernetes.repo <<EOF
[kubernetes]
name=Kubernetes
baseurl=https://pkgs.k8s.io/core:/stable:/${K8S_MINOR}/rpm/
enabled=1
gpgcheck=1
gpgkey=https://pkgs.k8s.io/core:/stable:/${K8S_MINOR}/rpm/repodata/repomd.xml.key
EOF

yum install -y kubelet-${K8S_VERSION} kubeadm-${K8S_VERSION} kubectl-${K8S_VERSION} \
  --disableexcludes=kubernetes

systemctl enable --now kubelet  # kubeadm 写入配置前 kubelet 会反复重启,这是正常的

Ubuntu 系列:

bash
apt-get update
apt-get install -y apt-transport-https ca-certificates curl gpg

mkdir -p /etc/apt/keyrings
curl -fsSL "https://pkgs.k8s.io/core:/stable:/${K8S_MINOR}/deb/Release.key" \
  | 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:/${K8S_MINOR}/deb/ /" \
  > /etc/apt/sources.list.d/kubernetes.list

apt-get update
apt-get install -y kubelet kubeadm kubectl
apt-mark hold kubelet kubeadm kubectl # 避免系统升级顺手升级 Kubernetes 组件

systemctl enable --now kubelet

kubelet 版本不能高于 kube-apiserver。后面升级集群时,一般先升控制平面,再逐台升工作节点。

初始化控制平面

用配置文件初始化比命令行参数更清楚,也方便留档:

yaml
# kubeadm-init.yaml
apiVersion: kubeadm.k8s.io/v1beta4
kind: ClusterConfiguration
kubernetesVersion: v1.36.0
imageRepository: registry.aliyuncs.com/google_containers
networking:
  podSubnet: 10.244.0.0/16     # 和后面 CNI 插件保持一致
  serviceSubnet: 10.96.0.0/12  # ClusterIP 地址池
---
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
cgroupDriver: systemd          # 和 containerd SystemdCgroup 保持一致
bash
kubeadm init --config kubeadm-init.yaml --upload-certs

配置 kubectl:

bash
mkdir -p $HOME/.kube
cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
chown "$(id -u):$(id -g)" $HOME/.kube/config

kubectl get nodes  # CNI 安装前,控制平面节点通常是 NotReady

/etc/kubernetes/admin.conf 是集群最高权限的 kubeconfig,适合管理节点使用。日常操作更适合单独做 RBAC 和 kubeconfig,按人分配权限。

安装 Calico

Calico 是生产环境常见的 CNI,支持 BGP、VXLAN、IPIP 和 NetworkPolicy。实验环境也可以用 Flannel,概念更少:

bash
curl -L -o calico.yaml https://raw.githubusercontent.com/projectcalico/calico/v3.31.0/manifests/calico.yaml

# 如果 kubeadm 的 Pod 网段不是 Calico 默认值,需要改 CALICO_IPV4POOL_CIDR
grep -n "CALICO_IPV4POOL_CIDR" calico.yaml

kubectl apply -f calico.yaml
kubectl -n kube-system get pods -l k8s-app=calico-node -o wide

国内环境拉 quay.io 镜像可能很慢。更稳的做法是提前把 Calico 镜像同步到内部 Harbor,再改 manifest 里的镜像地址。

加入工作节点

kubeadm 初始化成功后会输出 join 命令。在工作节点上执行:

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

Token 过期时,在控制平面重新生成:

bash
kubeadm token create --print-join-command

节点长时间 NotReady 时,看 kubelet 和容器运行时:

bash
systemctl status kubelet --no-pager
journalctl -u kubelet -n 100 --no-pager
crictl ps -a

基础验证

bash
kubectl create namespace demo
kubectl -n demo run nginx-test --image=nginx:1.26 --port=80
kubectl -n demo expose pod nginx-test --port=80 --target-port=80

kubectl -n demo get pod,svc -o wide
kubectl -n demo describe pod nginx-test

# 临时访问
kubectl -n demo port-forward svc/nginx-test 8080:80
curl http://127.0.0.1:8080/

集群 Ready 说明控制面和基础网络大致可用,不代表镜像仓库、存储、Ingress/Gateway 和监控都已经配好。