K8s 集群部署微服务 - DevOps(二)

K8s 集群部署微服务 - DevOps(二)


K8s 集群环境搭建 - yaml 版本(一)
K8s 集群部署中间件 - yaml 版本(二)
K8s 集群部署微服务 - yaml 版本(三)
K8s 集群部署微服务 - DevOps(一)
K8s 集群部署微服务 - DevOps(二)

文章目录

  • [K8s 集群部署微服务 - DevOps(二)](#K8s 集群部署微服务 - DevOps(二))
  • [一、安装 DevOps](#一、安装 DevOps)
  • 二、编写后端流水线
    • [1. 拉取代码](#1. 拉取代码)
    • [2. 项目编译](#2. 项目编译)
    • [3. 构建镜像](#3. 构建镜像)
    • [4. 推送镜像](#4. 推送镜像)
    • [5. 部署服务](#5. 部署服务)
  • 三、编写前端流水线
    • [1. 拉取代码](#1. 拉取代码)
    • [2. 项目编译](#2. 项目编译)
    • [3. 构建镜像](#3. 构建镜像)
    • [4. 推送镜像](#4. 推送镜像)
    • [5. 部署服务](#5. 部署服务)
  • 小结

一、安装 DevOps

  • 根据官方文档,需要创建新的企业空间,默认的企业空间不支持 DevOps。

  • 创建之后添加我们的集群进该企业空间:

  • 在扩展程中心安装 DevOps,点击安装即可。

    • 安装的过程中需要绑定 pvc 和 pv,所需要我们的k8s有默认的 stroageClass。

    • 安装的过程中需要拉取 jenkins 镜像,由于拉取比较慢,可能回到导致 UI 界面提示安装失败了,但其实还在拉取的过程中,需要拉取成功后,进行卸载 DevOps,在重新安装 DevOps。

    • 如果出现错误: Internal error occurred: failed calling webhook "reverseproxies.extensions.kubesphere.io",执行如下命令即可:

      java 复制代码
      kubectl delete validatingwebhookconfiguration extensions.kubesphere.io
    • 其他问题可尝试在 kubeSphere 开发者社区看下

  • 安装成功后:

  • 进去我们自定义的企业空间发现有 DevOps 功能了,到这就安装完成了。

二、编写后端流水线

  • 在部署服务之前,需要新建企业空间,不能在默认的企业空间进行部署,因为默认的企业空间是不能使用 DevOps 流水线的,想要 DevOps,必须要在新建的企业空间上进行。在 KubeSphere中,企业空间比命名空间更高层次的隔离单位。隔离关系如下:

    java集群 复制代码
    ├── 企业空间A(workspace-a)
    │   ├── 项目1(namespace-a1)
    │   │   ├── MySQL服务
    │   │   └── 其他资源
    │   └── 项目2(namespace-a2)
    └── 企业空间B(workspace-b)  # 👈 新创建的企业空间
        ├── 项目1(namespace-b1) # 这里看不到企业空间A的MySQL
        └── 项目2(namespace-b2)在这里插入代码片
  • 在新建的企业空间中,kubeSphere 上是看不到其他企业空间的任何服务的。但是一些系统服务,是所有命名空间可以共享的,在新建的企业空间无法修改,也无法看到这些服务,如需修改,需要再默认企业空间中进行修改配置。比如上篇文章中配置的服务:ks-apiserver 和 ks-controller-manager。

  • 在新建的企业空间中,创建流水线:DevOps -> 流水线 -> 点击创建:

  • 创建流水线之后,需要进行编辑,我这里的流水线分为以下几步:

    • 从 gitee 拉取代码;
    • 将拉取的代码进行项目编译;
    • 编译好之后构建镜像;
    • 推送进行到已经部署好的 harbor;
    • 使用镜像部署服务;
  • 当然你还可以添加额外的步骤,比如单元测试、质量检测等。

1. 拉取代码

  • 我用的 gitee 作为代码仓库。首先开通我们的项目支持 svn 拉取代码,因为我们创建的流水线,默认就是使用的 svn 拉取代码,用 https 会报错。

  • 在 kubeSphere 中创建 gitee 的凭证:用户名密码。用于从 gitee 拉取代码的凭证。

  • 创建代码仓库,添加代码地址,记得填写 svn 的地址,还要关联上我们上面创建的凭证。

  • 接下来编辑我们流水线的第一步:拉取代码

    • 代理选择:none
    • 任务第一步,关联上我们上面创建的代码仓库拉取代码。
    • 任务第二步:添加 shell 脚本,在拉取代码结束后打印出 "拉取代码成功"。
    • 任务第三步:添加了第二个 shell 脚本,打印我们拉取的项目的信息。
  • 编辑好第一步之后,先点击运行,测试下第一步是否有问题:

  • 成功运行了第一步,效果如下:

  • 日志如下:

2. 项目编译

  • 项目进行编译的时候,maven 会下载项目依赖。但在 kubeSphere 中,默认是没有配置镜像地址的,所以需要配置依赖地址。

  • 在 【集群管理】中,选择我们需要修改的集群,在菜单 【配置】- 【配置字典】- 命名空间【kubesphere-devops-worker】,找到服务名:ks-devops-agent,修改改配置,添加镜像地址:

  • 项目编译是在容器中进行的,我的 kubeSphere 版本 v4.2.0-community,提供的容器有 base 和 jnlp,其中 base 就是提供了 java 环境的容器。但是使用的是 jdk-21,而我需要部署的项目使用的 jdk 1.8。所以需要添加新的容器。

  • 【集群】- 选择我们的集群 - 【配置】 - 【配置字段】 - 命名空间【kubesphere-devops-system】- 配置名【jenkins-casc-config】:

  • 在 jenkins_user.yaml: templates 下添加我们需要的容器:base-jdk8

    java 复制代码
    - name: "base-jdk8"
      image: "swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/kubesphere/builder-maven:v4.1.4-podman"
      command: "cat"
      args: ""
      ttyEnabled: true
      privileged: false
      resourceRequestCpu: "100m"
      resourceLimitCpu: "4000m"
      resourceRequestMemory: "100Mi"
      resourceLimitMemory: "8192Mi"
  • 该容器来自渡渡鸟的容器镜像站,如果需要其他版本的 jdk,也可搜索满足需求的容器。当然也可以自己制作容器,放入私有的镜像仓库中。

  • 需要注意的是,我们需要知道我们使用的镜像的maven环境变量,知道maven的setting文件在容器的位置。然后将该文件挂载到上面我们配置的 ks-devops-agent 上,从而让 ks-devops-agent 的 mirrors 仓库生效。需要再 jenkins_user.yaml 下配置挂载:

    java 复制代码
    - name: "base-jdk8"
      resources:
        requests:
          ephemeral-storage: "1Gi"
        limits:
          ephemeral-storage: "10Gi"
      volumeMounts:
      - name: config-volume
        mountPath: /apache-maven-3.5.3/conf/settings.xml
        subPath: settings.xml
  • 如下是容器 base-jdk8 的maven环境变量,对应上面的配置:

  • 编辑流水线:指定容器名为:base-jdk8,并添加命令:mvn clean package

  • 运行测试成功:

3. 构建镜像

  • 在项目中编写 Dockerfile 文件,并推送到仓库中,流水线需要用该 Dockerfile 进行构建镜像。项目结构如下:与 src 齐平

  • dockerfile 内容如下:

    java 复制代码
    # 基础镜像:和你的 JDK 版本匹配(JDK 8 用 8-jre-slim,轻量且安全)
    FROM swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/openjdk:8-jre-slim
    
    # 避免中文乱码
    ENV LANG=C.UTF-8 LC_ALL=C.UTF-8
    
    # 创建非 root 用户(安全最佳实践,避免容器用 root 运行)
    RUN groupadd --system --gid 1001 appgroup && \
        useradd --system --uid 1001 --gid 1001 appuser
    
    # 工作目录(容器内目录,统一路径)
    WORKDIR /opt/mortal/app
    
    # 复制授权服务的 Jar 包到容器(注意:Jar 包名称要和 target/ 下的一致!)
    # 如果你想简化 Jar 包名称,可在复制时重命名为 app.jar(推荐,避免版本号变更导致 Dockerfile 修改)
    # [当前系统相对路径] [容器内绝对路径]
    COPY mortal-gateway/target/mortal-gateway-1.0-SNAPSHOT.jar /opt/mortal/app/mortal-gateway.jar
    
    # 给非 root 用户授权(避免启动时权限不足)
    RUN chown -R appuser:appgroup /opt/mortal/app
    
    # 切换到非 root 用户运行
    USER 1001
    
    # 暴露服务端口(和授权服务的 server.port 一致,比如 2001)
    EXPOSE 1888
    
    # 启动命令(exec 格式,支持优雅停机)
    ENTRYPOINT ["java", "-jar", "mortal-gateway.jar"]
    • 需要注意我们选择的 jdk 版本必须和 运行容器的主机架构一直才行,不然会报错。
    • mortal-gateway/target/mortal-gateway-1.0-SNAPSHOT.jar:该路径需要根据你编译后的 jar 位置来决定的。
  • 将 Dockerfile 文件推送到远程仓库,在我们运行流水线的第一步会将他拉去下来。

  • 接下来就是使用 docker build 进行打包了。但是前提是需要观察我们使用的 base-jdk8 镜像中是否安装了docker-cli,是否启动了 docker 守护进程,如果没有启动docker 守护进程,是无法使用docker build 的。通过观察发现,使用了 podman 代替了 docker:

  • podman 对比 docker:

    • 无守护进程:Podman 不需要运行 root 权限的守护进程。
    • rootless 容器:可以在普通用户权限下运行容器,更安全。
    • 没有特权提升:减少了安全风险。
  • 所以我们需要使用 podman build 代替 docker build。使用 podman 还需要解决下权限问题,不然会报错如下:

  • 在 jenkins_user.yaml 添加如下三处配置:

    java 复制代码
    envVars:
        - envVar:
            key: "USER"
            value: "root"
        - envVar:
            key: "HOME"
            value: "/root"
    
    ---
    securityContext:
        runAsUser: 0
        runAsGroup: 0
        allowPrivilegeEscalation: true
    
    --- 
    securityContext:
      fsGroup: 0
  • 效果如下:


  • 配置修改后大概两分钟后生效,接下来编写构建镜像流水线:

    java 复制代码
    podman build -f mortal-gateway/Dockerfile -t mortal.harbor.com/mortal-system/mortal-gateway:1.0-SNAPSHOT .
  • 测试运行成功:

  • 其他的服务也是按照上面的步骤来:在流水线中点击【添加并行阶段】即可:并行可以加快构建速度

  • 添加其他服务:

  • 测试成功:
  • 其他优化:

    • 用于 Dockerfile 中的 jdk镜像,上传至私有库 harbor中,这样可以加快拉取速度。该方式之前叙说过多次,便不再赘述。
    • 每次任务的构建都是通过 Agent ,每次构建创建的 pod 都是临时的,构建完成后就被销毁,下次构建又是全新的 Pod,所以本地永远没有缓存。导致每次构建又要重新拉取 jdk 镜像。
  • 查看日志如下,每次都是 copying,没有使用到缓存,服务多的情况下,每个服务都需要 copying,非常慢和浪费资源:

  • 修改 jenkins-casc-config ,添加镜像路径的挂载:

    java 复制代码
    - hostPathVolume:
    	hostPath: "/var/data/jenkins_podman_cache"
    	mountPath: "/var/lib/containers"
  • 其中 hostPath 是宿主机的路径,montPah 是 容器 base-jdk8 的容器路径。

    • hostPath:该路径宿主机如果没有的话,需要提前创建好:

      java 复制代码
      sudo mkdir -p /var/data/jenkins_podman_cache
      sudo chmod 777 /var/data/jenkins_podman_cache
    • montPah:该值需要确定我们使用的容器中,镜像的路径地址,比如我用的base-jdk8 信息如下:

  • 配置好之后再次启动流水线测试效果如下:发现成功使用了缓存,构建速度行得到了大大的提升。从 17.35min 提升到了 58.43 s,速度提升巨大。

4. 推送镜像

  • 推送镜像需要如下4步:

    • 指定容器,依旧是我们前面使用的 base-jdk8。

    • 添加 harbor 凭证:harbor 的用户名和密码。

    • 使用 harbor 凭证进行登录。podman login --tls-verify=false -u $HARBOR_USER -p $HARBOR_PASSWD mortal.harbor.com

    • 推送镜像:podman push --tls-verify=false mortal.harbor.com/mortal-system/mortal-authority:1.0-SNAPSHOT

  • 如上边成功推送了一个服务的镜像,其他服务的进行,点击【添加并行阶段】,和上述进行一样的设置,修改下镜像名称即可。

5. 部署服务

  • 部署服务分为以下几步:

    • 创建流水线访问 k8s 集群的凭证。
    • 添加该凭证。
    • 执行部署服务的脚本。
  • 创建凭证:选择类型为 kubeconfig,会自动带出内容,点击确定创建。

  • 在流水线里添加步骤,然后添加我们刚刚创建的凭证,变量起名为:KUBECONFIG_CONTENT,该变量下一步执行脚本需要用到。

  • 执行服务。

    java 复制代码
    mkdir -p ~/.kube
    echo "$KUBECONFIG_CONTENT" > ~/.kube/config
    envsubst < mortal-gateway/deploy/deploy.yaml | kubectl apply -f -
    • KUBECONFIG_CONTENT 为上面添加凭证的变量名。
    • mortal-gateway/deploy/deploy.yaml:为我们服务的 deploy.yaml 的位置。如果不清楚,可以先打印 ls 看下目录结构。
  • 我们的服务:deploy 位置

  • deploy.yaml 内容如下,集合了 deployment,service,pvc,ingress 等

    java 复制代码
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: gateway-service  # 部署名称
      namespace: mortal-system  # 建议创建专属命名空间,也可改为你的现有命名空间
      labels:
        app: gateway-service
    spec:
      replicas: 1  # 副本数,可根据需求调整
      selector:
        matchLabels:
          app: gateway-service
      template:
        metadata:
          labels:
            app: gateway-service
        spec:
          containers:
            - name: gateway-service
              # 镜像地址(harbor中的目标镜像)
              image: mortal.harbor.com/mortal-system/mortal-gateway:1.0-SNAPSHOT
              imagePullPolicy: Always  # 镜像拉取策略:本地无则拉取
              # 容器端口(对应微服务的启动端口,如8080、9999等,需与服务配置一致)
              ports:
                - containerPort: 1888
              # 环境变量配置(连接nacos、mysql,根据你的实际配置修改值)
              env:
                # 1. 微服务名称(对应 Nacos DataID 前缀)
                - name: SPRING_APPLICATION_NAME
                  value: "mortal-gateway-service"
                # 2. 激活 prod 环境
                - name: SPRING_PROFILES_ACTIVE
                  value: "prod"  # 激活prod环境配置
                # 3. Nacos 配置中心地址 + 命名空间
                - name: SPRING_CLOUD_NACOS_CONFIG_SERVER-ADDR
                  value: "nacos-headless:8848"
                - name: SPRING_CLOUD_NACOS_CONFIG_NAMESPACE
                  value: "db0788b5-43d5-4f7c-abf4-a9aa8308d272"
                # 4. Nacos 注册中心地址 + 命名空间(和配置中心一致)
                - name: SPRING_CLOUD_NACOS_DISCOVERY_SERVER-ADDR
                  value: "nacos-headless:8848"
                - name: SPRING_DATASOURCE_URL
                  value: "jdbc:mysql://mysql-headless:3306/mortal?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true"  # mysql地址(k8s中mysql的service名称)
                - name: SPRING_DATASOURCE_USERNAME
                  value: "root"  # mysql用户名
                - name: SPRING_DATASOURCE_PASSWORD
                  value: "root"  # mysql密码
              # 资源限制(根据集群资源调整)
              resources:
                limits:
                  cpu: "1"
                  memory: "1Gi"
                requests:
                  cpu: "500m"
                  memory: "512Mi"
              # 健康检查(可选,建议配置)
              livenessProbe:
                httpGet:
                  path: /actuator/health
                  port: 1888
                initialDelaySeconds: 60  # 启动后延迟60秒检查
                periodSeconds: 10  # 每10秒检查一次
              readinessProbe:
                httpGet:
                  path: /actuator/health
                  port: 1888
                initialDelaySeconds: 30
                periodSeconds: 5
              # 挂载动态PVC(与volumeClaimTemplates名称对应)
              volumeMounts:
                - name: gateway-log-volume  # 必须与volumeClaimTemplates.name一致
                  mountPath: /app/logs  # 容器内持久化目录
          volumes:  # 新增volumes字段引用PVC
            - name: gateway-log-volume
              persistentVolumeClaim:
                claimName: gateway-log-pvc  # 对应上面创建的PVC名称
    
    ---
    # Service配置(暴露微服务,供内部集群访问)
    apiVersion: v1
    kind: Service
    metadata:
      name: gateway-service
      namespace: mortal-system
    spec:
      selector:
        app: gateway-service
      type: ClusterIP  # 集群内部访问,如需外部访问可改为NodePort或LoadBalancer
      ports:
        - port: 1888  # Service端口
          targetPort: 1888  # 容器端口(与Deployment中containerPort一致)
    
    
    ---
    # gateway-pvc.yaml
    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
      name: gateway-log-pvc
      namespace: mortal-system
    spec:
      accessModes: [ "ReadWriteMany" ]
      storageClassName: "nfs-sc"
      resources:
        requests:
          storage: 2Gi
    
    
    ---
    apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
      name: gateway-ingress
      namespace: mortal-system
      annotations:
        nginx.ingress.kubernetes.io/use-regex: "true"
        # 1. 禁用 HTTPS 重定向(核心)
        nginx.ingress.kubernetes.io/ssl-redirect: "false"
        # 2. 禁用强制 SSL 重定向(覆盖全局)
        nginx.ingress.kubernetes.io/force-ssl-redirect: "false"
        # 3. 禁用永久重定向(针对 308 状态码)
        nginx.ingress.kubernetes.io/permanent-redirect-code: "307"
        # 4. 明确指定后端服务用 HTTP 协议(避免默认 HTTPS 转发)
        nginx.ingress.kubernetes.io/backend-protocol: "HTTP"
        # 5. 其他原有注解保留
        nginx.ingress.kubernetes.io/proxy-connect-timeout: "60"
        nginx.ingress.kubernetes.io/proxy-read-timeout: "60"
        nginx.ingress.kubernetes.io/proxy-send-timeout: "60"
    spec:
      ingressClassName: nginx
      rules:
        # 无域名规则(直接用 IP 访问)
        - http:
            paths:
              - path: /mortal-gateway/(.*)
                pathType: ImplementationSpecific
                backend:
                  service:
                    name: gateway-service
                    port:
                      number: 1888
        # 域名规则(可选,保留)
        - host: gateway.mortal.com
          http:
            paths:
              - path: /mortal-gateway/(.*)
                pathType: ImplementationSpecific
                backend:
                  service:
                    name: gateway-service
                    port:
                      number: 1888
  • 其中环境变量配置 env对应如下:

    java 复制代码
    server:
      port: 8888
      servlet:
        context-path: /mortal-gateway
    
    spring:
      application:
        name: mortal-gateway-service
      cloud:
        nacos:
          discovery:
            enabled: true #如果不想使用 Nacos 进行服务注册与发现,设置为false即可
            server-addr: 14.103.138.45:30848
            namespace: aa104d54-9e4b-470b-9662-31ab4aedefb4
            username: ${NACOS_USERNAME:nacos}
            password: ${NACOS_PASSWORD:nacos}
            metadata:
              management:
                context-path: ${server.servlet.context-path}/actuator
        gateway:
          # 网关全局跨域配置
          globalcors:
            cors-configurations:
              '[/**]':
                allowedOrigins: "*"
                allowedMethods: "*"
                allowedHeaders: "*"
                allowCredentials: true
            # 解决options请求被拦截的问题
            add-to-simple-url-handler-mapping: true
    
    # 这个地方独立配置,是网关的数据,代码 GatewayConfig.java 中读取被监听
    nacos:
      gateway:
        route:
          config:
            data-id: mortal-gateway-router
            group: prod
    
    # Spring Boot Actuator 健康检查配置(核心,对应K8s的liveness/readiness探针)
    management:
      endpoints:
        web:
          exposure:
            include: health  # 暴露health端点(如需更多如info、metrics可追加,用逗号分隔)
          base-path: /actuator  # Actuator基础路径,对应K8s探针路径的/actuator
      endpoint:
        health:
          enabled: true  # 启用健康检查端点
          show-details: always  # 显示健康检查详情(生产环境可改为when_authorized)
      health:
        livenessState:
          enabled: true  # 适配K8s存活探针(Spring Boot 2.3+推荐)
        readinessState:
          enabled: true  # 适配K8s就绪探针(Spring Boot 2.3+推荐)
  • 其他微服务添加并行阶段,重复按上述操作即可。

三、编写前端流水线

  • 有了前面部署 后端流水线的经验,部署前端流水线就快了很多了。首先创建 流水线,选择 node.js 模板。

1. 拉取代码

  • 添加 前段代码仓库地址,并添加仓库凭证,如下:

2. 项目编译

  • 这次我们使用 kubeSphere 自带的容器 base,来进行后续操作。

  • 选择容器为 base,执行命令:npm install 进行编译。

3. 构建镜像

  • 和前面 base-jdk8 一样需要修改 jenkins_user.yaml 的 base 容器的配置:防止权限问题。

    java 复制代码
    privileged: true
    envVars:
      - envVar:
          key: "USER"
          value: "root"
      - envVar:
          key: "HOME"
          value: "/root"
  • 像之前我们用 yaml 文件,用命令行发布前端项目一样,我们需要文件有:

    • Dockerfile
    • nginx.conf
    • .dockerignore
    • 以及要部署的 deployment,service,ingress 整合到 deploy.yaml 中,结构如下:
  • 各个文件代码如下:

    • Dockerfile:其中 node 和 nginx 镜像我是改为了私有镜像库中拉取,加快了构建速度。

      java 复制代码
      # ==================== 阶段1:构建 Vue 项目(Node 环境)====================
      FROM mortal.harbor.com/mortal-system/node:18-alpine AS builder
      
      # 设置工作目录(root 用户创建,有权限)
      WORKDIR /app
      
      # 1. 复制依赖文件(root 用户,无权限问题)
      COPY package.json package-lock.json ./
      
      # 2. 安装依赖(--unsafe-perm 尝试赋予权限,基础保障)
      RUN npm ci --unsafe-perm
      
      # 3. 复制项目所有核心文件(root 用户,无权限问题)
      COPY ../mortal-front/ ./
      
      # 4. 关键修复:手动给 node_modules/.bin 下所有命令赋予执行权限
      # 直接暴力解决 vue-tsc、vite 等命令的 Permission denied 问题
      RUN chmod +x -R node_modules/.bin/
      
      # 5. 构建 Vue 项目(此时命令已有权限,可正常执行)
      RUN npm run build
      
      # ==================== 阶段2:部署静态资源(Nginx 环境)====================
      FROM mortal.harbor.com/mortal-system/nginx:alpine
      
      # 复制 dist 产物到 Nginx 静态目录(已验证正确)
      COPY --from=builder /app/dist /usr/share/nginx/html
      
      # 启用自定义 Nginx 配置(关键!解决路由 404)
      COPY nginx.conf /etc/nginx/conf.d/default.conf
      
      # 暴露端口(可选,仅声明)
      EXPOSE 80
      
      # 启动 Nginx
      CMD ["nginx", "-g", "daemon off;"]
    • nginx.conf

      java 复制代码
      server {
          listen 80;
          server_name localhost;
          root /usr/share/nginx/html;
          index index.html;
      
          # 核心:所有路由请求转发到 index.html,支持 Vue Router History 模式
          location / {
              try_files $uri $uri/ /index.html;
              add_header Cache-Control "no-cache";
          }
      
          # 静态资源缓存(可选,优化性能)
          location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|svgz|woff|woff2|ttf|otf)$ {
              expires 7d;
              add_header Cache-Control "public, max-age=604800";
          }
      }
    • .dockerignore

      java 复制代码
      # 依赖目录
      node_modules
      .npm
      .yarn
      
      # 构建产物(本地构建的,Docker 内会重新构建)
      dist
      dist-ssr
      
      # 日志和缓存
      logs
      *.log
      .vite
      .eslintcache
      .prettiercache
      
      # 本地配置
      .env.local
      .env.development.local
      .env.production.local
      .git
      .gitignore
      README.md
    • deploy.yaml

      java 复制代码
      # frontend-prod-deploy.yaml
      apiVersion: apps/v1
      kind: Deployment
      metadata:
        name: mortal-frontend
        namespace: mortal-system
      spec:
        replicas: 1  # 生产环境建议 2-3 个副本,保证高可用
        selector:
          matchLabels:
            app: mortal-frontend
        template:
          metadata:
            labels:
              app: mortal-frontend
          spec:
            # 私有 Harbor 需配置镜像拉取密钥(和之前一致)
            imagePullSecrets:
            - name: harbor-secret
            containers:
            - name: mortal-frontend
              image: mortal.harbor.com/mortal-system/mortal-front:1.0-SNAPSHOT
              imagePullPolicy: Always
              ports:
              - containerPort: 80
              resources:
                requests:
                  cpu: 100m
                  memory: 128Mi
                limits:
                  cpu: 500m
                  memory: 256Mi
              livenessProbe:
                httpGet:
                  path: /
                  port: 80
                initialDelaySeconds: 30
                periodSeconds: 10
              readinessProbe:
                httpGet:
                  path: /
                  port: 80
                initialDelaySeconds: 5
                periodSeconds: 5
      
      ---
      # 1. ClusterIP Service(仅集群内访问,实现 Pod 负载均衡)
      apiVersion: v1
      kind: Service
      metadata:
        name: mortal-frontend-svc
        namespace: mortal-system
      spec:
        type: ClusterIP  # 类型改为 ClusterIP(默认类型,可不写)
        selector:
          app: mortal-frontend
        ports:
        - port: 80  # Service 内部端口(集群内其他服务访问用:http://mortal-frontend-svc:80)
          targetPort: 80  # 对应容器端口
      
      
      ---
      apiVersion: networking.k8s.io/v1
      kind: Ingress
      metadata:
        name: mortal-frontend-ingress
        namespace: mortal-system
        annotations:
          nginx.ingress.kubernetes.io/use-regex: "true"
          # 1. 禁用 HTTPS 重定向(核心)
          nginx.ingress.kubernetes.io/ssl-redirect: "false"
          # 2. 禁用强制 SSL 重定向(覆盖全局)
          nginx.ingress.kubernetes.io/force-ssl-redirect: "false"
          # 3. 禁用永久重定向(针对 308 状态码)
          nginx.ingress.kubernetes.io/permanent-redirect-code: "307"
          # 4. 明确指定后端服务用 HTTP 协议(避免默认 HTTPS 转发)
          nginx.ingress.kubernetes.io/backend-protocol: "HTTP"
          # 5. 其他原有注解保留
          nginx.ingress.kubernetes.io/proxy-connect-timeout: "60"
          nginx.ingress.kubernetes.io/proxy-read-timeout: "60"
          nginx.ingress.kubernetes.io/proxy-send-timeout: "60"
      spec:
        ingressClassName: nginx
        rules:
          # 无域名规则(直接用 IP 访问)
          - http:
              paths:
                - path: /(.*)
                  pathType: ImplementationSpecific
                  backend:
                    service:
                      name: mortal-frontend-svc
                      port:
                        number: 80
  • 流水线中执行构建镜像命令:

    java 复制代码
    podman build --tls-verify=false -f Dockerfile -t mortal.harbor.com/mortal-system/mortal-front:1.0-SNAPSHOT .

4. 推送镜像

  • 和后端推送进行一样,添加凭证 -> 登陆 harbor -> 推送镜像:

    java 复制代码
    # 登录 harbor
    podman login --tls-verify=false -u $HARBOR_USER -p $HARBOR_PASSWD mortal.harbor.com
    # 推送镜像
    podman push --tls-verify=false mortal.harbor.com/mortal-system/mortal-front:1.0-SNAPSHOT

5. 部署服务

  • 执行我们的 deploy.yaml 来发布项目,和后端一样,添加凭证 -> 执行脚本:代码上没有任何变化

    java 复制代码
    mkdir -p ~/.kube
    echo "$KUBECONFIG_CONTENT" > ~/.kube/config
    envsubst < deploy/deploy.yaml | kubectl apply -f -
  • 测试成功,并且能够成功访问前端项目:


小结

  • 至此便可通过流水线实现一键 CI/CD ,解决了前面我们使用命令行来部署服务的繁琐,并且可通过页面点击来实现服务的扩容与缩容,通过页面便可查看日志信息。
  • kubeSphere 流水线 也可设置触发机制,比如每次仓库更新,自动触发流水线。
相关推荐
一条懒鱼6667 小时前
k8s-网络
网络·容器·kubernetes
Su-RE7 小时前
springboo打包--微服务打包
微服务·架构·pycharm
智能运维指南7 小时前
2025年信创DevOps平台选型:以嘉为蓝鲸为核心的全流程落地指南
devops·研发管理·服务管理·嘉为蓝鲸
silence2507 小时前
k8s集群证书过期--手动更新证书
kubernetes
拾忆,想起7 小时前
Dubbo vs Spring Cloud Gateway:本质剖析与全面对比指南
微服务·性能优化·架构·dubbo·safari
运维栈记7 小时前
CKA题目分享-第八篇-StatefulSets与Headless Services
kubernetes·cka
音符犹如代码8 小时前
Kafka 技术架构与核心原理深度解析
大数据·微服务·架构·kafka
永不停歇的蜗牛8 小时前
K8S中Namespace(ns)、Pod、Service和ConfigMap(cm)四种重要的资源对象的关系
容器·贪心算法·kubernetes
码界奇点8 小时前
基于Spring Cloud Alibaba的分布式微服务权限管理系统设计与实现
分布式·spring cloud·微服务·架构·毕业设计·源代码管理