老镜 發表於 2019-8-16 13:36:00

基于docker-compose的Gitlab CI/CD实践&排坑指南

<h2>引言</h2>
<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;看过docker-compose真香的园友可能留意到当时是【把部署dll文件拷贝到生产机器】,即时打包成镜像并启动容器,并没有完成CI/CD。</p>
<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;经过长时间实操验证,终于完成基于Gitlab的CI/CD实践,本次实践的坑位很多, 实操过程尽量接近最佳实践(不做hack, 不做骚操作),记录下来加深理解。</p>
<p>&nbsp; &nbsp; &nbsp; &nbsp; 第一部分: Gitlab CI/CD 原理 和 Gitlab Runner 安装(这里使用shell执行器)</p>
<p>&nbsp; &nbsp; &nbsp; &nbsp; 第二部分: Gitlab CI/CD 实践:</p>
<ul>
<li>&nbsp; &nbsp; &nbsp;宏观项目架构图</li>
<li>&nbsp; &nbsp; .gitlab-ci.yml 文件</li>
<li>&nbsp; &nbsp; &nbsp;项目部署目录</li>
</ul>
<h2>第一部分:gitlab CICD原理</h2>
<h3>Gitlab CI/CD架构</h3>
<p><img src="https://img2018.cnblogs.com/blog/587720/201908/587720-20190815183039931-621270403.png" alt=""></p>
<p>&nbsp;</p>
<ul>
<li>Gitlab CI/CD&nbsp; &nbsp;存储【构建】和【构建状态】的api应用程序, 提供友好的管理界面,&nbsp; 构建过程由 .gitlab-ci.yml文件定义,而这个文件一般置于代码仓库的根目录。</li>
</ul>
<ul>
<li>Gitlab Runner 执行构建过程的应用程序,可与Gitlab Server 形成分布式部署, 如上图所示, 其通过api 与Gitlab Server交互</li>
</ul>
<h3>Gitlab CI/CD 配置界面 &amp; Gitlab Runner 安装</h3>
<p>Gitlab CI/CD提供配置界面(项目菜单栏-设置-CI/CD),可指定</p>
<p>&nbsp; - 将要使用何种形式的Runner</p>
<p>&nbsp; -&nbsp; 配置Runner要用到环境变量</p>
<p>&nbsp; &nbsp; &nbsp; 注册时需要关注的两个配置是:</p>
<ul>
<li>
<p>&nbsp; &nbsp; <em>tags</em>&nbsp;与此Runner相关的任务标签,&nbsp;用于在共享Runner中区分不同的Project,.gitlab-ci.yml会用到</p>
</li>
<li>
<p>&nbsp; &nbsp; runner&nbsp; executor&nbsp;  执行构建任务的方式,这里使用shell方式</p>
</li>
</ul>
<blockquote>
<p>Shell是最简单的配置执行器,需要将构建所需的所有依赖项手动安装在安装了Runner的同一台计算机上。</p>
</blockquote>
<p>&nbsp; &nbsp; &nbsp; 注册过程和结果请参考下图:</p>
<p><img src="https://img2018.cnblogs.com/blog/587720/201908/587720-20190815190514129-2046594366.png" alt=""></p>
<h2>第二部分:基于docker-compose的Gitlab-CI&nbsp; 实践</h2>
<h3>项目架构图</h3>
<p>&nbsp; &nbsp; &nbsp;&nbsp;<img src="https://img2018.cnblogs.com/blog/587720/201908/587720-20190816132112191-222245610.png" alt=""></p>
<p> </p>
<blockquote data-type="2" data-url="" data-author-name="" data-content-utf8-length="51" data-source-title="">
<p>原则上不允许自动部署Prod,本次使用Gitlab Runner服务器作为Gitlab CD的部署机器。</p>
</blockquote>
<p>Gitlab-CI Pipeline构建ReceiverAPP、webAPP镜像(附带本次git:tag)并推送到hub.docker.com;</p>
<p>Gitlab-CD docker-compose拉取远端nginx、ReceiveAPP、webapp镜像,启动容器。&nbsp;</p>
<p><img src="https://img2018.cnblogs.com/blog/587720/201908/587720-20190815193902211-1336940737.png" alt=""></p>
<ul>
<li>
<p>&nbsp; &nbsp; &nbsp;Pipeline对每一次提交或合并都会执行build任务, 形成Continous Intergation</p>
</li>
<li>
<p>&nbsp; &nbsp; &nbsp;Pipeline对git: tag会执行build_Image任务,自动构建至deploy_staging任务,这样就能形成基于git:tag的部署版本管理(部署出错,也能很快回滚到上次的部署tag)</p>
</li>
</ul>
<blockquote>
<p>本处使用Gitlab Runner 服务器作为staging部署机器; 原则上不允许自动随意部署Prod(实践中<span style="font-size: 12px">登陆到 Prod机器上执行部署命令,以下GitLab-CD也没有完成Prod的自动部署过程,自行补上登陆终端的脚本即可</span>)<span style="font-size: 1.17em">&nbsp; &nbsp;</span></p>
</blockquote>
<h3>.gitlab-ci.yml 文件</h3>
<p>  以上Gitlab Pipeline定义了 build--&gt;build_image--&gt;deploy 三个任务, 某些任务还包括不同分支Job,<strong>写.gitlab-ci.yml 的过程就是将以上执行动作</strong><strong>脚本化</strong>,更多Gitlab-CI的资料。</p>
<div class="cnblogs_code">
<pre> <span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)"> stages:
</span><span style="color: rgba(128, 0, 128, 1)">2</span>   -<span style="color: rgba(0, 0, 0, 1)"> build
</span><span style="color: rgba(128, 0, 128, 1)">3</span>   -<span style="color: rgba(0, 0, 0, 1)"> build_image
</span><span style="color: rgba(128, 0, 128, 1)">4</span>   -<span style="color: rgba(0, 0, 0, 1)"> deploy
</span><span style="color: rgba(128, 0, 128, 1)">5</span>
<span style="color: rgba(128, 0, 128, 1)">6</span><span style="color: rgba(0, 0, 0, 1)"> variables:         
</span><span style="color: rgba(128, 0, 128, 1)">7</span> # CI_DEBUG_TRACE: <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">true</span><span style="color: rgba(128, 0, 0, 1)">"</span>                                         
<span style="color: rgba(128, 0, 128, 1)">8</span>   deploy_path: <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">/home/xxxx/eqidmanager</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">     <span style="color: rgba(0, 128, 0, 1)"># CI变量,用于配置部署目录
</span></span><span style="color: rgba(128, 0, 128, 1)">9</span>
<span style="color: rgba(128, 0, 128, 1)">10</span><span style="color: rgba(0, 0, 0, 1)"> before_script:
</span><span style="color: rgba(128, 0, 128, 1)">11</span>   - <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">docker info</span><span style="color: rgba(128, 0, 0, 1)">"</span>
<span style="color: rgba(128, 0, 128, 1)">12</span>
<span style="color: rgba(128, 0, 128, 1)">13</span><span style="color: rgba(0, 0, 0, 1)"> build:
</span><span style="color: rgba(128, 0, 128, 1)">14</span><span style="color: rgba(0, 0, 0, 1)">   stage: build
</span><span style="color: rgba(128, 0, 128, 1)">15</span><span style="color: rgba(0, 0, 0, 1)">   script:
</span><span style="color: rgba(128, 0, 128, 1)">16</span>   - <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">for d in $(ls src);do echo $d;prog=$(pwd)/src/$d/$d.csproj; dotnet build $prog; done</span><span style="color: rgba(128, 0, 0, 1)">"</span>
<span style="color: rgba(128, 0, 128, 1)">17</span><span style="color: rgba(0, 0, 0, 1)">   tags:                                               
</span><span style="color: rgba(128, 0, 128, 1)">18</span>   - another-<span style="color: rgba(0, 0, 0, 1)">tag
</span><span style="color: rgba(128, 0, 128, 1)">19</span>
<span style="color: rgba(128, 0, 128, 1)">20</span><span style="color: rgba(0, 0, 0, 1)"> build_image:EqidManager:
</span><span style="color: rgba(128, 0, 128, 1)">21</span><span style="color: rgba(0, 0, 0, 1)">   stage: build_image
</span><span style="color: rgba(128, 0, 128, 1)">22</span><span style="color: rgba(0, 0, 0, 1)">   script:
</span><span style="color: rgba(128, 0, 128, 1)">23</span>   - dotnet publish src/EqidManager/EqidManager.csproj-c release -o ../../container/app/publish/   
<span style="color: rgba(128, 0, 128, 1)">24</span>   - docker build --pull-t $CI_REGISTRY_USER/eqidmanager:$CI_COMMIT_REF_NAMEcontainer/<span style="color: rgba(0, 0, 0, 1)">app
</span><span style="color: rgba(128, 0, 128, 1)">25</span>   - docker login -u $CI_REGISTRY_USER-<span style="color: rgba(0, 0, 0, 1)">p $CI_REGISTRY_PASSWORD      
</span><span style="color: rgba(128, 0, 128, 1)">26</span>   - docker push $CI_REGISTRY_USER/<span style="color: rgba(0, 0, 0, 1)">eqidmanager:$CI_COMMIT_REF_NAME   
</span><span style="color: rgba(128, 0, 128, 1)">27</span><span style="color: rgba(0, 0, 0, 1)">   tags:   
</span><span style="color: rgba(128, 0, 128, 1)">28</span>   - another-<span style="color: rgba(0, 0, 0, 1)">tag
</span><span style="color: rgba(128, 0, 128, 1)">29</span><span style="color: rgba(0, 0, 0, 1)">   only:              <span style="color: rgba(0, 128, 0, 1)">#Pipeline Job构建策略,代码仓库打tag会执行该任务, 支持正则
</span></span><span style="color: rgba(128, 0, 128, 1)">30</span>   -<span style="color: rgba(0, 0, 0, 1)"> tags
</span><span style="color: rgba(128, 0, 128, 1)">31</span>
<span style="color: rgba(128, 0, 128, 1)">32</span><span style="color: rgba(0, 0, 0, 1)"> build_image:EqidReceiver:
</span><span style="color: rgba(128, 0, 128, 1)">33</span><span style="color: rgba(0, 0, 0, 1)">   stage: build_image
</span><span style="color: rgba(128, 0, 128, 1)">34</span><span style="color: rgba(0, 0, 0, 1)">   script:
</span><span style="color: rgba(128, 0, 128, 1)">35</span>   - dotnet publish src/EqidReceiver/EqidReceiver.csproj-c release -o ../../container/receiver/<span style="color: rgba(0, 0, 0, 1)">publish
</span><span style="color: rgba(128, 0, 128, 1)">36</span>   - docker build -t $CI_REGISTRY_USER/eqidreceiver:$CI_COMMIT_REF_NAME container/<span style="color: rgba(0, 0, 0, 1)">receiver
</span><span style="color: rgba(128, 0, 128, 1)">37</span>   - docker login -u $CI_REGISTRY_USER-<span style="color: rgba(0, 0, 0, 1)">p $CI_REGISTRY_PASSWORD
</span><span style="color: rgba(128, 0, 128, 1)">38</span>   - docker push $CI_REGISTRY_USER/<span style="color: rgba(0, 0, 0, 1)">eqidreceiver:$CI_COMMIT_REF_NAME
</span><span style="color: rgba(128, 0, 128, 1)">39</span><span style="color: rgba(0, 0, 0, 1)">   tags:
</span><span style="color: rgba(128, 0, 128, 1)">40</span>   - my-<span style="color: rgba(0, 0, 0, 1)">tag
</span><span style="color: rgba(128, 0, 128, 1)">41</span><span style="color: rgba(0, 0, 0, 1)">   only:
</span><span style="color: rgba(128, 0, 128, 1)">42</span>   -<span style="color: rgba(0, 0, 0, 1)"> tags
</span><span style="color: rgba(128, 0, 128, 1)">43</span>
<span style="color: rgba(128, 0, 128, 1)">44</span><span style="color: rgba(0, 0, 0, 1)"> deploy:staging:
</span><span style="color: rgba(128, 0, 128, 1)">45</span><span style="color: rgba(0, 0, 0, 1)">   stage: deploy
</span><span style="color: rgba(128, 0, 128, 1)">46</span><span style="color: rgba(0, 0, 0, 1)">   script:
</span><span style="color: rgba(128, 0, 128, 1)">47</span>   -<span style="color: rgba(0, 0, 0, 1)"> cd $deploy_path
</span><span style="color: rgba(128, 0, 128, 1)">48</span>   - export TAG=<span style="color: rgba(0, 0, 0, 1)"><span style="color: rgba(255, 0, 0, 1)">$CI_COMMIT_REF_NAME</span>        <span style="color: rgba(0, 128, 0, 1)"># 引入本次CI的git:tag名称,覆盖.env文件默认配置
</span></span><span style="color: rgba(128, 0, 128, 1)">49</span>   - <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">docker-compose -f docker-compose.yml -f docker-compose.prod.yml build</span><span style="color: rgba(128, 0, 0, 1)">"</span>                        
<span style="color: rgba(128, 0, 128, 1)">50</span>   - <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d</span><span style="color: rgba(128, 0, 0, 1)">"</span>
<span style="color: rgba(128, 0, 128, 1)">51</span><span style="color: rgba(0, 0, 0, 1)">   tags:
</span><span style="color: rgba(128, 0, 128, 1)">52</span>   - my-<span style="color: rgba(0, 0, 0, 1)">tag
</span><span style="color: rgba(128, 0, 128, 1)">53</span>
<span style="color: rgba(128, 0, 128, 1)">54</span><span style="color: rgba(0, 0, 0, 1)"> deploy:prod:
</span><span style="color: rgba(128, 0, 128, 1)">55</span><span style="color: rgba(0, 0, 0, 1)">   stage: deploy
</span><span style="color: rgba(128, 0, 128, 1)">56</span><span style="color: rgba(0, 0, 0, 1)">   script:
</span><span style="color: rgba(128, 0, 128, 1)">57</span>   <span style="color: rgba(0, 128, 0, 1)">- # TODO 需要写脚本登陆到Prod机器上
</span><span style="color: rgba(128, 0, 128, 1)">58</span>   - export TAG=<span style="color: rgba(0, 0, 0, 1)">$CI_COMMIT_REF_NAME      
</span><span style="color: rgba(128, 0, 128, 1)">59</span>   -<span style="color: rgba(0, 0, 0, 1)"> cd $deploy_path
</span><span style="color: rgba(128, 0, 128, 1)">60</span>   - <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">docker-compose -f docker-compose.yml -f docker-compose.prod.yml build</span><span style="color: rgba(128, 0, 0, 1)">"</span>
<span style="color: rgba(128, 0, 128, 1)">61</span>   - <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d</span><span style="color: rgba(128, 0, 0, 1)">"</span>
<span style="color: rgba(128, 0, 128, 1)">62</span><span style="color: rgba(0, 0, 0, 1)">   tags:
</span><span style="color: rgba(128, 0, 128, 1)">63</span>   - my-<span style="color: rgba(0, 0, 0, 1)">tag
</span><span style="color: rgba(128, 0, 128, 1)">64</span>   when: manual</pre>
</div>
<p>&nbsp;这里有些知识点和 坑位需要指出:</p>
<p>第8行: 预先定义的环境变量,该变量定义gitlab CD的部署目录</p>
<p>第16行:&nbsp; 对src开发目录下两个程序执行dotnet build命令</p>
<p>第17行:tags定义具备该tags的Runner可以执行该任务, 注意这里的tags必须是字符串数组</p>
<p>第23-26行:构建镜像并推送到镜像仓库的过程,用到两种CI变量</p>
<p> &nbsp; - 密钥变量CI_REGISTRY_USER、CI_REGISTRY_PASSWORD ,可在GitLab-CI 界面配置</p>
<p>&nbsp; &nbsp; &nbsp;- 预定义变量CI_COMMIT_REF_NAME, 该变量标记构建项目的git:branch或git:tag名称,用于生成镜像tag</p>
<blockquote>
<p>&nbsp; &nbsp; &nbsp;注意变量可被重写,重写有优先级 http://www.ttlsa.com/auto/gitlab-cicd-variables-zh-docum</p>
</blockquote>
<p>第29行; only定义此Job只在产生git:tag时被触发,与上面我们使用 CI-COMMIT_REF_NAME 变量相呼应</p>
<p>第47行: Gialab-CI pipeline每个Job会重新拉取git源码执行Job任务(<span style="font-size: 12px">可登录到Gitlab Runner工作目录下观察Runner执行过程</span>),CD时需要选择合适目录,这是deploy_staging上使用deploy_path CI变量的原因</p>
<p>第48行:注入本次Gitlab-CI git:tag名称, 实际上是覆盖了.env同名环境变量</p>
<p>第49行:若存在docker-compose.yml、docker-compose.override.yml 两个文件,docker-compose命令会自动merge这2个文件(<span style="font-size: 12px">使用docker-compose config命令查看merge 之后的结果</span>)。</p>
<p>第64行:上述Job若没有出错,会自动执行下一步;而when指令定义该Job 是需要在界面上手动执行&nbsp;</p>
<h3>部署目录</h3>
<p>&nbsp;在Gitlab Runner服务器的{<span style="color: rgba(0, 0, 255, 1)">deploy_path</span>}路径下建立了如下部署文件:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">├── appsettings.secrets.json
├── docker-compose</span>.<span style="color: rgba(0, 0, 255, 1)">prod</span><span style="color: rgba(0, 0, 0, 1)">.yml
├── docker</span>-<span style="color: rgba(0, 0, 0, 1)">compose.yml
├── .env
├── EqidManager.db
├── nginx
│&nbsp;&nbsp; ├── Dockerfile
│&nbsp;&nbsp; └── nginx.conf
└── receiver.secrets.json</span></pre>
</div>
<ul>
<li>在部署目录建立定义docker-compose.yml、docker-compose.prod.yml 两个yml文件,<strong>前者定义常规容器服务,后者定义适用于本Prod环境的容器服务</strong></li>
<li>
<p>密钥文件不要进入代码管理,因此我们定义appsetting.secrets.json 和 receiver.secrets.json密钥文件,由dccker-compose.yml挂载进入容器</p>
</li>
<li>
<p>.env文件存储相对固定、与本次docker-compose命令相关的环境变量,docker-compose命令默认寻找同级目录下.env文件</p>
</li>
</ul>
<div class="cnblogs_code">
<pre>------.env 文件----<br><span style="background-color: rgba(255, 255, 0, 1)">TAG=</span><span style="color: rgba(0, 0, 0, 1)"><span style="background-color: rgba(255, 255, 0, 1)">master</span>     <span style="color: rgba(0, 128, 0, 1)"># 该TAG变量会在Pipeline:deploy_staging任务中被覆盖,形成基于git:tag的imageName:tag</span>
docker_host</span>=<span style="color: rgba(128, 0, 128, 1)">172.16</span>.<span style="color: rgba(128, 0, 128, 1)">1.1</span><span style="color: rgba(0, 0, 0, 1)">
COMPOSE_PROJECT_NAME</span>=<span style="color: rgba(0, 0, 0, 1)">EqidManager
DOCKER_REGISTRY</span>=***<span style="color: rgba(128, 0, 128, 1)"><br></span></pre>
</div>
<p> project打上git:tag之后,触发Gitlab Runner CI/CD Pipeline:&nbsp;</p>
<p>跳转到部署目录-&gt;应用本次git:tag-&gt;执行docker-compose命令拉取指定tag镜像并启动容器。</p>
<p>That'all, 本次应用Gitlab Runner(shell执行器)实践CI/CD,&nbsp;Gitlab菜单界面有所有构建构成的日志(便于排查构建问题);另外上文对于关键知识均附带传送门,可进一步对比研究。</p>
<div id="MySignature" style="display: block">
<div style="display: block; border: 2px solid rgba(110, 202, 168, 1); padding: 10px; background: rgba(240, 248, 255, 1)">
<div>作者:JulianHuang</div>
<div>
<p>码甲拙见,如有问题请下方留言大胆斧正;码字+Visio制图,均为原创,看官请不吝好评+关注,&nbsp; <span style="color: rgba(255, 102, 0, 1)">~。。~</span></p>
<p>本文欢迎转载,请转载页面明显位置注明原作者及原文链接<strong>。</strong></p>
</div>
</div>
</div>
<div>&nbsp;</div>

</div>
<div id="MySignature" role="contentinfo">
    <HR style="FILTER: alpha(opacity=100,finishopacity=0,style=3)" width="80%" color=#987cb9 SIZE=3>
<div style="text-align:center;">
<p>本文来自博客园,作者:{有态度的马甲},转载请注明原文链接:https://www.cnblogs.com/JulianHuang/p/11346615.html</p>
<strong style="color: red; ">欢迎关注我的原创技术、职场公众号, 加好友谈天说地,一起进化</strong>
<div><imgstyle="width: 250px;height:250px;" src="https://blog-static.cnblogs.com/files/JulianHuang/QR.gif" /> </div>

</div><br><br>
来源:https://www.cnblogs.com/JulianHuang/p/11346615.html
頁: [1]
查看完整版本: 基于docker-compose的Gitlab CI/CD实践&排坑指南