Akemi

Gitlab+Jenkins+argo+k8s CICD

2025/01/15

项目概述

场景概述
公有云gitlab存放dockerfile,公有云jenkins
现在要求公有gitlab更新时,自动构建dockerfile镜像
并部署到公有、私有两套k8s

现有条件

允许创建新git仓库
jenkins现有shell项目,拉取git代码后构建,使用容器ssh部署到公有环境

主要问题

不允许打vpn
可以用jenkins webhook感知gitlab更新,但jenkins无法连接私有k8s
即私有k8s无法感知gitlab更新

解决方案

创建新的B git,根据容器要求创建helm chart存放在B中

构建声明式jenkinsfile

当gitlab更新时,触发jenkins流水线,读取jenkinsfile

jenkinsfile中先拉取dockerfile与构建镜像

后拉取helm工程文件,修改变量文件的tag,再进行上传,不同的环境使用不同的vlaues文件进行管理

argo分别连接公有和私有k8s与B git,检测到helm工程变化后,自动同步资源文件到k8s

这个图是有点问题的,但懒得改了

细节说明

helm结构优化原则

1.同一应用使用不同分支,不同变量文件,同一模板文件
2.不同分支的目的是为了防止手动修改values时出现错误
3.应用太多时可使用父子chart,增强可扩展性与可维护性

同时需要基于实际上应用的需求,进行配合:
像本例子中,git中的内容只需要使用jenkinsfile的yq进行修改,再上传
不需要进行手动修改,所以将四个环境的values都放在同一个分支下

jenkins流水线git拉取问题

jenkins节点切忌使用git凭证助手
在进行拉取时,会导致git拉取报错——找不到仓库或无权限

如果不知道这是什么,那就不需要额外修改

argo连接k8s

需要通过argoCLI进行连接

或者如果你是helm部署的argo,也可以通过HelmChart资源将kubeconfig传入helm

helm工程

即一个变量文件+一个模板文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
变量文件
global:
common:
imagePullSecrets:
- name: harbor-token
checkerbackend:
enabled: true
name: checker-backend
replicas: 1
image:
repository: xxx
tag: "xxx"
pullPolicy: IfNotPresent
env:
- name: OSSUTIL
value: true
service:
type: NodePort
port: 8080
targetPort: 80
nodePort: 32007
hostAliases:
- ip:
hostnames:
affinity:
nodeAffinityKey: "node-role.kubernetes.io/controlplane"

template文件
{{- if and .Values.checkerbackend .Values.checkerbackend.enabled }}
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Values.checkerbackend.name }}
spec:
replicas: {{ .Values.checkerbackend.replicas }}
selector:
matchLabels:
app: {{ .Values.checkerbackend.name }}
template:
metadata:
labels:
app: {{ .Values.checkerbackend.name }}
spec:
imagePullSecrets:
{{- range .Values.global.common.imagePullSecrets }}
- name: {{ .name }}
{{- end }}
containers:
- name: {{ .Values.checkerbackend.name }}
image: "{{ .Values.checkerbackend.image.repository }}:{{ .Values.checkerbackend.image.tag }}"
imagePullPolicy: {{ .Values.checkerbackend.image.pullPolicy }}
env:
{{- range .Values.checkerbackend.env }}
- name: {{ .name }}
value: {{ .value | quote }}
{{- end }}
ports:
- containerPort: 80
hostAliases:
{{- if .Values.checkerbackend.hostAliases }}
{{- toYaml .Values.checkerbackend.hostAliases | nindent 10 }}
{{- end }}
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: {{ .Values.checkerbackend.affinity.nodeAffinityKey }}
operator: In
values:
- "true"
---
apiVersion: v1
kind: Service
metadata:
name: {{ .Values.checkerbackend.name }}
spec:
type: {{ .Values.checkerbackend.service.type }}
ports:
- port: {{ .Values.checkerbackend.service.port }}
targetPort: {{ .Values.checkerbackend.service.targetPort }}
nodePort: {{ .Values.checkerbackend.service.nodePort }}
selector:
app: {{ .Values.checkerbackend.name }}
{{- end }}

Jenkinsfile声明式

几个注意点

1.定义超时时间
2.将时间戳写入临时文件
3.使用withCredentials时,会将账密复制到变量
4.提前安装好yq工具来修改yaml格式文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
pipeline {
agent {
node {
label 'xxxx'
}
}
options {
timeout(time: 10, unit: 'MINUTES')
}
stages {
stage('Define Timestamp Tag') {
steps {
script {
def image_version = sh(script: 'date +"%Y%m%d%H%M"', returnStdout: true).trim()
writeFile file: '/tmp/image_version.txt', text: image_version
}
}
}
stage('Git Clone checker-backend and build docker images') {
steps {
checkout([
$class: 'GitSCM',
branches: [[name: '*/master']],
extensions: [],
submoduleCfg: [],
userRemoteConfigs: [[
url: 'xxxx',
credentialsId: 'xxx-alg-key'
]]
])
sh '''
image_version=$(cat /tmp/image_version.txt)
echo "[jenkins log]: docker build start."
docker build -f Dockerfile -t xxxx:${image_version} .
echo "[jenkins log]: docker build end. pushing this image to the remote repo."
docker push xxxx:${image_version}
echo "[jenkins log]: docker push end."
'''
}
}

stage('git clone helm') {
steps {
checkout([
$class: 'GitSCM',
branches: [[name: 'xxxx']],
extensions: [
[$class: 'CleanBeforeCheckout']
],
submoduleCfg: [],
userRemoteConfigs: [[
url: 'xxx.git',
credentialsId: 'xxx-public-key'
]]
])
}
}
stage('make changes and commit') {
steps {
script {
withCredentials([usernamePassword(credentialsId: 'xxx-public-key', passwordVariable: 'GIT_PASSWORD', usernameVariable: 'GIT_USERNAME')]) {
sh '''
git remote set-url origin https://${GIT_USERNAME}:${GIT_PASSWORD}@xxxx.git
git checkout xxxx
git pull

version=$(cat /tmp/image_version.txt)
formatted_version=$(printf '"%s"\n' "$version")
echo ".checkerbackend.image.tag = $formatted_version" > /tmp/yq_script.yaml
yq eval -i "$(cat /tmp/yq_script.yaml)" helm-checker-backend/values.yaml
rm /tmp/yq_script.yaml

git add .
git commit -m "checker-backend update $(cat /tmp/image_version.txt)" || true
git push origin xxxx
'''
}
}
}
}
}
}
CATALOG
  1. 1. 项目概述
  2. 2. 细节说明
  3. 3. helm工程
  4. 4. Jenkinsfile声明式