CI/CD 故障排查与容器运维核心原理

1.1 容器核心机制:PID1 与退出码

1.1.1 什么是 PID1?

在 Linux 系统中,所有进程拥有唯一进程编号(PID),PID1 是操作系统初始化第一个进程,负责收养孤儿进程、统一处理系统信号。

在 Docker 容器运行环境中:PID1 = Dockerfile 内 CMD/ENTRYPOINT 定义的启动进程

运行规则

容器生命周期完全依附 PID1:PID1 持续运行 → 容器存活;PID1 执行结束 / 异常崩溃 → 容器立刻销毁。

容器启动
    │
    ▼
启动 PID1(如 python app.py)
    │
    ├── 如果 PID1 持续运行 → 容器活着
    │
    └── 如果 PID1 执行完毕或崩溃 → 容器停止

1.1.2 为什么容器一启动就退出?

高频故障原因
  1. 启动进程为一次性任务:进程执行完毕无驻留逻辑,运行结束直接退出

  2. 应用启动异常:依赖缺失、端口占用、配置文件丢失导致程序崩溃

  3. Shell 包装启动命令:sh -c 执行单次指令,命令结束后 PID1 消亡

代码示例

❌ 错误写法(执行完立即退出)

CMD ["echo", "Hello"]

✅ 正确写法(前台常驻进程)

CMD ["python", "-u", "app.py"]

补充:Flask 项目需在代码内配置 app.run(host='0.0.0.0') 保持前台运行;Nginx 官方镜像默认前台运行,无需额外改造。

1.1.3 退出码的含义

执行 docker ps -a 可查看容器退出状态,通过退出码快速分类故障:

退出码 含义 常见场景
0 正常退出 批处理脚本执行完成;docker stop 停机
1 运行异常 依赖缺失、端口冲突、代码语法错误
137 进程被 SIGKILL 强制杀死 容器内存溢出 OOM;手动执行 docker kill
143 进程被 SIGTERM 终止 正常执行 docker stop 停止容器
排查要点
  • Exited (1):优先通过 docker logs 查看应用报错日志

  • Exited (137):核查容器内存配额,排查 OOM 问题

  • Exited (0)但预期常驻:启动命令为一次性任务,修改 CMD 为常驻进程

1.2 --restart 策略:容器崩溃后自动恢复

1.2.1 策略详解

--restart 用于定义容器退出后的自动重启逻辑,共 4 种配置:

策略 行为 适用场景
no(默认) 容器退出后永不自动重启 一次性批处理、数据库迁移脚本
always 无论退出码,永久自动重启 核心基础中间件服务
unless-stopped 除手动docker stop外,异常退出自动重启 生产业务容器(生产推荐)
on-failure[:max-retries] 仅非 0 退出码时重启,可限制重试次数 间歇性故障测试任务

启动示例

docker run -d --name myapp --restart=unless-stopped myapp:latest

注意事项:always策略下,服务器重启 / Docker 守护进程重启后容器会自动拉起;unless-stopped手动停机后不会随 Docker 重启自启,更贴合运维习惯。

1.2.2 在 CI 部署中怎么用?

在流水线部署脚本的 docker run 追加重启参数:

docker run -d --name myapp --restart=unless-stopped -p 5000:5000 myapp:ci-123

补充:若容器陷入无限重启循环,说明应用存在根本性代码 BUG,需查看日志定位根源。

1.3 部署假阳性:流水线绿了,但服务不可用

1.3.1 现象描述

CI/CD Job 执行返回 0、流水线状态 Passed(绿色),但业务访问返回 502 / 连接失败。

故障诱因
  1. docker run命令执行成功(返回码 0),但容器内部应用启动失败

  2. 应用启动耗时长(数据库连接、配置加载),部署完成后未就绪

  3. 服务器防火墙 / 安全组未放行容器映射端口

1.3.2 解决方案:部署后健康检查

在部署阶段脚本新增应用端口健康探测,探测失败主动让 Job 异常退出,流水线标红阻断发布。

# 最多等待10秒,每2秒探测一次,共探测5次
for i in 1 2 3 4 5; do
  if curl -s http://localhost:5000/health > /dev/null; then
    echo "应用启动成功"
    exit 0
  fi
  echo "等待应用启动... ($i/5)"
  sleep 2
done
echo "健康检查失败"
exit 1
配置要点
  1. 业务代码需实现 /health 健康接口,正常状态返回 HTTP 200

  2. 应用冷启动耗时过长可增大循环次数或 sleep 间隔

  3. 探测失败执行exit 1,让部署 Job 失败,避免无效上线

1.4 容器排错三板斧

容器异常(启动失败、循环重启、网络不通)优先使用以下三条命令逐层排查。

第一斧:docker ps -a 查看全量容器状态

docker ps -a

状态识别:

  • Up X minutes:容器正常运行

  • Exited (数字):容器异常退出,结合退出码 + 日志排错

  • Restarting (数字):容器持续循环重启,应用启动失败

第二斧:docker logs [容器名/ID] 查看标准输出 & 错误日志

# 查看全量日志
docker logs myapp
# 只查看末尾100行日志
docker logs --tail 100 myapp
# 实时滚动跟踪日志
docker logs -f myapp

示例报错:ModuleNotFoundError: No module named 'flask' → 项目依赖缺失,构建镜像阶段未安装依赖。

第三斧:docker inspect 读取容器结构化详情(JSON)

支持格式化输出关键字段,常用于 CI 自动化校验:

# 查询容器运行状态
docker inspect myapp --format='{{.State.Status}}'
# 查询容器退出码
docker inspect myapp --format='{{.State.ExitCode}}'

可拓展查询:端口映射、挂载目录、容器环境变量、网络配置等信息。

1.5 GitLab CI 变量安全机制

1.5.1 明文密码写入 yml 的安全隐患

❌ 高危错误写法(密钥硬编码进配置文件,仓库可见人员均可窃取密码)

script:
  - docker login -u myuser -p mypassword registry.com

1.5.2 解决方案:GitLab CI/CD 项目变量配置

配置路径:项目 → Settings → CI/CD → Variables → Add variable

  1. Key:自定义变量名(如ALIYUN_PASSWORD

  2. Value:填写真实密钥、密码

  3. Mask variable(掩码变量):日志中自动将原值替换为[MASKED]

  4. Protect variable(受保护变量):仅受保护分支可读取变量

使用方式:.gitlab-ci.yml脚本中直接引用$ALIYUN_PASSWORD

1.5.3 掩码变量使用注意事项

  1. 掩码仅全值匹配隐藏:密码abc123完整出现才会脱敏,片段abc不会被掩码

  2. 掩码变量不可在variables:区块嵌套拼接生成新变量,避免密钥泄漏

  3. 禁止将变量拼接至 URL 参数、日志输出字符串

1.5.4 受保护变量配置规范

  1. 路径:Settings → Repository → Protected branches,将生产分支(main/master)设置为受保护分支

  2. 生产密钥勾选Protect variable,dev/feature 等非生产分支流水线无法读取密钥

  3. 隔离测试环境与生产环境密钥,避免测试分支误触发生产部署

1.6 常见 CI/CD 故障场景与排查思路

故障症状 可能原因 排查步骤
流水线任务 Pending 阻塞 无可用运行中的 GitLab Runner 项目 CI/CD 配置核对,绑定可用共享 / 私有 Runner
脚本报错:docker: command not found Job 运行镜像不含 Docker 客户端 配置image: docker:24.0.7,启用 DinD 容器架构
拉镜像 / 安装依赖报 dial tcp: i/o timeout 构建环境外网网络超时 配置 Docker 国内镜像加速器、切换国内 Pip/Yum 源
本地代码测试正常,CI 测试 Job 报错 环境依赖 / 版本不一致 before_script 中打印 Python/JDK 版本、依赖清单对比
部署后容器不停重启 应用内部启动异常 docker logs查看程序报错,核对端口、配置文件
版本回滚提示 No previous version 旧版本信息落地失败 检查部署后置 after_script 执行状态、服务器目录读写权限
Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐