
# 持续集成及持续部署
在本方案中，需要在通过 code push 事件中触发 Jenkins 进行编译打包，通过邮件审批之后，将应用部署到 Kubernetes 集群中。
#### 安装Jenkins插件
除了前文安装 Jenkins 时默认安装的插件外，还需要安装 GitLab Plugin，Kubernetes CLI，Email Extension插件，详情可以参考[官方文档](https://www.jenkins.io/doc/book/managing/plugins/)。
在Jenkins Dashboard页面单击左侧"Manage Jenkins"，选择"System Configuration \> Manage Plugins"，在"Available"页签中筛选安装"GitLab"、"Kubernetes CLI"和"Email Extension Template"插件。
![](https://support.huaweicloud.com/bestpractice-cce/zh-cn_image_0000001901894221.png "点击放大")
上述安装的插件版本可能随时间变化发生变动。
#### 配置Gitlab Hook
当推送代码之后，Gitlab通过Webhook来通知Jenkins push event，所以首先需要配置Gitlab Hook。
1. 登录Jenkins界面单击"New Item"，新建流水线。 
   ![](https://support.huaweicloud.com/bestpractice-cce/zh-cn_image_0000001901768873.png "点击放大")
   
   
2. 复制URL，并单击高级选项。 
   ![](https://support.huaweicloud.com/bestpractice-cce/zh-cn_image_0000001855664012.png "点击放大")
   
   
3. 单击"Generate"生成Token并记录，其他选项可保持默认，单击保存。 
   ![](https://support.huaweicloud.com/bestpractice-cce/zh-cn_image_0000001855664956.png "点击放大")
   
   
4. 登录Gitlab启用webhook功能。 
   开启本地请求，因为 GitLab 10.6 版本以后为了安全，不允许向本地网络发送 WebHook 请求。
   ![](https://support.huaweicloud.com/bestpractice-cce/zh-cn_image_0000001901909061.png "点击放大")
   ![](https://support.huaweicloud.com/bestpractice-cce/zh-cn_image_0000001855671808.png "点击放大")
   
   
5. 登录到 Gitlab-\> java-demo项目中，左边列选择 Settings-\>webhook，在URL和 secret token 分别填入上文在 Jenkins 里记录下的值。 
   ![](https://support.huaweicloud.com/bestpractice-cce/zh-cn_image_0000001901954569.png "点击放大")
   
   
6. 在页面最下端，确认添加 webhook，并进行测试。 
   ![](https://support.huaweicloud.com/bestpractice-cce/zh-cn_image_0000001932992217.png "点击放大")
   
   
 
#### 邮件通知配置
邮件是Jenkins在消息通知、审批环节中比较通用的一种方式，本方案中也是选择邮件作为审批方式。
1. 在"Manage Jenkins"，选择"System"。 
   ![](https://support.huaweicloud.com/bestpractice-cce/zh-cn_image_0000001855838564.png "点击放大")
   
   
2. 配置邮箱基本信息，填写管理员邮箱地址。 
   ![](https://support.huaweicloud.com/bestpractice-cce/zh-cn_image_0000001901929901.png "点击放大")
   ![](https://support.huaweicloud.com/bestpractice-cce/public_sys-resources/notice_3.0-zh-cn.png)
   其中password不是邮箱密码，而是邮箱授权码。
   
   
 
#### 凭据配置
为了保证安全性，K8S 集群都必须通过 https 来访问。因此，需要在 Jenkins 上配置访问 K8S 集群的凭证。
1. 获取K8S 集群客户端 kubeconfig 配置，可参考[通过kubectl连接集群](https://support.huaweicloud.com/usermanual-cce/cce_10_0107.html)，获取kubectl配置文件。
2. 登录Jenkins 页面，进入"Manage Jenkins"，选择"Security \> Manage Credentials"，并通过 secret file 的方式创建集群凭证。
   
   ![](https://support.huaweicloud.com/bestpractice-cce/zh-cn_image_0000001915694201.png "点击放大")
   
   
   
3. 同理，重复以上步骤创建生产集群访问凭证。
 
#### Pipeline脚本编写
Pipeline是一套运行于Jenkins上的工作流框架，将原本独立运行于单个或者多个节点的任务连接起来，实现单个任务难以完成的复杂流程编排与可视化。其作为Jenkins2.X 最核心的特性，帮助Jenkins实现从CI到CD与DevOps的转变。因此，Pipeline脚本编写是整个方案实现的关键。
Pipeline脚本大致涉及到以下概念：
- node（节点） 节点是一个机器，它是 Jenkins 环境的一部分，同时也是执行该流水线的实体。
  
- stage（阶段） stage块定义了在整个流水线的不同执行阶段（比如 "Build", "Test" 和 "Deploy" 阶段），每个阶段执行一组特定的任务。
  
- step（步骤） step是阶段中的单个任务，如运行测试、部署代码等。 告诉Jenkins在特定的时间点要做的事情，例如，执行一段shell命令。（更多的Jenkins Pipeline语法可参考官网：<https://www.jenkins.io/zh/doc/book/pipeline/>）
  
1. 进入Jenkins的流水线，单击左侧导航栏的"Configuration"。
2. 配置pipeline脚本。以下pipeline脚本仅供您参考，您可根据自身业务自定义脚本内容。 
   示例脚本中的部分参数需要修改：
   - git_url：您Gitlab中代码仓库的SSH地址，需要替换为实际取值。
   
   - swr_login：登录命令为[获取长期的docker login命令](https://support.huaweicloud.com/bestpractice-cce/cce_bestpractice_10034.html#cce_bestpractice_10034__li159181181724)获取的命令。
   
   - swr_region：SWR的区域，填写实际使用的region。
   
   - organization：SWR中的实际组织名称。
   
   - build_name：制作的镜像名称。
   
   - test_credential ：添加到Jenkins的测试集群凭证，请填写凭证ID。如果需要部署在另一个集群，需要重新将这个集群的访问凭证添加到Jenkins，具体操作请参考[设置集群访问凭证]。
   
   - prod_credential ：添加到Jenkins的生产集群凭证，请填写凭证ID。如果需要部署在另一个集群，需要重新将这个集群的访问凭证添加到Jenkins，具体操作请参考[设置集群访问凭证]。
   
   - test_apiserver ：测试集群的APIserver地址，可参考[通过kubectl连接集群](https://support.huaweicloud.com/usermanual-cce/cce_10_0107.html)。需保证从Jenkins集群可以正常访问该地址。
   
   - prod_apiserver ：生产集群的APIserver地址，可参考[通过kubectl连接集群](https://support.huaweicloud.com/usermanual-cce/cce_10_0107.html)。需保证从Jenkins集群可以正常访问该地址。
   
   - test_email：测试的邮箱地址。
   
   - admin_email：审批人的邮箱地址。
   
   
   ```
   #!groovy
   //定义代码仓地址
   def git_url = 'ssh://git@xxxx:222/ccedemo/java-demo.git' 
   //定义SWR登录指令
   def swr_login = 'docker login -u cn-north-4@xxxx -p xxxxxx swr.cn-north-4.myhuaweicloud.com'
   //定义SWR区域
   def swr_region = 'cn-north-4'
   //定义需要上传的SWR组织名称
   def organization = 'testapp'
   //定义镜像名称
   def build_name = 'demo01'
   //部署测试集群的证书ID
   def test_credential = 'test_config'
   //部署生产集群的证书ID
   def prod_credential = 'prod_config'
   //测试集群的APIserver地址，需保证从Jenkins集群可以正常访问该地址
   def test_apiserver = 'https://xxx:5443'
   //生产集群的APIserver地址，需保证从Jenkins集群可以正常访问该地址
   def prod_apiserver = 'https://xxxx:5443'
   // 邮箱地址
   def test_email="xxxxx@xx.com"
   def admin_email="xxxx@xx.com"
   pipeline{
   agent any
   stages{
       stage('Git code'){
           steps{
               echo "1. Git code" 
               git url: git_url
               script { 
                   // git rev-parse --short HEAD命令的返回值，即commit ID，作为镜像的tag
                   build_tag = sh(returnStdout: true, script: 'git rev-parse --short HEAD').trim() 
                   image_url = "swr.${swr_region}.myhuaweicloud.com/${organization}/${build_name}:${build_tag}"  
                   } 
               }
       }
       stage('Build') {
           steps{
               echo "2. Build Docker Image Stage and Push Image" 
               sh "docker build -t  ${image_url} ." 
               sh swr_login
               sh "docker push ${image_url}"
               // 替换k8s资源文件中的镜像地址
               sh  "sed -i 's+demo01:v1+${image_url}+g' ./demo01.yaml"
           }
       }
       stage('Deploy Test Environment') {
           steps{
               //配置测试环境证书 withKubeCredentials
               echo "3. Deploy Test Environment"
               script {
                   try {
                       withKubeConfig([credentialsId: test_credential, serverUrl: test_apiserver]) {
                           sh 'kubectl apply -f ./demo01.yaml'
                           //该YAML文件位于代码仓中，此处仅做示例，请您自行替换
                       }
                       println "deploy success"
                       // 发送邮件给测试
                       mail subject: "【请测试】应用已部署到测试环境，请开始测试",
                       body: """测试通过后， <a href="${BUILD_URL}input">单击该链接使用账号登录后确认测试通过</h3>""",
                       charset: 'utf-8',
                       mimeType: 'text/html',
                       to: "$test_email"
                   } catch (e) {
                       RUN_FLAG = false
                       println "deploy failed!"
                       println e
                   }
               }
           }
       }
       stage('Test'){
           // 等待测试确认
           input{ 
               message "测试是否通过"
               submitter "admin"
           }
           steps{
               script{
                   println "4. Test Passed"
                   // 发送邮件给管理员
                   mail subject: "【请审核】发布到生产环境",
                   body: """测试已通过， <a href="${BUILD_URL}input">单击该链接使用账号登录后进行审批</h3>""",
                   charset: 'utf-8',
                   mimeType: 'text/html',
                   to: "$test_email"
               }
           }
       }
       stage('Approve'){
           input{ 
               message "是否发布到生产环境"
               submitter "admin" 
           }
           steps{
               script{
                   println "5. Approved and released it to the production environment."
               }
           }
       }
       stage('Deploy Produce Environment'){
           steps{
               echo "6. Deploy Produce Environment"
               script {
                   try {
                       withKubeConfig([credentialsId: prod_credential, serverUrl: prod_apiserver]) {
                           sh 'kubectl apply -f ./demo01.yaml'
                           //该YAML文件位于代码仓中，此处仅做示例，请您自行替换
                       }
                       println "deploy success"
                   } catch (e) {
                       println "deploy failed!"
                       println e
                   }
               }
           }
       }
   }
   ```
   
   
3. 单击保存后，完成整个项目的配置。
 
#### 持续构建和部署
1. 修改本地代码中的内容，进行提交，触发编译。 
   ```
   git add . && git commit -m "add template" && git push
   ```
   
   
2. 切回 Jenkins 页面，可以看到项目已经自动触发编译构建。 
   ![](https://support.huaweicloud.com/bestpractice-cce/zh-cn_image_0000001900642920.png "点击放大")
   
   
3. 大约等待几分钟后，收到测试邮件通知。 
   ![](https://support.huaweicloud.com/bestpractice-cce/zh-cn_image_0000001888152312.png "点击放大")
   
   
   
4. 登录测试集群页面，java-demo工作负载已经创建。 
   ![](https://support.huaweicloud.com/bestpractice-cce/zh-cn_image_0000001887992376.png "点击放大")
   
   
   
5. 本示例中认为升级测试通过，单击邮件中的链接，确认测试通过。 
   ![](https://support.huaweicloud.com/bestpractice-cce/zh-cn_image_0000001932992221.png "点击放大")
   
   
   
6. 审核人邮箱中将会收到审批邮件。 
   ![](https://support.huaweicloud.com/bestpractice-cce/zh-cn_image_0000001888152324.png "点击放大")
   
   
   
7. 由审核人决定可以部署生产环境，单击链接进入，并同意。 
   ![](https://support.huaweicloud.com/bestpractice-cce/zh-cn_image_0000001887992380.png "点击放大")
   
   
   
8. 登录生产集群页面，java-demo工作负载已经创建，生产环境发布成功。 
   ![](https://support.huaweicloud.com/bestpractice-cce/zh-cn_image_0000001932992225.png "点击放大")
   
   
   
 
