# K8S 基于 Jenkins 实现 SpringCloud 微服务 CI 与 CD 实践(一)

CI 阶段:开发人员 -> 提交代码 ->gitlab 仓库 ->Jenkins/CI 抓取代码 -> 漏洞扫描 -> 编译 -> 构建镜像 -> 推送 Harbor-> 部署应用至 K8S 测试环境;

CD 阶段:Jenkins/CD-> 拉取 Harbor 仓库对应项目镜像 -> 部署应用至 K8S 测试环境

1.jpg

# 一、部署 Harbor

# 1.1 安装基础环境
[root@harbor ~]# yum remove docker*
[root@harbor ~]# yum install -y yum-utils device-mapper-persistent-data lvm2
[root@harbor ~]# curl -o /etc/yum.repos.d/docker-ce.repo  https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
[root@harbor ~]# yum list docker-ce --showduplicates |sort -r 
[root@harbor ~]# yum install docker-ce docker-compose -y
### 提示:No match for argument: python-pip
#改成为 Centos 8 已经换成 python3 使用该命令解决 yum install python3-pip
[root@harbor ~]# yum -y install python-pip
#升级
[root@harbor ~]# pip3 install --upgrade pip
[root@harbor ~]# pip3 install docker-compose
[root@Harbor ~]# docker-compose -version
# 1.2 配置 Docker 加速
[root@harbor ~]# sudo mkdir -p /etc/docker
#Docker 加速 1
[root@harbor ~]# sudo tee /etc/docker/daemon.json <<-'EOF'
{
  "registry-mirrors": [
    "https://docker.credclouds.com",
    "https://k8s.credclouds.com",
    "https://quay.credclouds.com",
    "https://gcr.credclouds.com",
    "https://k8s-gcr.credclouds.com",
    "https://ghcr.credclouds.com",
    "https://do.nark.eu.org",
    "https://docker.m.daocloud.io",
    "https://docker.nju.edu.cn",
    "https://docker.mirrors.sjtug.sjtu.edu.cn",
    "https://docker.1panel.live",
    "https://docker.rainbond.cc"
  ],
  "exec-opts": ["native.cgroupdriver=systemd"]
}
EOF
#Docker 加速 2
[root@harbor ~]# sudo tee /etc/docker/daemon.json <<-'EOF'
{
  "registry-mirrors": [
    "https://docker.1panel.live",
    "https://hub.littlediary.cn",
    "https://docker.kejilion.pro",
    "https://docker.1ms.run",
    "https://lispy.org",
    "https://docker.xiaogenban1993.com",
    "https://docker.xuanyuan.me",
    "https://docker.mybacc.com",
    "https://docker-0.unsee.tech",
    "https://dockerpull.cn"
  ],
  "exec-opts": ["native.cgroupdriver=systemd"]
}
EOF
[root@harbor ~]# systemctl enable docker --now
# 1.3 装 Harbor
[root@harbor ~]# cd /soft/
[root@harbor ~]# wget https://github.com/goharbor/harbor/releases/download/v2.6.1/harbor-offline-installer-v2.6.1.tgz
[root@harbor soft]# tar xf harbor-offline-installer-v2.6.1.tgz
[root@harbor soft]# cd harbor
[root@Harbor harbor]# cp harbor.yml.tmpl harbor.yml
[root@Harbor harbor]# cat harbor.yml
...
hostname: s.hmallleasing.com
...
http:
  # port for http, default is 80. If https enabled, this port will redirect to https port
  port: 80
# https related config
https:
  # https port for harbor, default is 443
  port: 443
  # The path of cert and key files for nginx
  certificate: /soft/harbor/ssl/hmallleasing.com.pem
  private_key: /soft/harbor/ssl/hmallleasing.com.key
...
harbor_admin_password: Harbor12345
[root@harbor harbor]#  ./install.sh
# 1.4 配置 Nginx 负载均衡调度
[root@lb ~]# vim s.hmallleasing.com.conf
server {
    listen 443 ssl;
    server_name harbor.hmallleasing.com;
    client_max_body_size 1G; 
    ssl_prefer_server_ciphers on;
    ssl_certificate  /etc/nginx/sslkey/_.hmallleasing.com_chain.crt;
    ssl_certificate_key  /etc/nginx/sslkey/_.hmallleasing.com_key.key;
    location / {
        proxy_pass http://192.168.1.134;
#       include proxy_params;
#       proxy_set_header Host $http_host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        
        proxy_connect_timeout 30;
        proxy_send_timeout 60;
        proxy_read_timeout 60;
        
        proxy_buffering on;
        proxy_buffer_size 32k;
        proxy_buffers 4 128k;
        proxy_temp_file_write_size 10240k;		
        proxy_max_temp_file_size 10240k;
    }
}
server {
    listen 80;
    server_name s.hmallleasing.com;
    return 302 https://$server_name$request_uri;
}
# 1.5 推送镜像至 Harbor
[root@harbor harbor]# docker tag beae173ccac6 harbor.hmallleasing.com/ops/busybox.v1
[root@harbor harbor]# docker login harbor.hmallleasing.com
[root@harbor harbor]# docker push harbor.hmallleasing.com/ops/busybox.v1
# 1.6 Harbor 停止与启动
#停用 Harbor
[root@harbor harbor]# pwd
/soft/harbor
[root@harbor harbor]# docker-compose down
 #启动 Harbor
[root@harbor harbor]# docker-compose up -d
[root@harbor harbor]# docker-compose start

# 二、部署 Gitlab

通常 Gitlab 都是在独立服务器进行部署和维护,但为了更好的掌握 Kubernetes,所以本次课程采用资源清单方式将 Gitlab 以

StatefulSet 方式交付到 Kubernetes 中;

Gitlab 以容器方式运行,需要持久化如下几个目录中的数据:

持久化本地位置容器位置使用
${pvc}/data/var/opt/gitlab用于存储应用程序数据
${pvc}/logs/var/log/gitlab用于存储日志
${pvc}/config/etc/gitlab用于存储 GitLab 配置文件
# 2.1 下载 Gitlab 镜像推送至 Harbor
[root@harbor harbor]# docker pull gitlab/gitlab-ce:14.6.0-ce.0
[root@harbor harbor]# docker tag gitlab/gitlab-ce:14.6.0-ce.0 s.hmallleasing.com/base/gitlab-ce:14.6.0
[root@harbor harbor]# docker login s.hmallleasing.com
[root@harbor harbor]# docker push s.hmallleasing.com/base/gitlab-ce:14.6.0
# 2.2 创建 Service
[root@master01 gitlab]# cat 01-gitlab-svc.yaml 
apiVersion: v1
kind: Service
metadata:
  name: gitlab-svc
  namespace: ops
spec:
  clusterIP: None
  selector:
    app: gitlab
  ports:
  - name: http
    port: 80
    targetPort: 80
  - name: https
    port: 443
    targetPort: 443
# 2.3 创建 Statefulset
# cat 02-gitlab-sts.yaml 
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: gitlab
  namespace: ops
spec:
  serviceName: "gitlab-svc"               # 关联的 headlessService
  selector:
    matchLabels:
      app: gitlab
  template:
    metadata:
      labels:
        app: gitlab
    spec:
      imagePullSecrets:
      - name: harbor-admin
      containers:
      - name: gitlab-ce
        image: s.hmallleasing.com/base/gitlab-ce:14.6.0
        imagePullPolicy: IfNotPresent
        env:
        - name: GITLAB_ROOT_PASSWORD
          value: "admin123"
        - name: GITLAB_OMNIBUS_CONFIG
          value: |
            external_url "http://gitlab.hmallleasing.com"
            gitlab_rails['time_zone'] = 'Asia/Shanghai'
            node_exporter['enable'] = false
            redis_exporter['enable'] = false
            postgres_exporter['enable'] = false
            gitlab_exporter['enable'] = false
            grafana['enable'] = false
            grafana['reporting_enabled'] = false
            prometheus['enable'] = false
            prometheus['monitor_kubernetes'] = false
            gitlab_rails['gitlab_email_enabled'] = true
            gitlab_rails['gitlab_email_from'] = '373370405@qq.com'  #发件邮箱
            gitlab_rails['gitlab_email_display_name'] = 'Nfzl-Gitlab'  #发件人显示名称
            gitlab_rails['smtp_enable'] = true
            gitlab_rails['smtp_address'] = "smtp.qq.com"
            gitlab_rails['smtp_port'] = 465
            gitlab_rails['smtp_user_name'] = "373370405@qq.com"   #发件人邮箱账户
            gitlab_rails['smtp_password'] = "pohleicnfawvb"   #发件人邮箱客户端授权码
            gitlab_rails['smtp_domain'] = "smtp.qq.com"
            gitlab_rails['smtp_authentication'] = "login"
            gitlab_rails['smtp_enable_starttls_auto'] = true
            gitlab_rails['smtp_tls'] = true
        ports:
        - name: http
          containerPort: 80
        - name: https
          containerPort: 443
        volumeMounts:
        - name: data
          mountPath: /etc/gitlab
          subPath: config
        - name: data
          mountPath: /var/opt/gitlab
          subPath: data
        - name: data
          mountPath: /var/log/gitlab
          subPath: logs
  volumeClaimTemplates:         # pvc
  - metadata:
      name: data
    spec:
      accessModes: ["ReadWriteMany"]
      storageClassName: "nfs-storage"
      resources:
        requests:
          storage: 2Gi
# 2.4 创建 Ingress
[root@master01 gitlab]# cat 03-gitlab-ingress.yaml 
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: gitlab-ingress
  namespace: ops
spec:
  ingressClassName: "nginx"
  rules:
  - host: "gitlab.hmallleasing.com"
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: gitlab-svc
            port:
              name: http
# 2.5 更新资源清单
[root@master01 gitlab]# kubectl create ns ops
[root@master01 gitlab]# kubectl create secret docker-registry harbor-admin --docker-server=s.hmallleasing.com --docker-username=admin --docker-password=passwd -n ops
[root@master01 gitlab]# kubectl apply -f .
# 2.6 访问 Gitlab

PixPin_2025-06-09_14-27-11.png

# 三、部署 Sonarqube

SonarQube 是一个开源的代码质量管理系统,用于检测代码中的错误、漏洞。它可以与 Jenkins 集成,让我们能自动化进行

代码质量扫描。

1.jpg

Sonarqube 扫描流程:

  1. 使用 SonarScanner 客户端工具将代码源文件以 http/https 方式推送给 Sonarqube 服务端;
  2. Sonarqube 服务端基于 ElasticSerach 对代码进行分析,而后将分析结果存储至 Database;
  3. Sonarqube 服务端读取 Database 数据,然后将扫描结果进行前端展示;

所以,安装 Sonarqube 之前需要先安装依赖的数据库,后期进行漏洞扫描时还需要借助 SonarScanner 客户端;

# 3.1 部署 Pgsql
# 3.1.1 下载 postgresql 镜像推送至 Harbor
[root@master01 ~]# docker pull postgres:13.8
[root@master01 ~]# docker tag postgres:13.8 s.hmallleasing.com/base/postgres:13.8
[root@master01 ~]# docker login s.hmallleasing.com
[root@master01 ~]# docker push s.hmallleasing.com/base/postgres:13.8
# 3.1.2 部署 Pgsql-sts
[root@k8s-master01 sonarqube]# cat 01-pgsql-sts.yaml 
apiVersion: v1
kind: Service
metadata:
  name: pgsql-svc
  namespace: ops
spec:
  clusterIP: None
  selector:
    app: pgsql
  ports:
  - port: 5432
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: postgresql
  namespace: ops
spec:
  serviceName: "pgsql-svc"
  selector:
    matchLabels:
      app: pgsql
  template:
    metadata:
      labels:
        app: pgsql
    spec:
      imagePullSecrets:
      - name: harbor-admin
      containers:
      - name: postgresql
        image: s.hmallleasing.com/ops/postgres:13.8
        imagePullPolicy: IfNotPresent
        env:
        - name: POSTGRES_DB                   # 数据库
          value: sonardb
        - name: POSTGRES_USER                 # 用户
          value: sonar
        - name: POSTGRES_PASSWORD             # 密码
          value: "Superman*2023"
        ports:
        - containerPort: 5432
        volumeMounts:
        - name: db
          mountPath: /var/lib/postgresql/data
        - name: tz-config
          mountPath: /usr/share/zoneinfo/Asia/Shanghai
        - name: tz-config
          mountPath: /etc/localtime
        - name: timezone
          mountPath: /etc/timezone
      volumes:
      - name: tz-config
        hostPath:
          path: /usr/share/zoneinfo/Asia/Shanghai
          type: ""
      - name: timezone
        hostPath:
          path: /etc/timezone
          type: ""
  volumeClaimTemplates:         
  - metadata:
      name: db
    spec:
      accessModes: ["ReadWriteOnce"]
      storageClassName: "nfs-storage"
      resources:
        requests:
          storage: 5Gi
# 3.1.3 检查 postgresql
[root@k8s-master01 sonarqube]# kubectl exec -it postgresql-0 -n ops -- /bin/bash
root@postgresql-0:/# psql -Usonar -d sonardb 
psql (13.8 (Debian 13.8-1.pgdg110+1))
Type "help" for help.
sonardb=# \l
                             List of databases
   Name    | Owner | Encoding |  Collate   |   Ctype    | Access privileges 
-----------+-------+----------+------------+------------+-------------------
 postgres  | sonar | UTF8     | en_US.utf8 | en_US.utf8 | 
 sonardb   | sonar | UTF8     | en_US.utf8 | en_US.utf8 | 
 template0 | sonar | UTF8     | en_US.utf8 | en_US.utf8 | =c/sonar         +
           |       |          |            |            | sonar=CTc/sonar
 template1 | sonar | UTF8     | en_US.utf8 | en_US.utf8 | =c/sonar         +
           |       |          |            |            | sonar=CTc/sonar
(4 rows)
# 3.2 部署 Sonarqube
# 3.2.1 下载 sonarqube 镜像推送至 Harbor
[root@master01 ~]# docker pull sonarqube:9.9.8-community
[root@master01 ~]# docker tag sonarqube:9.9.8-community s.hmallleasing.com/base/sonarqube:9.9.8
[root@master01 ~]# docker login s.hmallleasing.com
[root@master01 ~]# docker push s.hmallleasing.com/base/sonarqube:9.9.8
# 3.2.2 部署 Sonarqube-sts
# cat 02-sonarqube-sts.yaml 
apiVersion: v1
kind: Service
metadata:
  name: sonarqube-svc
  namespace: ops
spec:
  clusterIP: None
  selector:
    app: sonarqube
  ports:
  - name: web
    port: 9000
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: sonarqube
  namespace: ops
spec:
  serviceName: "sonarqube-svc"
  selector:
    matchLabels:
      app: sonarqube
  template:
    metadata:
      labels:
        app: sonarqube
    spec:
      imagePullSecrets:
      - name: harbor-admin
      initContainers:
      - name: set-kernel
        image: busybox
        command: ["sh", "-c", "sysctl -w vm.max_map_count=524288 ; sysctl -w fs.file-max=131072 ; ulimit -n 131072 ; ulimit -u 8192"]
        securityContext:
          privileged: true
      containers:
      - name: sonarqube
        image: s.hmallleasing.com/base/sonarqube:9.9.8
        imagePullPolicy: IfNotPresent
        env:
        - name: JAVA_OPTS
          value: -Duser.timezone=Asia/Shanghai
        - name: SONARQUBE_JDBC_USERNAME               # 连接 pgsql 用户名
          value: sonar
        - name: SONARQUBE_JDBC_PASSWORD               # 连接 pgsql 密码
          value: "admin123"
        - name: SONARQUBE_JDBC_URL                    # 连接 pgsql 地址 / 数据库
          value: jdbc:postgresql://pgsql-svc:5432/sonardb
        resources:
          limits:
            cpu: 1500m
            memory: 2048Mi
        ports:
        - name: web
          containerPort: 9000
        volumeMounts:
        - name: data
          mountPath: /opt/sonarqube/data
          subPath: data
        - name: data
          mountPath: /opt/sonarqube/logs
          subPath: logs
        - name: data
          mountPath: /opt/sonarqube/extensions
          subPath: extensions
        - name: tz-config
          mountPath: /usr/share/zoneinfo/Asia/Shanghai
        - name: tz-config
          mountPath: /etc/localtime
        - name: timezone
          mountPath: /etc/timezone
      volumes:
      - name: tz-config
        hostPath:
          path: /usr/share/zoneinfo/Asia/Shanghai
          type: ""
      - name: timezone
        hostPath:
          path: /etc/timezone
          type: ""
  volumeClaimTemplates:         
  - metadata:
      name: data
    spec:
      accessModes: ["ReadWriteOnce"]
      storageClassName: "nfs-storage"
      resources:
        requests:
          storage: 5G
# 3.2.3 创建 Ingress
[root@k8s-master01 sonarqube]# cat 03-sonarqube-ingress.yaml 
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: sonarqube-ingress
  namespace: ops
spec:
  ingressClassName: "nginx"
  rules:
  - host: "sonar.hmallleasing.com"
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: sonarqube-svc
            port:
              name: web
# 3.2.4 访问 sonarqube

通过 http://sonar.hmallleasing.com ,用户名:admin,密码:admin

1.png

安装中文插件

image-20230322101425876.png

# 四、部署 Jenkins

既然是基于 Kubernetes 来实现 CI/CD,那么最好将 Jenkins 以 Pod 的形式运行在 Kubernetes 集群中。其次 Jenkins 没有数

据库,所有的数据都存储在本地,所以只需要将 Jenkins 的数据目录持久化下来就可以了。

# 4.1 下载 Jenkins 镜像
[root@master01 ~]# docker pull jenkins/jenkins:2.504.2-lts
[root@master01 ~]# docker tag jenkins/jenkins:2.504.2-lts s.hmallleasing.com/base/jenkins:2.504.2
[root@master01 ~]# docker login s.hmallleasing.com
[root@master01 ~]# docker push s.hmallleasing.com/base/jenkins:2.504.2
# 4.2 创建 RBAC

后期 Jenkins 需要创建 Slave Pod 来完成流水线的执行,为此我们需要一些权限。

[root@k8s-master01 jenkins]# cat 01-jenkins-rbac.yaml 
# serviceaccount
apiVersion: v1
kind: ServiceAccount
metadata:
  name: jenkins
  namespace: ops
---
# clusterRole
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: jenkins
rules:
  - apiGroups: ["extensions", "apps"]
    resources: ["deployments", "ingresses"]
    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", "events"]
    verbs: ["get", "list", "watch"]
  - apiGroups: [""]
    resources: ["secrets"]
    verbs: ["get"]
---
# clusterrolebinding
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: jenkins
  namespace: ops
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: jenkins
subjects:
  - kind: ServiceAccount
    name: jenkins
    namespace: ops
# 4.3 创建 Service
[root@k8s-master01 jenkins]# cat 02-jenkins-svc.yaml 
apiVersion: v1
kind: Service
metadata:
  name: jenkins-svc
  namespace: ops
spec:
  clusterIP: None
  selector:
    app: jenkins
  ports:
    - name: http
      port: 8080
      targetPort: 8080
    - name: agent
      port: 50000
      targetPort: 50000
# 4.4 创建 StatefulSet
[root@k8s-master01 jenkins]# cat 03-jenkins-sts.yaml 
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: jenkins
  namespace: ops
spec:
  serviceName: "jenkins-svc"
  selector:
    matchLabels:
      app: jenkins
  template:
    metadata:
      labels:
        app: jenkins
    spec:
      serviceAccount: jenkins
      imagePullSecrets:
      - name: harbor-admin
      containers:
        - name: jenkins
          image: s.hmallleasing.com/base/jenkins:2.504.2
          imagePullPolicy: IfNotPresent
          securityContext:     # 添加参数启用容器 root 权限
            privileged: true
            runAsUser: 0        # root 身份运行
          env:
          - name: JAVA_OPTS     
            value: -Duser.timezone=Asia/Shanghai
          ports:
            - name: http
              containerPort: 8080
            - name: agent
              containerPort: 50000 
          resources:
            limits:
              cpu: 1500m
              memory: 4096Mi
          readinessProbe:          # 就绪探针
            httpGet:
              path: /login
              port: 8080
            initialDelaySeconds: 60
            timeoutSeconds: 5
            failureThreshold: 12
          volumeMounts:
            - name: data
              mountPath: /var/jenkins_home
            - name: tz-config
              mountPath: /usr/share/zoneinfo/Asia/Shanghai
            - name: tz-config
              mountPath: /etc/localtime
            - name: timezone
              mountPath: /etc/timezone
      volumes:
      - name: tz-config
        hostPath:
          path: /usr/share/zoneinfo/Asia/Shanghai
          type: ""
      - name: timezone
        hostPath:
          path: /etc/timezone
          type: ""
  volumeClaimTemplates:
  - metadata:
      name: data
    spec:
      accessModes: ["ReadWriteOnce"]
      storageClassName: "nfs-storage"
      resources:
        requests:
          storage: 5Gi
# 4.5 创建 Ingress
[root@k8s-master01 jenkins]# cat 04-jenkins-ingress.yaml 
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: jenkins-ingress
  namespace: ops
spec:
  ingressClassName: "nginx"
  rules:
  - host: jenkins.hmallleasing.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: jenkins-svc 
            port:
              name: http
# 4.6 初始化 Jenkins

1、查看 jenkins 对应的初始化密码;

1.png

2、跳过插件安装,进入 jenkins 自行安装即可;

2.png

# 4.7 Jenkins 配置与插件

1、修改默认管理员密码

3.png

2、安装 Jenkins 插件

#1. 将 jenkins 插件的路径,更改为国内源
系统管理 -> 插件管理 -> 高级 -> 升级站点 -> 修改 URL->https://updates.jenkins.io/update-center.json 替换 https://mirrors.huaweicloud.com/jenkins/updates/update-center.json

#2. 安装 Jenkins 插件

中文插件: Localization: Chinese

Git 插件: git、gitlab

Sonar 插件: SonarQube Scanner

Pipeline 插件: pipeline、Stage View、BlueOcean

Kubernetes 插件: Kubernetes

# 五、Jenkins Pipeline

# 5.1 什么是 Pipeline

Pipeline 就是通过 “代码的方式” 将多个步骤的任务连接起来。共同来完成一件事;比如:应用发布就会牵扯到非常多的步骤,获取代码 -> 代码扫描 -> 代码编译 -> 制作镜像 -> 推送仓库 -> 部署应用,将这些步骤通过代码的方式组织在一起完成这次应用发布,这个就可以称之为流水线,亦或者是 Pipeline 流水线;

1.png

# 5.2 Pipeline 代码示例

agent: 节点

stage: 阶段

steps: 动作

pipeline{
    agent any    //目前只有一台jenkins,所以他会在本地执行
	
	environment {       //全局变量
		Harbor_Url="s.hmallleasing.com"
		Harbor_Pro="base"
	}
	
    stages {
        stage('下载代码') {
			environment {     //局部变量
				Image_Name="nf-flms-order:v1.1"
				Full_Image="${Harbor_Url}/${Harbor_Pro}/${Image_Name}"
			}		
            steps {
				sh 'echo "Get Gitlab Code ${Full_Image}"'
            }
        }
        stage('检测代码') {
			environment {     //局部变量
				Image_Name="nf-flms-static:v1.1"
				Full_Image="${Harbor_Url}/${Harbor_Pro}/${Image_Name}"		
			}
            steps {
                sh 'echo "Unit Test ${Full_Image}"'
            }
        }
        stage('编译代码') {
            steps {
                sh 'echo "Build Code"'
            }
        }
        stage('制作镜像') {
            steps {
			    sh 'echo "Build Docker"'
            }
        }
        stage('部署应用') {
            steps {
                sh 'echo "Deploy Code"'
            }
        }
    }
}

# 六、 Jenkins Slave 架构

# 6.1 架构基本说明

所谓 JenkinsMaster/Slave 架构,及在 Master 上进行任务分配。然后由 Slave 来完成,不过 Slave 运行方式有两种:

  • 静态 SLave:需要固定的节点,配置其对应环境,手动注册到 Master,然后执行任务,任务完成节点处于空闲等待状态;
  • 动态 Slave:由 Master 动态创建 Slave 的 Pod,自动注册到 Master,然后执行任务,任务结束 Pod 自动销毁;

静态 Jenkins Slave

1、能够分担主节点上的压力,加快构建速度(所有任务都由 Master 执行,造成构建速度缓慢,且任务多会出现排队现象

2、能够将特定的任务在特定的主机上运行(比如:不同的任务需要不同的编译环境)

2.png

痛点:

  • 1、Master 发生单点故障时,整个 Jenkins 都没办法使用;
  • 2、每个 Slave 的环境不一样,用于完成不同项目的编译打包工作,但这些不同环境的配置管理及维护都特别困难;
  • 3、有的 Slave 构建任务频繁,可能出现排队等待,而有的 Slave 又处于空闲状态,所以会出现资源分配严重不均衡;
  • 4、因为每个 Slave 都需要一台虚拟机,当 Slave 空间时,等于就是空跑,资源浪费明显;

** 动态 Jenkins Slave **

所谓动态 Slave,就是根据任务进行动态供应和动态删除。Jenkins Master 和 Jenkins Slave 都是以 Pod 的形式运行在 Kubernetes 集群节点上,Master 运行在其中一个节点上,其配置数据存储在一个持久卷声明中。而 Slave 则随机运行在各个节点上,但它不会一直处于运行状态,而是根据需求动态创建并自动删除。

3.png

优势:

  • 1、高可用性:(当 Jenkins Master 故障时,Kubernetes 会自动创建一个新的 Jenkins Master 容器,并将持久卷挂载至新创建的容器,保证数据不会丢失,从而实现 Jenkins 的高可用性。)
  • 2、高可扩缩性:(当 Kubernetes 集群因资源不足而导致任务长时间排队等待时,可以向集群新增节点,来环节压力。)
  • 3、资源分配合理:(Kubernetes 动态分配 Slave 至空闲节点,避免因单个节点资源利用率高而导致任务排队等待。)

# 七、Jenkins 动态 Slave 配置

# 7.1 配置 Kubernetes

系统管理 -> 节点管理 ->Cloud->Add a New Cloud->Kubernetes

1.png

由于 Jenkins 是通过 Pod 运行在 Kubernetes 集群中,所以通过 service 地址即可连接 Kubernetes 集群,正常通信。

2.png

# 7.2 配置 Jenkins

Jenkins 地址:http://jenkins-svc.ops.svc.cluster.local:8080

Jenkins 通道:jenkins-svc.ops.svc.cluster.local:50000

3.png

# 7.3 运行测试流水线

1、编写流水线

pipeline {
  agent {
    kubernetes {
      cloud 'kubernetes'
    }
  }
	stages {
		stage('输出主机名称') {
			steps {
				sh 'hostname'
			}
		}
		stage('等待片刻') {
			steps {
				sh 'sleep 60'
			}
		}
	}
}

2、jenkins 会自动拉起一个 Pod,运行 agent 容器注册到 Master 节点,然后进行任务的执行

[root@k8s-master01 03-jenkins]# kubectl get pods -n ops
NAME                                   READY   STATUS    RESTARTS      AGE
gitlab-0                               1/1     Running   0             20h
jenkins-0                              1/1     Running   2 (22m ago)   86m
pipeline-daemon1-4-z9rqq-0t4nr-ql2v3   1/1     Running   0             2s    #jenkins slave
postgresql-0                           1/1     Running   0             17h
sonarqube-0                            1/1     Running   0             17h

4.png

# 八、制作 Pod 模板镜像

jnlp 镜像是用来连接 JenkinsMaster 以及共享 Master 的 WORKSPACE,但该镜像并没有 maven、docker、kubectl 等常用命令,为此我们需要定制几个镜像,后期通过 Pipeline 将不同的任务交由同一个 Pod 的不同的容器来执行。

# 8.1 Maven

1、下载 settings.xml

[root@k8s-master01 maven]# wget https://linux.oldxu.net/settings_docker.xml

2、Dockerfile

[root@k8s-master01 maven]# cat Dockerfile
FROM maven:3.8.6-openjdk-8
ADD ./settings_docker.xml /usr/share/maven/conf/settings.xml
RUN /bin/cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime

3、构建镜像并推送私有仓库

[root@k8s-master01 maven]# docker build -t s.hmallleasing.com/base/maven:3.8.6 .
[root@k8s-master01 maven]# docker push s.hmallleasing.com/base/maven:3.8.6
# 8.2 sonar
[root@k8s-master01 ~]# docker pull emeraldsquad/sonar-scanner:2.3.0
[root@k8s-master01 ~]# docker tag emeraldsquad/sonar-scanner:2.3.0 s.hmallleasing.com/base/sonar-scanner:2.3.0
[root@k8s-master01 ~]# docker push s.hmallleasing.com/base/sonar-scanner:2.3.0
# 8.3 NodeJs

1、Dockerfile

[root@k8s-master01 nodejs]# cat Dockerfile
FROM centos:7
RUN /bin/cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
RUN curl --silent --location https://rpm.nodesource.com/setup_14.x |bash -
RUN curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo
RUN yum install nodejs gcc-c++ make vim -y && \
    yum clean all

2、构建镜像并推送私有仓库

[root@k8s-master01 nodejs]# docker build -t s.hmallleasing.com/base/nodejs:14.20 .
[root@k8s-master01 nodejs]# docker push s.hmallleasing.com/base/nodejs:14.20
# 8.4 Docker
[root@k8s-master01 ~]# docker pull docker:20.10 
[root@k8s-master01 ~]# docker tag docker:20.10 s.hmallleasing.com/base/docker:20.10
[root@k8s-master01 ~]# docker push s.hmallleasing.com/base/docker:20.10
# 8.5 kubectl

1、编写 kubernetes.repo 文件

cat <<EOF> ./kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://mirrors.aliyun.com/kubernetes-new/core/stable/v1.32/rpm/
enabled=1
gpgcheck=0
gpgkey=https://mirrors.aliyun.com/kubernetes-new/core/stable/v1.32/rpm/repodata/repomd.xml.key
EOF

2、Dockerfile

[root@k8s-master01 kubectl]# cat Dockerfile 
FROM centos:7
# 1、调整时区
RUN /bin/cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \
    echo 'Asia/Shanghai' >/etc/timezone
RUN curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo
# 2、添加 yum 源
ADD ./kubernetes.repo /etc/yum.repos.d/kubernetes.repo
# 3、安装 Kubectl
RUN yum makecache && yum install kubectl-1.32.3 -y && \
    yum clean all

3、构建镜像并推送私有仓库

[root@k8s-master01 kubectl]# docker build -t s.hmallleasing.com/base/kubectl:1.32.3 .
[root@k8s-master01 kubectl]# docker push s.hmallleasing.com/base/kubectl:1.32.3

# 九、测试 Pod 模板镜像

运行一个流水线任务,定义 Pod 模板中的容器名称以及容器镜像地址,而后定义任务,不同的任务由不同的容器来执行

# 9.1 定义 podTemplate
pipeline {
  agent {
    kubernetes {
      cloud 'kubernetes'
      yaml '''
        apiVersion: v1
        kind: Pod
        spec:
          imagePullSecrets:
          - name: harbor-admin
          volumes:
          - name: data
            nfs:
              server: 192.168.1.75
              path: /data/nfs/maven
          - name: dockersocket
            hostPath:
              path: /run/docker.sock
          containers:
          - name: maven
            image: s.hmallleasing.com/base/maven:3.8.6
            imagePullPolicy: IfNotPresent
            command: ["cat"]
            tty: true
            volumeMounts:
            - name: data
              mountPath: /root/.m2
          - name: nodejs
            image: s.hmallleasing.com/base/nodejs:14.20
            imagePullPolicy: IfNotPresent
            command: ["cat"]
            tty: true
          - name: sonar
            image: s.hmallleasing.com/base/sonar-scanner:2.3.0
            imagePullPolicy: IfNotPresent
            command: ["cat"]
            tty: true
          - name: docker
            image: s.hmallleasing.com/base/docker:20.10
            imagePullPolicy: IfNotPresent
            command: ["cat"]
            tty: true
            volumeMounts:
            - name: dockersocket
              mountPath: /run/docker.sock
          - name: kubectl
            image: s.hmallleasing.com/base/kubectl:1.32.3
            imagePullPolicy: IfNotPresent
            command: ["cat"]
            tty: true
      '''
    }
  }
  stages {
    stage('maven测试') {
      steps {
        container('maven') {
		  sh 'mvn --version'
        }
      }
    }
    stage('nodejs测试') {
      steps {
        container('nodejs') {
		  sh 'node -v'
        }
      }
    }	
	
    stage('docker测试') {
      steps {
        container('docker') {
		  sh 'docker ps'
        }
      }
    }		
    stage('kubectl测试') {
      steps {
        container('kubectl') {
		  sh 'kubectl version'
        }
      }
    }		
  }
}

#在 nfs 中创建 /data/maven 文件夹,将 maven 缓存 /root/.m2 挂载至该目录

# 9.2 运行 Pipeline

构建流水线,检查各个阶段调用容器执行的输出结果是否 OK;

5.png

此文章已被阅读次数:正在加载...更新于

请我喝[茶]~( ̄▽ ̄)~*

Xu Yong 微信支付

微信支付

Xu Yong 支付宝

支付宝