ziyun666888头像
关注

Docker + K8s 云原生多节点 Web 服务架构搭建与自动化运维

项目描述:

使用 kubeadm 搭建多节点 Kubernetes 集群(1 Master + 2 Worker,RHEL 9 虚拟机),将前端(Nginx)与后端(Spring Boot)应用容器化并推送至 Harbor 私有镜像仓库,集群节点统一从 Harbor 拉取镜像运行;使用 Deployment 管理前后端 Pod 的副本与滚动更新;使用 Service 实现集群内部服务发现与负载均衡;使用 Ingress 提供统一域名入口对外访问;在此基础上拓展 Jenkins Pipeline 实现 CI/CD 自动化发布,打通"代码 → 构建 → 入库 → 部署"全流程闭环。

架构图:

技术栈:

  • 容器与镜像:Docker、containerd 2.x、Harbor 私有镜像仓库
  • 容器编排:Kubernetes(kubeadm 多节点集群、Deployment、Service、Ingress-Nginx、Readiness 探针、滚动更新)
  • CI/CD:Jenkins Pipeline(参数化构建、凭据管理)、Buildah、Git
  • 应用层:Spring Boot(Java 17 + Maven)、Nginx 前端静态资源
  • 系统与网络:RHEL 9、VMware 虚拟化网络、iptables、tcpdump 抓包

操作步骤:

主机IP角色软件
docker-master172.25.254.50K8s 控制节点containerd、kubeadm、kubectl、kubelet、harbor仓库、Jenkins
docker-worker1172.25.254.60K8s 工作节点containerd、kubelet
docker-worker2172.25.254.70K8s 工作节点containerd、kubelet

阶段 1:三台机器统一基础环境初始化(所有节点执行)

1.1 主机域名解析 /etc/hosts

解释:配置本地域名解析

1.2 内核参数开启容器网络转发

需要加载一下内核模块:modprobe br_netfilter

编辑 /etc/sysctl.conf

解释:K8s 网络必需,开启 iptables 网桥转发、IP 转发,Pod 之间、Pod 访问外网网络互通

1.3 关闭 Swap 分区

bash

解释:Kubernetes 官方强制要求关闭交换分区,内存交换会影响调度与稳定性。

swapoff -a
sed -i '/swap/s/^/#/' /etc/fstab

阶段 2:docker-master、worker01、worker02 安装容器运行时 containerd

2.1 安装 containerd(CentOS Stream9)

解释:containerd 是 K8s 标准容器运行时,替代 docker;systemd cgroup 和宿主机 systemd 统一,避免资源管控异常。

  先配置docker源

 yum  install -y yum-utils  #安装依赖
 
[root@docker-master ~]# cat /etc/yum.repos.d/docker.repo
[docker]
name=Docker
baseurl=https://mirrors.aliyun.com/docker-ce/linux/centos/$releasever/$basearch/stable
enabled=1
gpgcheck=0

yum install makecache

yum install -y containerd-io

然后生成一份默认配置

containerd config default > /etc/containerd/config.toml

2.2 修改配置 /etc/containerd/config.toml

重点修改两处:

  1. pause 镜像替换国内源
  2. cgroup 驱动改为 systemd
[root@docker-master ~]# sed -i 's#sandbox_image = "registry.k8s.io/pause:3.6"#sandbox_image = "registry.aliyuncs.com/google_containers/pause:3.9"#' /etc/containerd/config.toml
[root@docker-master ~]# sed -i 's/SystemdCgroup \= false/SystemdCgroup \= true/' /etc/containerd/config.toml
[root@docker-master ~]# systemctl daemon-reload
[root@docker-master ~]# systemctl restart containerd.service

注意: 剩下的两个机子同理方法一样

阶段 3:master/worker 节点安装 Kubernetes 组件

新建 yum 源文件 /etc/yum.repos.d/kubernetes.repo

[root@docker-master ~]# cat /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg
[root@docker-master ~]#

安装组件:

  • kubeadm:集群初始化工具
  • kubectl:集群命令行客户端
  • kubelet:每个节点上管理 Pod 的代理服务
yum install -y kubeadm-1.28.2  kubectl-1.28.2 kubelet-1.28.2
systmcel enable --now kubelet
systemctl  enable --now kubelet

注意: 剩下的两个机子同理方法一样

阶段 4:master 节点初始化 K8s 集群

4.1 执行初始化命令

bash

kubeadm init --apiserver-advertise-address=172.25.254.50 --image-repository registry.aliyuncs.com/google_containers --kubernetes-version v1.28.2 --service-cidr=10.96.0.0/16 --pod-network-cidr=10.244.0.0/16

参数说明:

  • apiserver-advertise-address:master 内网通信地址
  • image-repository:阿里云镜像源,防止谷歌镜像拉取失败
  • service-cidr:集群 Service 虚拟 IP 网段
  • pod-network-cidr:Pod 网段,必须和 flannel 配置保持一致

这里执行成功他会生成一个:sha256:a1a113afade129c1c08f68aae3030b098d70681b61848edb7c2f56f911ce7cd2类似这种的一个,下面是我执行完生成的,可以参考看一下,那个太长了,我就截了一部分

rol-plane] Creating static Pod manifest for "kube-controller-manager"
[control-plane] Creating static Pod manifest for "kube-scheduler"
[kubelet-start] Writing kubelet environment file with flags to file "/var/lib/kubelet/kubeadm-flags.env"
[kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml"
[kubelet-start] Starting the kubelet
[wait-control-plane] Waiting for the kubelet to boot up the control plane as static Pods from directory "/etc/kubernetes/manifests". This can take up to 4m0s
[apiclient] All control plane components are healthy after 6.013376 seconds
[upload-config] Storing the configuration used in ConfigMap "kubeadm-config" in the "kube-system" Namespace
[kubelet] Creating a ConfigMap "kubelet-config" in namespace kube-system with the configuration for the kubelets in the cluster
[upload-certs] Skipping phase. Please see --upload-certs
[mark-control-plane] Marking the node docker-master as control-plane by adding the labels: [node-role.kubernetes.io/control-plane node.kubernetes.io/exclude-from-external-load-balancers]
[mark-control-plane] Marking the node docker-master as control-plane by adding the taints [node-role.kubernetes.io/control-plane:NoSchedule]
[bootstrap-token] Using token: ot4p5v.zmj33p4ipbe0puep
[bootstrap-token] Configuring bootstrap tokens, cluster-info ConfigMap, RBAC Roles
[bootstrap-token] Configured RBAC rules to allow Node Bootstrap tokens to get nodes
[bootstrap-token] Configured RBAC rules to allow Node Bootstrap tokens to post CSRs in order for nodes to get long term certificate credentials
[bootstrap-token] Configured RBAC rules to allow the csrapprover controller automatically approve CSRs from a Node Bootstrap Token
[bootstrap-token] Configured RBAC rules to allow certificate rotation for all node client certificates in the cluster
[bootstrap-token] Creating the "cluster-info" ConfigMap in the "kube-public" namespace
[kubelet-finalize] Updating "/etc/kubernetes/kubelet.conf" to point to a rotatable kubelet client certificate and key
[addons] Applied essential addon: CoreDNS
[addons] Applied essential addon: kube-proxyYour Kubernetes control-plane has initialized successfully!To start using your cluster, you need to run the following as a regular user:mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown \((id -u):\)(id -g) $HOME/.kube/configAlternatively, if you are the root user, you can run:export KUBECONFIG=/etc/kubernetes/admin.confYou should now deploy a pod network to the cluster.
Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
https://kubernetes.io/docs/concepts/cluster-administration/addons/Then you can join any number of worker nodes by running the following on each as root:kubeadm join 172.25.254.50:6443 --token ot4p5v.zmj33p4ipbe0puep 
--discovery-token-ca-cert-hash sha256:a1a113afade129c1c08f68aae3030b098d70681b61848edb7c2f56f911ce7cd2

注意:上面的集群初始化只有master机子上执行

worker节点加入集群:

[root@docker-worker1 ~]# kubeadm join 172.25.254.50:6443 --token ot4p5v.zmj33p4ipbe0puep --discovery-token-ca-cert-hash sha256:a1a113afade129c1c08f68aae3030b098d70681b61848edb7c2f56f911ce7cd2
 

[root@docker-worker2 ~]# kubeadm join 172.25.254.50:6443 --token ot4p5v.zmj33p4ipbe0puep --discovery-token-ca-cert-hash sha256:a1a113afade129c1c08f68aae3030b098d70681b61848edb7c2f56f911ce7cd2

4.2 配置 kubectl 权限

mkdir -p $HOME/.kube
cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
chown $(id -u):$(id -g) $HOME/.kube/config

4.3 部署 Flannel 网络插件

wget https://github.com/flannel-io/flannel/releases/download/v0.28.9/kube-flannel.yml
sed -i 's#ghcr.io/flannel-io/flannel#ghcr.nju.edu.cn/flannel-io/flannel#' kube-flannel.yml
sed -i 's#ghcr.io/flannel-io/flannel-cni-plugin#ghcr.nju.edu.cn/flannel-io/flannel-cni-plugin#' kube-flannel.yml
kubectl apply -f kube-flannel.yml
[root@docker-master ~]# kubectl get node
NAME             STATUS   ROLES           AGE     VERSION
docker-master    Ready    control-plane   3d22h   v1.28.2
docker-worker1   Ready    <none>          3d21h   v1.28.2
docker-worker2   Ready    <none>          3d21h   v1.28.2
[root@docker-master ~]# kubectl get pods -n kube-flannel
NAME                    READY   STATUS    RESTARTS   AGE
kube-flannel-ds-cvkn4   1/1     Running   0          42h
kube-flannel-ds-s94ng   1/1     Running   0          42h
kube-flannel-ds-v8pgd   1/1     Running   0          42h
[root@docker-master ~]#

状态全部 Ready 代表集群就绪。参考一下这样就代表集群部署完成了

阶段 5:devops-master(172.25.254.50)部署 Harbor 私有镜像仓库

5.1  安装 docker、docker-compose

前面已经装过docker源了这里就直接安装包就好了

yum install -y docker-ce docker-ce-cli docker-compose-plugin.x86_64
# 服务端   客户端   dockercompose的安装包
 systemctl enable --now docker.service   # 开机自启

5.2 下载 Harbor包

mkdir /opt/harbor
cd /opt/harbor/
wget https://gh.llkk.cc/https://github.com/goharbor/harbor/releases/download/v2.8.0/harbor-offline-installer-v2.8.0.tgz
tar xzf harbor-offline-installer-v2.8.0.tgz

5.3 修改配置 harbor.yml

cp harbor.yml.tmpl harbor.yml
vim harbor.yml
hostname: docker-master
harbor_admin_password: lee
# 这里我就修改了这两个地方 

5.4 一键安装

bash

./install.sh

5.5 关键操作:所有 K8s 节点配置 containerd 支持 http 仓库

master、worker01、worker02 全部执行

解释:Harbor 没有 https 证书,containerd 默认拒绝 http 仓库,必须配置此段。

mkdir -p /etc/containerd/certs.d/harbor.local
cat > /etc/containerd/certs.d/harbor.local/hosts.toml << EOF
server = "http://harbor.local"
[host."http://harbor.local"]
capabilities = ["pull","push","resolve"]
EOF
systemctl restart containerd

5.6 网页访问 172.25.254.50 

这里部署好之后创建一个项目:

阶段 6:devops-server 构建前后端镜像,推送 Harbor

6.1 后端 SpringBoot Dockerfile(backend/Dockerfile)

[root@docker-master backend]# mkdir -p /opt/code/backend/Dockerfile
[root@docker-master backend]# vim Dockerfile

FROM docker.io/library/maven:3.8.7-eclipse-temurin-17 AS builder
WORKDIR /app
COPY pom.xml .
COPY src ./src
RUN mvn clean package -Dmaven.test.skip=true

FROM docker.io/library/eclipse-temurin:17-jre-alpine
WORKDIR /app
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser
COPY --from=builder /app/target/*.jar app.jar
ENV TZ=Asia/Shanghai
ENTRYPOINT ["java","-jar","app.jar"]

这里我没有Springboot的后端代码 就手搓的

6.2 前端 Vue Dockerfile(front/Dockerfile)

[root@docker-master front]# mkdir -p /opt/code/front/Dockerfile
[root@docker-master front]# vim Dockerfile
FROM docker.io/library/nginx:alpine
COPY dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

前端也是手搓的一个静态的网页

6.3 前端配套 nginx.conf

[root@docker-master front]# mkdir -p /opt/code/front/nginx.conf
[root@docker-master front]# vim nginx.conf
server {
    listen 80;
    server_name localhost;
    root /usr/share/nginx/html;
    index index.html;
    # 前端/api路径反向代理到后端service
    location /api/ {
        proxy_pass http://backend-service:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

6.4 构建 + 推送镜像命令 

docker tag docker-master/web/web-back:v1.0.0 172.25.254.50/web/web-back:v1.0.0
docker push 172.25.254.50/web/web-back:v1.0.0


docker build -t 172.25.254.50/web/web-front:v1.0.0 /opt/code/front
docker push 172.25.254.50/web/web-front:v1.0.0

构建成功的:

推送成功的:

阶段 7:Master 节点应用 K8s 资源清单部署 Web 业务

7.1 namespace.yaml 命名空间

作用:资源隔离,不同业务放到不同命名空间

[root@docker-master k8s-yaml]# mkdri -p  /opt/code/k8s-yaml
[root@docker-master k8s-yaml]# vim namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: web-project
[root@docker-master k8s-yaml]# kubectl apply -f namespace.yaml

7.2 configmap.yaml 配置文件

作用:存放明文配置,挂载进 Pod,无需重新打包镜像修改配置

[root@docker-master k8s-yaml]# vim configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: web-config
  namespace: web-project
data:
  log.level: info
[root@docker-master k8s-yaml]# kubectl apply -f configmap.yaml

7.3 secret.yaml 敏感信息

作用:存放密码等敏感数据,base64 编码(不是加密)

[root@docker-master k8s-yaml]# vim secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: web-secret
  namespace: web-project
type: Opaque
data:
  db-passwd: Um9vdEAxMjM0NTY=
[root@docker-master k8s-yaml]# kubectl apply -f secret.yaml

7.4 mysql-pvc.yaml 持久化存储声明

解释:PVC 向集群申请存储,数据库数据持久化,Pod 重建数据不丢失

[root@docker-master k8s-yaml]# vim mysql-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mysql-pvc
  namespace: web-project
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi
[root@docker-master k8s-yaml]# kubectl apply -f mysql-pvc.yaml

7.5 backend-deploy.yaml 后端 Deployment+Service

关键点解释:

  1. replicas:2 运行 2 个后端实例实现高可用
  2. RollingUpdate:滚动更新,零停机发布
  3. resources:容器资源配额,防止资源抢占
  4. readinessProbe:就绪探针,健康才加入 Service 接收流量
  5. ClusterIP:集群内部访问,前端通过 service 名称访问后端
[root@docker-master k8s-yaml]# vim backend-deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-backend
  namespace: web-project
spec:
  replicas: 2
  selector:
    matchLabels:
      app: web-backend
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
  template:
    metadata:
      labels:
        app: web-backend
    spec:
      containers:
      - name: backend
        image: 172.25.254.50/web/web-back:v1.0.0 # 写刚刚构建的镜像名字 不要写我的
        resources:
          requests:
            cpu: "200m"
            memory: "512Mi"
          limits:
            cpu: "1000m"
            memory: "1Gi"
        env:
        - name: DB_PASSWORD
          valueFrom:
            secretKeyRef:
              name: web-secret
              key: db-passwd
        ports:
        - containerPort: 8080
        readinessProbe:
          httpGet:
            path: /actuator/health
            port: 8080
          initialDelaySeconds: 10
          periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
  name: backend-service
  namespace: web-project
spec:
  selector:
    app: web-backend
  ports:
  - port: 8080
    targetPort: 8080
  type: ClusterIP
[root@docker-master k8s-yaml]# kubectl apply -f backend-deploy.yaml

7.6 front-deploy.yaml 前端 Deployment+Service

[root@docker-master k8s-yaml]# vim front-deply.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-frontend
  namespace: web-project
spec:
  replicas: 2
  selector:
    matchLabels:
      app: web-frontend
  template:
    metadata:
      labels:
        app: web-frontend
    spec:
      containers:
      - name: frontend
        image: 172.25.254.50/web/web-front:v1.0.0  # 一样的写你构建的镜像
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: frontend-service
  namespace: web-project
spec:
  selector:
    app: web-frontend
  ports:
  - port: 80
    targetPort: 80
  type: ClusterIP
[root@docker-master k8s-yaml]# kubectl apply -f front-deploy.yaml

7.7 ingress.yaml 域名入口(前置条件:部署 ingress-nginx 控制器)

解释:Ingress 提供七层反向代理,外部通过域名web.demo.com访问前端服务

[root@docker-master k8s-yaml]# kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/cloud/deploy.yaml
[root@docker-master k8s-yaml]# vim ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: web-ingress
  namespace: web-project
spec:
  ingressClassName: nginx
  rules:
  - host: web.demo.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: frontend-service
            port:
              number: 80
[root@docker-master k8s-yaml]# kubectl apply -f ingress.yaml

这里还要配置  让、让containerd 知道拉 Harbor 镜像时"别走 HTTPS,用 HTTP"

每个机子都要配  master   worker1  worker 2

[root@docker-master k8s-yaml]# cd /etc/containerd/certs.d/
[root@docker-master certs.d]# ll
总用量 0
drwxr-xr-x 2 root root 24  8月 17 14:26 172.25.254.50
drwxr-xr-x 2 root root 24  8月 17 14:25 docker-master
drwxr-xr-x 2 root root 24  8月 17 14:26 docker-master:80
[root@docker-master certs.d]# cat 172.25.254.50/
cat: 172.25.254.50/: 是一个目录
[root@docker-master certs.d]# cd 172.25.254.50/
[root@docker-master 172.25.254.50]# ll
总用量 4
-rw-r--r-- 1 root root 129  8月 17 14:59 hosts.toml
[root@docker-master 172.25.254.50]# cat hosts.toml
server = "http://172.25.254.50"

[host."http://172.25.254.50"]
  capabilities = ["pull", "resolve", "push"]
  skip_verify = true

7.9 验证业务

第一个命令必须是 每一个 1/1 running  才代表全部启动好了

7.10浏览器访问 http://web.demo.com

这里我是桥接模式访问的  Nat模式没成功 

阶段 8:拓展自动化 CI/CD Jenkinsfile(流水线自动化发布)

8.1 构建文件 Jenkinsfile

[root@docker-master code]# mkdir /opt/code/Jenkinsfile
[root@docker-master code]# vim Jenkinsfile
def buildImage(tag, dockerfile, context, imageName) {
    def img = "${env.HARBOR_ADDR}/${env.HARBOR_PROJECT}/${imageName}:${tag}"
    sh "BUILDAH_ISOLATION=chroot buildah bud -t ${img} -f ${dockerfile} ${context}"
}

def pushImage(tag, imageName) {
    def img = "${env.HARBOR_ADDR}/${env.HARBOR_PROJECT}/${imageName}:${tag}"
    sh "buildah push --tls-verify=false ${img}"
}

def rolloutDeploy(app, imageName) {
    def img = "${env.HARBOR_ADDR}/${env.HARBOR_PROJECT}/${imageName}:${env.IMAGE_TAG}"
    // *= 替换该 Deployment 下所有容器的镜像, 避免容器名写错
    sh "kubectl --kubeconfig=${env.KUBE_CONFIG} -n ${env.NAMESPACE} set image deployment/${app} *=${img}"
    sh "kubectl --kubeconfig=${env.KUBE_CONFIG} -n ${env.NAMESPACE} rollout status deployment/${app} --timeout=180s"
}

pipeline {
    agent any

    environment {
        // ---- 环境参数 (按实际情况修改) ----
        HARBOR_ADDR    = '172.25.254.50'          // Harbor IP, HTTP 无 TLS
        HARBOR_PROJECT = 'web'                    // Harbor 项目名
        NAMESPACE      = 'web-project'            // K8s 命名空间
        FRONT_APP      = 'web-frontend'           // 前端 Deployment 名 (集群里的名字, 和目录无关)
        BACK_APP       = 'web-backend'            // 后端 Deployment 名
        FRONT_IMAGE    = 'web-front'              // 前端镜像名
        BACK_IMAGE     = 'web-back'               // 后端镜像名

        // ---- 凭据 (Jenkins 里配好后无需改动) ----
        HARBOR_CREDS   = credentials('harbor-credentials')  // 注入 HARBOR_CREDS_USR / HARBOR_CREDS_PSW
        KUBE_CONFIG    = credentials('kubeconfig')          // 注入 KUBE_CONFIG (kubeconfig 文件路径)
    }

    parameters {
        string(name: 'IMAGE_TAG', defaultValue: '',
               description: '镜像标签, 留空自动用 v<构建号> (如 v12)')
        choice(name: 'APP', choices: ['both', 'backend', 'frontend'],
               description: '构建/部署哪个应用: both=前后端都做')
    }

    stages {
        stage('拉取代码') {
            steps {
                // Git 仓库地址在 Jenkins 任务的源码管理里配置, 此处自动检出
                checkout scm
                script {
                    env.IMAGE_TAG = params.IMAGE_TAG ?: "v${env.BUILD_NUMBER}"
                    echo "本次构建镜像标签: ${env.IMAGE_TAG}"
                }
            }
        }

        stage('登录 Harbor') {
            steps {
                sh 'buildah login --tls-verify=false $HARBOR_ADDR -u $HARBOR_CREDS_USR -p $HARBOR_CREDS_PSW'
            }
        }

        stage('构建镜像') {
            steps {
                script {
                    if (params.APP != 'frontend') {
                        echo '===== 构建后端镜像 (Dockerfile 内自动 mvn package) ====='
                        buildImage(env.IMAGE_TAG, 'backend/Dockerfile', 'backend', env.BACK_IMAGE)
                    }
                    if (params.APP != 'backend') {
                        echo '===== 构建前端镜像 (直接打 dist 进 nginx) ====='
                        buildImage(env.IMAGE_TAG, 'front/Dockerfile', 'front', env.FRONT_IMAGE)
                    }
                }
            }
        }

        stage('推送 Harbor') {
            steps {
                script {
                    if (params.APP != 'frontend') {
                        pushImage(env.IMAGE_TAG, env.BACK_IMAGE)
                    }
                    if (params.APP != 'backend') {
                        pushImage(env.IMAGE_TAG, env.FRONT_IMAGE)
                    }
                }
            }
        }

        stage('滚动部署') {
            steps {
                script {
                    if (params.APP != 'frontend') {
                        echo '===== 滚动更新后端 ====='
                        rolloutDeploy(env.BACK_APP, env.BACK_IMAGE)
                    }
                    if (params.APP != 'backend') {
                        echo '===== 滚动更新前端 ====='
                        rolloutDeploy(env.FRONT_APP, env.FRONT_IMAGE)
                    }
                }
            }
        }
    }

    post {
        success {
            echo "流水线成功: ${env.JOB_NAME} #${env.BUILD_NUMBER}, 镜像标签=${env.IMAGE_TAG}"
        }
        failure {
            echo "流水线失败, 请查看控制台日志定位问题"
        }
    }
}

8.2 安装git 

作用:代码的档案库"——每执行一次 commit 就是把当前所有文件存一个版本。

[root@docker-master code]# yum install -y git
正在更新 Subscription Management 软件仓库。
无法读取客户身份

本系统尚未在权利服务器中注册。可使用 subscription-manager 进行注册。

上次元数据过期检查:0:49:40 前,执行于 2026年08月18日 星期二 10时48分39秒。
依赖关系解决。
==============================================================================================
 软件包                    架构            版本                      仓库                大小
==============================================================================================
安装:
 git                       x86_64          2.47.1-1.el9              AppStream           54 k
安装依赖关系:
 git-core                  x86_64          2.47.1-1.el9              AppStream          4.7 M
 git-core-doc              noarch          2.47.1-1.el9              AppStream          3.0 M
 perl-Error                noarch          1:0.17029-7.el9           AppStream           46 k
 perl-Git                  noarch          2.47.1-1.el9              AppStream           39 k
 perl-TermReadKey          x86_64          2.38-11.el9               AppStream           40 k
 perl-lib                  x86_64          0.65-481.el9              AppStream           15 k

事务概要
==============================================================================================
安装  7 软件包

总计:7.9 M
安装大小:40 M
下载软件包:
运行事务检查
事务检查成功。
运行事务测试
事务测试成功。
运行事务
  准备中  :                                                                               1/1
  安装    : git-core-2.47.1-1.el9.x86_64                                                  1/7
  安装    : git-core-doc-2.47.1-1.el9.noarch                                              2/7
  安装    : perl-lib-0.65-481.el9.x86_64                                                  3/7
  安装    : perl-TermReadKey-2.38-11.el9.x86_64                                           4/7
  安装    : perl-Error-1:0.17029-7.el9.noarch                                             5/7
  安装    : perl-Git-2.47.1-1.el9.noarch                                                  6/7
  安装    : git-2.47.1-1.el9.x86_64                                                       7/7
  运行脚本: git-2.47.1-1.el9.x86_64                                                       7/7
  验证    : git-2.47.1-1.el9.x86_64                                                       1/7
  验证    : git-core-2.47.1-1.el9.x86_64                                                  2/7
  验证    : git-core-doc-2.47.1-1.el9.noarch                                              3/7
  验证    : perl-Error-1:0.17029-7.el9.noarch                                             4/7
  验证    : perl-Git-2.47.1-1.el9.noarch                                                  5/7
  验证    : perl-TermReadKey-2.38-11.el9.x86_64                                           6/7
  验证    : perl-lib-0.65-481.el9.x86_64                                                  7/7
已更新安装的产品。

已安装:
  git-2.47.1-1.el9.x86_64                      git-core-2.47.1-1.el9.x86_64
  git-core-doc-2.47.1-1.el9.noarch             perl-Error-1:0.17029-7.el9.noarch
  perl-Git-2.47.1-1.el9.noarch                 perl-TermReadKey-2.38-11.el9.x86_64
  perl-lib-0.65-481.el9.x86_64

完毕!
[root@docker-master code]# git config --global user.name "chen"
[root@docker-master code]# git config --global user.email "chen@local"
[root@docker-master code]# pwd
/opt/code
[root@docker-master code]# git init
提示: 使用 'master' 作为初始分支的名称。这个默认分支名称可能会更改。要在新仓库中
提示: 配置使用初始分支名,并消除这条警告,请执行:
提示:
提示:  git config --global init.defaultBranch <名称>
提示:
提示: 除了 'master' 之外,通常选定的名字有 'main'、'trunk' 和 'development'。
提示: 可以通过以下命令重命名刚创建的分支:
提示:
提示:  git branch -m <name>
已初始化空的 Git 仓库于 /opt/code/.git/
[root@docker-master code]# git add .
[root@docker-master code]# git status | head -30
位于分支 master

尚无提交

要提交的变更:
  (使用 "git rm --cached <文件>..." 以取消暂存)
        新文件:   Jenkinsfile
        新文件:   backend/Dockerfile
        新文件:   backend/pom.xml
        新文件:   backend/src/main/java/demo/DemoApplication.java
        新文件:   front/Dockerfile
        新文件:   front/dist/index.html
        新文件:   front/nginx.conf
        新文件:   k8s-yaml/backend-deploy.yaml
        新文件:   k8s-yaml/configmap.yaml
        新文件:   k8s-yaml/front-deply.yaml
        新文件:   k8s-yaml/ingress.yaml
        新文件:   k8s-yaml/mysql-pvc.yaml
        新文件:   k8s-yaml/namespace.yaml
        新文件:   k8s-yaml/secret.yaml

[root@docker-master code]# git commit -m "init web-project"
[master(根提交) 31da318] init web-project
 14 files changed, 351 insertions(+)
 create mode 100644 Jenkinsfile
 create mode 100644 backend/Dockerfile
 create mode 100644 backend/pom.xml
 create mode 100644 backend/src/main/java/demo/DemoApplication.java
 create mode 100644 front/Dockerfile
 create mode 100644 front/dist/index.html
 create mode 100644 front/nginx.conf
 create mode 100644 k8s-yaml/backend-deploy.yaml
 create mode 100644 k8s-yaml/configmap.yaml
 create mode 100644 k8s-yaml/front-deply.yaml
 create mode 100644 k8s-yaml/ingress.yaml
 create mode 100644 k8s-yaml/mysql-pvc.yaml
 create mode 100644 k8s-yaml/namespace.yaml
 create mode 100644 k8s-yaml/secret.yaml

8.3 装 Jenkins + buildah

[root@docker-master code]# dnf install -y java-21-openjdk
正在更新 Subscription Management 软件仓库。
无法读取客户身份

本系统尚未在权利服务器中注册。可使用 subscription-manager 进行注册。

上次元数据过期检查:0:02:11 前,执行于 2026年08月18日 星期二 11时42分31秒。
依赖关系解决。
==============================================================================================
 软件包                         架构         版本                       仓库             大小
==============================================================================================
安装:
 java-21-openjdk                x86_64       1:21.0.6.0.7-2.el9         AppStream       429 k
安装依赖关系:
 java-21-openjdk-headless       x86_64       1:21.0.6.0.7-2.el9         AppStream        49 M

事务概要
==============================================================================================
安装  2 软件包

总计:50 M
安装大小:212 M
下载软件包:
运行事务检查
事务检查成功。
运行事务测试
事务测试成功。
运行事务
  运行脚本: java-21-openjdk-headless-1:21.0.6.0.7-2.el9.x86_64                            1/1
  准备中  :                                                                               1/1
  安装    : java-21-openjdk-headless-1:21.0.6.0.7-2.el9.x86_64                            1/2
  运行脚本: java-21-openjdk-headless-1:21.0.6.0.7-2.el9.x86_64                            1/2
  安装    : java-21-openjdk-1:21.0.6.0.7-2.el9.x86_64                                     2/2
  运行脚本: java-21-openjdk-1:21.0.6.0.7-2.el9.x86_64                                     2/2
  运行脚本: java-21-openjdk-headless-1:21.0.6.0.7-2.el9.x86_64                            2/2
  运行脚本: java-21-openjdk-1:21.0.6.0.7-2.el9.x86_64                                     2/2
  验证    : java-21-openjdk-1:21.0.6.0.7-2.el9.x86_64                                     1/2
  验证    : java-21-openjdk-headless-1:21.0.6.0.7-2.el9.x86_64                            2/2
已更新安装的产品。

已安装:
  java-21-openjdk-1:21.0.6.0.7-2.el9.x86_64
  java-21-openjdk-headless-1:21.0.6.0.7-2.el9.x86_64

完毕!
[root@docker-master code]# rpm --import https://pkg.jenkins.io/redhat-stable/jenkins.io-2023.k                                                                                       ey
[root@docker-master code]# yum install -y jenkins buildah
正在更新 Subscription Management 软件仓库。
无法读取客户身份

本系统尚未在权利服务器中注册。可使用 subscription-manager 进行注册。

Jenkins-stable                                                222  B/s | 833  B     00:03
Jenkins-stable                                                664  B/s | 1.6 kB     00:02
导入 GPG 公钥 0x14ABFC68:
 Userid: "Jenkins Project <[email protected]>"
 指纹: 5E38 6EAD B55F 0150 4CAE 8BCF 7198 F4B7 14AB FC68
 来自: https://pkg.jenkins.io/rpm-stable/repodata/repomd.xml.key
Jenkins-stable                                                482  B/s | 2.6 kB     00:05
软件包 buildah-1:1.31.3-1.el9.x86_64 已安装。
依赖关系解决。
==============================================================================================
 软件包                         架构          版本                     仓库              大小
==============================================================================================
安装:
 jenkins                        noarch        2.568.2-1                jenkins           96 M
升级:
 buildah                        x86_64        2:1.39.0-2.el9           AppStream         11 M
 containers-common              x86_64        2:1-117.el9_6            AppStream        155 k
安装依赖关系:
 containers-common-extra        x86_64        2:1-117.el9_6            AppStream         10 k

事务概要
==============================================================================================
安装  2 软件包
升级  2 软件包

总计:107 M
总下载:96 M
下载软件包:
jenkins-2.568.2-1.noarch.rpm                                  5.2 MB/s |  96 MB     00:18
----------------------------------------------------------------------------------------------
总计                                                          5.2 MB/s |  96 MB     00:18
Jenkins-stable                                                935  B/s | 1.6 kB     00:01
导入 GPG 公钥 0x14ABFC68:
 Userid: "Jenkins Project <[email protected]>"
 指纹: 5E38 6EAD B55F 0150 4CAE 8BCF 7198 F4B7 14AB FC68
 来自: https://pkg.jenkins.io/rpm-stable/repodata/repomd.xml.key
导入公钥成功
运行事务检查
事务检查成功。
运行事务测试
事务测试成功。
运行事务
  准备中  :                                                                               1/1
  升级    : containers-common-2:1-117.el9_6.x86_64                                        1/6
  安装    : containers-common-extra-2:1-117.el9_6.x86_64                                  2/6
  升级    : buildah-2:1.39.0-2.el9.x86_64                                                 3/6
  运行脚本: jenkins-2.568.2-1.noarch                                                      4/6
  安装    : jenkins-2.568.2-1.noarch                                                      4/6
  运行脚本: jenkins-2.568.2-1.noarch                                                      4/6
  清理    : buildah-1:1.31.3-1.el9.x86_64                                                 5/6
  清理    : containers-common-2:1-55.el9.x86_64                                           6/6
  运行脚本: containers-common-2:1-55.el9.x86_64                                           6/6
  验证    : jenkins-2.568.2-1.noarch                                                      1/6
  验证    : containers-common-extra-2:1-117.el9_6.x86_64                                  2/6
  验证    : buildah-2:1.39.0-2.el9.x86_64                                                 3/6
  验证    : buildah-1:1.31.3-1.el9.x86_64                                                 4/6
  验证    : containers-common-2:1-117.el9_6.x86_64                                        5/6
  验证    : containers-common-2:1-55.el9.x86_64                                           6/6
已更新安装的产品。

已升级:
  buildah-2:1.39.0-2.el9.x86_64             containers-common-2:1-117.el9_6.x86_64
已安装:
  containers-common-extra-2:1-117.el9_6.x86_64            jenkins-2.568.2-1.noarch

完毕!
sed -i 's/^User=jenkins/User=root/; s/^Group=jenkins/Group=root/' /usr/lib/systemd/system/jenkins.service
# 让 Jenkins 以 root 用户运行
systemctl daemon-reload
systemctl enable  --now jenkins.service

8.4 拿解锁密码

在 docker-master 上执行:

[root@docker-master code]# cat /var/lib/jenkins/secrets/initialAdminPassword
3c429837ab6145988af2f14bc806ba98

8.5 浏览器打开 Jenkins

第一步:

页面会要你输入 Administrator password → 粘贴的密码 → Continue

第二步

选 Install suggested plugins(安装建议插件),点完会开始自动安装(Git、Pipeline 等核心插件都在里面),等几分钟,进度条走完别关页面。

第三步

装完插件会让你创建第一个管理员:

  • Username: admin
  • Password: 自己设一个(比如 Admin@123,记住)
  • Full name / E-mail: 随便填

填完之后进去:

8.6 配置凭据

按照这个操作就好了


 

8.7 创建流水线任务

到这儿页面 点Build with parameters 进去什么都不改  直接按 build 就好了   

看到有个绿色的对勾就代表流水线通了  

在验证一下

harbor 仓库会出现标签 v13 

两个 Pod 应该都是 1/1 Running,AGE 是最近 1 分钟

这里整个项目就结束了

阶段9 总结

9.1 项目总结

  1. K8s 集群从 Harbor 拉取镜像:搭建 3 节点集群并部署 Harbor 私有仓库,配置 containerd 与 Harbor(HTTP 协议)对接,解决因协议不匹配导致的全量 Pod ImagePullBackOff——通过逐节点配置 /etc/containerd/certs.d/ 的 hosts.toml 声明 insecure registry 修复,并总结出配置目录不可携带端口号、多节点必须逐一配置的要点;
  2. Deployment 管理前后端 Pod 滚动更新:前后端应用分别由 Deployment 管理,配置资源限制与 Readiness 探针,修复后端探针路径(404 → /api/hello),保障滚动更新期间流量无损切换,通过 kubectl set image 实现零停机发布;
  3. Service 实现集群内部服务发现:使用 ClusterIP Service 为前后端提供稳定的集群内访问地址与负载均衡,配合 kube-proxy 实现后端多副本流量分发;
  4. Ingress 提供统一域名入口:部署 ingress-nginx 提供 http://web.demo.com 域名入口统一转发外部流量;排查外部访问不通问题,通过 tcpdump 抓包(SYN 无回包)定位根因为 Service externalTrafficPolicy: Local 导致跨节点流量被静默 DROP,改为 Cluster 策略后访问即时恢复;同时识别 VMware NAT 模式 vmnat 对 HTTP 转发的缺陷,改用桥接网络方案;
  5. 拓展:Jenkins Pipeline 实现 CI/CD 自动化发布:搭建 Jenkins + Buildah 流水线,实现参数化一键构建——拉取代码 → buildah 构建前后端镜像 → 推送 Harbor → kubectl set image 滚动部署;逐一解决 RHEL 9 环境下 Jenkins Java 21 版本要求、Git 插件拦截 file:// 本地仓库、镜像短名强制全限定名、buildah 与 crun 不兼容(BUILDAH_ISOLATION=chroot)等环境问题,流水线稳定运行,实现业务发布从手动操作到全自动化的转变。

个人收获

  1. 从零到一搭建并运维了一套生产级容器化平台:独立完成 3 节点 K8s 集群搭建、Harbor 镜像仓库部署、CI/CD 流水线落地,完整经历"环境规划 → 部署实施 → 故障排障 → 自动化交付"全生命周期,建立了云原生运维的整体架构观,不再停留在"会用命令"层面,而是理解每个组件在链路中的位置与职责;

  2. 形成了系统化的排障方法论:面对"外部访问不通"这类疑难问题,不再盲目试错,而是学会用 tcpdump 抓包、iptables 规则分析、事件日志排查等手段逐层定位——从"本机 curl 200 但外部不通"的假象中,通过 SYN 无回包这一关键线索锁定 externalTrafficPolicy: Local 导致的跨节点静默丢包,深刻体会到网络问题必须分层排查、用证据说话,而不是靠猜;

  3. 深化了对容器运行时与镜像分发原理的理解:通过解决 containerd 与 Harbor HTTP/HTTPS 协议不匹配问题,彻底搞懂了镜像拉取链路(客户端 → registry → 镜像存储)、certs.d 配置机制、insecure registry 的信任模型,以及多节点环境下"一处配置、全集群生效"的工程化要点;

  4. 理解了 DevOps 自动化对交付效率的价值:将原本手动执行"构建 → 推送 → 部署"的流程封装成 Jenkins Pipeline 一键完成,实现业务零停机滚动发布,切实体会到 CI/CD 不只是工具使用,更是把重复劳动变成标准化流程的工程思维;同时掌握 buildah 无守护进程构建、RHEL 环境兼容性处理(Java 版本、镜像短名、crun 兼容)等生产环境实战细节;

  5. 提升了抗压与持续学习能力:面对从镜像拉取、集群健康到外部访问、流水线搭建的长链路问题,通过 40+ 次迭代试错最终全部打通,学会了在报错信息、日志和社区文档中快速定位答案,养成了"先分析根因、再动手修复"的习惯,也验证了自己具备独立攻坚复杂环境问题的能力。

转载自 CSDN-专业IT技术社区

原文链接:https://blog.csdn.net/ziyun666888/article/details/163879920

文章来源转载

评论

赞0

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--