这是一个k8s集群结合jenkins做持续集成,以及jenkins流水线在k8s集群中运行。java编译后、nodejs编译后docker镜像制作。

准备环境

K8s集群1.15.10

Docker-ce 19.03

nfs卷

服务器信息

1
2
3
4
192.168.137.100:k8s-master/k8s-node
192.168.137.101:k8s-master/k8s-node
192.168.137.102:k8s-master/k8s-node
192.168.137.5:nfs服务、docker私有镜像仓库

nfs服务部署

nfsinstall.sh

1
2
3
4
5
6
#!/bin/bash
docker run -d --restart=always --name nfs_server --privileged -p 2049:2049 \
-v /data/nfs:/nfsroot \
-h nfsserver \
-e SHARED_DIRECTORY=/nfsroot \
huisebug/nfs-server:latest

执行脚本即可在服务器上部署一个nfs服务

docker私有镜像仓库

创建私有仓库带有简单的密码认证,用户名admin,密码huisebug

dockerregistryinstall.sh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/bin/bash
dockerregistryinstall(){
mkdir -p /data/dockerregistry/auth
docker run --entrypoint htpasswd registry:2 -Bbn admin huisebug > /data/dockerregistry/auth/htpasswd

docker run -d -p 80:5000 --restart=always --name registry \
-v /data/dockerregistry/auth/:/auth \
-e "REGISTRY_AUTH=htpasswd" \
-e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" \
-e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd \
-v /data/dockerregistry/data:/var/lib/registry \
registry:2
}
dockerregistryinstall

Jenkins部署

新建jenkins命名空间

jenkins-ns.yaml

1
2
3
4
5
6
7
apiVersion: v1
kind: Namespace
metadata:
name: jenkins
labels:
name: jenkins

持久化存储jenkins和maven

准备好2个nfs数据卷用于jenkins持久化和maven仓库持久化(此处我是使用1个,采用不同目录进行区分)

因为jenkins的kubernetes插件功能不是很完善,所以这里的pvc在pod中是不支持subpath的,所以需要在pv的path下写好对应的路径

登录到nfs所在机器192.168.137.5,建立pv所需的路径

1
2
3
cd /data/nfs
mkdir -p jenkins/home
mkdir -p jenkins/maven

jenkins-pv-pvc

jenkins-pv-pvc.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
apiVersion: v1
kind: PersistentVolume
metadata:
name: jenkins
labels:
app: jenkins
spec:
capacity:
storage: 20Gi
accessModes:
- ReadWriteOnce
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain
volumeMode: Filesystem
nfs:
server: 192.168.137.5
path: /jenkins/home

---
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
labels:
app: jenkins
name: jenkins
namespace: jenkins
spec:
accessModes:
- ReadWriteOnce
- ReadWriteMany
resources:
requests:
storage: 20Gi
storageClassName: ""
volumeMode: Filesystem
volumeName: jenkins

maven-pv-pvc

maven-pv-pvc.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
apiVersion: v1
kind: PersistentVolume
metadata:
name: maven
labels:
app: maven
spec:
capacity:
storage: 20Gi
accessModes:
- ReadWriteOnce
- ReadWriteMany
nfs:
server: 192.168.137.5
path: /jenkins/maven
persistentVolumeReclaimPolicy: Retain
volumeMode: Filesystem
---
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
labels:
app: maven
name: maven
namespace: jenkins
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 20Gi
storageClassName: ""
volumeMode: Filesystem
volumeName: maven

Jenkins-rbac

jenkins需要建立pod权限以调用k8s集群建立jenkins构建节点pod

jenkins-rbac.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
apiVersion: v1
kind: ServiceAccount
metadata:
name: jenkins
namespace: jenkins

---

kind: Role
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: jenkins
namespace: jenkins
rules:
- apiGroups: ["extensions", "apps"]
resources: ["deployments"]
verbs: ["create", "delete", "get", "list", "watch", "patch", "update"]
- apiGroups: [""]
resources: ["services"]
verbs: ["create", "delete", "get", "list", "watch", "patch", "update"]
- apiGroups: [""]
resources: ["pods"]
verbs: ["create","delete","get","list","patch","update","watch"]
- apiGroups: [""]
resources: ["pods/exec"]
verbs: ["create","delete","get","list","patch","update","watch"]
- apiGroups: [""]
resources: ["pods/log"]
verbs: ["get","list","watch"]
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get"]

---

apiVersion: rbac.authorization.k8s.io/v1beta1
kind: RoleBinding
metadata:
name: jenkins
namespace: jenkins
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: jenkins
subjects:
- kind: ServiceAccount
name: jenkins
namespace: jenkins

Jenkins

jenkins.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
83
84
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: jenkins
namespace: jenkins
spec:
template:
metadata:
labels:
app: jenkins
spec:
terminationGracePeriodSeconds: 10
serviceAccount: jenkins
containers:
- name: jenkins
image: jenkins/jenkins:lts
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8080
name: web
protocol: TCP
- containerPort: 50000
name: agent
protocol: TCP
resources:
limits:
cpu: 1000m
memory: 1Gi
requests:
cpu: 500m
memory: 512Mi
livenessProbe:
httpGet:
path: /login
port: 8080
initialDelaySeconds: 60
timeoutSeconds: 5
failureThreshold: 12
readinessProbe:
httpGet:
path: /login
port: 8080
initialDelaySeconds: 60
timeoutSeconds: 5
failureThreshold: 12
volumeMounts:
- name: jenkinshome
subPath: jenkins
mountPath: /var/jenkins_home
env:
- name: LIMITS_MEMORY
valueFrom:
resourceFieldRef:
resource: limits.memory
divisor: 1Mi
- name: JAVA_OPTS
value: -Xmx$(LIMITS_MEMORY)m -XshowSettings:vm -Dhudson.slaves.NodeProvisioner.initialDelay=0 -Dhudson.slaves.NodeProvisioner.MARGIN=50 -Dhudson.slaves.NodeProvisioner.MARGIN0=0.85 -Duser.timezone=Asia/Shanghai
securityContext:
fsGroup: 1000
volumes:
- name: jenkinshome
persistentVolumeClaim:
claimName: jenkins

---
apiVersion: v1
kind: Service
metadata:
name: jenkins
namespace: jenkins
labels:
app: jenkins
spec:
type: NodePort
selector:
app: jenkins
ports:
- name: web
port: 8080
targetPort: web
nodePort: 30000
- name: agent
port: 50000
targetPort: agent

注意事项:

如果jenkins提示权限问题

因为jenkins镜像中使用的账户是jenkins,而非root用户,所以需要将jenkins使用的pv下的path路径加上jenkins中提交的subpath重新授权

登录到nfs所在机器192.168.137.5

1
2
cd /data/nfs/jenkins/home
chown -R 1000 jenkins/

授权后重新部署jenkins

1
kubectl delete -f Jenkins.yaml && kubectl apply -f Jenkins.yaml

get_k8s_admin_cert

jenkins构建节点的docker镜像内置了kubectl客户端,用于job完成打包到部署的流程,所以需要执行get_k8s_admin_cert.sh脚本获取k8s管理员证书建立secret

因为jenkins的kubernetes插件功能不是很完善,所以这里的secret的key不能单独指定

get_k8s_admin_cert.sh

1
2
3
#!/bin/bash
#建立jenkins构建节点pod中kubectl客户端所需的k8s管理员证书
kubectl -n jenkins create secret generic k8sadmin --from-file=/root/.kube/config

验证访问

访问http://192.168.137.101:30000

准备完毕

Jenkins配置

安装kubernetes插件

因为网络原因需要将jenkins代理设置位置:http://jenkins访问url/pluginManager/advanced

将升级站点处的url地址修改为:http://mirror.xmission.com/jenkins/updates/current/update-center.json

然后点击提交

切换到插件管理进行插件安装

等待安装完毕即可

配置kubernetes节点信息

系统管理–节点管理—Configure Clouds

安装了kubernetes插件这里就会出现kubernetes可选项

Kubernetes Cloud details配置

点击Kubernetes Cloud details按钮即可开始配置

名称:kubernetes

kubernetes地址:https://kubernetes.default.svc.cluster.local

kubernetes命名空间:jenkins 即 jenkins服务所在的命名空间namespace

配置完毕后点击连接测试,查看是否可以连接成功

jenkins地址:http://jenkins.jenkins.svc.cluster.local:8080
即jenkins服务在k8s集群中使用完整的svc名称的访问地址

其他项默认即可

Pod Templates配置

因为我们需要定义一些功能,所以需要自定义jenkins构建节点pod的配置

点击添加pod模板后再点击Pod Template details按钮即可开始配置

注意事项:

  • 命令空间填写和jenkins同一命名空间

  • 标签列表就是后续job调用这个pod运行的标签,后续需要写到job中去

  • 容器列表名称的pod模板中的容器名必须为jnlp,这样就只会有一个容器

  • 容器镜像必须是以jenkins/jnlp-slave镜像为基础的镜像,可以以此为基础镜像增加服务,此处我的镜像增加了mvn、nodejs、docker、kubectl

  • 如果pod模板中容器名称不是jnlp,那么就会认为是一个额外的容器,就会默认建立一个容器名为jnlp使用镜像jenkins/jnlp-slave的容器。然后将模板中的容器名称作为pod中的第2个容器的容器名,镜像使用你填写的镜像

  • pipline工作中调用的容器永远是容器名为jnlp的容器

卷挂载(非必须的)

注意事项:

  • docker打包的时候需要将调度的k8s-node的docker.sock挂载到容器中,便于容器中的docker服务进行打包,然后推送镜像到私有仓库,推送镜像需要认证私有仓库信息。认证的时候是根据挂载的docker.sock的,所以k8s-node的docker服务需要将私有仓库信息添加到insecure-registries中,比如此处我的私有镜像仓库为172.168.137.5

  • java项目打包的时候会下载很多依赖插件,所以此处需要将maven的仓库持久化存储,加快打包效率,注意我的jenkinsbuild镜像中使用的是root用户运行的,所以maven的仓库数据是放在/root/.m2的。

  • secret认证是用于kubectl客户端连接k8s集群,便于k8s镜像发布,secret的创建在之前的get_k8s_admin_cert.sh已经建立

  • 其他的配置默认即可,例如:代理的空闲存活时间(分)代表jenkins构建节点pod的存活时间,一般默认即可

Jenkins自由风格Job验证

限制项目的运行节点

此处就对应之前pod template中的标签列表

构建操作,执行一个shell

1
2
3
4
5
java -version
mvn -v
nodejs -v
docker info
kubectl get pod -n jenkins

保存后点击构建

会出现下面的状态,正在创建pod以获取输出,如果一直没有变化,那么就是pod建立失败,需要查看一下kubernetes节点配置是否正确,大多数原因是因为pod的配置

等待建立构建的pod,可以在终端查看到

pod建立成功将会在jenkins控制台进行输出,也会输出pod的建立yaml,可以根据来排查问题

成功获取到了上述shell执行结果

Jenkins认证凭证

添加好jenkins认证凭证,可以用于后续的job拉取git仓库代码、pipline中使用git函数利用凭证的ID字段进行认证,上面的ID是可以手动定义的,后续将会用到

Jenkins Pipline

构建定制化的持续集成是必须的工作,pipline的使用就不过多介绍,此处介绍本次使用的pipline过程

参考github地址:https://github.com/huisebug/jenk8s-pipline.githttps://github.com/huisebug/jenk8s-install.git

  • build.groovy:流水线pipline文件

  • dockerfile:java环境Dockerfile,nodejs项目Dockerfile,jenkins构建节点的Dockerfile

  • jenkins:jenkins部署到k8s中的yaml

  • projects:存放需要打包的项目的信息,文件目录名区分项目,project_info文件中写入:项目名称#服务名称=服务的git代码地址

  • scripts:存放docker镜像制作的脚本

build.groovy

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
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
//当前的jenkins管理节点
node {
//配置全局变量
env.project = env.JOB_BASE_NAME.split("\\+")[0]
env.app_name = env.JOB_BASE_NAME.split("\\+")[1]
env.branch = env.JOB_BASE_NAME.split("\\+")[2]
println(env.project)
println(env.app_name)
println(env.branch)

/*
因为jenkins的版本原因,使用git方式拉取了jenkinsfile的项目所有源代码文件
在系统workspace目录下建立了一个job名+@script的文件目录来存放jenkinsfile源代码所有文件
pipline执行的时候是以job名称为文件目录名称下
类似于
[root@localhost workspace]# tree
.
├── jenkins+java+master
│ └── java-ci
├── jenkins+java+master@script
│ ├── build.groovy
│ └── projects
│ └── jenkins
│ └── project_info
*/

//jenkinsfile的源代码存放目录名称,此处是:job名称+@script
env.tempsuffix = '@script'
env.JenkinsfileREPO = env.JOB_BASE_NAME + env.tempsuffix

//获取代码仓库地址,作为全局变量提供给打包k8s节点使用
if (env.app_name == '') {
env.app_name = 'none'
env.git_repository = sh returnStdout: true, script: 'cat ../'+env.JenkinsfileREPO+'/projects/'+env.project+'project_info|grep '+env.project+'_repo|awk -F "=" \'{print $2}\''
env.ci_dir = env.project+'-ci'

} else {
env.git_repository = sh returnStdout: true, script: 'cat ../'+env.JenkinsfileREPO+'/projects/'+env.project+'/project_info|grep '+env.project+'#'+env.app_name+'_repo|awk -F "=" \'{print $2}\''
env.ci_dir = env.app_name+'-ci'
}
}

//CI打包k8s节点,这里就是匹配之前的pod template中的标签列表
node('jenkinsbuildnode') {
echo "本次build的项目的源代码地址: "+env.git_repository

stage('CI打包') {
/*tools {
maven '3.6.2'
jdk '1.8.0_242'
nodejs '10.19.0'
npm '5.8.0'
}*/


script {
/*判断是否推送到生产仓库
try {
timeout(time: 70, unit: 'SECONDS') {
def userInput = input(id: 'userInput', ok: '确定', message: '是否推送镜像到生产环境', parameters: [booleanParam(defaultValue: false, description: '', name: '发布到master仓库')])
//println userInput
println userInput.getClass()
if(userInput==true){
env.to_master_registry = "go"
}else {
env.to_master_registry = ""
}
}
}
catch(Exception ex) {
println("Catching exception")
echo 'do nothing, continue.......'
env.to_master_registry = ""
}*/
dir(env.ci_dir) {
echo "拉取git代码"
checkout([$class: 'GitSCM', branches: [[name: branch]], doGenerateSubmoduleConfigurations: false, extensions: [[$class: 'CleanBeforeCheckout']], submoduleCfg: [], userRemoteConfigs: [[credentialsId: 'huisebug', url: git_repository]]])
git_commit_hash = sh (script: "git log -n 1 --pretty=format:'%H'", returnStdout: true)
env.git_commit_hash = '-'+git_commit_hash[0..4]

project = sh returnStdout: true, script: 'cat app.info|grep devlang|awk -F ":" \'{print $2}\''
project = project.replace("\r","").replace(" ", "").replace('\n', '')
echo "项目类型是"+project
println project.class

def version = sh returnStdout: true, script: ''' cat app.info |grep version|awk -F ":" '{print $2}' '''
env.ver = version.replace("\r", "").replace("\n", "").replace(" ", "")
echo '打包版本'+env.ver

if (project == 'NodeJs'||project == 'nodejs') {
sh 'mkdir -pv target'
//sh 'find ./ -type f -exec dos2unix -q {} \\;'
sh 'npm cache clean --force'
sh 'npm config set registry http://registry.npm.taobao.org/'
sh 'cnpm install'
if (env.JOB_BASE_NAME.tokenize('+')[3] == 'dev') {
sh 'npm run build:dev'
} else if (env.JOB_BASE_NAME.tokenize('+')[3] == 'test') {
sh 'npm run build:test'
} else if (env.JOB_BASE_NAME.tokenize('+')[3] == 'sg') {
sh 'npm run build:sg'
} else {
sh 'npm run build'
}
sh 'mv dist target/'
echo project+'nodejs container Preparing....'

} else if (project == 'Java'||project == 'java') {
echo project+' java container Preparing....'
sh 'mvn -Dmaven.test.skip=true clean package'

} else if (project == 'Python'||project == 'python') {
sh 'mkdir -pv target'
//sh 'find ./ -type f -exec dos2unix -q {} \\;'
sh 'cp -rf `ls|grep -v app.info|grep -v target|xargs` target/'
echo project+' python container Preparing....'

} else if (project == 'HTML'||project == 'html') {
sh 'mkdir -pv target/dist'
//sh 'find ./ -type f -exec dos2unix -q {} \\;'
sh 'cp -rf `ls|grep -v app.info|grep -v target|xargs` target/dist'
echo project+' html container Preparing....'

} else {
sh 'mkdir -pv target/dist'
//sh 'find ./ -type f -exec dos2unix -q {} \\;'
sh 'cp -rf `ls|grep -v app.info|grep -v target|xargs` target/dist'
echo project+' html container Preparing....'
}
}
}
}
stage('docker镜像制作') {
script {
sh 'mkdir -pv dockerbuild'
dir('dockerbuild') {
deleteDir()
echo "拉取docker镜像制作脚本,代码分支一般默认是master",凭证信息credentialsId: 'huisebug' ,直接使用jenkins存放的凭证的ID
env.git_repo = 'https://github.com/huisebug/jenk8s-pipline.git'
checkout([$class: 'GitSCM', branches: [[name: 'master']], doGenerateSubmoduleConfigurations: false, extensions: [[$class: 'CleanBeforeCheckout']], submoduleCfg: [], userRemoteConfigs: [[credentialsId: 'huisebug', url: git_repo]]])

println('需要执行打包的容器名称有:')
sh 'bash scripts/dockerbuild.sh'+' '+env.WORKSPACE+'/'+env.ci_dir+' '+env.app_name+' '+env.ver+' '+env.git_commit_hash+' '+env.project+' '+project+' '+env.to_master_registry
}
}
}
}
  • pipline会首先在jenkins本地node执行build.groovy,根据当前的job名称来获取项目名称+服务名称+代码分支,例如:jenkins+java+master,然后去匹配projects文件目录中的信息,最终得到此次job的运行的服务名,git地址,分支信息传递到整个pipline的全局变量中

  • 然后运行在jenkins构建节点pod中运行拉取项目服务代码、项目服务编译、docker镜像制作

scripts/ dockerbuild.sh

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
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
#!/bin/bash

#脚本参数,$1:ci路径 $2:服务名称 $3:打包版本 $4:代码哈希 $5 项目名称 $6:项目类型(Html or Java or Nodejs) $7:生产仓库(可选)
#私库地址
REGISTRY='192.168.137.5'
#私库存放服务的仓库名
WAREHOUSE_NAME='huisebug'
#项目的CI目录名称
if [ x$2 != xnone ];then
app_name=$5-$2
ci_dir=$2-ci
else
app_name=$5
ci_dir=$5-ci
fi

#java项目镜像制作
java()
{
BASE_IMAGE=${REGISTRY}/base/openjdk:8
APP_HOME=/data/projects/${app_name}
EXE_CMD="java -jar"
EXE_BIN=${APP_HOME}/bin/${app_name}.jar
EXE_CONF='-Dlog_host=${log_host}'
#jvm内存参数,临时设置,后续可以传参自定义
EXE_LEVEL="-Xms1680M -Xmx1680M -Xmn1260M -Xss1M"
#jvm其他参数调优
EXE_OPTION="-server -XX:SurvivorRatio=8 -XX:+UseConcMarkSweepGC -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m -XX:MaxDirectMemorySize=1g -XX:+ExplicitGCInvokesConcurrent -XX:CMSInitiatingOccupancyFraction=80 -XX:-UseCMSInitiatingOccupancyOnly -Dsun.rmi.dgc.server.gcInterval=2592000000 -Dsun.rmi.dgc.client.gcInterval=2592000000 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=${APP_HOME}/log/java.hprof -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps"



cat > run.sh << \EOF
#!/bin/bash

#环境变量当前主机名
log_host=`hostname`
#jvm内存参数设置
java_mem=`env|grep java_mem|awk -F '=' '{print $2}'`
#服务日志是否在控制台输出,即容器即生命
log_echo=`env|grep log_echo|awk -F '=' '{print $2}'`

#判断变量java_mem是否为空,不存在即为空
if [[ x$java_mem == x ]];then
EXE_LEVEL=""
else
EXE_LEVEL=$java_mem
fi

#额外的jvm参数
if [[ x$general_para != x ]];then
EXT_OPTION=$general_para
else
EXT_OPTION=""
fi

#程序的参数
if [[ x$application_para != x ]];then
APP_OPTION=$application_para
else
APP_OPTION=""
fi

EOF

echo "if [[ x\${log_echo} != x ]];then" >> run.sh
echo ${EXE_CMD} \${EXE_LEVEL} ${EXE_OPTION} \${EXT_OPTION} ${EXE_CONF} ${EXE_BIN} \${APP_OPTION} >> run.sh
echo "else" >> run.sh
EXE_CMD="nohup java -jar"
echo ${EXE_CMD} \${EXE_LEVEL} ${EXE_OPTION} \${EXT_OPTION} ${EXE_CONF} ${EXE_BIN} \${APP_OPTION} >> run.sh
echo "tail -f /dev/null" >> run.sh
echo "fi" >> run.sh


#准备好将要复制到镜像中的服务文件
rm -rf source
mkdir -p source
mv ../${ci_dir}/target/* source/

#生成Dockerfile
cat > Dockerfile << EOF

FROM ${BASE_IMAGE}
RUN for dir in data bin log conf; do mkdir -p ${APP_HOME}/\$dir; done
ADD source ${APP_HOME}/bin
COPY run.sh /root/run.sh
CMD ["/bin/bash","/root/run.sh"]

EOF

}
#web项目镜像制作,包含nodejs和html
web()
{
#此处只做拷贝文件,启动方式按照$BASE_IMAGE的说明进行
BASE_IMAGE=${REGISTRY}/base/nginx:1.19
APP_HOME=/usr/share/nginx/html

#准备好将要复制到镜像中的服务文件,nodejs打包后的项目html全部放在dist目录下,在前面的pipline中已经将dist目录移动到了target目录中
rm -rf source
mkdir -p source
mv ../${ci_dir}/target/dist/* source/


#生成dockerfile
cat > Dockerfile << EOF
FROM ${BASE_IMAGE}
ADD source ${APP_HOME}
EOF

}


if [[ x$6 == xJava || x$6 == xjava ]];then
java $1 $2 $3 $4 $5 $6 $7 $8
else
web $1 $2 $3 $4 $5 $6 $7 $8
fi

#####################main()###################
cat Dockerfile
container_name=${app_name}
time_char=`date "+%y%m%d%H%M"`
container_version=$3-${time_char}$4
#私有仓库的认证
docker login 192.168.137.5 -u admin -p huisebug >/dev/null 2>&1
docker build -t ${REGISTRY}/${WAREHOUSE_NAME}/${container_name}:${container_version} .

#生成镜像文件#推送镜像文件
docker push ${REGISTRY}/${WAREHOUSE_NAME}/${container_name}:${container_version}

if [[ x$7 == xgo ]];then
echo '开始推送到生产镜像仓库'
#生产镜像仓库地址
proimagerepo=192.168.137.5
#生产镜像仓库认证
docker login ${proimagerepo} -u admin -p huisebug >/dev/null 2>&1
docker tag ${REGISTRY}/${WAREHOUSE_NAME}/${container_name}:${container_version} ${proimagerepo}/${WAREHOUSE_NAME}/${container_name}:${container_version}
docker push ${proimagerepo}/${WAREHOUSE_NAME}/${container_name}:${container_version}
echo '清除多余的标签'
docker rmi ${proimagerepo}/${WAREHOUSE_NAME}/${container_name}:${container_version}
fi
  • java的镜像制作过程中会需要项目的pom.xml中定义构建后的jar包名称,比如此处我的项目jar名称就应该为jenkins-java.jar。

  • java的docker镜像制作是可以后续在建立时使用docker的变量方式进行java传参和jar包传参;变量general_para是java参数传递,变量application_para是jar参数传递。

  • nodejs编译后就是web页面,所以nodejs和web直接copy到nginx容器即可

  • 使用的基础docker镜像是存放在私有镜像仓库的,所以需要推送镜像到私有镜像仓库,参考java和nginx基础镜像Dockerfile:https://github.com/huisebug/jenk8s-install.git

projects

此处我准备的项目名称为:jenkins 所以建立了一个jenkins文件目录

jenkins文件目录下存放了project_info文件
project_info

1
2
3
jenkins#java_repo=https://github.com/huisebug/jenk8s-java.git
jenkins#nodejs_repo=https://github.com/huisebug/jenk8s-nodejs.git
jenkins#html_repo=https://github.com/huisebug/jenk8s-html.git
  • 前面的jenkins代表项目名称,要求这个名称和文件目录名称相同

  • java_repo/nodejs_repo/html_repo将会去掉repo取名作为服务名称

  • git地址是对应的服务的git地址

调试项目介绍

此处我准备了三个不同类型的项目

java:https://github.com/huisebug/jenk8s-java.git

nodejs:https://github.com/huisebug/jenk8s-nodejs.git

html:https://github.com/huisebug/jenk8s-html.git

三个项目中都会有一个特殊的配置文件app.info

项目中特殊的配置文件app.info

1
2
devlang:java 
version:1.1
1
2
devlang:html 
version:1.1
1
2
devlang:nodejs 
version:1.1
  • 需要给项目中的代码根目录建立一个app.info文件,便于流水线识别这是什么类型的项目而调用对应的方法进行打包和制作docker镜像

  • devlang :开发语言

  • version :版本号

Jenkins流水线Job验证

job名称格式

注意job名称,以”+”号分割,分别是项目名称+服务名称+代码分支

流水线配置

使用git的方式拉取jenkinsfile(即build.groovy),轻量级检出的选项不勾选,勾选就会直接读取build.groovy的内容,不会clone整个项目代码,就无法获取到其他的项目代码

后续的项目我们只需要在建立job的时候复制然后建立就行了

java项目执行结果