云原生
Kubernetes基础
容器技术介绍
Docker快速入门
Containerd快速入门
K8S主要资源罗列
认识YAML
API资源对象
Kubernetes安全掌控
Kubernetes网络
Kubernetes高级调度
Kubernetes 存储
Kubernetes集群维护
Skywalking全链路监控
ConfigMap&Secret场景应用
Kubernetes基础概念及核心组件
水平自动扩容和缩容HPA
Jenkins
k8s中部署jenkins并利用master-slave模式实现CICD
Jenkins构建过程中常见问题排查与解决
Jenkins部署在k8s集群之外使用动态slave模式
Jenkins基于Helm的应用发布
Jenkins Pipeline语法
EFKStack
EFK日志平台部署管理
海量数据下的EFK架构优化升级
基于Loki的日志收集系统
Ingress
基于Kubernetes的Ingress-Nginx解决方案
Ingress-Nginx高级配置
使用 Ingress-Nginx 进行灰度(金丝雀)发布
Ingress-nginx优化配置
APM
Skywalking全链路监控
基于Helm部署Skywalking
应用接入Skywalking
服务网格
Istio
基于Istio的微服务可观察性
基于Istio的微服务Gateway实战
Kubernetes高可用集群部署
Kuberntes部署MetalLB负载均衡器
Ceph
使用cephadm部署ceph集群
使用Rook部署Ceph存储集群
openstack
glance上传镜像失败
mariadb运行不起来
创建域和项目错误_1
创建域和项目错误_2
安装计算节点
时钟源
网络创建失败
本文档使用 MrDoc 发布
-
+
首页
k8s中部署jenkins并利用master-slave模式实现CICD
# 1、Jenkins概念 ## 1.1 前言 Jenkins 是一个开源的自动化构建工具,用于持续集成和持续交付。它可以帮助开发团队自动化构建、测试和部署软件,提高软件开发的效率和质量。Jenkins 具有以下特点: - 开源:Jenkins 是一个开源软件,可以免费使用。 - 易用:Jenkins 提供了丰富的插件和模板,可以快速地搭建自动化构建和部署流程。 - 可扩展:Jenkins 支持插件扩展,可以满足不同项目的需求。 - 支持多种编程语言:Jenkins 支持多种编程语言,如 Java、Python、Ruby 等。 - 支持多种构建工具:Jenkins 支持多种构建工具,如 Maven、Gradle、Ansible 等。 - 支持多种部署方式:Jenkins 支持多种部署方式,如 SCM 仓库、Git 仓库、SVN 仓库等。 - 支持多种测试工具:Jenkins 支持多种测试工具,如 Selenium、JUnit、SonarQube 等。 总之,Jenkins 是一个功能强大的自动化构建工具,可以帮助开发团队提高软件开发的效率和质量。 在生产环境中我们往往会在物理机或者虚拟机上部署jenkins,但是这种部署方式会有一些痛点,如下: - 主 Master 发生单点故障时,整个流程都不可用了 - 每个 Slave 的配置环境不一样,来完成不同语言的编译打包等操作,但是这些差异化的配置导致管理起来非常不方便,维护起来也是比较费劲 - 资源分配不均衡,有的 Slave 要运行的 job 出现排队等待,而有的 Slave 处于空闲状态 - 资源有浪费,每台 Slave 可能是物理机或者虚拟机,当 Slave 处于空闲状态时,也不会完全释放掉资源。 - 扩展性差:在物理机或虚拟机上部署 Jenkins,当 Jenkins 需要扩展时,需要购买更多的服务器或虚拟机,扩展性较差。 正因为上面的这些种种痛点,我们渴望一种更高效更可靠的方式来完成这个 CI/CD 流程,而 Docker容器技术能很好的解决这个痛点,又特别是在 Kubernetes 集群环境下面能够更好来解决上面的问题。 - 优势 - 基于云原生现有K8s集群来解决问题,充分的利用现有资源,无需再申请新虚机; - Slave可在构建任务来之时动态创建,工作结束后自动销毁,释放资源; - 可通过K8s原生来管理Jenkins的调度策略,防止Slave调度分配不均匀; - 通过云原生控制器来管理Jenkins配置,后期比较利于维护、扩展; - Jenkins 小概率意外宕机场景,通过K8s的机制可以自愈; - 劣势 - 增加了系统复杂度; - 有一定技术壁垒; - 实现需要时间; ## 1.2 Master-Slave 工作原理 下图是基于 Kubernetes 搭建 Jenkins 集群的简单示意图:  从图中可以看出,Jenkins Master和Jenkins Slave以Pod的形式运行在Kubernetes集群的节点上。Master运行在其中的一个节点上,并将其配置数据存储到一个Volume中,而Slave运行在各个节点上,并且它并不总是处于运行状态,它会根据需求动态地创建并自动删除。 这种方法的工作流程大致如下:当Jenkins Master收到一个构建请求时,它会根据配置的Label动态地创建一个运行在Pod中的Jenkins Slave并将其注册到Master上。在运行完Job之后,这个Slave会被注销,Pod也会自动删除,恢复到原始状态。 **设计优势** - 动态伸缩 合理的使用资源,每次运行 Job 时,会自动创建一个 Jenkins Slave,Job 完成后, Slave 自动注销并删除容器,资源自动释放,而且 Kubernetes 会根据每个资源的使用情况,动态分配 Slave 到空闲的节点上创建,降低出现因某节点资源利用率高, 还排队等待在该节点的情况。 - 服务高可用 当 Jenkins Master 出现故障时,Kubernetes 会自动创建一个新的 Jenkins Master 容器,并且将 Volume 分配给新创建的容器,保证数据不丢失,从而达到集群服务高可用。 - 扩展性好 当 Kubernetes 集群的资源严重不足而导致 Job 排队等待时,可以很容易的添加一 个 Kubernetes Node 到集群中,从而实现扩展。 # 2、Jenkins Master部署 在kubernetes中我们可以通过helm或者自定义控制器文件部署,本文我们介绍自定义控制器文件部署方式。 **Jenkins使用NFS做数据持久化** 首先部署NFS服务器 ```bash # 安装NFS软件包: # 对于Ubuntu系统,可以使用以下命令安装NFS软件包: $ sudo apt-get update $ sudo apt-get install nfs-kernel-server # 对于CentOS系统,可以使用以下命令安装NFS软件包: $ sudo yum install nfs-utils # 配置NFS共享目录: # 在NFS服务器上创建共享目录,例如: sudo mkdir /nfsshare # 将该目录设置为可共享: $ sudo chmod 777 /nfsshare # 配置NFS服务器: # 编辑NFS服务器的配置文件: $ sudo nano /etc/exports # 在该文件中添加共享目录的配置: $ /nfsshare *(rw,sync,no_root_squash) # 保存并关闭文件。 # 启动NFS服务: $ sudo systemctl start nfs-kernel-server # 设置NFS服务开机自启动: sudo systemctl enable nfs-kernel-server # 配置NFS客户端: # 在NFS客户端上安装NFS软件包: $ sudo apt-get install nfs-kernel-client # 或 $ sudo yum install nfs-utils # 验证NFS共享: $ showmount -e 192.168.1.0 ``` **创建PV、PVC,为Jenkins提供数据持久化** ```yaml $ vim jenkins-pvc.yml --- apiVersion: v1 kind: PersistentVolume metadata: name: jenkins-pv spec: capacity: storage: 10Gi accessModes: - ReadWriteMany persistentVolumeReclaimPolicy: Delete nfs: server: 10.0.1.2 # nfs服务器地址 path: /volume1/k8s/ha-nfs/jenkins # nfs共享目录 --- apiVersion: v1 kind: PersistentVolumeClaim metadata: name: jenkins-pvc namespace: devops spec: accessModes: - ReadWriteMany resources: requests: storage: 10Gi ``` > 应用Jenkins-pvc.yaml时,需要确认没有默认的storageclass,否则不会直接使用文件内的nfs路径,而是使用默认storageclass进行pvc创建 **创建角色授权** ```yaml $ jenkins-rbac.yaml apiVersion: v1 kind: ServiceAccount metadata: name: jenkins-sa namespace: devops --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: jenkins-cr 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/v1 kind: ClusterRoleBinding metadata: name: jenkins-crd roleRef: kind: ClusterRole name: jenkins-cr apiGroup: rbac.authorization.k8s.io subjects: - kind: ServiceAccount name: jenkins-sa namespace: devops ``` **在Kubernetes中部署Jenkins,新建Deployment** ```yaml $ vim jenkins-deploy.yaml --- apiVersion: apps/v1 kind: Deployment metadata: name: jenkins namespace: devops spec: selector: matchLabels: app: jenkins template: metadata: labels: app: jenkins spec: terminationGracePeriodSeconds: 10 serviceAccount: jenkins-sa 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: 2000m memory: 3Gi requests: cpu: 500m memory: 1Gi 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 mountPath: /var/jenkins_home env: - name: JAVA_OPTS value: -XshowSettings:vm -Dhudson.slaves.NodeProvisioner.initialDelay=0 -Dhudson.slaves.NodeProvisioner.MARGIN=50 -Duser.timezone=Asia/Shanghai -Dhudson.slaves.NodeProvisioner.MARGIN0=0.85 securityContext: #ifsGroup: 1000 runAsUser: 0 volumes: - name: jenkinshome persistentVolumeClaim: claimName: jenkins-pvc ``` 默认情况下,Jenkins生成代理是保守的。 例如,如果队列中有两个构建,它不会立即生成两个执行器。它将生成一个执行器,并等待某个时间释放第一个执行器,然后再决定生成第二个执行器。Jenkins确保它生成的每个执行器都得到了最大限度的利用。 如果你想覆盖这个行为,并生成一个为每个构建队列不等待的执行器,所以在Jenkins启动时候添加这些参数: - `-Dhudson.slaves.NodeProvisioner.initialDelay=0` - `-Dhudson.slaves.NodeProvisioner.MARGIN=50` - `-Dhudson.slaves.NodeProvisioner.MARGIN0=0.85` **创建集群访问入口** ```yaml $ vim jenkins-svc.yaml --- apiVersion: v1 kind: Service metadata: name: jenkins namespace: devops labels: app: jenkins spec: selector: app: jenkins type: NodePort ports: - name: web port: 8080 targetPort: 8080 nodePort: 32000 - name: agent port: 50000 targetPort: 50000 ``` **部署Jenkins** ```bash $ kubectl apply -f jenkins-rbac.yaml -f jenkins-pvc.yaml -f jenkins-deploy.yaml -f jenkins-svc.yaml ``` 初始化的密码我们可以在 jenkins 的容器的日志中进行查看,也可以通过指定数据位置查看: ```bash $ kubectl exec -it jenkins-xxxxx -ndevops -- cat /var/jenkins_home/secrets/initialAdminPassword ``` **访问** 在配置文件中,我们配置的端口类型为NodePort,所以我们使用k8s节点ip+端口即可访问 ```bash # 获取端口 $ kubectl get svc -n devops NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE jenkins NodePort 10.15.151.20 <none> 8080:32000/TCP,50000:31510/TCP 16d ``` 地址: http://nodeip:32000 # 3、Jenkins Master配置 ## 3.1 插件安装 "系统管理" --> "插件管理" --> "可选插件" ```bash # 推荐插件 Chinese 中文 Gitlab Git Parameter Extended Choice Parameter 构建job时实用插件 Docker Groovy Kubernetes Pipeline Config File Provider active choices kubernetes Continuous Deploy http request build user vars description setter Describe With Params Build Name and Description Setter Pipeline Stage View ``` > 新版本jenkins中已弃用kubernetes Continuous Deploy,可以通过从本地系统中上传插件文件进行安装,下载地址:https://oss.zhoumx.net/kubernetes_Continuous_Deploy.hpi ## 3.2 全局凭据 (unrestricted) - 1、gitlab-auth-passwd,类型:Username with password - 2、Harbor,类型:Username with password - 3、kubeconfig,类型: Secret file    **Kubeconfig**: 如果是kubeadm部署的,配置文件: ```bash $ cat /root/.kube/config ``` ## 3.3 在Jenkins上添加k8s 系统管理 --> 节点管理 --> Configure Cloud 如果jenkins是直接部署在k8s之内的,就比较简单,不需要填写证书,直接这样,如图:  如果jenkins部署在集群外,新建一个按照3.2步骤新建一个凭据,通过ingress将k8s api接口暴露,配置DNS解析只想LoadBalancer。 点击Test Connection,如果出现 Connection test successful 或图中的提示信息证明 Jenkins 已经可以和 Kubernetes 系统正常通信 # 4、Jenkins Slave部署 ## 4.1 Slave Container 设计思路 我们都知道,基于k8s拉起的服务,实际上都是以Pod形式存在的,而Pod是个容器组, 最终服务实际是以Pod内的Container来支撑运行的。那么针对Slave的应用场景, Container应该如何设计? - Jenkins-Master在构建Job的时候,Kubernetes会创建Jenkins-Slave的Pod来完成 Job的构建 - 我们选择运行Jenkins-Slave的镜像为官方推荐镜像:jenkins/inbound-agent:latest,但是这个镜像里面并没有Maven 环境,为了方便使用,我们需要自定义一个新的镜像 这里我们使用docker进行slave的构建 Dockerfile文件内容如下: ```dockerfile FROM jenkins/inbound-agent:latest # 切换到 root 账户进行操作 USER root # 安装 maven COPY apache-maven-3.3.9-bin.tar.gz . RUN tar -zxf apache-maven-3.3.9-bin.tar.gz && \ mv apache-maven-3.3.9 /usr/local && \ rm -f apache-maven-3.3.9-bin.tar.gz && \ ln -s /usr/local/apache-maven-3.3.9/bin/mvn /usr/bin/mvn && \ ln -s /usr/local/apache-maven-3.3.9 /usr/local/apache-maven && \ mkdir -p /usr/local/apache-maven/repo COPY settings.xml /usr/local/apache-maven/conf/settings.xml USER jenkins ``` 构建镜像 ```bash docker build -t jenkins-slave-maven:v1 . ``` 上传harbor仓库(公共镜像全部放在/library/下) ```bash docker tag jenkins-slave-maven:v1 harbor.zhoumx.cc/library/jenkins-slave-maven:v1 ``` 为了不侵入宿主机Node上的docker服务,那么目前最佳的思路是要引入Docker in Docker技术来完成: ```bash docker tag docker:stable harbor.zhoumx.cc/library/docker:stable ``` ## 4.2 测试验证 新建任务 脚本如下: ```groovy def git_address = "https://gitlab.zhoumx.net/kubernetes/springdemo.git" def git_auth = "d94c267d-20be-4778-bfad-2a26f568a508" //创建一个Pod的模板,label为jenkins-slave podTemplate(label: 'jenkins-slave', cloud: 'kubernetes', containers: [ containerTemplate( name: 'jnlp', image: "harbor.zhoumx.cc/library/jenkins-slave-maven:v1" ) ] ) { //引用jenkins-slave的pod模块来构建Jenkins-Slave的pod node("jenkins-slave"){ stage('拉取代码'){ checkout([$class: 'GitSCM', branches: [[name: '*/main']], extensions: [], userRemoteConfigs: [[credentialsId: "${git_auth}", url: "${git_address}"]]]) } } } ``` 执行结果 ```bash Running on jenkins-slave-r5q0z-sx1s4 in /home/jenkins/agent/workspace/test-pipeline [Pipeline] { [Pipeline] stage [Pipeline] { (拉取代码) [Pipeline] checkout The recommended git tool is: NONE using credential d94c267d-20be-4778-bfad-2a26f568a508 Cloning the remote Git repository Avoid second fetch Cloning repository https://gitlab.zhoumx.net/kubernetes/springdemo.git > git init /home/jenkins/agent/workspace/test-pipeline # timeout=10 Fetching upstream changes from https://gitlab.zhoumx.net/kubernetes/springdemo.git > git --version # timeout=10 > git --version # 'git version 2.30.2' using GIT_ASKPASS to set credentials Gitlab-user > git fetch --tags --force --progress -- https://gitlab.zhoumx.net/kubernetes/springdemo.git +refs/heads/*:refs/remotes/origin/* # timeout=10 > git config remote.origin.url https://gitlab.zhoumx.net/kubernetes/springdemo.git # timeout=10 > git config --add remote.origin.fetch +refs/heads/*:refs/remotes/origin/* # timeout=10 > git rev-parse refs/remotes/origin/main^{commit} # timeout=10 Checking out Revision 910acad93981159051ee0d095742162e1bae2ce5 (refs/remotes/origin/main) Commit message: "更新.gitlab-ci.yml文件" > git config core.sparsecheckout # timeout=10 > git checkout -f 910acad93981159051ee0d095742162e1bae2ce5 # timeout=10 > git rev-list --no-walk 910acad93981159051ee0d095742162e1bae2ce5 # timeout=10 [Pipeline] } [Pipeline] // stage [Pipeline] } [Pipeline] // node [Pipeline] } [Pipeline] // podTemplate [Pipeline] End of Pipeline Finished: SUCCESS ```
阿星
2024年1月6日 18:11
转发文档
收藏文档
上一篇
下一篇
手机扫码
复制链接
手机扫一扫转发分享
复制链接
Markdown文件
PDF文档(打印)
分享
链接
类型
密码
更新密码