zoukankan      html  css  js  c++  java
  • k8s 线上安装 jenkins并结合 jenkinsfile 实现 helm 自动化部署

    需求

    1. 开发人员通过上传 gitlab 新分支代码,通过 jenkinsfile 结合jenkins 自动发现分支并自动化部署该分支对应的容器
    2. 更新代码可以实现容器平滑更新
    

    环境

    1. k8s 1.16 高可用集群环境
    2. harbor 私有仓库已搭建
    3. gitlab 可以使用
    4. 部署nfs server,可提供给jenkins 存储使用
    

    部署jenkins

    # 创建新名称空间
    kubectl create  ns   myjenkins 
     
    # 准备配置文件 deployment、 svc 、ingress 、证书
    1. mkdir /myjenkins/jenkins
    2. deployment 准备配置yaml文件,jenkins-deployment.yaml 
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: jenkins
      namespace: myjenkins
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: jenkins
      strategy:
        rollingUpdate:
          maxSurge: 1
          maxUnavailable: 0
        type: RollingUpdate
      template:
        metadata:
          creationTimestamp: null
          labels:
            app: jenkins
        spec:
          containers:
          - env:
            - name: JAVA_OPTS
              value: -Duser.timezone=Asia/Shanghai
            image: jenkins:lts
            imagePullPolicy: IfNotPresent
            name: jenkins
            ports:
            - containerPort: 8080
              name: web
              protocol: TCP
            - containerPort: 50000
              name: agent
              protocol: TCP
            resources: {}
            securityContext:
              runAsUser: 0
            volumeMounts:
            - mountPath: /var/jenkins_home
              name: jenkinshome
          dnsPolicy: ClusterFirst
          restartPolicy: Always
          volumes:
          - name: jenkinshome
            nfs:
              path: /data/upload/myjenkins
              server: 172.24.119.30
    
    3.jenkins agent 准备配置yaml 文件,这是jenkins 的 agent 配置,jenkins-agent.yaml 
    apiVersion: v1
    kind: Service
    metadata:
      labels:
        app: jenkins
      name: jenkins-agent
      namespace: myjenkins
    spec:
      ports:
      - name: agent
        port: 50000
        protocol: TCP
        targetPort: 50000
      selector:
        app: jenkins
      sessionAffinity: None
      type: ClusterIP
    status:
      loadBalancer: {}
    
    4. jenkins svc 配置yaml 文件 jenkins-svc.yaml 
    apiVersion: v1
    kind: Service
    metadata:
      labels:
        app: jenkins
      name: jenkins
      namespace: myjenkins
    spec:
      ports:
      - name: web
        port: 8080
        protocol: TCP
        targetPort: 8080
      selector:
        app: jenkins
      sessionAffinity: None
      type: ClusterIP
    status:
      loadBalancer: {}
    
    5. jenkins ingress 配置yaml 文件 jenkins-ingress.yaml 
    apiVersion: extensions/v1beta1
    kind: Ingress
    metadata:
      annotations:
        kubernetes.io/ingress.class: nginx
      name: jenkins
      namespace: myjenkins
    spec:
      rules:
      - host: myjenkins.tagtic.cn
        http:
          paths:
          - backend:
              serviceName: jenkins
              servicePort: 8080
            path: /
      tls:
      - hosts:
        - myjenkins.tagtic.cn
        secretName: all-tagtic.cn
    status:
      loadBalancer: {}
    
    # 创建以上准备好的yaml 文件
    kubectl create -f jenkins-deployment.yaml  
    kubectl create -f jenkins-agent.yaml 
    kubectl create -f jenkins-svc.yaml
    kubectl create -f jenkins-ingress.yaml 
    
    #创建证书,已准备好服务器证书
    kubectl create secret tls tls-secret --cert=1979891tagtic.cn.pem    --key=1979891tagtic.cn.key  -n myjenkins
    
    #登陆jenkins 
    通过执行  kubectl logs -n myjenkins  jenkins-7f89966ff9-622xm  获取jenkins 登陆密码
    
    #访问jenkins,浏览器输入
    https://myjenkins.tagtic.cn/
    
    

    配置jenkins

    1.按照推荐安装插件
    FAQ
    部署jenkins服务器出现Please wait while Jenkins is getting ready to work ...一直进不去该怎么办?
    需要你进入jenkins的工作目录,打开-----hudson.model.UpdateCenter.xml将 url 中的 
    https://updates.jenkins.io/update-center.json
    更改为https://mirrors.tuna.tsinghua.edu.cn/jenkins/updates/update-center.json
    是国内的清华大学的镜像地址。
    
    2.安装完插件修改 admin 权限密码
    
    3.jenkins 安装插件
    Gitlab Gitlab API Kubernetes
    
    4.点击系统管理--系统配置
      4.1 修改用法、jenkins url 、系统管理员邮件地址(报警邮箱地址) 
       用法: 尽可能的使用这个节点
       Jenkins URL:https://myjenkins.tagtic.cn/
       系统管理员邮件地址: yunweimonitor@infinities.com
    
    

    
      4.2.全局属性,增加环境变量键值对,这个环境变量在后续的 jenkinsfile 中会用到
           键: URL_SUFFIX   值:xy.a9vg.com
    
    

    
      4.3 Gitlab  需要填写 Connection name、Gitlab host URL、Credentials,写完 Test Connections
        Connection name:  duoniu glab 
        Gitlab host URL:  https://glab.tag.cn
        Credentials:Gitlab API token,这个凭据的tocken 需要在gitlab 的个人账户中生成,下面有详细生成方式。
    
    

      4.4 添加Gitlab API token 凭据
       点击 系统管理-->安全-->Manage Credentials-->添加凭据-凭据类型为 GitLab API token、API token 是以下说明在 gitlab 中生成的、ID 为自定义名称 glab_token
       Gitlab API Token 获取方式:
       登陆个人gitlab 账号-->点击个人头像--> 选择 settings-->选择 Access Tokens --> 输入 Name 和 Expries at --> 勾选api Access your API、read_user Read user information、read_registry Read Registry --> 点击“Create personal access token”,生成access token,记录下来。
         
    

    
      4.5 新增加 Global Pipeline Libraries,会加载 groovy 脚本。
         Library Name:  mykubernetes-standard
         Default version: master
       Retrieval method 
         选择  Modern SCM
         Git 项目仓库:  https://glab.tag.cn/test/kubernetes-standard.git  
         凭据需要添加个人登录gitlab 的账号和密码,和上面添加的 Gitlab API Token 类型是不一样的。
     
    

      4.5.1 新增加个人登陆gitlab 的凭据和 后续使用harbor 的凭据,这两个凭据类型为 Username with password ,
            个人登录gitlab 需要填写登录gitlab 的用户名、密码、ID自定义为 glab_pass
            harbor 需要填写登录harbor 的用户名、密码、ID 自定义为 harbor
    FAQ:
    1. Warning: CredentialId "glab_pass" could not be found.
    id 名称为: glab_pass
    因为  kubernetes-stand 有用到这个名称,或者修改 helm.groovy
    

    FAQ:
    ERROR: Could not find credentials entry with ID 'harbor'
    添加harbor 的凭据
    

    5. 新增加 邮件通知,填写完成可以写测试邮件看是否能发送成功
    添加 SMTP服务器:smtp.exmail.qq.com
         SMTP服务器:@infinities.com
    

    6. 点击 cloud 下面的配置云
    
    https://myjenkins.tagtic.cn/configureClouds/
    
    配置前提是k8s 集群已配置宽泛的 rbac 策略
    kubectl create clusterrolebinding permissive-binding 
      --clusterrole=cluster-admin 
      --user=admin 
      --user=kubelet 
      --group=system:serviceaccounts
    
    1.第一步添加 kubernetes 名称:kubernetes
    2.第二步连接 Kubernetes 地址:https://kubernetes.default.svc.cluster.local
    3.第三步Kubernetes 命名空间: myjenkins (jenkins 的名称空间)
    4.第四步Jenkins 地址:http://jenkins:8080
    5.第五步Jenkins 通道:jenkins-agent.myjenkins:50000
    6.第六步填写 jenkins pod 的 label: 键:jenkins 值:slave
    7.第七步连接 Kubernetes API 的最大连接数 32
    
    
    

    
    gitlab 钩子触发 jenkins 报错 403
    
    FAQ:
    1. 配置 jenkins 安全策略
    系统管理-->安全-->全局安全配置-->授权策略-->勾选匿名用户具有可读权限
    
    2. 系统管理--> 系统配置  Gitlab  Enable authentication for '/project' end-point  取消前面勾选
    

    第一种情况 gitlab 代码结构下有 Jenkinsfile

    与gitlab 代码同级的包含有 Dockerfile 和 Jenkinsfile,这个项目包含有4个分支
    
    

    #Jenkinsfile 文件内容
    变量含义:
    bn  --> 获取当前分支名称
    bn_replace --> 将分支名称中的 . 更换为 -
    suffix --> 为 jenkins 配置的全局环境变量
    
    def bn = "${env.BRANCH_NAME}";
    def bn_replace = bn.replace(".", "-");
    def suffix = "${env.URL_SUFFIX}";
    stage('Deliver for development') {
        if (bn.startsWith('build-')){
            helm{
                scmUrl="https://glab.tag/test/laravel-k8s-test.git"
                project="test-helm2-${bn_replace}"
                email="test@donews.com"
                namespace="default"
                branch="${bn}"
                registry="harbor"
                helm="donews/myapp"
                helmArgs=""" --set service.port=80,ingress.hosts={myhelm-${bn_replace}.${suffix}} 
                """
            }
            
        }
    }
    stage('Deliver for testing') {
        if (bn == 'dev'){
            helm{
                scmUrl="https://glab.tag/test/laravel-k8s-test.git"
                project="test-helmdev"
                email="test@donews.com"
                namespace="default"
                branch="dev"
                registry="harbor"
                helm="donews/myapp"
                helmArgs=""" --set service.port=80,ingress.hosts={myhelmdev-xy.a99.com}""" 
            }
            
        }
    }
    stage('Deploy for production') {
        if (bn == 'master') {
            helm{
                scmUrl="https://glab.tag/test/laravel-k8s-test.git"
                project="test-helmmaster"
                email="test@donews.com"
                namespace="default"
                branch="master"
                registry="harbor"
                helm="donews/myapp"
                helmArgs=""" --set service.port=80,ingress.hosts={myhelmmaster-xy.a99.com}"""  
            }
            
        }
    }
    
    

    groovy 使用

    jekins 系统配置中  Global Pipeline Libraries git 项目https://glab.tag.cn/test/kubernetes-standard.git ,在项目下新建立 vars 目录,vars 目录下 helm.groovy 内容
    
    import hudson.model.Result
    import hudson.model.Run
    import jenkins.model.CauseOfInterruption.UserInterruption
    
    def abortPreviousBuilds() {
        Run previousBuild = currentBuild.rawBuild.getPreviousBuildInProgress()
    
        while (previousBuild != null) {
            if (previousBuild.isInProgress()) {
                def executor = previousBuild.getExecutor()
                if (executor != null) {
                    echo ">> Aborting older build #${previousBuild.number}"
                    executor.interrupt(Result.ABORTED, new UserInterruption(
                        "Aborted by newer build #${currentBuild.number}"
                    ))
                }
            }
    
            previousBuild = previousBuild.getPreviousBuildInProgress()
        }
    }
    
    def call(body) {
        // evaluate the body block, and collect configuration into the object
        abortPreviousBuilds()
        def pipelineParams= [:]
        def reg_prefix = ""
    
        def label = "worker-${pipelineParams.project}"
        body.resolveStrategy = Closure.DELEGATE_FIRST
        body.delegate = pipelineParams
        body()
        if (!pipelineParams.branch){
            pipelineParams.branch = "master"
        }
        if (!pipelineParams.deployment){
            pipelineParams.deployment = pipelineParams.project
        }
    
        pipelineParams.registry = "harbor"
        reg_prefix = "k8s-harbor01.gdfsxxds.rjyun/xy/"
        pullSecret = "harbor"
    
        if (!! pipelineParams.oversea){
            http_proxy = "--build-arg HTTP_PROXY=localhost:3001"
        } else {
            http_proxy = ""
        }
    
    
        podTemplate(label: label, containers: [
          containerTemplate(name: 'docker', image: 'docker:18', command: 'cat', ttyEnabled: true),
          containerTemplate(name: 'kubectl', image: 'lachlanevenson/k8s-kubectl:v1.16.0', command: 'cat', ttyEnabled: true),
          containerTemplate(name: 'helm', image: 'k8s-harbor01.gdfsxxds.rjyun/xy/helm:2.15.2', command: 'cat', ttyEnabled: true),
        ],
        volumes: [
          hostPathVolume(mountPath: '/var/run/docker.sock', hostPath: '/var/run/docker.sock')
        ]) {
          node(label) {
            def myRepo = checkout([$class: 'GitSCM', branches: [[name: "*/${pipelineParams.branch}"]], doGenerateSubmoduleConfigurations: false, extensions:  [[$class: 'CloneOption', noTags: false, reference: '', shallow: true, timeout: 1000]]+[[$class: 'CheckoutOption', timeout: 1000]], submoduleCfg: [], userRemoteConfigs: [[credentialsId: 'glab_pass', url: pipelineParams.scmUrl]]]) 
            def gitCommit = myRepo.GIT_COMMIT
            def gitBranch = myRepo.GIT_BRANCH
            def shortGitCommit = "${gitCommit[0..10]}"
            def project = pipelineParams.scm
            
            stage('Create docker images') {
                gitlabCommitStatus {
                    container('docker') {
                        try{
                            withCredentials([[$class: 'UsernamePasswordMultiBinding',
                              credentialsId: pipelineParams.registry,
                              usernameVariable: 'DOCKER_HUB_USER',
                              passwordVariable: 'DOCKER_HUB_PASSWORD']]) {
                                retry(3) {
                                  sh """
                                    docker login -u ${DOCKER_HUB_USER} -p ${DOCKER_HUB_PASSWORD} ${reg_prefix}
                                    docker build  -t ${reg_prefix}${pipelineParams.project}:${shortGitCommit} .
                                    docker push ${reg_prefix}${pipelineParams.project}:${shortGitCommit}
                                    docker tag ${reg_prefix}${pipelineParams.project}:${shortGitCommit} ${reg_prefix}${pipelineParams.project}:latest
                                    docker push ${reg_prefix}${pipelineParams.project}:latest
                                    """
                                }
                            }
                        }
                        catch(Exception e){
                            println(e.toString());
                            currentBuild.result = 'FAILURE'
                            step([$class: 'Mailer', notifyEveryUnstableBuild: true, recipients: pipelineParams.email, sendToIndividuals: true])
                            throw e
                        }
                    }       
                }
            }
    
            if (pipelineParams.helm) {
                stage('Run helm') {
                    if (!!pipelineParams.helmArgs){
                        args = pipelineParams.helmArgs
                    } else {
                        args = ""
                    }
                    container('helm') {
                        try {
                            sh """
                            helm init --client-only --stable-repo-url=https://kubernetes.oss-cn-hangzhou.aliyuncs.com/charts
                            helm repo add donews http://chart.a99.com/
                            helm repo update
                            helm upgrade -i ${pipelineParams.project} ${pipelineParams.helm} 
                            --set image.repository=${reg_prefix}${pipelineParams.project},image.tag=${shortGitCommit} 
                            ${args} 
                            --namespace ${pipelineParams.namespace}
                            """
                            } 
                        catch(Exception e) {
                            println(e.toString());
                            currentBuild.result = 'FAILURE'
                            step([$class: 'Mailer', notifyEveryUnstableBuild: true, recipients: pipelineParams.email, sendToIndividuals: true])
                            throw e
                        }
                    }
                }
    
            } else {
                stage('Run kubectl') {
                  container('kubectl') {
                    try {
                        sh """
                        kubectl set image deployment/${pipelineParams.deployment}-deployment ${pipelineParams.project}=${reg_prefix}${pipelineParams.project}:${shortGitCommit} -n ${pipelineParams.namespace}
                        kubectl rollout status deployment ${pipelineParams.deployment}-deployment -n ${pipelineParams.namespace}
                        """
                        } 
                    catch(Exception e) {
                        println(e.toString());
                        currentBuild.result = 'FAILURE'
                        step([$class: 'Mailer', notifyEveryUnstableBuild: true, recipients: pipelineParams.email, sendToIndividuals: true])
                        throw e
                    }
    
                  }
                }
            }
            currentBuild.result = 'SUCCESS'
            step([$class: 'Mailer', notifyEveryUnstableBuild: true, recipients: pipelineParams.email, sendToIndividuals: true])
          }
        }
    }
    
    

    helm 自建chart 仓库,之前文章已介绍

    前面文章:  https://www.cnblogs.com/lixinliang/p/14304469.html
    

    jenkins 部署多分支流水线项目

    新建任务--> 起一个任务名称 duofenzhi  -->选择多分支流水线
    
    选择配置 -->分支源-->Git
    
    
    


    等 1分钟,jenkins 将会自动拉取gitlab 代码进行编译构建

    gitlab 添加自动触发,必须是 project :

    在gitlab -->项目下-->settings-->Integrations-->增加 Webhooks
    https://myjenkins.tagtic.cn/project/duofenzhi

    查看 jenkins 已构建完成

    查看k8s,容器已正常启动

    项目部署完成。

    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

    第二种情况 gitlab 代码结构下没有 Jenkinsfile

    #结构
    gitlab 代码下只有Dockerfile ,没有Jenkinsfile,不能使用Jenkins 多分支构建,只能使用流水线
    
    #新建立流水线
    新建任务-->新建名称test -->流水线
    GitLab Connection 选择 duoniu glab
    

    选择构建触发器,这是和gitlab 打通的渠道
    
    

    # 流水线
    新增加 Pipeline script
    
    helm{
        scmUrl="https://glab.tag/test/mall_h5.git"
        project="test"
        email="test@do.com"
        namespace="default"
        branch="dev"
        helm="donews/myapp"
        helmArgs=""" --set service.port=80,ingress.hosts={mytest-xy.aaa.com}"""  
    }
    
    注:
    这个helm 定义将会自动加载上文的 helm.groovy 脚本,使用helm 部署容器,这和 jenkinsfile 是不同的,因为Jenkinsfile 不用配置 jenkins 的流水线脚本
    
    

    查看 jenkins 已构建完成

    查看k8s,容器已正常启动

    项目部署完成。

    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

    第三种情况 gitlab 代码结构下也没有 Jenkinsfile,但是已经有 deployment svc ingress 这些配置

    #新建流水线
    新增加 Pipeline script
    
    def label = "worker-${UUID.randomUUID().toString()}"
    
    podTemplate(label: label, containers: [
      containerTemplate(name: 'docker', image: 'docker', command: 'cat', ttyEnabled: true),
      containerTemplate(name: 'kubectl', image: 'lachlanevenson/k8s-kubectl:v1.16.0', command: 'cat', ttyEnabled: true),
    ],
    volumes: [
      hostPathVolume(mountPath: '/var/run/docker.sock', hostPath: '/var/run/docker.sock')
    ]) {
      node(label) {
        def myRepo = checkout([$class: 'GitSCM', branches: [[name: '*/master']], doGenerateSubmoduleConfigurations: false, extensions:  [[$class: 'CloneOption', noTags: false, reference: '', shallow: true, timeout: 12000]]+[[$class: 'CheckoutOption', timeout: 7000]], submoduleCfg: [], userRemoteConfigs: [[credentialsId: 'glab_pass', url: 'https://glaxxx.cn/test/tgbusmall_api.git']]]) 
        def gitCommit = myRepo.GIT_COMMIT
        def gitBranch = myRepo.GIT_BRANCH
        def shortGitCommit = "${gitCommit[0..10]}"
        
        stage('Create docker images') {
            gitlabCommitStatus {
                container('docker') {
                    withCredentials([[$class: 'UsernamePasswordMultiBinding',
                      credentialsId: 'dockerreg',
                      usernameVariable: 'DOCKER_HUB_USER',
                      passwordVariable: 'DOCKER_HUB_PASSWORD']]) {
                      sh """
    				    cp dockerfile/dockerfile-release/Dockerfile .
                        docker login -u ${DOCKER_HUB_USER} -p ${DOCKER_HUB_PASSWORD} k8s-harbor01.gdfsxxds.rjyun
    					docker build  -t k8s-harbor01.gdfsxxds.rjyun/xy/tgbusmall-api:${shortGitCommit} . 
                        docker push k8s-harbor01.gdfsxxds.rjyun/xy/tgbusmall-api:${shortGitCommit}
                        """
                    }
                }       
            }
          
        }
        stage('Run kubectl') {
          container('kubectl') {
            sh """
            kubectl set image deployment/tgbusmall-api-deployment tgbusmall-api=k8s-harbor01.gdfsxxds.rjyun/xy/tgbusmall-api:${shortGitCommit} -n default
            kubectl rollout status deployment tgbusmall-api-deployment -n default
            """
          }
        }
      }
    }
    

    查看 jenkins 已构建完成

    查看k8s,容器已正常更新

    项目部署完成。

    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

    项目总结

    第一种方式jenkins 多分支流水线配置简单,只用在gitlab 代码下定义好 Jenkinsfile, 适合多分支代码测试,便捷开发和测试人员,通过 groovy 可以自动化部署。
    第二种方式 jenkins 流水线配置不用定义Jenkinsfile ,只用配置好 pipeline 内容即可,适合分支少项目,通过 groovy 也可以自动化部署。
    第三种方式前提是已经有部署好的 deployment、svc 和ingress ,只需要每次进行镜像替换即可,不推荐使用,因为每部署一个新的项目必须先手动准备好这些必配文件,不使用 groovy 自动部署。
    
  • 相关阅读:
    centos7安装ImageMagick
    php安装imagemagick扩展
    php编译安装redis扩展
    springboot和springsecurity使用JWT令牌
    springboot和springsecurity整合OAuth2
    SpringSecurity 整合SpringBoot结合jwt与rsa实现分布式认证授权
    【名额有限】云开发AI拓展能力等你来体验!
    干货:如何借助小程序云开发实现小程序支付功能(含源码)
    云开发数据库又增新技能!
    聚焦“云开发圆桌论坛”,大前端Serverless大佬们释放了这些讯号!
  • 原文地址:https://www.cnblogs.com/lixinliang/p/14324616.html
Copyright © 2011-2022 走看看