二十、Kubernetes基础-67-docker-k8s128-cluster-deployment
基于Docker-CE与cri-dockerd部署Kubernetes 1.28高可用集群——从容器运行时到控制平面的全链路实战
摘要:Kubernetes 1.24 正式移除内置 dockershim 后,以 Docker 作为容器运行时的集群必须通过 cri-dockerd 适配器桥接 CRI 接口。本文以 K8S 1.28 + Docker-CE 24.x + cri-dockerd 0.3.x 为技术栈,从内核参数调优、容器运行时编译安装、镜像加速、kubeadm 引导初始化到多节点 join 全流程,深入剖析每一步的底层原理与工程细节,覆盖生产级部署中常见的坑点与调优策略。
一、技术背景与架构演进
1.1 dockershim 的移除与 CRI 架构
Kubernetes 自 1.5 引入 CRI(Container Runtime Interface) 规范,旨在解耦 kubelet 与具体容器运行时。然而早期 Docker 并未实现 CRI,Kubernetes 在 kubelet 内部维护了一个名为 dockershim 的垫片层:
┌─────────────────────────────────────────────────┐
│ kubelet │
│ ┌───────────┐ ┌──────────────────────────┐ │
│ │ CRI API │───>│ dockershim (内置垫片) │ │
│ └───────────┘ └──────────┬───────────────┘ │
└──────────────────────────────┼──────────────────┘
│ Docker Engine API
┌─────────▼─────────┐
│ Docker Daemon │
│ ┌──────────────┐ │
│ │ containerd │ │
│ │ ┌─────────┐ │ │
│ │ │ runc │ │ │
│ │ └─────────┘ │ │
│ └──────────────┘ │
└───────────────────┘
这条调用链为 kubelet → dockershim → Docker Daemon → containerd → runc,存在两个核心问题:
- 维护成本:dockershim 约 2000 行代码嵌入 kubelet 主干,每次 Docker API 变更都需要同步适配
- 性能开销:请求经过 Docker Daemon 的额外序列化/反序列化,创建 Pod 的延迟增加 ~200ms
Kubernetes 1.20 发布弃用通知,1.24 正式移除 dockershim。此后若仍使用 Docker 作为运行时,必须部署外部适配器 cri-dockerd。
1.2 cri-dockerd 的定位
cri-dockerd 是 Mirantis 维护的开源项目,其本质是将原 dockershim 代码从 kubelet 中剥离为独立的 gRPC 服务:
┌──────────┐ CRI gRPC ┌──────────────┐ Docker API ┌──────────────┐
│ kubelet │ ──────────────────>│ cri-dockerd │ ────────────────>│ Docker Daemon│
└──────────┘ unix:///var/run/ └──────────────┘ └──────┬───────┘
cri-dockerd.sock │
┌──────▼───────┐
│ containerd │
└──────┬───────┘
┌──────▼───────┐
│ runc │
└──────────────┘
为什么还要用 Docker? 在企业环境中,Docker 的 docker build、docker compose、镜像调试(docker exec)等开发者工具链仍有不可替代的地位。cri-dockerd 让团队在保留 Docker 工作流的同时接入 CRI 标准。
1.3 版本矩阵
| 组件 | 版本 | 说明 |
|---|---|---|
| OS | CentOS 7.9 / Ubuntu 22.04 | 内核 ≥ 5.4 推荐 |
| Docker-CE | 24.0.x | 稳定长期支持 |
| cri-dockerd | 0.3.8+ | 适配 CRI v1 |
| Kubernetes | 1.28.x | 2023 LTS |
| Calico | 3.26.x | 网络插件(下篇) |
二、环境规划与系统预配置
2.1 节点规划
+──────────────+───────────────+────────────+───────────────+
│ 角色 │ 主机名 │ IP │ 配置 │
+──────────────+───────────────+────────────+───────────────+
│ Master-01 │ k8s-master01 │ 10.0.0.10 │ 4C/8G/50G │
│ Worker-01 │ k8s-worker01 │ 10.0.0.11 │ 4C/8G/50G │
│ Worker-02 │ k8s-worker02 │ 10.0.0.12 │ 4C/8G/50G │
+──────────────+───────────────+────────────+───────────────+
生产建议:Master 至少 3 节点实现 etcd 多数派仲裁;Worker 根据业务规模横向扩展。
2.2 主机名与 hosts 解析
# 所有节点执行
hostnamectl set-hostname k8s-master01 # 各节点对应修改
cat >> /etc/hosts << 'EOF'
10.0.0.10 k8s-master01
10.0.0.11 k8s-worker01
10.0.0.12 k8s-worker02
EOF
2.3 关闭 swap
kubelet 默认拒绝在开启 swap 的节点上运行(可通过 --fail-swap-on=false 覆盖,但生产环境不推荐):
swapoff -a
sed -ri 's/.*swap.*/#&/' /etc/fstab
# 验证
free -h # Swap 行应全部为 0
底层原因:Kubernetes 的 QoS 模型(Guaranteed / Burstable / BestEffort)依赖 cgroup 的内存限制精确控制。swap 的存在使得 OOM Killer 的触发时机不可预测,导致 Pod 驱逐策略失效。
2.4 关闭防火墙与 SELinux
# 防火墙
systemctl stop firewalld && systemctl disable firewalld
# SELinux
setenforce 0
sed -i 's/^SELINUX=enforcing$/SELINUX=disabled/' /etc/selinux/config
生产替代方案:不关闭防火墙,而是精确放行所需端口:
- Master:6443(API Server)、2379-2380(etcd)、10250-10252(kubelet/controller/scheduler)
- Worker:10250(kubelet)、30000-32767(NodePort)
2.5 内核参数与模块加载
# 加载必要内核模块
cat > /etc/modules-load.d/k8s.conf << 'EOF'
overlay
br_netfilter
EOF
modprobe overlay
modprobe br_netfilter
# 网络桥接与转发参数
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
参数解析:
| 参数 | 作用 |
|---|---|
overlay |
为 Docker 的 overlay2 存储驱动提供联合文件系统支持 |
br_netfilter |
使桥接流量经过 iptables/netfilter 处理,Pod 跨节点通信必需 |
bridge-nf-call-iptables |
二层桥接帧经过三层 iptables 规则,Service 的 DNAT/SNAT 依赖此项 |
ip_forward |
允许内核在不同网络接口间转发数据包,Pod 网络路由必需 |
2.6 时间同步
yum install -y chrony # CentOS
# apt install -y chrony # Ubuntu
systemctl enable chronyd --now
chronyc sources -v
节点间时钟偏差超过数秒将导致 etcd 选举异常、证书验证失败。
三、Docker-CE 安装与配置
3.1 配置 Docker 仓库
# CentOS
yum install -y yum-utils device-mapper-persistent-data lvm2
yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
# Ubuntu
apt-get update
apt-get install -y ca-certificates curl gnupg
install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://mirrors.aliyun.com/docker-ce/linux/ubuntu $(lsb_release -cs) stable" > /etc/apt/sources.list.d/docker.list
apt-get update
3.2 安装指定版本
# 查看可用版本
yum list docker-ce --showduplicates | sort -r | head -20
# 安装 Docker-CE 24.0
yum install -y docker-ce-24.0.7-1.el7 docker-ce-cli-24.0.7-1.el7 containerd.io
# Ubuntu
# apt-get install -y docker-ce=5:24.0.7-1~ubuntu.22.04~jammy docker-ce-cli=5:24.0.7-1~ubuntu.22.04~jammy containerd.io
3.3 配置 Docker Daemon
mkdir -p /etc/docker
cat > /etc/docker/daemon.json << 'EOF'
{
"exec-opts": ["native.cgroupdriver=systemd"],
"log-driver": "json-file",
"log-opts": {
"max-size": "100m",
"max-file": "3"
},
"storage-driver": "overlay2",
"storage-opts": [
"overlay2.override_kernel_check=true"
],
"registry-mirrors": [
"https://hub-mirror.c.163.com",
"https://mirror.baidubce.com"
],
"data-root": "/data/docker",
"live-restore": true
}
EOF
关键配置深度解析:
| 配置项 | 值 | 原理说明 |
|---|---|---|
cgroupdriver=systemd |
systemd | K8S 1.22+ 强制要求 kubelet 与运行时使用同一 cgroup 驱动。systemd 作为 PID 1 进程管理者,由它管理 cgroup 层级树可避免两套 cgroup 管理器竞争导致的资源泄漏 |
overlay2 |
存储驱动 | 基于 OverlayFS 的 CoW 文件系统,相比 aufs 性能更优(顺序读提升 ~20%),且为内核原生支持 |
live-restore |
true | Docker Daemon 重启时不中断运行中容器,对于 Daemon 升级场景至关重要 |
data-root |
/data/docker | 分离数据目录至独立磁盘,避免镜像/容器数据撑满系统盘 |
log-opts |
100m × 3 | 单容器日志滚动上限 300MB,防止日志洪泛导致磁盘耗尽(生产中曾有容器日志单文件达 TB 级别的事故) |
3.4 启动与验证
systemctl daemon-reload
systemctl enable docker --now
# 验证
docker info | grep -E "Cgroup Driver|Storage Driver|Docker Root Dir"
# 期望输出:
# Cgroup Driver: systemd
# Storage Driver: overlay2
# Docker Root Dir: /data/docker
docker run --rm hello-world
四、cri-dockerd 编译安装与配置
4.1 安装 Go 编译环境
cri-dockerd 使用 Go 语言开发,需要 Go 1.20+ 编译:
wget https://go.dev/dl/go1.21.5.linux-amd64.tar.gz
tar -C /usr/local -xzf go1.21.5.linux-amd64.tar.gz
cat >> /etc/profile.d/go.sh << 'EOF'
export GOROOT=/usr/local/go
export GOPATH=/opt/gopath
export PATH=$GOROOT/bin:$GOPATH/bin:$PATH
export GOPROXY=https://goproxy.cn,direct
EOF
source /etc/profile.d/go.sh
go version # go version go1.21.5 linux/amd64
4.2 编译 cri-dockerd
cd /opt
git clone https://github.com/Mirantis/cri-dockerd.git
cd cri-dockerd
git checkout v0.3.8
# 编译
make cri-dockerd
# 产物位于 cri-dockerd/cri-dockerd
# 安装二进制
install -o root -g root -m 0755 cri-dockerd /usr/local/bin/cri-dockerd
# 验证
cri-dockerd --version
# cri-dockerd 0.3.8 (commit-hash)
无法科学上网时的替代方案:从 GitHub Releases 页面直接下载预编译二进制:
wget https://github.com/Mirantis/cri-dockerd/releases/download/v0.3.8/cri-dockerd-0.3.8.amd64.tgz tar xf cri-dockerd-0.3.8.amd64.tgz install -o root -g root -m 0755 cri-dockerd/cri-dockerd /usr/local/bin/
4.3 配置 systemd 服务
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=docker.service
[Service]
Type=notify
ExecStart=/usr/local/bin/cri-dockerd \
--container-runtime-endpoint=unix:///var/run/cri-dockerd.sock \
--pod-infra-container-image=registry.aliyuncs.com/google_containers/pause:3.9 \
--network-plugin=cni \
--cni-bin-dir=/opt/cni/bin \
--cni-conf-dir=/etc/cni/net.d
ExecReload=/bin/kill -s HUP $MAINPID
TimeoutSec=0
RestartSec=2
Restart=always
StartLimitBurst=3
StartLimitInterval=60s
LimitNOFILE=infinity
LimitNPROC=infinity
LimitCORE=infinity
[Install]
WantedBy=multi-user.target
EOF
cat > /usr/lib/systemd/system/cri-docker.socket << 'EOF'
[Unit]
Description=CRI Docker Socket for the API
PartOf=cri-docker.service
[Socket]
ListenStream=/var/run/cri-dockerd.sock
SocketMode=0660
SocketUser=root
SocketGroup=docker
[Install]
WantedBy=sockets.target
EOF
关键参数解析:
--pod-infra-container-image:指定 pause 容器镜像(国内需替换为阿里云镜像)。pause 容器是 Pod 中所有容器的"父容器",它创建并持有 Pod 的网络命名空间(Network Namespace),其他容器通过--net=container:<pause-id>共享该命名空间实现 Pod 内容器间 localhost 通信--network-plugin=cni:声明使用 CNI 插件管理网络(Calico/Flannel),而非 Docker 的原生桥接网络Requires=docker.service:确保 Docker Daemon 先于 cri-dockerd 启动,构成强依赖链
4.4 启动服务
systemctl daemon-reload
systemctl enable cri-docker --now
# 验证 socket 存在
ls -la /var/run/cri-dockerd.sock
# srw-rw---- 1 root docker 0 ... /var/run/cri-dockerd.sock
# 验证服务状态
systemctl status cri-docker
# Active: active (running)
# 功能验证:通过 crictl 连接
cat > /etc/crictl.yaml << 'EOF'
runtime-endpoint: unix:///var/run/cri-dockerd.sock
image-endpoint: unix:///var/run/cri-dockerd.sock
timeout: 10
debug: false
EOF
crictl info # 应返回运行时信息
五、Kubernetes 1.28 组件安装
5.1 配置 K8S 仓库
# CentOS
cat > /etc/yum.repos.d/kubernetes.repo << 'EOF'
[kubernetes]
name=Kubernetes
baseurl=https://mirrors.aliyun.com/kubernetes-new/core/stable/v1.28/rpm/
enabled=1
gpgcheck=1
gpgkey=https://mirrors.aliyun.com/kubernetes-new/core/stable/v1.28/rpm/repodata/repomd.xml.key
EOF
# Ubuntu
curl -fsSL https://mirrors.aliyun.com/kubernetes-new/core/stable/v1.28/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://mirrors.aliyun.com/kubernetes-new/core/stable/v1.28/deb/ /" > /etc/apt/sources.list.d/kubernetes.list
apt-get update
5.2 安装 kubeadm / kubelet / kubectl
# CentOS
yum install -y kubelet-1.28.2 kubeadm-1.28.2 kubectl-1.28.2 --disableexcludes=kubernetes
# Ubuntu
# apt-get install -y kubelet=1.28.2-1.1 kubeadm=1.28.2-1.1 kubectl=1.28.2-1.1
# apt-mark hold kubelet kubeadm kubectl
systemctl enable kubelet
# 注意:此时 kubelet 会反复重启(CrashLoop),这是正常行为——它在等待 kubeadm init 提供配置
三组件职责:
| 组件 | 职责 |
|---|---|
| kubeadm | 集群引导工具,负责生成 CA 证书、静态 Pod 清单、bootstrap token 等 |
| kubelet | 节点代理,通过 CRI 接口管理 Pod 生命周期,向 API Server 上报节点状态 |
| kubectl | 命令行客户端,与 API Server 交互的主要工具 |
5.3 配置 kubelet 使用 cri-dockerd
cat > /etc/sysconfig/kubelet << 'EOF'
KUBELET_EXTRA_ARGS=--container-runtime-endpoint=unix:///var/run/cri-dockerd.sock
EOF
此配置确保 kubelet 连接 cri-dockerd 的 socket 而非默认的 containerd socket。
六、kubeadm 初始化 Master 节点
6.1 生成初始化配置文件
kubeadm config print init-defaults > kubeadm-config.yaml
编辑关键字段:
apiVersion: kubeadm.k8s.io/v1beta3
kind: InitConfiguration
nodeRegistration:
criSocket: unix:///var/run/cri-dockerd.sock # 指定 CRI socket
name: k8s-master01
localAPIEndpoint:
advertiseAddress: 10.0.0.10 # Master IP
bindPort: 6443
---
apiVersion: kubeadm.k8s.io/v1beta3
kind: ClusterConfiguration
kubernetesVersion: v1.28.2
controlPlaneEndpoint: "10.0.0.10:6443" # HA 场景替换为 VIP
imageRepository: registry.aliyuncs.com/google_containers # 国内镜像
networking:
serviceSubnet: 10.96.0.0/12
podSubnet: 10.244.0.0/16 # 与 Calico 配置一致
dnsDomain: cluster.local
etcd:
local:
dataDir: /var/lib/etcd
---
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
cgroupDriver: systemd # 与 Docker 一致
6.2 预拉取镜像
kubeadm config images pull \
--config kubeadm-config.yaml \
--cri-socket unix:///var/run/cri-dockerd.sock
# 验证
docker images | grep -E "kube-|etcd|coredns|pause"
6.3 执行初始化
kubeadm init --config kubeadm-config.yaml --upload-certs --v=5
初始化流程深度解析(kubeadm init 的 13 个阶段):
[init] → 版本校验、配置验证
[preflight] → 系统预检(端口、swap、cgroup、CRI 连通性)
[certs] → 生成 PKI 证书(CA、API Server、etcd、front-proxy 等共 ~10 对)
[kubeconfig] → 生成 kubeconfig(admin、controller-manager、scheduler、kubelet)
[kubelet-start] → 写入 kubelet 配置并启动
[control-plane] → 生成静态 Pod 清单(/etc/kubernetes/manifests/)
[etcd] → 生成 etcd 静态 Pod 清单
[upload-config] → 将配置存入 ConfigMap(kube-system/kubeadm-config)
[upload-certs] → 加密上传证书到 Secret(HA join 使用)
[mark-control] → 给 Master 节点打 taint(NoSchedule)
[bootstrap-token] → 创建 bootstrap token(Worker join 使用)
[kubelet-finalize] → 更新 kubelet 的客户端证书轮转配置
[addon] → 安装 CoreDNS 和 kube-proxy 附加组件
6.4 配置 kubectl
mkdir -p $HOME/.kube
cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
chown $(id -u):$(id -g) $HOME/.kube/config
# 验证
kubectl get nodes
# NAME STATUS ROLES AGE VERSION
# k8s-master01 NotReady control-plane 60s v1.28.2
# NotReady 是正常的——尚未安装网络插件
6.5 证书体系详解
kubeadm 生成的证书位于 /etc/kubernetes/pki/:
/etc/kubernetes/pki/
├── ca.crt / ca.key # 集群根 CA
├── apiserver.crt / apiserver.key # API Server 服务端证书
├── apiserver-kubelet-client.crt/key # API Server → kubelet 客户端证书
├── apiserver-etcd-client.crt/key # API Server → etcd 客户端证书
├── front-proxy-ca.crt/key # 聚合 API 层 CA
├── front-proxy-client.crt/key # 聚合 API 客户端证书
├── etcd/
│ ├── ca.crt / ca.key # etcd 独立 CA
│ ├── server.crt / server.key # etcd 服务端证书
│ ├── peer.crt / peer.key # etcd 节点间通信证书
│ └── healthcheck-client.crt/key # etcd 健康检查证书
└── sa.key / sa.pub # ServiceAccount 签发密钥对
安全要点:
ca.key、sa.key、etcd/ca.key是集群最高权限密钥,泄露等同于集群完全失控。生产环境应严格管控其访问权限(chmod 0600)并考虑硬件安全模块(HSM)存储。
七、Worker 节点加入集群
7.1 在 Worker 节点执行 join
Master 初始化成功后输出的 join 命令:
kubeadm join 10.0.0.10:6443 \
--token <token> \
--discovery-token-ca-cert-hash sha256:<hash> \
--cri-socket unix:///var/run/cri-dockerd.sock
关键:必须追加
--cri-socket参数指向 cri-dockerd,否则 kubeadm 会尝试连接默认的 containerd socket 导致失败。
7.2 Token 过期处理
Token 默认 24 小时过期。若需重新生成:
# Master 上执行
kubeadm token create --print-join-command
# 获取 ca-cert-hash
openssl x509 -pubkey -in /etc/kubernetes/pki/ca.crt | \
openssl rsa -pubin -outform der 2>/dev/null | \
openssl dgst -sha256 -hex | sed 's/^.* //'
7.3 验证集群状态
kubectl get nodes -o wide
# NAME STATUS ROLES AGE VERSION INTERNAL-IP OS-IMAGE
# k8s-master01 NotReady control-plane 5m v1.28.2 10.0.0.10 CentOS 7.9
# k8s-worker01 NotReady <none> 2m v1.28.2 10.0.0.11 CentOS 7.9
# k8s-worker02 NotReady <none> 1m v1.28.2 10.0.0.12 CentOS 7.9
所有节点显示 NotReady 是预期行为——需要安装 CNI 网络插件(Calico),这将在下一篇文章中详细介绍。
八、常见问题与排查
8.1 kubelet 启动失败
journalctl -xeu kubelet | tail -50
| 错误信息 | 原因 | 解决 |
|---|---|---|
failed to run Kubelet: running with swap on is not supported |
swap 未关闭 | swapoff -a |
Error getting node...connection refused |
API Server 未就绪 | 检查静态 Pod:crictl ps |
failed to get sandbox image... |
pause 镜像拉取失败 | 配置 cri-dockerd 的 --pod-infra-container-image |
cgroup driver "cgroupfs" != "systemd" |
cgroup 驱动不一致 | Docker daemon.json 和 kubelet 配置统一为 systemd |
8.2 cri-dockerd 连接失败
# 检查 socket 是否存在
ls -la /var/run/cri-dockerd.sock
# 检查服务日志
journalctl -u cri-docker -f
# 手动测试 CRI 连通性
crictl --runtime-endpoint unix:///var/run/cri-dockerd.sock info
8.3 镜像拉取超时
# 检查 Docker 镜像加速是否生效
docker info | grep -A 5 "Registry Mirrors"
# 手动预拉取并重命名
docker pull registry.aliyuncs.com/google_containers/kube-apiserver:v1.28.2
docker tag registry.aliyuncs.com/google_containers/kube-apiserver:v1.28.2 \
registry.k8s.io/kube-apiserver:v1.28.2
8.4 kubeadm reset(重置重来)
kubeadm reset --cri-socket unix:///var/run/cri-dockerd.sock -f
rm -rf /etc/kubernetes/ /var/lib/etcd/ /var/lib/kubelet/ $HOME/.kube
iptables -F && iptables -t nat -F && iptables -t mangle -F && iptables -X
九、总结
本文完整覆盖了基于 Docker-CE + cri-dockerd 部署 Kubernetes 1.28 集群的全流程:
- 架构理解:厘清 dockershim 移除后的 CRI 架构演进,理解 cri-dockerd 的桥接角色
- 系统预配置:内核参数、cgroup 驱动、网络模块——每项配置都有其 Kubernetes 网络模型层面的必然性
- Docker-CE:overlay2 存储驱动、systemd cgroup 驱动、日志滚动策略的生产级配置
- cri-dockerd:从源码编译到 systemd 服务配置,深入 pause 容器与 CNI 的关联
- kubeadm 引导:13 个初始化阶段的逐步解析,PKI 证书体系的安全要点
- Worker 加入:token 管理与 CRI socket 指定
下一篇将介绍 Calico 网络插件的部署与集群可用性全面验证,包括 Pod 跨节点通信、Service 负载均衡、DNS 解析、网络策略等内容。
参考资料:
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)