基于jenkins的CICD(一)

原文: 基于jenkins的CICD

参考:https://www.jenkins.io/doc/book/installing/kubernetes/

参考:https://github.com/jenkinsci/docker-inbound-agent

namespace.yml

apiVersion: v1
kind: Namespace
metadata:
  name: kube-ops

pv.yml

---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: opspvc
  namespace: jenkins
spec:
  accessModes:
    - ReadWriteMany
  storageClassName: nfs-client
  resources:
    requests:
      storage: 20Gi

rbac.yaml

定义了一个clusterrole,允许创建deployment,service和pod。进入容器,查看容器日志以及secret的权限

  • resources: ["pods/exec"] 可以进入容器以及拷贝权限。这种资源类型叫subresource
  • resources: ["pods/log"] 可以查看容器的日志
apiVersion: v1
kind: ServiceAccount
metadata:
  name: jenkins2
  namespace: jenkins

---

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: jenkins
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: jenkins2
subjects:
  - kind: ServiceAccount
    name: jenkins2
    namespace: jenkinss

deployment.yml

使用的镜像是官方的jenkins镜像:jenkins/jenkins:lts, 如果需要给镜像里面打包插件或者设置环境变量的话可以参考 https://github.com/jenkinsci/docker ,pvc挂载到/var/jenkins_home目录下面

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: jenkins2
  namespace: jenkins
spec: 
  selector:
    matchLabels:
      app: jenkins2
  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

service.yml

web端需要暴露8080端口

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

一、创建资源

正常情况下不会有什么问题,如果有,那就看日志

kubectl create -f xxx.yaml

二、访问测试

[root@master jenkins]# kubectl -n jenkins exec -it jenkins2-5d577cc67d-4g5nh -- cat /var/jenkins_home/secrets/initialAdminPassword
7e0afde2bc7d46f08d048f41218f4189
image-20210823145828366

安装推荐插件

image-20210823145951614

三、k8s部署jenkins的优势

我们知道持续构建与发布是我们日常工作中必不可少的一个步骤,目前大多公司都采用 Jenkins 集群来搭建符合需求的 CI/CD 流程,然而传统的 Jenkins Slave 一主多从方式会存在一些痛点,比如:

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

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

k8s-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 到集群中,从而实现扩展。

四、配置

配置jenkins,让jenkins可以自动创建slave的pod,去完成CI构建工作

4.1、安装kubernetes插件

首页 --> manage jenkins --> 插件管理 --> 可选插件 --> kubernetes

image-20210823150815274

4.2、configure a cloud

也就是jenkins对接kubernetes,自动创建slave,传统情况下是添加node节点

首页 --> manage jenkins --> 节点管理 --> cloud configure --> Add a new cloud --> kubernetes

image-20210823151313514

4.3、Kubernetes Cloud details

填写kubernetes的访问地址,命名空间,jenkins地址等信息

image-20210823195117678
image-20210823195320074

4.4、配置pod模板(Pod Templates)

其实就是配置 Jenkins Slave 运行的 Pod 模板

填写Pod名称,命名空间,pod标签,pod容器名字,容器镜像,工作目录,挂载docker.sock,挂载.kube目录

官方的镜像是jenkins/jnlp-slave,但是今天使用的阳明大佬制作的镜像,跑通了再换官方的镜像

image-20210823202809635
image-20210823203201209

挂载docker.sock文件到容器里面

image-20210823203259983

service account

image-20210823203539118

其他默认,保存

五、创建一个freestyle job测试

image-20210823203800465

描述信息随便写下

填写项目运行的节点标签

添加构建步骤,选择执行shell命令

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

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

保存,并点击立即构建

此时在后台可以看到k8s枣jenkins分区下面创建了一个jnlp开头的pod,随后又删除了。这是因为编译构建结束了,pod就终止了,如果想pod多停留一会儿需要修改上面配置的pod模版信息,如下

然后查看构建的日志信息

image-20210823204940712