现如今持续集成(CI)和持续交付/部署(CD)大家已经不陌生了,它们是为了帮助项目能够更快的进行版本迭代。在这篇文章中,我将介绍如何使用使用 GitLab CI/CD 工具进行项目的自动化打包和部署。
CI/CD 介绍
在软件工程中,CI/CD 或 CICD 通常指的是持续集成(Continuous Integration)和持续交付(Continuous Delivery)或持续部署(Continuous Deploy)的组合实践。CI/CD 通过在应用程序的构建、测试和部署中实施自动化,在开发和运营团队之间架起了桥梁。
持续集成
在日常的应用开发中,我们经常会让多位开发人员同时开发不同的功能模块,但是这样会使分支源代码的合并工作很繁琐耗时。这是因为不同开发人员的提交可能会存在冲突。持续集成的目的就是帮助开发人员更加频繁的将代码更改合并到主干分支上。而一旦开发人员的分支代码被合并,系统就会通过自动构建应用并运行不同级别的自动化测试(单元测试和集成测试)来验证这些更改,以确保这些变更并没有引入新的问题到应用中。
持续交付
完成持续集成中构建以及单元测试和集成测试的自动化流程后,持续交付可以自动将已验证的代码发布到存储库(如GitLab)中。此时,运维团队可以轻松快速的将应用部署到线上环境。
持续部署
持续部署是最后的阶段,它可以自动将应用发布到线上环境了,并且记录上线记录方便进行版本回溯。
CI/CD 能够自动完成构建,测试和部署的管道线。它有如下的优势:
- 较小代码的合并,快速的功能迭代
- 每次的构建,测试,部署都是有记录的,能够缩短解决问题的时间
- 每次代码变更都需要进行单元测试和集成测试,降低应用出现问题
- 通过和 GitHub 或 GitLab 这样的源码管理平台配合,使得应用易于维护和更新
CI/CD 工具介绍
近年来随着 DevOps 的兴起,导致了对 CI/CD 工具的强烈需求。目前行业内比较主要流行的 CI/CD 工具是 Jenkins 和 GitLab CI/CD。此外 GitHub 也在 2018年10月推出了 GitHub Actions 来支持 CI/CD。这三者都能实现应用从源码到上线的自动化,但还是各有侧重。
工具 | 源码管理 | 私有化 | 适用群体 |
---|---|---|---|
Jenkins | 不支持 | 支持 | 运维人员 |
GitLab CI/CD | 支持 | 支持 | 企业,开发运维人员 |
GitHub Actions | 支持 | 不支持 | 个人开发者 |
GitLab 是由 GitLab Inc 开发,一款基于 Git 的完全集成的软件开发平台。它的社区版是免费的,提供了 Git 仓库管理,issue 跟踪,代码评审和 CI/CD 等功能。这些功能基本将一个应用的开发周期涉及的操作一网打尽。其实 GitHub 也有这些功能,唯一的缺点可能就是不支持私有化部署。毕竟企业里的项目源码是最重要的财富,放在公网上还是比较忌讳的。所以本文主要围绕 GitLab CI/CD 来介绍它的原理以及实际应用。
原理
谈及 GitLab CI/CD,就不可避开谈及 GitLab Runner 项目,它是一个开源项目,用于执行任务,并将执行结果传输回 GitLab。 Runner 可以安装在 物理机上,也可以安装在虚拟机中,也可以通过 Docker (推荐)的方式安装。它是以守护进程/服务运行着。那 Runner 如何和 GitLab 交互呢,需要我们注册和初始化 Runner,所需要的信息是下图中的链接(标记3)和 token(标记4),并选择一个执行器来执行 .gitlab-ci.yml 文件中的命令(推荐 docker,确保干净,轻量的系统环境)。
GitLab CI/CD 的原理就是 GitLab Runner 服务来启动一个执行器来运行 .gitlab-ci.yml 中各个阶段的脚本命令,并将运行的结果返回给 GitLab。
配置
要使用 GitLab CI/CD 的功能,除了要设置运行环境 GitLab Runner 之外,还需要告诉执行器如何执行。这里就涉及到 .gitlab-ci.yml 的配置。将该文件置于仓库的根目录即可,GitLab CI/CD 会自动去解析此文件。.gitlab-ci.yml 文件的配置详细可以参考 官方文档,这里主要围绕一个样例来介绍一下常用命令。
1# 定义多个阶段stage
2stages:
3 #- test
4 - build
5 - develop
6 - testing
7 - product
8
9# 定义任务job级别的变量
10variables:
11 GIT_STRATEGY: none
12 PROJECT_REPO_NAMESPACE: fmkg
13 PROJECT_REPO_NAME: kg-api
14 SERVER: 127.0.0.1
15 PORT: 22
16 USER: spark
17 DEPLOY_PATH: '/data/spark/yskg-3.0-deploy'
18
19# 定义所有任务前的操作脚本
20before_script:
21 - export ROOT_PATH=$(pwd)
22 - echo 'root path:' $ROOT_PATH
23 - echo 'user:' $DOCKER_USER
24 - eval $(ssh-agent -s)
25 - echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -
26 - mkdir -p ~/.ssh
27 - chmod 700 ~/.ssh
28 - echo "$DOCKER_PW" | docker login http://dockerhub.xxx.com -u $DOCKER_USER --password-stdin
29 - git clone -b $CI_COMMIT_REF_NAME --depth=1 ssh://git@git.xxx.com:58422/$PROJECT_REPO_NAMESPACE/$PROJECT_REPO_NAME.git
30 - cd $PROJECT_REPO_NAME
31 - echo 'commit id:' $CI_COMMIT_SHA
32 - echo 'commit user:' $GITLAB_USER_NAME
33 - echo 'commit e-mail:' $GITLAB_USER_EMAIL
34 - export COMMIT_MESSAGE=$(git log -p -1 --pretty=format:"%s"|head -1)
35 - echo 'commit message:' $COMMIT_MESSAGE
36 - export DATE=$(git log --pretty=format:"%cd %H" --date=format:'%Y%m%d' | grep ${CI_COMMIT_SHA} | awk '{print $1}')
37 - echo 'date:' $DATE
38 - export DOCKER_TAG_SUFFIX="ci"_$DATE"_"${CI_COMMIT_SHA:0:7}
39 - echo 'docker tag suffix:' $DOCKER_TAG_SUFFIX
40
41# 定义代码测试任务test_stage
42#test_stage:
43# stage: test
44# script:
45# - echo "test stage"
46
47# 定义构建镜像任务build_docker
48build_docker:
49 stage: build
50 script:
51 - cd $ROOT_PATH/$PROJECT_REPO_NAME
52 - sh docker/build.sh $DOCKER_TAG_SUFFIX
53 only:
54 - dev
55 allow_failure: false
56
57# 定义开发环境部署任务deploy_dev
58deploy_dev:
59 stage: develop
60 script:
61 - ssh -f -p58422 -o "StrictHostKeyChecking no" haojunyu@jumper-huabei2-vpc.xxx.com -L $PORT:1.1.1.1:22 -N
62 - cmd="cd "${DEPLOY_PATH}" && sh cicd_deploy.sh "${DOCKER_TAG_SUFFIX}
63 - echo $cmd
64 - ssh -o "StrictHostKeyChecking no" $USER@$SERVER -p $PORT $cmd
65 when: manual # 定义何时开始任务 on_success/on_failure/always/manual
66 only:
67 - dev # 定义仅为某些git分支创建任务
68 allow_failure: false # 是否允许任务失败
69
70# 定义测试环境部署任务deploy_test
71deploy_test:
72 stage: testing
73 script:
74 - ssh -f -p58422 -o "StrictHostKeyChecking no" haojunyu@jumper-huabei2-vpc.xxx.com -L $PORT:1.1.1.1:22 -N
75 - cmd="cd /data/spark/yskg-3.0-test && sh cicd_deploy.sh "${DOCKER_TAG_SUFFIX}
76 - echo $cmd
77 - ssh -o "StrictHostKeyChecking no" $USER@$SERVER -p $PORT $cmd
78 when: manual
79 only:
80 - dev
81 allow_failure: false
82
83# 定义生产环境部署任务deploy_prod
84deploy_prod:
85 stage: product
86 script:
87 - ssh -f -p58422 -o "StrictHostKeyChecking no" haojunyu@jumper-huabei2-vpc.xxx.com -L $PORT:1.1.1.1:22 -N
88 - cmd="cd /data/spark/yskg-3.0-deploy && sh cicd_deploy.sh "${DOCKER_TAG_SUFFIX}
89 - echo $cmd
90 - ssh -o "StrictHostKeyChecking no" $USER@$SERVER -p $PORT $cmd
91 when: manual
92 only:
93 - dev
94 allow_failure: false
GitLab 因为集成了代码管理和 CI/CD,所以每一次代码提交 Commit 或者代码合并 MR 都会触发一次 Pipeline 的构建任务,该任务包含多个阶段 stage,比如样例中的测试 test(注释掉了), 构建 build, 发布到开发环境 develop,发布到测试环境 testing,发布到生成环境 product。
阶段 stage 是 Pipeline 上的步骤。一次 Pipeline 会包含多个阶段 Stage。每个阶段 Stage 可以包含多个工作 job。阶段有以下特点:
- 所有阶段 stage 会按照顺序运行,即前一个 stage 完成,后一个 stage 才可以启动。
- 只有当所有 stage 完成后,该 Pipeline 才会成功。如果前一个 stage 失败,后面的 stage 将不会被执行。该 Pipline 也就失败了。
工作 job 是 Pipline 中的最小独立运行单元,表示某个 stage 里执行的工作任务。在一个 stage 中可以有多个 job。这些 job 有如下特点:
- 同一个 stage 中的 job 会并行执行
- 同一个 stage 中只有所有 job 都成功,该 stage 才会成功;任何一个 job 失败,该 stage 都失败。
TIPS:
敏感数据如用户名,密码,私钥等设置在 Secret variables 中,不要定义在 .gitlab-ci.yml 的 variables 里。
效果展示
配置完 .gitlab-ci.yml 后,提交到 GitLab 的仓库中,每当代码更新时,在 CI/CD -> Pipelines 中就会生成一条新版本的流水线任务,在 Stages 列中会依次触发每个流程要走的脚本。样例如下图所示: