基于Kubernetes Jenkins CICD
传统构建jenkins会有许多的问题,我们需要采用一种更高效可靠的方式来完成这个CI/CD流程,而Docker虚拟化容器技能很好的解决这个痛点,又特别是在Kubernetes集群环境下面能够更好来解决上面的问题
一、在Kubernetes 安装 Jenkins优点
目前很多公司采用Jenkins集群搭建复合需求的CI/CD流程,但是会存在一些问题
正因为上面的问题,我们需要采用一种更高效可靠的方式来完成这个CI/CD流程,而Docker虚拟化容器技能很好的解决这个痛点,又特别是在Kubernetes集群环境下面能够更好来解决上面的问题

如上图我们可以看到Jenkins master和Jenkins slave以Pod形式运行在Kubernetes集群的Node上,Master运行在其中一个节点,并且将其配置数据存储到一个volume上去,slave运行在各个节点上,但是它的状态并不是一直处于运行状态,它会按照需求动态的创建并自动删除
这种方式流程大致为: 当Jenkins Master接受到Build请求后,会根据配置的Label动态创建一个运行在Pod中的Jenkins Slave并注册到Master上,当运行完Job后,这个Slave会被注销并且这个Pod也会自动删除,恢复到最初的状态(这个策略可以设置)
二、Kubernetes 安装Jenkins
这里我们使用k8s的pv进行存储数据,当然也可以使用storage class对象来自动创建
创建pv需要使用nfs,我这里直接创建一台nfs服务器
我们在k8s集群的任意一个节点配置一台nfs集群,其他节点主要是挂载 for i in k8s-01 k8s-02 k8s-03 k8s-04;do ssh root@$i "yum install nfs-utils rpcbind -y";done #我这里使用单独的NFS服务器,IP为192.168.0.100 #NFS服务节点操作如下 mkdir -p /data1/k8s-vloume echo "/data1/k8s-vloume *(rw,no_root_squash,sync)" >/etc/exports systemctl start rpcbind systemctl enable rpcbind systemctl enable nfs systemctl restart nfs #IP改成网段 其他k8s节点直接启动rpcbind并且挂载目录就可以 #挂载 systemctl start rpcbind systemctl enable rpcbind mkdir /data1/k8s-vloume -p mount -t nfs 192.168.0.100:/data1/k8s-vloume /data1/k8s-vloume #创建完成你们自己测一下就可以了
这里需要创建一个命名空间
kubectl create namespace abcdocker #在创建一下jenkins存储yaml的目录 mkdir -p /opt/jenkins
接下来我们开始创建Jenkins Deployment
vim /opt/jenkins/jenkins_deployment.yaml
apiVersion: apps/v1beta1
kind: Deployment
metadata:
name: jenkins #deployment名称
namespace: abcdocker #命名空间
spec:
template:
metadata:
labels:
app: jenkins
spec:
terminationGracePeriodSeconds: 10 #优雅停止pod
serviceAccount: jenkins #后面还需要创建服务账户
containers:
- name: jenkins
image: jenkins/jenkins:lts #镜像版本
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8080 #外部访问端口
name: web
protocol: TCP
- containerPort: 50000 #jenkins save发现端口
name: agent
protocol: TCP
resources:
limits:
cpu: 1000m
memory: 1Gi
requests:
cpu: 500m
memory: 512Mi
livenessProbe:
httpGet:
path: /login
port: 8080
initialDelaySeconds: 60 #容器初始化完成后,等待60秒进行探针检查
timeoutSeconds: 5
failureThreshold: 12 #当Pod成功启动且检查失败时,Kubernetes将在放弃之前尝试failureThreshold次。放弃生存检查意味着重新启动Pod。而放弃就绪检查,Pod将被标记为未就绪。默认为3.最小值为1
readinessProbe:
httpGet:
path: /login
port: 8080
initialDelaySeconds: 60
timeoutSeconds: 5
failureThreshold: 12
volumeMounts: #需要将jenkins_home目录挂载出来
- 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: opspvc #这里将上面创建的pv关联到pvc上
#这里不进行创建
因为要保证Jenkins持久化缓存数据,我们创建了一个PV
cat >/opt/jenkins/jenkins_pv.yaml <<EOF
apiVersion: v1
kind: PersistentVolume
metadata:
name: opspv
spec:
capacity:
storage: 20Gi
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Delete
nfs:
server: 192.168.0.100
path: /data1/k8s-vloume
---
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: opspvc
namespace: abcdocker
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 20Gi
EOF
#注意修改NFS挂载目录以及NFS Server
#nfs后面的目录是nfs挂载的目录,因为pod会执行mount -t ip:/data1/所以后面是nfs挂载目录,而并非是宿主机的目录
另外还需要一个拥有相关权限的serviceAccount的Jenkins用户,这里我们手动赋予Jenkins一些必要的权限,当然直接给cluster-admin的集群角色权限也是可以的
cat >/opt/jenkins/jenkins_rbac.yaml <<EOF
apiVersion: v1
kind: ServiceAccount
metadata:
name: jenkins
namespace: abcdocker
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: 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: ClusterRoleBinding
metadata:
name: jenkins
namespace: abcdocker
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: jenkins
subjects:
- kind: ServiceAccount
name: jenkins
namespace: abcdocker
EOF
现在还缺少一个svc,因为我们虽然现在jenkins已经在内部可以访问,但是我们在外面是无法访问的。接下来我们创建一个svc
cat >/opt/jenkins/jenkins_svc.yaml <<EOF
apiVersion: v1
kind: Service
metadata:
name: jenkins
namespace: abcdocker
labels:
app: jenkins
spec:
selector:
app: jenkins
type: NodePort
ports:
- name: web
port: 8080
targetPort: web
nodePort: 30002
- name: agent
port: 50000
targetPort: agent
EOF
接下来我们进行创建
[root@abcdocker-k8s01 jenkins]# ll total 16 -rw-r--r-- 1 root root 2453 Aug 29 17:42 jenkins_deployment.yaml -rw-r--r-- 1 root root 407 Aug 29 17:43 jenkins_pv.yaml -rw-r--r-- 1 root root 1120 Aug 29 17:43 jenkins_rbac.yaml -rw-r--r-- 1 root root 289 Aug 29 17:43 jenkins_svc.yaml
这里可以直接使用kubectl apple -f .,我为了排查错误,进行一个顺序执行
#这里我先执行pv然后在执行rbac,依次deployment和svc [root@abcdocker-k8s01 jenkins]# kubectl apply -f jenkins_pv.yaml persistentvolume/opspv created persistentvolumeclaim/opspvc created [root@abcdocker-k8s01 jenkins]# kubectl apply -f jenkins_rbac.yaml serviceaccount/jenkins created clusterrole.rbac.authorization.k8s.io/jenkins created clusterrolebinding.rbac.authorization.k8s.io/jenkins created [root@abcdocker-k8s01 jenkins]# kubectl apply -f jenkins_deployment.yaml deployment.extensions/jenkins created [root@abcdocker-k8s01 jenkins]# kubectl apply -f jenkins_rbac.yaml serviceaccount/jenkins unchanged clusterrole.rbac.authorization.k8s.io/jenkins unchanged clusterrolebinding.rbac.authorization.k8s.io/jenkins unchanged [root@abcdocker-k8s01 jenkins]# kubectl apply -f jenkins_svc.yaml service/jenkins created
创建完毕后,我们进行检查
#首先查看pvc状态 kubectl get pv,pvc -n abcdocker NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE persistentvolume/opspv 20Gi RWX Delete Bound abcdocker/opspvc 65s NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE persistentvolumeclaim/opspvc Bound opspv 20Gi RWX 65s #接下来查看rbac kubectl get serviceaccounts -n abcdocker |grep jenkins jenkins 1 104s #最后检查pod和svc kubectl get pod,svc -n abcdocker NAME READY STATUS RESTARTS AGE pod/jenkins-6b874b8d7-dhv57 1/1 Running 0 8m26s NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/jenkins NodePort 10.254.154.161 8080:30002/TCP,50000:21866/TCP 62s #需要说明一下8080端口为我们jenkins访问端口 #50000端口为jenkins save发现端口
Pod正常运行了,我们就可以通过浏览器访问集群任意IP的svc端口

管理员密码文件路径,也可以在pod 日志里面看到
cat /data1/k8s-vloume/jenkins/secrets/initialAdminPassword 6d3c67495bcb43c2a74d97309702dab7 #因为我们持久化在/data/k8s下,所以jenkins的所有配置都在这下面
直接选择推荐安装就可以

等待安装完毕

安装完成后我们进入jenkins主页面

接下来就是配置Jenkins
我们需要安装k8s插件,让Jenkins构建Job可以动态生成Slave的Pod
点击Jenkins-->插件-->安装插件Kubernetes

安装完毕后会重启Jenkins,然后我们就可以配置插件
Manage Jenkins >>Configure System 找到插件

在配置上拉到最下面,ADD一个Kubernetes

Name 配置的名称 Kubernetes URL 这里的URL是K8s内部的URL,实际上就是svcname 这里不需要修改直接复制就行 Kubernetes Namespace k8s的命名空间 (实际上就是Jenkins所在的命名空间)
https://kubernetes.default.svc.cluster.local
svcname+default+后面参数复制就可以

Jenkins URL 这里的URL是jenkins的svc名称加上命名空间,实际上就是在k8s集群内部访问jenkins的一个方法,这里也不需要修改 http://jenkins.abcdocker.svc.cluster.local:8080 #其他默认即可

添加一个 Jenkins Slave Pod模板

Name = Pod 名称 Namespave = Pod命名空间 Labels = Pod标签

接下来是容器的模板配置
Name 容器的名称 Docker image 镜像,这里直接使用我的镜像 #jenkins 2.176.x版本以上使用:registry.cn-beijing.aliyuncs.com/abcdocker/jenkins:v1.2 #jenkins 2.176.x版本以下使用:registry.cn-beijing.aliyuncs.com/abcdocker/jenkins:v1.1 #jenkins 2.176以上版本有maven命令使用:registry.cn-beijing.aliyuncs.com/abcdocker/jenkins:v1.4 #镜像里面主要是运行了kubectl命令和Docker的配置,下面我们还需要将kubeconfig挂载进来 ######## 如果需要自定义maven仓库可以使用下面的dockerfile [root@k8s-01 jenkins]# cat Dockerfile FROM registry.cn-beijing.aliyuncs.com/abcdocker/jenkins:v1.2 MAINTAINER abcdocker "i4t.com" COPY maven /usr/local/maven RUN cd /usr/local/maven
红色代表不可以修改,否则jenkins slave启动会提示错误,反复重启

如果jenkins提示权限错误,请在配置中添加jenkins rbac创建的serviceaccounts

上面说了,镜像主要是用于kubectl,kubectl是需要kubeconfig文件,所以我们还需要创建一个volume的配置

如果我们想在容器里面执行docker命令,需要将docker.sock挂载进去
还需要将kubeconfig挂在进容器


然后我们点击保存即可
接下来添加一个job,来测试一下jenkins是否可以在slave节点上执行

接下来进行配置Jenkins
这里的label标签就是我们在配置中配置的标签

配置标签的地方

添加一个shell命令,测试docker和kubectl

现在我们先看一下Pod是否可以动态获取
$ kubectl get pod -n abcdocker NAME READY STATUS RESTARTS AGE jenkins-6b874b8d7-xxwwr 1/1 Running 0 31h #目前只有一个jenkins在运行,接下来我们进行构建项目
容器正在创建,目前属于等待状态

这里可以看到已经新启动了一个pod节点
$ kubectl get pod -n abcdocker NAME READY STATUS RESTARTS AGE jenkins-6b874b8d7-xxwwr 1/1 Running 0 2d jenkins-slave-423xz 0/1 ContainerCreating 0 5s
等待容器创建成功,Jenkins就会绑定到这个Pod节点上,进行执行任务

如果遇到pod一直重启的情况,请使用kubectl get pod -n xxx -o wide查看pod运行节点,使用docker ps过滤出pod,通过docker logs -f查看错误日志
当Jenkins slave节点执行完毕任务后,默认情况下执行完job就会被删除
$ kubectl get pod -n abcdocker NAME READY STATUS RESTARTS AGE jenkins-6b874b8d7-xxwwr 1/1 Running 0 2d
如果想让job等待30分钟释放,可以在下图添加

小结 Jenkins Master收到Build请求时,会根据配置的Label动态创建一个运行在Pod中的Jenkins Slave并注册到Master上,当Job运行完,这个Slave会被注销并且这个Pod也会自动删除,恢复到最初状态
三、项目演示 使用Jenkins Pipline部署
Jenkins Pipline介绍
Gitlab配置
接下来我们在代码仓库创建一个分支

配置root用户免密
首先先查看服务器公钥

上git配置key

接下来在服务器上提交git代码就可以

上传代码
#这里需要有git命令 yum install -y git git config --global user.name "root" git config --global user.email "admin@example.com" #克隆 git clone git@10.4.82.15:root/abcdocker-test.git cd abcdocker-test wget http://down.i4t.com/hello-world-war-master.zip unzip hello-world-war-master.zip mv hello-world-war-master/* . rm -rf hello-world-war-master* #提交代码 #接下来看图就行了
gitlab配置完毕

四、配置Dockerfile
首先我们需要自定义一个tomcat基础镜像,也可以使用dockerhub提供的
cat >>Dockerfile <<EOF
FROM centos
MAINTAINER www.i4t.com "cyh@i4t.com"
WORKDIR /tmp
## Please see https://i4t.com/3552.html install JDK
COPY jdk1.8.0_66.tar.gz /tmp
RUN tar zxf /tmp/jdk1.8.0_66.tar.gz -C /usr/local/ && rm -rf /tmp/jdk1.8.0_66.tar.gz &&
ln -s /usr/local/jdk1.8.0_66 /usr/local/jdk
#/etc/profile
ENV JAVA_HOME /usr/local/jdk
ENV CLASSPATH=.:$JAVA_HOME/jre/lib/rt.jar:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
ENV PATH $PATH:$JAVA_HOME/bin
# add apache
# Please see i4t.com && baidu.com
COPY apache-tomcat-8.5.39.tar.gz /tmp
RUN tar zxf apache-tomcat-8.5.39.tar.gz -C /usr/local && rm -rf /tmp/apache-tomcat-8.5.39.tar.gz &&
mv /usr/local/apache-tomcat-8.5.39 /usr/local/tomcat &&
rm -rf /usr/local/tomcat/webapps/docs examples host-manager manager
EXPOSE 8080
ENTRYPOINT /usr/local/tomcat/bin/startup.sh && tail -f /usr/local/tomcat/logs/catalina.out
EOF
###接下来需要下载相对应的安装包 (Dockerfile和JDK和tomcat需要放在一个目录下)
wget http://down.i4t.com/jdk1.8.0_66.tar.gz
wget http://down.i4t.com/apache-tomcat-8.5.39.tar.gz
接下来可以进行打包
docker build -t registry.cn-beijing.aliyuncs.com/abcdocker/tomcat:v2 . #如果不想自己做tomcat镜像,也可以使用dockerhub上的镜像。也可以使用我这里打包好的镜像
现在我们已经完成tomcat镜像的创建,接下来配置Jenkins,每次打包上线自动替换ROOT目录
五、编写jenkins Pipline
Jenkins pipline 语法介绍
配置gitlab账号密码
由于Jenkins需要使用gitlab账号密码,这里我们做一下免密



配置如下

创建Jenkins pipline项目

我这里添加参数化构建

接下来编写jenkins pipline脚本
node('abcdocker-slave') {
stage('Git Clone') {
checkout([$class: 'GitSCM', branches: [[name: '${git}']], doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [], userRemoteConfigs: [[credentialsId: 'gitlab_password', url: 'http://192.168.0.100/root/abcdocker-test.git']]])
}
stage('Maven Build') {
sh '''
/usr/local/maven/bin/mvn clean package -Dmaven.test.skip=true -f $WORKSPACE
echo FROM registry.cn-beijing.aliyuncs.com/abcdocker/tomcat:test2 >$WORKSPACE/target/Dockerfile
echo RUN rm -rf /usr/local/tomcat/webapps/* >>$WORKSPACE/target/Dockerfile
echo COPY hello-world-war-1.0.0 /usr/local/tomcat/webapps/ROOT >>$WORKSPACE/target/Dockerfile
'''
echo "3.Docker Build Stage"
sh '''
docker build -t registry.cn-beijing.aliyuncs.com/abcdocker/tomcat:${git} ${WORKSPACE}/target/
docker login --username=用户名 registry.cn-beijing.aliyuncs.com --password 镜像密码
docker push registry.cn-beijing.aliyuncs.com/abcdocker/tomcat:${git}
'''
}
stage('Deploy') {
echo "4. Deploy Stage"
sh'kubectl set image deployment/tomcat -n abcdocker-test my-tomcat=registry.cn-beijing.aliyuncs.com/abcdocker/tomcat:${git}'
}
}
Pipline脚本讲解
node('abcdocker-slave') {
#这里代表node节点运行在标签为abcdocker-slave上,前面已经定义了,这里就不在定义
gitlab拉取代码配置
stage('Git Clone') {
checkout([$class: 'GitSCM', branches: [[name: '${git}']], doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [], userRemoteConfigs: [[credentialsId: 'gitlab_password', url: 'http://192.168.0.100/root/abcdocker-test.git']]])
}
#这里就是一个选择git地址,然后分支。不会生成可以参考pipline语法
在下图生成的配置直接拷贝到pipline里面ji

Java maven打包步骤
我这里使用阿里云的镜像仓库,演示一下如何上传。
这里需要注意的是,我们的jenkins slave节点的Pod镜像需要使用下面我提供的版本,否则没有maven命令
registry.cn-beijing.aliyuncs.com/abcdocker/jenkins:v1.4
stage('Maven Build') {
sh '''
/usr/local/maven/bin/mvn clean package -Dmaven.test.skip=true -f $WORKSPACE
echo FROM registry.cn-beijing.aliyuncs.com/abcdocker/tomcat:test2 >$WORKSPACE/target/Dockerfile
echo RUN rm -rf /usr/local/tomcat/webapps/* >>$WORKSPACE/target/Dockerfile
echo COPY hello-world-war-1.0.0 /usr/local/tomcat/webapps/ROOT >>$WORKSPACE/target/Dockerfile
'''
echo "3.Docker Build Stage"
sh '''
docker build -t registry.cn-beijing.aliyuncs.com/abcdocker/tomcat:${git} ${WORKSPACE}/target/
docker login --username=cyh60441314 registry.cn-beijing.aliyuncs.com --password XXXX密码
docker push registry.cn-beijing.aliyuncs.com/abcdocker/tomcat:${git}
'''
}
接下来是使用kubectl set替换镜像
stage('Deploy') {
echo "4. Deploy Stage"
sh'kubectl set image deployment/tomcat -n abcdocker-test my-tomcat=registry.cn-beijing.aliyuncs.com/abcdocker/tomcat:${git}'
}
命令参数解释
kubectl set image deployment/SVC_NAME -n namespace_name container_name=images:v1 #注意后面是容器名称,并不是svc名称
完整Jenkins Pipline脚本如下
node('abcdocker-slave') {
stage('Git Clone') {
checkout([$class: 'GitSCM', branches: [[name: '${git}']], doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [], userRemoteConfigs: [[credentialsId: 'gitlab_password', url: 'http://192.168.0.100/root/abcdocker-test.git']]])
}
stage('Maven Build') {
sh '''
/usr/local/maven/bin/mvn clean package -Dmaven.test.skip=true -f $WORKSPACE
echo FROM registry.cn-beijing.aliyuncs.com/abcdocker/tomcat:test2 >$WORKSPACE/target/Dockerfile
echo RUN rm -rf /usr/local/tomcat/webapps/* >>$WORKSPACE/target/Dockerfile
echo COPY hello-world-war-1.0.0 /usr/local/tomcat/webapps/ROOT >>$WORKSPACE/target/Dockerfile
'''
echo "3.Docker Build Stage"
sh '''
docker build -t registry.cn-beijing.aliyuncs.com/abcdocker/tomcat:${git} ${WORKSPACE}/target/
docker login --username=用户名 registry.cn-beijing.aliyuncs.com --password harbor密码
docker push registry.cn-beijing.aliyuncs.com/abcdocker/tomcat:${git}
'''
}
stage('Deploy') {
echo "4. Deploy Stage"
sh'kubectl set image deployment/tomcat -n abcdocker-test my-tomcat=registry.cn-beijing.aliyuncs.com/abcdocker/tomcat:${git}'
}
}
然后jenkins我们点击保存
创建tomcat svc环境
#创建命名空间 kubectl create namespace abcdocker-test
创建tomcat deploy文件
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
labels:
name: tomcat
name: tomcat
namespace: abcdocker-test
spec:
replicas: 1
selector:
matchLabels:
name: tomcat
strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
type: RollingUpdate
template:
metadata:
creationTimestamp: null
labels:
app: tomcat
name: tomcat
spec:
containers:
- env:
- name: JAVA_OPTS
value: -server -d64 -XX:MetaspaceSize=256m
- name: MAX_HEAP
value: 512m
- name: MIN_HEAP
value: 512m
image: registry.cn-beijing.aliyuncs.com/abcdocker/tomcat:v2
imagePullPolicy: IfNotPresent
name: my-tomcat
ports:
- containerPort: 8080
protocol: TCP
resources:
limits:
memory: 1536Mi
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
创建svc
apiVersion: v1
kind: Service
metadata:
name: my-tomcat-svc
namespace: abcdocker-test
labels:
app: tomcat
spec:
selector:
app: tomcat
type: NodePort
ports:
- name: web
port: 8080
#targetPort: 8080
nodePort: 30005
创建之后我们查看状态
[root@k8s-01 ~]# kubectl get pod,svc -n abcdocker-test NAME READY STATUS RESTARTS AGE pod/tomcat-855dccd5bc-wkn5z 1/1 Running 0 27m NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/my-tomcat-svc NodePort 10.254.62.11 8080:30005/TCP 26h
访问页面,查看是否正常 (集群任意IP+30005)

项目部署
接下来我们演示一下环境部署

点击Build

我这里没有区分环境,这里也是可以区分环境的

日志分析




替换镜像

效果图

Jenkins上也可以看到每个步骤的构建时间

相关文章:
- Kubernetes 1.14 二进制集群安装
- CentOS 7 ETCD集群配置大全
- Kubenetes 1.13.5 集群二进制安装
- Kuerbernetes 1.11 集群二进制安装