基于 k8s 构建企业 Jenkins 持续集成平台

内容:
1、安装nfs服务器
2、部署Jenkins
3、Kubernetes 动态创建 Jenkins Slave 配置

1.安装nfs服务器

yum install -y nfs-utils

配置nfs可挂载数据目录

[root@localhost ~]# vim /etc/exports
/mnt/data/ 192.168.10.0/24(rw,sync,fsid=0)

启动nfs:

systemctl start rpcbind.service && systemctl start nfs-server.service

修改nfs 数据目录权限为777:(此处不修改,后面pod写入会有问题)

chmod 777 /mnt/data

使配置生效

[root@localhost jenkins]# exportfs -r

可以查看到已经ok

[root@localhost jenkins]# exportfs
/mnt/data/ 192.168.10.0/24

nfs服务器已经安装好了,在客户机查看nfs可挂载目录:

[root@localhost jenkins]# showmount -e 192.168.10.95
Export list for 192.168.10.95:
/mnt/data 192.168.10.0/24

2、部署Jenkins
新建kube-ops 命名空间

$ kubectl create namespace kube-ops

新建Deployment文件(jenkins2.yaml)

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: jenkins2
  namespace: kube-ops
spec:
  template:
    metadata:
      labels:
        app: jenkins2
    spec:
      terminationGracePeriodSeconds: 10
      serviceAccount: jenkins2
      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: jenkins2
          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

新建jenkins2-svc.yaml文件

apiVersion: v1
kind: Service
metadata:
  name: jenkins2
  namespace: kube-ops
  labels:
    app: jenkins2
spec:
  selector:
    app: jenkins2
  type: NodePort
  ports:
  - name: web
    port: 8080
    targetPort: web
    nodePort: 30002
  - name: agent
    port: 50000

创建PVC对象:
我们将容器的 /var/jenkins_home 目录挂载到了一个名为 opspvc 的 PVC 对象上面,所以我们同样还得提前创建一个对应的 PVC 对象,我们可以使用 StorageClass 对象来自动创建。

apiVersion: v1
kind: PersistentVolume
metadata:
  name: opspv
spec:
  capacity:
    storage: 10Gi
  accessModes:
  - ReadWriteMany
  persistentVolumeReclaimPolicy: Delete
  nfs:
    server: 192.168.10.95
    path: /mnt/data

---
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: opspvc
  namespace: kube-ops
spec:
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 10Gi

创建需要用到的 PVC 对象:

$ kubectl  create -f  opspvc.yaml

我们这里还需要使用到一个拥有相关权限的 serviceAccount:jenkins2,我们这里只是给 jenkins 赋予了一些必要的权限,当然如果你对 serviceAccount 的权限不是很熟悉的话,我们给这个 sa 绑定一个 cluster-admin 的集群角色权限也是可以的,当然这样具有一定的安全风险:(rbac.yaml)

apiVersion: v1
kind: ServiceAccount
metadata:
  name: jenkins2
  namespace: kube-ops

---

kind: Role
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  name: jenkins2
  namespace: kube-ops
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: jenkins2
  namespace: kube-ops
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: jenkins2
subjects:
  - kind: ServiceAccount
    name: jenkins2
    namespace: kube-ops

创建需要用到的 rbac对象:

$ kubectl  create -f  rbac.yaml

直接创建 Jenkins 服务:

kubectl apply -f jenkins2.yaml

创建service服务:

kubectl apply -f jenkins2-svc.yaml

查看pod:

[root@localhost jenkins]# kubectl get pod -n kube-ops
NAME                        READY   STATUS    RESTARTS   AGE
jenkins2-696b8fbdbb-vcpgx   1/1     Running   1          29m
[root@localhost jenkins]# kubectl get svc -n kube-ops
NAME       TYPE       CLUSTER-IP    EXTERNAL-IP   PORT(S)                          AGE
jenkins2   NodePort   10.68.49.91   <none>        8080:30002/TCP,50000:25192/TCP   20m
[root@localhost jenkins]# kubectl get ep jenkins2
Error from server (NotFound): endpoints "jenkins2" not found
[root@localhost jenkins]# kubectl get ep jenkins2 -n kube-ops
NAME       ENDPOINTS                            AGE
jenkins2   172.20.0.32:8080,172.20.0.32:50000   20m
[root@localhost jenkins]#

这里说明jenkins pod 初始化可能时间稍微 有点长,才会完全达到,如下状态
在这里插入图片描述
访问 node+30002端口:
基于 k8s 构建企业 Jenkins 持续集成平台_第1张图片
密码在jenkins pod 日志中:
基于 k8s 构建企业 Jenkins 持续集成平台_第2张图片
或者进入pod查看:

[root@localhost jenkins]# kubectl exec -ti  jenkins2-696b8fbdbb-vcpgx /bin/bash -n kube-ops
jenkins@jenkins2-696b8fbdbb-vcpgx:/$ cat /var/jenkins_home/secrets/initialAdminPassword
c2f9b7ed95da4257a09a51f5b2b157f3

接下来 就是初始化jenkins了 过程略。

3.Kubernetes 动态创建 Jenkins Slave 配置
基于 k8s 构建企业 Jenkins 持续集成平台_第3张图片
从图上可以看到 Jenkins Master 和 Jenkins Slave 以 Pod 形式运行在 Kubernetes 集群的 Node 上,Master 运行在其中一个节点,并且将其配置数据存储到一个 Volume 上去,Slave 运行在各个节点上,并且它不是一直处于运行状态,它会按照需求动态的创建并自动删除。
这种方式的工作流程大致为:当 Jenkins Master 接受到 Build 请求时,会根据配置的 Label 动态创建一个运行在 Pod 中的 Jenkins Slave 并注册到 Master 上,当运行完 Job 后,这个 Slave 会被注销并且这个 Pod 也会自动删除,恢复到最初状态。

那么我们使用这种方式带来了哪些好处呢?
1.服务高可用,当 Jenkins Master 出现故障时,Kubernetes 会自动创建一个新的 Jenkins Master 容器,并且将 Volume 分配给新创建的容器,保证数据不丢失,从而达到集群服务高可用。
2.动态伸缩,合理使用资源,每次运行 Job 时,会自动创建一个 Jenkins Slave,Job 完成后,Slave 自动注销并删除容器,资源自动释放,而且 Kubernetes 会根据每个资源的使用情况,动态分配 Slave 到空闲的节点上创建,降低出现因某节点资源利用率高,还排队等待在该节点的情况。
3.扩展性好,当 Kubernetes 集群的资源严重不足而导致 Job 排队等待时,可以很容易的添加一个 Kubernetes Node 到集群中,从而实现扩展。

安装kubernetes plugin,让他能够动态的生成 Slave 的 Pod。
基于 k8s 构建企业 Jenkins 持续集成平台_第4张图片
安装完毕后,点击 Manage Jenkins —> Configure System —> (拖到最下方)Add a new cloud —> 选择 Kubernetes,然后填写 Kubernetes 和 Jenkins 配置信息。
在这里插入图片描述
基于 k8s 构建企业 Jenkins 持续集成平台_第5张图片
基于 k8s 构建企业 Jenkins 持续集成平台_第6张图片
基于 k8s 构建企业 Jenkins 持续集成平台_第7张图片
注意 namespace,我们这里填 kube-ops,然后点击Test Connection,如果出现 Connection test successful 的提示信息证明 Jenkins 已经可以和 Kubernetes 系统正常通信了,然后下方的 Jenkins URL 地址:http://jenkins2.kube-ops.svc.cluster.local:8080,这里的格式为:服务名.namespace.svc.cluster.local:8080,根据上面创建的jenkins 的服务名填写,我这里是之前创建的名为jenkins,如果是用上面我们创建的就应该是jenkins2。

第3步配置 Pod Template,其实就是配置 Jenkins Slave 运行的 Pod 模板,命名空间我们同样是用 kube-ops,Labels 这里也非常重要,对于后面执行 Job 的时候需要用到该值,然后我们这里使用的是 registry.cn-beijing.aliyuncs.com/abcdocker/jenkins:v1.2 这个镜像,这个镜像加入了 kubectl 等一些实用的工具。

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
基于 k8s 构建企业 Jenkins 持续集成平台_第8张图片
另外需要注意我们这里需要在下面挂载两个主机目录,一个是 /var/run/docker.sock,该文件是用于 Pod 中的容器能够共享宿主机的 Docker,这就是大家说的 docker in docker 的方式,Docker 二进制文件我们已经打包到上面的镜像中了,另外一个目录下 /root/.kube 目录,我们将这个目录挂载到容器的 /home/jenkins/.kube 目录下面这是为了让我们能够在 Pod 的容器中能够使用 kubectl 工具来访问我们的 Kubernetes 集群,方便我们后面在 Slave Pod 部署 Kubernetes 应用。

基于 k8s 构建企业 Jenkins 持续集成平台_第9张图片
另外还有几个参数需要注意,如下图中的Time in minutes to retain slave when idle,这个参数表示的意思是当处于空闲状态的时候保留 Slave Pod 多长时间,这个参数最好我们保存默认就行了,如果你设置过大的话,Job 任务执行完成后,对应的 Slave Pod 就不会立即被销毁删除。
基于 k8s 构建企业 Jenkins 持续集成平台_第10张图片
如果jenkins提示权限错误,请在配置中添加jenkins rbac创建的serviceaccounts
在这里插入图片描述
测试:
Kubernetes 插件的配置工作完成了,接下来我们就来添加一个 Job 任务,看是否能够在 Slave Pod 中执行,任务执行完成后看 Pod 是否会被销毁。

在 Jenkins 首页点击create new jobs,创建一个测试的任务,输入任务名称,然后我们选择 Freestyle project 类型的任务:
基于 k8s 构建企业 Jenkins 持续集成平台_第11张图片
注意在下面的 Label Expression 这里要填入haimaxy-jnlp,就是前面我们配置的 Slave Pod 中的 Label,这两个地方必须保持一致
基于 k8s 构建企业 Jenkins 持续集成平台_第12张图片
然后往下拉,在 Build 区域选择Execute shell
基于 k8s 构建企业 Jenkins 持续集成平台_第13张图片
然后输入我们测试命令:

echo "测试 Kubernetes 动态生成 jenkins slave"echo "==============docker in docker==========="
docker info

echo "=============kubectl============="
kubectl get pods

基于 k8s 构建企业 Jenkins 持续集成平台_第14张图片
保存后我们直接在页面点击做成的 Build now 触发构建即可,然后观察 Kubernetes 集群中 Pod 的变化:(可以先把cnych/jenkins:jnlp 镜像pull 下来,我这边时间有点长,不然回job build会失败)

[root@localhost ~]# kubectl get pods -n kube-ops
NAME                        READY   STATUS    RESTARTS   AGE
jenkins2-696b8fbdbb-vcpgx   1/1     Running   1          135m
[root@localhost ~]#

可以看到在我们点击立刻构建的时候可以看到一个新的 Pod:jnlp-w3r3k 被创建了,这就是我们的 Jenkins Slave。任务执行完成后我们可以看到任务信息,比如我们这里是 花费了 5.2s 时间在 jnlp-w3r3k 这个 Slave上面

[root@localhost ~]# kubectl get pods -n kube-ops
NAME                        READY   STATUS              RESTARTS   AGE
jenkins2-696b8fbdbb-vcpgx   1/1     Running             1          136m
jnlp-w3r3k                  0/1     ContainerCreating   0          0s
[root@localhost ~]#

jenkins job执行结果
基于 k8s 构建企业 Jenkins 持续集成平台_第15张图片
基于 k8s 构建企业 Jenkins 持续集成平台_第16张图片
到这里证明我们的任务已经构建完成,然后这个时候我们再去集群查看我们的 Pod 列表,发现 kube-ops 这个 namespace 下面已经没有之前的 Slave 这个 Pod 了。

[root@localhost ~]# kubectl get pods -n kube-ops
NAME                        READY   STATUS    RESTARTS   AGE
jenkins2-696b8fbdbb-vcpgx   1/1     Running   1          3h12m
[root@localhost ~]# 

到这里我们就完成了使用 Kubernetes 动态生成 Jenkins Slave 的方法。

你可能感兴趣的:(jenkins,CI/CD,k8s)