敏捷开发与Gitlab CI/CD持续集成

概述

敏捷软件开发(Agile software development),是一种应对快速变化的需求的一种软件开发能力。强调开发团队与业务需求方之间的紧密协作、面对面沟通(认为比书面的文档更有效)、频繁交付新的软件版本、紧凑而自我组织型的团队、能够很好地适应需求变化的代码编写和团队组织方法,也更注重软件开发过程中人的作用。敏捷开发的流程分为几个阶段:编码 -> 构建 -> 集成 -> 测试 -> 交付 -> 部署。而CI/CD是实现这一理念的方法。

持续集成CI(Continuous integration)

持续集成(Continuous integration),简称CI,是一种软件开发实践。开发人员提交代码后,系统自动进行构建、(单元)测试,通过自动化测试保障所有的提交在合并主线之后不会出现质量问题,对可能出现的一些问题进行预警。

CI

需要具备的条件

  • 需要为每个新功能、代码改进、或者问题修复创建自动化测试用例。
  • 你需要一个持续集成服务器,它可以监控代码提交情况,对每个新的提交进行自动化测试。
  • 尽可能快地提交代码。

带来的效益

  • 通过自动化测试可以拿到测试结果,避免将一些问题提交到交付生产中。

  • 发布编译将会更加容易,因为合并之初已经将所有问题都规避了。

  • 减少工作问题切换,研发可以很快获得构建失败的消息,在开始下一个任务之前就可以很快解决。

  • 测试成本大幅降低,你的CI服务器可以在几秒钟之内运行上百条测试。

  • 团队花费在测试上面的时间会大幅缩短,将会更加侧重于质量方面的提升上面。

持续交付CD(Continuous Delivery)

持续交付(Continuous Delivery),简称CD:是一种软件工程的手法。持续交付在持续集成的基础上,将集成后的代码部署到更贴近真实运行环境的「类生产环境」(production-like environments)中,也就是我们通常说的预发布环境。交付给质量团队或者用户,以供评审。如果评审通过,代码就进入生产阶段。持续交付并不是指软件每一个改动都要尽快部署到产品环境中,它指的是任何的代码修改都可以在任何时候实施部署。

持续交付流程

需要具备的条件

  • 需要有强大的持续集成组件和足够多的测试用例满足代码的需求。
  • 部署需要自动化。触发是手动的,但是部署一旦开始,就不能人为干预。
  • 团队可能需要接受特性开关,没有完成的功能模块不会影响到线上产品。

带来的效益

  • 繁琐的部署工作没有了。开发团队不再需要花费几天的时间去准备一个发布。

  • 你可以更快的进行交付,这样就加快了与需求方之间的反馈环。

  • 轻松应对小变更,加速迭代。

持续部署CD(Continuous Deploymen)

持续部署(Continuous Deployment),也是简称CD:指当交付的代码通过评审之后,自动部署到生产环境中。持续部署是持续交付的最高阶段。开发人员可以专注于构建软件,他们看到他们的修改在他们完成工作后几分钟就上线了。基本上,当开发人员在主分支中合并一个提交时,这个分支将被构建、测试,如果一切顺利,则部署到生产环境中。

持续部署流程

需要具备的条件

  • 研发团队测试理念比较完善。测试单元的健壮性直接决定着交付质量。
  • 文档和部署频率要保持一致。
  • 特征标志成为发布重大变化过程的固有部分,以确保您可以与其他部门(支持,市场营销,公关…)协调。

带来的效益

  • 发布频率更快,因为你不需要停下来等待发布。每一处提交都会自动触发发布流。
  • 在小批量发布的时候,风险降低了,发现问题也可以很轻松的修复。
  • 客户每天都可以看到我们的持续改进和提升,而不是每个月或者每季度,或者每年。

持续交付与持续部署的关系

持续部署意味着所有的变更都会被自动部署到生产环境中。持续交付意味着所有的变更都可以被部署到生产环境中,但是出于业务考虑,可以选择不部署。如果要实施持续部署,必须先实施持续交付。

  1. 持续交付表示的是一种能力;
  2. 而持续部署则是一种方式。

Gitlab CI/CD介绍

Gitlab CI/CD是Gitlab一个简洁好用的的持续集成/持续交付/持续部署的框架。为项目配置一个或者多个 GitLab Runner,然后添加一个.gitlab-ci.yml文件到项目根目录,进行提交或者推送代码到Gitlab服务器,就可以很方便地持续集成/部署代码。.gitlab-ci.yml文件会告诉Gitlab Runner做什么。

Gitlab CI/CD运行原理

开发者推送、提交代码到Gitlab,Gitlab通过项目的.gitlab-ci.yml文件配置,找到指定的项目gitlab runner,runner运行相关的命令,进行编译、 集成、测试、交付、部署,一切顺利地话会分发到各个服务器(测试服务器、预发布服务器、正式服务器等),此时一个迭代开发上线流程走完。流程图如下。

Gitlab CI_CD运行原理

GitLab Runner

GitLab Runner是一个开源项目,用于运行项目持续集成、持续部署作业并将结果发送回GitLab,与GitLab CI/CD一起使用。GitLab Runner是用Go编写的,可以作为单个二进制文件运行,不需要语言特定的要求,运行在Linux,macOS和Windows操作系统上。只要您可以在其上编译Go二进制文件,其他操作系统可能会起作用,也可以运行在Docker上。

执行流程

默认情况下,它运行有三个流水线阶段stage:buildtest,和deploy,可以在.gitlab-ci.yml的stages自定义,多个阶段是按顺序执行的,可以修改顺序,如果其中一个阶段失败,则后续的阶段不会被执行,整个过程被认为失败。

stage执行流程

Stage 在 .gitlab-ci.yml 中通过如下的方式定义:

stages: 
  - build
  - test
  - deploy

任务Job可以关联到一个阶段stage,当一个 Stage 执行的时候,与其关联的所有 Job 都会被执行。一个阶段可以有多个任务Job,同个阶段的任务直接是可并行执行的。

Job 在 .gitlab-ci.yml 中关联Stage示例如下:

build:
  stage: build
  script:
    - cd ~/go/src/$CI_PROJECT_NAME/
    - go build

至此,执行流程就讲完了,接下来就是详细的配置过程。

具体配置过程

安装gitlab runner

这里以安装到Linux为例,参考官网教程,选用一台编译服务器用root账号执行。

下载gitlab runner二进制文件放在bin目录:

wget -O /usr/local/bin/gitlab-runner https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-386

赋予它可执行权限

chmod +x /usr/local/bin/gitlab-runner

创建一个gitlab CI用户:

useradd --comment 'GitLab Runner' --create-home gitlab-runner --shell /bin/bash

安装并作为服务运行

# 安装
gitlab-runner install --user=gitlab-runner --working-directory=/home/gitlab-runner
# 运行
gitlab-runner start

注册到gitlab

在gitlab项目获取URL地址和token令牌,如下:
runner配置

此时得到

URL地址:https://gitlab.xxxx.com
token令牌:TOKEN
  1. 启动注册功能:

    gitlab-runner register
    
  2. 输入gitlab项目的URL地址,:

    Please enter the gitlab-ci coordinator URL (e.g. https://gitlab.com )
    https://gitlab.xxxx.com
    
  3. 输入令牌:

    Please enter the gitlab-ci token for this runner
    TOKEN
    
  4. 输入Runner的描述,可以在GitLab修改:

     Please enter the gitlab-ci description for this runner
     [hostame] my-runner
    
  5. 给Runner添加标签,这里我标记为运行goland的:

    Please enter the gitlab-ci tags for this runner (comma separated):
    goland
    

    .gitlab-ci.yml 中可以设置 tags 字段来声明,当前任务只在拥有匹配 Tags 的 Runner 上运行。比如 iOS 编译阶段只能在 Mac Runner 上运行,那么就可以设置这个 Runner 的 Tags 为 ‘iOS’,并且在 iOS 工程中在字段 tags 中,添加‘iOS’ 值。 Tags 最好不要随便命名,遵循适当的命名规则会让后期 CI 的维护轻松许多。

  6. 选择Runner执行程序,这里选择用shell:

    Please enter the executor: ssh, docker+machine, docker-ssh+machine, kubernetes, docker, parallels, virtualbox, docker-ssh, shell:
    shell
    

在gitlab项目查看是否允许,或者管理此runner:

runner运行状态

runner其它命令

  1. gitlab-runner --debug run,如果你遇到一些错误,可以使用这个命令来在前端(控制台运行),查看log
  2. gitlab-runner stop ,停止运行
  3. gitlab-runner run --user jafir(普通用户),如果需要切换用户可以使用这个
  4. gitlab-runner uninstall,如果想卸载从头再来
  5. gitlab-runner status,查看状态
  6. gitlab-runner verify,查看runner是否在运行后
  7. gitlab-runner verify --delete,删除注册的用户,如果想要从头再来
  8. 删除 ~/.gitlab-runner/config.toml(注册的用户的配置文件),和/etc/gitlab-runner/config.toml,如果想要从头再来

创建.gitlab-ci.yml文件

这里用go语言的编译发版的示例,你可以根据自己的需求配置:

stages: 
  - build
  - deploy

before_script:
    - export NOW_DATE_TIME=$CI_PROJECT_NAME$(git show -s --format=%cd --date=format:%m-%d_%H:%M $CI_COMMIT_SHA)

variables:
  SUPERVISORD_WORKER: "ci_cd_test-worker:ci_cd_test-worker_00"
  FILE_NAME: "meisha_account"
  TEST_HOST_IP: '127.0.0.1'
  PROD_HOST_IP: '127.0.0.2'

build_stage:
  stage: build
  script:
    - rm -rf ~/go/src/$CI_PROJECT_NAME
    - cp -r $CI_PROJECT_DIR ~/go/src/
    - cd ~/go/src/$CI_PROJECT_NAME/
    - go get github.com/jsooo/log
    - go build
  tags:
    - golang

deploy_test:
  stage: deploy
  only:
    refs:
      - test
  script:
    - ssh -T -q -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o CheckHostIP=false test@$TEST_HOST_IP -p 23 "cd /home/test/projects/go/src/$CI_PROJECT_NAME/ && mv $FILE_NAME $NOW_DATE_TIME"
    - scp -P 56000 -q ~/go/src/$CI_PROJECT_NAME/$FILE_NAME test@$TEST_HOST_IP:/home/test/projects/go/src/$CI_PROJECT_NAME/
    - ssh -T -q -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o CheckHostIP=false root@$TEST_HOST_IP -p 23 "supervisorctl -c /etc/supervisord/supervisord.conf restart $SUPERVISORD_WORKER"
    - echo '发到测试了'
  when: on_success
  tags:
    - golang

deploy_master:
  stage: deploy
  only:
    refs:
      - master
  script:
    - ssh -T -q -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o CheckHostIP=false user_00@$PROD_HOST_IP -p 23 "cd /data/publish/$CI_PROJECT_NAME/ && mv $FILE_NAME $NOW_DATE_TIME"
    - scp -P 56000 -q ~/go/src/$CI_PROJECT_NAME/$FILE_NAME user_00@PROD_HOST_IP:/data/publish/$CI_PROJECT_NAME/
    - ssh -T -q -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o CheckHostIP=false root@$PROD_HOST_IP -p 23 "supervisorctl -c /etc/supervisord/supervisord.conf restart $SUPERVISORD_WORKER"
    - echo '发到测试了'
  when: on_success
  tags:
    - golang

配置说明

  1. before_script 这一部分执行阶段都会进行,我这执行的是获取git commit提交的时间,赋值给NOW_DATE_TIME 变量;
  2. variables 是定义一些变量,这里定义了测试、正式的IP地址,打包编译的文件名,supervisorctl的进程名;
  3. build_stagedeploy_testdeploy_master是一个个任务,可以自定义名称,它们用stage关联了某个阶段,比如build_stage关联了build阶段,deploy_test关联了deploy阶段,deploy_master也关联了deploy阶段。
  4. only 定义哪个分支提交才会执行,我这里定义了提交到test分支就部署到测试环境,提交到master分支就发布到正式环境。
  5. script 是任务的主要内容,需要执行的命令都写在这。在build阶段,用go build命令打包go程序。在deploy阶段就把打包后的文件复制到服务器,然后用supervisorctl重启进程。
  6. when 定义了什么时候才执行。
  7. tags标签指定gitlab runner。

具体配置可以参考gitlab的官网:https://docs.gitlab.com/ee/ci/yaml/README.html