一、配置错误:YAML文件里的"坑"

在GitLab CI/CD中,最常见的失败原因就是.gitlab-ci.yml文件配置错误。这个YAML文件对缩进、语法和关键字都非常敏感,稍不注意就会导致流水线直接报错。

举个例子(技术栈:GitLab CI/CD):

# 错误示例:缩进混乱导致解析失败
build_job:  # 顶级关键字需要顶格写
 script:    # script必须缩进
- echo "hello"  # 列表项要用正确缩进
  - echo "world"  # 这里多了一层缩进,会导致错误

# 正确写法:
build_job:
  script:
    - echo "hello"
    - echo "world"

YAML文件还容易犯的错误包括:

  1. 漏写关键字段(比如忘记定义stage
  2. 错误使用变量语法(比如该用${VAR}时用了$VAR
  3. 使用了GitLab不支持的指令

建议每次修改后都用在线YAML校验工具检查语法,或者使用GitLab自带的流水线编辑器,它能实时提示语法错误。

二、环境差异:本地能跑,CI就挂

开发环境与CI环境的不一致是另一个常见问题。比如:

  • Node.js版本不同(本地用v16,CI用v14)
  • 缺少系统依赖(比如ImageMagick没安装)
  • 文件路径大小写敏感(Windows不敏感,Linux敏感)

看这个例子(技术栈:Node.js):

// package.json
{
  "scripts": {
    // 在Linux CI环境会报错,因为有些系统没有/usr/bin/env
    "build": "node build.js",  
    // 更健壮的写法:
    "build": "/usr/bin/env node build.js"
  }
}

解决方案:

  1. 使用environment明确指定环境变量
  2. before_script中安装缺失的依赖
  3. 用Docker镜像确保环境一致
# 示例:指定Node.js版本
image: node:16-alpine

before_script:
  - apk add --no-cache imagemagick  # 安装缺失的库

三、依赖问题:那些年我们下不到的包

依赖下载失败在CI中特别常见,尤其是:

  • npm包下载超时(网络问题)
  • 私有仓库认证失败
  • 依赖版本冲突

比如这个场景(技术栈:Python):

# 错误处理依赖的示例
test_job:
  script:
    - pip install -r requirements.txt  # 如果失败会继续执行后续步骤
    - pytest

# 正确做法:添加retry和错误处理
test_job:
  retry: 2  # 自动重试2次
  script:
    - pip install --retries 3 -r requirements.txt || exit 1
    - pytest

对于私有仓库,需要配置认证:

# 在CI变量中设置NPM_TOKEN
before_script:
  - echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > .npmrc

四、资源限制:被掐断的进程

GitLab Runner默认会有资源限制:

  • 超时(默认1小时)
  • 内存不足(导致OOM)
  • 并发数限制

典型表现是流水线运行到一半突然终止。解决方案:

# 调整超时时间
job_with_timeout:
  script: ./long_running_script.sh
  timeout: 2h  # 设置为2小时

# 限制内存使用
job_with_memory_limit:
  script: 
    - docker run --memory="512m" my_image  # 限制容器内存

对于资源密集型任务,建议:

  1. 使用tags指定更强大的Runner
  2. 拆分大任务为多个小job
  3. 监控资源使用情况

五、权限问题:你想干但CI不让

权限问题通常表现为:

  • 无法克隆子模块
  • 无法写入构建目录
  • 部署密钥失效

示例修复方案:

# 正确配置子模块克隆
build_job:
  variables:
    GIT_SUBMODULE_STRATEGY: recursive  # 递归克隆子模块
  script:
    - ls -la  # 检查文件权限

# 处理文件权限问题
fix_permissions:
  script:
    - chmod -R 755 ./dist  # 确保有执行权限

对于部署密钥,需要在GitLab的CI/CD变量中设置SSH_PRIVATE_KEY,然后:

before_script:
  - mkdir -p ~/.ssh
  - echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa
  - chmod 600 ~/.ssh/id_rsa

六、测试失败:红彤彤的测试报告

测试失败本身不是CI问题,但需要优化展示方式。比如:

# 生成JUnit格式的测试报告
test:
  stage: test
  script:
    - pytest --junitxml=report.xml
  artifacts:
    when: always  # 即使测试失败也保留报告
    paths:
      - report.xml
    reports:
      junit: report.xml

这样GitLab会在流水线页面直接显示测试失败详情,点击就能跳转到具体错误。

七、缓存与制品:用对了提速10倍

错误使用缓存会导致:

  • 缓存过期导致重复下载
  • 不同job之间缓存冲突
  • 缓存太大拖慢速度

最佳实践示例:

# 为不同分支设置独立缓存键
cache:
  key: "$CI_COMMIT_REF_SLUG"  # 按分支名缓存
  paths:
    - node_modules/
    - .cache/

# 正确上传制品
build:
  script: ./build.sh
  artifacts:
    paths:
      - dist/  # 将构建结果传递给后续job

注意:

  1. 频繁变化的文件不要放缓存
  2. 大文件应该用artifacts而非缓存
  3. 定期清理历史缓存

八、调试技巧:当所有方法都失效时

终极调试手段包括:

  1. 添加详细日志:
debug_job:
  script:
    - echo "当前路径:$(pwd)"
    - echo "环境变量:"
    - printenv
  1. 使用CI_DEBUG_TRACE开启详细输出:
# 在CI变量中设置CI_DEBUG_TRACE为true
  1. 通过interactive web terminal实时调试(需要Runner支持)

总结

排查CI/CD失败的关键是:

  1. 从错误日志入手,找到第一个报错点
  2. 对比本地与CI环境的差异
  3. 善用缓存和制品加速排查
  4. 小步验证,每次只改一个变量

记住,每个失败的流水线都是提升自动化水平的机会。