K8s 集群部署微服务 - yaml 版本(三)
| K8s 集群环境搭建 - yaml 版本(一) |
|---|
| K8s 集群部署中间件 - yaml 版本(二) |
| K8s 集群部署微服务 - yaml 版本(三) |
文章目录
- [K8s 集群部署微服务 - yaml 版本(三)](#K8s 集群部署微服务 - yaml 版本(三))
- 一、制作镜像
-
- [1. package 生成可直接运行的 jar包](#1. package 生成可直接运行的 jar包)
- [2. 制作镜像](#2. 制作镜像)
- [3. 验证镜像](#3. 验证镜像)
- [4. 上传镜像](#4. 上传镜像)
- 二、授权服务部署
-
- [1. Service](#1. Service)
- [2. Deployment](#2. Deployment)
- 三、业务服务部署
-
- [1. Service](#1. Service)
- [2. PVC](#2. PVC)
- [3. Deployment](#3. Deployment)
- 四、网关服务部署
-
- [1. Service](#1. Service)
- [2. PVC](#2. PVC)
- [3. Deployment](#3. Deployment)
- [4. Ingress](#4. Ingress)
- [5. 验证](#5. 验证)
- 五、前端项目部署
-
- [1. 制作镜像](#1. 制作镜像)
- [2. 部署服务](#2. 部署服务)
- 3.验证
- 疑问与总结
- [为什么 Deployment 需要单独创建PVC,而不是像 StatefulSet 一样定义volumeClaimTemplates?](#为什么 Deployment 需要单独创建PVC,而不是像 StatefulSet 一样定义volumeClaimTemplates?)
- [部署 Deployment 是如何关联上 Nacos 配置中心的配置文件?](#部署 Deployment 是如何关联上 Nacos 配置中心的配置文件?)
- 结尾
一、制作镜像
1. package 生成可直接运行的 jar包
-
使用 k8s 部署服务之前,需要将我们的服务制作成镜像,以便在后续使用该镜像启动服务。
-
制作镜像需要做一下几步:
-
将我们需要运行的微服务通过 maven 生成 可直接运行的 jar 包。在该微服务的 pom 文件中需要加上 springboot 打包插件,通过该插件则无需手动配置类路径、依赖包,只需通过 mvn package 生成可执行 Jar 包,直接用 java -jar 启动,实现「一键部署」,没有该插件 jar 是无法直接运行的,会报错。注:加载需要打包的微服务pom中,而不是 parent 中。
java<build> <plugins> <!-- 核心:Spring Boot打包插件,负责生成主清单 --> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>2.6.13</version> <!-- 如2.7.10 --> <executions> <execution> <goals> <goal>repackage</goal> <!-- 关键:重新打包为可执行Jar --> </goals> </execution> </executions> </plugin> </plugins> </build>
-
-
通过 maven package 生成 jar包。如果该微服务依赖了其他的公共服务,需要先将依赖的服务先执行 maven install,从而把包上传至本地仓库,供本地项目使用,不然会打包失败,无法找到依赖的项目。
- mvn package:「本地打包」------ 生成可执行 / 依赖包,仅存本地项目。
- mvn install:「本地仓库安装」------ 把包上传到本地仓库,供所有本地项目引用。
2. 制作镜像
-
生成了 jar 包之后,如果我们需要将包上传至服务器,可通过如下面命令上传。
scp [源文件] user@[目标ip]:[目录],输入用户登录密码即可。javascp .\mortal-authority-1.0-SNAPSHOT.jar root@14.103.138.45:/opt/mortal/jar -
编写 dockerfile 来制作镜像。
java# 基础镜像:和你的 JDK 版本匹配(JDK 8 用 8-jre-slim,轻量且安全) FROM 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 jar/mortal-authority-1.0-SNAPSHOT.jar /opt/mortal/app/mortal-authority.jar # 给非 root 用户授权(避免启动时权限不足) RUN chown -R appuser:appgroup /opt/mortal/app # 切换到非 root 用户运行 USER 1001 # 暴露服务端口(和授权服务的 server.port 一致,比如 2001) EXPOSE 2001 # 启动命令(exec 格式,支持优雅停机) ENTRYPOINT ["java", "-jar", "mortal-authority.jar"] -
通过下面命令制作镜像:
javadocker build -f dockerfile/mortal-authority-service.Dockerfile -t mortal.harbor.com/mortal-system/mortal-authority:1.0-SNAPSHOT .- dockerfile/mortal-authority-service.Dockerfile:我们需要使用的 dockerfile 文件。
- mortal.harbor.com/mortal-system/mortal-authority:1.0-SNAPSHOT:制作成的镜像名:版本号。
- . :构建上下文 Context。告诉 Docker 「构建镜像时,可以访问哪些文件 / 目录」------ Docker 构建时,会把「构建上下文」下的所有文件递归打包,发送给 Docker 守护进程(daemon),供 Dockerfile 中的 COPY、ADD 等指令使用。与 dockerfile 文件中的指令:COPY jar/mortal-authority-1.0-SNAPSHOT.jar /opt/mortal/app/mortal-authority.jar。这里的 target/xxx.jar 路径,就是相对于「构建上下文(.)」的 ------ 如果构建上下文不是 target 所在的目录,COPY 会失败!
-
查看镜像:
javadocker images
3. 验证镜像
-
成功制作成镜像后需要先验证下镜像是否可用,防止到 k8s 部署时出错时,方便问题排查。运行镜像:
javadocker run -d -p 2001:2001 --name auth-tes mortal.harbor.com/mortal-system/mortal-authority:1.0-SNAPSHOT -
如果运行没问题则继续后续操作。
4. 上传镜像
-
将我们的镜像上传至镜像仓库:
javadocker push mortal.harbor.com/mortal-system/mortal-authority:1.0-SNAPSHOT
-
至此镜像制作完了,方便后续引用。
二、授权服务部署
- 首先需要确定部署一个微服务需要的资源有哪些:
- Deployment:定义微服务部署规则(镜像地址、副本数、资源限制、健康检查、配置注入、日志存储挂载)。必须。
- Service(ClusterIP):暴露微服务为 K8s 内部服务,供集群内其他服务(或网关)访问。必须。
- PersistentVolumeClaim(PVC):挂载 NFS 存储用于微服务日志持久化(若不需要日志持久化,可省略)。非必须。
- 以及多个微服务共用一个 Ingress,实现外部访问入口。由于我们需要部署 网关微服务,所以我们会将 Ingress 的请求转发给网关服务,由网关实现更细粒度的路由、鉴权、限流等功能,结构更清晰。
1. Service
- 提供给集群内部访问的入口。
java
# Service配置(暴露微服务,供内部集群访问)
apiVersion: v1
kind: Service
metadata:
name: author-service
namespace: mortal-system
spec:
selector:
app: author-service
type: ClusterIP # 集群内部访问,如需外部访问可改为NodePort或LoadBalancer
ports:
- port: 2001 # Service端口
targetPort: 2001 # 容器端口(与Deployment中containerPort一致)
- 查看部署的 service:
kubectl get svc -n [namespace]

2. Deployment
- 授权服务
java
apiVersion: apps/v1
kind: Deployment
metadata:
name: author-service # 部署名称
namespace: mortal-system # 建议创建专属命名空间,也可改为你的现有命名空间
labels:
app: author-service
spec:
replicas: 3 # 副本数,可根据需求调整
selector:
matchLabels:
app: author-service
template:
metadata:
labels:
app: author-service
spec:
containers:
- name: author-service
# 镜像地址(harbor中的目标镜像)
image: mortal.harbor.com/mortal-system/mortal-authority:1.0-SNAPSHOT
imagePullPolicy: IfNotPresent # 镜像拉取策略:本地无则拉取
# 容器端口(对应微服务的启动端口,如8080、9999等,需与服务配置一致)
ports:
- containerPort: 2001
# 环境变量配置(连接nacos、mysql,根据你的实际配置修改值)
env:
# 1. 微服务名称(对应 Nacos DataID 前缀)
- name: SPRING_APPLICATION_NAME
value: "mortal-authority-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: /mortal-authority/actuator/health
port: 2001
initialDelaySeconds: 60 # 启动后延迟60秒检查
periodSeconds: 10 # 每10秒检查一次
readinessProbe:
httpGet:
path: /mortal-authority/actuator/health
port: 2001
initialDelaySeconds: 30
periodSeconds: 5
# 挂载动态PVC(与volumeClaimTemplates名称对应)
volumeMounts:
- name: author-log-volume # 必须与volumeClaimTemplates.name一致
mountPath: /app/logs # 容器内持久化目录
volumes: # 新增volumes字段引用PVC
- name: author-log-volume
persistentVolumeClaim:
claimName: author-log-pvc # 对应上面创建的PVC名称
-
需要注意 就绪探针 的配置
java# 健康检查(可选,建议配置) livenessProbe: httpGet: path: /mortal-authority/actuator/health port: 2001 initialDelaySeconds: 60 # 启动后延迟60秒检查 periodSeconds: 10 # 每10秒检查一次 readinessProbe: httpGet: path: /mortal-authority/actuator/health port: 2001 initialDelaySeconds: 30 periodSeconds: 5 -
path 为 :
[项目配置文件中 server.servlet.context-path] / actuator/health,否则会出现就绪探针失败的问题:context deadline exceeded。- 就绪探针失败的 Pod 会被 K8s 从 Service 的负载均衡池中 "剔除"------ 不会接收任何请求(包括集群内其他微服务的调用、外部通过 Ingress 的访问),直到探针检测成功(Pod 就绪)。
-
查看部署的服务:

-
虽然是 running ,但也要查看日志,因为 就绪探针失败,也是 running。如下就是失败了:

-
所以需要使用命令检查下:
javakubectl describe pod [podName] -n [namespace]
三、业务服务部署
1. Service
-
提供向集群内提供服务的 Service:
java# Service配置(暴露微服务,供内部集群访问) apiVersion: v1 kind: Service metadata: name: system-service namespace: mortal-system spec: selector: app: system-service type: ClusterIP # 集群内部访问,如需外部访问可改为NodePort或LoadBalancer ports: - port: 8001 # Service端口 targetPort: 8001 # 容器端口(与Deployment中containerPort一致)
2. PVC
-
申请存储空间,用来存储日志。
java# author-pvc.yaml apiVersion: v1 kind: PersistentVolumeClaim metadata: name: system-log-pvc namespace: mortal-system spec: accessModes: [ "ReadWriteMany" ] storageClassName: "nfs-sc" resources: requests: storage: 2Gi
3. Deployment
- 部署服务,我这里是部署的系统管理服务:
java
apiVersion: apps/v1
kind: Deployment
metadata:
name: system-service # 部署名称
namespace: mortal-system # 建议创建专属命名空间,也可改为你的现有命名空间
labels:
app: system-service
spec:
replicas: 3 # 副本数,可根据需求调整
selector:
matchLabels:
app: system-service
template:
metadata:
labels:
app: system-service
spec:
containers:
- name: system-service
# 镜像地址(harbor中的目标镜像)
image: mortal.harbor.com/mortal-system/mortal-service-system:1.0-SNAPSHOT
imagePullPolicy: IfNotPresent # 镜像拉取策略:本地无则拉取
# 容器端口(对应微服务的启动端口,如8080、9999等,需与服务配置一致)
ports:
- containerPort: 8001
# 环境变量配置(连接nacos、mysql,根据你的实际配置修改值)
env:
# 1. 微服务名称(对应 Nacos DataID 前缀)
- name: SPRING_APPLICATION_NAME
value: "mortal-service-system"
# 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: /mortal-service-system/actuator/health
port: 8001
initialDelaySeconds: 60 # 启动后延迟60秒检查
periodSeconds: 10 # 每10秒检查一次
readinessProbe:
httpGet:
path: /mortal-service-system/actuator/health
port: 8001
initialDelaySeconds: 30
periodSeconds: 5
# 挂载动态PVC(与volumeClaimTemplates名称对应)
volumeMounts:
- name: author-log-volume # 必须与volumeClaimTemplates.name一致
mountPath: /app/logs # 容器内持久化目录
volumes: # 新增volumes字段引用PVC
- name: author-log-volume
persistentVolumeClaim:
claimName: author-log-pvc # 对应上面创建的PVC名称
四、网关服务部署
1. Service
-
提供给集群内部访问的入口。
java# 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一致) -
name 为:gateway-service

2. PVC
-
创建网关微服务的 pv 和 pvc:
java# 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
3. Deployment
- 部署 gateway 网关微服务,其中的配置文件由 nacos 配置中心管理,并在 Deployment 中指定配置文件。
SPRING_APPLICATION_NAME - SPRING_PROFILES_ACTIVE.yaml。并将服务注册进 nacos 注册中心。

-
Deployment 代码如下:
javaapiVersion: apps/v1 kind: Deployment metadata: name: gateway-service # 部署名称 namespace: mortal-system # 建议创建专属命名空间,也可改为你的现有命名空间 labels: app: gateway-service spec: replicas: 3 # 副本数,可根据需求调整 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: IfNotPresent # 镜像拉取策略:本地无则拉取 # 容器端口(对应微服务的启动端口,如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名称 -
部署成功后:

4. Ingress
- 我们需要网关服务能够对外提供服务,并且由网关服务负责进行转发。
java
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
5. 验证
-
首先查看 ingress 向外提供的端口号,可以使用 k8s 集群中任意节点的 ip + 端口来访问:

-
可以发现 http 请求端口是 31360 映射 80。我们这里用的是 http 请求,所以可以用任意节点的 ip + 31360/ xxx 来访问ingress 向外提供的服务。 (443:31689 对应的是https 请求)。
-
使用 postman 发起服务注册服务: 请求流程是: postman -> ingress -> gateway-service -> gateway 服务 (转发) -> authority-servie -> authority 服务。
五、前端项目部署
-
我的前端项目为 vue3 + typeScript。
-
使用 k8s 部署前端项目的流程是:
- 编写 front.Dockerfile 文件:构建镜像。
- 编写 front.dockerignore,用于 Docker 构建镜像时需要排除的文件 / 目录,减小镜像体积。
- 上传 项目源码:用于构建镜像。
- 编写 front-service.yaml:向k8s集群内提供服务。
- 编写 front-k8s.yaml:服务部署。
- 编写 front-ingress.yaml:向集群外提供服务。
- 编写 nginx.conf : 解决 Vue Router History 模式的「刷新 404」问题,优化静态资源缓存,减轻 K8s 集群压力。
-
我上传后文件结构如下:
front/ # 你执行 docker build 的目录(当前目录)
├── dockerfile/
│ └── front.Dockerfile # 你的 Dockerfile
│ └── nginx.conf
├── mortal # 项目源码
│ └── package.json
│ └── package-lock.json
└── .dockerignore # 注意不要排除 package.json
-
文件上传后需要制作镜像了
1. 制作镜像
-
front.Dockerfile
java# ==================== 阶段1:构建 Vue 项目(Node 环境)==================== FROM node:18-alpine AS builder # 设置工作目录(root 用户创建,有权限) WORKDIR /app # 1. 复制依赖文件(root 用户,无权限问题) COPY mortal/package.json mortal/package-lock.json ./ # 2. 安装依赖(--unsafe-perm 尝试赋予权限,基础保障) RUN npm ci --unsafe-perm # 3. 复制项目所有核心文件(root 用户,无权限问题) COPY mortal/ ./ # 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 nginx:alpine # 复制 dist 产物到 Nginx 静态目录(已验证正确) COPY --from=builder /app/dist /usr/share/nginx/html # 启用自定义 Nginx 配置(关键!解决路由 404) COPY dockerfile/nginx.conf /etc/nginx/conf.d/default.conf # 暴露端口(可选,仅声明) EXPOSE 80 # 启动 Nginx CMD ["nginx", "-g", "daemon off;"] -
front.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 -
nginx.conf:
javaserver { 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"; } } -
在 front 目录下执行制作镜像命令:
javadocker build -f dockerfile/front.Dockerfile -t mortal.harbor.com/mortal-system/mortal-front:1.0-SNAPSHOT . -
查看镜像:

-
镜像制作完之后,先不急后续流程,先验证镜像是否可用,使用镜像部署容器:
javadocker run -d -p 8888:80 --name mortal-front-test mortal.harbor.com/mortal-system/mortal-front:1.0-SNAPSHOT -
如果镜像可正常访问服务,则继续后续流程:
-
上传进行至 harbor:
javadocker push mortal.harbor.com/mortal-system/mortal-front:1.0-SNAPSHOT -
后续部署服务需要使用我们制作的镜像。
2. 部署服务
-
部署 Service 向集群内提供服务:front-service.yaml
java--- # 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 # 对应容器端口 -
使用我们上传的镜像进行服务部署:front-k8s.yaml
java# frontend-prod-deploy.yaml apiVersion: apps/v1 kind: Deployment metadata: name: mortal-frontend namespace: mortal-system spec: replicas: 3 # 生产环境建议 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 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 -
部署 ingress,向集群外提供服务,让我们能够访问页面:
javaapiVersion: 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 -
至此前端服务就部署完毕了,如果使用域名访问,需要修改 ingress,配置域名访问规则。
3.验证
- 通过前面已经部署的 ingress-controller 知道,通过端口 31360 -> 80 映射,我们访问 任意节点ip + 端口 + / xxx 来访问前端页面:

- 发现可以正常访问,部署完成。
疑问与总结
为什么 Deployment 需要单独创建PVC,而不是像 StatefulSet 一样定义volumeClaimTemplates?
-
首先 volumeClaimTemplates 是 StatefulSet(有状态服务) 的专属字段。为 StatefulSet 管理每一个 pod 都会单独创建一个独立的、与 pod 生命周期绑定的 pvc 以及对应的 pv。每个 Pod 挂载自己的专属存储,数据互不干扰,且 Pod 重建(即使调度到其他节点)仍能绑定到原来的 PVC。
-
Deployment(无状态服务)并不需要 "专属存储",所有副本共享同一套数据(存在 MySQL/MinIO),本地仅存储临时文件、日志(可选):
- 日志可通过 NFS 共享存储挂载(所有副本写日志到同一个 NFS 目录,或按 Pod 名分目录),无需每个副本专属 PVC。
- 临时文件不需要持久化,Pod 销毁后可丢失,用 emptyDir 即可。如果给 Deployment 加 volumeClaimTemplates,会导致每个副本有独立存储,但这些存储里的数据毫无意义(因为服务无状态),反而浪费资源。
-
对比:Deployment vs StatefulSet 存储设计差异:
特性 Deployment(无状态) StatefulSet(有状态) 存储需求 共享存储(日志)或无持久化 每个实例专属存储(核心数据) 存储绑定关系 所有副本挂载同一个 PVC 每个副本绑定独立 PVC(由模板生成) 实例身份 无身份(名称随机) 有固定身份(名称有序:xxx-0、xxx-1) 数据独立性 无(数据存在共享存储) 有(每个实例数据独立) 是否支持 volumeClaimTemplates 不支持(设计目标冲突) 支持(核心特性)
部署 Deployment 是如何关联上 Nacos 配置中心的配置文件?
-
Nacos 配置文件的 DataID 默认拼接规则是: s p r i n g . a p p l i c a t i o n . n a m e − {spring.application.name}- spring.application.name−{spring.profiles.active}.yaml,对应 Deployment 配置如下:(file-extension 默认为 yaml)

-
nacos 对应配置:

-
结尾
- 至此使用命令行部署yaml文件的方式来部署项目差不多就结束了,其中有些安全要求会更高,需要添加一些额外安全配置,比如部署 Nacos 的时候可以开启安全模式,比如一些用户名密码统一放到 Secret 中进行管理,并且不使用明文的方式,不过大致的流程就是这样的。总的来看,可以发现如下痛点:
- 每一次部署,无论是中间件,还是微服务,都需要编写大量的 yaml 文件,这些yaml 文件难以管理。
- 每次服务部署,扩容,缩容,查看日志,都需要手动敲命令行,非常繁琐。
- 每次服务更新都要重新 package,重新上传jar包,重新制作镜像并上传 harbor,重新 部署 deployment 从而更新其中的镜像,这个过程,可想而知...
- 带着这些痛点,继续探索。