K8s实战:vue2+SpringBoot+Nacos全栈部署避坑指南

发布时间:2026/10/20 11:52:05
K8s实战:vue2+SpringBoot+Nacos全栈部署避坑指南 简介这是一份面向阿里云Kubernetes环境的微服务部署资料整合了Vue2前端、Nginx反向代理、Spring Boot 2.5后端以及Nacos 2.0.3注册中心的完整部署方案。资源针对前后端分离项目在云端集群中的落地场景涵盖容器镜像构建、Deployment/Service编排、Ingress接入、Nacos有状态服务及MySQL初始化等关键环节适合已有一定K8s基础、希望直接复用或深入学习部署流程的运维与开发人员。资源包共16个文件整体约34.69MB其中8个YAML编排文件用于K8s对象定义2个Shell脚本实现一键部署2个Dockerfile负责镜像构建另有SQL初始化脚本、Nginx配置、后端JAR包和前端静态包各文件类型用途清晰便于按需替换和二次修改。目前已有1341人学习下载。借助该方案读者可快速获得一条可运行的部署链路理解Nacos集群在K8s中的搭建方式并可将示例中的配置与脚本迁移到自有项目中节省环境搭建与排错时间。1. 装这套栈之前先把四个组件的版本坑位摆清楚“阿里云K8s vue2 nginx springboot2.5 nacos2.0.3”这套组合在过去一年里我先后在两个项目上搭过。第一次我信心满满以为按着老教程一步步来半小时就能跑通。结果从上午耗到晚上直到把内网镜像地址、gRPC 端口和 health 探针这三层坑踩完才算消停。这类环境的坑不在于单个组件有多难而是四件套凑到一起后会形成“版本叠加坑”前端 vue2 构建对 node 版本挑食换到 node 18 可能压出诡异产物后端 springboot2.5 对接 nacos2.0.3服务发现不再只靠 8848还多了 9848 这个 gRPC 长连接端口中间再加一套 MySQL8 认证插件兼容问题几乎把“本地能跑、K8s 上挂”的剧本演全了。适合谁手里有 K8s 集群想把 vue2 和 springboot 微服务从 docker-compose 迁到 K8s 的开发者。这篇笔记会按“镜像准备 → Nacos 上集群 → 业务编排 → 排错 → 验证”往下走照着能复现踩过的坑也一并写出来。2. 镜像准备vue2nginx 前端和 springboot2.5 后端镜像一次打好所有 Pod 在集群里跑的都是同一份镜像。镜像层面的问题会传染到后面每一个环节所以第一步先把前端和后端的 Dockerfile 做扎实。2.1 先做前端镜像node16 构建、nginx1.24 收尾vue2nginx 的镜像我的 Dockerfile 固定长这样# Dockerfile.vue FROM node:16-alpine AS build WORKDIR /app COPY package*.json ./ RUN npm ci COPY . . RUN npm run build FROM nginx:1.24-alpine COPY --frombuild /app/dist /usr/share/nginx/html COPY nginx.conf /etc/nginx/conf.d/default.conf EXPOSE 80node:16-alpine 这个构建基础镜像我已经固定了很久。vue2 项目里只要带着 sass-loader、node-sass 这类依赖在 node18 上很容易报 module build failed同样的源码切回 node16 就一次通过。npm ci 依赖 package-lock.json做的是全量安装比 npm install 更可控如果项目没提交 lock 文件就退回 npm install。后面 nginx 层只做两件事把 build 产物拷进镜像再把自定义的 nginx.conf 覆盖到 conf.d 目录。这里有个细节nginx 默认镜像的 /etc/nginx/nginx.conf 会 include /etc/nginx/conf.d/*.conf所以写进 default.conf 就能被加载不用去改主配置文件。构建命令docker build -f Dockerfile.vue -t app-web:1.0.0 .2.2 后端镜像maven 多阶段把体积降下来后端 springboot2.5 的镜像我一般拆两段# Dockerfile.api FROM maven:3.6.3-jdk-8 AS build WORKDIR /app COPY pom.xml . RUN mvn dependency:go-offline -B COPY src ./src RUN mvn package -DskipTests -B FROM openjdk:8-jre-alpine WORKDIR /app COPY --frombuild /app/target/*.jar app.jar EXPOSE 8080 ENTRYPOINT [java,-XX:UseContainerSupport,-Duser.timezoneGMT08,-jar,/app/app.jar]有人会问AC K8s 里跑着 2025 年的基础组件为什么后端还要用 JDK8因为 springboot2.5 官方支持 JDK8nacos2.0.3 服务端也需要 JDK8三个组件用同一套 Java 运行时底座最不容易出偏差。maven 构建环境和 JRE 运行环境拆开最终镜像里只留 JAR 包和 JRE体积能控制在 150MB 上下。-XX:UseContainerSupport这个参数是必加的。没有它JVM 会把宿主机内存当成可用内存在 K8s 设置了 resources.limits 时容易触发 OOMKilled。-Duser.timezone顺手解决日志时间差 8 小时的问题。2.3 推送到镜像仓库ACK 节点走内网地址拉取镜像打完后要推送到仓库ACK 集群里推荐用容器镜像服务 ACRdocker tag app-web:1.0.0 registry.cn-xxxx.aliyuncs.com/yourns/app-web:1.0.0 docker push registry.cn-xxxx.aliyuncs.com/yourns/app-web:1.0.0tag 的地址前缀要以 ACR 控制台里显示为准。ACK 节点拉镜像时尽量选 ACR 提供的内网地址也就是控制台标注为“内网”的那一个下载速度快也更稳。镜像地址要和集群在同一地域跨地域拉镜像在流量高峰期往往慢到怀疑人生。imagePullPolicy 我一般设为 Always避免节点缓存了旧镜像导致发布后还在跑老版本。这个坑在频繁发版时最容易碰上镜像 tag 不变只改代码节点默认 IfNotPresent 就不重新拉取。3. Nacos 2.0.3 上集群数据库初始化、ConfigMap 和 gRPC 端口一个都不能少Nacos 2.0.3 要跑成高可用就不能图省事用 Deployment 单副本跑。这里我直接用 StatefulSet 部署三节点顺带把持久化和端口一次配齐。3.1 先准备 MySQL建库建用户导入官方 SQLNacos 服务端把配置和服务元数据都放 MySQL。先手工建库建用户CREATE DATABASE nacos_config DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE USER nacos% IDENTIFIED BY Nacos#2025; GRANT ALL PRIVILEGES ON nacos_config.* TO nacos%; FLUSH PRIVILEGES; USE nacos_config; SOURCE /path/to/mysql-schema.sql;mysql-schema.sql 从 nacos2.0.3 官方安装包里 conf 目录拿必须用 2.0.3 同版本的 SQL 初始化。我见过有人拿 1.x 的 schema 去初始化 2.0 服务端控制台能开但服务列表和配置记录查着查着就缺字段排查起来非常费劲。MySQL 实例我建议跑在单独的命名空间甚至单独的 StatefulSet 里别和 Nacos 挤在同一份资源里。两者生命周期完全独立能避免一次故障连带两个组件一起挂。3.2 用 StatefulSet 跑三节点稳定网络标识和持久化Nacos 集群节点之间要互相感知依赖的是固定主机名。StatefulSet 里 Pod 名是 nacos-0、nacos-1、nacos-2 这种稳定序号配合 Headless Service 就能得到固定域名。用 Deployment 的话Pod 名每次重建都换Nacos 节点列表会对不上。先写 ConfigMap# nacos-config.yaml apiVersion: v1 kind: ConfigMap metadata: name: nacos-config namespace: infra data: NACOS_SERVERS: nacos-0.nacos-headless.infra:8848 nacos-1.nacos-headless.infra:8848 nacos-2.nacos-headless.infra:8848 MYSQL_SERVICE_HOST: mysql-svc.infra MYSQL_SERVICE_DB_NAME: nacos_config MYSQL_SERVICE_USER: nacos MYSQL_SERVICE_PASSWORD: Nacos#2025 MODE: cluster JVM_XMS: 1g JVM_XMX: 1gNACOS_SERVERS 变量必须用空格分隔三个节点地址末尾不能带分号。MYSQL_SERVICE_* 是 Nacos 官方镜像约定要读的环境变量直接通过 envFrom 注入比改配置文件省事。JVM_XMS 建议给到 1gNacos 2.0 相对吃内存太小的堆容易在启动阶段就异常退出。StatefulSet 完整配置# nacos-statefulset.yaml apiVersion: apps/v1 kind: StatefulSet metadata: name: nacos namespace: infra spec: serviceName: nacos-headless replicas: 3 selector: matchLabels: app: nacos template: metadata: labels: app: nacos spec: containers: - name: nacos image: registry.cn-xxxx.aliyuncs.com/yourns/nacos-server:2.0.3 envFrom: - configMapRef: name: nacos-config ports: - containerPort: 8848 - containerPort: 9848 - containerPort: 9849 volumeMounts: - name: data mountPath: /home/nacos/data volumeClaimTemplates: - metadata: name: data spec: accessModes: [ ReadWriteOnce ] resources: requests: storage: 10GivolumeClaimTemplates 是 StatefulSet 和 Deployment 的关键区别。Nacos 的 data 目录里放着集群节点元数据如果只是 ephemeral 存储Pod 重建后节点信息全丢集群会反复重新选主。阿里云集群里 storageClassName 我一般选云盘 ESSD性能和数据可靠性都比 emptyDir 稳妥。3.3 gRPC 端口映射9848 和 9849 别漏Nacos 2.0 的端口模型和 1.x 不一样了。8848 还负责 HTTP 接口和控制台但客户端连接走的是 gRPC 长连接端口固定是 88481000 也就是 98489849 则是服务端之间通信用的偏移端口。规则是固定的偏移量 1000。对应的 Headless Service 必须把这几个端口都暴露# nacos-service.yaml apiVersion: v1 kind: Service metadata: name: nacos-headless namespace: infra spec: clusterIP: None selector: app: nacos ports: - name: http port: 8848 targetPort: 8848 - name: grpc port: 9848 targetPort: 9848 - name: grpc-server port: 9849 targetPort: 9849如果还需要公网访问 Nacos 控制台除了 Ingress 转发 8848 之外还要额外转发 9848。我只放通 8848 的时候客户端注册服务偶尔成功、长连接却一直重建控制台能看到部分节点但服务列表总是缺斤短两。这个坑后面第 5 章还会展开。4. K8s 资源编排SpringBoot 的 Deployment、探针与 Ingress 暴露镜像和 Nacos 都就位后剩下的就是把业务应用编排进集群。前端 Ingress 接收入口流量后端 Deployment 对外提供接口。4.1 前端 nginx.conftry_files 和接口转发一起配vue2 项目的 nginx.conf 是事故高发区。很多人直接把本地 Docker 里的 nginx 配置原封不动拷过来缺了 try_fileshistory 模式下刷新 /login 就直接 404。server { listen 80; server_name _; root /usr/share/nginx/html; index index.html; location / { try_files $uri $uri/ /index.html; expires 1d; } location /api/ { proxy_pass http://api-svc.default.svc.cluster.local:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } location ~* \.(js|css|png|jpg|jpeg|gif|svg|ico|woff2?)$ { expires 30d; add_header Cache-Control public, no-transform; } }try_files $uri $uri/ /index.html;解决的是 SPA 路由回退问题先找真实文件找不到就统一交给 index.html让 vue-router 自己接管。第二段location /api/把前端发起的 /api/xxx 请求转发给后端 Service。这里 proxy_pass 我故意不写 URI请求路径会原样保留后端 Controller 按 /api/xxx 接收。如果你想去掉 /api 前缀再转给后端proxy_pass 就要写成http://api-svc...:8080/;两边路径必须对齐否则接口 404 的排查会非常绕。expires 1d 只给 index.html静态资源带 hash 的可以放开 30 天。index.html 如果缓存时间太长发版后用户很容易停留在旧页面。4.2 后端 Deployment探针、环境变量一起注入springboot2.5 的 Deployment 我习惯把 Nacos 地址通过环境变量注入配置文件留在镜像里保持不动# api-deployment.yaml apiVersion: apps/v1 kind: Deployment metadata: name: api-server namespace: default spec: replicas: 3 selector: matchLabels: app: api-server template: metadata: labels: app: api-server spec: containers: - name: api-server image: registry.cn-xxxx.aliyuncs.com/yourns/api-server:1.0.0 ports: - containerPort: 8080 env: - name: SPRING_PROFILES_ACTIVE value: k8s - name: SPRING_CLOUD_NACOS_DISCOVERY_SERVER_ADDR value: nacos-headless.infra:8848 - name: SPRING_CLOUD_NACOS_CONFIG_SERVER_ADDR value: nacos-headless.infra:8848 readinessProbe: httpGet: path: /actuator/health port: 8080 initialDelaySeconds: 30 periodSeconds: 10 timeoutSeconds: 2 livenessProbe: httpGet: path: /actuator/health port: 8080 initialDelaySeconds: 60 periodSeconds: 15 timeoutSeconds: 2readinessProbe 和 livenessProbe 是这套环境里保命的两道闸门。readiness 探针决定流量要不要放进来liveness 探针决定容器要不要重启。springboot2.5 自带 actuator前提是 pom 里引入 spring-boot-starter-actuator并且在 application.yml 里放开management.endpoints.web.exposure.includehealth,metrics。探针路径直接写 /actuator/health 就行这个端点返回 JSON 里的 status 字段。注意探针的 path 不要带 spring security 的鉴权。如果项目里加了安全框架必须把 /actuator/health 加入白名单否则探针一直拿 401Pod 永远起不来。4.3 后端 Service客户端地址用 Service 名不用 Pod IP后端 Service 用 ClusterIP 类型就够了# api-service.yaml apiVersion: v1 kind: Service metadata: name: api-svc namespace: default spec: selector: app: api-server ports: - name: http port: 8080 targetPort: 8080这个 Service 同时被 nginx.conf 的 proxy_pass 和 Ingress 引用。springboot 向 Nacos 注册时上报的地址是 Pod IP由 K8s 网络自己管理不需要也不应该在配置里硬编码。客户端配置 Nacos 地址时写的是 nacos-headless.infra:8848那是 Nacos 服务端地址和业务自己的注册地址是两回事。4.4 Ingress 暴露前端入口最后把前端路由打出去# ingress.yaml apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: app-ingress namespace: default annotations: nginx.ingress.kubernetes.io/proxy-body-size: 10m spec: ingressClassName: nginx rules: - host: app.example.com http: paths: - path: / pathType: Prefix backend: service: name: web-svc port: number: 80这里 Ingress 只负责前端静态页面入口接口转发已经由前端 nginx.conf 里的 proxy_pass 完成了。业务链路是Ingress → 前端 nginx → 后端 Service少一层转发就少一层排查点。proxy-body-size 注解设成 10m是因为 Nginx 默认 body 大小只有 1m上传文件稍微大一点就直接 413。5. 避坑五个最容易翻车的地方这里把我在部署过程中真实遇到过的故障整理出来每一条都是现场排过的。5.1 接入层问题前端路由 404 与探针失败坑一vue2 history 模式刷新任意子路由就 404。现象首页能开点进 /login 再刷新nginx 直接返回 404。原因nginx 没配 try_files。history 模式下 URL 路径不是真实文件路径nginx 找不到 /login 这个文件自然就 404 了。解决在 location / 里补try_files $uri $uri/ /index.html;。改完 nginx.conf 要重新构建前端镜像进容器手工改文件在 Pod 重建后会丢。坑二SpringBoot 的健康探针一直失败Pod 处于 NotReady。现象Pod 状态一直是 0/1readiness 探针日志全是 404 或 401。原因actuator 依赖没加到 pom或者 management.endpoints.web.exposure.include 只配了 metrics 没包含 health另一个隐蔽原因是 spring security 把 /actuator/health 拦截了。解决先把 actuator 依赖补齐再确认 exposure 里包含 health安全配置里放行健康检查路径。进 Pod 手动验证curl http://localhost:8080/actuator/health看到{status:UP}后再让探针接手。5.2 注册与配置中心问题认证插件、gRPC 端口、命名空间坑三Nacos 连 MySQL8 起不来日志里有 caching_sha2_password 字样。现象Nacos 容器反复 CrashLoopBackOff启动日志抛 SQL 认证异常。原因MySQL8 默认认证插件是 caching_sha2_passwordNacos 2.0.3 自带的 MySQL 驱动对它的支持不完整。解决给 Nacos 的 MySQL 用户单独指定 mysql_native_password 认证方式ALTER USER nacos% IDENTIFIED WITH mysql_native_password BY Nacos#2025; FLUSH PRIVILEGES;如果不允许改认证插件那就升级 Nacos 的 MySQL 驱动但工程量更大。我偏好前一种方案环境隔离干净。坑四服务列表间歇性消失控制台节点数不对。现象部署完成后服务注册上了但过一会儿再看就掉线或者列表里实例一直处于不健康状态。原因Nacos 2.0 客户端连接服务端走的是 gRPC 长连接9848 端口被 Service 或安全组挡住了。客户端实际处于“连接成功又断开、断开再重连”的循环里。解决检查 nacos Headless Service 是否暴露了 9848 和 9849如果有 LoadBalancer 或 Ingress也要确认这两个端口同时转发。只放 8848 是 Nacos2.0 部署最常见的失误。坑五服务能注册但配置中心一直拉不到配置。现象Nacos 控制台里能看到配置客户端启动日志却提示 config data is empty。原因命名空间对不上。Nacos 控制台显示的是“命名空间ID”不是名称。客户端 yml 里如果写了 namespace: public而控制台默认 public 的 ID 其实是空字符串两边就对不齐。解决把客户端 namespace 去掉或者显式使用控制台里创建的命名空间 IDspring: cloud: nacos: config: server-addr: nacos-headless.infra:8848 namespace: dev_id_123 group: DEFAULT_GROUP排查时可以直接用 Nacos 的 OpenAPI 手动查一次配置确认服务端文件存在再回头看客户端配置这样能快速定位是命名空间还是 group 的问题。6. 验证跑通用四段 curl 串起整套系统部署完成后推荐按顺序做四步验证。最后把它们整理成一个脚本每次发版强制跑一遍。先确认资源状态kubectl get pod -n infra -o wide kubectl get svc -n infra kubectl get ingress -n defaultPod 都处于 Running 且 READY 正常Ingress 的 ADDRESS 不为空再往下走。第二步验证前端入口和 SPA 路由curl -s -o /dev/null -w %{http_code} %{time_total}\n http://app.example.com/ curl -s -o /dev/null -w %{http_code}\n http://app.example.com/login第一条返回 200 说明 Ingress 和前端镜像通了第二条也返回 200说明 try_files 生效history 路由没有 404。第三步验证接口转发链路curl -s http://app.example.com/api/user/1 | head -c 500能返回 JSON 数据说明 前端nginx → 后端Service → springboot 整个链路是通的。如果这里 404需要区分是前端 proxy_pass 的问题还是后端 Service 选择器的问题进 Pod 里直接访问 127.0.0.1 的 health 端点做一个隔离判断。第四步验证 Nacos 注册中心curl -s http://nacos-headless.infra:8848/nacos/v1/ns/instance/list?serviceNameapi-servergroupNameDEFAULT_GROUP或者打开控制台看服务列表确认 api-server 有 3 个健康实例。到这一步注册中心、配置中心、前端入口、后端接口四个环节全部验证完成。这些小技巧我后来整理成了一个 check.sh发版后自动执行并把输出追加到日志。从那以后我每次部署完都会强制走一遍这个脚本不只看页面能不能打开而是分别确认静态资源、SPA 路由、接口代理、服务注册四个环节都通过再通知测试进场。这套流程我在模拟项目X和另一个跨平台系统上都验证过稳住了不少应急场面希望帮到你。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询