k8s+jenkins+gitlab+harbor

k8s中安装jenkins

传统jenkins集群存在的问题
  • 主master发生单点故障时,整个流程都不可用
  • 每个Slave的环境配置不一样,来完成不同语言的编译打包,但是这些差异化的配置导致管理起来不方便,维护麻烦
  • 资源分配不均衡,有的slave要运行的job出现排队等待,而有的salve处于空闲状态
  • 资源有浪费,每台slave可能是物理机或者虚拟机,当slave处于空闲状态时,也不能完全释放掉资源

正因为上面的问题,我们需要采用一种更高效可靠的方式来完成这个CI/CD流程。
而Docker虚拟化容器技能很好的解决这个痛点,又特别是在Kubernetes集群环境下面能够更好来解决上面的问题。

k8s部署jenkins点评

mark

如上图我们可以看到Jenkins master和Jenkins slave以Pod形式运行在Kubernetes集群的Node。
Master运行在其中一个节点,并且将其配置数据存储到一个volume上去,slave运行在各个节点上。
但是它的状态并不是一直处于运行状态,它会按照需求动态的创建并自动删除。

  • 运行流程

    1
    2
    1、当Jenkins Master接受到Build请求后,会根据配置的Label动态创建一个运行在Pod中的Jenkins Slave并注册到Master上。
    2、当运行完Job后,这个Slave会被注销并且这个Pod也会自动删除,恢复到最初的状态(这个策略可以设置)
  • 优点:

    • 服务高可用

      1
      2
      1、当Jenkins Master出现故障时,Kubernetes会自动创建一个新的Jenkins Master容器。
      2、然后将Volume分配给新创建的容器,保证数据不丢失,从而达到集群服务高可用的作用。
    • 动态伸缩

      1
      2
      3
      1、合理使用资源,每次运行Job时,会自动创建一个Jenkins Slave。
      2、Job完成后,Slave自动注销并删除容器,资源自动释放。
      3、Kubernetes会根据每个资源的使用情况,动态分配slave到空闲的节点上创建,降低出现因某节点资源利用率高,降低出现因某节点利用率高出现排队的情况。
    • 扩展性好

      1
      当Kubernetes集群的资源严重不足导致Job排队等待时,可以很容易的添加一个k8s节点到集群,从而实现扩展。

Kubernetes安装配置Jenkins

搭建nfs服务器
1
2
3
4
5
6
7
8
9
10
11
12
1、服务端配置
yum install nfs-utils rpcbind -y
mkdir -p /data/k8s-nfs
echo "/data/k8s-nfs *(rw,no_root_squash,sync)" > /etc/exports
systemctl start rpcbind && systemctl enable rpcbind
systemctl start nfs && systemctl enable nfs
2、客户端配置
yum install nfs-utils -y
mkdir -p /data
mount -t nfs 192.168.100.2:/data/k8s-nfs /data
# 建议加入开机挂载
echo "192.168.100.2:/data/k8s-nfs /data nfs defaults 0 0" >>/etc/fstab
编写yaml文件
  • 创建命名空间

    1
    2
    kubectl create namespace jenkins-cicd
    # k8s不支持"_"下划线
  • 创建jenkins持久化存储

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
[root@master k8s-jenkins]# cat jenkins_pv.yaml 
apiVersion: v1
kind: PersistentVolume
metadata:
name: jenkins-pv
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteMany
nfs:
path: /data/k8s-nfs/jenkins_home
server: 192.168.100.2
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: jenkins-pvc
namespace: jenkins-cicd
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 10Gi
  • 创建jenkins的deployment
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
[root@master k8s-jenkins]# cat jenkins_deployment.yaml 
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: jenkins
namespace: jenkins-cicd
spec:
template:
metadata:
name: jenkins
labels:
app: jenkins
spec:
terminationGracePeriodSeconds: 5
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: 1
memory: 1Gi
requests:
cpu: 0.5
memory: 500Mi
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: jenkins-home
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
securityContext:
fsGroup: 1000
volumes:
- name: jenkins-home
persistentVolumeClaim:
claimName: jenkins-pvc
#hostPath:
#path: /data/k8s-data/jenkins_home
  • 创建serviceAccount的Jenkins用户
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
[root@master k8s-jenkins]# cat jenkins_rbac.yaml 
apiVersion: v1
kind: ServiceAccount
metadata:
name: jenkins
namespace: jenkins-cicd
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: jenkins
rules:
- apiGroups: ["extensions","apps"]
resources: ["deployments"]
verbs: ["create","delete","get","list","patch","update","watch"]
- apiGroups: [""]
resources: ["services"]
verbs: ["create","delete","get","list","patch","update","watch"]
- 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: jenkins-cicd
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: jenkins
subjects:
- kind: ServiceAccount
name: jenkins
namespace: jenkins-cicd
  • 创建jenkins svc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[root@master k8s-jenkins]# cat jenkins_svc.yaml 
apiVersion: v1
kind: Service
metadata:
name: jenkins
namespace: jenkins-cicd
labels:
app: jenkins
spec:
type: NodePort
selector:
app: jenkins
ports:
- name: web
port: 8080
targetPort: web
nodePort: 30003
- name: agent
port: 50000
targetPort: agent
  • 创建harbor仓库secret
1
2
3
4
5
kubectl create secret -n jenkins docker-registry regcred --docker-server=192.168.100.2 --docker-username=admin --docker-password=xxzx@789 --docker-email=admin@qq.com
# yaml文件中加入secret用于访问镜像仓库
imagePullSecrets:
- name: regcred
# 和containers: ,同一位置
先创建pv然后在执行rbac,再创建deployment和svc

mark

  • 登录jenkins

    1
    2
    3
    4
    # 可查看pod日志
    kubectl logs -n jenkins-cicd jenkins-5458f468bf-mw6n4
    # 也可在持久化目录下查看
    cat /data/k8s-data/jenkins_home/secrets/initialAdminPassword
  • 安装插件

    1
    2
    # 可直接安装推荐插件也可自定义安装
    需安装的插件:Kubernetes

配置动态生成slave的pod

  • 添加kubernetes
1
进入系统管理 >系统配置 下拉,找到cloud,添加kubernetes

mark

1
2
3
这里name就是配置的名称,url这里写的是k8s内部域名。
jenkins的url就是svcname+namespace+cluster.local:port。
其他配置默认即可
  • 添加jenkins slave模版
1
2
3
Name:Pod名称
Namespave:Pod命名空间
Labels:Pod标签
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# slave镜像Dockerfile
# 需提前下载好jenkins-slave
FROM centos:7
ARG AGENT_WORKDIR=/root/agent
RUN curl --create-dirs -sSLo /usr/share/jenkins/slave.jar https://repo.jenkins-ci.org/public/org/jenkins-ci/main/remoting/3.16/remoting-3.16.jar \
&& chmod 755 /usr/share/jenkins

COPY epel-apache-maven.repo /etc/yum.repos.d/epel-apache-maven.repo
RUN yum -y install git libtool-ltdl java-1.8.0-openjdk apache-maven

ENV AGENT_WORKDIR=${AGENT_WORKDIR}
COPY jenkins-slave /usr/local/bin/jenkins-slave

RUN mkdir /root/.jenkins && mkdir -p ${AGENT_WORKDIR}
ENV JAVA_HOME=/usr/lib/jvm/java-1.8.0
ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
WORKDIR /root
ENTRYPOINT ["jenkins-slave"]
1
2
# 也可使用创建好的slave镜像,下载下来上传到自己的镜像仓库
registry.cn-beijing.aliyuncs.com/abcdocker/jenkins:v1.2

mark

1
2
1、容器模版的name必须为jnlp、工作目录需为/home/jenkins/agent,否则可能无法连接上jenkins slave反复重启。
2、docker镜像这里采用的别人做好的,registry.cn-beijing.aliyuncs.com/abcdocker/jenkins:v1.4。我上传到自己的harbor镜像仓库上。
  • 添加volume卷
    1
    2
    # 镜像主要是用于kubectl,kubectl是需要kubeconfig文件,所以我们还需要创建一个volume的配置
    选择Host path volume

mark

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

mark

  • 应用并保存

添加jenkins job并测试

  • 创建一个freestyle project
  • 限制运行节点
    1
    通过上面pod模版的label限制

mark

  • 添加构建 执行shell测试

    1
    2
    3
    4
    5
    6
    7
    添加一个shell命令,测试docker和kubectl
    echo "*******测试jenkins-slave******"
    sleep 3
    echo "*******测试docker******"
    docker info
    echo "*******测试kubectl******"
    kubectl get pod -n jenkins-cicd
  • 立即构建

    1
    2
    3
    4
    返回项目,点击立即构建,观察。
    1、先拉取指定镜像,然后创建一个slave pod
    2、slave连接上后开始执行相关任务命令
    3、执行完后自动释放该pod(这里设置了执行完保留3分钟,便于观察)

mark
mark
mark

使用pipeline发布

准备代码
  • 创建测试gitlab项目
    mark
  • 客户端配置gitlab免密(本例暂未做免密)
    1
    将需免密登录的公钥配置到gitlab

mark

  • 上传测试代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    1、拉取代码
    git clone http://192.168.100.3/root/my-demo4.git
    2、下载测试代码
    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*
    3、上传代码
    git add .
    git commit -m "xxx"
    git push origin master

mark

编写tomcat Dockerfile
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
FROM centos
WORKDIR /tmp
# 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
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

# 完成tomcat镜像的创建,接下来配置Jenkins,每次打包上线自动替换ROOT目录
配置jenkins pipeline
  • 添加gitlab凭证
    mark
    mark
创建并配置pipeline项目任务
  • 创建任务
    mark

  • 添加参数化构建(需安装插件:Git Parameter Plug-In)
    mark

  • 配置触发器(可选,生产环境不建议配置)
    mark
    mark

    • 配置gitlab
      mark
      mark
      1
      点击push events如出现200则说明配置成功
  • 配置流水线(也可直接写pipeline script)
    mark

  • 应用保存

  • 代码提交上添加Jenkinsfile脚本

    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
    # 代码库上创建deploy目录及Jenkinsfile文件
    node('jenkins-slave') {
    stage('Git Clone'){
    checkout([$class: 'GitSCM', branches: [[name: '${branch}']], doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [], userRemoteConfigs: [[credentialsId: 'gitlab_root_id', url:'http://172.20.2.195:81/root/my-demo1.git']]])
    //git credentialsId: '92ae8184-9661-425d-8609-3c994ddecb21', url: 'http://192.168.100.3/root/my-demo1.git'
    }
    stage('Maven Build'){
    echo "Maven Build"
    sh '''
    /usr/local/maven/bin/mvn -B clean package -Dmaven.test.skip=true -Dautoconfig.skip -s settings.xml
    '''
    }
    stage("Docker Build Stage"){
    echo "Docker Build Stage"
    sh '''
    docker build -t 172.20.5.117/ops/tomcat:v${BUILD_NUMBER} .
    docker login --username=admin 172.20.5.117 --password "xxzx@789"
    docker push 172.20.5.117/ops/tomcat:v${BUILD_NUMBER}
    '''
    }
    stage("Deploy"){
    echo "K8s Deploy"
    sh '''
    kubectl set image -n jenkins deploy/my-tomcat my-tomcat=172.20.5.117/ops/tomcat:v${BUILD_NUMBER}
    '''
    }
    }
创建tomcat的deploy和svc
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
[root@master k8s-jenkins]# cat jenkins_tomcat.yaml 
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-tomcat
namespace: jenkins-cicd
spec:
replicas: 1
selector:
matchLabels:
app: my-tomcat
minReadySeconds: 1
progressDeadlineSeconds: 60
revisionHistoryLimit: 5
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
template:
metadata:
name: my-tomcat
labels:
app: my-tomcat
spec:
containers:
- name: my-tomcat
image: 192.168.100.2/ops/tomcat:v1
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8080
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[root@master k8s-jenkins]# cat jenkins_tomcat_svc.yaml 
apiVersion: v1
kind: Service
metadata:
name: my-tomcat
namespace: jenkins-cicd
labels:
app: my-tomcat
spec:
type: NodePort
ports:
- port: 8080
nodePort: 30004
selector:
app: my-tomcat

mark
mark

提交发布测试

  • jenkins上开始构建
    mark
  • 查看构建生成的镜像版本
    mark
    mark
  • harbor上查看镜像推送情况
    mark
  • 访问测试
    mark
-------------本文结束感谢您的阅读-------------
原创技术分享,感谢您的支持。