Skip to content

kubeadm 单主集群

Kubernetes 自建集群常见几种部署方式,差异主要在自动化程度、控制平面高可用和资源占用:

部署方式适合场景位置
kubeadm 单主结构清楚,适合理解控制平面、节点加入和基础组件关系02
kubeadm 高可用多控制平面和 API Server 高可用入口03
Kubespray批量交付、Ansible 管理和更完整的集群参数04
K3s小规格环境、边缘场景和轻量 Kubernetes 集群05

kubeadm 单主集群保留了标准 Kubernetes 组件形态,控制平面和工作节点关系比较清楚。理解组件关系之后,kubectl 命令、YAML 示例和排错思路在 kubeadm、Kubespray、K3s 等部署方式下都能复用。


Kubernetes 组件不少——apiserver、etcd、scheduler、controller-manager、kubelet、containerd、kube-proxy、CNI。全部手工二进制部署时,每个组件的证书、配置、启动参数、systemd unit 都要自己写,出问题后排查链路也更长。

kubeadm 用一条 init 命令拉起控制平面,一条 join 命令加入节点。它把证书生成、配置分发、静态 Pod manifest 写入、kubeconfig 创建这些事自动化了,同时保留了对每个组件配置文件的查看和修改入口。适合搭建结构清楚、便于理解组件关系的集群。

单控制平面适合实验、测试和内部验证;生产环境需要多控制平面加 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 的 requests/limits 和 QoS 判断会失效:

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

内核模块和参数。br_netfilter 让 bridge 流量经过 iptables——CNI 策略和 kube-proxy 转发都依赖这个:

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 一致性——看起来像认证失败的问题底层可能只是 NTP 没同步:

bash
systemctl enable --now chronyd
chronyc sources -v

安装 containerd

kubelet 通过 CRI 接口调用容器运行时。containerd 的 cgroup 驱动要和 kubelet 一致——两边都用 systemd,否则容器资源统计异常:

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

生成配置——关键是把 SystemdCgroup 改成 true

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 缺少 /etc/kubernetes/manifests/ 下的静态 Pod manifest,
# 会反复重启——这是正常的,等 init 完成后就好了

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

systemctl enable --now kubelet

初始化控制平面

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

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
  serviceSubnet: 10.96.0.0/12
---
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
cgroupDriver: systemd
bash
kubeadm init --config kubeadm-init.yaml --upload-certs

kubeadm init 实际做了几件事:生成 CA 和组件证书、写 kubeconfig、生成静态 Pod manifest(etcd.yaml、kube-apiserver.yaml、kube-controller-manager.yaml、kube-scheduler.yaml)放入 /etc/kubernetes/manifests/、启动 kubelet 拉起这些静态 Pod。

配置 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——没有 CNI 时 Pod 网络未就绪

安装 Calico

Calico 是生产环境常见的 CNI,支持 BGP、VXLAN、IPIP 和 NetworkPolicy:

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

# 如果 kubeadm 的 Pod 网段不是 Calico 默认值 (192.168.0.0/16),需要改 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

CNI 安装完成后,控制平面节点从 NotReady 变为 Ready——CNI Pod 在节点上运行并完成网络初始化。国内环境拉 quay.io 镜像可能很慢,更稳的做法是提前把 Calico 镜像同步到内部 Harbor 再改 manifest 里的镜像地址。

加入工作节点

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

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

join 过程:kubelet 用 token 和 CA hash 向 API Server 注册,获取证书,下载集群配置,启动 kube-proxy 等组件。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

基础验证

CNI 就绪且所有节点 Ready 后,跑一个测试 Pod 验证调度→拉镜像→Service 转发链路:

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 port-forward svc/nginx-test 8080:80
curl http://127.0.0.1:8080/

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