欢迎光临
我们一直在努力

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-master 172.25.254.50 K8s 控制节点 containerd、kubeadm、kubectl、kubelet、harbor仓库、Jenkins
docker-worker1 172.25.254.60 K8s 工作节点 containerd、kubelet
docker-worker2 172.25.254.70 K8s 工作节点 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

重点修改两处:

  • pause 镜像替换国内源
  • 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

    关键点解释:

  • replicas:2 运行 2 个后端实例实现高可用
  • RollingUpdate:滚动更新,零停机发布
  • resources:容器资源配额,防止资源抢占
  • readinessProbe:就绪探针,健康才加入 Service 接收流量
  • 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 <jenkinsci-board@googlegroups.com>"
    指纹: 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 <jenkinsci-board@googlegroups.com>"
    指纹: 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 项目总结

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

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

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

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

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

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

  • 赞(0)
    未经允许不得转载:171主机测评 » Docker + K8s 云原生多节点 Web 服务架构搭建与自动化运维
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址