首页 > 其他分享 >最详细的k8s中部署Jenkins教程+基于jenkins+k8s(container)实现CI/CD

最详细的k8s中部署Jenkins教程+基于jenkins+k8s(container)实现CI/CD

时间:2024-05-29 09:31:50浏览次数:11  
标签:CI container name demo boot Jenkins jenkins k8s root

k8s中部署Jenkins

系统环境:

  • • kubernetes 版本:1.23.3

  • • jenkins 版本:2.172

  • • jenkins 部署示例文件 Github 地址:https://github.com/my-dlq/blog-example/tree/master/jenkins-deploy

一、设置存储目录

在 Kubenetes 环境下所起的应用都是一个个 Docker 镜像,为了保证应用重启的情况下数据安全,所以需要将 Jenkins 持久化到存储中。这里用的是 NFS 网路存储,方便在 Kubernetes 环境下应用启动节点转义数据一致。当然也可以选择存储到本地,但是为了保证应用数据一致,需要设置 Jenkins 固定到某一 Kubernetes 节点。

1、安装 NFS 服务端

详情请看 CentOS7 搭建 nfs 服务器 ,这里不过多叙述。

2、挂载 NFS 并设置存储文件夹

如果不能直接操作 NFS 服务端创建文件夹,需要知道 NFS 服务器地址,然后将其挂在到本地目录,进入其中创建 Jenkins 目录空间。

(1)、挂载 NFS

$ mount -o vers=4.1 192.168.2.11:/nfs/ /nfs

(2)、在 NFS 共享存储文件夹下创建存储 Jenkins 数据的文件夹

$ mkdir -p /nfs/data/jenkins

二、创建 PV & PVC

创建 PV 绑定 NFS 创建的 Jenkins 目录,然后创建 PVC 绑定这个 PV,将此 PVC 用于后面创建 Jenkins 服务时挂载的存储。

1、准备 PV & PVC 部署文件

一定要确保 PV 的空间大于 PVC,否则无法关联

jenkins-pv-pvc.yaml

apiVersion: v1
kind: PersistentVolume
metadata:
  name: jenkins
  labels:
    app: jenkins
spec:
  capacity:          
    storage: Gi
  accessModes:       
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain  
  mountOptions:   #NFS挂在选项
    - hard
    - nfsvers=4.1    
  nfs:            #NFS设置
    path: /nfs/data/jenkins   
    server: 192.168.2.11
---
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: jenkins
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: Gi   #生产环境空间一定要设置比较大点
  selector:
    matchLabels:
      app: jenkins
2、创建 PV & PVC

提前将 namespace 修改成你自己的 namespace

  • • -n:指定 namespace

$ kubectl apply -f jenkins-pv-pvc.yaml -n public

三、创建 ServiceAccount & ClusterRoleBinding

此 kubernetes 集群用的是 RBAC 安全插件,必须创建权限给一个 ServiceAccount,然后将此 ServiceAccount 绑定到 Jenkins 服务,这样赋予 Jenkins 服务一定权限执行一些操作,为了方便,这里将 cluster-admin 绑定到 ServiceAccount 以保证 Jenkins 能拥有一定的权限。

注意:请提前修改 yaml 中的 namespace

(1)、jenkins-rbac.yaml

apiVersion: v1
kind: ServiceAccount
metadata:
  name: jenkins-admin       #ServiceAccount名
  namespace: mydlqcloud     #指定namespace,一定要修改成你自己的namespace
  labels:
    name: jenkins
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  name: jenkins-admin
  labels:
    name: jenkins
subjects:
  - kind: ServiceAccount
    name: jenkins-admin
    namespace: mydlqcloud
roleRef:
  kind: ClusterRole
  name: cluster-admin
  apiGroup: rbac.authorization.k8s.io

(2)、创建 RBAC 命令

$ kubectl create -f jenkins-rbac.yaml

四、创建 Service & Deployment

这里开始部署 Jenkins 服务,创建 Service 与 Deployment,其中 Service 暴露两个接口 80880 与 50000。而 Deployment 里面要注意的是要设置上面创建的 ServiceAccount ,并且设置容器安全策略为“runAsUser: 0”以 Root 权限运行容器,而且暴露8080、50000两个端口。

1、创建 Service & Deployment 部署文件

jenkins-deployment.yaml

apiVersion: v1
kind: Service
metadata:
  name: jenkins
  labels:
    app: jenkins
spec:
  type: NodePort
  ports:
  - name: http
    port:           #服务端口
    targetPort: 
    nodePort:   #NodePort方式暴露 Jenkins 端口
  - name: jnlp
    port:          #代理端口
    targetPort: 
    nodePort: 
  selector:
    app: jenkins
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: jenkins
  labels:
    app: jenkins
spec:
  selector:
    matchLabels:
      app: jenkins
  replicas: 
  template:
    metadata:
      labels:
        app: jenkins
    spec:
      serviceAccountName: jenkins-admin
      containers:
      - name: jenkins
        image: registry.cn-shanghai.aliyuncs.com/mydlq/jenkins:2.172
        securityContext:                     
          runAsUser:        #设置以ROOT用户运行容器
          privileged: true   #拥有特权
        ports:
        - name: http
          containerPort: 
        - name: jnlp
          containerPort: 
        resources:
          limits:
            memory: Gi
            cpu: "1000m"
          requests:
            memory: Gi
            cpu: "500m"
        env:
        - name: LIMITS_MEMORY
          valueFrom:
            resourceFieldRef:
              resource: limits.memory
              divisor: Mi
        - name: "JAVA_OPTS"  #设置变量,指定时区和 jenkins slave 执行者设置
          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
                 "    
        - name: "JENKINS_OPTS"
          value: "--prefix=/jenkins"         #设置路径前缀加上 Jenkins
        volumeMounts:                        #设置要挂在的目录
        - name: data
          mountPath: /var/jenkins_home
      volumes:
      - name: data
        persistentVolumeClaim:
          claimName: jenkins      #设置PVC

参数说明

  • • JAVA_OPTS: JVM 参数设置

  • • JENKINS_OPTS: Jenkins 参数设置

  • • 设置执行任务时候不等待:

默认情况下,Jenkins生成代理是保守的。例如,如果队列中有两个构建,它不会立即生成两个执行器。它将生成一个执行器,并等待某个时间释放第一个执行器,然后再决定生成第二个执行器。Jenkins确保它生成的每个执行器都得到了最大限度的利用。如果你想覆盖这个行为,并生成一个执行器为每个构建队列立即不等待,所以在Jenkins启动时候添加这些参数:

  • • -Dhudson.slaves.NodeProvisioner.initialDelay=0

  • • -Dhudson.slaves.NodeProvisioner.MARGIN=50

  • • -Dhudson.slaves.NodeProvisioner.MARGIN0=0.85

2、部署 Jenkins

执行 Kuberctl 命令将 Jenkins 部署到 Kubernetes 集群。

注意:将“-n”后面的 namespace 换成你自己的 namespace

  • • -n:指定应用启动的 namespace

$ kubectl create -f jenkins-deployment.yaml -n mydlqcloud

五、获取 Jenkins 生成的 Token

在安装 Jenkins 时候,它默认生成一段随机字符串,用于安装验证。这里访问它的安装日志,获取它生成的 Token 字符串。

(1)、查看 Jenkins Pod 启动日志

注意:这里“-n”指的是要 namespace,后面跟的 namespace 请替换成你jenkins 启动的 namespace

$ kubectl log $(kubectl get pods -n mydlqcloud | awk '{print $1}' | grep jenkins) -n mydlqcloud

(2)、查看日志中生成的 Token 字符串

查看日志,默认给的token为:

*************************************************************
*************************************************************
*************************************************************
Jenkins initial setup is required. An admin user has been created and a password generated.
Please use the following password to proceed to installation:

3b3e0dda9d6746358ade987775f924ef

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

六、启动 Jenkins 进行安装

输入集群地址和 Jenkins Service 提供的 NodePort 端口,访问 Jenkins 进行安装步骤,可以按下一步步执行:

1、进入Jenkins

输入集群地址和上面设置的Nodeport方式的端口 32001,然后输入上面获取的 Token 字符串。例如,本人的集群IP为 192.168.2.11 ,所以就可以访问 http://192.168.2.11:32001/jenkins ,进入后可以看到下面的界面。

图片

2、安装插件

选择自定义插件来进行安装

图片

安装一些常用的插件,这里可以选择一下,推荐安装下面插件。

图片

图片

图片

确定后可以看到正在安装插件界面

图片

3、设置用户名、密码

图片

然后进入 Jenkins 界面

图片

七、其他

1、插件因网络问题安装失败

如果 Jenkins 安装插件失败,则可以按一下设置

  • • (1)、进入地址 http://192.168.2.11:32001/jenkins/pluginManager/advanced

  • • (2)、将最下面的 Update Site 的 URL 地址替换成:http://mirror.esuni.jp/jenkins/updates/update-center.json

  • • (3)、点“submit”按钮,然后点右下角角 “check now”

  • • (4)、然后输入地址 http://192.168.2.11:32001/jenkins/restart 重启 jenkins 后再重新安装插件

PS:上面地址替换成你们的集群地址及端口。

图片

图片

基于jenkins+k8s(container)实现CI/CD

CI/CD是什么

CI(持续集成)

持续集成是一种软件开发实践,通过自动化工具对代码进行编译、测试和打包,减少人工干预,提高构建效率。它的核心理念是将代码频繁地集成到共享存储库中,并通过自动化构建和测试流程来验证代码的正确性。这样做可以为开发人员提供即时的反馈,帮助他们快速定位并修复问题,从而加速软件开发周期并提高软件质量。

CD(持续交付)

持续交付是一种软件开发实践,通过自动化工具建立一套自动化的流水线,将应用程序部署到不同的环境中,例如开发环境、测试环境和生产环境等。这种流水线能够自动化地执行构建、测试、部署和发布等步骤,使得软件可以在不同环境中快速、可靠地交付给最终用户。持续交付的目标是确保软件可以随时随地以可靠的方式交付给用户,从而缩短交付周期、降低发布风险。

CI/CD的关系

CI和CD是相互关联的两个概念,持续集成是持续交付的基础,只有实现了持续集成,才能够实现持续交付。持续集成提高了代码的质量和可维护性,为持续交付提供了更好的基础;而持续交付则能够更快地将代码交付给用户,从而促进持续集成的实施。这两者相辅相成,共同推动着软件开发过程的持续改进和交付效率的提升。

CI/CD项目简介

利用 Jenkins、SonarQube、Harbor、Container、Kubernetes技术,搭建一个完整的 CI/CD 管道,模拟实际生产环境项目开发部署流程,实现持续集成、持续交付和持续部署。通过自动化构建、测试、代码质量检查和容器化部署,将开发人员从繁琐的手动操作中解放出来,提高团队的开发效率、软件质量和安全性,实现持续更新迭代和持续部署交付。

CI/CD流程图

图片

jenkins-jenkins for k8s.drawio.png

流程说明

开发测试阶段

  1. 开发人员需求确定后,从master分支拉取最新代码,在dev分支完成开发后,将dev分支合并到test分支并推送至gitlab仓库。

  2. Gitlab监听到test分支代码更新,请求jenkins的webhook地址,触发持续构建和持续部署流程,准备将代码部署至测试环境。

  3. Jenkins从Gitlab中拉取项目源码,编译并打成jar包,然后调用SonarQube完成代码扫描。

  4. 扫描完成调用docker或者container打包成容器镜像,并推送至Harbor镜像仓库。

  5. jenkins修改yaml文件,将资源部署至测试环境。

  6. Jenkins完成测试环境CICD流程后,将结果邮件通知给开发运维和测试人员。

  7. 测试人员访问测试环境,功能验证无误后反馈给开发主管,至此开发测试阶段完成。

生产发布阶段

  1. 开发主管将test分支代码合并至master分支,并推送至gitlab仓库。

  2. gitlab监听到master分支代码更改,请求jenkins的webhook地址,开始触发生产环境cicd流程,使用k8s滚动更新项目版本。

  3. jenkins完成cicd流程后自动发送邮件通知,用户访问新版本服务。

项目代码仓库地址

gitee:https://gitee.com/cuiliang0302/sprint_boot_demo

github:https://github.com/cuiliang0302/sprint-boot-demo

Jenkins动态slave介绍

为什么需要动态slave

  1. 配置管理困难:不同项目可能使用不同的编程语言、框架或库,这导致了每个Slave的配置环境各不相同。因此,需要动态Slave能够根据不同的项目需求,灵活配置不同的运行环境,从而简化配置管理和维护工作。

  2. 资源分配不均衡:在使用静态Slave时,可能会出现某些Slave处于空闲状态,而其他Slave却处于繁忙状态,导致资源分配不均衡。动态Slave可以根据当前任务的需求自动调配资源,使得任务能够在空闲的Slave上尽快执行,从而提高资源利用率和任务执行效率。

  3. 资源浪费:静态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 也会自动删除,恢复到最初状态。

服务部署

本项目所有服务均运行在k8s集群上,使用nfs共享存储,具体部署配置过程可参考下文,此处不再赘述。

k8s部署

参考文档:https://www.cuiliangblog.cn/detail/section/15287639

nfs共享存储部署

参考文档:https://www.cuiliangblog.cn/detail/section/116191364

container部署

参考文档:https://www.cuiliangblog.cn/detail/section/99861101

harbor部署

参考文档:https://www.cuiliangblog.cn/detail/section/99861101

gitlab部署

参考文档:https://www.cuiliangblog.cn/detail/section/131418586

jenkins部署

参考文档:https://www.cuiliangblog.cn/detail/section/131416735

SonarQube部署

参考文档:https://www.cuiliangblog.cn/detail/section/165547985

项目与权限配置

Harbor配置

创建项目Harbor的项目分为公开和私有的: 公开项目:所有用户都可以访问,通常存放公共的镜像,默认有一个library公开项目。私有项目:只有授权用户才可以访问,通常存放项目本身的镜像。我们可以为微服务项目创建一个新的项目

图片

创建用户创建一个普通用户cuiliang。

图片

配置项目用户权限在spring_boot_demo项目中添加普通用户cuiliang,并设置角色为开发者。

图片

权限说明

角色权限
访客对项目有只读权限
开发人员对项目有读写权限
维护人员对项目有读写权限、创建webhook权限
项目管理员出上述外,还有用户管理等权限

上传下载镜像测试可参考文章https://www.cuiliangblog.cn/detail/section/15189547,此处不再赘述。

gitlab项目权限配置

具体gitlab权限配置参考文档:https://www.cuiliangblog.cn/detail/section/131513569

创建开发组develop,用户cuiliang,项目springboot demo创建组管理员用户登录,创建群组,组名称为develop,组权限为私有

图片

创建项目创建sprint boot demo项目,并指定develop,项目类型为私有

图片

创建用户创建一个普通用户cuiliang

图片

用户添加到组中将cuiliang添加到群组develop中,cuiliang角色为Developer

图片

配置分支权限

图片

用户权限验证使用任意一台机器模拟开发人员拉取代码,完成开发后推送至代码仓库。拉取仓库代码

[root@tiaoban opt]# git clone https://gitee.com/cuiliang0302/sprint_boot_demo.git
正克隆到 'sprint_boot_demo'...
remote: Enumerating objects: 69, done.
remote: Counting objects: 100% (69/69), done.
remote: Compressing objects: 100% (54/54), done.
remote: Total 69 (delta 15), reused 0 (delta 0), pack-reused 0
接收对象中: 100% (69/69), 73.15 KiB | 1.49 MiB/s, 完成.
处理 delta 中: 100% (15/15), 完成.
[root@tiaoban opt]# cd sprint_boot_demo/
[root@tiaoban sprint_boot_demo]# ls
email.html  Jenkinsfile  LICENSE  mvnw  mvnw.cmd  pom.xml  readme.md  sonar-project.properties  src  test
推送至gitlab仓库
# 修改远程仓库地址
[root@tiaoban sprint_boot_demo]# git remote set-url origin http://gitlab.local.com/develop/sprint_boot_demo.git
[root@tiaoban sprint_boot_demo]# git remote -v
origin  http://gitlab.local.com/develop/sprint_boot_demo.git (fetch)
origin  http://gitlab.local.com/develop/sprint_boot_demo.git (push)
# 推送代码至gitlab
[root@tiaoban sprint_boot_demo]# git push --set-upstream origin --all
Username for 'http://gitlab.local.com': cuiliang
Password for 'http://[email protected]': 
枚举对象中: 55, 完成.
对象计数中: 100% (55/55), 完成.
使用 4 个线程进行压缩
压缩对象中: 100% (34/34), 完成.
写入对象中: 100% (55/55), 71.51 KiB | 71.51 MiB/s, 完成.
总共 55(差异 10),复用 52(差异 9),包复用 0
To http://gitlab.local.com/develop/sprint-boot-demo.git
 * [new branch]      main -> main
分支 'main' 设置为跟踪 'origin/main'。

查看验证

图片

jenkins配置

插件安装与配置

GitLab插件安装与配置:

https://www.cuiliangblog.cn/detail/section/127410630

SonarQube Scanner插件安装与配置:

https://www.cuiliangblog.cn/detail/section/165534414

Kubernetes插件安装与配置:

https://www.cuiliangblog.cn/detail/section/127230452

Email Extension邮件推送插件安装与配置:

https://www.cuiliangblog.cn/detail/section/133029974

Version Number版本号插件安装与配置:

https://plugins.jenkins.io/versionnumber/

Content Replace文件内容替换插件安装与配置:

https://plugins.jenkins.io/content-replace/

jenkins slave镜像制作

安装完Kubernetes插件后,默认的slave镜像仅包含一些基础功能和软件包,如果需要构建镜像,执行kubectl命令,则需要引入其他container或者自定义slave镜像。 

关于镜像构建问题,如果k8s容器运行时为docker,可以直接使用docker in docker方案,启动一个docker:dind容器,通过Docker pipeline插件执行镜像构建与推送操作,具体内容可参考https://www.cuiliangblog.cn/detail/section/166573065。 

如果k8s容器运行时为container,则使用nerdctl+buildkitd方案,启动一个buildkit容器,通过nerdctl命令执行镜像构建与推送操作,具体内容可参考https://www.cuiliangblog.cn/detail/section/167380911。本次实验以container环境为例,通过nerdctl+buildkitd方案演示如何构建并推送镜像。

构建jenkins-slave镜像

[root@tiaoban jenkins]# cat Dockerfile 
FROM jenkins/inbound-agent:latest-jdk17
USER root
COPY kubectl /usr/bin/kubectl
COPY nerdctl /usr/bin/nerdctl
COPY buildctl /usr/bin/buildctl
[root@tiaoban jenkins]# 
[root@tiaoban jenkins]# docker build -t harbor.local.com/cicd/jenkins-slave:v1.0 .

测试jenkins-slave镜像构建容器与操作k8s

以下操作在k8s集群master机器,容器运行时为container节点执行测试

# 启动buildkit镜像构建服务
# 挂载/run/containerd/containerd.sock方便container调用buildkitd
# 挂载/var/lib/buildkit,以便于将构建过程中下载的镜像持久化存储,方便下次构建时使用缓存
# 挂载/run/buildkit/目录方便nerctl调用buildkitd
[root@master3 ~]# nerdctl run --name buildkit -d --privileged=true \
-v /run/buildkit/:/run/buildkit/ \
-v /var/lib/buildkit:/var/lib/buildkit \
-v /run/containerd/containerd.sock:/run/containerd/containerd.sock \
moby/buildkit:v0.13.2
[root@master3 ~]# nerdctl ps
CONTAINER ID    IMAGE                    COMMAND        CREATED          STATUS    PORTS    NAMES
a8de5299dd84    moby/buildkit:v0.13.2    "buildkitd"    4 seconds ago    Up                 buildkit
# 启动jenkins-slave容器
# 挂载/run/containerd/containerd.sock方便netdctl操作container
# 挂载/run/buildkit/目录方便nerctl调用buildkitd构建镜像
# 挂载/root/.kube/目录方便kubectl工具操作k8s
[root@master3 ~]# nerdctl run --name jenkins-slave -it --privileged=true \
-v /run/buildkit/:/run/buildkit/ \
-v /root/.kube/:/root/.kube/ \
-v /run/containerd/containerd.sock:/run/containerd/containerd.sock \
harbor.local.com/cicd/jenkins-slave:v1.0 bash
# 测试container管理
root@28dcd3a667c9:/home/jenkins# nerdctl ps
CONTAINER ID    IMAGE                                       COMMAND                   CREATED           STATUS    PORTS    NAMES
28dcd3a667c9    harbor.local.com/cicd/jenkins-slave:v1.0    "/usr/local/bin/jenk…"    11 seconds ago    Up                 jenkins-slave
a8de5299dd84    harbor.local.com/cicd/buildkit:v0.13.2      "buildkitd"               11 minutes ago    Up                 buildkit
# 测试k8s管理
root@28dcd3a667c9:/home/jenkins# kubectl get node
NAME      STATUS   ROLES           AGE    VERSION
master1   Ready    control-plane   241d   v1.27.6
master2   Ready    control-plane   241d   v1.27.6
master3   Ready    control-plane   241d   v1.27.6
work1     Ready    <none>          241d   v1.27.6
work2     Ready    <none>          241d   v1.27.6
work3     Ready    <none>          241d   v1.27.6
# 测试镜像构建
root@28dcd3a667c9:/home/jenkins# echo 'FROM busybox' >> Dockerfile
root@28dcd3a667c9:/home/jenkins# echo 'CMD ["echo","hello","container"]' >> Dockerfile
root@28dcd3a667c9:/home/jenkins# cat Dockerfile 
FROM busybox
CMD ["echo","hello","container"]
root@28dcd3a667c9:/home/jenkins# nerdctl build -t test:v1 .
root@28dcd3a667c9:/home/jenkins# nerdctl images
REPOSITORY                             TAG        IMAGE ID        CREATED           PLATFORM       SIZE         BLOB SIZE
test                                   v1         4943762c7956    7 seconds ago     linux/amd64    4.1 MiB      2.1 MiB
harbor.local.com/cicd/buildkit         v0.13.2    c3cb08891c15    15 minutes ago    linux/amd64    190.3 MiB    87.2 MiB
harbor.local.com/cicd/jenkins-slave    v1.0       1d5c5b1572bc    6 minutes ago     linux/amd64    384.7 MiB    169.8 MiB

job任务创建与配置

配置webhook构建触发器,当分支代码提交时触发构建,具体配置如下:

图片

流水线选择SCM从代码仓库中获取jenkinsfile,脚本路径填写Jenkinsfile-k8s.groov

图片

效果演示

开发测试阶段

模拟开发人员完成功能开发后提交代码至test分支

[root@tiaoban sprint_boot_demo]# git checkout -b test  origin/test
分支 'test' 设置为跟踪 'origin/test'。
切换到一个新分支 'test'
[root@tiaoban sprint_boot_demo]# git branch -a
  master
* test
  remotes/origin/HEAD -> origin/master
  remotes/origin/master
  remotes/origin/test
# 修改SpringBoot首页内容为Version:v2
[root@tiaoban sprint_boot_demo]# cat src/main/java/com/example/springbootdemo/HelloWorldController.java
package com.example.springbootdemo;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class HelloWorldController {
    @RequestMapping("/")
    @ResponseBody
    public String hello() {
        return "<h1>Hello SpringBoot</h1><p>Version:v2 Env:test</p>";
    }
    @RequestMapping("/health")
    @ResponseBody
    public String healthy() {
        return "ok";
    }
}
[root@tiaoban sprint_boot_demo]# git add .
[root@tiaoban sprint_boot_demo]# git commit -m "test环境更新版本至v2"
[test 68fb576] test环境更新版本至v2
 1 file changed, 1 insertion(+), 1 deletion(-)
[root@tiaoban sprint_boot_demo]# git push 
枚举对象中: 17, 完成.
对象计数中: 100% (17/17), 完成.
使用 4 个线程进行压缩
压缩对象中: 100% (6/6), 完成.
写入对象中: 100% (9/9), 707 字节 | 707.00 KiB/s, 完成.
总共 9(差异 2),复用 0(差异 0),包复用 0
remote: 
remote: To create a merge request for test, visit:
remote:   http://gitlab.local.com/develop/sprint_boot_demo/-/merge_requests/new?merge_request%5Bsource_branch%5D=test
remote: 
To http://gitlab.local.com/develop/sprint_boot_demo.git
   86a166a..68fb576  test -> test

此时查看cicd名称空间下的pod信息,发现已经创建一个名为springbootdemo-275-rf832-h6jkq-630x8的pod,包含3个container,分别是jnlp、maven、buildkitd。

[root@tiaoban ~]# kubectl get pod -n cicd
NAME                                   READY   STATUS    RESTARTS        AGE
gitlab-5997c5cdcd-2rvgz                1/1     Running   14 (100m ago)   15d
jenkins-6df7d6479b-v25rt               1/1     Running   9 (100m ago)    5d13h
sonarqube-postgresql-0                 1/1     Running   14 (100m ago)   15d
sonarqube-sonarqube-0                  1/1     Running   14 (100m ago)   15d
springbootdemo-275-rf832-h6jkq-630x8   3/3     Running   0               65s

查看jenkins任务信息,已顺利完成了集成部署工作。

图片

并且收到了jenkins自动发出的邮件,内容如下:

图片

查看SonarQube代码扫描信息,未发现异常代码。

图片

查看k8s,已成功创建相关资源。

[root@tiaoban sprint_boot_demo]# kubectl get all -n test
NAME                        READY   STATUS    RESTARTS   AGE
pod/demo-5d44f794d9-s7jw2   1/1     Running   0          7m38s

NAME           TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE
service/demo   ClusterIP   10.111.167.204   <none>        8888/TCP   4d3h

NAME                   READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/demo   1/1     1            1           4d3h

NAME                              DESIRED   CURRENT   READY   AGE
replicaset.apps/demo-5d44f794d9   1         1         1       7m38s

此时模拟测试人员,访问测试环境域名

图片

至此,开发测试阶段演示完成。

生产发布阶段

接下来演示master分支代码提交后,触发生产环境版本发布流程。

[root@tiaoban sprint_boot_demo]# git branch -a
  master
* test
  remotes/origin/HEAD -> origin/master
  remotes/origin/master
  remotes/origin/test
[root@tiaoban sprint_boot_demo]# git checkout master
切换到分支 'master'
您的分支与上游分支 'origin/master' 一致。
[root@tiaoban sprint_boot_demo]# vim src/main/java/com/example/springbootdemo/HelloWorldController.java
[root@tiaoban sprint_boot_demo]# cat src/main/java/com/example/springbootdemo/HelloWorldController.java
package com.example.springbootdemo;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class HelloWorldController {
    @RequestMapping("/")
    @ResponseBody
    public String hello() {
        return "<h1>Hello SpringBoot</h1><p>Version:v2 Env:prod</p>";
    }
    @RequestMapping("/health")
    @ResponseBody
    public String healthy() {
        return "ok";
    }
}
[root@tiaoban sprint_boot_demo]# git add .
[root@tiaoban sprint_boot_demo]# git commit -m "生产环境更新版本至v2"
[master 889fc5c] 生产环境更新版本至v2
 1 file changed, 1 insertion(+), 1 deletion(-)
[root@tiaoban sprint_boot_demo]# git push
枚举对象中: 17, 完成.
对象计数中: 100% (17/17), 完成.
使用 4 个线程进行压缩
压缩对象中: 100% (6/6), 完成.
写入对象中: 100% (9/9), 719 字节 | 719.00 KiB/s, 完成.
总共 9(差异 2),复用 0(差异 0),包复用 0
To http://gitlab.local.com/develop/sprint_boot_demo.git
   600a1b6..889fc5c  master -> master
待收到邮件通知后,查看k8s资源,已经在prod名称空间下创建相关资源

[root@tiaoban sprint_boot_demo]# kubectl get all -n prod
NAME                        READY   STATUS    RESTARTS   AGE
pod/demo-7c57975bd8-7nmnx   1/1     Running   0          41s

NAME           TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)    AGE
service/demo   ClusterIP   10.97.0.219   <none>        8888/TCP   41s

NAME                   READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/demo   1/1     1            1           41s

NAME                              DESIRED   CURRENT   READY   AGE
replicaset.apps/demo-7c57975bd8   1         1         1       41s

此时访问生产环境域名,服务可以正常访问。

图片

此时查看Harbor仓库镜像信息,其中p开头的为生产环境镜像,t开头的为测试环境镜像。

图片

jenkinsfile

完整的jenkinsfile如下所示,由于每个项目使用的开发语言和版本各不相同,因此建议将jenkinsfile存储在代码仓库随项目一同管理,使用yaml格式可以最大程度的定制slave容器。

pipeline {
    agent {
        kubernetes {
            // 定义要在 Kubernetes 中运行的 Pod 模板
            yaml '''
apiVersion: v1
kind: Pod
metadata:
  labels:
    app: jenkins-slave
spec:
  containers:
  - name: jnlp
    image: harbor.local.com/cicd/jenkins-slave:v1.0
    resources:
      limits:
        memory: "512Mi"
        cpu: "500m"
    securityContext:
      privileged: true
    volumeMounts:
    - name: buildkit
      mountPath: "/run/buildkit/"
    - name: containerd
      mountPath: "/run/containerd/containerd.sock"
    - name: kube-config
      mountPath: "/root/.kube/"
      readOnly: true
  - name: maven
    image: harbor.local.com/cicd/maven:3.9.3
    resources:
      limits:
        memory: "512Mi"
        cpu: "500m"
    command:
      - 'sleep'
    args:
      - '9999'
    volumeMounts:
      - name: maven-data
        mountPath: "/root/.m2"
  - name: buildkitd
    image: harbor.local.com/cicd/buildkit:v0.13.2
    resources:
      limits:
        memory: "256Mi"
        cpu: "500m"
    securityContext:
      privileged: true
    volumeMounts:
    - name: buildkit
      mountPath: "/run/buildkit/"
    - name: buildkit-data
      mountPath: "/var/lib/buildkit/"
    - name: containerd
      mountPath: "/run/containerd/containerd.sock"
  volumes:
  - name: maven-data
    persistentVolumeClaim:
      claimName: jenkins-maven
  - name: buildkit
    hostPath:
      path: /run/buildkit/
  - name: buildkit-data
    hostPath:
      path: /var/lib/buildkit/
  - name: containerd
    hostPath:
      path: /run/containerd/containerd.sock
  - name: kube-config
    secret:
      secretName: kube-config
            '''
      retries 2
        }
    }
    environment {
        // 全局变量
        HARBOR_CRED = "harbor-cuiliang-password"
        IMAGE_NAME = ""
        IMAGE_APP = "demo"
        branchName = ""
    }
    stages {
        stage('拉取代码') {
            environment {
                // gitlab仓库信息
                GITLAB_CRED = "gitlab-cuiliang-password"
                GITLAB_URL = "http://gitlab.cicd.svc/develop/sprint_boot_demo.git"
            }
            steps {
                echo '开始拉取代码'
                checkout scmGit(branches: [[name: '*/*']], extensions: [], userRemoteConfigs: [[credentialsId: "${GITLAB_CRED}", url: "${GITLAB_URL}"]])
                // 获取当前拉取的分支名称
                script {
                    def branch = env.GIT_BRANCH ?: 'master'
                    branchName = branch.split('/')[-1]
                }
                echo '拉取代码完成'
            }
        }
        stage('编译打包') {
            steps {
                container('maven') {
                    // 指定使用maven container进行打包
                    echo '开始编译打包'
                    sh 'mvn clean package'
                    echo '编译打包完成'
                }
            }
        }
        stage('代码审查') {
            environment {
                // SonarQube信息
                SONARQUBE_SCANNER = "SonarQubeScanner"
                SONARQUBE_SERVER = "SonarQubeServer"
            }
            steps {
                echo '开始代码审查'
                script {
                    def scannerHome = tool "${SONARQUBE_SCANNER}"
                    withSonarQubeEnv("${SONARQUBE_SERVER}") {
                        sh "${scannerHome}/bin/sonar-scanner"
                    }
                }
                echo '代码审查完成'
            }
        }
        stage('构建镜像') {
            environment {
                // harbor仓库信息
                HARBOR_URL = "harbor.local.com"
                HARBOR_PROJECT = "spring_boot_demo"
                // 镜像标签
                IMAGE_TAG = ''
                // 镜像名称
                IMAGE_NAME = ''
            }
            steps {
                echo '开始构建镜像'
                script {
                    if (branchName == 'master') {
                        IMAGE_TAG = VersionNumber versionPrefix: 'p', versionNumberString: '${BUILD_DATE_FORMATTED, "yyMMdd"}.${BUILDS_TODAY}'
                    } else if (branchName == 'test') {
                        IMAGE_TAG = VersionNumber versionPrefix: 't', versionNumberString: '${BUILD_DATE_FORMATTED, "yyMMdd"}.${BUILDS_TODAY}'
                    } else {
                        error("Unsupported branch: ${params.BRANCH}")
                    }
                    IMAGE_NAME = "${HARBOR_URL}/${HARBOR_PROJECT}/${IMAGE_APP}:${IMAGE_TAG}"
                    sh "nerdctl build --insecure-registry -t ${IMAGE_NAME} . "
                }
                echo '构建镜像完成'
                echo '开始推送镜像'
                // 获取harbor账号密码
                withCredentials([usernamePassword(credentialsId: "${HARBOR_CRED}", passwordVariable: 'HARBOR_PASSWORD', usernameVariable: 'HARBOR_USERNAME')]) {
                    // 登录Harbor仓库
                    sh """nerdctl login --insecure-registry ${HARBOR_URL} -u ${HARBOR_USERNAME} -p ${HARBOR_PASSWORD}
          nerdctl push --insecure-registry ${IMAGE_NAME}"""
                }
                echo '推送镜像完成'
                echo '开始删除镜像'
                script {
                    sh "nerdctl rmi -f ${IMAGE_NAME}"
                }
                echo '删除镜像完成'
            }
        }
        stage('项目部署') {
            environment {
                // 资源清单名称
                YAML_NAME = "k8s.yaml"
            }
            steps {
                echo '开始修改资源清单'
                script {
                    if (branchName == 'master' ) {
                        NAME_SPACE = 'prod'
                        DOMAIN_NAME = 'demo.local.com'
                    } else if (branchName == 'test') {
                        NAME_SPACE = 'test'
                        DOMAIN_NAME = 'demo.test.com'
                    } else {
                        error("Unsupported branch: ${params.BRANCH}")
                    }
                }
                // 使用Content Replace插件进行k8s资源清单内容替换
                contentReplace(configs: [fileContentReplaceConfig(configs: [fileContentReplaceItemConfig(replace: "${IMAGE_NAME}", search: 'IMAGE_NAME'),
                                                                            fileContentReplaceItemConfig(replace: "${NAME_SPACE}", search: 'NAME_SPACE'),
                                                                            fileContentReplaceItemConfig(replace: "${DOMAIN_NAME}", search: 'DOMAIN_NAME')],
                        fileEncoding: 'UTF-8',
                        filePath: "${YAML_NAME}",
                        lineSeparator: 'Unix')])
                echo '修改资源清单完成'
                sh "cat ${YAML_NAME}"
                echo '开始部署资源清单'
                sh "kubectl apply -f ${YAML_NAME}"
                echo '部署资源清单完成'
            }
        }
    }
    post {
        always {
            echo '开始发送邮件通知'
            emailext(subject: '构建通知:${PROJECT_NAME} - Build # ${BUILD_NUMBER} - ${BUILD_STATUS}!',
                    body: '${FILE,path="email.html"}',
                    to: '[email protected]')
            echo '邮件通知发送完成'
        }
    }
}

标签:CI,container,name,demo,boot,Jenkins,jenkins,k8s,root
From: https://blog.csdn.net/weixin_45623111/article/details/139226764

相关文章

  • CI3使用网页缓存
     开启缓存将下面的代码放到任何一个控制器的方法内,你就可以开启缓存了:$this->output->cache($n);其中 $n 是缓存更新的时间(单位分钟)。上面的代码可以放在方法的任何位置,它出现的顺序对缓存没有影响,所以你可以把它放到任何你认为合理的地方。一旦该代码被放在方法内......
  • 安装centos开机出现Kernel panic - not syncing: Attempted to kill init无法启动解决
    一、安装centos开机出现Kernelpanic-notsyncing:Attemptedtokillinit无法启动的解决方法  装系统总会遇到各种新鲜问题,不过不要紧,只问题才能提升解决问题的能力,今天重新装了个CENTOS6.5的64位版,可能是进行了分区(boot单独挂载到了一个分区),开机时centos报错:Kernelp......
  • GitHub——敏捷开发,CI/CD的倡导者和受益者
    1.简介GitHub是一个面向开源及私有软件项目的托管平台,因为只支持Git作为唯一的版本库格式进行托管,故名GitHub。Github拥有1亿以上的开发人员,400万以上组织机构和3.3亿以上资料库。2.发展历程GitHub平台于2007年10月1日开始开发,由GitHub公司(曾称LogicalAwesome)的开发者ChrisWa......
  • 【实用软件】达芬奇Davinci Resolve 19最新版安装教程
    下载链接:https://docs.qq.com/doc/DRHV1b3FHZmJGSnpO详细图文教程:https://www.yuque.com/zhefengerhuanzaigua/bld6x5/gcq1zk8g2821xw9t软件介绍DaVinciResolveStudio是一款世界上第一个结合了专业离线和在线编辑,色彩校正,音频后期制作和Fusion视觉特效于一体的软件工具的......
  • 使用skywalking对k8s应用进行链路监控
    方案一、修改代码,引入jar包方案二、无侵入,使用探针自动注入到pod以下使用helm安装,提前安装helm环境1.添加SkyWalking的helm仓库helmrepoaddapache-skywalkinghttps://apache.jfrog.io/artifactory/skywalking-helm2.更新Helm仓库helmrepoupdate3.创建一个k8s命......
  • 微服务实践k8s&dapr开发部署实验(3)订阅发布
    自托管模式运行dapr新建订阅webapi项目,取名为backend项目增加docker支持,取消https支持修改Program.csvarbuilder=WebApplication.CreateBuilder(args);builder.Services.AddControllers();builder.Services.AddEndpointsApiExplorer();builder.Services.AddSwaggerGen......
  • 顶刊IJCV 2024 | EfficientSCI++:高效的视频单曝光压缩成像重建框架
    前言 来自浙江大学和西湖大学的研究人员提出了一种基于CNN-Transformer架构的高效、大尺度视频单曝光压缩成像重建算法EfficientSCI++。欢迎关注公众号CV技术指南,专注于计算机视觉的技术总结、最新技术跟踪、经典论文解读、CV招聘信息。本文转载自PaperWeekly仅用于学术分......
  • docker containerd runc containerd-shim等组件的关系
    早期kubelet创建容器工作原理因为docker出生的比k8s早,所以k8s早期的容器运行时都是基于docker的,kubelet通过docker的api创建容器。后来,k8s官方不想绑死在docker这架马车上,就把容器运行时抽象出来,定义了一个接口,叫CRI(containerruntimeinterface),容器......
  • 容器启动流程(containerd 和 runc)
    启动流程containerd作为一个api服务,提供了一系列的接口供外部调用,比如创建容器、删除容器、创建镜像、删除镜像等等。使用docker和ctr等工具,都是通过调用containerd的api来实现的。kubelet通过cri调用containerd和这些不一样,后续我会介绍到。containerd......
  • CISCN 2024 reverse 国赛复盘
    asm_re手撕汇编,配合GPT分析,加上一点点的猜测。在汇编推出可以看到加密逻辑:value=ord(f[1])value*=0x50;value+=0x14;value^=0x4D;value+=0x1E;print(value)已经知道第一个为f,f经过加密得到0x1FD7,l可以得到0x21B7。同理,根据数据段可以还原出flag,......