利用gitlab的ci/cd发布PHP代码

 

公司某个PHP项目发布一直手动执行shell(基本逻辑是备份,指定php文件名并scp, reload php-fpm).用起来相当麻烦而且容易出错, 开发人员提出能不能协助简化操作.

对比了以下方案:

  1. 改写shell为rsync, 实现自动差异对比, 省去输入文件名的麻烦.
  2. 使用国内比较流行的jenkins搭建一套发布系统.
  3. 最新的gitlab(9.x)支持内置的ci/cd功能.

通过详细的功能性和便捷性考察, 最终选择使用gitlab内置的ci功能. 原因有:

  1. 一步到位, 用shell不方便查询发布历史和发布结果.
  2. 现在有一个低版本的源码编译的gitlab, 再引入jenkins还需要引入账号权限设置, 成本有些高.
  3. gitlab ci功能满足, 方便管理. BTW, gitlab的产品理念和公司理念都很不错.

 

实施步骤:

升级gitlab(源码编译改为同版本号的Omnibus版, 然后升级Omnibus版本到最新版)

安装gitlab-runner, 并考察几种发布方式的优缺点.

 

刚开始出于安全性考虑, gitlab-runner选用的是docker模式, 后来发现性能比shell模式差比较多.由于系统边界管理做的还不错, 为了追求性能改为了shell模式. 各位看各自的实际情况进行选择.

 

安装步骤官网描述的很清晰, 就不再赘述. 只列出了.gitlab-ci.yml的例子.

 

这个例子能用gitlab的账号系统, 登录后一键点击实现:

  1. 发布代码到预发布环境.
  2. 发布代码到生产环境.
  3. 手动会退到指定版本.
  4. 可以方便查看发布频率和状态.
before_script:
  - JOB_NAME=( $CI_BUILD_NAME ) && export SSH_HOST=${JOB_NAME[1]} && echo $SSH_HOST
  - RUN_MODE_SETTING=production
  - date
  - git status
  - git show && echo $?

stages:
  - deploy_staging
  - deploy_production
  - re_trigger_deploy

.rsync_artifacts:
  script: &rsync_artifacts |
    rsync -rtlpvz --delete $CI_PROJECT_DIR/ $SSH_USER@$SSH_HOST:$PROD_BASE_DIR/ --exclude='.git' --exclude='.gitlab-ci.yml' --exclude='.gitignore' --exclude='some/folder/'
    date

.reload_php7_fpm:
  script: &reload_php7_fpm |
    ssh $SSH_USER@$SSH_HOST "sudo /bin/bash -c 'date >> /tmp/ci.log; /etc/init.d/php7-fpm reload >>/tmp/ci.log 2>&1' &"

.do_other_command:
  script: &do_other_command |
    ssh $SSH_USER@$SSH_HOST "sudo /bin/bash -c 'date >> /tmp/ci.log;/bin/bash other_shell.sh arg >>/tmp/ci.log 2>&1' &"

.deploy_production: &deploy_production
  stage: deploy_production
  environment: production
  only:
    - master
    - triggers

.deploy_staging: &deploy_staging
  stage: deploy_staging
  environment: staging
  when: manual

.deploy_staging_template: &deploy_staging_template
  <<: *deploy_staging
  script:
    - '[ "${DEPLOY_PRODUCTION}" != "true" ] || exit 0'
    - *rsync_artifacts
    - *reload_php7_fpm
    - export RUN_MODE_SETTING=beta
    - *do_other_command
    - date

.deploy_production_template: &deploy_production_template
  <<: *deploy_production
  script:
    - '[ "${DEPLOY_PRODUCTION}" != "true" ] && exit 0'
    - *rsync_artifacts
    - *reload_php7_fpm

# ################上面是模板,下面是调用配置################
# 发布到服务器1
deploy_production 1.1.1.1: *deploy_production_template
# 发布到服务器2
deploy_production 2.2.2.2: *deploy_production_template
# 发布到预发布环境3
deploy_staging 3.3.3.3: *deploy_staging_template
# 发布到服务器4, 并执行其他命令
deploy_production 4.4.4.4:
  <<: *deploy_production
  script:
    - '[ "${DEPLOY_PRODUCTION}" != "true" ] && exit 0'
    - *rsync_artifacts
    - *reload_php7_fpm
    - *do_other_command
    - date

trigger_build:
  stage: re_trigger_deploy
  environment: production
  script:
    - echo ${CI_PROJECT_ID}
    - "curl -X POST -F token=${CI_TRIGGER_TOKEN} -F ref=master -F variables[DEPLOY_PRODUCTION]=true https://gitlab.yourcompany.com/api/v3/projects/${CI_PROJECT_ID}/trigger/builds"
  when: manual
  tags:
    - api
# 手工回退的方法:
# 1. 回退的SHA-1点打tag.例如: revert-20170322, 并把tag推送到服务器.
# 2. 模拟curl -X POST -F token=${CI_TRIGGER_TOKEN} -F "ref=revert-20170322" -F "variables[DEPLOY_PRODUCTION]=true" https://gitlab.yourcompany.com/api/v3/projects/${CI_PROJECT_ID}/trigger/builds

不足: 由于这个版本的gitlab ci在工作流下一步判断的时候逻辑有问题, shell的function功能支持也不是很好, 有了这么一个workaround的方式. 后续版本的gitlab可能会修复这些问题, 会更方便.