Jenkins 中自动打包推送发布K8s应用

Jenkins 中自动打包推送发布K8s应用

好久没写新内容,来个不务正业的小分享吧。接下来打算写一个关于归因分析的专题,正好找找状态。

安装Jenkins分享已经很多,这里很多前置内容我们不再赘述,直接进入与发布至K8s有关的部分,一些知识补充我们不在文中展开,但是提供查询地址,咸淡自取。

文中最后样例用了阿里云,不过实际与什么云关系不大,这些是K8s的机制,在哪里都基本一样。

插件安装

要联通K8s,我们需要安装一些基础的插件,一些分享中可能会有一些更多的插件安装,不过在这里我们主要使用文中的几个即可。

  1. Kubernetes
  2. Kubernetes CLI
  3. Kubernetes Client API
  4. Kubernetes Credentials

在K8s创建serviceaccount

创建sa路径比较多,有的控制台提供了创建了界面,这里我们介绍2种,一种使用命令行直接创建,最简单,一种是编写一个yaml文件提交创建。

注意:这里有前提需要有机器可以通过kubectl连接上我们的目标K8s集群,如果机器缺乏相应环境及config文件,可以参考K8s官网的手册,一步一步下来就OK了。不过下载安装文件可能需要魔法。

方式一:使用命令行创建用户

kubectl create serviceaccount uat-publisher
//uat-publisher 请替换成你自己的账号,这里用于做示例而已

更多创建参数可参考官方资料:

https://kubernetes.io/docs/reference/kubectl/generated/kubectl_create/kubectl_create_serviceaccount/

方式二:创建一个本地文件并提交

vim uat-publisher.yaml

创建一个文件,并输入以下内容

apiVersion: v1
kind: ServiceAccount
metadata:
  namespace: uat
  name: uat-publisher

保存文件,并用以下命令提交至K8s。(这是一个最简文件,官方依然有提供其它非常丰富的配置项,不过我们这里不需要)。

kubectl apply -f uat-publisher.yaml

这样我们就创建好账号了。

创建ROLE

role也是同样,可以在管理平台创建或者编写一个yaml文件,然后apply。

vim role-uat-publisher.yaml
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  namespace: uat
  name: uat-publisher-role

rules:
- apiGroups: [""]
  #resources: ["pods","pods/log","pods/exec", "pods/attach", "pods/status", "events", "replicationcontrollers", "services", "configmaps", "persistentvolumeclaims"]
  resources: ["namespaces","pods","pods/log","pods/exec", "pods/attach", "pods/status","services"]
  verbs: ["get", "watch", "list", "create", "update", "patch", "delete"]

- apiGroups: [ "apps"]
  resources: ["deployments", "daemonsets", "statefulsets","replicasets"]
  verbs: ["get", "list", "watch", "exec"]

# 根据自己的实际需求编写rules
kubectl apply -f role-uat-publisher.yaml

关于ROLE中rules的一些细节可参考官网资料:

https://kubernetes.io/docs/reference/kubernetes-api/authorization-resources/role-v1/

将ROLE与SA绑定

这里绑定依然有两种方式,直接命令行或apply文件。

方式一:直接命令行绑定

kubectl create rolebinding admin-binding --role=uat-publisher --serviceaccount=uat:uat-publisher

官方同样提供了其它参数可选,我们可以参考:

https://kubernetes.io/docs/reference/kubectl/generated/kubectl_create/kubectl_create_rolebinding/

方式二:编写yaml文件提交

vim bind-role-uat-publisher.yaml
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  namespace: uat
  name: uat-publisher-role

subjects:
- kind: ServiceAccount
  namespace: uat
  name: uat-publisher

roleRef:
  kind: Role
  name: uat-publisher-role
  apiGroup: rbac.authorization.k8s.io

接下来我们提交绑定即可

kubectl apply -f bind-role-uat-publisher.yaml

为账号生成TOKEN

这里我们就比较简单的使用命令行来生成TOKEN了

kubectl create token uat-publisher --namespace uat

复制返回的这一串长长的字符串即可。

在Jenkins中配置密钥信息

  1. 找到Manage Credentials。
    在这里插入图片描述

  2. 创建一个新的 secret 类型的,secret的字符串粘贴入刚才获取到的TOKEN
    在这里插入图片描述

  3. 去一个ID和名字,要记住ID,下一步我们就要用到了。

在构件中添加发布流程

这里为了节约时间,我就贴一段相对完整的文件出来。
最后的效果会是这样:
在这里插入图片描述

def checkOut(project){
    checkout([
            $class: 'GitSCM',
            branches: [[name: '${branchOrTag}']],
            userRemoteConfigs: [[
                                        credentialsId: 'YOUR-CREDENTIAL',
                                        url: project.gitUrl
                                ]]
    ])
    if (newTag) {
        sh "git tag -a -f -m \"${newTag}\" ${newTag} && git push -f origin ${newTag}"
        branchOrTag = newTag
    }
}
def codeAnalysis() {
    script {
        scannerHome = tool 'sonarScanner'
    }
    withSonarQubeEnv('sonar') {
        sh "cd PROJ-PATH && ${scannerHome}/bin/sonar-scanner " +
                "-Dsonar.projectKey=uat.PROJ-NAME " +
                "-Dsonar.projectName=uat.PROJ-NAME " +
                "-Dsonar.projectVersion=1.0 " +
                "-Dsonar.sources=src/ " +
                "-Dsonar.language=javascript"
    }
    sleep(5)
    timeout(time: 5, unit: 'MINUTES') {
        script {
            def qg = waitForQualityGate()
            if (qg.status != 'OK') {
                error "Pipeline aborted due to a quality gate failure: ${qg.status}"
            }
        }
    }
}
def nodeBuild(project) {
    docker.withRegistry('https://registry-vpc.cn-shanghai.aliyuncs.com', 'aliyun-docker-registry'){
        docker.image('registry-vpc.cn-shanghai.aliyuncs.com/YOUR-NAME/pnpm-circleci-node:16.13.1-pnpm7.5.1').inside('-v /data2/node_modules:/home/jenkins/workspace/UAT/YOUR-NAME/projects/YOUR-NAME/node_modules'){
            withEnv(['HOME=.']){
                sh "cd projects/YOUR-NAME/ && pwd && pnpm install && pnpm run uat "
            }
        }
    }
}
def dockerBuild(project) {
    configFileProvider([
            configFile(fileId: 'nginx-docker-file', variable: 'DOCKER_FILE')
    ]) {
        sh "cp -f ${DOCKER_FILE} projects/YOUR-NAME/dist/Dockerfile"
    }
    def imageTag = branchOrTag.toLowerCase()
    if (imageTag.indexOf('/') >= 0){
        imageTag = imageTag.substring(imageTag.indexOf('/')+1)
    }
    docker.withRegistry('https://registry-vpc.cn-shanghai.aliyuncs.com', 'aliyun-docker-registry'){
        docker
                .build("registry-vpc.cn-shanghai.aliyuncs.com/YOUR-NAME/${project.image}:${imageTag}", "${project.distPath}")
                .push()
    }
}
def deploy(project) {
    if (UAT == 'true') {
        def imageTag = branchOrTag.toLowerCase()
        if (imageTag.indexOf('/') >= 0){
            imageTag = imageTag.substring(imageTag.indexOf('/')+1)
        }
        withKubeConfig(credentialsId: '刚才的credentialsID', serverUrl: 'YOUR-K8S-SERVER') {
            sh "kubectl set image deployment/${project.image}  YOUR-NAME=registry.cn-shanghai.aliyuncs.com/YOUR-NAME/${project.image}:${imageTag} -n uat"
        }
    }
}
def success() {
    dingtalk(
            robot: 'jinkens-dingtalk',
            type: 'MARKDOWN',
            title: "成功: ${BUILD_TAG}",
            text: ["### 项目信息",
                   '---',
                   "- 项目主体: ${JOB_BASE_NAME}",
                   "",
                   '---',
                   "### 构建信息",
                   '---',
                   "- 构建编号: <font color=#008000>${BUILD_ID}</font>",
                   "- 构建分支: ${branchOrTag}",
                   "- 构建状态: **<font color=#008000>${currentBuild.result}</font>**",
                   "- 项目地址: ${BUILD_URL}",
                   "- 构建日志: ${BUILD_URL}console",
                   "",
                   '---',
                   "### 是否发布",
                   '---',
                   "- 是否自动发布: ${UAT}",
                   "",
                   '---',
                   "### 执行人",
                   '---',
                   "- ${USER}"],
            at: ["${NOTICE}"]
    )
}
def failure() {
    // Dingtalk message
    dingtalk(
            robot: 'jinkens-dingtalk',
            type: 'MARKDOWN',
            title: "失败: ${BUILD_TAG}",
            text: ["### 项目信息",
                   '---',
                   "- 项目主体: ${JOB_BASE_NAME}",
                   "",
                   '---',
                   "### 构建信息",
                   '---',
                   "- 构建编号: <font color=#dd3238>${BUILD_ID}</font>",
                   "- 构建分支: ${branchOrTag}",
                   "- 构建状态: **<font color=#dd3238>${currentBuild.result}</font>**",
                   "- 项目地址: ${BUILD_URL}",
                   "- 构建日志: ${BUILD_URL}console",
                   "",
                   '---',
                   "### 是否发布",
                   '---',
                   "- 是否自动发布: ${UAT}",
                   "",
                   '---',
                   "### 执行人",
                   '---',
                   "- ${USER}"],
            at: ["${NOTICE}"]
    )
}

def thisProject = [
        name: 'PROJ-NAME',
        gitUrl: 'ssh://git@CODE.git',
        image: 'IMAGE-NAME',
        distPath: 'PROJ-PATH'
]
pipeline {
    agent any
    stages {
        stage('Checkout') {
            steps {
                script {
                    checkOut(thisProject)
                }
            }
        }
        stage('Code Scan') {
            steps {
                script {
                    //echo 'skiped'
                    codeAnalysis()
                }
            }
        }
        stage('Node build') {
            steps {
                script {
                    nodeBuild(thisProject)
                }
            }
        }
        stage('Docker build') {
            steps {
                script {
                    dockerBuild(thisProject)
                }
            }

        }
        stage('Depoly') {
            steps {
                script {
                    deploy(thisProject)
                }
            }
        }
    }
    post {
        success {
            echo "success"
            script {
                success()
                //echo "success"
            }
        }
        failure {
            echo "failure"
            script {
                failure()
            }
        }
    }
}


上一篇:Apache HTTP Server 配置SSL证书(Windows)


下一篇:Nginx跳转模块之location与rewrite