尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

K8s部署前后端分离微服务:Vue2+Nginx+SpringBoot+Nacos全流程实践

K8s部署前后端分离微服务:Vue2+Nginx+SpringBoot+Nacos全流程实践 简介面向云原生开发者和运维人员的阿里云k8s实战部署资源通过一套完整解决方案将Vue2nginxSpring Boot 2.5Nacos 2.0.3整合部署到Kubernetes集群帮助解决多组件网络与依赖配置繁琐的痛点适合具备一定k8s基础、希望快速上线微服务项目的中高级开发者。目前已有1337人学习。包内共16个文件压缩后34.69MB按前端应用、后端服务、Nacos中间件三大部分组织涵盖8个YAML资源清单StatefulSet、Deployment、Service、Ingress等、2个Shell一键部署脚本、2个Dockerfile、数据库初始化SQL、nginx配置、应用JAR包及前端静态资源压缩包。从数据库初始化、Nacos集群创建到前端发布、后端服务接入全流程配置清晰脚本化一键执行大幅降低手动拼接不同组件的学习成本读者可在真实项目中直接复用这套生产级k8s部署模板。1. 技术选型与整体架构设计1.1 服务拆分与握手逻辑拿到“阿里云K8s部署Vue2NginxSpringBoot2.5Nacos2.0.3”这个标题其实要做的本质是把一套前后端分离的微服务应用从单机部署搬到Kubernetes集群里跑起来。前端用Vue2打包成静态资源交给Nginx托管后端用SpringBoot2.5提供API接口服务注册和配置统一交给Nacos2.0.3管理最后全部跑在阿里云的容器服务ACK上。这套架构里最关键的不是某个单独组件的用法而是组件之间的通信链路。我落地的时候习惯先画清两条链路外部请求链路浏览器 → SLB阿里云负载均衡→ Ingress → Service → Nginx Pod → 静态资源 / 反向代理到后端Service服务注册链路SpringBoot Pod启动时向Nacos注册自身IP和端口 → Nacos维护服务列表 → 服务间通过Nacos发现彼此这里有个新手特别容易搞混的地方Nginx在这一套里到底承担什么角色它不是代替Ingress也不是代替网关而是两个层面的东西。Ingress负责把集群外部的流量接进来Nginx在Pod内部负责托管前端静态资源、把 /api 开头的请求反代到后端的Service地址。两者职责不同但可以配合使用也能用Ingress直接把前端流量和后端流量分流到不同Service跳过Pod内的Nginx反代。1.2 为什么这套组合是稳妥的选Vue2而不是Vue3选SpringBoot2.5而不是3.x选Nacos2.0.3而不是最新版很多人以为是保守实际上是企业项目迭代到一定阶段最现实的选择。Vue2生态成熟Element UI之类的组件库配合度高SpringBoot2.5在Java8环境下运行稳定部署成本低Nacos2.0.3在K8s里有专门的部署方案文档踩坑成本可控。从K8s的视角看这套组合还有几个实际好处。静态资源打包到Nginx镜像里天然符合不可变基础设施的理念——每次发版就是打一个新镜像而不是到服务器上去覆盖文件。SpringBoot2.5自带Actuator可以很方便地对接K8s的存活探针和就绪探针。Nacos作为注册中心和配置中心在集群版部署时正好发挥CP模式的价值多副本之间数据同步避免单点故障。提示如果团队刚开始接触K8s建议不要同时上服务网格Istio之类先把这套经典组合稳定跑起来再逐步引入更复杂的流量治理能力。2. 镜像构建与基础环境准备2.1 前端Vue2多阶段构建前端镜像构建是这套部署里最容易踩坑的环节。很多人直接拿一个Node镜像把代码放进去启动那肯定不行——运行时应只有Nginx和静态资源不要带上Node环境。正确做法是用Docker多阶段构建构建阶段用Node镜像跑npm install和npm run build运行阶段用Nginx镜像把dist目录拷贝进去。我用的前端Dockerfile大致是这样的# 构建阶段 FROM node:16-alpine AS build-stage WORKDIR /app COPY package*.json ./ RUN npm install --registryhttps://registry.npmmirror.com COPY . . RUN npm run build # 运行阶段 FROM nginx:1.24-alpine COPY --frombuild-stage /app/dist /usr/share/nginx/html COPY nginx.conf /etc/nginx/conf.d/default.conf EXPOSE 80 CMD [nginx, -g, daemon off;]这里有个小细节值得说npm install放在COPY全部代码之前单独执行利用了Docker层缓存。只要 package.json 没变后续构建就会直接复用这一层发布速度能快一大半。还有如果你的部署环境在阿里云Node版本建议固定到16Vue2项目在Node18以上构建偶尔会有OpenSSL兼容问题报错是error:0308010C:digital envelope routines::unsupported遇到这个别慌要么降Node版本要么在构建命令里加NODE_OPTIONS--openssl-legacy-provider。Nginx配置我单独拿出来说因为这直接决定了前端路由能不能正常工作server { listen 80; server_name _; root /usr/share/nginx/html; index index.html; location / { try_files $uri $uri/ /index.html; } location /api/ { proxy_pass http://backend-service:8080/api/; 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; } }try_files $uri $uri/ /index.html;这一行是Vue2 History路由模式即去掉#号的美观URL的命根子没有它刷新非首页路由会出现404。/api/反代指向的是K8s集群内部的Service名backend-service这里不需要写IPK8s的DNS解析会自动处理。2.2 后端SpringBoot2.5镜像实操后端镜像相对简单但要注意Java版本。SpringBoot2.5官方要求Java8以上实际我建议用Java 8或Java 11跑一是运行时更稳定二是阿里云的基础镜像体积可控。FROM maven:3.8-openjdk-8 AS build WORKDIR /app COPY pom.xml . RUN mvn dependency:go-offline -B COPY src ./src RUN mvn clean package -DskipTests FROM openjdk:8-jre-alpine ENV TZAsia/Shanghai RUN apk add --no-cache tzdata WORKDIR /app COPY --frombuild /app/target/app.jar app.jar EXPOSE 8080 ENTRYPOINT [java, -jar, -Xms512m, -Xmx512m, app.jar]dependency:go-offline这个命令平时用得不多但配合Docker缓存很好用。它会把所有依赖先拉一遍之后只要pom.xml不变就不会重复联网拉依赖。生产环境里如果对镜像体积有要求可以用JRE 11或多阶段裁减这里用 alpine 版本已经能控制得很小了。健康检查探针建议也在Dockerfile层面留好口子。SpringBoot2.5自带/actuator/health端点前提是pom里引入spring-boot-starter-actuator。这个端点会用在K8s的livenss和readiness探针上所以别关掉它。2.3 Nacos2.0.3镜像处理Nacos镜像比较特殊官方提供了nacos/nacos-server:v2.0.3但直接拿去用在K8s上会有一个坑默认用的是内嵌Derby数据库数据存在容器里Pod一重启配置就丢了。生产环境必须改成MySQL存储。我的做法是在K8s集群里额外部署一个MySQL实例或者直接使用云数据库RDS更省心然后给Nacos传入环境变量MYSQL_SERVICE_HOST、MYSQL_SERVICE_DB_NAME、MYSQL_SERVICE_USER、MYSQL_SERVICE_PASSWORD同时挂载初始化SQL脚本。Nacos官方的MySQL初始化脚本在conf/nacos-mysql.sql里可以先手工执行到数据库中。这里提醒一句Nacos2.0.x的grpc端口是6848服务端和9848客户端这个一定要在Service里暴露出来。很多人按1.x的经验只开了8848端口结果SpringBoot客户端连接时报错找不到服务。9848端口实际上是通过8848端口自动偏移出来的如果设置了NACOS_SERVER_PORT自定义端口grpc端口也会跟着偏移。3. K8s资源配置与部署详解3.1 命名空间与ConfigMap规划正式部署之前先规划好命名空间。这一步看起来很简单但生产环境里能帮你省掉无数麻烦。我习惯按环境划分dev、test、prod或者按业务线划分。下面以app-namespace为例。apiVersion: v1 kind: Namespace metadata: name: app-namespace后端应用的配置建议放到ConfigMap里而不是写死在镜像中。这样环境变量、数据库连接地址、Nacos地址等都可以在部署时动态注入镜像本身不用变。apiVersion: v1 kind: ConfigMap metadata: name: backend-config namespace: app-namespace data: SPRING_PROFILES_ACTIVE: prod SPRING_DATASOURCE_URL: jdbc:mysql://mysql-service:3306/app_db?useUnicodetruecharacterEncodingutf8useSSLfalse SPRING_DATASOURCE_USERNAME: app_user SPRING_DATASOURCE_PASSWORD: yourpassword SPRING_CLOUD_NACOS_DISCOVERY_SERVER_ADDR: nacos-service:8848 SPRING_CLOUD_NACOS_CONFIG_SERVER_ADDR: nacos-service:8848注意ConfigMap里的密码是明文真正生产环境建议用K8s的Secret。这里为了演示方便就不绕弯子了。3.2 Deployment、Service与探针配置下面是一个相对完整的后端Deployment配置重点在于探针和资源限制apiVersion: apps/v1 kind: Deployment metadata: name: backend-deployment namespace: app-namespace spec: replicas: 2 selector: matchLabels: app: backend template: metadata: labels: app: backend spec: containers: - name: backend image: registry.cn-hangzhou.aliyuncs.com/yournamespace/backend:1.0.0 ports: - containerPort: 8080 envFrom: - configMapRef: name: backend-config readinessProbe: httpGet: path: /actuator/health port: 8080 initialDelaySeconds: 30 periodSeconds: 10 timeoutSeconds: 5 failureThreshold: 3 livenessProbe: httpGet: path: /actuator/health port: 8080 initialDelaySeconds: 60 periodSeconds: 20 timeoutSeconds: 5 failureThreshold: 3 resources: requests: cpu: 250m memory: 512Mi limits: cpu: 1 memory: 1Gi imagePullPolicy: IfNotPresentreadinessProbe和livenessProbe的差别要搞清楚。readiness决定流量要不要打进来——服务还没准备就绪之前提前就绪会有一段时间50x报错liveness决定Pod要不要重启——服务挂死时探活失败K8s会杀掉重启。SpringBoot启动本身比较慢尤其还要注册到Nacos所以initialDelaySeconds别设太小我见过太多人卡在这里。Service的配置相对简单apiVersion: v1 kind: Service metadata: name: backend-service namespace: app-namespace spec: selector: app: backend ports: - port: 8080 targetPort: 8080前端Nginx的Deployment和Service逻辑完全一样区别在于镜像不同、端口是80、探针路径改成/或/index.html。前端Pod一般不需要外部直接访问通过Ingress暴露即可。3.3 Nacos2.0.3的K8s部署要点Nacos在K8s里部署我推荐用StatefulSet而不是Deployment因为Nacos节点之间需要稳定的网络标识和存储。下面是精简版配置apiVersion: apps/v1 kind: StatefulSet metadata: name: nacos namespace: app-namespace spec: serviceName: nacos-service replicas: 2 selector: matchLabels: app: nacos template: metadata: labels: app: nacos spec: containers: - name: nacos image: nacos/nacos-server:v2.0.3 ports: - containerPort: 8848 - containerPort: 9848 env: - name: MODE value: cluster - name: NACOS_SERVERS value: nacos-0.nacos-service.app-namespace.svc.cluster.local:8848,nacos-1.nacos-service.app-namespace.svc.cluster.local:8848 - name: MYSQL_SERVICE_HOST value: mysql-service - name: MYSQL_SERVICE_DB_NAME value: nacos_config - name: MYSQL_SERVICE_USER value: nacos - name: MYSQL_SERVICE_PASSWORD value: nacos123 volumeMounts: - name: data mountPath: /home/nacos/data volumeClaimTemplates: - metadata: name: data spec: accessModes: [ReadWriteOnce] resources: requests: storage: 10Gi对应的Headless ServiceapiVersion: v1 kind: Service metadata: name: nacos-service namespace: app-namespace spec: clusterIP: None selector: app: nacos ports: - name: http port: 8848 targetPort: 8848 - name: grpc port: 9848 targetPort: 9848StatefulSet配Headless Service是一套固定组合利用nacos-0.nacos-service这种稳定的DNS名称互相通信Nacos集群模式下节点之间就是这么发现彼此的。3.4 Ingress暴露服务前端和后端都部署好之后需要一个统一的入口把流量接进来。我用的Ingress配置apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: app-ingress namespace: app-namespace annotations: nginx.ingress.kubernetes.io/proxy-body-size: 50m spec: rules: - host: app.example.com http: paths: - path: / pathType: Prefix backend: service: name: frontend-service port: number: 80 - path: /api pathType: Prefix backend: service: name: backend-service port: number: 8080这里把/api单独分流到后端Service这样前端的Nginx配置里可以不用配置反代由Ingress层直接完成路由。两种方式都行看团队习惯。如果后端接口请求还涉及文件上传务必设置proxy-body-size默认是1m传个图片就会报413。阿里云ACK集群默认集成了Ingress Controller会自动创建公网SLB。如果你的集群是自己建的需要先部署Nginx Ingress Controller否则上面这个Ingress资源创建了也不生效。4. 常见问题与排查技巧实录4.1 服务注册不上Nacos这是SpringBoot服务启动后最常遇到的问题。服务起来半天了Nacos控制台里看不到服务列表或者服务列表里有但实例信息为空。先查三个地方Nacos地址通不通进Pod里执行curl nacos-service:8848/nacos/v1/console/health/readiness能看到{code:200}说明网络通。9848端口是不是被挡了Nacos2.0客户端发现服务用的是grpc走的是9848端口。如果用Service暴露时只映射了8848客户端连不上grpc就会报Client not connected, current status:STARTING。命名空间对不对SpringBoot配置里如果指定了namespace但Nacos上没有建对应ID的命名空间服务会注册到一个不存在的命名空间里控制台自然看不到。建议先不指定namespace跑通最简单的链路然后再加命名空间隔离。4.2 前端刷新404我见过几十次的问题几乎每次都有人问。Vue2项目在Nginx里部署后从首页跳转别的页面完全正常但直接刷新某个子路由就出现404。原因很简单Nginx尝试去找一个不存在的物理路径比如/about但前端项目里根本没有about.html。解决办法就是那行try_files $uri $uri/ /index.html;。如果改完Nginx配置还是不生效注意浏览器缓存强制刷新一下再试。另外如果你把前端资源放CDN上也要保证CDN回源时支持这条规则。4.3 Nacos重启后配置丢失如果没接MySQLNacos0.2.0.3用的是内嵌Derby配置数据存在容器里。Pod被重新调度到其他节点或者副本缩容后再扩容数据就没了一大半。这个问题在生产环境尤其严重配置中心里的数据是团队资产不能用临时存储保存。接到MySQL后还可能出现连不上数据库的情况。我用云数据库RDS时遇到过白名单问题——RDS默认只允许指定的IP访问K8s集群的出口IP需要提前加到白名单里。自己搭MySQL的话注意建好数据库和账号权限要授权到位CREATE DATABASE nacos_config DEFAULT CHARACTER SET utf8mb4; CREATE USER nacos% IDENTIFIED BY nacos123; GRANT ALL PRIVILEGES ON nacos_config.* TO nacos%; FLUSH PRIVILEGES;4.4 镜像拉不下来阿里云ACK集群的节点在拉取个人或第三方镜像仓库时有时候会因为网络限制拉不下来。碰到ErrImagePull或ImagePullBackOff先kubectl describe pod看具体报错。如果是manifest unknown多半是镜像tag写错了到镜像仓库确认一下。如果是connection refused或timeout就得检查是否需要配置私有仓库的Secret。阿里云ACR容器镜像服务有公网地址和VPC地址之分。建议ACK集群内拉镜像用VPC地址速度快还稳定。跨账号拉取需要配置imagePullSecrets可以预先创建一个docker-registry类型的Secret关联到ServiceAccount。4.5 资源限制导致OOMKilledSpringBoot应用如果设置了limits内存太小会频繁触发OOMKilledPod反复重启看起来就像死循环。JVM默认会根据宿主机可用内存自动调整堆大小但如果容器限制为512MJVM可能把它认成宿主机的内存堆扩容直接把容器冲爆。我一般在Dockerfile或启动命令里显式设置-Xms512m -Xmx512m同时给K8s的limits留出堆外内存的余量比如堆512M就用1Gi。这样JVM只会在512M的堆范围内运行不会越界去抢K8s给它限定的内存。4.6 常用排查命令速查部署过程中会遇到各种奇怪问题这套排查命令我用了很多年基本够用场景命令查看Pod状态kubectl get pods -n app-namespace查看Pod日志kubectl logs -f pod-name -n app-namespace查看Pod详细事件kubectl describe pod pod-name -n app-namespace进入Pod调试kubectl exec -it pod-name -n app-namespace -- /bin/sh测试集群内DNS解析kubectl run curl-test --imagecurlimages/curl -it --rm -- sh查看Service端点kubectl get endpoints -n app-namespace修改Deployment镜像触发滚动更新kubectl set image deployment/backend-deployment backendxxx:v2 -n app-namespace查看滚动更新状态kubectl rollout status deployment/backend-deployment -n app-namespace注意kubectl exec进Pod后基础镜像里不一定有curl命令。建议在调试用的临时Pod里装一个完整工具集或者直接跑一个busybox镜像来测试网络连通性。5. 结合实际部署过程的一些补充5.1 阿里云ACK集群的创建与镜像仓库在阿里云上实操这套方案时集群创建本身没什么难度。在容器服务控制台里创建ACK集群选择专有版还是托管版我建议一般业务选托管版Master节点不用自己维护。创建时选好VPC、交换机节点规格按业务量规划至少2个节点起步。镜像仓库选择上建议开通阿里云容器镜像服务ACR个人版免费额度基本够用把前端、后端、Nacos镜像都推到这里。推镜像之前先创建命名空间和镜像仓库然后用控制台给的docker命令操作就行。需要注意的是ACK集群在工作节点上拉取ACR镜像时如果仓库是私有的需要配置Secret。不过一个技巧是把ACR的仓库属性设为私有后可以用RAM用户授权的方式让ACK自动获取拉取权限流程上会简单不少。5.2 持续集成与更新的思路部署这套组合如果每次发版都手动打镜像、手动更新Deployment效率太低也容易出错。我建议至少做到“流水线化”代码推到Git仓库触发构建构建产物打到Docker镜像并推送到仓库然后自动更新K8s的Deployment镜像版本。在K8s层面更新镜像的方式有很多种最简单的是文末命令里的kubectl set image但更推荐把镜像版本记录在Deployment的label或annotation中用kubectl apply整体更新同时配合rollout status持续查看滚动节奏。这个过程中因为后端是双副本Deployment滚动更新时天然能做到“先起一个新的、停一个旧的”用户无感。5.3 备份、监控与弹性补强K8s部署这套组合后最让人放心的是有一个无形的“运维底线”探针保证进程不会死Service保证流量不中断滚动更新保证发版不停机。但监控还是要主动做起来至少要有三样东西集群监控云监控或PrometheusGrafana、日志采集日志服务或ELK、JVM监控对SpringBoot来说很有价值。我自己实际部署过这套组合后最大的体会是不要把K8s当成一个好玩的玩具它是一个要深度适配到团队工作流里的基础设施。从本地能跑到K8s里能跑这中间隔着很多细致的检查镜像、网络、存储、内存限制每一项都要亲自踩一遍才知道问题在哪。如果之前只做过单机Docker部署建议在K8s上跑这套组合时先把Deployment、Service、ConfigMap、StatefulSet这几个资源吃透尤其是StatefulSet与Deployment的差异这在Nacos这类有状态组件上至关重要。Nacos2.0.3配合MySQL落库配合Headless Service的稳定网络标识配合滚动更新策略一套有状态的基础服务就能跑得很稳。前端Nginx镜像做好多阶段构建后端JVM内存边界控制好再配合Ingress把两条流量路由分开整条链路在生产环境跑上几个月都不会有太多动静。真到有问题的时候按照前面的排查清单一步步来大概率能很快找到症结所在。本文还有配套的精品资源点击获取
返回列表