Jenkins Pipeline实现自动构建部署到Kubernetes

Jenkins Pipeline实现自动构建部署到Kubernetes

本文中采用jenkins pipeline执行整个jenkins的构建过程,在pipeline中使用不同的docker容器实现检出代码、代码编译、构建镜像并上传到镜像仓库、部署到kuberneres。

配置jenkins

1.插件配置

更换插件源

Manage Jenkins => Plugin Manager => Advanced => Update Site => mirrors.tuna.tsinghua.edu.cn #清华源


插件源

安装插件

Manage Jenkins => Plugin Manager => Available => Localization: Chinese (Simplified)

Manage Jenkins => Plugin Manager => Available => Blue Ocean

Manage Jenkins => Plugin Manager => Available => Kubernetes(helm安装默认会有该插件)

Manage Jenkins => Plugin Manager => Available => docker-workflow

重启jenkins生效(可以在域名后加/restart进行重启)

2.前期配置

  • .kube/config
kubectl create secret generic jenkins-k8s-cfg -n jenkins --from-file=/root/.kube/config 
secret/jenkins-k8s-cfg created 

k8s中使用kubectl命令时需要yaml格式的服务器及授权信息配置文件。这里将kubectl的yaml配置文件的内容以base64编码后保存在jenkins的凭据中。pipeline任务执行时,先从jenkins凭据中获取内容,进行base64解码后将配置保存为~/.kube/config文件。kubectl的配置文件的内容如下:

apiVersion: v1 
clusters: 
- cluster: 
    certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUM2akNDQWRLZ0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFRc0ZBREFWTVJNd0VRWURWUVFERXdwcmRXSmwKY201bGRHVnpNQjRYRFRJeU1ESXhOakEyTkRBeE5Gb1hEVE15TURJeE5EQTJORFV4TkZvd0ZURVRNQkVHQTFVRQpBeE1LYTNWaVpYSnVaWFJsY3pDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBUHEzCmRGSDJMbytrenFabzhMNEYrNUtJWUhQVDQzZk1pK1NJb2VZdDNFTE9TZ1Y5bHJLeVJLZDBrNkVpV0FNZ3U2N3EKTW1SbUc1SHJKWGgycVgwL0RXaFVmRENFL09ESFhyOCtNaTBtcGU2dmVXTi8xZ2NJS2hyaFQ2MXlPY1RWVngvYgpDS2QzaTFIVTdUR1I4T3JmQ1RleDlLUUdQbmhhbUZHcXNtNW8yaS9jNmFVdW4xeTBEYlp4NHFEcktFTG5UTytoCjlNaFJ4U2crT3lObGVyQjB5Y0xKNWZOSVJ6U1A0bzkrV3NkTDQ2bU5RaytzZ3FpaU1HdGFldEVMQ2x1WDdkK0EKamxHZy92a0pWVnBDYkNjcDJDdHpnT0tqTWd4YmI2N3I2ZnY1NndiaEoxc0R2Mkdvb1JnR0htbi9JRlhNK2xTVwpsTTlZRloxZHQwc01GSGhUdDRzQ0F3RUFBYU5GTUVNd0RnWURWUjBQQVFIL0JBUURBZ0trTUJJR0ExVWRFd0VCCi93UUlNQVlCQWY4Q0FRQXdIUVlEVlIwT0JCWUVGRFJva1d3U0VPMElpM1d6OUhLcU16bnRaYVN3TUEwR0NTcUcKU0liM0RRRUJDd1VBQTRJQkFRRGtoZHQwVEljU0hLVGVvRERpbGNpUHJ3SnJlaTJPRklhUXAvWi9pdzZ2bk5SRQpQUkZLVENSYk9IKzV1eTk5dWJKSHdMamFuMWRJYk15YzhMd2Q3WnhWMzRwNHkwNkdzUVRZVjE5aUtTWUNUOGlZCmlyWGJWRUNFNlN3SUc4WTVDL285Lys5U2tmMkN0WEd6K3pHTnptVms2c0JxaVlIMkNCVkhIaTlEOHkrNC85b1EKcWY4amNudmZzRi85QzQ2ME01SytxZG1MY3dsc29LRklBMitYb0NRRjMvcU1SR21Bay95QjlCbmxsbkxHcHRmcQpwNnc1SmtSRW9JczVwTkNvK01iWEMzT3VhZzlTZndDamgxdEtVOEdxdjdndldsa3NneWZSQVBkTTkxa2YzRk9xCndvZGVZQmZOSElpK0Y4VjR2YWdDekovaW9US2dXSUpoOThiTkNrcW0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo= 
    server: https://172.172.172.172:6443 
  name: cc869q4ursfehmf5j0av0 
contexts: 
- context: 
    cluster: cc869q4ursfehmf5j0av0 
    user: "1009752" 
  name: cc869q4ursfehmf5j0av0@1009752 
current-context: cc869q4ursfehmf5j0av0@1009752 
kind: Config 
preferences: {} 
users: 
- name: "1009752" 
  user: 
    client-certificate-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUM4VENDQWRtZ0F3SUJBZ0lJVTRRbHNITGorTlV3RFFZSktvWklodmNOQVFFTEJRQXdGVEVUTUJFR0ExVUUKQXhNS2EzVmlaWEp1WlhSbGN6QWVGdzB5TWpBeU1UWXdOalF3TVRSYUZ3MDBNakF6TWpRd05qSTJOVEJhTUJJeApFREFPQmdOVkJBTVRCekV3TURrM05USXdnZ0VpTUEwR0NTcUdTSWIzRFFFQkFRVUFBNElCRHdBd2dnRUtBb0lCCkFRRFBrcGwzZmhaWStRSXh3RzNaOTNiemZaTExxemtpaFZ0N1B4WitGZXNTQ2MzNE1aZ1J5eGhiVlM5VXZ2Z20KTkNOemwzUVdVOWg2bEMvQjQ3NUh3eDFXZkZZN1ZMcjM5T1ZueWk2NE5wUENRNDd1SXVDRnhmYnBNZnlBWG4rZgpSN3RNS2VqV0tSTDFzRUhrSi85QkdiT3VEUWJGa1BmUUpoZ3VYZ0tpazFzbWQxZ2Y2OHNnclR4NHpSeHNDcTJuClU2V1dIL1lVTnQzOUIveTI5SXRYSVBMczVnb0paSVJyS2JtZEFZekExbkdGajMyc2tCRWpHU21XQnNUcTdGYXkKWXB4anBNc29LVVE3RzBkS0dsdXlDOGhySFFNdVpPblNSVkJpSkNUZ3ZycmpwMnhJOHlkZUhCOXBhVlJWdkJqQwpibzJmb3oyWFVKeUE1NkdLZnNyWklQTjNBZ01CQUFHalNEQkdNQTRHQTFVZER3RUIvd1FFQXdJRm9EQVRCZ05WCkhTVUVEREFLQmdnckJnRUZCUWNEQWpBZkJnTlZIU01FR0RBV2dCUTBhSkZzRWhEdENJdDFzL1J5cWpNNTdXV2sKc0RBTkJna3Foa2lHOXcwQkFRc0ZBQU9DQVFFQTFxNjEvK3NuRFZpendhUXNWZXR3WFcwcGZJd0wzNjZBNWJabApPNzV2OUpXWmQzN3A2ZXltRkhacWx0RWNncjNMNlNJUWdzanM4MWR0dEYyTCtOSk1vQk5JbFkvY3JCREx2VDQvCjNOZUtIUEVFQlRaODZVZE9tWCsrVEJHUGhUSllaam9YRXhBOWYwaUQrTHlsTFR3YmxvMnFrY2JoZTAyaFM4Y1AKRWs3QnhhYU9HR09lYXZ3SjFLMHJEN25hWGpkWFZvTEVoUnFRZmNVWTZwQUQ4ZzQ3V3JUK3lkdEhPSHdBajhEVgpOMEFpZDVRYW5qdFE1WU40ZXdsVi92SXlLV1dzTnRYbEpYM1BiaW1lQmVuYTZTdGJlTVNvUXRyZmZteHZWNFZSCkdFVENBdFdiZE90WS90V3BTR2tkZXdDL1VVY01PUlFBU3FmK3U3d1liY1JkWDIweEpRPT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo= 
    client-key-data: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFb3dJQkFBS0NBUUVBejVLWmQzNFdXUGtDTWNCdDJmZDI4MzJTeTZzNUlvVmJlejhXZmhYckVnbk4rREdZCkVjc1lXMVV2Vkw3NEpqUWpjNWQwRmxQWWVwUXZ3ZU8rUjhNZFZueFdPMVM2OS9UbFo4b3V1RGFUd2tPTzdpTGcKaGNYMjZUSDhnRjUvbjBlN1RDbm8xaWtTOWJCQjVDZi9RUm16cmcwR3haRDMwQ1lZTGw0Q29wTmJKbmRZSCt2TApJSzA4ZU0wY2JBcXRwMU9sbGgvMkZEYmQvUWY4dHZTTFZ5RHk3T1lLQ1dTRWF5bTVuUUdNd05aeGhZOTlySkFSCkl4a3BsZ2JFNnV4V3NtS2NZNlRMS0NsRU94dEhTaHBic2d2SWF4MERMbVRwMGtWUVlpUWs0TDY2NDZkc1NQTW4KWGh3ZmFXbFVWYndZd202Tm42TTlsMUNjZ09laGluN0syU0R6ZHdJREFRQUJBb0lCQUM2NkFLUWVsU2RJUHM4MgpVMFJLNmRNY1JJeG1xZ0QwWmtpVFJodnlFUzNKV1QxeTVKbTB6M0hYOWtTc2pLNEU5S29Ud1FVNXNMUy9ZUlRUCmtNVVV6aWptNm42R3IyUWNFL3hPVGtBWHdTRnRGZUxUcFJsWUhDSVlwNCs2Tmg2RUJreTU3bmt2Vzk1RTVFQloKSktQaUJrbTdFdmdzanl5ak5yUlJZMlIzcVlBNTI1QStJNWhLR25oczd4Z2R1RmRZUmlleUlDN1diaTlzeTljZwpqTWRxZlJZeDF6QnJ1WUlDRi8rWmlleWFZVk15RlRwREFZVlhYeEx0RWpmOW8zenV6ek14RURTOFEwd05jRGd6Clh2c3paM0dWL2Nack5kYVNib0szRTI1TmRiZmN5NDBIYlJjUzZLOStheGQ2OGMwVEFuQkFVRitoekhGZGJvV0wKSEVBSjhiRUNnWUVBM0hja0hOVnBSTG9ML1dBeUl0L3Azc3pKak84SHdRK2g3bWJud2EvdGhNWCtLVStFa2JOZQo1c3dDMXRzRmRib1o1cHFMYkMyYXNIUGpGMDI4UEFWT1hHS0pRcG1KZmkxNkY0aVdFRHNOMXY0NGQ4b1JQSlRzCjNKMHBqdlBWTVUvK3hnNnJFWGRsaGFCN3l1b1Bxc0FLdVdmVnRJcU1vOURLUFA4cG9DUkRUS2tDZ1lFQThRZDYKSzZQc2xkRDhGOU9wZVhYTFRrSFlXUDJsQmExNUJIbW5mR3NOaGg4Qy9qUUpocFVZS2IxcGdXZDhUaHJGSkw4cwplTTcweXNEL2cyVUEyR1c0aWs2ckVNdFpjWnhvNkhHZHRvQjZIMzhFdWhqeXdsT2FuWkdmZHpTQ21rT2pVS0dkCmxDcDk1YkhVZkxQNitxcm5CWWRyaEtTdXpOcGFUVGtiSk5zdE14OENnWUVBakVMa1g4L0t0R1hsUU9OVm9taHcKbzBZSXFMaGVaNDZwaUVKQ0NqRUNYT21XWlBQcE5NVTRpSCtkQXRLL2E0SGwvcGV1UWpuVlk1T25FK2xwNjRtNApyQUFvc1kxN015Sjl2Y0V4TDh4U1pzakFaajNScjRoVUZadUxtQjQ3TWE5aU9PR1dKbnVleEdBd3dnMGR3SldECmZTZTVoNjVxQWNMUkYvT0NxUmJKRmxrQ2dZQkd4MGhUK2w0bUhHbDY0YmY1cCtLY3gyOHRxc1pENVk0TkIyQnMKZzg5RDNHVjNtblpWcEFDbGVTNjdSbFZWWmowUUxtZHp6bXBLQStURWcrdDhvRnBSbXdwVzh5Ly8wY3RvdnJ4dQpkRW5vWjlkT2dyYis0NmdhcjBHaHNyVExjaU5LTVR0aDRCeDZULzh5NDNOSEd0cWVZQWJ0dTZKdEc0MjY4Wk1MCi9xVEdSd0tCZ0JwUzc3WDZ3ald0c3RGOGRrbDVSUmdFZXhOUUcxMzdLblY1aEgxbjVnamFzYWFGYXg3SUcwSVUKZzZ1WVJJZ3VWazhEMDN1WDZRWXNGRVNEUG91RlVGTVdnWitBanVoVys3c3NVRFByZW5veklGWmgrd2xsdEwvZgpkQ1lwb2lEQTh6TmV6c0wrMDRhQ2djeVZNZUlCUnlxTzI1THpZUzNVT0g0VW9XTkdRRDNECi0tLS0tRU5EIFJTQSBQUklWQVRFIEtFWS0tLS0tCg== 
 

将以上信息保存到kube-config.yml

base64 kube-config.yml > kube-config.txt

在jenkins凭据中增加配置文件内容。在凭据设置界面,类型选择为“Secret text”,ID设置为“jenkins-k8s-config”(此处的ID必须与Jenkinsfile中的保持一致),Secret设置为上面经过base64编码后的配置文件内容。


Secret text
  • 为 maven 容器配置缓存和挂载 settings.xml
<?xml version="1.0" encoding="UTF-8"?> 
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0" 
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
          xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd"> 
    <mirrors> 
        <!-- 阿里云仓库 --> 
        <mirror> 
            <id>alimaven</id> 
            <mirrorOf>central</mirrorOf> 
            <name>aliyun maven</name> 
            <url>http://maven.aliyun.com/nexus/content/repositories/central/</url> 
        </mirror> 
        <!-- 中央仓库1 --> 
        <mirror> 
            <id>repo1</id> 
            <mirrorOf>central</mirrorOf> 
            <name>Human Readable Name for this Mirror.</name> 
            <url>http://repo1.maven.org/maven2/</url> 
        </mirror> 
        <!-- 中央仓库2 --> 
        <mirror> 
            <id>repo2</id> 
            <mirrorOf>central</mirrorOf> 
            <name>Human Readable Name for this Mirror.</name> 
            <url>http://repo2.maven.org/maven2/</url> 
        </mirror> 
    </mirrors>  
</settings> 
 


kubectl -n jenkins create configmap maven-config --from-file=settings.xml
  • gitlab配置公钥

创建密钥

mkdir -p /usr/local/src/ssh/.ssh/ 
ssh-keygen -t rsa -b 2048 -C "abc@abc.com"  -N "" -f /usr/local/src/ssh/.ssh/id_rsa 

gitlab配置公钥



jenkins 创建凭据

选择ssh username with private key类型 添加私钥




  • 配置镜像仓库密钥


  • jenkins动态slave测试

pipeline文件

def label = "jenkins-slave-${UUID.randomUUID().toString()}" // 
podTemplate(label: label, cloud: 'kubernetes') { 
    node(label) { 
        stage('Run shell') { 
            sh 'sleep 10s' 
            sh 'echo hello world.' 
} 





部署项目

创建pipeline项目


参数化构建工程,选择字符参数

名称: APP_NAME

默认值: deployment-demo

描述: 项目名称,用于创建deployment名称

名称: APP_NS

默认值: default

描述: namespace名称,deployment资源创建指定的ns

名称: GIT_VER

默认值: release

描述: 项目所在git中央仓库对应项目的分支或者版本号,例如release分支

名称: GIT_REPO

默认值: ssh://git@git.abc.com:tour/java/abc.git

描述: 项目所在的git中央仓库的地址

名称: MVN_DIR

默认值: ./

描述: 编译项目目录,默认为项目的根目录

名称: MVN_CMD

默认值: mvn -B clean package -Dmaven.test.skip=true -Dautoconfig.skip

描述: 执行mvn编译所用的命令

名称: IMAGE_NAME

默认值: base

描述: docker镜像名称,格式:<仓库名>/<镜像名>

名称: IMAGE_REPO

默认值: registry.cn-beijing.cr.aliyuncs.com

描述: docker镜像仓库名称

名称: BUSINESS_LINE

默认值: xundian

描述: 业务线名称

名称: BRANCH_TAG

默认值: master

描述: 代码分支名称











流水线

// 流水线的最外层结构,代表整条pipeline,包含着pipeline完整逻辑 
pipeline { 
  // 环境变量的定义 
  environment { 
   K8S_CONFIG = credentials('jenkins-k8s-config')  // 通过 ${params.xxx} 的方式对此参数进行引用 
  // pipeline中单独指令,用于指定流水的执行位置,它可能是代表着slave主机的某个物理机、虚拟机或者容器 
  agent { 
    kubernetes { 
      yaml ''' 
        apiVersion: v1 
        kind: Pod 
        metadata: 
          labels: 
            label: jenkins-mvn 
        spec: 
          containers: 
          - name: docker 
            image: registry.cn-beijing.aliyuncs.com/aquaman0319/docker:19.03-dind 
            command: 
            - dockerd 
            - --host=unix:///var/run/docker.sock 
            - --host=tcp://0.0.0.0:8000 
            securityContext: 
              privileged: true 
            tty: true 
          - name: kubectl 
            image: registry.cn-beijing.aliyuncs.com/aquaman0319/helm-kubectl-docker:v1.21.1-v3.6.0 
            command: 
            - cat 
            tty: true 
          - name: maven38 
            image: registry.cn-beijing.aliyuncs.com/aquaman0319/maven:3.8-jdk-8 
            command: 
            - cat 
            tty: true 
            volumeMounts: 
            - mountPath: "/root/.m2/repository" 
              name: "volume-0" 
            - mountPath: "/root/.m2" 
              name: "volume-1" 
          volumes: 
            - name: "volume-0" 
              persistentVolumeClaim: 
                claimName: "jenkins-m2" 
            - name: "volume-1" 
              configMap: 
                name: "maven-config"     
  // 用于包含所有的stage的定义 
  stages { 
    // 阶段,代表流水线的一个单独的功能完成时期,例如编译等 
    stage('检出代码') { 
          // 步骤,用于在stage中定义完成该阶段功能所需经历的一系列步骤 
      steps { 
        // 递归删除WORKSPACE下的文件和文件夹,避免缓存导致构建问题 
        deleteDir() 
                // credentialsId为ssh 凭据的id 
        checkout([$class: 'GitSCM', branches: [[name: "${params.GIT_VER}"]], doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [], userRemoteConfigs: [[credentialsId: "gitlabauth", url: "${params.GIT_REPO}"]]])         
    stage('构建代码') { 
      steps{ 
          // 用于创建容器的容器模板 
              container('maven38') { 
          sh "cd ${params.MVN_DIR} && ${params.MVN_CMD}" 
          // post定义将在pipeline运行或stage结束时运行的操作 
      post { 
        // 成功之后提取制品 
        success { 
          archiveArtifacts "target/*.jar" 
    stage('构建并推送镜像') { 
      steps { 
            container('docker') { 
        script { 
                    echo "构建Docker镜像" 
                    def CREATE_DATE=sh (script: "date '+%Y%m%d'",returnStdout: true).trim() 
                    TAG = "${params.BRANCH_TAG}"+"."+"${CREATE_DATE}"+"."+"${BUILD_NUMBER}" 
                    echo "${TAG}" 
                    sh (script: "cp ./Dockerfile ./target && cd ./target ") 
                    withDockerRegistry([credentialsId:'jenkins-docker-cfg', url:"https://${IMAGE_REPO}"]){ 
                        def image = docker.build("${params.IMAGE_REPO}/${params.BUSINESS_LINE}/${params.IMAGE_NAME}:${TAG}", ".") 
                        image.push("${TAG}") 
                    echo "已推送Docker镜像到镜像仓库" 
                    sh (script: "echo '${params.IMAGE_REPO}/${params.BUSINESS_LINE}/${params.IMAGE_NAME}:${TAG}' > tmp_image") 
    stage('更新服务') { 
      steps { 
            container('kubectl') { 
        sh "mkdir -p ~/.kube" 
        sh "echo ${K8S_CONFIG} | base64 -d > ~/.kube/config" 
              sh "kubectl apply -f devops/deploy-dev.yaml -n xundian"