Jenkins 中自动打包推送发布K8s应用
好久没写新内容,来个不务正业的小分享吧。接下来打算写一个关于归因分析的专题,正好找找状态。
安装Jenkins分享已经很多,这里很多前置内容我们不再赘述,直接进入与发布至K8s有关的部分,一些知识补充我们不在文中展开,但是提供查询地址,咸淡自取。
文中最后样例用了阿里云,不过实际与什么云关系不大,这些是K8s的机制,在哪里都基本一样。
插件安装
要联通K8s,我们需要安装一些基础的插件,一些分享中可能会有一些更多的插件安装,不过在这里我们主要使用文中的几个即可。
- Kubernetes
- Kubernetes CLI
- Kubernetes Client API
- 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中配置密钥信息
-
找到Manage Credentials。
-
创建一个新的 secret 类型的,secret的字符串粘贴入刚才获取到的TOKEN
-
去一个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()
}
}
}
}