基于K8s的DevOps平台实践

Posted Albert Edison

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于K8s的DevOps平台实践相关的知识,希望对你有一定的参考价值。

文章目录


1. 流水线入门

工厂的流水线如下:

官方文档 的流水线如下:

为什么叫做流水线?其实和工厂产品的生产线类似,pipeline 是从源码到发布到线上环境。

关于流水线,需要知道的几个点:

  • 重要的功能插件,帮助 Jenkins 定义了一套工作流框架;

  • Pipeline 的实现方式是一套 Groovy DSL( 领域专用语言 ),所有的发布流程都可以表述为一段 Groovy 脚本;

  • 将 WebUI 上需要定义的任务,以脚本代码的方式表述出来;

  • 帮助jenkins实现持续集成 CI(Continue Integration)和持续部署 CD(Continue Deliver)的重要手段;

🍑 流水线基础语法

官方文档

两种语法类型:

  • Scripted Pipeline,脚本式流水线,最初支持的类型
  • Declarative Pipeline,声明式流水线,为 Pipeline plugin 在 2.5 版本之后新增的一种脚本类型,后续 Open Blue Ocean 所支持的类型。与原先的 Scripted Pipeline 一样,都可以用来编写脚本。Declarative Pipeline 是后续 Open Blue Ocean 所支持的类型,写法简单,支持内嵌 Scripted Pipeline 代码

注意:为与 BlueOcean 脚本编辑器兼容,通常建议使用 Declarative Pipeline 的方式进行编写,从 jenkins 社区的动向来看,很明显这种语法结构也会是未来的趋势。

🍑 脚本示例

json 脚本

pipeline  
    agent label '172.21.51.68'
    environment  
        PROJECT = 'myblog'
    
    stages 
        stage('Checkout')  
            steps  
                checkout scm 
            
        
        stage('Build')  
            steps  
                sh 'make' 
            
        
        stage('Test')
            steps 
                sh 'make check'
                junit 'reports/**/*.xml' 
            
        
        stage('Deploy') 
            steps 
                sh 'make publish'
            
        
    
	post 
        success  
            echo 'Congratulations!'
        
		failure  
            echo 'Oh no!'
        
        always  
            echo 'I will always say Hello again!'
        
    

🍑 脚本解释

  • checkout 步骤为检出代码;scm 是一个特殊变量,指示 checkout 步骤克隆触发此 Pipeline 运行的特定修订

  • agent:指明使用哪个 agent 节点来执行任务,定义于 pipeline 顶层或者 stage 内部

    • any,可以使用任意可用的 agent 来执行

    • label,在提供了标签的 Jenkins 环境中可用的代理上执行流水线或阶段。例如:agent label 'my-defined-label' ,最常见的使用方式

    • none,当在 pipeline 块的顶部没有全局代理,该参数将会被分配到整个流水线的运行中并且每个 stage 部分都需要包含他自己的 agent 部分。比如:agent none

    • docker, 使用给定的容器执行流水线或阶段。 在指定的节点中,通过运行容器来执行任务

agent 
    docker 
        image 'maven:3-alpine'
        label 'my-defined-label'
        args  '-v /tmp:/tmp'
    

  • options:允许从流水线内部配置特定于流水线的选项。

    • buildDiscarder,为最近的流水线运行的特定数量保存组件和控制台输出。例如: options buildDiscarder(logRotator(numToKeepStr: '10'))
    • disableConcurrentBuilds,不允许同时执行流水线。 可被用来防止同时访问共享资源等。 例如: options disableConcurrentBuilds()
    • timeout,设置流水线运行的超时时间, 在此之后,Jenkins将中止流水线。例如: options timeout(time: 1, unit: 'HOURS')
    • retry,在失败时, 重新尝试整个流水线的指定次数。 For example: options retry(3)
  • environment:指令制定一个 键-值对序列,该序列将被定义为所有步骤的环境变量

  • stages:包含一系列一个或多个 stage 指令,stages 部分是流水线描述的大部分 “work” 的位置。 建议 stages 至少包含一个 stage 指令用于连续交付过程的每个离散部分,比如构建、测试和部署。

pipeline 
    agent any
    stages  
        stage('Example') 
            steps 
                echo 'Hello World'
            
        
    

  • steps:在给定的 stage 指令中执行的定义了一系列的一个或多个 steps

  • post:定义一个或多个 steps ,这些阶段根据流水线或阶段的完成情况而运行 post 支持以下 post-condition 块中的其中之一: alwayschangedfailuresuccessunstableaborted

    • always,无论流水线或阶段的完成状态如何,都允许在 post 部分运行该步骤
    • changed,当前流水线或阶段的完成状态与它之前的运行不同时,才允许在 post 部分运行该步骤
    • failure,当前流水线或阶段的完成状态为 “failure”,才允许在 post 部分运行该步骤,通常 web UI 是红色
    • success,当前流水线或阶段的完成状态为 “success”,才允许在 post 部分运行该步骤,通常 web UI 是蓝色或绿色
    • unstable,当前流水线或阶段的完成状态为 “unstable”,才允许在 post 部分运行该步骤,通常由于测试失败,代码违规等造成。通常 web UI 是黄色
    • aborted,只有当前流水线或阶段的完成状态为 “aborted”,才允许在 post 部分运行该步骤,通常由于流水线被手动的aborted。通常 web UI 是灰色

创建 pipeline 示意:新建任务 -> 流水线

jenkins/pipelines/p1.yaml

pipeline 
   agent label '172.21.51.68'
   environment  
      PROJECT = 'myblog'
   
   stages 
      stage('printenv') 
         steps 
            echo 'Hello World'
            sh 'printenv'
         
      
      stage('check') 
         steps 
            checkout([$class: 'GitSCM', branches: [[name: '*/master']], doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [], userRemoteConfigs: [[credentialsId: 'gitlab-user', url: 'http://gitlab.luffy.com/root/myblog.git']]])
         
      
      stage('build-image') 
         steps 
            sh 'docker build . -t myblog:latest -f Dockerfile'
         
      
      stage('send-msg') 
         steps 
            sh """
            curl 'https://oapi.dingtalk.com/robot/send?access_token=4778abd23dbdbaf66fc6f413e6ab9c0103a039b0054201344a22a5692cdcc54e' \\
   -H 'Content-Type: application/json' \\
   -d '"msgtype": "text", 
        "text": 
             "content": "我就是我, 是不一样的烟火"
        
      '
      """
         
      
   

点击 “立即构建”,同样的,我们可以配置触发器,使用 webhook 的方式接收项目的 push 事件

  • 构建触发器选择 Build when a change is pushed to GitLab.
  • 生成 Secret token
  • 配置 gitlab,创建 webhook,发送 test push events 测试

🍑 Blue Ocean

官方文档

关于 Blue Ocean 我们需要知道的几点:

  • 是一个插件, 旨在为 Pipeline 提供丰富的体验 ;
  • 连续交付(CD)Pipeline 的复杂可视化,允许快速和直观地了解 Pipeline 的状态;
  • 目前支持的类型仅针对于 Pipeline,尚不能替代 Jenkins 经典版UI

思考:

  1. 每个项目都把大量的 pipeline 脚本写在 Jenkins 端,对于谁去维护及维护成本是一个问题
  2. 没法做版本控制

2. Jenkinsfile实践

Jenkins Pipeline 提供了一套可扩展的工具,用于将 “简单到复杂” 的交付流程实现为 “持续交付即代码”。

Jenkins Pipeline 的定义通常被写入到一个文本文件(称为 Jenkinsfile )中,该文件可以被放入项目的源代码控制库中。

🍑 演示一

演示1:使用 Jenkinsfile 管理 pipeline

  • 在项目中新建 Jenkinsfile 文件,拷贝已有 script 内容
  • 配置 pipeline 任务,流水线定义为 Pipeline Script from SCM
  • 执行 push 代码测试

Jenkinsfile:jenkins/pipelines/p2.yaml

pipeline 
   agent label '172.21.51.68'
   environment  
      PROJECT = 'myblog'
   
   stages 
      stage('printenv') 
         steps 
            echo 'Hello World'
            sh 'printenv'
         
      
      stage('check') 
         steps 
            checkout([$class: 'GitSCM', branches: [[name: '*/master']], doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [], userRemoteConfigs: [[credentialsId: 'gitlab-user', url: 'http://gitlab.luffy.com/root/myblog.git']]])
         
      
      stage('build-image') 
         steps 
            sh 'docker build . -t myblog:latest -f Dockerfile'
         
      
      stage('send-msg') 
         steps 
            sh """
            curl 'https://oapi.dingtalk.com/robot/send?access_token=4778abd23dbdbaf66fc6f413e6ab9c0103a039b0054201344a22a5692cdcc54e' \\
   -H 'Content-Type: application/json' \\
   -d '"msgtype": "text", 
        "text": 
             "content": "我就是我, 是不一样的烟火"
        
      '
      """
         
      
   

🍑 演示二

演示2:优化及丰富流水线内容

  • 优化代码检出阶段

    由于目前已经配置了使用 git 仓库地址,且使用 SCM 来检测项目,因此代码检出阶段完全没有必要再去指定一次

  • 构建镜像的 tag 使用 git 的 commit id

  • 增加 post 阶段的消息通知,丰富通知内容

  • 配置 webhook,实现 myblog 代码推送后,触发 Jenkinsfile 任务执行

jenkins/pipelines/p3.yaml

pipeline 
    agent  label '172.21.51.68'

    stages 
        stage('printenv') 
            steps 
            echo 'Hello World'
            sh 'printenv'
            
        
        stage('check') 
            steps 
                checkout scm
            
        
        stage('build-image') 
            steps 
            	retry(2)  sh 'docker build . -t myblog:$GIT_COMMIT'
            
        
    
    post 
        success  
            echo 'Congratulations!'
            sh """
                curl 'https://oapi.dingtalk.com/robot/send?access_token=4778abd23dbdbaf66fc6f413e6ab9c0103a039b0054201344a22a5692cdcc54e' \\
                    -H 'Content-Type: application/json' \\
                    -d '"msgtype": "text", 
                            "text": 
                                "content": "😄👍构建成功👍😄\\n 关键字:luffy\\n 项目名称: $JOB_BASE_NAME\\n Commit Id: $GIT_COMMIT\\n 构建地址:$RUN_DISPLAY_URL"
                        
                '
            """
        
        failure 
            echo 'Oh no!'
            sh """
                curl 'https://oapi.dingtalk.com/robot/send?access_token=4778abd23dbdbaf66fc6f413e6ab9c0103a039b0054201344a22a5692cdcc54e' \\
                    -H 'Content-Type: application/json' \\
                    -d '"msgtype": "text", 
                            "text": 
                                "content": "😖❌构建失败❌😖\\n 关键字:luffy\\n 项目名称: $JOB_BASE_NAME\\n Commit Id: $GIT_COMMIT\\n 构建地址:$RUN_DISPLAY_URL"
                        
                '
            """
        
        always  
            echo 'I will always say Hello again!'
        
    

🍑 演示三

演示3:使用 k8s 部署服务

  • 新建 deploy 目录,将 k8s 所需的文件放到 deploy 目录中

  • 将镜像地址改成模板,在 pipeline 中使用新构建的镜像进行替换

  • 执行 kubectl apply -f deploy 应用更改,需要配置 kubectl 认证

$ scp -r k8s-master:/root/.kube /root

jenkins/pipelines/p4.yaml

pipeline 
    agent  label '172.21.51.68'

    environment 
        IMAGE_REPO = "172.21.51.143:5000/myblog"
    

    stages 
        stage('printenv') 
            steps 
              echo 'Hello World'
              sh 'printenv'
            
        
        stage('check') 
            steps 
                checkout scm
            
        
        stage('build-image') 
            steps 
                retry(2)  sh 'docker build . -t $IMAGE_REPO:$GIT_COMMIT'
            
        
        stage('push-image') 
            steps 
                retry(2)  sh 'docker push $IMAGE_REPO:$GIT_COMMIT'
            
        
        stage('deploy') 
            steps 
                sh "sed -i 's#IMAGE_URL#$IMAGE_REPO:$GIT_COMMIT#g' manifests/*"
                timeout(time: 1, unit: 'MINUTES') 
                    sh "kubectl apply -f manifests/"
                
            
        
    
    post 
        success  
            echo 'Congratulations!'
            sh """
                curl 'https://oapi.dingtalk.com/robot/send?access_token=4778abd23dbdbaf66fc6f413e6ab9c0103a039b0054201344a22a5692cdcc54e' \\
                    -H 'Content-Type: application/json' \\
                    -d '"msgtype": "text", 
                            "text": 
                                "content": "😄👍构建成功👍😄\\n 关键字:myblog\\n 项目名称: $JOB_BASE_NAME\\n Commit Id: $GIT_COMMIT\\n 构建地址:$RUN_DISPLAY_URL"
                        
                '
            """
        
        failure 
            echo 'Oh no!'
            sh """
                curl 'https://oapi.dingtalk.com/robot/send?access_token=4778abd23dbdbaf66fc6f413e6ab9c0103a039b0054201344a22a5692cdcc54e' \\
                    -H 'Content-Type: application/json' \\
                    -d '"msgtype": "text", 
                            "text": 
                                "content": "😖❌构建失败❌😖\\n 关键字:luffy\\n 项目名称: $JOB_BASE_NAME\\n Commit Id: $GIT_COMMIT\\n 构建地址:$RUN_DISPLAY_URL"
                        
                '
            """
        
        always  
            echo 'I will always say Hello again!'
        
    

🍑 演示四

演示4:使用凭据管理敏感信息

上述 Jenkinsfile 中存在的问题是敏感信息使用明文,暴漏在代码中,如何管理流水线中的敏感信息(包含账号密码),之前我们在对接 gitlab 的时候,需要账号密码,已经使用过凭据来管理这类敏感信息,同样的,我们可以使用凭据来存储钉钉的 token 信息,那么,创建好凭据后,如何在 Jenkinsfile 中获取已有凭据的内容?

Jenkins 的声明式流水线语法有一个 credentials() 辅助方法(在 environment 指令中使用),它支持 secret 文本带密码的用户名,以及 secret 文件凭据。

下面的流水线代码片段展示了如何创建一个使用带密码的用户名凭据的环境变量的流水线。

在该示例中,带密码的用户名凭据被分配了环境变量,用来使你的组织或团队以一个公用账户访问 Bitbucket 仓库;这些凭据已在 Jenkins 中配置了凭据 ID jenkins-bitbucket-common-creds

当在 environment 指令中设置凭据环境变量时:

environment 
    BITBUCKET_COMMON_CREDS = credentials('jenkins-bitbucket-common-creds')

这实际设置了下面的三个环境变量:

  • BITBUCKET_COMMON_CREDS - 包含一个以冒号分隔的用户名和密码,格式为 username:password
  • BITBUCKET_COMMON_CREDS_USR - 附加的一个仅包含用户名部分的变量。
  • BITBUCKET_COMMON_CREDS_PSW - 附加的一个仅包含密码部分的变量。
pipeline 
    agent 
        // 此处定义 agent 的细节
    
    environment 
        //顶层流水线块中使用的 environment 指令将适用于流水线中的所有步骤。 
        BITBUCKET_COMMON_CREDS = credentials('jenkins-bitbucket-common-creds')
    
    stages 
        stage('Example stage 1') 
 			//在一个 stage 中定义的 environment 指令只会将给定的环境变量应用于 stage 中的步骤。
            environment 
                BITBUCKET_COMMON_CREDS = credentials('another-credential-id')
            
            steps 
                // 
            
        
        stage('Example stage 2') 
            steps 
                // 
            
        
    

因此对 Jenkinsfile 做改造:jenkins/pipelines/p5.yaml

pipeline 
    agent  label '172.21.51.68'

    environment 
        IMAGE_REPO = "172.21.51.143:5000/myblog"
        DINGTALK_CREDS = credentials('dingTalk')
    

    stages 
        stage('printenv') 
            steps 
            echo 'Hello World'
            sh 'printenv'
            
        
        stage('check') 
            steps 
                checkout scm
            
        
        stage('build-image') 
            steps 
                retry(2)  sh 'docker build . -t $IMAGE_REPO:$GIT_COMMIT'
            
        
        stage('push-image') 
            steps 
                retry(2)  sh 'docker push $IMAGE_REPO:$GIT_COMMIT'
            
        
        stage('deploy') 
            steps 
                sh "sed -i 's#IMAGE_URL#$IMAGE_REPO:$GIT_COMMIT#g' manifests/*"
                timeout(time: 1, unit: 'MINUTES') 
                    sh "kubectl apply -f manifests/"
                
            
        
    
    post 
        success  
            echo 'Congratulations!'
            sh """
                curl 'https://oapi.dingtalk.com/robot/send?access_token=$DINGTALK_CREDS_PSW' \\
                    -H 'Content-Type: application/json' \\
                    -d '"msgtype": "text", 
                            "text": 
                                "content": "😄👍构建成功👍😄\\n 关键字:luffy\\n 项目名称: $JOB_BASE_NAME\\n Commit Id: $GIT_COMMIT\\n 构建地址:$RUN_DISPLAY_URL"
                        
                '
            """
        
        failure 
            echo 'Oh no!'
            sh """
                curl 'https://oapi.dingtalk.com/robot/send?access_token=$DINGTALK_CREDS_PSW' \\
                    -H 'Content-Type: application/json' \\
                    -d '"msgtype": "text", 
                            "text": 
                                "content": "😖❌构建失败❌😖\\n 关键字:luffy\\n 项目名称: $JOB_BASE_NAME\\n Commit Id: $GIT_COMMIT\\n 构建地址:$RUN_DISPLAY_URL"
                        
                '
            """
        
        always  
            echo 'I will always say Hello again!'
        
    

🍑 总结

上面我们已经通过 Jenkinsfile 完成了最简单的项目的构建和部署,那么我们来思考目前的方式:

目前都是在项目的单一分支下进行操作,企业内一般会使用 feature、develop、release、master 等多个分支来管理整个代码提交流程,如何根据不同的分支来做构建?

构建视图中如何区分不同的分支?

如何不配置 webhook 的方式实现构建?

如何根据不同的分支选择发布到不同的环境(开发、测试、生产)?

3. 多分支流水线实践

官方示例

我们简化一下流程,假如使用 develop 分支作为开发分支,master 分支作为集成测试分支,看一下如何使用多分支流水线来管理。

🍑 演示一

演示1:多分支流水线的使用

(1)提交develop分支:

$ git checkout -b develop
$ git push --set-upstream origin develop

(2)禁用 pipeline 项目

(3)Jenkins 端创建多分支流水线项目

  • 增加 git 分支源
  • 发现标签
  • 根据名称过滤,develop|master|v.*
  • 高级克隆,设置浅克隆

保存后,会自动检索项目中所有存在 Jenkinsfile 文件的分支和标签,若匹配我们设置的过滤正则表达式,则会添加到多分支的构建视图中。所有添加到视图中的分支和标签,会默认执行一次构建任务。

🍑 演示二

演示2:美化消息通知内容

  • 添加构建阶段记录
  • 使用 markdown 格式,添加构建分支消息

jenkins/pipelines/p6.yaml

pipeline 
    agent  label '172.21.51.68'

    environment 
        IMAGE_REPO = "172.21.51.143:5000/myblog"
        DINGTALK_CREDS = credentials('dingTalk')
        TAB_STR = "\\n                    \\n                    "
    

    stages 
        stage('printenv') 
            steps 
                script
                    sh "git log --oneline -n 1 > gitlog.file"
                    env.GIT_LOG = readFile("gitlog.file").trim()
                
                sh 'printenv'
            
        
        stage('checkout') 
            steps 
                checkout scm
                script
                    env.BUILD_TASKS = env.STAGE_NAME + "√..." + env.TAB_STR
                
            
        
        stage('build-image') 
            steps 
                retry(2)  sh 'docker build . -t $IMAGE_REPO:$GIT_COMMIT'
                script
                    env.BUILD_TASKS += env.STAGE_NAME + "√..." + env.TAB_STR
                
            
        
        stage('push-image') 
            steps 
                retry(2)  sh 'docker push $IMAGE_REPO:$GIT_COMMIT'
                script
                    env.BUILD_TASKS += env.STAGE_NAME + "√..." + env.TAB_STR
                
            
        
        stage('deploy') 
            steps 
                sh "sed -i 's#IMAGE_URL#$IMAGE_REPO:$GIT_COMMIT#g' manifests/*"
                timeout(time: 1, unit: 'MINUTES') 
                    sh "kubectl apply -f manifests/"
                
                script
                    env.BUILD_TASKS += env.STAGE_NAME + "√..." + env.TAB_STR
                
            
        
    
    post 
        success  
            echo 'Congratulations!'
            sh """
                curl 'https://oapi.dingtalk.com/robot/send?access_token=$DINGTALK_CREDS_PSW' \\
                    -H 'Content-Type: application/json' \\
                    -d '
                        "msgtype": "markdown",
                        "markdown": 
                            "title":"myblog",
                            "text": "😄👍 构建成功 👍😄  \\n**项目名称**:luffy  \\n**Git log**: $GIT_LOG   \\n**构建分支**: $GIT_BRANCH   \\n**构建地址**:$RUN_DISPLAY_URL  \\n**构建任务**:$BUILD_TASKS"
                        
                    '
            """ 
        
        failure 
            echo 'Oh no!'
            sh """
                curl 'https://oapi.dingtalk.com/robot/send?access_token=$DINGTALK_CREDS_PSW' \\
                    -H 'Content-Type: application/json' \\
                    -d '
                        "msgtype": "markdown",
                        "markdown": 
                            "title":"myblog",
                            "text": "😖❌ 构建失败 ❌😖  \\n**项目名称**:luffy  \\n**Git log**: $GIT_LOG   \\n**构建分支**: $GIT_BRANCH  \\n**构建地址**:$RUN_DISPLAY_URL  \\n**构建任务**:$BUILD_TASKS"
                        
                    '
            """
        
        always  
            echo 'I will always say Hello again!'
        
    

🍑 演示三

演示3:通知 gitlab 构建状态

Jenkins 端做了构建,可以通过 gitlab 通过的 api 将构建状态通知过去,作为开发人员发起 Merge Request 或者合并 Merge Request 的依据之一。

注意一定要指定 gitLabConnection('gitlab'),不然没法认证到 Gitlab 端

jenkins/pipelines/p7.yaml

pipeline 
    agent  label '172.21.51.68'
    
    options 
		buildDiscarder(logRotator(numToKeepStr: '10'))
		disableConcurrentBuilds()
		timeout(time: 20, unit: 'MINUTES')
		gitLabConnection('gitlab')
	

    environment 
        IMAGE_REPO = "172.21.51.143:5000/demo/myblog"
        DINGTALK_CREDS = credentials('dingTalk')
        TAB_STR = "\\n                    \\n                    "
    

    stages 
        stage('printenv') 
            steps 
                script
                    sh "git log --oneline -n 1 > gitlog.file"
                    env.GIT_LOG = readFile("gitlog.file").trim()
                
                sh 'printenv'
            
        
        stage('checkout') 
            steps 
                checkout scm
                updateGitlabCommitStatus(name: env.STAGE_NAME, state: 'success')
                script
                    env.BUILD_TASKS = env.STAGE_NAME + "√..." + env.TAB_STR
                
            
        
        stage('build-image') 
            steps 
                retry(2)  sh 'docker build . -t $IMAGE_REPO:$GIT_COMMIT'
                updateGitlabCommitStatus(name: env.STAGE_NAME, state: 'success')
                script
                    env.BUILD_TASKS += env.STAGE_NAME + "√..." + env.TAB_STR
                
            
        
        stage('push-image') 
            steps 
                retry(2)  sh 'docker push

基于K8s的DevOps平台实践

文章目录


前言

今天是「基于K8s的DevOps平台实践」的最后一篇,前两篇地址如下:

上面两篇均排在云原生领域榜第一

1. Jenkins与k8s集成

  1. Jenkins 如何对接 kubernetes 集群
  2. 使用 kubernetes 的 Pod-Template 来作为动态的 agent 执行 Jenkins 任务
  3. 如何制作 agent 容器实现不同类型的业务的集成
  4. 集成代码扫描、docker 镜像自动构建、k8s 服务部署、自动化测试

🍑 插件安装及配置

插件官方文档

  1. [系统管理] -> [插件管理] -> [搜索kubernetes] -> 直接安装

    若安装失败,请先更新 bouncycastle API Plugin 并重新启动Jenkins

  2. [系统管理] -> [系统配置] -> [Add a new cloud]

  3. 配置地址信息

    • Kubernetes 地址:https://kubernetes.default
    • Kubernetes 命名空间:jenkins
    • 服务证书不用写(我们在安装 Jenkins 的时候已经指定过 serviceAccount),均使用默认
    • 连接测试,成功会提示:Connection test successful
    • Jenkins地址:http://jenkins:8080
    • Jenkins 通道 :jenkins:50000
  4. 配置 Pod Template

    • 名称:jnlp-slave

    • 命名空间:jenkins

    • 标签列表:jnlp-slave,作为 agent 的 label 选择用

    • 连接 Jenkins 的超时时间(秒):300,设置连接 jenkins 超时时间

    • 工作空间卷:选择 hostpath,设置 /opt/jenkins,注意需要设置目录权限,否则 Pod 没有权限

$ chown -R 1000:1000 /opt/jenkins
$ chmod 700 /opt/jenkins

🍑 演示动态slave pod

# 为准备运行jnlp-slave-agent的pod的节点打上label
$ kubectl label node k8s-slave1 agent=true

### 回放一次多分支流水线develop分支
agent  label 'jnlp-slave'

执行任务,会下载默认的 jnlp-slave 镜像,地址为 jenkins/inbound-agent:4.3-4,我们可以先在 k8s-master 节点拉取下来该镜像:

$ docker pull jenkins/inbound-agent:4.3-4

保存 jenkinsfile 提交后,会出现报错,因为我们的 agent 已经不再是宿主机,而是 Pod 中的容器内,报错如下:

因此我们需要将用到的命令行工具集成到 Pod 的容器内,但是思考如下问题:

  • 目前是用的 jnlp 的容器,是 java 的环境,我们在此基础上需要集成很多工具,能不能创建一个新的容器,让新容器来做具体的任务,jnlp-slave 容器只用来负责连接 jenkins-master
  • 针对不同的构建环境(java、python、go、nodejs),可以制作不同的容器,来执行对应的任务

🍑 Pod-Template中容器镜像的制作

为解决上述问题,我们制作一个 tools 镜像,集成常用的工具,来完成常见的构建任务,需要注意的几点:

  • 使用 alpine 基础镜像,自身体积比较小
  • 替换国内安装源
  • 为了使用 docker,安装了 docker
  • 为了克隆代码,安装 git
  • 为了后续做 python 的测试等任务,安装 python 环境
  • 为了在容器中调用 kubectl 的命令,拷贝了 kubectl 的二进制文件
  • 为了认证 kubectl,需要在容器内部生成 .kube 目录及 config 文件
$ mkdir tools;
$ cd tools;
$ cp `which kubectl` .
$ cp ~/.kube/config .

Dockerfile

jenkins/custom-images/tools/Dockerfile

FROM alpine:3.13.4
LABEL maintainer="inspur_lyx@hotmail.com"
USER root

RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apk/repositories && \\
    apk update && \\
    apk add  --no-cache openrc docker git curl tar gcc g++ make \\
    bash shadow openjdk8 python2 python2-dev py-pip python3-dev openssl-dev libffi-dev \\
    libstdc++ harfbuzz nss freetype ttf-freefont && \\
    mkdir -p /root/.kube && \\
    usermod -a -G docker root

COPY config /root/.kube/

RUN rm -rf /var/cache/apk/* 
#-----------------安装 kubectl--------------------#
COPY kubectl /usr/local/bin/
RUN chmod +x /usr/local/bin/kubectl
# ------------------------------------------------#

执行镜像构建并推送到仓库中:

$ docker build . -t 172.21.51.143:5000/devops/tools:v1
$ docker push 172.21.51.143:5000/devops/tools:v1

我们可以直接使用该镜像做测试:

## 启动临时镜像做测试
$ docker run --rm -ti 172.21.51.143:5000/devops/tools:v1 bash
# / git clone http://xxxxxx.git
# / kubectl get no
# / python3
#/ docker

## 重新挂载docker的sock文件
docker run -v /var/run/docker.sock:/var/run/docker.sock --rm -ti 172.21.51.143:5000/devops/tools:v1 bash

🍑 实践通过Jenkinsfile实现demo项目自动发布到kubenetes环境

实践通过 Jenkinsfile 实现 demo 项目自动发布到 kubenetes 环境

在卷栏目,添加卷,Host Path Volume,不然在容器中使用 docker 会提示 docker 服务未启动

tools 容器做好后,我们需要对 Jenkinsfile 做如下调整:

jenkins/pipelines/p8.yaml

pipeline 
    agent  label 'jnlp-slave'
    
    options 
		buildDiscarder(logRotator(numToKeepStr: '10'))
		disableConcurrentBuilds()
		timeout(time: 20, unit: 'MINUTES')
		gitLabConnection('gitlab')
	

    environment 
        IMAGE_REPO = "172.21.51.143:5000/myblog"
        DINGTALK_CREDS = credentials('dingTalk')
        TAB_STR = "\\n                    \\n                    "
    

    stages 
        stage('printenv') 
            steps 
                script
                    sh "git log --oneline -n 1 > gitlog.file"
                    env.GIT_LOG = readFile("gitlog.file").trim()
                
                sh 'printenv'
            
        
        stage('checkout') 
            steps 
                container('tools') 
                    checkout scm
                
                updateGitlabCommitStatus(name: env.STAGE_NAME, state: 'success')
                script
                    env.BUILD_TASKS = env.STAGE_NAME + "√..." + env.TAB_STR
                
            
        
        stage('build-image') 
            steps 
                container('tools') 
                    retry(2)  sh 'docker build . -t $IMAGE_REPO:$GIT_COMMIT'
                
                updateGitlabCommitStatus(name: env.STAGE_NAME, state: 'success')
                script
                    env.BUILD_TASKS += env.STAGE_NAME + "√..." + env.TAB_STR
                
            
        
        stage('push-image') 
            steps 
                container('tools') 
                    retry(2)  sh 'docker push $IMAGE_REPO:$GIT_COMMIT'
                
                updateGitlabCommitStatus(name: env.STAGE_NAME, state: 'success')
                script
                    env.BUILD_TASKS += env.STAGE_NAME + "√..." + env.TAB_STR
                
            
        
        stage('deploy') 
            steps 
                container('tools') 
                    sh "sed -i 's#IMAGE_URL#$IMAGE_REPO:$GIT_COMMIT#g' manifests/*"
                    timeout(time: 1, unit: 'MINUTES') 
                        sh "kubectl apply -f manifests/"
                    
                
                updateGitlabCommitStatus(name: env.STAGE_NAME, state: 'success')
                script
                    env.BUILD_TASKS += env.STAGE_NAME + "√..." + env.TAB_STR
                
            
        
    
    post 
        success  
            echo 'Congratulations!'
            sh """
                curl 'https://oapi.dingtalk.com/robot/send?access_token=$DINGTALK_CREDS_PSW' \\
                    -H 'Content-Type: application/json' \\
                    -d '
                        "msgtype": "markdown",
                        "markdown": 
                            "title":"myblog",
                            "text": "😄👍 构建成功 👍😄  \\n**项目名称**:luffy  \\n**Git log**: $GIT_LOG   \\n**构建分支**: $BRANCH_NAME   \\n**构建地址**:$RUN_DISPLAY_URL  \\n**构建任务**:$BUILD_TASKS"
                        
                    '
            """ 
        
        failure 
            echo 'Oh no!'
            sh """
                curl 'https://oapi.dingtalk.com/robot/send?access_token=$DINGTALK_CREDS_PSW' \\
                    -H 'Content-Type: application/json' \\
                    -d '
                        "msgtype": "markdown",
                        "markdown": 
                            "title":"myblog",
                            "text": "😖❌ 构建失败 ❌😖  \\n**项目名称**:luffy  \\n**Git log**: $GIT_LOG   \\n**构建分支**: $BRANCH_NAME  \\n**构建地址**:$RUN_DISPLAY_URL  \\n**构建任务**:$BUILD_TASKS"
                        
                    '
            """
        
        always  
            echo 'I will always say Hello again!'
        
    

2. Jenkins集成Sonarqube

集成 sonarQube 实现代码扫描

Sonar 可以从以下七个维度检测代码质量,而作为开发人员至少需要处理前 5 种代码质量问题。

  1. 不遵循代码标准
    sonar 可以通过 PMD、CheckStyle、Findbugs 等等代码规则检测工具规范代码编写。
  2. 潜在的缺陷
    sonar 可以通过 PMD、CheckStyle、Findbugs 等等代码规则检测工具检 测出潜在的缺陷。
  3. 糟糕的复杂度分布
    文件、类、方法等,如果复杂度过高将难以改变,这会使得开发人员 难以理解它们, 且如果没有自动化的单元测试,对于程序中的任何组件的改变都将可能导致需要全面的回归测试。
  4. 重复
    显然程序中包含大量复制粘贴的代码是质量低下的,sonar 可以展示 源码中重复严重的地方。
  5. 注释不足或者过多
    没有注释将使代码可读性变差,特别是当不可避免地出现人员变动时,程序的可读性将大幅下降而过多的注释又会使得开发人员将精力过多地花费在阅读注释上,亦违背初衷。
  6. 缺乏单元测试
    sonar 可以很方便地统计并展示单元测试覆盖率。
  7. 糟糕的设计
    通过 sonar 可以找出循环,展示包与包、类与类之间的相互依赖关系,可以检测自定义的架构规则通过 sonar 可以管理第三方的 jar 包,可以利用 LCOM4 检测单个任务规则的应用情况,检测耦合。

🍑 sonarqube架构简介

如图所示:

  1. CS 架构
    • sonarqube scanner
    • sonarqube server
  2. SonarQube Scanner 扫描仪在本地执行代码扫描任务
  3. 执行完后,将分析报告被发送到 SonarQube 服务器进行处理
  4. SonarQube 服务器处理和存储分析报告导致 SonarQube 数据库,并显示结果在 UI 中

🍑 sonarqube on kubernetes环境搭建

  1. 资源文件准备

sonar/sonar.yaml

  • 和 gitlab 共享 postgres 数据库
  • 使用 ingress 地址 sonar.luffy.com 进行访问
  • 使用 initContainers 进行系统参数调整
apiVersion: v1
kind: Service
metadata:
  name: sonarqube
  namespace: jenkins
  labels:
    app: sonarqube
spec:
  ports:
  - name: sonarqube
    port: 9000
    targetPort: 9000
    protocol: TCP
  selector:
    app: sonarqube
---
apiVersion: apps/v1
kind: Deployment
metadata:
  namespace: jenkins
  name: sonarqube
  labels:
    app: sonarqube
spec:
  replicas: 1
  selector:
    matchLabels:
      app: sonarqube
  template:
    metadata:
      labels:
        app: sonarqube
    spec:
      initContainers:
      - command:
        - /sbin/sysctl
        - -w
        - vm.max_map_count=262144
        image: alpine:3.6
        imagePullPolicy: IfNotPresent
        name: elasticsearch-logging-init
        resources: 
        securityContext:
          privileged: true
      containers:
      - name: sonarqube
        image: sonarqube:7.9-community
        ports:
        - containerPort: 9000
        env:
        - name: SONARQUBE_JDBC_USERNAME
          valueFrom:
            secretKeyRef:
              name: gitlab-secret
              key: postgres.user.root
        - name: SONARQUBE_JDBC_PASSWORD
          valueFrom:
            secretKeyRef:
              name: gitlab-secret
              key: postgres.pwd.root
        - name: SONARQUBE_JDBC_URL
          value: "jdbc:postgresql://postgres:5432/sonar"
        livenessProbe:
          httpGet:
            path: /sessions/new
            port: 9000
          initialDelaySeconds: 60
          periodSeconds: 30
        readinessProbe:
          httpGet:
            path: /sessions/new
            port: 9000
          initialDelaySeconds: 60
          periodSeconds: 30
          failureThreshold: 6
        resources:
          limits:
            cpu: 2000m
            memory: 4096Mi
          requests:
            cpu: 300m
            memory: 512Mi
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: sonarqube
  namespace: jenkins
spec:
  rules:
  - host: sonar.luffy.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service: 
            name: sonarqube
            port:
              number: 9000
  1. sonarqube 服务端安装

# 创建sonar数据库
$ kubectl -n jenkins exec -ti postgres-5859dc6f58-mgqz9 bash
#/ psql 
# create database sonar;

## 创建sonarqube服务器
$ kubectl create -f sonar.yaml

## 配置本地hosts解析
172.21.51.143 sonar.luffy.com

## 访问sonarqube,初始用户名密码为 admin/admin
$ curl http://sonar.luffy.com
  1. sonar-scanner的安装

    下载地址: sonar

  2. 演示sonar代码扫描功能

  • 在项目根目录中准备配置文件 sonar-project.properties
sonar.projectKey=myblog
sonar.projectName=myblog
# if you want disabled the DTD verification for a proxy problem for example, true by default
sonar.coverage.dtdVerification=false
# JUnit like test report, default value is test.xml
sonar.sources=blog,myblog
  • 配置 sonarqube 服务器地址

    • 由于sonar-scanner 需要将扫描结果上报给 sonarqube 服务器做质量分析,因此我们需要在 sonar-scanner 中配置 sonarqube 的服务器地址:

    • 在集群宿主机中测试,先配置一下 hosts 文件,然后配置 sonar 的地址:

$ cat /etc/hosts
172.21.51.143  sonar.luffy.com

$ cat sonar-scanner/conf/sonar-scanner.properties
#----- Default SonarQube server
#sonar.host.url=http://localhost:9000
sonar.host.url=http://sonar.luffy.com
#----- Default source code encoding
#sonar.sourceEncoding=UTF-8
  • 为了使所有的 pod 都可以通过sonar.luffy.com访问,可以配置 coredns 的静态解析
# 静态解析
$ kubectl -n kube-system edit cm coredns 
...
             hosts 
                 172.21.51.143 jenkins.luffy.com gitlab.luffy.com sonar.luffy.com
                 fallthrough
          
  • 执行扫描
## 在项目的根目录下执行
$ /opt/sonar-scanner-4.2.0.1873-linux/bin/sonar-scanner  -X 
  • sonarqube 界面查看结果

    • 登录 sonarqube 界面查看结果,Quality Gates 说明

java 项目的配置文件通常格式为:

sonar.projectKey=eureka-cluster
sonar.projectName=eureka-cluster
# if you want disabled the DTD verification for a proxy problem for example, true by default
# JUnit like test report, default value is test.xml
sonar.sources=src/main/java
sonar.language=java
sonar.tests=src/test/java
sonar.java.binaries=target/classes

🍑 插件安装及配置

  1. 集成到 tools 容器中

    由于我们的代码拉取、构建任务均是在 tools 容器中进行,因此我们需要把 scanner 集成到我们的 tools 容器中,又因为 scanner 是一个 cli 客户端,因此我们直接把包解压好,拷贝到 tools 容器内部,配置一下 PATH 路径即可,注意两点:

    • 直接在 tools 镜像中配置 http://sonar.luffy.com

    • 由于 tools 已经集成了 java 环境,因此可以直接剔除 scanner 自带的 jre

    • 删掉 sonar-scanner/jre 目录

    • 修改 sonar-scanner/bin/sonar-scanner

use_embedded_jre=false

$ cd tools
$ cp -r /opt/sonar-scanner-4.2.0.1873-linux/ sonar-scanner
## sonar配置,由于我们是在Pod中使用,也可以直接配置:sonar.host.url=http://sonarqube:9000
$ cat sonar-scanner/conf/sonar-scanner.properties
#----- Default SonarQube server
sonar.host.url=http://sonar.luffy.com

#----- Default source code encoding
#sonar.sourceEncoding=UTF-8

$ rm -rf sonar-scanner/jre
$ vi sonar-scanner/bin/sonar-scanner
...
use_embedded_jre=false
...

Dockerfile

jenkins/custom-images/tools/Dockerfile2

FROM alpine:3.13.4
LABEL maintainer="inspur_lyx@hotmail.com"
USER root

RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apk/repositories && \\
    apk update && \\
    apk add  --no-cache openrc docker git curl tar gcc g++ make \\
    bash shadow openjdk8 python2 python2-dev py-pip python3-dev openssl-dev libffi-dev \\
    libstdc++ harfbuzz nss freetype ttf-freefont && \\
    mkdir -p /root/.kube && \\
    usermod -a -G docker root

COPY config /root/.kube/


RUN rm -rf /var/cache/apk/*

#-----------------安装 kubectl--------------------#
COPY kubectl /usr/local/bin/
RUN chmod +x /usr/local/bin/kubectl
# ------------------------------------------------#

#---------------安装 sonar-scanner-----------------#
COPY sonar-scanner /usr/lib/sonar-scanner
RUN ln -s /usr/lib/sonar-scanner/bin/sonar-scanner /usr/local/bin/sonar-scanner && chmod +x /usr/local/bin/sonar-scanner
ENV SONAR_RUNNER_HOME=/usr/lib/sonar-scanner
# ------------------------------------------------#

重新构建镜像,并推送到仓库:

$ docker build . -t 172.21.51.143:5000/devops/tools:v2
$ docker push 172.21.51.143:5000/devops/tools:v2  
  1. 修改 Jenkins PodTemplate

    为了在新的构建任务中可以拉取 v2 版本的 tools 镜像,需要更新 PodTemplate

  2. 安装并配置 sonar 插件

    由于 sonarqube 的扫描的结果需要进行 Quality Gates 的检测,那么我们在容器中执行完代码扫描任务后,如何知道本次扫描是否通过了 Quality Gates,那么就需要借助于 sonarqube 实现的 jenkins 的插件。

    • 安装插件

      插件中心搜索 sonarqube,直接安装

    • 配置插件

      系统管理 -> 系统配置 -> SonarQube servers -> Add SonarQube

      • Name:sonarqube

      • Server URL:http://sonar.luffy.com

      • Server authentication token

        ① 登录 sonarqube -> My Account -> Security -> Generate Token

        ② 登录 Jenkins,添加全局凭据,类型为 Secret text

    • 如何在 jenkinsfile 中使用

      我们在 官方介绍 中可以看到:

🍑 Jenkinsfile集成sonarqube演示

jenkins/pipelines/p9.yaml

pipeline 
    agent  label 'jnlp-slave'
    
    options 
		buildDiscarder(logRotator(numToKeepStr: '10'))
		disableConcurrentBuilds()
		timeout(time: 20, unit: 'MINUTES')
		gitLabConnection('gitlab')
	

    environment 
        IMAGE_REPO = "172.21.51.143:5000/myblog"
        DINGTALK_CREDS = credentials('dingTalk')
        TAB_STR = "\\n                    \\n                    "
    

    stages 
        stage('git-log') 
            steps 
                script
                    sh "git log --oneline -n 1 > gitlog.file"
                    env.GIT_LOG = readFile("gitlog.file").trim()
                
                sh 'printenv'
            
                
        stage('checkout') 
            steps 
                container('tools') 
                    checkout scm
                
                updateGitlabCommitStatus(name: env.STAGE_NAME, state: 'success')
                script
                    env.BUILD_TASKS = env.STAGE_NAME + "√..." + env.TAB_STR
                
            
        
        stage('CI')
            failFast true
            parallel 
                stage('Unit Test') 
                    steps 
                        echo "Unit Test Stage Skip..."
                    
                
                stage('Code Scan') 
                    steps 
                        container('tools') 
                            withSonarQubeEnv('sonarqube') 
                                sh 'sonar-scanner -X'
                                sleep 3
                            
                            script 
                                timeout(1) 
                                    def qg = waitForQualityGate('sonarqube')
                                    if (qg.status != 'OK') 
                                        error "未通过Sonarqube的代码质量阈检查,请及时修改!failure: $qg.status"
                                    
                                
                            
                        
                    
                
            
        
        stage('build-image') 
            steps 
                container('tools') 
                    retry(2)  sh 'docker build . -t $IMAGE_REPO:$GIT_COMMIT'
                
                updateGitlabCommitStatus(name: env.STAGE_NAME, state: 'success')
                script
                    env.BUILD_TASKS += env.STAGE_NAME + "√..." + env.TAB_STR
                
            
        
        stage('push-image') 
            steps 
                container('tools') 
                    retry(2)  sh 'docker push $IMAGE_REPO:$GIT_COMMIT'
                
                updateGitlabCommitStatus(name: env.STAGE_NAME, state: 'success')
                script
                    env.BUILD_TASKS += env.STAGE_NAME + "√..." + env.TAB_STR
                
            
        
        stage('deploy') 
            steps 
                container('tools') 
                    sh "sed -i 's#IMAGE_URL#$IMAGE_REPO:$GIT_COMMIT#g' manifests/*"
                    timeout(time: 1, unit: 'MINUTES') 
                        sh "kubectl apply -f manifests/"
                    
                
                updateGitlabCommitStatus(name: env.STAGE_NAME, state: 'success')
                script
                    env.BUILD_TASKS += env.STAGE_NAME + "√..." + env.TAB_STR
                
            
        
    
    post 
        success  
            echo 'Congratulations!'
            sh """
                curl 'https://oapi.dingtalk.com/robot/send?access_token=$DINGTALK_CREDS_PSW' \\
                    -H 'Content-Type: application/json' \\
                    -d '
                        "msgtype": "markdown",
                        "markdown": 
                            "title":"myblog",
                            "text": "😄👍 构建成功 👍😄  \\n**项目名称**:luffy  \\n**Git log**: $GIT_LOG   \\n**构建分支**: $BRANCH_NAME   \\n**构建地址**:$RUN_DISPLAY_URL  \\n**构建任务**:$BUILD_TASKS"
                        
                    '
            """ 
        
        failure 
            echo 'Oh no!'
            sh """
                curl 'https://oapi.dingtalk.com/robot/send?access_token=$DINGTALK_CREDS_PSW' \\
                    -H 'Content-Type: application/json' \\
                    -d '
                        "msgtype": "markdown",
                        "markdown": 
                            "title":"myblog",
                            "text": "😖❌ 构建失败 ❌😖  \\n**项目名称**:luffy  \\n**Git log**: $GIT_LOG   \\n**构建分支**: $BRANCH_NAME  \\n**构建地址**:$RUN_DISPLAY_URL  \\n**构建任务**:$BUILD_TASKS"
                        
                    '
            """
        
        always  
            echo 'I will always say Hello again!'
        
    

若 Jenkins 执行任务过程中 sonarqube 端报类似下图的错:

则需要在 sonarqube 服务端进行如下配置,添加一个 webhook:

3. Jenkins集成robotFramework

集成 RobotFramework 实现验收测试

一个基于 Python 语言,用于验收测试和验收测试驱动开发(ATDD)的通用测试自动化框架,提供了一套特定的语法,并且有非常丰富的测试库 。

🍑 robot用例简介

robot/robot.txt

*** Settings ***
Library           RequestsLibrary
Library    

以上是关于基于K8s的DevOps平台实践的主要内容,如果未能解决你的问题,请参考以下文章

基于K8s的DevOps平台实践

基于K8s的DevOps平台实践

基于K8s的DevOps平台实践

DEVOPS架构师 -- 05从零开始构建基于Kubernetes的DevOps平台

有货基于Kubernetes容器环境的持续交付实践

jenkins X实践系列 —— 背景知识

(c)2006-2024 SYSTEM All Rights Reserved IT常识