DevOps 项目阶段笔记二:Docker Engine + cri-dockerd
1. 本阶段目标
上一阶段已经完成 VM 基础初始化、SSH 免密、Ansible Inventory、静态 IP、Kubernetes 节点 swap/内核参数等配置。
本阶段从 Docker 开始,原计划完成:
Ansible 自动安装 Docker Engine
↓
配置 Docker systemd cgroup driver
↓
在 Kubernetes 节点安装 cri-dockerd
↓
验证 Docker
↓
验证 cri-dockerd
↓
为后续 Kubernetes 安装准备 CRI
Kubernetes (K8s)
│
├── 需要容器运行时(Container Runtime)来运行 Pod 里的容器
│
└── CRI(Container Runtime Interface)
│ ← Kubernetes 定义的统一接口标准
│
┌──────┴──────┐
│ │
containerd cri-dockerd
│ │
│ └── 调用 Docker Engine(dockerd)
│ │
│ └── 最终管理容器
│
(直接通过 CRI 与 K8s 通信,效率更高)
一句话总结:
- Docker = 底层引擎,会干活但不讲 K8s 的语言
- CRI = K8s 规定的通用语言标准
- cri-dockerd = 翻译官,把 K8s 的指令转成 Docker 能听懂的命令
所以你的流程本质上是:装了个只会说英语的人(Docker),再给他配个翻译(cri-dockerd),让他能跟只说中文的老板(K8s)沟通。
节点规划:
| IP | 主机名 | 角色 |
|---|---|---|
| 192.168.205.141 | k8s-master | Master |
| 192.168.205.142 | k8s-node1 | Worker |
| 192.168.205.143 | k8s-node2 | Worker |
| 192.168.205.144 | devops | DevOps 工具 |
| 192.168.205.145 | elk | ELK 日志 |
Docker 计划安装到: k8s-master, k8s-node1, k8s-node2, devops
cri-dockerd 只安装到: k8s-master, k8s-node1, k8s-node2
ELK 节点本阶段不安装 Docker。
2. 本阶段架构设计
Kubernetes 不直接使用 Docker Engine 作为 CRI,因此本项目采用:
kubelet
↓
cri-dockerd
↓
Docker Engine
后续 kubeadm 初始化时计划使用:
unix:///run/cri-dockerd.sock
Docker cgroup driver 统一计划配置为:
systemd
后续 kubelet 也将保持:
systemd
目标是保证:
Docker cgroup driver = systemd
kubelet cgroup driver = systemd
3. 本阶段新增文件
Ansible 项目目录新增:
~/devops-lab/ansible/
├── ansible.cfg
├── init.yml
├── network.yml
├── docker.yml ← 新增
├── inventory/
│ └── hosts.ini
└── scripts/
├── init.sh
├── docker-install.sh ← 新增
└── cri-dockerd-install.sh ← 新增
本阶段主要文件:
docker.ymlscripts/docker-install.shscripts/cri-dockerd-install.sh
4. Docker 安装方案
4.1 docker-install.sh
Docker 安装通过 scripts/docker-install.sh 统一执行。
脚本主要工作:
检查 Ubuntu 系统
↓
处理可能冲突的软件包
↓
安装 ca-certificates / curl / gnupg
↓
配置 Docker APT repository
5. Docker daemon 配置
Docker 配置文件:/etc/docker/daemon.json
使用的核心配置:
json
{
"exec-opts": ["native.cgroupdriver=systemd"],
"log-driver": "json-file",
"log-opts": {
"max-size": "100m",
"max-file": "3"
},
"storage-driver": "overlay2"
}
其中最重要的是:
json
"exec-opts": ["native.cgroupdriver=systemd"]
用途: 将 Docker cgroup driver 设置为 systemd,与 kubelet 保持一致。
另外 Docker 日志采用 json-file,并限制单个日志文件 max-size=100m、max-file=3,避免实验环境中容器日志无限增长。
6. cri-dockerd 安装方案
使用版本: cri-dockerd 0.4.4
下载文件: cri-dockerd-0.4.4.amd64.tgz
下载地址:
https://github.com/Mirantis/cri-dockerd/releases/download/v0.4.4/cri-dockerd-0.4.4.amd64.tgz
SHA256:
109b7540053a507dd85ad7b9fee9cee9caae55feedcc28365d3e7ab4fb2172d5
安装后的二进制路径: /usr/local/bin/cri-dockerd
计划生成 systemd 单元:
/etc/systemd/system/cri-docker.service/etc/systemd/system/cri-docker.socket
最终 CRI Socket: /run/cri-dockerd.sock
Kubernetes 后续使用: unix:///run/cri-dockerd.sock
7. Ansible Playbook 设计
docker.yml 分成两个 Play。
Play 1: Docker
目标组: hosts: "k8s_nodes:devops_servers"
也就是:k8s-master, k8s-node1, k8s-node2, devops
主要任务:
- 运行 docker-install.sh
- 加入 docker group
- 检查 docker.service
- 检查 Docker cgroup driver
- 输出状态
由于当前使用 Ansible 2.9.6,script 模块继续使用 free-form 写法:
yaml
script: "./scripts/docker-install.sh"
没有使用较新版本的:
yaml
script:
cmd: ...
Play 2: cri-dockerd
目标组: hosts: k8s_nodes
即:k8s-master, k8s-node1, k8s-node2
主要任务:
- 运行 cri-dockerd-install.sh
- 检查 cri-docker.service
- 检查 cri-docker.socket
- 检查 /run/cri-dockerd.sock
- 输出 CRI 状态
8. 问题一:docker.yml YAML 语法错误
问题现象
第一次运行:
bash
ansible-playbook \
-i inventory/hosts.ini \
docker.yml \
--syntax-check
报错:
ERROR! Syntax Error while loading YAML.
mapping values are not allowed in this context
定位:docker.yml: line 25, column 47
问题行:
yaml
-name: Check Docker cgroup driver
shell: docker info 2>/dev/null| awk -F':' '/Cgroup Driver/{print$2}'
错误位置:-F': 后面的 : 被 YAML 误解。
原因
Shell 命令本身没有问题,问题来自 YAML 解析。命令中 : 即冒号后面跟空格,YAML 会尝试将它解释成 key: value,因此一整行没有被正确识别为普通 shell 字符串。
修复
改用 YAML folded scalar:
yaml
- name: Check Docker cgroup driver
shell: >
docker info 2>/dev/null | awk -F':' '/Cgroup Driver/{print$2}'
register: docker_cgroup
changed_when: false
failed_when: docker_cgroup.stdout != "systemd"
之后继续执行 Playbook。
实践经验
复杂 Shell 命令中如果包含 : . " ${} 等符号,在 Ansible YAML 中最好避免直接写成长单行。优先使用 shell: > 或 shell: | 的多行写法。简单命令仍然可以写成单行,如:
yaml
shell: systemctl is-active docker
9. 问题二:Install cri-dockerd 阶段疑似卡住
问题现象
执行 docker.yml 时出现:
TASK [Install cri-dockerd]
changed: [k8s-node1]
changed: [k8s-node2]
之后长时间没有新的输出。
此时可以确认:
- k8s-node1 cri-dockerd task 已返回 changed ✓
- k8s-node2 cri-dockerd task 已返回 changed ✓
- k8s-master 尚未正常返回 ✗
最初怀疑:master 下载 GitHub release 太慢。
10. 对 master 的第一次排查
登录 k8s-master,检查:
bash
ps -ef | grep -E 'curl|cri-dockerd|systemctl' | grep -v grep
结果:无输出
说明当时 master 上不存在明显的 curl、cri-dockerd、systemctl 安装进程。
进一步执行:
bash
ps -ef | grep -E 'docker-install|cri-dockerd-install|ansible|python|bash|sh|apt|dpkg|tar|gpg' | grep -v grep
没有发现正在执行的 cri-dockerd-install.sh、curl、tar、sha256sum、apt、dpkg。
但发现 SSH/SFTP 相关进程,例如:
sshd: master@notty
/usr/lib/openssh/sftp-server
11. 确认 master 实际没有安装成功
在 master 上检查:
bash
/usr/local/bin/cri-dockerd --version
结果:
-bash: /usr/local/bin/cri-dockerd: No such file or directory
检查服务:
bash
sudo systemctl is-active cri-docker.service
结果:inactive
检查 socket:
bash
sudo systemctl is-active cri-docker.socket
结果:inactive
检查 Unix Socket:
bash
ls -l /run/cri-dockerd.sock
结果:
ls: cannot access '/run/cri-dockerd.sock': No such file or directory
由此确认: k8s-master 的 cri-dockerd 当时实际上没有安装成功,而不是"已经安装成功,只是 Ansible 没返回"。
12. 排查 GitHub 下载是否存在问题
由于安装脚本需要从 GitHub 下载 cri-dockerd-0.4.4.amd64.tgz,因此在 master 上进行独立下载测试。
进入 /tmp,执行:
bash
curl -fL \
"https://github.com/Mirantis/cri-dockerd/releases/download/v0.4.4/cri-dockerd-0.4.4.amd64.tgz" \
-o cri-dockerd-test.tgz
实际结果:
100 15.5M 100 15.5M
下载大约数秒完成。
说明:
- GitHub 可访问 ✓
- DNS 正常 ✓
- Release URL 正常 ✓
- 下载速度正常 ✓
因此排除了"master 因 GitHub 下载过慢导致卡死"这一判断。
13. 校验下载文件
执行:
bash
sha256sum /tmp/cri-dockerd-test.tgz
实际输出:
109b7540053a507dd85ad7b9fee9cee9caae55feedcc28365d3e7ab4fb2172d5 /tmp/cri-dockerd-test.tgz
与脚本预期 SHA256 完全一致:
109b7540053a507dd85ad7b9fee9cee9caae55feedcc28365d3e7ab4fb2172d5
因此进一步排除:
- 下载文件损坏 ✗
- 版本 URL 错误 ✗
- checksum 错误 ✗
14. 对下载命令进行健壮性优化
最初脚本中的下载命令:
bash
curl -fL \
"${DOWNLOAD_URL}" \
-o cri-dockerd.tgz
为了避免未来网络异常时长时间无响应,修改为:
bash
curl -fL \
--connect-timeout 10 \
--max-time 300 \
--retry 3 \
--retry-delay 5 \
"${DOWNLOAD_URL}" \
-o cri-dockerd.tgz
含义:
| 参数 | 含义 |
|---|---|
--connect-timeout 10 |
连接建立最多等待 10 秒 |
--max-time 300 |
整个 curl 最多运行 300 秒 |
--retry 3 |
失败后最多重试 3 次 |
--retry-delay 5 |
两次重试之间等待 5 秒 |
这不是本次故障的最终原因,但提高了脚本在自动化环境中的可靠性。
15. 只针对 master 重新执行 cri-dockerd 安装
没有重新执行整个 Docker 阶段。而是在 Ansible Control Node devops 上单独运行:
bash
ansible k8s-master \
-i inventory/hosts.ini \
-b \
-m script \
-a './scripts/cri-dockerd-install.sh' \
-vv
结果:
k8s-master | CHANGED
返回码:rc: 0
说明脚本成功执行。
16. master 最终安装输出
此次执行确认:
=== cri-dockerd installation ===
Host: k8s-master
Version: 0.4.4
Docker 检查:
[INFO] Docker is running
下载成功:
100 15.5M 100 15.5M
SHA256:
cri-dockerd.tgz: OK
[OK] SHA256 verification passed
systemd unit 启用:
Created symlink /etc/systemd/system/multi-user.target.wants/cri-docker.service
Created symlink /etc/systemd/system/sockets.target.wants/cri-docker.socket
cri-dockerd 版本:
cri-dockerd 0.4.4 (98c4823)
Socket:
srw-rw--- 1 root docker ... /run/cri-dockerd.sock
最终输出:
[OK] cri-dockerd installation completed.
17. 关于 SSH stderr
Ansible 返回中出现:
Shared connection to 192.168.205.141 closed.
它位于 stderr,但本次 rc: 0,并且任务状态 CHANGED,cri-dockerd 的版本、socket、systemd 启用过程也全部完成。因此这条信息本身不代表安装失败,而是 SSH 复用连接关闭时产生的信息。
18. 本次"卡住"的最终结论
可以明确排除:
- GitHub 下载速度慢 ✗
- DNS 故障 ✗
- Release URL 错误 ✗
- 下载文件损坏 ✗
- SHA256 错误 ✗
- cri-dockerd 0.4.4 包本身无法下载 ✗
因为 master 手动下载 15.5 MB 几秒即可完成,并且 SHA256 完全匹配。
之后使用相同 Ansible script 模块单独针对 master 重试,成功完成。
结合故障时看到的 sshd: master@notty 和 /usr/lib/openssh/sftp-server,以及安装脚本本身没有继续运行的现象,怀疑第一次执行时 Ansible/SSH/SFTP 会话出现了临时异常。
但是: 该根因没有通过进一步日志或复现得到最终确认。因此工程笔记中不把它写成确定原因。
正确记录应是:
| 项目 | 内容 |
|---|---|
| 故障现象 | 首次通过 docker.yml 执行时,master 的 cri-dockerd task 未正常结束 |
| 已排除 | GitHub/DNS/下载速度/下载文件/SHA256 |
| 处理 | 单独使用 Ansible script 模块针对 master 重试 |
| 结果 | 成功 |
| 根因 | 疑似首次 Ansible SSH/SFTP 会话异常,但本阶段未进一步复现,因此待确认,其实就是忘了开代理 |
19. 本阶段已经确认完成的内容
k8s-master
已经确认:
| 项目 | 状态 |
|---|---|
| Docker 正在运行 | ✓ |
| cri-dockerd 0.4.4 | ✓ |
| SHA256 校验 | ✓ |
| cri-docker.service unit 已创建/启用 | ✓ |
| cri-docker.socket unit 已创建/启用 | ✓ |
| /run/cri-dockerd.sock 已生成 | ✓ |
最终 socket:/run/cri-dockerd.sock
k8s-node1
Ansible 原始执行结果:
TASK [Install cri-dockerd]
changed: [k8s-node1]
因此 cri-dockerd 安装任务成功返回。更完整的版本、service、socket 独立验收输出:本阶段未单独留存。
k8s-node2
Ansible 原始执行结果:
TASK [Install cri-dockerd]
changed: [k8s-node2]
因此 cri-dockerd 安装任务成功返回。更完整的版本、service、socket 独立验收输出:本阶段未单独留存。
devops
本阶段设计为安装 Docker Engine。但是当前记录中没有保存 devops 节点最终 docker --version、systemctl is-active docker、docker info 的单独执行输出。
因此严格记录为:
- Docker 安装任务:已纳入 docker.yml ✓
- 最终独立验收:本阶段未留存
elk
本阶段:不安装 Docker,不安装 cri-dockerd。
20. 统一验收结果
本阶段已完成 Docker 与 cri-dockerd 的全节点统一验收,因此不再标记为"待执行"。
20.1 Docker 四节点验收
执行:
bash
ansible 'k8s_nodes:devops_servers' \
-i inventory/hosts.ini \
-b \
-m shell \
-a 'echo "===$(hostname)==="; docker --version; systemctl is-active docker; docker info 2>/dev/null | grep "Cgroup Driver"' \
-K
验收作用: 一次确认以下三项:
docker --version--- 确认 Docker Engine 已正确安装,并记录实际版本systemctl is-active docker--- 确认 Docker daemon 正常运行,正确结果为activedocker info | grep "Cgroup Driver"--- 确认 Docker cgroup driver 已按照 Kubernetes 规划配置为systemd
实际验收结果:
| 节点 | Docker 版本 | 服务状态 | Cgroup Driver |
|---|---|---|---|
| k8s-master | Docker version 28.1.1, build 4eba377 | active | systemd |
| k8s-node1 | Docker version 28.1.1, build 4eba377 | active | systemd |
| k8s-node2 | Docker version 28.1.1, build 4eba377 | active | systemd |
| devops | Docker version 28.1.1, build 4eba377 | active | systemd |
结论:
| 节点 | Docker 28.1.1 | active | systemd |
|---|---|---|---|
| k8s-master | ✓ | ✓ | ✓ |
| k8s-node1 | ✓ | ✓ | ✓ |
| k8s-node2 | ✓ | ✓ | ✓ |
| devops | ✓ | ✓ | ✓ |
Docker 四节点统一验收:✓ 通过
20.2 cri-dockerd 三节点综合验收
执行:
bash
ansible k8s_nodes \
-i inventory/hosts.ini \
-b \
-m shell \
-a 'echo "===$(hostname)==="; /usr/local/bin/cri-dockerd --version; systemctl is-active cri-docker.service; systemctl is-active cri-docker.socket; test -S /run/cri-dockerd.sock && echo "CRI socket OK"' \
-K
验收作用: 确认三个 Kubernetes 节点上的:
cri-dockerd二进制及版本cri-docker.servicecri-docker.socket/run/cri-dockerd.sock
其中:
/usr/local/bin/cri-dockerd --version--- 确认 cri-dockerd 已正确安装systemctl is-active cri-docker.service--- 确认 cri-dockerd 服务处于运行状态systemctl is-active cri-docker.socket--- 确认 systemd socket 处于运行状态test -S /run/cri-dockerd.sock--- 确认该路径不仅存在,而且确实是 Unix Socket
实际验收结果:
三台节点均返回:
cri-dockerd 0.4.4 (98c4823)
active
active
CRI socket OK
具体状态:
| 节点 | cri-dockerd 版本 | service | socket | CRI socket |
|---|---|---|---|---|
| k8s-master | 0.4.4 (98c4823) | active | active | OK |
| k8s-node1 | 0.4.4 (98c4823) | active | active | OK |
| k8s-node2 | 0.4.4 (98c4823) | active | active | OK |
结论:
| 节点 | cri-dockerd | service active | socket active | CRI socket OK |
|---|---|---|---|---|
| k8s-master | ✓ | ✓ | ✓ | ✓ |
| k8s-node1 | ✓ | ✓ | ✓ | ✓ |
| k8s-node2 | ✓ | ✓ | ✓ | ✓ |
cri-dockerd 三节点统一验收:✓ 通过
20.3 CRI Socket 路径与权限验收
执行:
bash
ansible k8s_nodes \
-i inventory/hosts.ini \
-b \
-m shell \
-a 'ls -l /run/cri-dockerd.sock' \
-K
验收作用: 进一步确认 Kubernetes 后续实际使用的 CRI Socket /run/cri-dockerd.sock 已经在三台 Kubernetes 节点正确创建,并检查其文件类型、属主和属组。
实际结果:
| 节点 | 结果 |
|---|---|
| k8s-master | srw-rw--- 1 root docker ... /run/cri-dockerd.sock |
| k8s-node1 | srw-rw--- 1 root docker ... /run/cri-dockerd.sock |
| k8s-node2 | srw-rw--- 1 root docker ... /run/cri-dockerd.sock |
其中 s 表示该文件类型为 Unix Socket ,属主和属组为 root:docker。
后续 kubeadm 使用的 CRI Endpoint:unix:///run/cri-dockerd.sock
CRI Socket 三节点验收:✓ 通过
20.4 最终验收结论
本阶段统一验收最终结果:
Docker 四节点:
| 节点 | Docker 版本 | 状态 | Cgroup Driver |
|---|---|---|---|
| k8s-master | 28.1.1 | active | systemd ✓ |
| k8s-node1 | 28.1.1 | active | systemd ✓ |
| k8s-node2 | 28.1.1 | active | systemd ✓ |
| devops | 28.1.1 | active | systemd ✓ |
cri-dockerd 三节点:
| 节点 | 版本 | service | socket |
|---|---|---|---|
| k8s-master | 0.4.4 | active | active ✓ |
| k8s-node1 | 0.4.4 | active | active ✓ |
| k8s-node2 | 0.4.4 | active | active ✓ |
CRI Socket:
| 节点 | 路径 | 状态 |
|---|---|---|
| k8s-master | /run/cri-dockerd.sock | ✓ |
| k8s-node1 | /run/cri-dockerd.sock | ✓ |
| k8s-node2 | /run/cri-dockerd.sock | ✓ |
因此此前"Docker/cri-dockerd 全节点批量验收待执行/待确认"正式更新为:
| 项目 | 状态 |
|---|---|
| Docker 四节点批量验收 | ✓ |
| cri-dockerd 三节点批量验收 | ✓ |
| /run/cri-dockerd.sock 三节点验收 | ✓ |
阶段 2:Docker Engine + systemd cgroup + cri-dockerd ✓ 验收完成
后续除非修改 Docker 配置、重装容器运行时,或 Kubernetes 故障排查需要重新确认 CRI,否则无需重复进行本阶段验收。
注:Ansible 使用 shell 模块执行这些检查时显示
CHANGED属正常现象。本次验收主要依据命令$? rc=0以及实际输出是否符合预期判断,并不表示验收命令修改了系统配置。
21. 本阶段关键配置汇总
Docker 节点:
- k8s-master
- k8s-node1
- k8s-node2
- devops
cri-dockerd 节点:
- k8s-master
- k8s-node1
- k8s-node2
Docker cgroup 目标配置: systemd
Docker storage driver 配置: overlay2
最终批量实际状态: 已统一验收 ✓
cri-dockerd:
| 项目 | 值 |
|---|---|
| 版本 | 0.4.4 |
| Master 已确认版本 | cri-dockerd 0.4.4 (98c4823) |
| binary 路径 | /usr/local/bin/cri-dockerd |
| CRI Socket | /run/cri-dockerd.sock |
| Kubernetes 后续 CRI Endpoint | unix:///run/cri-dockerd.sock |
22. 本阶段学到的内容
1. Kubernetes 使用 Docker 需要 CRI 适配层
当前项目不是:
kubelet
↓
Docker
而是:
kubelet
↓
cri-dockerd
↓
Docker Engine
因此后面 kubeadm 必须显式使用 unix:///run/cri-dockerd.sock。
2. Docker 与 kubelet 的 cgroup driver 必须统一规划
本项目统一选择 systemd,显式配置。后续 Kubernetes 阶段仍需验证 kubelet 的对应配置。
3. YAML 错误不一定是 Shell 错误
这次 awk -F':' Shell 本身合法,但 YAML 对 : 有特殊语义。因此复杂 shell 命令优先使用 shell: > 可以减少 YAML 引号和转义问题。
4. Ansible script 看起来"卡住"时不能直接判断为下载慢
script 模块执行期间,控制台不会像手工终端一样持续展示脚本内部的所有实时输出。所以看到 TASK [Install...] 长时间没有变化,只能说明 task 尚未正常返回,不能直接推出 curl 下载很慢。应该通过 ps、systemctl、文件是否生成、手工 curl、Ansible -vv 逐层定位。
5. 排障要先区分"服务有问题"还是"自动化执行有问题"
本次 master 排查很典型:cri-dockerd binary 不存在、service inactive、socket inactive、socket 文件不存在------说明不是"服务装好了但启动失败",而是"安装过程本身没有完成"。排障方向因此转向安装脚本/Ansible/SSH/下载,而不是继续研究 cri-docker service 日志。
6. 网络问题最好直接做最小化验证
通过 curl + sha256sum 直接确认 DNS、网络、GitHub、URL、速度、文件完整性,比反复重跑整个 Playbook 更容易缩小故障范围。
7. 故障根因没有证据时不要强行下结论
虽然第一次失败过程中出现了 sshd: master@notty 和 sftp-server,并且后续单独重新执行成功,但没有完整日志证明 SSH/SFTP 就是根因。因此当前合理结论是"疑似 Ansible SSH/SFTP 临时会话异常,根因待确认",而不是把猜测记录成事实。
8. 自动化脚本需要主动设置网络超时
即使这次并不是下载慢导致的问题,也暴露了原脚本 curl -fL ... 缺少网络失败边界。优化后加入 connect timeout、overall timeout、retry、retry delay,可以避免未来 CI/CD 或批量 Ansible 执行时某一台机器永久等待。
23. 本阶段状态
当前可记录为:
| 项目 | 状态 |
|---|---|
| Docker 自动安装脚本 | ✓ 已编写并执行 |
| Docker daemon systemd cgroup 配置 | ✓ 已写入方案 |
| cri-dockerd 0.4.4 安装脚本 | ✓ |
| k8s-master cri-dockerd | ✓ 已明确验证 |
| k8s-node1 cri-dockerd | ✓ Ansible task 成功 |
| k8s-node2 cri-dockerd | ✓ Ansible task 成功 |
| CRI socket on master | ✓ 已明确验证 |
| Docker/cri-dockerd 全节点批量验收 | ✓ 已完成 |
因此阶段主体已经完成。