引言
看过docker-compose真香的园友可能留意到当时是【把部署dll文件拷贝到生产机器】,即时打包成镜像并启动容器,并没有完成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 中的角色,本次演示【自定义Runner】,gitlab runner安装------> gitlab runner注册 (与Gitlab Project建立联系的过程)
注册时需要关注的两个配置是:
-
tags 可理解为与这个Gitlab Runner有关的Pipeline任务, 在.gitlab-ci.yml会用到
-
runner executor 可理解为执行的初始环境,这里使用shell方式
注册过程和结果请参考下图:
第二部分:基于docker-compose的Gitlab-CI 实践
项目架构图
Gitlab-CI Pipeline构建ReceiverAPP, webAPP镜像(附带本次git:tag)并推送到hub.docker.com;
Gitlab-CD docker-compose拉取远端nginx、ReceiveAPP、webapp镜像启动容器。
-
Pipeline对每一次提交或合并都会执行build任务, 形成Continous Intergation
-
Pipeline对git: tag会执行build_Image任务,自动构建至deploy_staging任务,这样就能形成基于git:tag的部署版本管理(部署出错,也能很快回滚到上次的部署tag)
本处使用Gitlab Runner 服务器作为staging部署机器; 原则上不允许自动随意部署Prod(实践中登陆到 Prod机器上执行部署命令,以下GitLab-CD也没有完成Prod的自动部署过程,自行补上登陆终端的脚本即可)
.gitlab-ci.yml 文件
以上Gitlab Pipeline定义了 build-->build_image-->deploy 三个任务, 某些任务还包括不同分支Job,写.gitlab-ci.yml 的过程就是将以上执行动作脚本化,更多Gitlab-CI的资料。
1 stages: 2 - build 3 - build_image 4 - deploy 5 6 variables: 7 # CI_DEBUG_TRACE: "true" 8 deploy_path: "/home/xxxx/eqidmanager" # CI变量,用于配置部署目录 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: #Pipeline Job构建策略,代码仓库打tag会执行该任务, 支持正则 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的git:tag名称,覆盖.env文件默认配置 49 - "docker-compose -f docker-compose.yml -f docker-compose.prod.yml build" 50 - "docker-compose -f docker-compose.yml -f docker-compose.prod.yml 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 -f docker-compose.yml -f docker-compose.prod.yml build" 61 - "docker-compose -f docker-compose.yml -f docker-compose.prod.yml 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变量
- 密钥变量CI_REGISTRY_USER、CI_REGISTRY_PASSWORD ,可在GitLab-CI 界面配置
- 预定义变量CI_COMMIT_REF_NAME, 该变量标记构建项目的git:branch或git:tag名称,用于生成镜像tag
注意变量可被重写,重写有优先级 http://www.ttlsa.com/auto/gitlab-cicd-variables-zh-docum ent/
第29行; only定义此Job只在产生git:tag时被触发,与上面我们使用 CI-COMMIT_REF_NAME 变量相呼应
第47行: Gialab-CI pipeline每个Job会重新拉取git源码执行Job任务(可登录到Gitlab Runner工作目录下观察Runner执行过程),CD时需要选择合适目录,这是deploy_staging上使用deploy_path CI变量的原因
第48行:注入本次Gitlab-CI git:tag名称, 实际上是覆盖了.env同名环境变量
第49行:若存在docker-compose.yml、docker-compose.override.yml 两个文件,docker-compose命令会自动merge这2个文件(使用docker-compose config命令查看merge 之后的结果)。
第64行:上述Job若没有出错,会自动执行下一步;而when指令定义该Job 是需要在界面上手动执行
Continous Deploy
在Gitlab Runner服务器的{deploy_path}路径下建立了如下部署文件:
├── appsettings.secrets.json ├── docker-compose.prod.yml ├── docker-compose.yml ├── .env ├── EqidManager.db ├── nginx │ ├── Dockerfile │ └── nginx.conf └── receiver.secrets.json
- 最佳实践:在部署目录建立定义docker-compose.yml、docker-compose.prod.yml 两个yml文件,前者定义常规容器服务,后者定义适用于本Prod环境的容器服务
-
软件工作有种最佳实践, 密钥文件不要进入代码管理,因此我们定义appsetting.secrets.json 和 receiver.secrets.json密钥文件,由dccker-compose.yml挂载进入容器
-
.env文件存储相对固定、与本次docker-compose命令相关的环境变量,docker-compose命令默认寻找同级目录下.env文件
------.env 文件----
TAG=master # 该TAG变量会在Pipeline:deploy_staging任务中被覆盖,形成基于git:tag的imageName:tag docker_host=172.16.1.1 COMPOSE_PROJECT_NAME=EqidManager DOCKER_REGISTRY=***
依据.gitlab-ci.yml 文件定义的 deploy_staging脚本:
跳转到部署目录---->应用本次Gitlab-CD要用的git : tag----> 执行docker-compose 命令拉取指定tag镜像并启动容器。
That'all , Runner Job都能在Pipeline找到对应的执行日志; 另外上文对于关键知识均附带传送门,可进一步对比研究。
码甲拙见,如有问题请下方留言大胆斧正;码字+Visio制图,均为原创,看官请不吝好评+关注, ~。。~
本文欢迎转载,请转载页面明显位置注明原作者及原文链接。