kubernetes(32):持续集成(1)-k8s集群中搭建Jenkins

k8s集群中搭建Jenkins

https://www.qikqiak.com/k8s-book/docs/36.Jenkins%20Slave.html

https://help.aliyun.com/document_detail/106712.html?spm=a2c4g.11186623.6.813.626e4330lQtSFi

 

基于Kubernete的CI/CD,可以使用的工具有很多,比如Jenkins、Gitlab CI已经新兴的drone之类的,我还是最为熟悉的Jenkins来做CI/CD的工具。

Jenkins简介jenkins(一):持续集成和Jenkins简介

1 K8s集群安装jenkins

1.1 新建命名空间

添加创建一个 namespace:

kubectl create namespace kube-ops

 

1.2 创建pv-pvc

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

kubernetes(13):k8s数据持久化-pv和pvc—NFS实现

 

mkdir /data/k8s
chown -R 1000 /data/k8s  #后面有坑,Jenkins权限问题,提前执行
apiVersion: v1
kind: PersistentVolume
metadata:
  name: opspv
spec:
  capacity:
    storage: 20Gi
  accessModes:
  - ReadWriteMany
  persistentVolumeReclaimPolicy: Delete
  nfs:
    server: 10.6.76.25
    path: /data/k8s

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

 

 

[root@k8s-master ops]# kubectl  apply -f  pvc.yaml
persistentvolume/opspv created
persistentvolumeclaim/opspvc created
[root@k8s-master ops]#
[root@k8s-master ops]# kubectl get  pvc -n kube-ops
NAME     STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
opspvc   Bound    opspv    20Gi       RWX                           60s
[root@k8s-master ops]# kubectl get  pv | grep ops
opspv                                                                               20Gi       RWX            Delete           Bound        kube-ops/opspvc                                                           73s
[root@k8s-master ops]# kubectl get  pvc -n kube-ops
NAME     STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
opspvc   Bound    opspv    20Gi       RWX                           75s
[root@k8s-master ops]#

 

 

1.3 创建ServiceAccount配置权限

 

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

 

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

---

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

 

[root@k8s-master ops]# kubectl  apply -f  rbac.yaml
serviceaccount/jenkins2 created
clusterrole.rbac.authorization.k8s.io/jenkins2 created
clusterrolebinding.rbac.authorization.k8s.io/jenkins2 created

 

1.4  创建Jenkins

通过 NodePort 的形式来暴露 Jenkins 的 web 服务,固定为30002端口,另外还需要暴露一个 agent 的端口,这个端口主要是用于 Jenkins 的 master 和 slave 之间通信使用的。

 

# cat 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
      securityContext:
        fsGroup: 1000
      volumes:
      - name: jenkinshome
        persistentVolumeClaim:
          claimName: opspvc

---
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
    targetPort: agent

 

 

[root@k8s-master ops]# kubectl  apply -f  jenkins2.yaml
deployment.extensions/jenkins2 created
service/jenkins2 created
[root@k8s-master ops]#

 

创建完成后,要去拉取镜像可能需要等待一会儿,然后我们查看下 Pod 的状态

[root@k8s-master ops]# kubectl -n kube-ops get pod
NAME                       READY   STATUS             RESTARTS   AGE
jenkins2-59764f8f65-rcvh5   0/1     CrashLoopBackOff   4          4m20s
[root@k8s-master ops]#

[root@k8s-master ops]# kubectl -n kube-ops describe pod jenkins2-59764f8f65-rcvh5
    Liveness:     http-get http://:8080/login delay=60s timeout=5s period=10s #success=1 #failure=12
    Readiness:    http-get http://:8080/login delay=60s timeout=5s period=10s #success=1 #failure=12
    Environment:  <none>
    Mounts:
      /var/jenkins_home from jenkinshome (rw,path="jenkins")
      /var/run/secrets/kubernetes.io/serviceaccount from jenkins2-token-97q9h (ro)

 

#查看日志

[root@k8s-master ops]# kubectl -n kube-ops logs jenkins2-59764f8f65-rcvh5
Can not write to /var/jenkins_home/copy_reference_file.log. Wrong volume permissions?
touch: cannot touch '/var/jenkins_home/copy_reference_file.log': Permission denied
[root@k8s-master ops]#

 

没有权限在 jenkins 的 home 目录下面创建文件,这是因为默认的镜像使用的是 jenkins 这个用户,而我们通过 PVC 挂载到 nfs 服务器的共享数据目录下面却是 root 用户的,所以没有权限访问该目录,要解决该问题,也很简单,我只需要在 nfs 共享数据目录下面把我们的目录权限重新分配下即可:

chown -R 1000 /data/k8s/jenkins2

 

 

 

#再次执行

[root@k8s-master ops]# kubectl delete -f jenkins2.yaml
deployment.extensions "jenkins2" deleted
service "jenkins2" deleted
[root@k8s-master ops]# kubectl apply -f jenkins2.yaml
deployment.extensions/jenkins2 created
service/jenkins created
[root@k8s-master ops]#

 

 

#等待个2分钟

[root@k8s-master ops]# kubectl -n kube-ops get pod
NAME                       READY   STATUS    RESTARTS   AGE
jenkins2-59764f8f65-62r5w   1/1     Running   0          3m31s

 

 

 

1.5  密码登陆

任意节点的 IP:30002 端口就可以访问 jenkins 服务

 

 

 

初始化的密码我们可以在 jenkins 的容器的日志中进行查看,也可以直接在 nfs 的共享数据目录中查看:

 

[root@k8s-master ops]# cat /data/k8s/jenkins/secrets/initialAdminPassword
739b9126bc23434495a96d5d65619e70
[root@k8s-master ops]#

[root@k8s-master ops]# kubectl -n kube-ops logs jenkins2-59764f8f65-62r5w
…..
739b9126bc23434495a96d5d65619e70

This may also be found at: /var/jenkins_home/secrets/initialAdminPassword
….

 

 

然后选择安装推荐的插件即可

 

 

 

 

安装完成后添加管理员帐号即可进入到 jenkins 主界面:

 

 

 

 

 

2  k8s环境使用Jenkins优点

传统的 Jenkins Slave 一主多从方式会存在一些痛点,比如:

  • 主 Master 发生单点故障时,整个流程都不可用了
  • 每个 Slave 的配置环境不一样,来完成不同语言的编译打包等操作,但是这些差异化的配置导致管理起来非常不方便,维护起来也是比较费劲
  • 资源分配不均衡,有的 Slave 要运行的 job 出现排队等待,而有的 Slave 处于空闲状态
  • 资源有浪费,每台 Slave 可能是物理机或者虚拟机,当 Slave 处于空闲状态时,也不会完全释放掉资源。

正因为上面的这些种种痛点,我们渴望一种更高效更可靠的方式来完成这个 CI/CD 流程,而 Docker 虚拟化容器技术能很好的解决这个痛点,又特别是在 Kubernetes 集群环境下面能够更好来解决上面的问题,下图是基于 Kubernetes 搭建 Jenkins 集群的简单示意图:

 

 

 

 

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

这种方式的工作流程大致为:当 Jenkins Master 接受到 Build 请求时,会根据配置的 Label 动态创建一个运行在 Pod 中的 Jenkins Slave 并注册到 Master 上,当运行完 Job 后,这个 Slave 会被注销并且这个 Pod 也会自动删除,恢复到最初状态。

那么我们使用这种方式带来了哪些好处呢?

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

是不是以前我们面临的种种问题在 Kubernetes 集群环境下面是不是都没有了啊?看上去非常完美。

 

posted on 2019-10-18 11:05  光阴8023  阅读(1400)  评论(2编辑  收藏  举报