【干货】基于镜像部署的Gitlab-CI/CD实践和坑位指南

C#

浏览数:193

2019-8-16

AD:资源代下载服务

引言

         看过前文的博友可能注意到我是把 部署dll文件拷贝到生产机器,之后在生产机器上使用docker-compose即时创建镜像, 并没有完成CI/CD, 只是在原来传统部署方式下 将部署文件容器化。

         经过长时间实操验证,终于完成基于Gitlab的CI/CD实践,本次实践的坑位很多, 实操的过程尽量接近 最佳实践(不做hack, 不做骚操作),记录下来也方便自己加深理解。

        第一部分: Gitlab CI/CD 原理 和 Gitlab Runner 安装(这里使用shell执行器)

        第二部分: Gitlab CI/CD 实践:

  •      宏观项目架构图
  •      解决方案开发目录
  •     .gitlab-ci.yml 文件
  •      项目部署目录

第一部分:gitlab CICD原理

       ① Gitlab CI/CD架构:

  • Gitlab CI/CD  存储【定义的构建】和【构建状态】的api应用程序, 提供友好的界面管理项目构建,  构建过程由 .gitlab-ci.yml 文件定义,而这个文件一般置于代码仓库的根目录。
  • Gitlab Runner  执行构建过程的应用程序,可与gitlab Server 形成分布式部署, 如上图所示, 其通过api 与gitlab Server交互

   

  ② Gitlab CI/CD 配置界面 & Gitlab Runner 安装

           Gitlab CICD的界面配置权限取决于你在gitlab server 中的角色,我的角色是maintainer, 足够创建自定义的runner。

            本次演示的是创建自定义Runner,gitlab runner安装完毕,很明显现在需要与gitlab项目建立联系,这就是向gitlab runner注册项目的过程。

      注册时需要关注的两个配置是:

  •     tags  可理解为与这个Runner有关的Pipeline任务, 在.gitlab-ci.yml  要用到
  •     runner  executor:可理解为执行的初始环境,这里使用shell方式

      注册过程和结果请参考下图:

第二部分:基于docker-compose的Gitlab-CI  实践

     ①  项目架构图

      

       我们的目标是在部署机器上使用docker-compose拉取Gitlab CI生成的ReceiverAPP, webAPP远端镜像,快速产生容器。 

  •      对每一次提交或合并都会执行 build 任务, 形成Continous Intergation

  •       对git: tag会执行build_Image任务,上述两个任务都成功,会自动执行 deploy_staging任务,这样就能形成基于git:tag的部署版本管理(部署出错,也能很快回滚到上次的部署tag)

本处使用Gitlab Runner 服务器作为staging部署机器; 原则上不允许随意部署Prod, 常规让开发人员手动登陆到 Prod机器上执行部署命令,以下GitLab-CI 也没有完成Prod的自动部署过程,自行补上登陆终端的脚本即可。

     

  ②  开发目录

   .gitlab-ci.yml 需要放置在仓库根目录下,以下.gitlab-CI.yml 文件定义了src目录下 EqidManager , EqidReceiver 两个项目的构建过程, 写.gitlab-ci.yml 的过程就是 将平日里使用docker打包镜像并发布的行为 脚本化

├── container
   ── app/Dockerfile
── receiver/Dockerfile
├── docker-compose.dcproj 
├── docker-compose.override.yml
├── docker-compose.yml
├── .dockerignore
├── .env
├── Eqid-Manager.sln
├── .git
├── .gitattributes
├── .gitignore
├── .gitlab-ci.yml
├── NuGet.Config
├── src
── EqidManager
── EqidReceiver
└── test
── EqidProxyServer.Tests

   

  ③ .gitlab-ci.yml 文件

       对比开发目录,我们定义了  build–>build_image–>deploy 三个任务, 某些任务还包括不同分支Job:

 1 stages:
 2   - build
 3   - build_image
 4   - deploy
 5 
 6 variables:         
 7 # CI_DEBUG_TRACE: "true"                                         # 增加调试追踪
 8   deploy_path: "/home/xxxx/eqidmanager"
 9 
10 before_script:
11   - "docker info"
12 
13 build:
14   stage: build
15   script: 
16     - "for d in $(ls src);do echo $d;prog=$(pwd)/src/$d/$d.csproj; dotnet build $prog; done"
17   tags:                                                 # 这里必须是字符串数组
18     - another-tag
19 
20 build_image:EqidManager:
21   stage: build_image
22   script:
23     - dotnet publish src/EqidManager/EqidManager.csproj  -c release -o ../../container/app/publish/    
24     - docker build --pull  -t $CI_REGISTRY_USER/eqidmanager:$CI_COMMIT_REF_NAME  container/app
25     - docker login -u $CI_REGISTRY_USER  -p $CI_REGISTRY_PASSWORD      
26     - docker push $CI_REGISTRY_USER/eqidmanager:$CI_COMMIT_REF_NAME     
27   tags:    
28     - another-tag
29   only:                                 # 构建策略
30     - tags
31 
32 build_image:EqidReceiver:
33   stage: build_image
34   script:
35     - dotnet publish src/EqidReceiver/EqidReceiver.csproj  -c release -o ../../container/receiver/publish
36     - docker build -t $CI_REGISTRY_USER/eqidreceiver:$CI_COMMIT_REF_NAME container/receiver
37     - docker login -u $CI_REGISTRY_USER  -p $CI_REGISTRY_PASSWORD
38     - docker push $CI_REGISTRY_USER/eqidreceiver:$CI_COMMIT_REF_NAME
39   tags: 
40     - my-tag
41   only:
42     - tags
43 
44 deploy:staging:
45   stage: deploy
46   script:
47     - cd $deploy_path
48     - export TAG=$CI_COMMIT_REF_NAME                # 引入本次CI的tag名称,覆盖.env 文件默认配置
49     - "docker-compose build"                        # 根据docker-compose命令的用法, 会自动merge docker-compose.yml和override文件
50     - "docker-compose up -d" 
51   tags: 
52     - my-tag
53 
54 deploy:prod:
55   stage: deploy
56   script:
57     - # TODO 需要写脚本登陆到Prod机器上
58     - export TAG=$CI_COMMIT_REF_NAME        
59     - cd $deploy_path
60     - "docker-compose  build"
61     - "docker-compose  up -d" 
62   tags:
63     - my-tag
64   when: manual

 这里有些知识点和 坑位需要指出:

第8行: 预先定义的环境变量,该变量定义gitlab CD的部署目录

第16行: 对src开发目录下两个程序执行dotnet build命令

第17行: tags定义具备该tags的Runner可以执行该任务, 注意这里的tags必须是字符串数组

第23-26行:常规构建镜像并推送到镜像仓库的过程,用到
   – 密钥环境变量CI_REGISTRY_USER、CI_REGISTRY_PASSWORD ,可在GitLab-CI 界面配置
     – 预定义环境变量CI_COMMIT_REF_NAME, 该变量标记构建项目的分支或tag名称, 与下方的only 形成呼应
     –  注意变量可被重写,重写有优先级 http://www.ttlsa.com/auto/gitlab-cicd-variables-zh-document/


第29行; only定义此Job只在git:tags时执行,也就是说切出tag,才会构建镜像

第47行: Gialab-ci pipeline每个stage都会重新拉取git源码执行pipeline任务,我们不能在gitlab Runner的工作目录下部署项目,这也是上面我们定义 deploy_path CI变量的原因

第48行:注入本次Gitlab CI 标签名, 实际上是覆盖了.env同名环境变量

第49行: 部署目录会定义docker-compose.yml 和 docker-compose.override.yml 两个文件,前者用于容器服务的易变配置,后者定义 容器服务的基础配置, docker-compose 命令会自动merge这2个文件

更多Gitlab-CI的用法: https://docs.gitlab.com/ee/ci/yaml/

  ④ Continous Deploy 部署目录

 在staging机器的deploy_path目录下建立了如下部署文件:

 其中 appsetting.secrets.json 和 receiver.secrets.json 会在dccker-compose.yml 中被挂载, 这也是一种原则: 密钥文件不要进入git的代码管理

├── appsettings.secrets.json
├── docker-compose.override.yml
├── docker-compose.yml
├── .env
├── EqidManager.db
├── nginx
│   ├── Dockerfile
│   └── nginx.conf
└── receiver.secrets.json

  dockee-compose 默认会使用同级目录下的.env 文件, 这个文件存储相对固定的环境变量:

----- .env 文件----
TAG=master           # 该TAG变量会在CI:deploy_staging脚本中被覆盖 docker_host=172.16.1.1 COMPOSE_PROJECT_NAME=EqidManager DOCKER_REGISTRY=***

 这样staging部署环境也准备完毕,依据.gitlab-ci.yml 文件定义的 deploy_staging脚本:

 跳转到部署目录—> 应用本次gitlab-ci 的git:tag—-> 执行docker-compose 命令拉取镜像并部署。

作者:
JulianHuang

码甲拙见,如有问题请下方留言大胆斧正;码字+Visio制图,均为原创,看官请不吝好评+关注,  ~。。~

本文欢迎转载,请转载页面明显位置注明原作者及原文链接

作者:头号码甲