阿里云K8s部署Vue2+SpringBoot2.5+Nacos2.0.3全流程 简介这份资源面向需要在阿里云Kubernetes集群上落地前后端分离项目的开发与运维人员提供一套可直接参考的部署方案覆盖Vue2前端、Nginx、SpringBoot2.5后端与Nacos2.0.3注册配置中心的完整编排。包内共16个文件以8个yaml清单为核心配合2个sh部署脚本、2个Dockerfile、1个SQL初始化脚本、1个Nginx配置文件以及1个jar与1个gz前端产物压缩包约34.69MB结构紧凑、开箱即用。内容围绕Nacos的StatefulSet、Headless Service、Ingress与MySQL配置展开同时给出前端镜像构建与一键部署脚本便于读者理解服务发现、配置管理与网关暴露的串联方式。已有1341人学习下载适合希望快速搭建K8s微服务实验环境、对照排查部署问题的中高级开发者参考。1. 从一台跳板机到整套环境为什么我坚持在阿里云 k8s 上重装一遍 vue2nginxspringboot2.5nacos2.0.3去年帮一个朋友救火他们那套前后端分离的项目原本跑在几台 ECS 上vue2 打包出来的静态资源塞在 nginx 的 html 目录里springboot2.5 打成 jar 包用 nohup 挂着nacos2.0.3 单机模式跑在另一台机器上。平时没事一到发版就鸡飞狗跳nginx 配置改错一个分号整个前端 502nacos 那台机器磁盘满了注册中心直接失联后端服务全部掉线。后来我建议他们干脆整体搬到阿里云 k8s 上用 Deployment 管后端、用 ConfigMap 管 nginx 配置、用 StatefulSet 跑 nacos。这套组合听起来组件多但真装下来最耗时间的不是写 YAML而是版本对齐和网络连通性排查。这篇笔记就把我从零搭这套环境的完整过程拆开包括我踩过的坑和验证跑通的方法适合正在把传统部署往 k8s 迁移的开发者照着走一遍。2. 装前把版本和环境列清楚k8s 1.24、nacos2.0.3 与 springboot2.5 的兼容账2.1 组件版本对照与选型理由这套环境里最容易翻车的地方不是安装命令而是版本之间的隐性依赖。nacos2.0.3 要求 JDK 8 以上springboot2.5 默认带的是 spring-cloud-alibaba 2021.1 对应的 nacos-client 2.0.3如果你把 nacos-server 升到 2.2 以上客户端不跟着升gRPC 端口会连不上。k8s 这边1.24 版本移除了 dockershim如果你还在用 docker 作为容器运行时需要额外装 cri-dockerd我一般直接换成 containerd省掉这层麻烦。组件版本关键依赖备注阿里云 ACK1.24.6containerd 1.6托管版省去 master 维护nacos2.0.3JDK 8 / MySQL 5.7必须用 MySQL 存配置springboot2.5.xJDK 8打包成可执行 jarvue2.xNode 14/16构建静态文件nginx1.21无用 ConfigMap 挂配置选 nacos2.0.3 而不是更新的版本是因为 springboot2.5 配套的 spring-cloud-alibaba 2021.1 里锁定的就是 2.0.3强行升 server 端会出现「服务注册上了但配置拉不到」的玄学问题。我在这上面浪费过一整个下午后来老老实实按依赖树里的版本走。2.2 阿里云 ACK 集群创建与 kubectl 本地配置在阿里云控制台创建 ACK 集群时网络插件选 Terway因为后面 nacos 集群节点之间需要固定 IP 通信Terway 支持给 Pod 分配独立 ENI。节点规格我选的是 4 核 8G 两台nacos 和业务服务分开调度。创建完成后在集群信息页下载 kubeconfig放到本地~/.kube/config。# 把阿里云下载的 kubeconfig 合并到本地默认配置 mkdir -p ~/.kube cp ~/Downloads/ack-kubeconfig.yaml ~/.kube/config # 验证连通性能看到节点列表说明配置正确 kubectl get nodes -o wide这里有个细节阿里云下载的 kubeconfig 里 server 地址是内网访问地址如果你本地不在同一个 VPC需要改成公网访问地址或者在集群里开一个 EIP。我一般是在集群的「基本信息」里找到公网访问入口把 server 那行替换掉。kubectl get nodes返回 Ready 状态后本地环境就算通了。2.3 命名空间与镜像仓库准备不要把所有东西都塞在 default 命名空间里后面排查问题会乱。我习惯按组件拆开nacos放注册中心web放前端app放后端服务。# 创建三个命名空间隔离不同层的资源 kubectl create namespace nacos kubectl create namespace web kubectl create namespace app # 查看命名空间列表确认创建成功 kubectl get ns镜像仓库用阿里云 ACR个人版免费但有限额企业版按量付费。我一般给每个命名空间配一个对应的 ACR 命名空间镜像 tag 用日期加 commit 短 hash比如app-backend:20240521-a1b2c3回滚的时候能精确找到版本。在 ACK 里拉取 ACR 镜像需要配置免密插件控制台里点一下「配置免密组件」就行不用手动创建 secret。3. 部署 nacos2.0.3StatefulSet 加 MySQL 存储的完整配置3.1 为什么 nacos 必须用 MySQL 而不是内嵌 Derbynacos 单机模式默认用内嵌 Derby 数据库数据存在容器本地Pod 一重启配置全丢。生产环境必须换成 MySQL而且要用 StatefulSet 部署集群模式至少三个节点否则注册中心本身就成了单点。我见过有人用 Deployment 跑 nacos 集群结果三个 Pod 的 IP 一直变节点之间选主失败控制台一直显示「节点列表为空」。StatefulSet 给每个 Pod 固定的 DNS 名称nacos 集群配置里直接用这个名称互相发现。3.2 初始化 MySQL 数据库与导入 nacos 表结构先在阿里云 RDS 上创建一个数据库nacos_config字符集用 utf8mb4。然后从 nacos2.0.3 的安装包里找到mysql-schema.sql导入进去。-- 创建 nacos 专用数据库字符集必须 utf8mb4否则配置内容有中文会乱码 CREATE DATABASE nacos_config DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; -- 创建专用账号不要用 root权限只给这个库 CREATE USER nacos% IDENTIFIED BY Nacos2024; GRANT ALL PRIVILEGES ON nacos_config.* TO nacos%; FLUSH PRIVILEGES;导入表结构后检查config_info和users两张表是否存在。users表里默认有一条 nacos/nacos 的记录第一次登录后必须改密码否则控制台会一直弹安全提示。3.3 StatefulSet 编排文件与关键参数说明下面是我实际用的 StatefulSet 片段重点看环境变量和存储挂载。apiVersion: apps/v1 kind: StatefulSet metadata: name: nacos namespace: nacos spec: serviceName: nacos-headless replicas: 3 selector: matchLabels: app: nacos template: metadata: labels: app: nacos spec: containers: - name: nacos image: nacos/nacos-server:v2.0.3 env: - name: MODE value: cluster # 集群模式单机模式写 standalone - name: SPRING_DATASOURCE_PLATFORM value: mysql # 启用外部 MySQL 存储 - name: MYSQL_SERVICE_HOST value: rm-xxxx.mysql.rds.aliyuncs.com - name: MYSQL_SERVICE_DB_NAME value: nacos_config - name: MYSQL_SERVICE_USER value: nacos - name: MYSQL_SERVICE_PASSWORD value: Nacos2024 - name: NACOS_SERVERS value: nacos-0.nacos-headless.nacos.svc.cluster.local:8848 nacos-1.nacos-headless.nacos.svc.cluster.local:8848 nacos-2.nacos-headless.nacos.svc.cluster.local:8848 ports: - containerPort: 8848 name: http - containerPort: 9848 name: grpcNACOS_SERVERS这个变量是集群发现的关键必须用 headless service 的完整域名。9848 端口是 nacos2.0 新增的 gRPC 端口客户端通过它做长连接如果 Service 里没暴露这个端口服务能注册但心跳会断表现为「服务列表里实例时有时无」。我一开始只暴露了 8848排查了半天才发现是 gRPC 端口没开。3.4 创建 headless service 与 NodePort 访问入口StatefulSet 需要配合 headless service 才能给每个 Pod 稳定 DNS。apiVersion: v1 kind: Service metadata: name: nacos-headless namespace: nacos spec: clusterIP: None # headless service不分配 ClusterIP selector: app: nacos ports: - name: http port: 8848 - name: grpc port: 9848 --- apiVersion: v1 kind: Service metadata: name: nacos-nodeport namespace: nacos spec: type: NodePort selector: app: nacos ports: - name: http port: 8848 nodePort: 30848 # 固定 NodePort方便控制台访问应用这两个 YAML 后用kubectl get pods -n nacos看三个 Pod 是否都 Running。然后浏览器访问http://节点公网IP:30848/nacos默认账号 nacos/nacos。登录后第一件事是改密码第二件事是去「集群管理」里确认三个节点都在列表里如果只显示一个检查NACOS_SERVERS变量有没有写错。4. 部署 springboot2.5 后端镜像构建、ConfigMap 与健康检查4.1 打包 jar 与编写 Dockerfilespringboot2.5 项目用 maven 打包注意跳过测试否则 CI 里跑测试用例可能因为连不上 nacos 而失败。# 在项目根目录执行-DskipTests 跳过测试-U 强制更新快照依赖 mvn clean package -DskipTests -U # 打包完成后 jar 在 target 目录下确认文件名 ls target/*.jarDockerfile 我习惯用分层构建把依赖 jar 和业务代码分开这样改代码后重新构建镜像时依赖层能复用缓存。FROM openjdk:8-jre-slim WORKDIR /app # 先拷贝依赖目录利用 Docker 缓存层 COPY target/lib/ ./lib/ COPY target/*.jar app.jar # 启动参数里指定 nacos 地址用环境变量注入 ENTRYPOINT [java, -jar, app.jar, --spring.cloud.nacos.discovery.server-addr${NACOS_ADDR}, --spring.cloud.nacos.config.server-addr${NACOS_ADDR}]${NACOS_ADDR}在 k8s 的 Deployment 里通过环境变量传入值是nacos-headless.nacos.svc.cluster.local:8848。这样后端服务启动时就能自动注册到 nacos不需要在代码里写死地址。4.2 Deployment 编排与 nacos 地址注入apiVersion: apps/v1 kind: Deployment metadata: name: backend namespace: app spec: replicas: 2 selector: matchLabels: app: backend template: metadata: labels: app: backend spec: containers: - name: backend image: registry.cn-hangzhou.aliyuncs.com/your-ns/backend:20240521-a1b2c3 env: - name: NACOS_ADDR value: nacos-headless.nacos.svc.cluster.local:8848 - name: SPRING_PROFILES_ACTIVE value: k8s ports: - containerPort: 8080 readinessProbe: # 就绪探针确保服务注册后再接流量 httpGet: path: /actuator/health port: 8080 initialDelaySeconds: 30 periodSeconds: 10 livenessProbe: # 存活探针卡死时自动重启 httpGet: path: /actuator/health port: 8080 initialDelaySeconds: 60 periodSeconds: 20readinessProbe的initialDelaySeconds给 30 秒是因为 springboot 启动加上向 nacos 注册需要时间给太短会导致 Pod 还没注册好就被判定为未就绪反复重启。livenessProbe的延迟给 60 秒避免启动过程中被误杀。这两个探针的路径依赖spring-boot-starter-actuator记得在 pom 里加上。4.3 用 ConfigMap 管理 application-k8s.yml不要把配置文件打进镜像里改一个参数就要重新构建。用 ConfigMap 挂载到容器里改完重启 Pod 就生效。# 从本地文件创建 ConfigMap文件名就是挂载后的文件名 kubectl create configmap backend-config --from-fileapplication-k8s.yml -n app然后在 Deployment 里挂载volumeMounts: - name: config mountPath: /app/config volumes: - name: config configMap: name: backend-config启动参数里加上--spring.config.additional-location/app/config/application-k8s.ymlspringboot 会优先读这个路径的配置。这样改数据库连接、改日志级别只需要kubectl edit configmap然后滚动重启不用碰镜像。5. 部署 vue2nginx静态资源构建与反向代理配置5.1 vue2 构建产物与 nginx 镜像打包vue2 项目用npm run build生成 dist 目录然后塞进 nginx 镜像。# 安装依赖建议用 npm ci 而不是 npm install保证锁文件一致 npm ci # 构建生产环境包生成 dist 目录 npm run build # 确认 dist 里有 index.html 和 static 目录 ls dist/Dockerfile 很简单把 dist 拷到 nginx 的 html 目录再替换默认配置。FROM nginx:1.21-alpine # 删除默认配置避免干扰 RUN rm -f /etc/nginx/conf.d/default.conf COPY dist/ /usr/share/nginx/html/ COPY nginx.conf /etc/nginx/conf.d/app.conf EXPOSE 805.2 nginx.conf 反向代理到后端 Servicenginx 配置里最关键的是proxy_pass指向后端 Service 的 ClusterIP 域名。server { listen 80; server_name _; root /usr/share/nginx/html; index index.html; # vue-router 的 history 模式找不到文件时回退到 index.html location / { try_files $uri $uri/ /index.html; } # 后端接口代理/api 开头的请求转发到 backend service location /api/ { proxy_pass http://backend.app.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_connect_timeout 30s; proxy_read_timeout 60s; } }proxy_pass末尾的斜杠要注意http://backend.app.svc.cluster.local:8080/带斜杠表示把/api/替换掉后端收到的路径是/user/list而不是/api/user/list。如果后端接口本身带/api前缀就把末尾斜杠去掉。这个细节我踩过坑前端一直报 404查了半天才发现是斜杠的问题。5.3 Service 与 Ingress 暴露前端入口前端 Service 用 ClusterIP通过 Ingress 暴露到公网。阿里云 ACK 的 Ingress 需要先装 nginx-ingress-controller 组件控制台里一键安装。apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: web-ingress namespace: web annotations: nginx.ingress.kubernetes.io/rewrite-target: / spec: ingressClassName: nginx rules: - host: app.example.com http: paths: - path: / pathType: Prefix backend: service: name: web-service port: number: 80把域名解析到 Ingress 的公网 IP 后访问http://app.example.com就能看到前端页面。如果页面能打开但接口 502检查后端 Pod 是否 Ready以及 nginx 配置里的 Service 域名是否写对。6. 避坑与排查这套环境里我踩过的五个坑6.1 nacos 控制台显示节点为空现象三个 nacos Pod 都 Running但控制台「集群管理」里只显示一个节点或者节点列表为空。原因通常是NACOS_SERVERS环境变量里的域名解析不到或者 9848 端口没在 headless service 里暴露。解决进入任意一个 nacos Pod执行nslookup nacos-0.nacos-headless.nacos.svc.cluster.local确认能解析到 IP然后检查 headless service 的 ports 里有没有 9848。6.2 springboot 服务注册上了但配置拉不到现象nacos 服务列表里能看到后端实例但日志里报config not found或者启动时卡在拉取配置。原因是 nacos2.0 的 gRPC 端口 9848 在客户端侧被防火墙拦截或者客户端版本和服务端不匹配。解决确认 spring-cloud-alibaba 版本是 2021.1对应的 nacos-client 是 2.0.3然后在 Pod 里telnet nacos-headless.nacos.svc.cluster.local 9848测试连通性。6.3 nginx 代理后端接口 404现象前端页面正常但调接口返回 404。原因是proxy_pass末尾斜杠导致路径被截断或重复。解决根据后端接口的实际路径决定加不加斜杠。如果后端接口是/user/listnginx 里location /api/配proxy_pass http://backend:8080/如果后端接口是/api/user/list则配proxy_pass http://backend:8080不带斜杠。6.4 Pod 一直 CrashLoopBackOff现象后端 Pod 启动后几秒就退出反复重启。原因可能是livenessProbe的initialDelaySeconds太短springboot 还没启动完就被判定为失败。解决把initialDelaySeconds调到 60 以上failureThreshold调到 5给足启动时间。另外检查readinessProbe的路径/actuator/health是否在 actuator 里暴露了。6.5 镜像拉取失败 ImagePullBackOff现象Pod 状态是ImagePullBackOffdescribe 里报pull access denied。原因是 ACK 节点没有 ACR 的免密权限或者镜像 tag 写错。解决在 ACK 控制台确认「配置免密组件」已安装并且该组件绑定的角色有 ACR 的拉取权限。如果是跨命名空间的 ACR检查镜像地址是否完整。7. 装完怎么验证跑通从注册中心到前端接口的完整链路检查装完不代表跑通我一般会按「注册中心 → 后端 → 前端 → 全链路」的顺序走一遍验证。第一步登录 nacos 控制台在「服务管理」里确认后端服务的实例数和 Pod 数量一致并且健康状态都是true。如果实例数是 0说明后端没注册上回去检查NACOS_ADDR环境变量和网络连通性。第二步在集群里起一个临时 Pod用 curl 直接调后端接口绕过 nginx 和 Ingress确认后端本身是通的。# 起一个临时 curl 容器直接访问后端 Service kubectl run curl-test --imagecurlimages/curl -n app --rm -it -- sh # 在容器里执行确认后端返回 200 curl -v http://backend.app.svc.cluster.local:8080/actuator/health第三步从前端域名访问打开浏览器开发者工具看接口请求的响应码和响应体。如果接口返回 502检查 nginx 的proxy_pass地址如果返回 404检查路径重写规则如果跨域报错检查 nginx 有没有加Access-Control-Allow-Origin头。第四步模拟一次发版改一下 ConfigMap 里的日志级别滚动重启后端 Deployment观察 nacos 控制台里实例是否先下线再上线前端接口是否短暂 502 后恢复。这个过程中如果出现长时间不可用说明readinessProbe配置有问题需要调整initialDelaySeconds和periodSeconds。我自己的习惯是每次整套环境搭完后都会用kubectl get pods -A -o wide把所有 Pod 的 IP、节点、状态过一遍再对着 nacos 控制台的服务列表数一遍实例数两个数字对不上就不算完。这套流程走下来后面无论换节点还是扩副本心里都有底。希望帮到你。本文还有配套的精品资源点击获取