kubeadm安装K8s实现底层走Docker:cri-dockerd桥接方案实战 之前帮业务团队搭建测试集群时客户方的需求很明确用 kubeadm 安装 Kubernetes底层的容器运行时仍然保持 Docker 的使用习惯。这个需求看起来简单实际操作时却有一个绕不开的坎——Kubernetes 从 1.24 版本开始已经正式移除了 dockershimkubelet 无法再直接调用 Docker 来管理容器。网上不少教程还在沿用旧版流程直接照抄很容易出现 kubelet 起不来、容器运行时连接失败、Node 一直是 NotReady 的情况。本文基于最近落地的一套完整流程把kubeadm 安装 k8s、底层使用 Docker 运行时的方案拆开来讲重点说明新旧版本的差异、cri-dockerd 的桥接作用、完整的安装命令、初始化配置和常见排错思路。本文适合两类读者一类是刚开始接触 Kubernetes、想先跑通一套 kubeadm 集群的初学者另一类是在公司内部维护运维平台、需要保留 Docker 工作流但又要跟上新版 k8s 的开发者。跟着文章走完你会得到一套可复用的安装方案并且能理解“k8s 底层走 docker”这句话在现代版本中到底意味着什么而不是简单在命令行里敲几个安装命令就算完事。1. 先把概念理清楚k8s、kubeadm、docker、cri-dockerd 分别是什么1.1 Kubernetes 与 kubeadm 的分工Kubernetes 本身是一个容器编排平台它负责容器的调度、伸缩、服务发现、滚动更新等能力但 Kubernetes 不直接运行容器。真正运行容器的组件是 kubelet 和容器运行时。kubelet 是运行在每个节点上的代理进程它负责接收控制平面下发的 Pod 定义再调用容器运行时去创建、删除和管理容器。kubeadm 则是 Kubernetes 官方提供的集群初始化工具它把安装集群过程中比较复杂的步骤封装了起来例如生成证书、启动 etcd、引导 kubelet、创建控制平面组件等。使用 kubeadm 的好处是安装过程更标准化官方升级工具也是基于 kubeadm 这套机制设计的。这里要强调一个容易混淆的点kubeadm 不是容器运行时它只是帮助初始化集群的工具。集群底层使用 Docker 还是 containerd 作为运行时取决于节点上安装的容器运行时以及 kubelet 的配置。1.2 dockershim 被移除后“k8s 底层走 docker”要靠 cri-dockerd 实现很多用 Kubernetes 比较早的开发者都熟悉一种情况节点上装好 Dockerkubelet 通过内置的 dockershim 模块直接调用 Docker 来创建和管理容器。这是 Kubernetes 1.23 及以前版本常见的运行方式。但从 1.24 版本开始Kubernetes 移除了内置的 dockershim。原因很简单Docker 本身实现的并不是 Kubernetes 的 CRIContainer Runtime Interface接口。早期 k8s 是通过一个额外模块把 Docker 的 API 翻译成 CRI 调用这个模块就是 dockershim。dockershim 被移除之后如果仍然想用 Docker 作为 kubelet 的运行时就必须由外部项目提供同样的适配能力。这个替代项目就是cri-dockerd。简单理解cri-dockerd 是一个适配器服务它对外提供 CRI 接口对内调用 Docker Engine 的 API 来管理容器。安装完 Docker 之后再安装并启动 cri-dockerdkubelet 就能通过 cri-dockerd 与 Docker 通信实现“底层走 docker 容器”的效果。1.3 本文采用的整体架构在本文的方案中每个节点上的组件分工如下组件作用Docker Engine真正负责容器的创建、启动、停止等底层操作cri-dockerd把 Docker Engine 的接口适配为 Kubernetes CRI 接口kubelet接收控制平面指令通过 cri-dockerd 管理本节点容器kubeadm初始化集群、生成证书、引导控制平面组件启动containerd/shime本文不额外安装Docker 自带的 containerd 作为容器底层实现需要注意的是Docker 内部本身也依赖 containerd。安装了 Docker 之后系统里会多出一个 containerd 进程但这不是给 kubelet 直接用的运行时而是 Docker Engine 管理容器的底层依赖。kubelet 的通信目标始终是 cri-dockerd再由 cri-dockerd 交给 Docker。1.4 为什么仍然有人坚持用 Docker 作为运行时从资源占用和官方推荐的角度来说新集群默认使用 containerd 更省内存因为 containerd 比完整 Docker Engine 更轻量也少了一层适配。但在实际企业场景中仍然有不少团队希望保留 Docker 工作流。常见原因包括已有大量脚本、CI/CD 流程依赖docker build、docker run、docker exec等命令。团队成员对容器排障更熟悉 Docker 命令不希望切换到crictl和nerdctl。部分业务侧工具依赖 Docker 生成的日志目录、网络模式或容器配置。如果你的团队也属于这种情况那么本文这套“kubeadm Docker cri-dockerd”的安装方案就比较适合作为内部基础环境的搭建参考。2. 环境准备与版本规划2.1 服务器要求与 IP 规划本文示例以 Ubuntu 22.04 LTS 为主使用三台服务器组成一个最小的高可用前置测试集群一台作为 Master 控制平面节点两台作为 Worker 工作节点。如果你只是学习验证也可以用一台服务器同时扮演 Master 和 Worker但生产环境不建议这样使用控制平面节点和工作负载节点混部会带来资源竞争和故障隔离问题。建议的硬件配置如下角色配置建议数量Master 节点2核 CPU、4G 内存、40G 磁盘1Worker 节点2核 CPU、4G 内存、50G 磁盘2IP 规划示例主机名IP 地址角色master01192.168.100.10Masterworker01192.168.100.11Workerworker02192.168.100.12Worker2.2 关于标题中“1.37.x”版本编号的说明先回答标题里“最新 k8s 1.37.x”这个版本号的问题。标题中写到的 1.37.x 是一个较新的版本号概念在实际社区中Kubernetes 的版本迭代频率大约是每年三个大版本因此不能简单把某个数字当作永久的最新版。在实际安装时我建议不要盲目追最特殊的数字而是以 kubeadm 官方维护的稳定版本为准。你可以先执行下面的命令查看当前环境下 kubeadm 所支持或当前的版本信息。kubeadm version kubeadm config images list在这篇文章中所有命令都不依赖某个精确到小版本的编号而是用v1.xx.x占位符表示你需要确认的版本。安装思路和配置项在较新的几个 Kubernetes 版本中是通用的但不同版本之间可能存在 API 字段的细微变化因此你需要在真正执行命令前确认目标版本。2.3 操作系统基础配置每台服务器都需要执行下面的基础配置。首先修改主机名并写入 /etc/hosts保证节点之间可以通过主机名互相访问。# 在 master01 上执行 hostnamectl set-hostname master01 # 在 worker01 上执行 hostnamectl set-hostname worker01 # 在 worker02 上执行 hostnamectl set-hostname worker02编辑 /etc/hosts把三台机器的解析记录写进去。cat /etc/hosts EOF 192.168.100.10 master01 192.168.100.11 worker01 192.168.100.12 worker02 EOF接下来关闭 swap。Kubernetes 对 swap 的支持一直比较谨慎虽然新版本引入了部分 swap 配置能力但为了避免 kubelet 资源统计和 Pod 配额计算出现意外官方在常规集群安装流程中仍然建议关闭 swap。swapoff -a sed -ri s/.*swap.*/#/ /etc/fstab同时需要加载一些内核模块并调整网络相关参数让 iptables 能正确转发网桥流量。创建一个内核模块加载配置文件。cat /etc/modules-load.d/k8s.conf EOF overlay br_netfilter EOF systemctl restart systemd-modules-load然后配置 sysctl 参数。cat /etc/sysctl.d/k8s.conf EOF net.bridge.bridge-nf-call-iptables 1 net.bridge.bridge-nf-call-ip6tables 1 net.ipv4.ip_forward 1 EOF sysctl --system检查模块是否加载成功。lsmod | grep br_netfilter lsmod | grep overlay如果输出中能看到这两个模块说明内核配置已经生效。2.4 关于发行版的兼容性提示本文以 Ubuntu 22.04 LTS 为例Docker 官方仓库和阿里云 Kubernetes 软件源都提供对应的 apt 源。如果你使用的是 Rocky Linux 9 或 CentOS Stream安装思路基本相同只是软件源配置和包管理命令需要调整。例如 k8s 软件源在 CentOS 系上会使用yum-config-manager --add-repo或直接写入/etc/yum.repos.d/kubernetes.repo。具体命令差异我会在相关小节里简单提示。3. 安装 Docker 与 cri-dockerd3.1 安装 Docker Engine在每台节点上安装 Docker。先安装依赖包并配置 Docker 官方 apt 源。apt-get update apt-get install -y ca-certificates curl gnupg导入 Docker 官方 GPG 密钥。install -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg chmod ar /etc/apt/keyrings/docker.gpg添加 Docker apt 源。不同系统架构下源地址中的[archamd64]需要根据实际情况调整。echo deb [arch$(dpkg --print-architecture) signed-by/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable /etc/apt/sources.list.d/docker.list安装 Docker Engine 及相关组件。apt-get update apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin启动 Docker 并设置开机自启。systemctl enable --now docker docker version看到 Client 和 Server 两部分版本信息说明 Docker 安装成功。3.2 配置 Docker 镜像加速国内使用 Docker 拉取镜像时经常遇到超时或速度慢的问题建议在 /etc/docker/daemon.json 中配置 registry mirror。不同网络环境下合适的镜像加速地址不同你可以根据你所在环境选择可靠的加速服务也可以在内网部署一个 Harbor 或 Docker Registry 作为镜像缓存这样团队拉镜像更快也更安全。{ registry-mirrors: [ https://docker.mirrors.example.com ], exec-opts: [native.cgroupdriversystemd], log-driver: json-file, log-opts: { max-size: 100m, max-file: 3 } }配置好之后重启 Docker。systemctl daemon-reload systemctl restart docker这里exec-opts把 Docker 的 cgroup driver 设为 systemd与后面 kubelet 和 cri-dockerd 的配置保持一致避免出现 cgroup driver 不匹配的报错。日志限制配置也很重要容器日志如果不做轮转时间长了会占满磁盘。3.3 安装并配置 cri-dockerd安装 cri-dockerd 是“k8s 底层走 docker”的关键一步。cri-dockerd 的发布包可以在其 GitHub Releases 页面找到下载时需要选择与你 Kubernetes 版本匹配的版本。下面给出通用安装思路示例中使用变量表示版本号你需要根据实际 release 中的文件名调整。# 下载 cri-dockerd 压缩包 wget https://github.com/Mirantis/cri-dockerd/releases/download/v${CRI_DOCKERD_VERSION}/cri-dockerd-${CRI_DOCKERD_VERSION}.amd64.tgz # 解压 tar zxvf cri-dockerd-${CRI_DOCKERD_VERSION}.amd64.tgz # 拷贝到系统目录 cp cri-dockerd/cri-dockerd /usr/local/bin/然后创建 systemd 服务文件。cri-dockerd 需要依赖 docker.service并且通过 CRI 接口的 socket 与 kubelet 通信。创建一个服务文件 /etc/systemd/system/cri-docker.service。[Unit] DescriptionCRI Interface for Docker Application Container Engine Documentationhttps://docs.mirantis.com Afterdocker.service Requiresdocker.service [Service] Typenotify ExecStart/usr/local/bin/cri-dockerd --container-runtime-endpoint fd:// Restarton-failure RestartSec5 [Install] WantedBymulti-user.target另外还需要启用 socket 监听文件可以创建 /etc/systemd/system/cri-docker.socket不过多数场景下上面的 service 文件已经足够。保存后重新加载 systemd 并启动服务。systemctl daemon-reload systemctl enable --now cri-docker systemctl status cri-docker如果服务处于 activerunning状态说明 cri-dockerd 启动成功。检查 socket 文件是否存在。ls -l /var/run/cri-dockerd.sock这一步非常重要。kubeadm 初始化时如果找不到这个 socket会直接报错。后面的 kubeadm 配置中需要把criSocket指向/var/run/cri-dockerd.sock。3.4 验证容器运行时链路在继续安装 k8s 组件之前可以先用一个简单方式验证 Docker 和 cri-dockerd 的链路是否正常。运行一个测试容器看看 Docker 是否能正常创建容器进程。docker run --rm hello-world如果能看到 Hello from Docker! 的输出说明 Docker Engine 正常工作。cri-dockerd 的验证可以稍后在 kubeadm 初始化时观察只要 socket 文件存在且服务不退出一般就没有问题。4. 安装 kubeadm、kubelet、kubectl4.1 配置 Kubernetes 软件源三台节点都需要安装 kubeadm、kubelet 和 kubectl。由于 Kubernetes 官方的软件源在国内访问不稳定这里以阿里云镜像源为例。Ubuntu 系统执行如下命令配置 apt 源。apt-get update apt-get install -y apt-transport-https ca-certificates curl gpg下载并导入 Kubernetes 软件源的 GPG 密钥。curl -fsSL https://mirrors.aliyun.com/kubernetes/apt/doc/apt-key.gpg | gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg写入 apt 源。echo deb [signed-by/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://mirrors.aliyun.com/kubernetes/apt/ kubernetes-xenial main /etc/apt/sources.list.d/kubernetes.list如果你使用 Rocky Linux 或 CentOS Stream可以在 /etc/yum.repos.d/kubernetes.repo 中配置[kubernetes] nameKubernetes baseurlhttps://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64/ enabled1 gpgcheck04.2 安装 kubeadm、kubelet、kubectl更新软件源后安装这三个组件。这里不锁定具体小版本但在生产环境强烈建议固定版本避免升级导致组件版本不一致。apt-get update apt-get install -y kubelet kubeadm kubectl安装完成后使用apt-mark hold锁定版本防止误升级。apt-mark hold kubelet kubeadm kubectl检查安装结果。kubeadm version kubectl version --client kubelet --version这三个组件的版本必须保持一致。如果出现版本不一致kubeadm 初始化时可能提示版本不匹配需要手工调整。4.3 设置 kubelet 开机自启先设置 kubelet 开机自启但不要现在启动因为 kubelet 在加入集群之前会不断重启报错属于正常现象。systemctl enable kubelet此时如果查看 kubelet 状态很可能是 activeexiting或 failed原因是缺少集群配置。等 kubeadm init 完成后kubelet 才能获得正确配置。5. 初始化 Master 控制平面节点5.1 准备 kubeadm 初始化配置文件在 master01 上执行初始化操作。推荐使用配置文件而不是纯命令行参数因为配置文件便于查看和修改也能更好地指定 criSocket、Pod 网段等关键参数。创建 /etc/kubernetes/kubeadm-init.yamlapiVersion: kubeadm.k8s.io/v1beta3 kind: InitConfiguration localAPIEndpoint: advertiseAddress: 192.168.100.10 bindPort: 6443 nodeRegistration: criSocket: unix:///var/run/cri-dockerd.sock name: master01 --- apiVersion: kubeadm.k8s.io/v1beta3 kind: ClusterConfiguration kubernetesVersion: v1.xx.x controlPlaneEndpoint: 192.168.100.10:6443 networking: podSubnet: 10.244.0.0/16 serviceSubnet: 10.96.0.0/12 --- apiVersion: kubelet.config.k8s.io/v1beta1 kind: KubeletConfiguration cgroupDriver: systemd解释几个关键字段advertiseAddressMaster 节点对外提供 API 服务的地址就是 master01 的 IP。criSocket指定 kubelet 使用的 CRI socket这里是 cri-dockerd 的 socket 路径。podSubnetPod 网段不同 CNI 插件有固定要求。本文使用 Flannel所以设置为 10.244.0.0/16。serviceSubnetService 网段保持默认即可。cgroupDriver必须和 Docker 的 cgroup driver 保持一致统一使用 systemd。如果你的 kubeadm 版本较新初始化时可能会提示apiVersion过期或需要迁移可以先用kubeadm config migrate将老配置迁移到新版本格式。5.2 拉取所需镜像kubeadm init 会自动拉取控制平面组件镜像。如果镜像拉取失败可以先手动查看需要哪些镜像再决定是否配置镜像加速或使用镜像仓库代理。kubeadm config images list --config /etc/kubernetes/kubeadm-init.yaml查看列表后可以手动拉取所有必选镜像。kubeadm config images pull --config /etc/kubernetes/kubeadm-init.yaml如果这一步出现超时说明镜像源或网络有问题。在国内常见做法是提前在节点上配置好 Docker registry mirror或者用代理仓库中转然后再执行 pull。5.3 执行 kubeadm init确认镜像拉取正常后执行初始化。kubeadm init --config /etc/kubernetes/kubeadm-init.yaml --upload-certs初始化过程需要几分钟时间。看到类似下面的输出说明初始化成功Your Kubernetes control-plane has initialized successfully!同时kubeadm 会输出一段 join 命令形如kubeadm join 192.168.100.10:6443 --token xxx --discovery-token-ca-cert-hash sha256:xxx这段命令需要保存下来后面 Worker 节点加入集群时要用。如果当时没有保存可以后续在 master01 上重新生成。5.4 配置 kubectl初始化成功后root 用户需要执行以下命令让 kubectl 能找到集群配置文件。export KUBECONFIG/etc/kubernetes/admin.conf为了长期使用可以把这段配置写入当前用户的环境变量文件。echo export KUBECONFIG/etc/kubernetes/admin.conf ~/.bashrc source ~/.bashrc如果使用普通用户操作 kubectl需要把 admin.conf 拷贝到用户目录下并调整权限。mkdir -p $HOME/.kube sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config sudo chown $(id -u):$(id -g) $HOME/.kube/config5.5 安装 Pod 网络插件CNIMaster 节点初始化完成后集群内部网络还是不通的需要安装 CNI 插件。本文使用 Flannel因为它配置简单适合测试和学习场景。kubectl apply -f https://raw.githubusercontent.com/flannel-io/flannel/master/Documentation/kube-flannel.yml如果你的网络环境无法访问 GitHub需要先把 kube-flannel.yml 下载并传到服务器上或者在内网搭建一个元数据代理服务器。等待一段时间后检查 Pod 状态。kubectl get pods -n kube-flannelFlannel 的 Pod 处于 Running 状态后再查看节点状态。kubectl get nodes如果 master01 的状态为 Ready说明 Master 节点初始化完成。5.6 验证控制平面组件控制平面核心组件运行在 kube-system 命名空间中包含 etcd、kube-apiserver、kube-controller-manager、kube-scheduler 和 kube-proxy。kubectl get pods -n kube-system -o wide正常状态下这些 Pod 都应该处于 Running 或 Completed 状态。如果某些 Pod 一直 ContainerCreating需要重点排查镜像拉取是否成功、CNI 是否安装完成。6. 将 Worker 节点加入集群6.1 准备 Worker 节点环境Worker 节点需要完成与 Master 节点相同的准备工作包括修改主机名和 /etc/hosts。关闭 swap。加载内核模块并设置 sysctl。安装 Docker、cri-dockerd、kubeadm、kubelet、kubectl。如果已经按前面章节把 worker01 和 worker02 准备好了就不用重复执行。6.2 获取并执行 join 命令在 master01 上执行下面的命令重新生成 join 命令。kubeadm token create --print-join-command输出类似kubeadm join 192.168.100.10:6443 --token xxxxx --discovery-token-ca-cert-hash sha256:xxxxx在 worker01 和 worker02 上分别执行这段命令。需要注意的是旧版本的 join 命令在 kubeadm 1.24 之后是可选的新版本默认会通过 API 获取集群信息但--discovery-token-ca-cert-hash参数仍然建议加上提高安全校验强度。由于我们使用 cri-dockerd 作为运行时join 时还需要指定--cri-socket参数否则 kubeadm 会尝试自动探测运行时有可能选到 containerd 而报错。kubeadm join 192.168.100.10:6443 \ --token xxxxx \ --discovery-token-ca-cert-hash sha256:xxxxx \ --cri-socket unix:///var/run/cri-dockerd.sock如果 join 成功会看到类似输出This node has joined the cluster: * Certificate signing request was sent to apiserver * The Kubelet was informed of the new secure connection6.3 验证集群节点状态在 master01 上查看节点。kubectl get nodes -o wide正常情况下master01、worker01、worker02 都应该是 Ready 状态VERSION 列保持一致INTERNAL-IP 分别是对应的三个 IP。如果某个节点始终是 NotReady先在该节点上检查 kubelet 日志。journalctl -u kubelet -f同时检查该节点的 Pod 网络是否正常kubectl get pods -n kube-system -o wide | grep node-name大多数 NotReady 问题的根源是 Flannel Pod 没起来而不是节点本身有问题。7. 常见问题与排查思路7.1 kubeadm init 拉取镜像失败现象执行kubeadm config images pull时长时间卡住或超时。原因国内网络访问 gcr.io、registry.k8s.io 不稳定。解决思路给 Docker 配置可用的 registry mirror。使用镜像仓库中转或把镜像列表导出后在可访问外网的机器拉取再导入。如果使用阿里云等厂商的镜像源按镜像列表重打 tag。7.2 kubelet 一直处于 failed 状态现象systemctl status kubelet显示 failed日志中反复出现连接 apiserver 失败。原因kubelet 在加入集群前没有配置文件不断尝试连接不存在的 apiserver属于正常现象。解决思路确认 kubelet 是否开机自启。确认 kubeadm init 是否成功。查看journalctl -u kubelet -f的具体内容。如果初始化后仍然 failed重点检查--cgroup-driver配置是否与 Docker 一致。7.3 cri-dockerd 报错或 socket 不存在现象kubeadm init 时提示找不到unix:///var/run/cri-dockerd.sock。原因cri-dockerd 没有启动或者服务文件配置错误。解决思路systemctl status cri-docker ls -l /var/run/cri-dockerd.sock如果服务没起来查看 journalctl 日志journalctl -u cri-docker -f常见错误是ExecStart路径写错或没有给/usr/local/bin/cri-dockerd执行权限。7.4 Node 一直是 NotReady现象kubectl get nodes显示 NotReady。原因CNI 插件未安装或 Flannel Pod 未正常运行。解决思路kubectl get pods -n kube-flannel -o wide如果 Flannel Pod 的 STATUS 是 ImagePullBackOff说明镜像拉取失败需要配置镜像加速或手动拉取。如果 Pod 一直 CrashLoopBackOff查看日志。kubectl logs -n kube-flannel pod-name还需要确认节点上/etc/cni/net.d/目录下是否有 Flannel 配置文件。7.5 join 时找不到运行时现象执行 join 时提示Container runtime is not running或CRI socket is not supported。原因kubeadm 没有识别到 cri-dockerd探测到了 containerd 或其它运行时。解决思路join 命令中显式指定--cri-socket unix:///var/run/cri-dockerd.sock。也可以在 /etc/default/kubelet 中设置KUBELET_EXTRA_ARGS--container-runtime-endpointunix:///var/run/cri-dockerd.sock。7.6 cgroup driver 不匹配现象kubelet 日志中出现failed to run Kubelet: failed to create kubelet component或cgroup driver cgroupfs is different from systemd之类的内容。原因Docker、cri-dockerd、kubelet 的 cgroup driver 不一致。解决思路在 Docker daemon.json 中设置exec-opts: [native.cgroupdriversystemd]。在 kubeadm 初始化配置文件中设置KubeletConfiguration.cgroupDriver: systemd。7.7 常见问题速查表问题现象常见原因解决思路镜像拉取超时网络访问镜像仓库不稳定配置 Docker registry mirror 或使用代理仓库kubelet 反复重启集群尚未初始化等 kubeadm init 完成后观察cri-dockerd.sock 不存在cri-dockerd 未启动检查服务状态和启动日志Node NotReadyCNI 插件未就绪安装 Flannel 或 Calico检查 Podjoin 失败运行时 socket 未指定显式指定--cri-socketcgroup driver 不一致多处配置冲突统一为 systemdtoken 过期默认 24 小时有效期重新生成 token8. 生产环境最佳实践与建议8.1 版本固定与升级策略生产环境不能随便执行apt-get upgrade升级 kubeadm、kubelet、kubectl。三个组件必须保持相同版本升级前先看官方 Release Notes升级顺序建议是先升级 kubeadm再升级 kubelet 和 kubectl最后执行kubeadm upgrade plan查看集群升级路径。8.2 数据备份etcd 是重中之重etcd 保存了 Kubernetes 集群的全部状态数据包括资源对象、配置、证书等。常规测试环境可以不考虑但生产环境必须定期备份 etcd。ETCDCTL_API3 etcdctl --endpointshttps://127.0.0.1:2379 \ --cacert/etc/kubernetes/pki/etcd/ca.crt \ --cert/etc/kubernetes/pki/etcd/server.crt \ --key/etc/kubernetes/pki/etcd/server.key \ snapshot save /backup/etcd-snapshot-$(date %Y%m%d).db备份文件要存放在独立的存储位置避免和系统盘故障绑定在一起。8.3 运行时选型建议虽然本文完整介绍了通过 cri-dockerd 让 k8s 走 Docker 的方案但对于新搭建的集群如果团队没有硬性依赖我会建议默认使用 containerd。原因是 containerd 更轻量少一层适配排障链路过短且始终是 Kubernetes 社区优先保障和测试的运行时。如果你的团队出于业务习惯坚持使用 Docker那么要注意cri-dockerd 的维护活跃度相比 containerd 低一些不能长期不升级。以后 k8s 升级时先确认 cri-dockerd 是否有对应版本的兼容发布。保留 Docker 工作流不代表生产集群必须用 Docker 作为运行时也可以在开发测试环境使用 Docker 运行时正式环境切换 containerd。8.4 安全与权限最小化kubeadm 使用的 admin.conf 拥有集群最高权限不能随意下发。日常运维建议给不同角色创建独立的 ServiceAccount并通过 RBAC 限制其权限范围。kubelet 默认端口 10250 如果暴露给不可信网络存在未授权访问风险建议通过防火墙或安全组限制 10250、6443 等端口的来源。8.5 日志与监控集群安装完成后下一步建议部署 metrics-server否则kubectl top无法查看资源占用。同时可以考虑安装 Kubernetes Dashboard 作为可视化管理入口。日志方面容器日志已经通过 Docker 的 json-file 驱动写到了宿主机但生产环境通常需要把日志采集到 Elasticsearch、Loki 或对象存储中才能方便检索和长期留存。8.6 不要把控制平面节点当作业务节点即使 Master 节点有富余资源也不建议直接在 master01 上运行业务 Pod。默认情况下 k8s 会通过 taint 阻止普通 Pod 调度到控制平面节点如果因为资源紧张而取消 taint会让控制平面组件和业务负载争抢资源出现 api-server 响应慢等问题。8.7 网络与安全组配置如果集群部署在云服务器上需要在安全组或防火墙中放通以下端口组件端口方向kube-apiserver6443所有节点可访问kubelet10250Master 访问 Workeretcd2379-2380控制平面节点之间Flannel VXLAN8472/UDP节点之间NodePort Service30000-32767按需对外开放配置安全组时遵循最小放通原则只对需要的来源 IP 开放。9. 结语顺着这条路继续往下走你现在应该已经理解了 kubeadm 安装 k8s 的完整流程也清楚了“底层走 docker 容器”在当前版本中并不是直接装完 Docker 就能用而是需要在 kubelet 和 Docker 之间接入 cri-dockerd。这套方案在开发测试环境、企业内网环境经常会用到尤其是运维脚本和发布工具链还依赖 Docker 命令的情况下性价比很高。集群搭建成功只是一个起点。接下来可以按下面的顺序继续深入部署一个简单的 Nginx Deployment验证 Pod 调度和服务暴露。安装 metrics-server 和 Kubernetes Dashboard把监控和运维入口补齐。尝试在集群中部署有状态应用比如 MySQL理解 PersistentVolume 和 StatefulSet 的关系。研究 Ingress Controller把外部流量的访问方式从 NodePort 切换为域名方式。动手实践是最快的排错方式。当你亲手把一套集群从零开始初始化、加入节点、部署应用、排查网络问题后再回头看 kubeadm 的日志输出和配置文件很多抽象概念就会自然串联起来。如果你在安装过程中遇到本文没有覆盖到的问题可以先对照kubeadm init输出的完整日志再结合journalctl -u kubelet和journalctl -u cri-docker定位大部分初始化失败都逃不出镜像、socket、cgroup driver 和网络这几类原因。希望这篇文章能帮你顺利搭出一套稳定可用的 kubeadm 集群。