Jenkinsfile说明
当我们在使用jenkins进行CI/CD的时候,简单的内容我们可以通过jenkins页面来实现配置。但是如果有复杂的需求还是需要通过jenkinsfile来实现
jenkinsfile简单介绍
Jenkinsfile使用两种语法进行编写,分别是声明式和脚本式。下面我们会用声明式的写法来提供一个jenkinsfile,并进行说明
Jenkinsfile模板
pipeline {
agent {
kubernetes {
yaml '''
apiVersion: v1
kind: Pod
metadata:
labels:
jenkinsci: jenkins-golang
spec:
containers:
- name: golang
image: golang:1.18.3-bullseye
tty: true
- name: kaniko
image: registry.cn-hangzhou.aliyuncs.com/cefso/kaniko-project.executor:debug
command: ["/busybox/cat"]
tty: true
- name: kubelet
image: registry.cn-hangzhou.aliyuncs.com/cefso/kubectl:v1.23.1
command: ["cat"]
tty: true
'''
}
}
environment {
PROJECT_NAME = "${JOB_NAME.split('/')[0]}"
VSERSION = "${BRANCH_NAME.replace('_', '-')}-${BUILD_ID}"
}
stages {
stage('git clone') {
steps {
checkout scm
}
}
stage('format & test') {
steps {
container('golang') {
sh 'go fmt $(go list ./... | grep -v /vendor/)'
sh 'go vet $(go list ./... | grep -v /vendor/)'
sh 'go test -race $(go list ./... | grep -v /vendor/)'
}
}
}
stage('build') {
steps {
container('golang') {
sh 'mkdir -p mybinaries'
sh 'go build -o mybinaries/mybin-${VSERSION} ./...'
}
}
}
stage('archiveArtifacts') {
steps {
archiveArtifacts artifacts: 'mybinaries/*', followSymlinks: false
}
}
stage('build images') {
steps {
container('kaniko') {
sh 'mkdir -p /kaniko/.docker'
sh 'export IFS=\'\''
withCredentials([usernamePassword(credentialsId: 'aliyunRegistry', usernameVariable: 'CI_REGISTRY_USER', passwordVariable: 'CI_REGISTRY_PASSWORD')]) {
sh '''
echo 'diable bash debug'
set +x
echo "{\\"auths\\":{\\"registry.cn-hangzhou.aliyuncs.com\\":{\\"auth\\":\\"$(printf "%s:%s" "${CI_REGISTRY_USER}" "${CI_REGISTRY_PASSWORD}" | base64 | tr -d \'\\n\')\\"}}}" > /kaniko/.docker/config.json
set -x
echo 'enable bash debug'
'''
}
sh '''
/kaniko/executor \
--context "${WORKSPACE}" \
--build-arg "version=${VSERSION}" \
--dockerfile "${WORKSPACE}/Dockerfile" \
--destination "registry.cn-hangzhou.aliyuncs.com/cefso/${PROJECT_NAME}:${VSERSION}" \
--force
'''
}
}
}
stage('deploy') {
steps {
container('kubelet') {
withKubeConfig(credentialsId: 'jenkins-robot-secret', serverUrl: 'https://172.16.0.30:6443') {
sh 'kubectl get pods -A'
}
}
}
}
}
}jenkinsfile详解
agent部分和pod模板
jenkinsfile内容
kubernetes {
yaml '''
apiVersion: v1
kind: Pod
metadata:
labels:
jenkinsci: jenkins-golang
spec:
containers:
- name: golang
image: golang:1.18.3-bullseye
tty: true
- name: kaniko
image: registry.cn-hangzhou.aliyuncs.com/cefso/kaniko-project.executor:debug
command: ["/busybox/cat"]
tty: true
- name: kubelet
image: registry.cn-hangzhou.aliyuncs.com/cefso/kubectl:v1.23.1
command: ["cat"]
tty: true
'''
}
}我们这里使用了jenkins的kubernetes插件,并且jenkins是部署在k8s集群中的,所以无需额外配置即可将我们的构建放入到kubernetes集群中以pod的形式运行。如果jenkins没有安装到k8s集群中则需要手动配置才能以pod的形式进行构建。
kubernetes插件查考文档
https://plugins.jenkins.io/kubernetes/
配置的位置如下
系统管理-节点管理

configure clouds

在这里配置连接到k8s集群即可

这里我们通过yaml文件的形式定义了我们构建时使用的pod信息,pod包含3个容器
apiVersion: v1 kind: Pod metadata: labels: jenkinsci: jenkins-golang spec: containers: # 容器1,名称golang,镜像为golang:1.18.3-bullseye - name: golang image: golang:1.18.3-bullseye tty: true # 容器1,名称kaniko,镜像为registry.cn-hangzhou.aliyuncs.com/cefso/kaniko-project.executor:debug - name: kaniko image: registry.cn-hangzhou.aliyuncs.com/cefso/kaniko-project.executor:debug command: ["/busybox/cat"] tty: true # 容器1,名称kubelet,镜像为registry.cn-hangzhou.aliyuncs.com/cefso/kubectl:v1.23.1 - name: kubelet image: registry.cn-hangzhou.aliyuncs.com/cefso/kubectl:v1.23.1 command: ["cat"] tty: true
其中
golang的容器是用来进行代码的构建等操作的,如果后续需要构建java应用就可以调整为maven的镜像来使用
kaniko容器用来构建我们的docker镜像(这部分可以通用)
kubelet容器是用来做应用的部署的(这部分也可以通用)
这里需要注意我们的每个容器都有不同的名字,后续我们可以通过容器的名字来指定我们当前的pipeline步骤运行在那个容器中
环境变量配置
我们将后续常用的一些环境变量进行配置,方便后续的使用
environment {
PROJECT_NAME = "${JOB_NAME.split('/')[0]}"
VSERSION = "${BRANCH_NAME.replace('_', '-')}-${BUILD_ID}"
}克隆代码
这里我们使用了jenkinsfile的一个语法,这里的checkout scm是在多分支流水线中最常用的一个语法。jenkins会根据我们的现在正在构建的分支来克隆相关代码。
stage('git clone') {
steps {
// 克隆当前分支代码(即我们构建的是那个分支就克隆那个分支的代码)
checkout scm
}
}格式化代码和测试
这里是golang代码常用的一个步骤,对代码进行格式化和测试
stage('format & test') {
steps {
container('golang') {
sh 'go fmt $(go list ./... | grep -v /vendor/)'
sh 'go vet $(go list ./... | grep -v /vendor/)'
sh 'go test -race $(go list ./... | grep -v /vendor/)'
}
}
}这里我们需要注意一段代码
container('golang') {
// ***
}还记的我们上面说我们可以使用指定的容器来进行pipeline的操作,这里我们就通过container的语法来指定了当前操作的步骤是在golang的容器中运行
构建
这部分没什么好说的,正常的构建代码
stage('build') {
steps {
container('golang') {
sh 'mkdir -p mybinaries'
sh 'go build -o mybinaries/mybin-${VSERSION} ./...'
}
}
}产物
产物是经常被忽略的部分。但是通过产物功能,我们可以很方便的在页面上下载好构建的成品,无论是进行测试,还是部署问题排查都会很方便。尤其是当我们在pod中进行构建,构建完成后pod就会被销毁,此时产物就显得更加重要

stage('archiveArtifacts') {
steps {
archiveArtifacts artifacts: 'mybinaries/*', followSymlinks: false
}
}镜像构建和推送
stage('build images') {
steps {
container('kaniko') {
sh 'mkdir -p /kaniko/.docker'
sh 'export IFS=\'\''
withCredentials([usernamePassword(credentialsId: 'aliyunRegistry', usernameVariable: 'CI_REGISTRY_USER', passwordVariable: 'CI_REGISTRY_PASSWORD')]) {
sh '''
echo 'diable bash debug'
set +x
echo "{\\"auths\\":{\\"registry.cn-hangzhou.aliyuncs.com\\":{\\"auth\\":\\"$(printf "%s:%s" "${CI_REGISTRY_USER}" "${CI_REGISTRY_PASSWORD}" | base64 | tr -d \'\\n\')\\"}}}" > /kaniko/.docker/config.json
set -x
echo 'enable bash debug'
'''
}
sh '''
/kaniko/executor \
--context "${WORKSPACE}" \
--build-arg "version=${VSERSION}" \
--dockerfile "${WORKSPACE}/Dockerfile" \
--destination "registry.cn-hangzhou.aliyuncs.com/cefso/${PROJECT_NAME}:${VSERSION}" \
--force
'''
}
}
}Kaniko
https://github.com/GoogleContainerTools/kaniko
kaniko是谷歌开源的一款用来构建容器镜像的工具。与docker不同,Kaniko 并不依赖于Docker daemon进程,完全是在用户空间根据Dockerfile的内容逐行执行命令来构建镜像,这就使得在一些无法获取 docker daemon 进程的环境下也能够构建镜像,比如在标准的Kubernetes Cluster上。
Kaniko 以容器镜像的方式来运行的,同时需要三个参数: Dockerfile,上下文,以及远端镜像仓库的地址。
Kaniko会先提取基础镜像(Dockerfile FROM 之后的镜像)的文件系统,然后根据Dockerfile中所描述的,一条条执行命令,每一条命令执行完以后会在用户空间下面创建一个snapshot,并与存储与内存中的上一个状态进行比对,如果有变化,就将新的修改生成一个镜像层添加在基础镜像上,并且将相关的修改信息写入镜像元数据中。等所有命令执行完,kaniko会将最终镜像推送到指定的远端镜像仓库。
需要说明几点:
args 部分
这部分就是上面所讲的,kaniko运行时需要三个参数: Dockerfile(--dockerfile),上下文(--context),远端镜像仓库(--destination)
secret 部分
推送至指定远端镜像仓库需要credential的支持,所以需要将credential以secret的方式挂载到/kaniko/.docker/这个目录下,文件名称为config.json,内容如下:
{
"auths": {
"https://index.docker.io/v1/": {
"auth": "AbcdEdfgEdggds="
}
}
}其中auth的值为: echo"docker_registry_username:docker_registry_password"|base64
部署
这里我们又多了一个语法withKubeConfig,通过这个语法我们可以实现在容器中使用kubectl命令时的鉴权。该语法由Kubernetes CLI插件提供
stage('deploy') {
steps {
container('kubelet') {
withKubeConfig(credentialsId: 'jenkins-robot-secret', serverUrl: 'https://172.16.0.30:6443') {
sh 'kubectl set image deployment/${PROJECT_NAME} ${PROJECT_NAME}=registry.cn-hangzhou.aliyuncs.com/cefso/${PROJECT_NAME}:${VSERSION}'
}
}
}
}Kubernetes CLI 参考链接
https://plugins.jenkins.io/kubernetes-cli/
创建jenkins访问集群凭证
创建serviceaccount
kubectl -n default create serviceaccount jenkins-robot
创建clusterrolebinding,我们将权限绑定到cluster-admin上
kubectl -n default create clusterrolebinding jenkins-robot-binding --clusterrole=cluster-admin --serviceaccount=default:jenkins-robot
1.22以上版本集群并不会自动为serviceaccount创建token,所以我们需要手动创建token
kubectl apply -f - <<EOF apiVersion: v1 kind: Secret metadata: name: jenkins-robot-secret annotations: kubernetes.io/service-account.name: jenkins-robot type: kubernetes.io/service-account-token EOF
获取我们创建的token
kubectl -n default get secrets jenkins-robot-secret -o go-template --template '{{index .data "token"}}' | base64 -d将token配置到jenkins上

类型选择Secret text,将我们上面获取的token填写到Secret中,后续通过ID调用即可

// 这里credentialsId填写我们上面的ID,serverUrl填写集群api server地址即可
withKubeConfig(credentialsId: 'jenkins-robot-secret', serverUrl: 'https://172.16.0.30:6443') {
// ***
}





