Skip to main content

Jenkins部署项目和推送镜像和部署到k8s

Jenkins配置

  1. 创建项目, 让Jenkins知道你要通过哪个仓库触发CD动作 选择源码管理, 填写URL和对应的仓库认证方式
  2. 触发构建, 让Jenkins知道你通过什么动作触发 选择构建触发器, 这里选择使用GitLab webhook URL
  3. 触发之后添加一些额外选项, 这里选择Generic Webhook Trigger(通用Webhook触发器) 选择Generic Webhook TriggerPost content parameters, 添加变量,把隐私参数写到这里 本文所需的参数和值, 请根据你的项目实际需要进行修改: 示例: ![[Pasted image 20230910215700.png]]
  4. ref: $.ref
  5. gitUrl: $project.git_http_url
  6. projectID: $.project_id gitlab的项目ID
  7. k8sApiServce: https://127.0.0.1:6443/apis/apps/v1
  8. GitlabApiServer: gitlab的api地址, 默认是url/api/v4 文档 选择Generic Webhook TriggerToken, 这里的输入gitlab的一个Token, 建议与gitlab的项目仓库同名,复制http://JENKINS_URL/generic-webhook-trigger/invoke到Gitlab中 ![[Pasted image 20230910215739.png]] 进入到Gitlab, 找到项目设置-> 访问令牌

![[Pasted image 20230910220239.png]] 复制生成的Gitlab访问令牌的值, 添加到Jenkins凭据中 ![[Pasted image 20230910220606.png]]

  1. 获取token
kubectl get secret -n <namespace>

例子:

kubectl get secret jenkins -n jenkins -o jsonpath={".data.token"} | base64 -d

创建namespace, ServiceAccount ,Secret, ClusterRoleBinding 干了这事

---
apiVersion: v1
kind: Namespace
metadata:
name: jenkins
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: jenkins-service-account
namespace: jenkins
---
apiVersion: v1
kind: Secret
metadata:
name: jenkins-secret
namespace: jenkins
annotations:
kubernetes.io/service-account.name: jenkins-service-account
type: kubernetes.io/service-account-token
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: jenkins-cluster-admin-rbac
subjects:
- kind: ServiceAccount
# Reference to upper's `metadata.name`
name: jenkins-service-account
# Reference to upper's `metadata.namespace`
namespace: jenkins
roleRef:
kind: ClusterRole
name: cluster-admin
apiGroup: rbac.authorization.k8s.io

gitlab

glpat-L2Paidzu7Q8sd_szmqzP

Jenkinsfile.groovy


import java.util.Base64

// 封装http请求
def k8sHttpReq(reqType, reqUrl, reqBody) {
def result
// k8s的api可以参考https://kubernetes.io/zh-cn/docs/reference/using-api/api-concepts/
withCredentials([string(credentialsId: 'kubernetes-token', variable: 'k8stoken')]) {
result = httpRequest consoleLogResponseBody: true,
customHeaders: [
[maskValue: true, name: 'Authorization', value: "Bearer ${k8stoken}"],
[maskValue: false, name: 'Content-Type', value: 'application/yaml'],
[maskValue: false, name: 'Accept', value: 'application/yaml']
],
httpMode: reqType,
ignoreSslErrors: true,
requestBody: reqBody,
url: "${k8sApiServer}/${reqUrl}",
wrapAsMultipart: false
}

return result
}

// 获取某一个deployment
def getDeployment(nameSpace, deployName) {
apiUrl = "namespaces/${nameSpace}/deployments/${deployName}"
response = k8sHttpReq('GET', apiUrl, '')
return response
}

// 更新deployment
def updateDeployment(nameSpace, deployName, deployBody) {
apiUrl = "namespaces/${nameSpace}/deployments/${deployName}"

response = k8sHttpReq('PATCH', apiUrl, deployBody)
println(response)
}

// 获取所有的deployments
def getAllDeployment(nameSpace) {
apiUrl = "namespaces/${nameSpace}/deployments"
response = k8sHttpReq('GET', apiUrl, '')
return response.content
}

// 创建一个deployment
def createDeployment(nameSpace, deployBody) {
apiUrl = "namespaces/${nameSpace}/deployments"
response = k8sHttpReq('POST', apiUrl, deployBody)
return response.content
}

// 删除一个deployment
def deleteDeployment(nameSpace, deployName) {
apiUrl = "namespaces/${nameSpace}/deployments/${deployName}"
response = k8sHttpReq('DELETE', apiUrl, '')
return response.content
}

// 封装http请求
def httpReq(reqType, reqUrl, reqBody) {
result = httpRequest consoleLogResponseBody: true,
contentType: 'APPLICATION_JSON',
customHeaders: [[maskValue: true, name: 'PRIVATE-TOKEN', value: "${gitlabToken}"]],
httpMode: reqType,
ignoreSslErrors: true,
requestBody: reqBody,
url: "${gitlabApiServer}/${reqUrl}",
wrapAsMultipart: false
return result
}

// 获取文件内容
def getRepoFile(projectId, filePath) {
apiUrl = "projects/${projectId}/repository/files/${filePath}/raw"

response = httpReq('GET', apiUrl, '')
return response.content
}

// 更新文件内容
// def updateRepoFile(projectId, filePath, fileContent) {
// apiUrl = "projects/${projectId}/repository/files/${filePath}"
// reqBody = """
// {"branch": "main", "encoding": "base64", "content": "${fileContent}", "commit_message": "Update a new file"}
// """
// response = httpReq('PUT', apiUrl, reqBody)
// println(response.content)
// }

pipeline {
agent any

stages {
// 拉取gitlab代码
stage('Pull Repository') {
steps {
script {
branchName = ref - 'refs/heads/' // 获取分支名
// 拉取分支的语法可以用jenkins自动生成,然后修改下就可以
checkout scmGit(branches: [[name: branchName]], extensions: [], userRemoteConfigs: [[credentialsId: 'gitlab-auth', url: gitUrl]])
}
}
}

// 上传镜像
stage("Upload Image") {
steps {
script {
// credentialsId: Jenkins的Harbor的token的ID
// usernameVariable: Jenkins的Harbor的用户名的环境变量, 通过流水线语法生成脚本
// passwordVariable: Jenkins的Harbor的密码的环境变量, 通过流水线语法生成脚本
withCredentials([usernamePassword(credentialsId: 'Harbor', passwordVariable: 'password', usernameVariable: 'username')]) {
sh """
# 登陆Harbor
# -u Harbor用户名
# -p Harbor密码
docker login -u ${username} -p ${password} ${harborUrl}
# 构建镜像,docker会使用go项目中写好的docerfile文件构建镜像
# --progress=plain 文本方式显示构建过程
docker build --progress=plain --no-cache -t ${harborImageName}:${branchName} .
# 打Tag
# docker tag test2:${branchName} www.shandong.shop/test1/test2:${branchName}

docker tag ${harborImageName}:${branchName} ${harborUrl}/${harborImageDir}/${harborImageName}:${branchName}
sleep 1
# 上传镜像
# 推送到Harbor
docker push ${harborUrl}/${harborImageDir}/${harborImageName}:${branchName}
sleep 1
# 删除构建的二进制文件
# docker rmi ${harborImageName}:${branchName}
"""
}
}
}
}

// 部署到容器编排
stage('Deploy container orchestration') {
steps {
script {
// dockerImage: Harbor的镜像地址
dockerImage = "${harborUrl}/${harborImageDir}/${harborImageName}:${branchName}"
// 下载版本库文件
// 使用前面创建的k8s-info-service项目,查看gitlab该项目的id为4,所以第一个参数传4
response = getRepoFile(projectID, "${deploy_file}")

// 替换文件的内容(替换镜像)
fileData = readYaml text: """${response}"""
fileData["spec"]["template"]["spec"]["containers"][0]["image"] = dockerImage

// 将yaml转换为字符串
String ret = writeYaml returnText: true, data: fileData

// 将字符串进行base64编码
String base64encodedString = Base64.getEncoder().encodeToString(ret.getBytes("utf-8"));
println(base64encodedString)

// TODO 更新gitlab中的内容
// updateRepoFile(projectID, 'build%2fuat%2fdeploy.yaml', base64encodedString)

//获取部署的deployment状态
deployment = getAllDeployment("demoapp")

// 如果demo-go-app不存在,部署
yamlData = readYaml text: deployment

// 定义一个包含所有的deployemt的数组
deploymentList = []

// 遍历,将demoapp名称空间下的deployment都取出来
for (item in yamlData.items) {
deploymentList.add(item.metadata.name)
}

// 如果已经包含了这个deployment,则先删除,再创建
if (deploymentList.contains('demo-go-app')) {
println("如果已经包含了这个deployment")
deleteDeployment("demoapp", "demo-go-app")
sleep 1
createDeployment("demoapp", ret)
} else {
// 没有包含这个deployment,创建
println("没有包含这个deployment,创建")
createDeployment("demoapp", ret)
}
}
}
}
}
}

Jenkins 2


//所有的脚本命令都放在pipeline中
pipeline {
//指定任务在哪个集群节点中执行
agent any

//声明全局变量,方便后面使用
environment {
harborUser = 'admin'
harborPassword = 'Harbor12345'
harborAddress = '192.168.2.152:30003'
harborRepo = 'go'
}

stages {
stage('拉取代码') {
steps {
echo '开始拉取git仓库代码……'
checkout scmGit(branches: [[name: 'dev']], extensions: [], userRemoteConfigs: [[url: 'http://192.168.2.158:7080/root/test.git']])
echo '开始拉取git仓库代码完毕。'
}
}
stage('构建项目') {
steps {
echo '开始通过maven构建项目……'
sh 'node -v'
sh 'npm install pnpm -g'
sh 'ls'
sh 'cd ./web && pnpm install'
sh 'ls'
sh 'pnpm build'
echo '通过maven构建项目完毕'
}
}
stage('代码检查') {
steps {
echo 'TODO 通过SonarQube做代码质量检测'
}
}
stage('制作镜像') {
steps {
echo '通过Docker制作自定义镜像……'
sh '''mv ./target/*.jar ./docker/
docker build -t ${JOB_NAME}:dev ./docker/'''
echo '通过Docker制作自定义镜像完毕'
}
}
stage('推送镜像') {
steps {
echo '将自定义对象推送到Harbor仓库……'
sh '''docker login -u ${harborUser} -p ${harborPassword} ${harborAddress}
docker tag ${JOB_NAME}:dev ${harborAddress}/${harborRepo}/${JOB_NAME}:dev
docker push ${harborAddress}/${harborRepo}/${JOB_NAME}:dev
docker image prune -f'''
echo '将自定义对象推送到Harbor仓库完成'
}
}
stage('将yml文件传到k8smaster') {
steps {
echo '将yml文件传到k8smaster……'
sshPublisher(publishers: [sshPublisherDesc(configName: 'k8smaster', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: '', execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: '', sourceFiles: 'pipeline-auto.yml')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
echo '将yml文件传到k8smaster完毕'
}
}
stage('远程执行k8smaster的kubectl命令') {
steps {
echo '远程执行k8smaster的kubectl命令……'
sshPublisher(publishers: [sshPublisherDesc(configName: 'k8smaster', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: '''kubectl apply -f /usr/local/k8s/pipeline-auto.yml
//强制重新部署容器
kubectl rollout restart deployment pipeline-auto -n test''', execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: '', sourceFiles: '')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
echo '远程执行k8smaster的kubectl命令完毕'
}
}
}
}

参考

k8s的api