GitLab CI/CD 教程:从入门到实践
本教程旨在引导您了解 GitLab CI/CD 的基础知识,从理解其核心概念到为 Web 应用程序构建实用的管道。
什么是 CI/CD?
CI/CD 代表持续集成/持续交付(或部署)。它是一种方法论,在整个应用程序开发生命周期中引入自动化和持续监控,从集成和测试阶段到交付和部署。
- 持续集成 (CI): 开发人员频繁地将他们的代码更改合并到中央存储库中。然后运行自动化构建和测试,以及时快速地发现集成错误。
- 持续交付 (CD): CI 的延伸,确保所有代码更改都自动构建、测试并准备好发布到生产环境。它确保您可以快速持续地向客户发布新更改。
- 持续部署 (CD): 将持续交付更进一步,自动部署通过管道所有阶段的每一个更改。无需人工干预。
为什么选择 GitLab CI/CD?
GitLab CI/CD 是 GitLab 中一个功能强大的内置工具,允许您直接在 GitLab 存储库中实现 CI/CD。它使用一个 .gitlab-ci.yml 文件来定义您的管道,该文件位于项目存储库的根目录中。这种紧密集成提供了以下几个优势:
- 单一应用程序: 从源代码管理到 CI/CD,所有内容都集中在一个地方。
- 易于设置: 配置通过 YAML 文件完成,使其版本受控且易于管理。
- 可伸缩性: 可以处理具有许多阶段和作业的复杂管道。
- 灵活性: 支持各种语言、框架和部署目标。
.gitlab-ci.yml 文件:您的管道定义
.gitlab-ci.yml 文件是您定义 CI/CD 管道的地方。它是一个 YAML 文件,位于项目根目录中。当您将代码推送到 GitLab 存储库时,GitLab Runner(执行您作业的代理)会查找此文件并执行定义的管道。
让我们来看一个针对 Node.js Web 应用程序的综合示例,其中包含详细的注释来解释每个部分。您应该在项目的根目录中创建一个名为 .gitlab-ci.yml 的文件,并将以下内容粘贴到其中:
“`yaml
.gitlab-ci.yml
定义所有作业默认使用的 Docker 镜像,除非另有指定。
此镜像应包含项目所需的工具(例如,Node.js、npm、git)。
image: node:18-alpine
定义管道的阶段。阶段默认并行运行,但阶段内的作业按顺序运行。
这里的顺序定义了阶段的执行顺序。
stages:
– build
– test
– deploy
==================================================================================================
全局缓存配置
==================================================================================================
缓存允许您在作业和管道运行之间重用文件,显著加快执行速度。
这里,我们根据 package-lock.json 文件缓存 Node.js 模块 (node_modules)。
cache:
key:
files:
– package-lock.json # 使用 package-lock.json 生成唯一的缓存键
paths:
– node_modules/ # 要缓存的目录
policy: pull-push # 默认策略:作业前拉取缓存,作业后推送缓存
==================================================================================================
构建阶段作业
==================================================================================================
安装依赖项并构建应用程序的作业。
build_job:
stage: build # 将此作业分配给 ‘build’ 阶段
script:
– echo “Running build job…”
– npm ci # 安装依赖项 (npm ci 在 CI/CD 中优于 npm install)
– npm run build # 执行 package.json 中定义的构建命令
artifacts:
paths:
– build/ # 构建的应用程序文件的路径(例如,React 构建输出)
expire_in: 1 day # 这些工件的保留时间
# 仅在推送到主分支或合并请求时运行此作业
rules:
– if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
– if: $CI_MERGE_REQUEST_IID
==================================================================================================
测试阶段作业
==================================================================================================
运行单元测试的作业。
unit_test_job:
stage: test # 将此作业分配给 ‘test’ 阶段
script:
– echo “Running unit tests…”
– npm ci # 确保安装了依赖项(缓存应该有帮助)
– npm test # 执行 package.json 中定义的测试命令
# 此作业依赖于 ‘build_job’ 成功完成。
# 如果需要,它还将自动从之前的阶段检索工件。
needs: [“build_job”]
rules:
– if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
– if: $CI_MERGE_REQUEST_IID
运行 linting/代码质量检查的作业。
lint_job:
stage: test # 将此作业分配给 ‘test’ 阶段
script:
– echo “Running linting…”
– npm ci # 确保安装了依赖项
– npm run lint # 执行 lint 命令
needs: [“build_job”] # linting 可以与单元测试并行运行,但在构建之后
allow_failure: true # 即使 linting 失败也允许管道继续(可选,取决于策略)
rules:
– if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
– if: $CI_MERGE_REQUEST_IID
==================================================================================================
部署阶段作业
==================================================================================================
部署到预发布环境的作业。
deploy_staging_job:
stage: deploy # 将此作业分配给 ‘deploy’ 阶段
image: alpine/git:latest # 如果需要(例如,带有 git、curl、ssh),为部署使用不同的镜像
script:
– echo “Deploying to staging environment…”
# 在真实场景中,这会涉及以下命令:
# – scp -r build/ [email protected]:/var/www/html
# – ssh [email protected] “sudo systemctl restart webapp”
– echo “Simulating deployment to staging. Artifacts from build_job are available here.”
– ls -la build/ # 验证工件是否存在
environment:
name: staging # 定义环境名称,用于在 GitLab UI 中跟踪部署
url: https://staging.example.com # 可选:部署环境的 URL
needs: [“build_job”, “unit_test_job”] # 确保构建和测试通过后才部署
rules:
– if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH # 仅从默认分支部署预发布环境
部署到生产环境的作业(手动触发)。
deploy_production_job:
stage: deploy # 将此作业分配给 ‘deploy’ 阶段
image: alpine/git:latest # 为部署使用不同的镜像
script:
– echo “Deploying to production environment…”
# 在真实场景中,这会涉及更健壮的部署步骤:
# – kubectl apply -f k8s/production-deployment.yaml
# – aws s3 sync build/ s3://production-bucket
– echo “Simulating deployment to production. Artifacts from build_job are available here.”
– ls -la build/
environment:
name: production # 定义环境名称
url: https://www.example.com # 可选:部署环境的 URL
needs: [“build_job”, “unit_test_job”] # 确保构建和测试通过后才部署到生产环境
when: manual # 此作业仅在从 GitLab UI 手动触发时运行
rules:
– if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH # 仅允许从默认分支部署生产环境
==================================================================================================
可选:审查应用程序部署(用于合并请求)
==================================================================================================
此作业为每个合并请求创建一个临时“审查应用程序”,
允许审阅者在合并之前实时查看更改。
review_app_job:
stage: deploy
image: alpine/git:latest
script:
– echo “Deploying review app for MR $CI_MERGE_REQUEST_IID…”
# 在真实场景中,这会预配一个临时环境:
# – deploy_script –environment=review-$CI_MERGE_REQUEST_IID –branch=$CI_COMMIT_REF_SLUG
– echo “Review app URL: https://review-$CI_MERGE_REQUEST_IID.example.com”
environment:
name: review/$CI_COMMIT_REF_SLUG # 基于分支的动态环境名称
url: https://review-$CI_MERGE_REQUEST_IID.example.com
on_stop: stop_review_app_job # 定义一个停止此环境的作业
rules:
– if: $CI_MERGE_REQUEST_IID # 仅在合并请求时运行
当合并请求关闭或合并时停止审查应用程序的作业。
stop_review_app_job:
stage: deploy
image: alpine/git:latest
script:
– echo “Stopping review app for MR $CI_MERGE_REQUEST_IID…”
# – undeploy_script –environment=review-$CI_MERGE_REQUEST_IID
environment:
name: review/$CI_COMMIT_REF_SLUG
action: stop # 将此作业标记为停止环境
rules:
– if: $CI_MERGE_REQUEST_IID
when: manual # 通常是手动操作,也可以在 MR 关闭/合并时自动触发
“`
核心概念解释
1. image
- 目的: 指定 GitLab Runner 将用于执行作业的 Docker 镜像。此镜像应包含项目所需的所有工具、语言和依赖项(例如,Node.js、Python、Java、Maven、npm、git)。
- 示例:
image: node:18-alpine意味着所有作业都将在基于node:18-alpine镜像的 Docker 容器中运行,该镜像包含 Node.js 和 npm。 - 按作业
image: 如果特定作业需要不同的环境(例如,部署作业可能需要alpine/git用于ssh或kubectl),您可以为这些作业覆盖全局image。
2. stages
- 目的: 定义作业的逻辑顺序和分组。同一阶段内的作业并行运行。阶段本身按顺序运行。
- 示例:
stages: [build, test, deploy]意味着所有build作业将在任何test作业开始之前完成,并且所有test作业将在任何deploy作业开始之前完成。
3. jobs
- 目的: 管道的基本组成部分。每个作业都是一个独立的工作单元,执行一系列命令。
- 结构: 每个作业都有一个唯一的名称(例如,
build_job、unit_test_job)并且必须属于一个stage。 - 作业内的关键关键字:
script:作业的核心,包含要执行的 shell 命令。stage:将作业分配给已定义的阶段。image:(可选)覆盖此特定作业的全局镜像。before_script:(可选)在作业中的主script之前运行的命令。after_script:(可选)在作业中的主script之后运行的命令,无论其成功或失败。
4. script
- 目的: 包含作业将执行的实际 shell 命令。这些命令在指定的 Docker 镜像的上下文中运行。
- 示例:
npm ci、npm run build、npm test。
5. cache
- 目的: 通过在作业和后续管道运行之间重用文件(如已安装的依赖项)来加速管道执行。
key: 定义如何识别缓存。使用files:与package-lock.json确保只有当依赖项更改时才使缓存无效并重新构建。paths: 指定要缓存的目录或文件(例如,node_modules/)。policy: 控制何时拉取 (pull)、推送 (push) 或两者 (pull-push) 缓存。
6. artifacts
- 目的: 允许您指定应从作业工作区中提取并由 GitLab 保存的文件或目录。然后可以从 GitLab UI 下载这些工件,或将其传递给管道中的后续作业。
paths: 指定要收集的文件/目录(例如,build/)。expire_in: 定义工件应存储多长时间。- 用例: 将编译后的代码从
build作业传递到deploy作业。
7. needs
- 目的: 显式定义作业之间的依赖关系。具有
needs的作业只有在所有指定的作业都成功完成后才会开始,无论它们所处的阶段如何。这允许不同阶段的作业在满足依赖关系后更早地并行运行。 - 示例:
needs: ["build_job", "unit_test_job"]意味着当前作业将等待build_job和unit_test_job都成功完成才能开始。
8. rules (或 only/except)
- 目的: 根据各种条件(例如,分支名称、标签、合并请求状态)控制何时将作业包含在管道中。
rules(推荐): 比only/except更灵活和强大。它允许复杂的条件逻辑。if:指定一个条件(例如,$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH)。when:定义作业何时运行(on_success、on_failure、manual、always、never)。
- 示例:
if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH使作业仅在默认分支(例如,main)上运行。if: $CI_MERGE_REQUEST_IID使其仅在合并请求时运行。
9. environment
- 目的: 用于部署作业,以定义和跟踪部署环境(例如,
staging、production、review)。GitLab 提供了一个专门用于环境的 UI。 name: 环境的名称。url:(可选)部署应用程序的 URL,将显示在 GitLab UI 中。on_stop:(可选)指定一个应运行以拆除环境的作业(对于动态审查应用程序很有用)。
10. when: manual
- 目的: 使作业需要从 GitLab UI 手动触发。这通常用于生产等敏感部署,需要人工批准。
11. 预定义 CI/CD 变量
GitLab 提供了许多预定义变量,您可以在 .gitlab-ci.yml 文件中使用。一些常见的变量包括:
$CI_COMMIT_BRANCH:管道运行所针对的分支或标签的名称。$CI_DEFAULT_BRANCH:项目的默认分支(通常是main或master)。$CI_MERGE_REQUEST_IID:如果管道用于合并请求,则为合并请求的 IID。$CI_PROJECT_DIR:克隆项目目录的完整路径。$CI_COMMIT_SHORT_SHA:提交 SHA 的前 8 个字符。
入门
- 创建 GitLab 项目: 如果您没有 GitLab 项目,请在 GitLab 上创建一个新项目。
- 添加您的代码: 将您的应用程序代码推送到存储库。
- 创建
.gitlab-ci.yml: 将上面提供的示例.gitlab-ci.yml内容复制到项目根目录中名为.gitlab-ci.yml的文件中。 - 提交并推送: 提交
.gitlab-ci.yml文件并将其推送到您的 GitLab 存储库。 - 观察管道: 在您的 GitLab 项目中,转到
CI/CD > Pipelines。您应该会看到您的管道正在运行。 - 配置 Runner: 确保您已配置并向项目注册了 GitLab Runner。对于 GitLab.com 上的共享 Runner,这通常会自动处理。对于自托管 GitLab,您需要设置自己的 Runner。
后续步骤
- 探索更多
rules: 深入了解作业的复杂条件逻辑。 - 变量: 了解自定义 CI/CD 变量(项目、组或实例级别),用于敏感数据(API 密钥、密码)或配置。
- Include: 在多个项目之间重用
.gitlab-ci.yml配置。 - 模板: 利用 GitLab 的内置 CI/CD 模板,适用于常见的语言和框架。
- 安全扫描: 集成 SAST、DAST、依赖项扫描等安全工具。
- 审查应用程序: 为每个合并请求设置动态环境。
本教程为使用 GitLab 构建健壮的 CI/CD 管道奠定了坚实的基础。祝您自动化愉快!