在Google Kubernetes集群创建分布式Jenkins(一)

因为项目需要,在GKE的集群上需要创建一个CICD的环境,记录一下安装部署一个分布式Jenkins集群的过程。

分布式Jenkins由一个主服务器和多个Agent组成,Agent可以执行主服务器分派的任务。如下图所示:

如上图,Jenkins Agent可以运行不同的操作系统,执行主服务器分派的编译打包或测试等任务。

在Jenkins的官网上介绍了在K8S上安装的几种方式,包括了Helm, operator等。但是我直接用Helm最新版的Jenkins安装始终遇到问题,日志报错信息是Google的OAUTH插件编译有问题,如果用旧版本的可以安装成功,但是我不想用旧的版本,而新版本的问题暂时没找到解决的思路,另外Helm安装的values配置过于繁杂。我考虑用最传统的Yaml manifest的方式来部署这个Jenkins。

创建Jenkin主服务器

首先是创建namespace的manifest,配置如下:

复制代码
apiVersion: v1
kind: Namespace
metadata:
  name: "jenkins"
  labels:
    name: "jenkins"

定义Storage class的manifest

复制代码
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: storage-jenkins
provisioner: kubernetes.io/gce-pd
parameters:
  type: pd-standard
  fstype: ext4
volumeBindingMode: WaitForFirstConsumer

定义PVC,保存Jenkins的相关数据

复制代码
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: jenkins-pvc 
  namespace: jenkins
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: "storage-jenkins" 
  resources:
    requests:
      storage: 30Gi 

然后就是创建一个deployment,部署Jenkins pod,注意在containers的env里面增加了一个环境变量,定义了jenkins uri的前缀,然后在probe的path里也相应的加了/jenkins前缀。

复制代码
apiVersion: apps/v1
kind: Deployment
metadata:
  name: jenkins
  namespace: jenkins
spec:
  selector:
    matchLabels:
      app: jenkins
  replicas: 1
  template:
    metadata:
      labels:
        app: jenkins
    spec:
      volumes:
        - name: pvc-jenkins 
          persistentVolumeClaim:
            claimName: jenkins-pvc 
      containers:
      - name: jenkins
        image: "jenkins/jenkins:lts"
        securityContext:
          runAsUser: 0
        volumeMounts:
          - mountPath: "/var/jenkins_home/"
            name: pvc-jenkins
        env:
        - name: JENKINS_OPTS
          value: --prefix=/jenkins 
        ports:
        - containerPort: 8080
        - containerPort: 50000 
        resources:
          limits:
            cpu: "2"
            memory: "4Gi"
          requests:
            cpu: "1"
            memory: "2Gi"
        livenessProbe:
          httpGet:
            path: "/jenkins/login"
            port: 8080
          initialDelaySeconds: 90
          periodSeconds: 10
          timeoutSeconds: 5
          failureThreshold: 5
        readinessProbe:
          httpGet:
            path: "/jenkins/login"
            port: 8080
          initialDelaySeconds: 60
          periodSeconds: 10
          timeoutSeconds: 5
          failureThreshold: 3

创建一个service来暴露Jenkins的地址和端口,注意这里加了GCP的neg注解,使得可以使用ingress来暴露,另外指定了jenkins agent和jenkis master之间通讯的接口50000

复制代码
apiVersion: v1
kind: Service
metadata:
  name: jenkins-svc
  namespace: jenkins
  annotations:
   cloud.google.com/neg: '{"ingress": true}'
  labels:
    app: jenkins
spec:
  ports:
  - name: jenkinsport
    port: 8080
    targetPort: 8080
  - name: slaveconnectors
    port: 50000
    targetPort: 50000
  selector:
    app: jenkins

再创建一个ingress,使得可以从公网访问

复制代码
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: jenkins-ingress
  namespace: jenkins
spec:
  rules:
      - http:
         paths:
            - path: /jenkins
              pathType: Prefix
              backend:
                 service:
                     name: jenkins-svc
                     port:
                       number: 8080

最后创建一个kustomization.yaml,把以上部分组合起来

复制代码
resources:
  - namespace.yaml
  - storage-class.yaml
  - pvc.yaml
  - deployment.yaml
  - service.yaml
  - ingress.yaml

然后运行命令kubectl apply -k kustomization_dir/即可部署。等待ingress创建完成后,在gcp console的网页上找到相应的地址,在浏览器上访问即可进入jenkins的配置页面。如果不能访问,我们需要检查一下Firewall是否没有放通。

初次访问配置页面,需要输入admin password,这个可以从Jenkins pod的日志查到。

复制代码
kubectl logs <jenkins_pod_name> -n jenkins

之后我们可以安装建议的插件,然后创建一个Jenkins的用户

安装Jenkins动态Agent

有了主服务器之后,我们就可以配置动态Agent了。以下是Jenkins创建动态Agent的流程图

  1. 当触发Jenkins任务的时候,Jenkins kubernetes插件会给K8S服务器发起一个API调用,创建一个Agent pod.
  2. 这个Jenkins agent pod会运行在K8S上来运行Jenkins任务,当任务完成时会自动中止。
  3. 当Jenkins agent pod启动时,会读取环境变量配置并和Jenkins主服务器通过JNLP的方式沟通。

安装Jenkins kubernetes插件

在Jenkins的Manage Jenkins页面,选择Plugin,搜索Kubernetes并安装。安装完成后重启Jenkins

1. 连接Kubernetes

在Jenkins的Manage Jenkins->Clouds->New Cloud,类型选择Kubernetes.

然后在配置项里面,Kubernetes url和Kubernetes server certificate key都不用输入,因为我们的Jenkins master server是在同一个Kubernetes里面。Namespace输入jenkins。然后赋予jenkins这个namespace下的default用户cluster-admin的role。如以下命令:

复制代码
kubectl create clusterrolebinding jenkins --clusterrole cluster-admin --serviceaccount=jenkins:default

点击Test Connection,如果正常就会显示连接成功。

2. 配置Jenkins URL

输入Jenkins service的internal DNS的名称,因为我们之前定义了jenkins前缀,所以URL是http://jenkins-svc.jenkins.svc.cluster.local:8080/jenkins

3. 创建Pod和Container template

Pod label key设置为jenkins,value设置为slave。Pod template name输入jenkins-slave,namespace输入Jenkins,labels输入slave。Containers里面先不加container template,Jenkins默认是用Jenkins/inbound-agent这个镜像

然后需要Add volume detail,因为我们的Jenkins agent是docker in docker,所以需要选择Host path volume,然后把kubernetes节点的docker daemon socket mount到Jenkins agent pod. 在Host path和mount path分别输入/var/run/docker.sock

4. 配置Service Account

因为Jenkins agent pod需要权限来调用GCP的服务,例如推送image到repository等。需要创建一个service account来给agent使用。

首先在K8S的jenkins namespace创建一个service account

复制代码
kubectl create serviceaccount jenkins-sa --namespace jenkins

在GCP IAM里面创建一个service account

复制代码
gcloud iam service-accounts create jenkins-sa --project=PROJECT_ID

给这个service account赋予需要的role,可以用以下命令

复制代码
gcloud projects add-iam-policy-binding PROJECT_ID --member "serviceAccount:jenkins-sa@PROJECT_ID.iam.gserviceaccount.com" --role "ROLE_NAME"

把这两个service account绑定起来,使得K8S的service account可以模仿GCP的service account

复制代码
gcloud iam service-accounts add-iam-policy-binding jenkins-
sa@PROJECT_ID.iam.gserviceaccount.com --role roles/iam.workloadIdentityUser --member
"serviceAccount:PROJECT_ID.svc.id.goog[jenkins/jenkins-sa]"

Annotate K8S的service account,加上email地址

复制代码
kubectl annotate serviceaccount jenkins-sa --namespace jenkins
iam.gke.io/gcp-service-account=jenkins-sa@PROJECT_ID-
v1.iam.gserviceaccount.com

最后还要把Run as user id设为0,使得jenkins agent pod以root用户来运行,以避免权限问题。最后Save即可完成配置。

5. 设置Agent连接端口

回到Manage jenkins的manage globle security里面,在Agent的设置下选择指定端口,填入50000

运行Jenkins CICD pipeline

现在可以构建一个CICD pipeline了。下图是构建CICD的一个流程图

这里面包括了几个步骤

  1. 往Git仓库提交代码,通过hook触发jenkins任务
  2. Jenkins启动agent pod,运行打包任务
  3. 把打包的镜像推送到registry
  4. 更新部署的manifest,更新镜像的标签
  5. 把应用部署到GKE

我这里将扩展一下这些步骤,增加Jenkin和Github webhook的集成,使得用户提交PR的时候自动触发UT检查,然后当用户执行merge操作的时候来触发打包任务。这部分的内容比较多,我将在下一篇博客中记录具体的操作过程。

现在我将建立一个简单的任务,测试一下我们配置的Jenkins是否能正常工作。

在主页面选择新建任务,然后选择pipeline,然后在pipeline script里面输入以下内容

Groovy 复制代码
pipeline {
  agent{
    kubernetes{
        label 'slave'
    }
  }
  environment {
    ZONE = "us-central1"
    PROJECT_ID = "curious-athlete-401708"
  }

  stages{      
    stage("test"){
      steps{
        script{
          sh 'echo "testing"'
        }
      }
    }
  }
}

运行这个任务,我们可以看到页面会显示自动创建一个jenkins-slave-xxxx的pod,然后在这个pod上运行我们的pipeline。运行结束之后我们可以查看Job的console log,可以看到成功执行了pipeline里面定义的echo testing

可见我们已经成功配置了一个Kubernetes上的分布式jenkins环境。

相关推荐
一勺菠萝丶1 小时前
Redis实现分布式锁的进阶版:Redisson实战指南
数据库·redis·分布式
不爱学英文的码字机器3 小时前
容器编排的革命:Kubernetes如何引领IT的云原生时代
云原生·容器·kubernetes
特立独行的猫a3 小时前
鸿蒙HarmonyOS多设备流转:分布式的智能协同技术介绍
分布式·华为·harmonyos·碰一碰·跨设备流转
SizeTheMoment5 小时前
RabbitMQ Topic & RPC
分布式·rabbitmq
好想有猫猫6 小时前
【Redis】List 列表
数据库·c++·redis·分布式·缓存·list
Themberfue9 小时前
RabbitMQ ⑤-顺序性保障 || 消息积压 || 幂等性
java·分布式·消息队列·rabbitmq
fashia9 小时前
Java转Go日记(三十六):简单的分布式
开发语言·分布式·后端·zookeeper·golang·go
Aurora_NeAr9 小时前
Redis设计与实现——分布式Redis
数据库·redis·分布式
好吃的肘子10 小时前
kafka入门(二)
分布式·kafka·linq
TPBoreas15 小时前
Jenkins服务器配置密钥对
运维·服务器·jenkins