CI/CD配置手册

snow chuai汇总、整理、撰写---2024/03/21


1. 拓扑
+-----------------------+   +-----------------------+   +----------------------+
|     [ Gitlab Node ]   |   |    [ Jenkins Node ]   |   |    [ Harbor Node ]   |
|  [gitlab.1000y.cloud] |   | [jenkins.1000y.cloud] |   |[k8srepos.1000y.cloud]|
+-----------+-----------+   +-----------+-----------+   +------+---------------+
        eth0|192.168.1.26           eth0|192.168.1.27      eth0|192.168.1.249
            |---------------------------+----------------------|
                                        |                            
                                    eth0|192.168.1.11~16     
                            +-----------+-----------+
                            |  [kubernetes cluster] |
                            +-----------------------+

前提: 1. kubernetes集群部署完成 2. Harbor节点部署完成且成功与kubernetes集群成功对接
2. 安装Docker-CE
1) 安装Docker-CE
[root@jenkins ~]# yum install yum-utils device-mapper-persistent-data lvm2 -y
[root@jenkins ~]# yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo [root@jenkins ~]# sed -i 's+download.docker.com+mirrors.aliyun.com/docker-ce+' /etc/yum.repos.d/docker-ce.repo
[root@jenkins ~]# yum makecache fast [root@jenkins ~]# yum install docker-ce -y
2) 添加Harbor证书 [root@jenkins ~]# scp k8srepos.1000y.cloud:/etc/pki/CA/cacert.pem . [root@jenkins ~]# cat cacert.pem >> /etc/pki/tls/certs/ca-bundle.crt
3) 配置加速器 [root@jenkins ~]# vim /etc/docker/daemon.json { "registry-mirrors": ["https://3laho3y3.mirror.aliyuncs.com"], "insecure-registries": ["https://srv7.1000y.cloud"] }
4) 启动Docker [root@jenkins ~]# systemctl restart docker
5) 登陆Harbor [root@jenkins ~]# docker login k8srepos.1000y.cloud Username: admin # Harbor节点的账户 Password: # Harbor节点账户的密码 WARNING! Your password will be stored unencrypted in /root/.docker/config.json. Configure a credential helper to remove this warning. See https://docs.docker.com/engine/reference/commandline/login/#credentials-store
Login Succeeded
6) 测试 [root@jenkins ~]# docker pull k8srepos.1000y.cloud/k8s/nginx Using default tag: latest latest: Pulling from k8s/nginx a2abf6c4d29d: Pull complete a9edb18cadd1: Pull complete 589b7251471a: Pull complete 186b1aaa4aa6: Pull complete b4df32aa5a72: Pull complete a0bcbecc962e: Pull complete Digest: sha256:ee89b00528ff4f02f2405e4ee221743ebc3f8e8dd0bfd5c4c20a2fa2aaa7ede3 Status: Downloaded newer image for k8srepos.1000y.cloud/k8s/nginx:latest k8srepos.1000y.cloud/k8s/nginx:latest
[root@jenkins ~]# docker rmi k8srepos.1000y.cloud/k8s/nginx:latest Untagged: k8srepos.1000y.cloud/k8s/nginx:latest Untagged: k8srepos.1000y.cloud/k8s/nginx@sha256:ee89b00528ff4f02f2405e4ee221743ebc3f8e8dd0bfd5c4c20a2fa2aaa7ede3 Deleted: sha256:605c77e624ddb75e6110f997c58876baa13f8754486b461117934b24a9dc3a85 Deleted: sha256:b625d8e29573fa369e799ca7c5df8b7a902126d2b7cbeb390af59e4b9e1210c5 Deleted: sha256:7850d382fb05e393e211067c5ca0aada2111fcbe550a90fed04d1c634bd31a14 Deleted: sha256:02b80ac2055edd757a996c3d554e6a8906fd3521e14d1227440afd5163a5f1c4 Deleted: sha256:b92aa5824592ecb46e6d169f8e694a99150ccef01a2aabea7b9c02356cdabe7c Deleted: sha256:780238f18c540007376dd5e904f583896a69fe620876cabc06977a3af4ba4fb5 Deleted: sha256:2edcec3590a4ec7f40cf0743c15d78fb39d8326bc029073b41ef9727da6c851f
3. 部署Jenkins
1) 安装Jenkins
[root@jenkins ~]# docker run -d -u root --name=jenkins --restart=always -p 8080:8080 -p 50000:50000 \
-v /data/jenkins:/var/jenkins_home k8srepos.1000y.cloud/k8s/jenkins:2.450-jdk11
2) 修改Jenkins插件为国内源 [root@jenkins ~]# vim /data/jenkins/hudson.model.UpdateCenter.xml <?xml version='1.1' encoding='UTF-8'?> <sites> <site> <id>default</id> 将https:......改为如下内容 <url>https://mirror.tuna.tsinghua.edu.cn/jenkins/updates/update-center.json</url> </site> </sites>
3) 如已有准备好的插件,可直接进行复制 [root@jenkins ~]# curl -O http://registry.1000y.cloud/repos/k8s-cicd/jenkins-plugins.tar.gz
[root@jenkins ~]# tar xfz jenkins-plugins.tar.gz
[root@jenkins ~]# cp -Rp plugins/* /data/jenkins/plugins/
[root@jenkins ~]# docker restart jenkins
4) 访问及初始化配置Jenkins [浏览器]===>http://jenkins.1000y.cloud:8080
[root@jenkins ~]# cat /data/jenkins/secrets/initialAdminPassword e3b883c88a874a8784cd658d076e3be9






5) 安装Jenkins必要的CI/CD所需的插件
# 如果没有进行 docker restart jenkins, 但已经复制保存的插件到jenkins。也可通过浏览器输入: http://jenkins.1000y.cloud:8080/restart 来重启jenkins
通过搜索,勾选所需要的插件:
插件清单: Git Git Parameter Git Pipeline for Blue Ocean GitLab Credentials Credentials Binding Blue Ocean Blue Ocean Pipeline Editor Blue Ocean Core JS Pipeline SCM API for Blue Ocean Dashboard for Blue Ocean Build With Parameters Dynamic Extended Choice Parameter Extended Choice Parameter List Git Branches Parameter Pipeline Pipeline: Declarative Kubernetes Kubernetes CLI Kubernetes Credentials Image Tag Parameter
4. 部署GitLab
4.1 配置MAIL Server
[root@gitlab ~]# vim /etc/postfix/main.cf
......
......
......
......
......
......
开启113行,注释116行,改成如下: inet_interfaces = all #inet_interfaces = $myhostname #inet_interfaces = $myhostname, localhost #inet_interfaces = localhost
...... ...... ...... ...... ...... ......
[root@gitlab ~]# systemctl restart postfix
4.2 安装及配置Gitlab
1) 安装Gitlab
[root@gitlab ~]# curl -O https://packages.gitlab.com/install/repositories/gitlab/gitlab-ce/script.rpm.sh
[root@gitlab ~]# sh script.rpm.sh
[root@gitlab ~]# yum install gitlab-ce -y # 或直接写repo文件 [root@node1 ~]# vim /etc/yum.repos.d/gitlab-ce.repo [gitlab-ce] name=Gitlab-CE baseurl=http://192.168.1.254/repos/gitlab-ce/yum/el7/ enabled=1 gpgcheck=0
2) 修改Gitlab安装配置文件 [root@gitlab ~]# vim /etc/gitlab/gitlab.rb ...... ...... ...... ...... ...... ......
# 将32行改为如下内容(填写你自己的FQDN) external_url 'http://gitlab.1000y.cloud'
...... ...... ...... ...... ...... ......
3) 安装Gitlab [root@gitlab ~]# gitlab-ctl reconfigure ...... ...... ...... ...... ...... ...... Notes: Default admin account has been configured with following details: Username: root Password: You didn't opt-in to print initial root password to STDOUT. # 提示初始化密码所存放的位置 Password stored to /etc/gitlab/initial_root_password. This file will be cleaned up in first reconfigure run after 24 hours.
NOTE: Because these credentials might be present in your log files in plain text, it is highly recommended to reset the password following https://docs.gitlab.com/ee/security/reset_user_password.html#reset-your-root-password.
gitlab Reconfigured!
[root@gitlab ~]# cat /etc/gitlab/initial_root_password | grep Password: Password: wBlTUPr+On2bnkYK1SXvxLb2YseFA3N+OWVZz8Bx8q8=
2) 配置Gitlab [浏览器]=>http://gitlab.1000y.cloud




密码修改后,将会自动强制重新登陆
3) 创建一个Gitlab群组

组名为 kubernetes,类型为: 私有

3) 创建一个Porject

# 项目名称: spring-boot-project

5. Jenkins与Gitlab互联测试
1) 设定Jenkins端ssh密钥
[root@jenkins ~]# ssh-keygen -q -N '' -C "snowchuai@1000y.cloud"
Enter file in which to save the key (/root/.ssh/id_rsa):	# 回车
2) 复制jenkins ssh公钥至Gitlab (1) Jenkins端操作 [root@jenkins ~]# cat ~/.ssh/id_rsa.pub ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDNyNXAc/hgI72ZseiDj9lj5Uwnfh4isAUVzmERQ6jzcm/vjCo3Ok5vC6bu4YDYJ953yerL5XWkqyCGWF xKF/Or/gFwz7EfeFtTOIkYbSC39kOadawkOOp66oODwQVe3/8AOuKIYMf9ywo1E4t4EkmcJerW9FQrI0AU0jJEnN1WA1Vg7V9QcayXMCTdI2IC/p0ta6WQ 3IhRjIjdF8T23sGepc9gj4C8VO6Cr8hBdkE7aEosAmR3CMInkOi1w9ou6tMFmR/8vScdNEkExs3XpmkhqE68U6mKYxzsRgyk/xvvkpRHS2VK89twKlmMpI wg4D+RW6VArqWzallSDLtDX4Zr snowchuai@1000y.cloud
(2) Gitlab WEB UI端操作



3) Jenkins端测试 [root@jenkins ~]# yum install git -y
[root@jenkins ~]# git clone git@gitlab.1000y.cloud:kubernetes/spring-boot-project.git Cloning into 'spring-boot-project'... The authenticity of host 'gitlab.1000y.cloud (192.168.1.27)' can't be established. ECDSA key fingerprint is SHA256:uoSm+T/hW98A1OoH1Mxb4AJS8FPylojFSEhI2J4la+Q. ECDSA key fingerprint is MD5:98:0c:c1:97:25:ca:bd:ae:a0:6d:98:6b:15:b8:d9:2e. Are you sure you want to continue connecting (yes/no)? yes Warning: Permanently added 'gitlab.1000y.cloud,192.168.1.27' (ECDSA) to the list of known hosts. remote: Enumerating objects: 3, done. remote: Counting objects: 100% (3/3), done. remote: Compressing objects: 100% (2/2), done. remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0 Receiving objects: 100% (3/3), done.
[root@jenkins ~]# curl -O http://registry.1000y.cloud/repos/k8s-cicd/spring-boot-project.tar.gz
[root@jenkins ~]# tar xf spring-boot-project
[root@jenkins ~]# cd spring-boot-project/ [root@jenkins spring-boot-project]# git add .
[root@jenkins spring-boot-project]# git commit -am "Spring Boot Project" [main c85538c] Spring Boot Project ...... ...... ...... ...... ...... ...... create mode 100644 target/spring-cloud-eureka-0.0.1-SNAPSHOT.jar.original
[root@jenkins spring-boot-project]# git push origin main Counting objects: 34, done. Delta compression using up to 4 threads. Compressing objects: 100% (23/23), done. Writing objects: 100% (32/32), 40.05 MiB | 11.48 MiB/s, done. Total 32 (delta 0), reused 0 (delta 0) To git@gitlab.1000y.cloud:kubernetes/spring-boot-project.git 103aa20..c85538c main -> main
4) Gitlab端测试
5) Jenkins端--Jenkins容器测试与Gitlab SSH连接 # 本测试必须操作,后期CI/CD测试时,也需要Jenkins容器能够SSH连接Gitalb。如果前期没有导入gitlab服务器的ssh host key,则会触发报错信息 [root@jenkins ~]# docker exec -it jenkins /bin/bash root@e5ff65a88997:/# ssh gitlab.1000y.cloud The authenticity of host 'gitlab.1000y.cloud (192.168.1.27)' can't be established. ED25519 key fingerprint is SHA256:lBeG3WSuUw3S+DEBV1RC+XCEbPEwKdruOp0+95TKc8c. This key is not known by any other names. Are you sure you want to continue connecting (yes/no/[fingerprint])? yes Warning: Permanently added 'gitlab.1000y.cloud' (ED25519) to the list of known hosts. root@gitlab.1000y.cloud's password: # 输入gitlab.1000y.cloud的root密码 Last login: Fri Mar 22 13:37:38 2024 from 192.168.1.28 [root@gitlab ~]#
6. 配置Jekins
6.1 Jenkins添加凭证
1) 取消插件告警


2) 加添凭证 (1) 添加kubernetes凭证



(2) 添加HarBor凭证


(3) 添加Gitlab凭证---[添加Jenkins SSH私钥]

6.2 Jenkins Agent配置
1) 取消插件告警
# 通常情况下,Jenkins Slave 会通过 Jenkins Master 节点的 50000 端口与之通信,所以需要开启 Agent 的 50000 端口。


6.3 定义Jenkins Slave节点并配置
1) 定义标签
# 1. 实际使用时,没有必要把整个 Kubernetes 集群的节点都充当创建 Jenkins Slave Pod 的节点, 可以选择任意的一个或多个节点作为创建 Slave Pod 的节点。
2. 本章节选择kubernetes节点中srv6.1000y.cloud[角色: Worker/Node]主机作为 Jenkins Slave 节点
[root@srv1 ~]# kubectl label node srv6.1000y.cloud build=true
node/srv6.1000y.cloud labeled
[root@srv1 ~]# kubectl get nodes -l build=true NAME STATUS ROLES AGE VERSION srv6.1000y.cloud Ready <none> 4d6h v1.28.2
2) 为worker节点安装docker-ce # Pod的生成要需Dockerfile进行制作,所以必须安装Docker [root@srv6 ~]# yum install yum-utils device-mapper-persistent-data lvm2 -y
[root@srv6 ~]# yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo [root@srv6 ~]# sed -i 's+download.docker.com+mirrors.aliyun.com/docker-ce+' /etc/yum.repos.d/docker-ce.repo
[root@srv6 ~]# yum makecache fast [root@srv6 ~]# yum install docker-ce -y
[root@srv6 ~]# vim /etc/docker/daemon.json { "registry-mirrors": ["https://3laho3y3.mirror.aliyuncs.com"], "insecure-registries": ["https://srv7.1000y.cloud"] }
[root@srv6 ~]# systemctl enable --now docker
[root@srv6 ~]# docker login k8srepos.1000y.cloud Username: admin # Harbor节点的账户 Password: # Harbor节点账户的密码 WARNING! Your password will be stored unencrypted in /root/.docker/config.json. Configure a credential helper to remove this warning. See https://docs.docker.com/engine/reference/commandline/login/#credentials-store
Login Succeeded
3) 创建Jenkins工作目录 [root@srv6 ~]# mkdir /opt/workspace [root@srv6 ~]# chmod 777 /opt/workspace
6.4 Jenkins与kubernetes集群对接



# Kubernetes 可以叫做 “kubernetes-study"

7. CI/CD平台设计

8. CI/CD实现
8.1 创建Jenkinsfile
1. Jenkinsfile已保存在目录中
2. 如打算手工创建,可点击代码首页的“+”号,然后点击 New file
8.2 Jenkinsfile详解
1) 详解1---顶层的 Agent,定义的是 Kubernetes 的 Pod 作为 Jenkins 的 Slave 
pipeline{
    agent{
        # 定义使用Kubernetes作为agent
        kubernetes {
            # 这里选择的云为之前在 Jenkins 上配置的名字
            cloud 'kubernetes-study'
            slaveConnectTimeout 1200
            # 将 workspace 改为 hostPath,因为该 Slave 会固定节点创建,如果有存储可用,可以改为 PVC 的模式
            workspaceVolume hostPathWorkspaceVolume(hostPath: "/opt/workspace", readOnly: false)
            yaml '''
apiVersion: v1
kind: Pod
spec:
  containers:
    # jnlp 容器,用于和 Jenkins 主节点通信,注意:这里的jnlp容器中的jdk版本需要和安装的Jenkins容器中的jdk保持版本一致
    - name: jnlp
      image: k8srepos.1000y.cloud/k8s/inbound-agent:bookworm-jdk11 
      imagePullPolicy: IfNotPresent
      args: [\'$(JENKINS_SECRET)\', \'$(JENKINS_NAME)\']
      volumeMounts:
        - name: "localtime"
          mountPath: "/etc/localtime"
          readOnly: false
    # build 容器,包含执行构建的命令,比如 Java 的需要 mvn 构建,就可以用一个 maven 的镜像
    - name: build     # 容器的名字,流水线的 stage 可以直接使用该名字
      # 使用 Maven 镜像,包含 mvn 工具。NodeJS 可以用 node 的镜像
      image: k8srepos.1000y.cloud/k8s/maven:3.5.3
      imagePullPolicy: IfNotPresent
      tty: true
      command:
        - "cat"
      env:
        - name: "LANGUAGE"
          value: "en_US:en"
        - name: "LC_ALL"
          value: "en_US.UTF-8"
        - name: "LANG"
          value: "en_US.UTF-8"
      volumeMounts:
        - name: "localtime"
          mountPath: "/etc/localtime"
          readOnly: false
        # Pod 单独创建了一个缓存的 volume,将其挂载到了 maven 插件的缓存目录,默认是 /root/.m2
        - name: "cachedir"
          mountPath: "/root/.m2"
          readOnly: false
    # 发版容器,因为最终是发版至 Kubernetes 上的,所以需要有一个 kubectl 命令
    - name: kubectl
      # 镜像的版本可以替换为其它的版本,也可以不进行替换,因为只是执行 set 命令,所以版本是兼容的
      image: k8srepos.1000y.cloud/k8s/citools/kubectl:self-1.17
      imagePullPolicy: IfNotPresent
      tty: true
      command:
        - "cat"
      env:
        - name: "LANGUAGE"
          value: "en_US:en"
        - name: "LC_ALL"
          value: "en_US.UTF-8"
        - name: "LANG"
          value: "en_US.UTF-8"
      volumeMounts:
        - name: "localtime"
          mountPath: "/etc/localtime"
          readOnly: false
    # 用于生成镜像的容器,需要包含 docker 命令
    - name: docker
      image: k8srepos.1000y.cloud/k8s/citools/docker:19.03.9-git
      imagePullPolicy: IfNotPresent
      tty: true
      command:
        - "cat"
      env:
        - name: "LANGUAGE"
          value: "en_US:en"
        - name: "LC_ALL"
          value: "en_US.UTF-8"
        - name: "LANG"
          value: "en_US.UTF-8"
      volumeMounts:
        - name: "localtime"
          mountPath: "/etc/localtime"
          readOnly: false
        # 由于容器没有启动 docker 服务,所以将宿主机的 docker 经常挂载至容器即可
        - name: "dockersock"
          mountPath: "/var/run/docker.sock"
          readOnly: false
  restartPolicy: "Never"
  # 固定节点部署,也就是说这里我们一般部署,只需要一个节点去执行流水线操作,所以这里固定一个节点,在需要使用的 node 节点上添加该配置的标签
  nodeSelector:
    build: "true"
  securityContext: {}
  volumes:
    - name: "dockersock"
      hostPath: 
        path: "/var/run/docker.sock"
    - name: "localtime"
      hostPath: 
        path: "/usr/share/zoneinfo/Asia/Shanghai"
    - name: "cachedir"
      hostPath:
        path: "/opt/m2"
'''
        }
2) 详解2---拉取代码的 stage,这个 stage 是一个并行的 stage,因为考虑了该流水线是手动触发还是提交代码后自动触发 
        stage("Pulling Code"){
            parallel {
                stage('Pulling Code by Jenkins'){
                    when {
                        expression {
                            # 假如 env.gitlabBranch 为空,则该流水线为手动触发,那么就会执行该 stage,如果不为空则会执行同级的另外一个 stage
                            env.gitlabBranch == null
                        }
                    }
                    steps {
                        # 这里使用的是 git 插件拉取代码,BRANCH 变量取自于前面介绍的 parameters 配置
                        # git@gitlab.1000y.cloud:kubernetes/spring-boot-project.git  代码地址
                        # credentialsId: 'gitlab-key' 之前在 Jenkins 上创建的拉取代码的 key
                        git(changelog: true, poll: true, url: 'git@gitlab.1000y.cloud:kubernetes/spring-boot-project.git', branch: "${BRANCH}", credentialsId: 'gitlab-key')
                        script {
                            # 定义一些变量用于生成镜像的 Tag
                            # 获取最近一次提交的 Commit ID
                            COMMIT_ID = sh(returnStdout: true, script: "git log -n 1 --pretty=format:'%h'").trim()
                            # 赋值给 TAG 变量,后面的 docker build 可以取到该 TAG 的值
                            TAG = BUILD_TAG + '-' + COMMIT_ID
                            println "Current branch is ${BRANCH}, Commit ID is ${COMMIT_ID}, Image TAG is ${TAG}"
                        }
                    }
                }
                stage('Pulling Code by trigger'){
                    when {
                        expression {
                            # 如果 env.gitlabBranch 不为空,说明该流水线是通过 webhook 触发,则此时执行该 stage,上述的 stage 不再执行。此时 BRANCH 变量为空
                            env.gitlabBranch != null
                        }
                    }
                    steps {
                        # 以下配置和上述一致,只是此时 branch: env.gitlabBranch 取的值为 env.gitlabBranch
                        git(changelog: true, poll: true, url: 'git@gitlab:kubernetes/spring-boot-project.git', branch: env.gitlabBranch, credentialsId: 'gitlab-key')
                        script {
                            COMMIT_ID = sh(returnStdout: true, script: "git log -n 1 --pretty=format:'%h'").trim()
                            TAG = BUILD_TAG + '-' + COMMIT_ID
                            println "Current branch is ${env.gitlabBranch}, Commit ID is ${COMMIT_ID}, Image TAG is ${TAG}"
                        }
                    }
                }
            }
        }
3) 详解3---生成对应的的镜像,此时可以使用 Pod 模板的 docker 容器 
        stage("Docker build for creating image"){
            # 首先取出 HARBOR 的账号密码(也就是之前在Jenkins上配置的认证信息)
            environment {
                HARBOR_USER = credentials('HARBOR_ACCOUNT')
            }
            steps {
                # 指定使用 docker 容器
                container(name: "docker"){
                    sh """
                      echo "===================开始构建Docker镜像及推送==================="
                      echo ${HARBOR_USER_USR} ${HARBOR_USER_PSW} ${TAG}
                      # 执行build命令,Dockerfile会在后面创建,也是放在代码仓库,和 Jenkinsfile 同级
                      docker build -t ${HARBOR_ADDRESS}/${REGISTRY_DIR}/${IMAGE_NAME}:${TAG} .
                      # 登陆 Harbor,HARBOR_USER_USR和HARBOR_USER_PSW由上面的environment生成
                      docker login -u ${HARBOR_USER_USR} -p ${HARBOR_USER_PSW} ${HARBOR_ADDRESS}
                      # 将镜像推送至镜像仓库
                      docker push ${HARBOR_ADDRESS}/${REGISTRY_DIR}/${IMAGE_NAME}:${TAG}
                    """
                }
            }
        }
4) 详解4---将该镜像发版至 Kubernetes 集群中,此时使用的是包含 kubectl 命令的容器
        stage("Deploying to k8s"){
            # 获取连接 Kubernetes 集群的证书(也就是之前在jenkins上配置的认证信息)
            environment {
                MY_KUBECONFIG = credentials('k8s-kubeconfig')
            }
            steps {
                # 指定使用 kubectl 容器
                container(name: "kubectl"){
                    sh """
                      echo "===================开始更新Deployment资源镜像==================="
                      # 直接通过 set 命令更改 Deployment 的镜像即可
                      /usr/local/bin/kubectl --kubeconfig ${MY_KUBECONFIG} set image deploy -l app=${IMAGE_NAME} ${IMAGE_NAME}=${HARBOR_ADDRESS}/${REGISTRY_DIR}/${IMAGE_NAME}:${TAG} -n ${NAMESPACE}
                    """
                }
            }
        }
    }
5) 详解5---Jenkinsfile 中最后的环境变量和 parameters 的配置 
    # 定义一些全局的环境变量
    environment {
        COMMIT_ID = ""
        HARBOR_ADDRESS = "k8srepos.1000y.cloud"    # Harbor地址
        REGISTRY_DIR = "k8s"         # Harbor的项目目录
        IMAGE_NAME = "spring-boot-project"  # 镜像的名称
        NAMESPACE = "kubernetes"            # 该应用部署在 Kubernetes 集群中的哪个命名空间
        TAG = ""                            # 镜像的Tag,在此使用 BUILD_TAG + COMMIT_ID 组成
    }
    parameters {
        # 类似于 choice、input 类型的参数,这里使用 GitParameter 插件
        # 该字段会在 Jenkins 页面生成一个选择分支的选项
        gitParameter(branch: '', branchFilter: 'origin/(.*)', defaultValue: '', description: 'Branch for build and deploy', name: 'BRANCH', quickFilterEnabled: false, selectedValue: 'NONE', sortMode: 'NONE', tagFilter: '*', type: 'PT_BRANCH')
    }
8.3 定义Dockerfile
1) 创建Dockerfile
1. Dockerfile已保存在目录中
2. 如打算手工创建,可点击代码首页的“+”号,然后点击 New file
2) Dockerfile说明 # 基础镜像可以按需修改,可以更改为公司自有镜像 FROM k8srepos.1000y.cloud/k8s/jre:8u211-data # jar 包名称改成实际的名称,本示例为 spring-cloud-eureka-0.0.1-SNAPSHOT.jar COPY target/spring-cloud-eureka-0.0.1-SNAPSHOT.jar ./ # 启动 Jar 包 CMD java -jar spring-cloud-eureka-0.0.1-SNAPSHOT.jar
8.4 定义Kuberntes资源
1) 撰写资源清单
[root@srv1 ~]# mkdir cicd-test
[root@srv1 ~]# vim cicd-test/test-cicd.yaml
---
apiVersion: v1
kind: Service
metadata:
  labels:
    app: spring-boot-project
  name: spring-boot-project
  namespace: kubernetes    # 和流水线中定义的变量(NAMESPACE)保持一致
spec:
  ports:
  - name: web
    port: 8761           # 端口按照实际情况进行修改
    protocol: TCP
    targetPort: 8761     # 端口按照实际情况进行修改
  selector:
    app: spring-boot-project
  sessionAffinity: None
  type: NodePort         # 这里根据实际情况进行修改,这里测试所以用的 NodePort 类型
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: spring-boot-project
  namespace: kubernetes    # 和流水线中定义的变量(NAMESPACE)保持一致
spec:
  rules:
  - host: srv1.1000y.cloud
    http:
      paths:
      - backend:
          service:
            name: spring-boot-project
            port:
              number: 8761
        path: /
        pathType: ImplementationSpecific
---
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: spring-boot-project    # Deployment 标签,和流水线的 set -l 保持一致,用于更新镜像版本
  name: spring-boot-project     # Deployment 名称
  namespace: kubernetes         # 和流水线中定义的变量(NAMESPACE)保持一致
spec:
  replicas: 1
  selector:
    matchLabels:
      app: spring-boot-project    # 匹配 Pod 的标签
  strategy:
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
    type: RollingUpdate
  template:
    metadata:
      labels:
        app: spring-boot-project  # Pod 的标签,需要和上面 selector 保持一致
    spec:
      containers:
      - name: spring-boot-project      # 容器的名称,需要和流水线 set 命令的容器名称一致
        image: k8srepos.1000y.cloud/k8s/demoapp:v1.4       # 此处使用的 demoapp 作为原始的镜像,通过 Jenkins 构建并发版后,变成 Java 应用镜像
        imagePullPolicy: IfNotPresent
        env:
        - name: TZ
          value: Asia/Shanghai
        - name: LANG
          value: C.UTF-8
        ports:
        - containerPort: 8761     # 容器端口号,根据实际 java 应用进行修改
          name: web
          protocol: TCP
        lifecycle: {}
        livenessProbe:
          failureThreshold: 2
          initialDelaySeconds: 30
          periodSeconds: 10
          successThreshold: 1
          tcpSocket:
            port: 8761           # 端口号和健康检查按照实际情况进行修改
          timeoutSeconds: 2
        readinessProbe:
          failureThreshold: 2
          initialDelaySeconds: 30
          periodSeconds: 10
          successThreshold: 1
          tcpSocket:
            port: 8761           # 端口号和健康检查按照实际情况进行修改
          timeoutSeconds: 2
        resources:               # 资源请求按照实际情况修改
          limits:
            cpu: 994m
            memory: 1170Mi
          requests: 
            cpu: 10m
            memory: 55Mi
      dnsPolicy: ClusterFirst
      imagePullSecrets:          
        - name: harborkey        # Harbor 仓库密钥,需要和上述创建的 Secret 一致
      restartPolicy: Always
      securityContext: {}
      serviceAccountName: default
2) 创建kubernetes相关资源 [root@srv1 ~]# kubectl create namespace kubernetes namespace/kubernetes created
[root@srv1 ~]# kubectl create secret docker-registry harborkey --docker-server=k8srepos.1000y.cloud --docker-username=admin --docker-password=123456 --docker-email=snow@1000y.cloud -n kubernetes secret/harborkey created
[root@srv1 ~]# kubectl apply -f cicd-test/test-cicd.yaml service/spring-boot-project created ingress.networking.k8s.io/spring-boot-project created deployment.apps/spring-boot-project created
3) 确认kuberntes资源生成 # READY状态0/1是正常 [root@srv1 ~]# kubectl get pod -n kubernetes NAME READY STATUS RESTARTS AGE spring-boot-project-7d4f5d769-4pnrw 0/1 Running 0 26s
8.5 实现CI/CD
1) 创建Jenkins Job



2) 构建项目---1 # 第一次构建会出现失败,可直接忽略

3) 构建项目---2 # 第一次构建结束后,可以看到 Build Now 变成了 Build with Parameters。点击 Build with Parameters 后,可以读取到 Git 仓库的分支,之后可以选择分支进行手动构建


4) Blue Ocean 查看直观构建流程


5) 确认Harbor仓库已经存在项目所需的镜像
6) 确认kubernetes资源信息 [root@srv1 ~]# kubectl get pod -n kubernetes NAME READY STATUS RESTARTS AGE spring-boot-project-6db89f4c5b-ftqzm 1/1 Running 0 8m13s
[root@srv1 ~]# kubectl get deploy spring-boot-project -n kubernetes -o jsonpath='{.spec.template.spec.containers[0].image}' k8srepos.1000y.cloud/k8s/spring-boot-project:jenkins-spring-boot-project-2-c85538c
[root@srv1 ~]# kubectl get svc -n kubernetes NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE spring-boot-project NodePort 10.107.221.159 <none> 8761:30694/TCP 23m
7) 结果确认 [浏览器]===>srv1.1000y.cloud:30694
8.5 自动触发构建与实现
1) 说明
1. 之前的构建都是采用手动选择分支进行构建的,实际使用时,项目可能有很多,如果都是手动触发可能比较消耗人力。所以推荐可以按需配置自动触发,即提交代码后自动触发 Jenkins 进行构建任务。
2) 自动触发构建 (1) Jenkins配置


(2) Gitlab配置







(3) 测试配置

8.6 一次构建多次部署机制实现
1) 说明
1. 上面通过 Gitlab 的事件触发 Jenkins 任务,在实际使用过程中,一般 UAT 和生产环境,一般不需要再次构建,而是选择其他环境产生的镜像进行发版。这里同样以上面的 Java 项目示例

2) 实现 # 创建一个新的 Job,名字为 java-project-uat,类型 Pipeline




3) Pipeline脚本 pipeline{ agent{ kubernetes { cloud 'kubernetes-study' slaveConnectTimeout 1200 yaml ''' apiVersion: v1 kind: Pod spec: containers: # 只需要配置 jnlp 和 kubectl 镜像即可 - name: jnlp image: k8srepos.1000y.cloud/k8s/inbound-agent:bookworm-jdk11 imagePullPolicy: IfNotPresent args: [\'$(JENKINS_SECRET)\', \'$(JENKINS_NAME)\'] - name: kubectl image: k8srepos.1000y.cloud/k8s/citools/kubectl:self-1.17 imagePullPolicy: IfNotPresent tty: true command: - "cat" env: - name: "LANGUAGE" value: "en_US:en" - name: "LC_ALL" value: "en_US.UTF-8" - name: "LANG" value: "en_US.UTF-8" restartPolicy: "Never" nodeSelector: build: "true" securityContext: {} ''' } } stages{ stage("Deploy"){ environment { MY_KUBECONFIG = credentials('k8s-kubeconfig') } steps { container(name: "kubectl"){ sh """ echo ${IMAGE_TAG} # 该变量即为前台选择的镜像 echo "===================开始更新Deployment资源镜像===================" /usr/local/bin/kubectl --kubeconfig ${MY_KUBECONFIG} set image deploy -l app=${IMAGE_NAME} ${IMAGE_NAME}=${HARBOR_ADDRESS}/${IMAGE_TAG} -n ${NAMESPACE} sleep 3 /usr/local/bin/kubectl --kubeconfig ${MY_KUBECONFIG} get po -l app=${IMAGE_NAME} -n ${NAMESPACE} """ } } } } environment { HARBOR_ADDRESS = "k8srepos.1000y.cloud" IMAGE_NAME = "spring-boot-project" NAMESPACE = "kubernetes" TAG = "" } }
4) 测试 (1) 为Jenkins容器增加对Harbor HTTPS自签证书的信任 # 将harbor的自签证书增加download至Jenkins服务节点 [root@jenkins ~]# docker exec jenkins sh -c "echo | openssl s_client -connect k8srepos.1000y.cloud:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > /var/tmp/sonarqube.crt" depth=0 C = CN, ST = BeiJing, O = 1000y.cloud, OU = tech, CN = k8srepos.1000y.cloud verify error:num=20:unable to get local issuer certificate verify return:1 depth=0 C = CN, ST = BeiJing, O = 1000y.cloud, OU = tech, CN = k8srepos.1000y.cloud verify error:num=21:unable to verify the first certificate verify return:1 depth=0 C = CN, ST = BeiJing, O = 1000y.cloud, OU = tech, CN = k8srepos.1000y.cloud verify return:1 DONE
# 将导出的证书导入至Jenkins容器,导入的密码为changeit [root@jenkins ~]# docker exec jenkins sh -c "keytool -import -alias k8srepos \ -file /var/tmp/sonarqube.crt -cacerts -storepass changeit -noprompt -trustcacerts" Certificate was added to keystore
# 重启Jenkins [浏览器]===>http://jenkins.1000y.cloud:8080/restart
(2) 测试


[root@srv1 ~]# kubectl get pod -n kubernetes NAME READY STATUS RESTARTS AGE spring-boot-project-988d4cccc-bh885 1/1 Running 0 118s

 

如对您有帮助,请随缘打个赏。^-^

gold