简介:本资源是一套面向Kubernetes初学者与中级开发者的阿里云K8s生产级部署实践方案,聚焦Vue2前端+SpringBoot2.5后端+Nacos2.0.3服务注册中心的全栈微服务上云落地。资源提供开箱即用的一键部署能力,覆盖容器化构建、服务编排、配置管理及流量接入等关键环节,适用于企业级微服务项目迁移或云原生教学实验场景。压缩包共16个文件,含8个K8s YAML编排文件(涵盖StatefulSet、Ingress、Service等核心资源)、2个Shell部署脚本、2个Dockerfile(分别构建Vue静态站点与SpringBoot应用)、1个MySQL初始化SQL、1个Nginx配置文件、1个SpringBoot可执行JAR及1个dist前端打包压缩包,整体体积34.69MB,结构清晰、模块解耦。目前已有1340人学习下载,读者可直接复用整套YAML模板与脚本,快速搭建高可用Nacos集群、Vue+SpringBoot联调环境,并掌握K8s下前后端分离项目的标准化发布流程。
1. 阿里云 K8s 上跑通 Vue2 + Nginx + SpringBoot2.5 + Nacos2.0.3:不是堆组件,而是让四层链路真正“活”起来
你手头有个老项目:Vue2 写的管理后台、SpringBoot2.5 做后端服务、Nacos2.0.3 当注册中心和配置中心、Nginx 做静态资源托管和反向代理——它在本地npm run serve+mvn spring-boot:run+sh startup.sh -m standalone能跑,但一上阿里云 Kubernetes 就卡在「页面白屏」「接口 502」「Nacos 注册失败」「Config 不生效」这四连击。这不是环境差异,是四层技术栈在 K8s 编排语境下彻底失联:Vue2 打包产物没被 Nginx 正确挂载;SpringBoot 的bootstrap.yml读不到 Nacos 配置;Nacos 本身没做高可用和持久化;Nginx 的反向代理路径和 K8s Service 的端口映射对不上。本文不讲「K8s 是什么」,只解决一个具体问题:用阿里云 ACK(容器服务 Kubernetes 版)把这套组合稳稳落地,所有组件可独立升级、可观测、可回滚,且能复现到任意新集群。适合正在迁移老系统、接手遗留 Vue2+SpringBoot 项目的 DevOps 工程师、全栈工程师和架构支持岗——你不需要从零搭 K8s,但必须知道每个 YAML 文件里哪一行改错会导致整个链路静默崩溃。
2. 四层拆解:为什么必须分治部署,而不是塞进一个 Pod
K8s 不是 Docker Compose 的放大版。把 Vue2、Nginx、SpringBoot、Nacos 全塞进一个 Pod,看似省事,实则埋下三颗雷:第一,Nacos 作为中间件需独立扩缩容和持久化,和前端静态资源强耦合会拖垮整个 Pod 生命周期;第二,Vue2 构建产物是纯静态文件,Nginx 容器只需挂载 ConfigMap + emptyDir 即可,没必要和 Java 进程共存;第三,SpringBoot2.5 默认启用 Actuator 和 Nacos 自动注册,若与 Nacos 同 Pod,网络通信走 localhost,掩盖了 Service DNS 解析失败的真实问题。生产级部署必须分治:Nacos 独立 StatefulSet(带 PVC),SpringBoot 独立 Deployment(带 readinessProbe),Nginx 独立 Deployment(挂载构建产物 ConfigMap),Vue2 构建产物提前注入 Nginx 镜像或通过 InitContainer 拉取。下面按实际交付顺序展开——先搞定 Nacos,再铺 SpringBoot,最后接 Vue2+Nginx,每一步都卡在真实线上踩过的坑上。
2.1 Nacos2.0.3:用 StatefulSet + PVC 实现数据不丢、重启不重注册
Nacos2.0.3 要求 MySQL 存储(官方明确弃用 Derby),且集群模式需至少 3 节点。阿里云 ACK 上最稳妥做法是:单节点 Standalone 模式 + 阿里云 RDS MySQL + PVC 持久化日志与插件目录。别信「Standalone 不能上生产」——只要配置中心和注册中心流量可控(QPS < 500),Standalone 更易观测、升级无脑、故障面小。关键 YAML 片段如下:
# nacos-sts.yaml apiVersion: apps/v1 kind: StatefulSet metadata: name: nacos namespace: middleware spec: serviceName: "nacos-headless" replicas: 1 selector: matchLabels: app: nacos template: metadata: labels: app: nacos spec: containers: - name: nacos image: nacos/nacos-server:v2.0.3 env: - name: MODE value: "standalone" - name: SPRING_PROFILES_ACTIVE value: "mysql" - name: MYSQL_SERVICE_HOST value: "rm-xxx.mysql.rds.aliyuncs.com" # 替换为你的 RDS 地址 - name: MYSQL_SERVICE_PORT value: "3306" - name: MYSQL_SERVICE_DB_NAME value: "nacos_config" - name: MYSQL_SERVICE_USER valueFrom: secretKeyRef: name: nacos-db-secret key: username - name: MYSQL_SERVICE_PASSWORD valueFrom: secretKeyRef: name: nacos-db-secret key: password ports: - containerPort: 8848 name: client volumeMounts: - name: nacos-logs mountPath: /home/nacos/logs - name: nacos-plugins mountPath: /home/nacos/plugins volumes: - name: nacos-logs persistentVolumeClaim: claimName: nacos-logs-pvc - name: nacos-plugins persistentVolumeClaim: claimName: nacos-plugins-pvc --- apiVersion: v1 kind: Service metadata: name: nacos namespace: middleware spec: ports: - port: 8848 targetPort: 8848 protocol: TCP selector: app: nacos type: ClusterIP注意:
nacos-db-secret必须用kubectl create secret generic nacos-db-secret --from-literal=username=xxx --from-literal=password=xxx创建,绝不能明文写在 YAML 里。PVC 使用阿里云 NAS 或云盘均可,但nacos-logs-pvc必须设置accessModes: ["ReadWriteOnce"],否则 StatefulSet 启动失败——这是阿里云 ACK 控制台创建 PVC 时默认选错的高频坑。
2.2 SpringBoot2.5:用 ConfigMap 注入 bootstrap.yml,用 ServiceAccount 绑定 Nacos 权限
SpringBoot2.5 的bootstrap.yml必须在应用启动前加载,而 K8s 中 ConfigMap 挂载是异步的。常见错误是把bootstrap.yml直接写进镜像,导致无法动态更新 Nacos 地址。正确做法:将bootstrap.yml抽成 ConfigMap,挂载到/config目录,再通过-Dspring.config.location=file:/config/强制指定加载路径。同时,SpringBoot 需要访问 K8s API 获取服务发现信息(如 Nacos Service 的 ClusterIP),必须绑定 ServiceAccount:
# springboot-deploy.yaml apiVersion: apps/v1 kind: Deployment metadata: name: springboot-app namespace: default spec: replicas: 2 selector: matchLabels: app: springboot-app template: metadata: labels: app: springboot-app spec: serviceAccountName: nacos-access-sa # 关键! containers: - name: app image: registry.cn-hangzhou.aliyuncs.com/your-namespace/springboot25:v1.0 env: - name: SPRING_PROFILES_ACTIVE value: "prod" - name: NACOS_SERVER_ADDR value: "http://nacos.middleware.svc.cluster.local:8848" # 注意命名空间和 svc 名 volumeMounts: - name: config-volume mountPath: /config livenessProbe: httpGet: path: /actuator/health port: 8080 initialDelaySeconds: 60 periodSeconds: 30 readinessProbe: httpGet: path: /actuator/health port: 8080 initialDelaySeconds: 30 periodSeconds: 10 volumes: - name: config-volume configMap: name: springboot-bootstrap-cm --- apiVersion: v1 kind: ConfigMap metadata: name: springboot-bootstrap-cm namespace: default data: bootstrap.yml: | spring: application: name: user-service cloud: nacos: discovery: server-addr: ${NACOS_SERVER_ADDR} namespace: public config: server-addr: ${NACOS_SERVER_ADDR} file-extension: yaml group: DEFAULT_GROUP management: endpoints: web: exposure: include: health,info,prometheus提示:
NACOS_SERVER_ADDR使用nacos.middleware.svc.cluster.local是因为 Nacos 部署在middleware命名空间,K8s DNS 规则是<service>.<namespace>.svc.cluster.local。若填错,SpringBoot 启动日志会卡在Waiting for nacos server to start...,但不会报错——这是玄学级静默失败,必须kubectl logs -f deploy/springboot-app查看完整日志才能定位。
2.3 Vue2 + Nginx:用 InitContainer 下载构建产物,避免镜像臃肿
Vue2 项目构建产物(dist 目录)体积大(常 > 50MB),若直接 COPY 进 Nginx 镜像,每次前端发版都要重建镜像、推 Registry、滚动更新——CI/CD 流水线变慢,镜像仓库积压。更优解:Nginx 镜像保持最小化(alpine:latest),用 InitContainer 从阿里云 OSS 或私有 Harbor 拉取最新 dist.zip,解压到 emptyDir,再由主容器挂载。这样前端发版只需上传 zip,后端无需感知:
# nginx-deploy.yaml apiVersion: apps/v1 kind: Deployment metadata: name: nginx-vue2 namespace: default spec: replicas: 2 selector: matchLabels: app: nginx-vue2 template: metadata: labels: app: nginx-vue2 spec: initContainers: - name: download-dist image: curlimages/curl:8.4.0 command: ['sh', '-c'] args: - | curl -fSL https://your-bucket.oss-cn-hangzhou.aliyuncs.com/dist-v2.5.0.zip -o /dist/dist.zip && unzip -o /dist/dist.zip -d /dist/ volumeMounts: - name: dist-volume mountPath: /dist containers: - name: nginx image: nginx:alpine ports: - containerPort: 80 volumeMounts: - name: dist-volume mountPath: /usr/share/nginx/html - name: nginx-config mountPath: /etc/nginx/conf.d/default.conf subPath: nginx.conf volumes: - name: dist-volume emptyDir: {} - name: nginx-config configMap: name: nginx-vue2-cm --- apiVersion: v1 kind: ConfigMap metadata: name: nginx-vue2-cm namespace: default data: nginx.conf: | server { listen 80; server_name _; location / { root /usr/share/nginx/html; index index.html index.htm; try_files $uri $uri/ /index.html; # Vue Router history 模式必需 } location /api/ { proxy_pass http://springboot-app.default.svc.cluster.local:8080/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }血泪经验:
try_files $uri $uri/ /index.html这行必须加,否则 Vue2 的router.push('/user')在刷新时 404。另外,proxy_pass后的 URL 末尾斜杠/决定路径重写行为——加斜杠会剥离/api/前缀,不加则原样透传,务必和 SpringBoot 的@RequestMapping("/api/**")对齐。
3. 避坑:阿里云 K8s 上 Vue2+SpringBoot+Nacos 链路的 5 个静默崩溃点
K8s 部署不是「写完 YAML 就完事」,而是「每一步都有隐藏开关」。以下全是我在阿里云 ACK(v1.24.6)上真实翻车、抓包、查日志、对比文档才确认的坑,按发生频率排序:
3.1 现象:Nacos 页面能打开,但 SpringBoot 日志显示failed to request serverAddr
原因:Nacos Service 的clusterIP被设为None(Headless Service),而 SpringBoot 用http://nacos.middleware.svc.cluster.local:8848访问时,K8s DNS 返回的是 Pod IP 列表,但 Nacos Standalone 模式只监听0.0.0.0:8848,Pod IP 可能因网络插件(Terway)未就绪导致连接超时。
解决:删掉nacos-headlessService,只保留type: ClusterIP的nacosService,并确保selector标签与 StatefulSet 一致。验证命令:kubectl exec -it deploy/springboot-app -- curl -v http://nacos.middleware.svc.cluster.local:8848/nacos/v1/console/server/state。
3.2 现象:Vue2 页面加载 JS 报net::ERR_ABORTED 404,但 Nginx 容器内ls /usr/share/nginx/html显示文件存在
原因:InitContainer 下载 dist.zip 时,OSS URL 用了临时 token(STS Token),过期后 InitContainer 失败,但主容器仍启动(因restartPolicy: Always默认),导致挂载目录为空。K8s 不校验 InitContainer 成功才启动主容器。
解决:在 InitContainer 中加set -e并检查 zip 解压结果:[ -f /dist/index.html ] || exit 1。同时,OSS URL 改用永久域名(如https://bucket-name.oss-cn-hangzhou.aliyuncs.com/dist.zip),或用阿里云 RAM 角色授权。
3.3 现象:SpringBoot 注册到 Nacos 的 IP 是 Pod IP(如172.20.1.12),但前端调用http://nacos.middleware.svc.cluster.local:8848/nacos/v1/ns/instance/list?serviceName=user-service返回的实例 IP 无法被浏览器直连
原因:Nacos 默认用hostname做注册 IP,而 K8s Pod hostname 是随机字符串。必须强制 SpringBoot 发送真实可路由 IP。
解决:在 SpringBoot 的bootstrap.yml中加:
spring: cloud: nacos: discovery: ip: ${HOST_IP} # 关键! prefer-ip-address: true并在 Deployment 中注入:
env: - name: HOST_IP valueFrom: fieldRef: fieldPath: status.podIP3.4 现象:Nginx 反向代理/api/到 SpringBoot,返回502 Bad Gateway,但curl http://springboot-app:8080/actuator/health在 Nginx 容器内成功
原因:SpringBoot 的server.servlet.context-path设为/api,导致实际健康检查路径是/api/actuator/health,而 Nginx 的proxy_pass未适配。
解决:要么删掉 SpringBoot 的context-path(推荐),要么在 Nginx 配置中改为:
location /api/ { proxy_pass http://springboot-app.default.svc.cluster.local:8080/api/; }3.5 现象:Nacos 配置中心修改后,SpringBoot 不自动刷新@Value注解的值
原因:SpringBoot2.5 默认关闭配置自动刷新,需显式引入spring-cloud-starter-alibaba-nacos-config并开启refresh。
解决:在pom.xml中确认依赖版本匹配:
<dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> <version>2.2.9.RELEASE</version> <!-- 必须与 SpringBoot2.5 兼容 --> </dependency>并在bootstrap.yml中加:
spring: cloud: nacos: config: refresh-enabled: true4. 验证链路:四步诊断法,5 分钟定位任一环节断裂
上线不是终点,而是观测起点。我习惯用一套固定流程快速验证四层是否贯通,不依赖 Grafana 或 Prometheus(它们可能还没部署好):
4.1 第一步:确认 Nacos 服务可达性(绕过 DNS)
在任意 Pod 内执行:
kubectl run -i --tty --rm debug --image=busybox:1.35 --restart=Never -- sh # 进入后执行: nslookup nacos.middleware.svc.cluster.local # 应返回 ClusterIP wget -qO- http://<nacos-clusterip>:8848/nacos/v1/console/server/state | head -5 # 应返回 JSON若nslookup失败,查 CoreDNS 日志:kubectl logs -n kube-system deploy/coredns;若wget超时,查 Nacos Pod Event:kubectl describe pod -n middleware nacos-0。
4.2 第二步:确认 SpringBoot 已注册且配置加载成功
访问 Nacos 控制台 →「服务列表」→ 找到user-service→ 点「详情」→ 查「实例列表」IP 是否为 Pod IP;再点「配置管理」→「历史版本」→ 找到对应dataId(如user-service.yaml)→ 点「编辑」→ 修改内容 → 点「发布」。然后立刻在 SpringBoot Pod 日志中搜RefreshScope:
kubectl logs -l app=springboot-app | grep -i "refreshscope\|nacos config" # 正常应有:"[NacosConfigManager] Loading nacos data..." 和 "Refreshing scope 'refresh'"4.3 第三步:确认 Nginx 静态资源与反向代理双通
用kubectl port-forward service/nginx-vue2 8080:80本机访问http://localhost:8080,打开浏览器开发者工具:
- Network 标签页:
index.html、app.js状态码应为200; - 切换到
XHR:/api/user/info请求应返回200,Preview 显示 JSON; - 若 JS 加载失败,右键「检查元素」→ Console 标签页看
Failed to load resource的具体 URL,对照 Nginxlocation规则修正。
4.4 第四步:模拟真实用户路径,端到端压测
用hey工具(比 ab 更准)发起混合请求:
# 安装 hey:go install github.com/rakyll/hey@latest hey -z 30s -c 10 -m GET "http://<ingress-ip>/" hey -z 30s -c 10 -m POST -H "Content-Type: application/json" -d '{"id":1}' "http://<ingress-ip>/api/user/update"观察三处指标:
| 指标 | 健康阈值 | 定位方向 |
|---|---|---|
error rate | < 0.1% | Nginx 层(4xx/5xx)或 SpringBoot 层(500) |
latency p95 | < 800ms | SpringBoot 业务逻辑或 Nacos 配置拉取延迟 |
throughput | ≥ 120 req/s | 阿里云 SLB 后端服务器组权重或 Pod CPU limit 过低 |
我的习惯:每次发版后必跑这四步,用
kubectl get pods -A -o wide快速扫一眼所有 Pod 的STATUS和NODE,再用kubectl top pods -n default看 CPU/MEM 是否突增。曾经一次因 Nacos PVC 的storageClassName写错,导致 StatefulSet 卡在Pending,但kubectl get pods只显示0/1 Running,不看kubectl describe根本发现不了——这就是后悔药没地方买。
5. 进阶技巧:用阿里云 ARMS + SAE 实现 Vue2 前端错误监控与 SpringBoot 无侵入链路追踪
Vue2 项目上线后最头疼的不是接口 500,而是用户点击按钮没反应、白屏但控制台无报错——这种问题在 K8s 环境下更难定位。SpringBoot 也一样,/api/order/create接口慢,但日志里只有Started...和Completed...,中间耗时黑洞。这时候,与其自己埋点,不如用阿里云现成能力:
5.1 Vue2 前端错误捕获:ARMS 前端监控 SDK 零代码接入
ARMS(应用实时监控服务)提供@alicloud/alipay-arms-webSDK,专为 Vue2 设计。不用改业务代码,只需在main.js最顶部插入:
// main.js import { defineApp } from '@alicloud/alipay-arms-web' defineApp({ pid: 'your-arms-pid', // 在 ARMS 控制台创建应用获取 region: 'cn-hangzhou', // 与 ACK 集群同地域 enableSPA: true, vue: Vue, // Vue2 实例 })编译后部署,ARMS 控制台 →「前端监控」→「错误分析」即可看到:
- 按 URL 聚类的 JS 错误(如
Cannot read property 'name' of undefined); - 按设备/浏览器/地域分布的错误热力图;
- 点击错误条目 →「调用栈」→「关联请求」→ 自动关联到后端 SpringBoot 的 TraceID。
关键参数说明:
enableSPA: true启用单页应用路由监控;region必须填 ACK 集群所在地域,否则上报失败;pid是 ARMS 应用唯一标识,不是 AK/SK——这点新手常混淆。
5.2 SpringBoot 链路追踪:SAE(Serverless 应用引擎)自动注入 SkyWalking Agent
别自己折腾-javaagent参数。阿里云 SAE 支持「无侵入接入」:在 SAE 控制台创建应用时,勾选「启用分布式链路追踪」,选择 SkyWalking 协议,SAE 会自动为每个 Pod 注入 Agent 并配置skywalking-agent.jar。SpringBoot2.5 项目只需加依赖:
<dependency> <groupId>org.apache.skywalking</groupId> <artifactId>apm-toolkit-trace</artifactId> <version>8.15.0</version> <!-- 与 SAE 内置版本一致 --> </dependency>然后在 Controller 方法上加@Trace注解:
@RestController public class UserController { @GetMapping("/user/{id}") @Trace // 自动记录入口方法 public User getUser(@PathVariable Long id) { return userService.findById(id); } }ARMS 控制台 →「分布式链路追踪」→ 搜索/user/123,即可看到完整调用链:Nginx → SpringBoot Controller → UserService → Nacos Config Read → MySQL Query,每段耗时、状态码、SQL 语句(开启show-sql)全部可视。
5.3 故障自愈:用阿里云事件总线(EventBridge)联动告警与修复
当 ARMS 监控到 Vue2 错误率突增 > 5%,或 SAE 链路追踪发现某接口 P95 耗时 > 2s,可触发自动化响应:
- ARMS 告警 → 推送到阿里云事件总线;
- 事件总线规则匹配
source: arms+detail-type: FrontendError→ 调用函数计算(FC); - FC 脚本执行:
kubectl rollout restart deploy nginx-vue2(前端缓存问题)或kubectl scale deploy springboot-app --replicas=4(突发流量)。
这套组合拳,让我把平均故障恢复时间(MTTR)从 47 分钟压到 3 分钟内。现在团队的习惯是:任何线上问题,先看 ARMS 错误趋势图,再查 SAE 链路追踪,最后用事件总线一键回滚——而不是先登跳板机、再查日志、再猜原因。
希望帮到你。
本文还有配套的精品资源,点击获取