使用 cri-docker 搭建 Kubernetes 集群完整教程(v1.36.3)

前言

本文基于 cri-docker(Docker 作为 CRI 容器运行时) 搭建一套 Kubernetes v1.36.3 集群,节点规划为 1 个 Master + 2 个 Node ,网络插件采用 Calico(关闭 IPIP/VXLAN,使用 BGP 模式)。操作命令来自真实实验环境,按步骤复制粘贴即可,适合初学 Kubernetes 的同学照着做。

小提示 :cri-docker 与 containerd 是两种 CRI 容器运行时,二选一安装即可,不要同时安装。本文主体走 cri-docker 路线(4.2.1),4.2.2 同时附上 containerd 方式供参考。


一、环境准备与节点规划

开始前先用 VMware/VirtualBox 克隆三台 CentOS/RHEL 虚拟机 ,分别作为 Master、Node01、Node02。三台主机的 IP 与主机名规划如下(文中以 192.168.194.x 网段为例,请按你实际网段修改):

角色 主机名 计划 IP 别名
Master k8s-master01 192.168.194.11 m1
Node01 k8s-node01 192.168.194.12 n1
Node02 k8s-node02 192.168.194.13 n2
Harbor hb.reg.com 192.168.194.20 harbor

文中用到 m1n1n2harbor 这些别名,方便后续 scp 拷贝文件时短命令直接使用。


二、集群准备(Master / Node01 / Node02)

2.1 配置 Master 节点

克隆一台 k8s-1 虚拟机用作 Master,按下面步骤配置。

2.1.1 修改主机名
bash 复制代码
[root@localhost ~]# hostnamectl hostname k8s-master01 && bash
2.1.2 修改网卡

先查看当前网卡信息:

bash 复制代码
[root@k8s-master01 ~]# ls /etc/NetworkManager/system-connections/
ens160.nmconnection
[root@k8s-master01 ~]# ip a
2: ens160: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether 00:0c:29:eb:22:8e brd ff:ff:ff:ff:ff:ff

修改 ens160 的 IP 为静态 IP:

bash 复制代码
[root@k8s-master01 ~]# nmcli c m ens160 ipv4.method manual ipv4.addresses 192.168.194.11/24 ipv4.dns "223.5.5.5 8.8.8.8" ipv4.gateway 192.168.10.2 connection.autoconnect yes
[root@k8s-master01 ~]# nmcli c up ens160
Connection successfully activated (D-Bus active path: /org/freedesktop/NetworkManager/ActiveConnection/4)

⚠️ 网关地址(gateway)请按你实际路由器的地址修改,文中 192.168.10.2 仅为示例。

2.1.3 配置主机映射
bash 复制代码
[root@k8s-master01 ~]# cat > /etc/hosts <<EOF
192.168.194.11 k8s-master01 m1
192.168.194.12 k8s-node01 n1
192.168.194.13 k8s-node02 n2
192.168.194.20 hb.reg.com harbor
EOF

配置完成后,给 Master 做个快照(防止后续操作失误,方便回滚)。

2.2 配置 Node01 节点

克隆一台 k8s-2 虚拟机用作 Node01。

2.2.1 修改主机名
bash 复制代码
[root@localhost ~]# hostnamectl hostname k8s-node01 && bash
2.2.2 修改网卡
bash 复制代码
[root@k8s-node01 ~]# ls /etc/NetworkManager/system-connections/
ens160.nmconnection
[root@k8s-node01 ~]# ip a
2: ens160: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether 00:0c:29:eb:22:8e brd ff:ff:ff:ff:ff:ff
    altname enp3s0
    inet 192.168.10.134/24 brd 192.168.10.255 scope global dynamic noprefixroute ens160

修改 ens160 的 IP:

bash 复制代码
[root@k8s-node01 ~]# nmcli c m ens160 ipv4.method manual ipv4.addresses 192.168.194.12/24 ipv4.dns "223.5.5.5 8.8.8.8" ipv4.gateway 192.168.194.2 connection.autoconnect yes
[root@k8s-node01 ~]# nmcli c up ens160
Connection successfully activated (D-Bus active path: /org/freedesktop/NetworkManager/ActiveConnection/4)
2.2.3 配置主机映射
bash 复制代码
[root@k8s-node01 ~]# cat > /etc/hosts <<EOF
192.168.194.11 k8s-master01 m1
192.168.194.12 k8s-node01 n1
192.168.194.13 k8s-node02 n2
192.168.194.20 hb.reg.com harbor
EOF

配置好后,给 Node01 做一个快照

2.3 配置 Node02 节点

克隆一台 k8s-3 虚拟机用作 Node02。

2.3.1 修改主机名
bash 复制代码
[root@192 ~]# hostnamectl hostname k8s-node02 && bash
2.3.2 修改网卡
bash 复制代码
[root@k8s-node02 ~]# ls /etc/NetworkManager/system-connections/
ens160.nmconnection
[root@k8s-node02 ~]# ip a
2: ens160: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether 00:0c:29:eb:22:8e brd ff:ff:ff:ff:ff:ff
    altname enp3s0
    inet 192.168.10.135/24 brd 192.168.10.255 scope global dynamic noprefixroute ens160

修改 ens160 的 IP(注意此处是单数 address,写法略有差异,但效果一致):

bash 复制代码
[root@k8s-node02 ~]# nmcli c m ens160 ipv4.method manual ipv4.address 192.168.194.13/24 ipv4.dns "223.5.5.5 8.8.8.8" ipv4.gateway 192.168.194.2 connection.autoconnect yes
[root@k8s-node02 ~]# nmcli c up ens160
Connection successfully activated (D-Bus active path: /org/freedesktop/NetworkManager/ActiveConnection/4)
2.3.3 配置主机映射
bash 复制代码
[root@k8s-node02 ~]# cat > /etc/hosts <<EOF
192.168.194.11 k8s-master01 m1
192.168.194.12 k8s-node01 n1
192.168.194.13 k8s-node02 n2
192.168.194.20 hb.reg.com harbor
EOF

配置好后,给 Node02 做一个快照


三、搭建 Kubernetes 集群

⚠️ 本节 3.1、3.2、3.3 的安装操作需要在 k8s-1、k8s-2、k8s-3 三台机器上全部执行(除非特别标注只作用于 Master)。

3.1 安装 Docker

3.1.1 添加 docker 源
bash 复制代码
dnf install yum-utils -y
yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/rhel/docker-ce.repo
3.1.2 安装 docker
bash 复制代码
dnf install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin -y
3.1.3 配置 docker
bash 复制代码
cat > /etc/docker/daemon.json <<EOF
{
    "default-ipc-mode": "shareable",
    "data-root": "/data/docker",
    "exec-opts": ["native.cgroupdriver=systemd"],
    "log-driver": "json-file",
    "log-opts": {
        "max-size": "100m",
        "max-file": "50"
    },
    "insecure-registries": ["https://hb.reg.com"],
    "registry-mirrors": [
        "https://docker.m.daocloud.io",
        "https://docker.ims.run"
    ]
}
EOF

解释:data-root 指定 Docker 数据目录;exec-opts 设置 cgroup 驱动为 systemd(与 kubelet 保持一致,避免 cgroupfs 冲突);insecure-registries 用于访问自建 Harbor 私仓;registry-mirrors 为国内镜像加速。

3.1.4 启动 docker
bash 复制代码
systemctl enable --now docker
3.1.5 验证 docker
bash 复制代码
[root@localhost ~]# docker version
Client: Docker Engine - Community
 Version:           29.6.2
 API version:       1.55
 Go version:        go1.26.5
 Git commit:        dfc4efb
 Built:             Thu Jul 16 16:15:33 2026
 OS/Arch:           linux/amd64
 Context:           default

Server: Docker Engine - Community
 Engine:
  Version:          29.6.2
  API version:      1.55 (minimum version 1.40)
  Go version:       go1.26.5
  Git commit:       3d80467
  Built:            Thu Jul 16 16:11:37 2026
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          v2.2.6
  GitCommit:        11ce9d5f3c68c941867e82890e93e815c1304f1b
 runc:
  Version:          1.3.6
  GitCommit:        v1.3.6-0-g491b69ba
 docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0

看到 Server: 那段能正常输出,说明 Docker 已成功启动。

3.2 安装 CRI 容器运行时

CRI 为容器运行时接口,常见有 cri-dockercontainerd 两种,二选一安装即可,不要都装。这里给出两条路线。

3.2.1 方案一:安装 cri-docker(本文主线)

(1)下载 cri-docker

bash 复制代码
wget https://github.com/Mirantis/cri-dockerd/releases/download/v0.4.6/cri-dockerd-0.4.6.amd64.tgz

(2)解压文件

bash 复制代码
tar -xf cri-dockerd-0.4.6.amd64.tgz

(3)将文件复制到 /usr/bin 目录

bash 复制代码
cp cri-docker/cri-docker /usr/bin/

(4)设置可执行权限

bash 复制代码
chmod +x /usr/bin/cri-docker

(5)配置 cri-docker 服务

bash 复制代码
cat > /usr/lib/systemd/system/cri-docker.service <<EOF
[Unit]
Description=CRI Interface for Docker Application Container Engine
Documentation=https://docs.mirantis.com
After=network-online.target firewalld.service docker.service
Wants=network-online.target
Requires=cri-docker.socket

[Service]
Type=notify
ExecStart=/usr/bin/cri-dockerd --container-runtime-endpoint fd:// --network-plugin=cni --pod-infra-container-image=registry.k8s.io/pause:3.10.2
ExecReload=/bin/kill -s HUP $MAINPID
TimeoutSec=0
RestartSec=2
Restart=always

StartLimitBurst=3

StartLimitInterval=60s

LimitNOFILE=infinity
LimitNPROC=infinity
LimitCORE=infinity

TasksMax=infinity
Delegate=yes
KillMode=process

[Install]
WantedBy=multi-user.target
EOF

💡 进阶说明 :这个 unit 文件也可以直接从 cri-dockerd-0.4.6.amd64.tgz 包内的 packaging/systemd/ 目录获取,然后修改 /etc/systemd/system/cri-docker.service,为 Service.ExecStart 增加 --network-plugin--pod-infra-container-image 两个选项。其中 --pod-infra-container-image(pause 根容器镜像)的 tag 需根据你的 Kubernetes 版本来定,例如本教程为 3.10.2

(6)配置 cri-docker 套接字

bash 复制代码
cat > /usr/lib/systemd/system/cri-docker.socket <<EOF
[Unit]
Description=CRI Docker Socket for the API
PartOf=cri-docker.service

[Socket]
ListenStream=%t/cri-dockerd.sock
SocketMode=0660
SocketUser=root
SocketGroup=docker

[Install]
WantedBy=sockets.target
EOF

拷贝 cri-docker 服务与套接字到 Node 节点

bash 复制代码
[root@k8s-master01 ~]# scp cri-docker.service cri-docker.socket n1:/usr/lib/systemd/system
cri-docker.service                                                                                                         100%  665   939.5KB/s   00:00
cri-docker.socket                                                                                                          100%  204   584.0KB/s   00:00
[root@k8s-master01 ~]# scp cri-docker.service cri-docker.socket n2:/usr/lib/systemd/system
cri-docker.service                                                                                                         100%  665     1.3MB/s   00:00
cri-docker.socket                                                                                                          100%  204   675.6KB/s   00:00

上传并加载 pause 根容器镜像

bash 复制代码
[root@k8s-master01 ~]# docker load -i pause_3.10.2.tar.gz
Loaded image: registry.k8s.io/pause:3.10.2
[root@k8s-master01 ~]# docker images
                                                                                                                              i Info →   U  In Use
IMAGE                          ID             DISK USAGE   CONTENT SIZE   EXTRA
registry.k8s.io/pause:3.10.2   9aec5a02507b       1.06MB          318kB

(7)启动 cri-docker 服务

bash 复制代码
systemctl daemon-reload
systemctl enable --now cri-docker
systemctl is-active cri-docker

systemctl is-active 返回 active 即启动成功。


3.2.2 方案二:安装 containerd(二选一,供参考)

这种方式见这篇文章:

containerd安装kubenetes集群

3.3 安装 Kubernetes

3.3.1 配置仓库

(1)添加 Kubernetes 源

官方阿里云镜像站:https://developer.aliyun.com/mirror/kubernetes

bash 复制代码
cat <<EOF | tee /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://mirrors.aliyun.com/kubernetes-new/core/stable/v1.36/rpm/
enabled=1
gpgcheck=1
gpgkey=https://mirrors.aliyun.com/kubernetes-new/core/stable/v1.36/rpm/repodata/repomd.xml.key
EOF

(2)安装 kubernetes

bash 复制代码
yum install -y kubelet kubeadm kubectl

三个工具的作用分别如下:

工具 作用
kubeadm 集群初始化工具,用于一键搭建 / 加入集群
kubelet 节点代理服务,运行在每个节点上,负责管理容器并上报状态
kubectl 命令行客户端,用户向集群下发指令的交互工具

(3)让 kubelet 开机自启

bash 复制代码
systemctl enable kubelet

注意:此时 kubelet 会因缺少配置而启动失败,属正常现象,kubeadm init 会补齐配置,无需担心。

(4)验证安装

bash 复制代码
[root@k8s-master01 ~]# kubeadm version
kubeadm version: &version.Info{Major:"1", Minor:"36", EmulationMajor:"", EmulationMinor:"", MinCompatibilityMajor:"", MinCompatibilityMinor:"", GitVersion:"v1.36.3", GitCommit:"0f29094e5b73085e3802ecc1298ecae13866bfe6", GitTreeState:"clean", BuildDate:"2026-07-22T18:09:52Z", GoVersion:"go1.26.5", Compiler:"gc", Platform:"linux/amd64"}

(5)查看所需镜像

bash 复制代码
[root@k8s-master01 ~]# kubeadm config images list
registry.k8s.io/kube-apiserver:v1.36.3
registry.k8s.io/kube-controller-manager:v1.36.3
registry.k8s.io/kube-scheduler:v1.36.3
registry.k8s.io/kube-proxy:v1.36.3
registry.k8s.io/coredns/coredns:v1.14.2
registry.k8s.io/pause:3.10.2
registry.k8s.io/etcd:3.6.8-0

# 获取镜像:从阿里云镜像仓库拉取后,改 tag 推送到自建 Harbor
images=$(kubeadm config images list --kubernetes-version=1.36.3 | awk -F "/" '{print $NF}')
for i in $(images)
do
    docker pull registry.aliyuncs.com/google_containers/$i
    docker tag registry.aliyuncs.com/google_containers/$i harbor.registry.com/k8s/$i
    docker push harbor.registry.com/k8s/$i
    docker rmi registry.aliyuncs.com/google_containers/$i
done

📌 注意 :Master 节点需要导入全部 镜像;Node 节点只需导入 kube-proxycorednspause 这三个镜像。

3.4 初始化主节点

此操作只在 k8s-master01 节点执行。

官方文档:https://kubernetes.io/zh-cn/docs/reference/setup-tools/kubeadm/kubeadm-init/#custom-image

bash 复制代码
kubeadm init --apiserver-advertise-address=192.168.194.11 \
--image-repository=registry.aliyuncs.com/google_containers \
--kubernetes-version=1.36.3 \
--service-cidr=10.10.0.0/12 \
--pod-network-cidr=10.244.0.0/16 \
--ignore-preflight-errors=all \
--cri-socket unix:///var/run/cri-dockerd.sock

⚠️ 红色(加粗)部分 --apiserver-advertise-address 改成你自己的 Master 节点 IP。

各参数含义:

参数 说明
--apiserver-advertise-address 指定 Master 主机的 IP 地址
--image-repository 指定控制平面镜像仓库(默认 registry.k8s.io),此处用阿里云镜像加速
--kubernetes-version=1.36.3 固定集群版本,避免自动升级导致兼容性问题
--service-cidr=10.10.0.0/12 指定 Service 资源的网络范围
--pod-network-cidr=10.244.0.0/16 指定 Pod 的网络范围,需与后面 Calico 配置保持一致
--ignore-preflight-errors=all 跳过所有前置检查错误
--cri-socket unix:///var/run/cri-dockerd.sock 指定 CRI 的 socket 路径(cri-docker 用这个;若用 containerd 则为 unix:///run/containerd/containerd.sock

命令执行后输出(关键日志):

bash 复制代码
[init] Using Kubernetes version: v1.36.3
[preflight] Running pre-flight checks
[preflight] Pulling images required for setting up a Kubernetes cluster
[preflight] This might take a minute or two, depending on the speed of your internet connection
[preflight] You can also perform this action beforehand using 'kubeadm config images pull'
W0727 10:53:18.622170    5240 checks.go:907] detected that the sandbox image "registry.k8s.io/pause:3.10.2" of the container runtime is inconsistent with that used by kubeadm. It is recommended to use "registry.aliyuncs.com/google_containers/pause:3.10.2" as the CRI sandbox image.
[certs] Using certificateDir folder "/etc/kubernetes/pki"
[certs] Generating "ca" certificate and key
[certs] Generating "apiserver" certificate and key
[certs] apiserver serving cert is signed for DNS names [k8s-master01 kubernetes kubernetes.default kubernetes.default.svc.cluster.local] and IPs [10.0.0.1 192.168.194.11]
[certs] Generating "apiserver-kubelet-client" certificate and key
[certs] Generating "front-proxy-ca" certificate and key
[certs] Generating "front-proxy-client" certificate and key
[certs] Generating "etcd/ca" certificate and key
[certs] Generating "etcd/server" certificate and key
[certs] etcd/server serving cert is signed for DNS names [k8s-master01 localhost] and IPs [192.168.194.11 127.0.0.1 ::1]
[certs] Generating "etcd/peer" certificate and key
[certs] etcd/peer serving cert is signed for DNS names [k8s-master01 localhost] and IPs [192.168.194.11 127.0.0.1 ::1]
[certs] Generating "etcd/healthcheck-client" certificate and key
[certs] Generating "apiserver-etcd-client" certificate and key
[certs] Generating "sa" key and public key
[kubeconfig] Using kubeconfig folder "/etc/kubernetes"
[kubeconfig] Writing "admin.conf" kubeconfig file
[kubeconfig] Writing "super-admin.conf" kubeconfig file
[kubeconfig] Writing "kubelet.conf" kubeconfig file
[kubeconfig] Writing "controller-manager.conf" kubeconfig file
[kubeconfig] Writing "scheduler.conf" kubeconfig file
[etcd] Creating static Pod manifest for local etcd in "/etc/kubernetes/manifests"
[control-plane] Using manifest folder "/etc/kubernetes/manifests"
[control-plane] Creating static Pod manifest for "kube-apiserver"
[control-plane] Creating static Pod manifest for "kube-controller-manager"
[control-plane] Creating static Pod manifest for "kube-scheduler"
[kubelet-start] Writing kubelet environment file with flags to file "/var/lib/kubelet/kubeadm-flags.env"
[kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/instance-config.yaml"
[patches] Applied patch of type "application/strategic-merge-patch+json" to target "kubeletconfiguration"
[kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml"
[kubelet-start] Starting the kubelet
[wait-control-plane] Waiting for the kubelet to boot up the control plane as static Pods from directory "/etc/kubernetes/manifests"
[kubelet-check] Waiting for a healthy kubelet at http://127.0.0.1:10248/healthz. This can take up to 4m0s
[kubelet-check] The kubelet is healthy after 539.349µs
[control-plane-check] Waiting for healthy control plane components. This can take up to 4m0s
[control-plane-check] Checking kube-apiserver at https://192.168.194.11:6443/livez
[control-plane-check] Checking kube-controller-manager at https://127.0.0.1:10257/healthz
[control-plane-check] Checking kube-scheduler at https://127.0.0.1:10259/livez
[control-plane-check] kube-scheduler is healthy after 3.451971ms
[control-plane-check] kube-controller-manager is healthy after 4.228132ms
[control-plane-check] kube-apiserver is healthy after 1.50375793s
[upload-config] Storing the configuration used in ConfigMap "kubeadm-config" in the "kube-system" Namespace
[kubelet] Creating a ConfigMap "kubelet-config" in namespace kube-system with the configuration for the kubelets in the cluster
[upload-certs] Skipping phase. Please see --upload-certs
[mark-control-plane] Marking the node k8s-master01 as control-plane by adding the labels: [node-role.kubernetes.io/control-plane node.kubernetes.io/exclude-from-external-load-balancers]
[mark-control-plane] Marking the node k8s-master01 as control-plane by adding the taints [node-role.kubernetes.io/control-plane:NoSchedule]
[bootstrap-token] Using token: bkey2t.b5mr4s5qvtimzbdi
[bootstrap-token] Configuring bootstrap tokens, cluster-info ConfigMap, RBAC Roles
[bootstrap-token] Configured RBAC rules to allow Node Bootstrap tokens to get nodes
[bootstrap-token] Configured RBAC rules to allow Node Bootstrap tokens to post CSRs in order for nodes to get long term certificate credentials
[bootstrap-token] Configured RBAC rules to allow the csrapprover controller automatically approve CSRs from a Node Bootstrap Token
[bootstrap-token] Configured RBAC rules to allow certificate rotation for all node client certificates in the cluster
[bootstrap-token] Configured RBAC rules to allow the API server kubelet client certificate to access the kubelet API
[bootstrap-token] Creating the "cluster-info" ConfigMap in the "kube-public" namespace
[kubelet-finalize] Updating "/etc/kubernetes/kubelet.conf" to point to a rotatable kubelet client certificate and key
[addons] Applied essential addon: CoreDNS
[addons] Applied essential addon: kube-proxy

Your Kubernetes control-plane has 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

Alternatively, if you are the root user, you can run:

  export KUBECONFIG=/etc/kubernetes/admin.conf

You should now deploy a pod network to the cluster.
Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
  https://kubernetes.io/docs/concepts/cluster-administration/addons/

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

kubeadm join 192.168.194.11:6443 --token bkey2t.b5mr4s5qvtimzbdi \
        --discovery-token-ca-cert-hash sha256:a65e6e61df6e25fa81ad15e88d4c531484779ceb1e840e7a86c77c110baaddd8

上面这段日志的核心解读 (便于理解 kubeadm init 到底做了什么):

  • 初始化阶段 [init]:声明使用的 K8s 版本。
  • 预检查阶段 [preflight]:检查系统环境是否满足要求(swap 关闭、内核版本、端口占用等),并拉取控制平面镜像。这里会有一条关于 pause 镜像不一致的告警,属正常提示,可忽略(不影响功能)。
  • 证书阶段 [certs]:在 /etc/kubernetes/pki 生成集群各类证书,包括 ca(根证书)、apiserveretcdfront-proxysa(Service Account)等。
  • kubeconfig 阶段 [kubeconfig]:生成 admin.confsuper-admin.confkubelet.confcontroller-manager.confscheduler.conf 等不同身份的访问配置文件。
  • 控制平面阶段 [control-plane]:生成 kube-apiserverkube-controller-managerkube-scheduleretcd静态 Pod(static Pod)清单 ,放在 /etc/kubernetes/manifests/,由 kubelet 自动拉起。
  • 健康检查 [control-plane-check]:等待 kube-apiserver 等控制平面组件就绪(分别探测 :6443/livez:10257/healthz:10259/livez)。
  • 控制面标签/污点 [mark-control-plane]:给 Master 打上 node-role.kubernetes.io/control-plane 标签,并添加 NoSchedule 污点,阻止普通 Pod 调度到 Master。
  • 引导令牌(bootstrap-token):生成加入集群需要的 token,并配置相关 RBAC 规则。
  • 附加组件:部署 CoreDNS(集群 DNS,负责服务发现)和 kube-proxy(负责网络代理与负载均衡)。

最关键的初始化成功标志 是打印出 Your Kubernetes control-plane has initialized successfully!,以及末尾的 kubeadm join ... 命令(用于工作节点加入)。

3.5 创建 .kube 目录

根据提示,在 Master 家目录下创建 .kube 目录,并将 admin.conf 复制过去:

bash 复制代码
[root@k8s-master01 ~]# mkdir -p $HOME/.kube
[root@k8s-master01 ~]# sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
[root@k8s-master01 ~]# sudo chown $(id -u):$(id -g) $HOME/.kube/config

执行后查看节点:

bash 复制代码
[root@k8s-master01 ~]# kubectl get node
NAME           STATUS     ROLES           AGE   VERSION
k8s-master01   NotReady   control-plane   75s   v1.36.3

可以看到集群已出现一个节点(此时还是 NotReady,因为还没装网络插件,属正常)。

3.6 增加工作节点

在 Node01 和 Node02 上分别执行上面的 kubeadm join 命令加入集群。

Node01 节点:

bash 复制代码
[root@k8s-node01 ~]# kubeadm join 192.168.194.11:6443 --token bkey2t.b5mr4s5qvtimzbdi \
        --discovery-token-ca-cert-hash sha256:a65e6e61df6e25fa81ad15e88d4c531484779ceb1e840e7a86c77c110baaddd8 \
        --cri-socket unix:///var/run/cri-dockerd.sock
[preflight] Running pre-flight checks
[preflight] Reading configuration from the "kubeadm-config" ConfigMap in namespace "kube-system"...
[preflight] Use 'kubeadm init phase upload-config kubeadm --config your-config-file' to re-upload it.
W0727 10:57:08.683757    4627 utils.go:69] The recommended value for "bindAddress" in "KubeProxyConfiguration" is: ::; the provided value is: 0.0.0.0
[kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/instance-config.yaml"
[patches] Applied patch of type "application/strategic-merge-patch+json" to target "kubeletconfiguration"
[kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml"
[kubelet-start] Writing kubelet environment file with flags to file "/var/lib/kubelet/kubeadm-flags.env"
[kubelet-start] Starting the kubelet
[kubelet-check] Waiting for a healthy kubelet at http://127.0.0.1:10248/healthz. This can take up to 4m0s
[kubelet-check] The kubelet is healthy after 501.014458ms
[kubelet-start] Waiting for the kubelet to perform the TLS Bootstrap

This node has joined the cluster:
* Certificate signing request was sent to apiserver and a response was received.
* The Kubelet was informed of the new secure connection details.

Run 'kubectl get nodes' on the control-plane to see this node join the cluster.

⚠️ 末尾的 --cri-socket unix:///var/run/cri-dockerd.sock 必须加,因为底层容器运行时是 Docker(cri-docker);如果用的是原生 containerd 就不需要加这段。

Node02 节点:

bash 复制代码
[root@k8s-node02 ~]# kubeadm join 192.168.194.11:6443 --token bkey2t.b5mr4s5qvtimzbdi \
        --discovery-token-ca-cert-hash sha256:a65e6e61df6e25fa81ad15e88d4c531484779ceb1e840e7a86c77c110baaddd8 \
        --cri-socket unix:///var/run/cri-dockerd.sock
[preflight] Running pre-flight checks
[preflight] Reading configuration from the "kubeadm-config" ConfigMap in namespace "kube-system"...
[preflight] Use 'kubeadm init phase upload-config kubeadm --config your-config-file' to re-upload it.
W0727 10:57:12.008529    4626 utils.go:69] The recommended value for "bindAddress" in "KubeProxyConfiguration" is: ::; the provided value is: 0.0.0.0
[kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/instance-config.yaml"
[patches] Applied patch of type "application/strategic-merge-patch+jst" to target "kubeletconfiguration"
[kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml"
[kubelet-start] Writing kubelet environment file with flags to file "/var/lib/kubelet/kubeadm-flags.env"
[kubelet-start] Starting the kubelet
[kubelet-check] Waiting for a healthy kubelet at http://127.0.0.1:10248/healthz. This can take up to 4m0s
[kubelet-check] The kubelet is healthy after 500.884942ms
[kubelet-start] Waiting for the kubelet to perform the TLS Bootstrap

This node has joined the cluster:
* Certificate signing request was sent to apiserver and a response was received.
* The Kubelet was informed of the new secure connection details.

Run 'kubectl get nodes' on the control-plane to see this node join the cluster.

join 命令参数解析:

参数 说明
kubeadm join <masterIP>:6443 指定 Master 节点 IP 和 API 端口
--token 引导令牌;若过期,用 kubeadm token create --print-join-command 重新生成
--discovery-token-ca-cert-hash 用于验证 Master 证书的哈希值
--cri-socket unix:///var/run/cri-dockerd.sock 指定 CRI 的 socket 路径

3.7 查看集群状态

在 Master 上查看当前集群节点:

bash 复制代码
[root@k8s-master01 ~]# kubectl get node
NAME           STATUS     ROLES           AGE     VERSION
k8s-master01   NotReady   control-plane   4m21s   v1.36.3
k8s-node01     NotReady   <none>          52s     v1.36.3
k8s-node02     NotReady   <none>          49s     v1.36.3

三个节点都已创建,但都处于 NotReady 状态------因为还没装网络插件(CNI)。接下来部署 Calico。


四、部署 Calico 网络插件

Kubernetes 集群要正常工作,所有容器必须工作在一个扁平的网络空间里,这就需要部署网络插件(CNI)。下面用 Calico 实现,只需在 Master 节点安装即可。

4.1 下载配置文件

浏览器打开 Calico 官方安装文档:

text 复制代码
https://docs.tigera.io/calico/latest/getting-started/kubernetes/self-managed-onprem/onpremises#install-calico

点击 Manifest 下的 "Install Calico with Kubernetes API datastore, more than 50 nodes"

复制命令将配置文件下载到本地:

bash 复制代码
# 1. 下载配置文件
[root@k8s-master01 ~]# curl https://raw.githubusercontent.com/projectcalico/calico/v3.32.1/manifests/calico-typha.yaml -o calico.yaml
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  347k  100  347k    0     0    99k      0  0:00:03  0:00:03 --:--:--   99k

查看是否下载成功:

bash 复制代码
[root@k8s-master01 ~]# ls
calico.yaml  etcd_3.6.8.tar.gz  k8s-images_v1.36.3.tar.gz

查看所需镜像:

bash 复制代码
[root@k8s-master01 ~]# grep image calico.yaml
          image: quay.io/calico/cni:v3.32.1
          imagePullPolicy: IfNotPresent
          image: quay.io/calico/cni:v3.32.1
          imagePullPolicy: IfNotPresent
          image: quay.io/calico/node:v3.32.1
          imagePullPolicy: IfNotPresent
          image: quay.io/calico/node:v3.32.1
          imagePullPolicy: IfNotPresent
          image: quay.io/calico/kube-controllers:v3.32.1
          imagePullPolicy: IfNotPresent
        - image: quay.io/calico/typha:v3.32.1
          imagePullPolicy: IfNotPresent

4.2 修改配置文件

calico.yaml 中的 CALICO_IPV4POOL_CIDR 改为初始化集群时 --pod-network-cidr=10.244.0.0/16 指定的地址,并把 CALICO_IPV4POOL_IPIPCALICO_IPV4POOL_VXLAN 都关闭(使用 BGP 模式):

bash 复制代码
[root@k8s-master01 ~]# vim calico.yaml
kind: DaemonSet
# Enable IPIP
- name: CALICO_IPV4POOL_IPIP
#  value: "Always"
  value: "Off"      # 这里 Always 改为 Off
# Enable or Disable VXLAN on the default IP pool.
- name: CALICO_IPV4POOL_VXLAN
  value: "Never"
# Enable or Disable VXLAN on the default IPv6 IP pool.
- name: CALICO_IPV6POOL_VXLAN
  value: "Never"

# no effect. This should fall within `--cluster-cidr`
# - name: CALICO_IPV4POOL_CIDR
#   value: "192.168.0.0/16"
- name: CALICO_IPV4POOL_CIDR
  value: "10.244.0.0/16"       # 将 192.168.0.0/16 改为 10.244.0.0/16

# Disable file logging so `kubectl logs` works.

修改完成后保存退出。

4.3 上传镜像文件

由于 Calico 镜像在国内很难下载,可提前下载好并上传到网盘,再从网盘下载到本地导入集群中。注意:三个节点都需要导入。 需要导入的镜像 calico-images_v3.32.1.tar.gz 分别放在三个节点的家目录:

bash 复制代码
# 1. master01 节点
[root@k8s-master01 ~]# ls
anaconda-ks.cfg  calico-images_v3.32.1.tar.gz  calico.yaml

# 2. node01 节点
[root@k8s-node01 ~]# ls
anaconda-ks.cfg  calico-images_v3.32.1.tar.gz

# 3. node02 节点
[root@k8s-node02 ~]# ls
anaconda-ks.cfg  calico-images_v3.32.1.tar.gz

4.4 导入镜像文件

在三个节点上分别通过 docker load 导入 Calico 镜像:

bash 复制代码
# 1. master01 节点
[root@k8s-master01 ~]# docker load -i calico-images_v3.32.1.tar.gz
37c1775e0302: Loading layer [==================================================>]  3.693MB/3.693MB
9ad9e3f4f50f: Loading layer [==================================================>]  78.75MB/78.75MB
Loaded image: calico/kube-controllers:v3.32.1
f3dac1398271: Loading layer [==================================================>]  70.16MB/70.16MB
Loaded image: calico/typha:v3.32.1
ac1e39af291c: Loading layer [==================================================>]  216.1MB/216.1MB
5f70bf18a086: Loading layer [==================================================>]  1.024kB/1.024kB
Loaded image: calico/cni:v3.32.1
55c8cc0817d5: Loading layer [==================================================>]  380.4MB/380.4MB
Loaded image: calico/node:v3.32.1

# 2. node01 节点
[root@k8s-node01 ~]# docker load -i calico-images_v3.32.1.tar.gz
37c1775e0302: Loading layer [==================================================>]  3.693MB/3.693MB
9ad9e3f4f50f: Loading layer [==================================================>]  78.75MB/78.75MB
Loaded image: calico/kube-controllers:v3.32.1
f3dac1398271: Loading layer [==================================================>]  70.16MB/70.16MB
Loaded image: calico/typha:v3.32.1
ac1e39af291c: Loading layer [==================================================>]  216.1MB/216.1MB
5f70bf18a086: Loading layer [==================================================>]  1.024kB/1.024kB
Loaded image: calico/cni:v3.32.1
55c8cc0817d5: Loading layer [==================================================>]  380.4MB/380.4MB
Loaded image: calico/node:v3.32.1

# 3. node02 节点
[root@k8s-node02 ~]# docker load -i calico-images_v3.32.1.tar.gz
37c1775e0302: Loading layer [==================================================>]  3.693MB/3.693MB
9ad9e3f4f50f: Loading layer [==================================================>]  78.75MB/78.75MB
Loaded image: calico/kube-controllers:v3.32.1
f3dac1398271: Loading layer [==================================================>]  70.16MB/70.16MB
Loaded image: calico/typha:v3.32.1
ac1e39af291c: Loading layer [==================================================>]  216.1MB/216.1MB
5f70bf18a086: Loading layer [==================================================>]  1.024kB/1.024kB
Loaded image: calico/cni:v3.32.1
55c8cc0817d5: Loading layer [==================================================>]  380.4MB/380.4MB
Loaded image: calico/node:v3.32.1

4.5 安装网络插件

在 master01 节点上执行:

bash 复制代码
[root@k8s-master01 ~]# kubectl apply -f calico.yaml
poddisruptionbudget.policy/calico-kube-controllers created
poddisruptionbudget.policy/calico-typha created
serviceaccount/calico-kube-controllers created
serviceaccount/calico-node created
serviceaccount/calico-cni-plugin created
configmap/calico-config created
customresourcedefinition.apiextensions.k8s.io/bgpconfigurations.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/bgpfilters.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/bgppeers.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/blockaffinities.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/caliconodestatuses.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/clusterinformations.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/felixconfigurations.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/globalnetworkpolicies.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/globalnetworksets.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/hostendpoints.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/ipamblocks.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/ipamconfigs.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/ipamhandles.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/ippools.crd.projectcalico.org created
Warning: unrecognized format "cidr"
customresourcedefinition.apiextensions.k8s.io/ipreservations.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/kubecontrollersconfigurations.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/networkpolicies.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/networksets.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/stagedglobalnetworkpolicies.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/stagedkubernetesnetworkpolicies.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/stagednetworkpolicies.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/tiers.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/clusternetworkpolicies.policy.networking.k8s.io created
clusterrole.rbac.authorization.k8s.io/calico-kube-controllers created
clusterrole.rbac.authorization.k8s.io/calico-node created
clusterrole.rbac.authorization.k8s.io/calico-cni-plugin created
clusterrole.rbac.authorization.k8s.io/calico-tier-getter created
clusterrolebinding.rbac.authorization.k8s.io/calico-kube-controllers created
clusterrolebinding.rbac.authorization.k8s.io/calico-node created
clusterrolebinding.rbac.authorization.k8s.io/calico-cni-plugin created
clusterrolebinding.rbac.authorization.k8s.io/calico-tier-getter created
service/calico-typha created
daemonset.apps/calico-node created
deployment.apps/calico-kube-controllers created
deployment.apps/calico-typha created

五、验证集群

安装完成后,查看当前集群状态:

bash 复制代码
[root@k8s-master01 ~]# kubectl get node
NAME           STATUS   ROLES           AGE    VERSION
k8s-master01   Ready    control-plane   3h27m  v1.36.3
k8s-node01     Ready    <none>          3h14m  v1.36.3
k8s-node02     Ready    <none>          3h14m  v1.36.3

此时三个节点全部处于 Ready 状态,说明集群搭建成功!🎉

可以再执行 kubectl get pods -A 查看系统组件运行情况:

bash 复制代码
kubectl get pods -A

看到 kube-system 命名空间下的 corednscalicokube-proxy 等 Pod 都处于 Running 状态,即可确认集群完全可用。


常见坑点与注意事项

  1. 容器运行时二选一 :cri-docker 和 containerd 不要同时安装,否则会冲突。
  2. cri-socket 参数 :初始化/加入集群时,若用 cri-docker 必须带 --cri-socket unix:///var/run/cri-dockerd.sock;用 containerd 则用 unix:///run/containerd/containerd.sock
  3. Pod 网段一致kubeadm init--pod-network-cidr(如 10.244.0.0/16)必须与 Calico 配置里的 CALICO_IPV4POOL_CIDR 保持一致,否则网络异常。
  4. Aliyun/sandbox 镜像告警可忽略 :初始化时那条 pause 镜像不一致的 W 告警,不影响功能,可忽略。
  5. 节点 NotReady :若 join 后节点仍 NotReady,通常是网络插件未部署或镜像未导入,检查 kubectl get pods -A
  6. kubelet 启动失败属正常 :安装阶段 systemctl enable kubelet 后 kubelet 会因为缺配置报错,kubeadm init 会补齐,属正常现象。
  7. 网关/网段按实际修改 :文中的 IP 网段 192.168.194.x、网关、hosts 映射都是示例,务必改成你环境的真实值。
相关推荐
starzy19902 小时前
K8s 集群容器管理工具选型与实战指南
云原生·容器
养海绵宝宝的小蜗2 小时前
K8S总结
云原生·容器·kubernetes
Yiiz.2 小时前
Kubernetes部署
云原生·容器·kubernetes
qizhideyu3 小时前
kubernetes
云原生·容器·kubernetes
Csxyzj3 小时前
kubernetes集群部署方法
java·linux·kubernetes
wish3663 小时前
K8S 免镜像部署:通过 API 上传文件动态创建服务(以 Ollama 为例)
人工智能·云原生·容器·kubernetes·local llm
小小克3 小时前
k8s集群部署的方法
云原生·容器·kubernetes
kobeyyl3 小时前
K8s集群部署核心原理与主流部署方法全解析
云原生·容器·kubernetes
艾伦_耶格宇3 小时前
【DOCKER容器实战】-3 ElasticSearch + Kibana
elasticsearch·docker·jenkins