K3S轻量部署SpringBoot+Vue:云原生架构实战指南
发布时间:2026/8/26 10:53:03 作者:尧图编辑部 阅读量:1,286

1. 项目概述为什么选择K3S来部署SpringBootVue最近在折腾一个内部用的数据看板后端是SpringBoot前端是Vue 3。项目不大但涉及到几个微服务模块本地开发调试没问题一到部署环节就头疼服务器资源有限用完整的K8s集群感觉杀鸡用牛刀维护成本太高用传统的Docker Compose编排服务发现、负载均衡、配置管理又得自己手动拼凑不够优雅。折腾了一圈最后把目光锁定在了K3S上。K3S可以理解为Kubernetes的“轻量版”由Rancher实验室出品它把K8s的很多外部依赖比如etcd都打包或替换成了更轻量的组件安装包就几十兆对资源的要求极低单节点也能跑起来但该有的核心功能一个不少Pod部署、Service暴露、Ingress路由、ConfigMap/Secret配置管理全都能支持。对于SpringBootVue这种经典的前后端分离项目K3S提供了一个近乎完美的部署平台。后端Java服务可以打包成Jar再通过Docker镜像运行也可以直接用构建好的镜像前端Vue项目通过npm run build生成静态文件用Nginx镜像托管。K3S负责把这两者组织起来后端服务可以水平扩展前端通过Ingress统一对外提供访问入口配置和密钥也能集中管理安全性比直接扔服务器上高得多。简单说如果你正在寻找一种比裸机部署更可靠、比传统虚拟机更灵活、比完整K8s更轻量的应用部署方案那么用K3S来部署SpringBootVue项目是一个非常务实且高效的选择。它特别适合中小型团队、个人开发者、边缘计算场景或者只是想学习云原生部署的伙计们。2. 整体架构与核心组件选型在动手之前我们先得把部署的蓝图画清楚。一个典型的SpringBootVue应用在K3S里的部署架构主要包含以下几个核心部分2.1 后端服务SpringBootSpringBoot应用是我们的业务核心。部署时我们通常将其打包成一个可执行的Jar文件然后基于OpenJDK或Alpine JDK等基础镜像制作成Docker镜像。在K3S中这个镜像会以Pod的形式运行。为了确保稳定性和可扩展性我们至少需要部署2个副本Replica。服务之间的通信、以及前端对后端的API调用都通过K3S的Service来实现内部负载均衡。2.2 前端服务VueVue项目经过构建后生成的是纯粹的HTML、CSS、JavaScript静态资源。我们不需要Node.js运行时只需要一个Web服务器来托管这些文件。Nginx是最常见的选择它的镜像小巧配置灵活。我们将构建好的dist目录复制到Nginx镜像内并配置好路由规则将所有API请求代理到后端的SpringBoot Service。2.3 配置与路由中枢K3S原生对象这是K3S发挥威力的地方我们主要用到三类资源ConfigMap Secret用来管理配置。例如SpringBoot的application.yml中数据库连接信息、Vue项目中可能用到的环境变量都可以从代码中抽离通过ConfigMap注入。而数据库密码、API密钥等敏感信息则必须使用Secret来存储以密文形式挂载到Pod中。Service定义一组Pod的访问策略。它为后端的SpringBoot Pods提供一个稳定的内部域名比如springboot-svc和端口前端Nginx通过这个内部域名就能访问到后端无需关心后端Pod的具体IP地址。Ingress管理外部访问。它是集群的入口我们通过定义Ingress规则将不同的HTTP请求路径例如/api/*转发到后端Service/转发到前端Nginx Service路由到对应的后端服务。通常还需要一个Ingress Controller如Nginx Ingress Controller来具体实现这些规则。2.4 存储与数据库可选按需引入对于有状态服务比如MySQL或Redis虽然也可以部署在K3S内但对于生产环境我个人更倾向于使用云托管的数据库服务如RDS或独立的数据库服务器。这主要是出于数据持久化、备份和性能的考虑。如果坚持在K3S内部署则需要妥善配置PersistentVolumePV和PersistentVolumeClaimPVC来保证数据不丢失。整个数据流是这样的用户访问网站域名 - 请求到达K3S集群的Ingress Controller - Ingress规则将/路径的请求路由到前端Nginx Service - Nginx Pod返回静态页面 - 页面中的JavaScript发起API调用路径为/api/... - 请求再次经过Ingress被路由到后端SpringBoot Service - SpringBoot Pod处理请求并返回数据。3. 实战部署从零搭建K3S环境理论讲完我们进入实战环节。假设你有一台干净的Linux服务器Ubuntu 20.04/22.04或CentOS 7/8下面是一步步的操作指南。3.1 K3S服务器安装与初始化K3S的安装简单到令人发指。使用官方的一键安装脚本即可。这里我们安装一个单节点集群同时包含server和agent角色这对于学习和测试环境足够了。# 使用国内镜像加速安装避免网络问题 curl -sfL https://rancher-mirror.rancher.cn/k3s/k3s-install.sh | INSTALL_K3S_MIRRORcn sh -安装完成后K3S服务会自动启动。检查安装状态sudo systemctl status k3s获取管理集群所需的配置文件sudo cat /etc/rancher/k3s/k3s.yaml将这个文件的内容复制到你本地机器的~/.kube/config并修改其中的server地址为你的K3S服务器IP例如https://192.168.1.100:6443。然后安装kubectl命令行工具就可以远程管理集群了。注意生产环境或多节点集群的安装会更复杂一些需要指定K3S_URL和K3S_TOKEN。单节点模式默认的--cluster-init参数会使用内置的sqlite代替etcd牺牲了一些高可用性但换来了极致的简洁。3.2 必备工具安装与配置光有K3S还不够我们还需要一些“脚手架”工具。HelmK8s的包管理工具我们用它来安装Nginx Ingress Controller。curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bashNginx Ingress Controller这是实现Ingress规则的关键组件。# 添加Helm仓库 helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx helm repo update # 安装ingress-nginx helm install ingress-nginx ingress-nginx/ingress-nginx \ --namespace ingress-nginx \ --create-namespace \ --set controller.service.typeNodePort # 暂时用NodePort方式暴露方便测试安装后执行kubectl get svc -n ingress-nginx可以看到一个类型为NodePort的服务记下它的端口例如80:32688/TCP后面我们会用服务器的IP加这个端口32688来访问服务。3.3 构建与推送应用镜像这是承上启下的关键一步。我们需要为SpringBoot和Vue项目分别制作Docker镜像并推送到一个镜像仓库。这里以Docker Hub为例。SpringBoot Dockerfile示例# 使用多阶段构建减少最终镜像体积 FROM maven:3.8.6-eclipse-temurin-11 AS build WORKDIR /app COPY pom.xml . RUN mvn dependency:go-offline -B COPY src ./src RUN mvn clean package -DskipTests FROM eclipse-temurin:11-jre-alpine WORKDIR /app COPY --frombuild /app/target/*.jar app.jar # 建议通过环境变量传入JVM参数而非写死在镜像里 ENTRYPOINT [java, -jar, /app/app.jar]构建并推送docker build -t yourdockerhub/springboot-app:latest . docker push yourdockerhub/springboot-app:latestVue Dockerfile示例# 构建阶段 FROM node:18-alpine AS build WORKDIR /app COPY package*.json ./ RUN npm ci --onlyproduction COPY . . RUN npm run build # 运行阶段 FROM nginx:alpine COPY --frombuild /app/dist /usr/share/nginx/html # 复制自定义的nginx配置用于代理API请求 COPY nginx.conf /etc/nginx/conf.d/default.conf EXPOSE 80其中nginx.conf是关键它定义了如何将API请求转发到后端server { listen 80; server_name localhost; location / { root /usr/share/nginx/html; index index.html index.htm; try_files $uri $uri/ /index.html; } # 代理所有以/api开头的请求到后端服务 location /api/ { proxy_pass http://springboot-svc:8080/; # 注意这里用的是K3S Service名 proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }同样构建并推送Vue镜像docker build -t yourdockerhub/vue-app:latest . docker push yourdockerhub/vue-app:latest实操心得镜像标签Tag不要永远用latest。建议使用Git提交哈希或版本号如v1.0.0-gitabc123。这样部署时可以精确回滚。在K3S的YAML文件中引用带版本的镜像是走向持续部署的第一步。4. 编写K3S部署清单YAML一切准备就绪现在用K3S能读懂的语言——YAML文件来描述我们的整个应用。通常我们会为每个组件创建独立的YAML文件但为了方便管理也可以放在一个文件里用---分隔。4.1 部署后端SpringBoot应用创建一个文件叫deploy-springboot.yaml。apiVersion: apps/v1 kind: Deployment metadata: name: springboot-deployment spec: replicas: 2 # 启动两个Pod实例 selector: matchLabels: app: springboot-app template: metadata: labels: app: springboot-app spec: containers: - name: springboot-container image: yourdockerhub/springboot-app:latest # 替换为你的镜像 ports: - containerPort: 8080 env: - name: SPRING_PROFILES_ACTIVE value: prod # 可以从ConfigMap或Secret中注入更多环境变量 # resources: # requests: # memory: 512Mi # cpu: 250m # limits: # memory: 1Gi # cpu: 500m --- apiVersion: v1 kind: Service metadata: name: springboot-svc # 这个名称很重要前端Nginx和Ingress会用到 spec: selector: app: springboot-app ports: - protocol: TCP port: 80 # Service对集群内暴露的端口 targetPort: 8080 # 容器内部的端口这个文件定义了两件事1. 一个Deployment确保始终有2个SpringBoot应用的Pod在运行2. 一个Service为这组Pod提供一个统一的内部访问入口springboot-svc:80。4.2 部署前端Vue应用创建文件deploy-vue.yaml。apiVersion: apps/v1 kind: Deployment metadata: name: vue-deployment spec: replicas: 2 selector: matchLabels: app: vue-app template: metadata: labels: app: vue-app spec: containers: - name: vue-container image: yourdockerhub/vue-app:latest # 替换为你的镜像 ports: - containerPort: 80 --- apiVersion: v1 kind: Service metadata: name: vue-svc spec: selector: app: vue-app ports: - protocol: TCP port: 80 targetPort: 80前端部署相对简单核心是保证Nginx容器运行并暴露80端口。4.3 配置Ingress统一入口这是将内外网打通的关键。创建文件ingress.yaml。apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: app-ingress annotations: kubernetes.io/ingress.class: nginx # 指定使用nginx ingress controller spec: rules: - http: paths: - path: / pathType: Prefix backend: service: name: vue-svc port: number: 80 - path: /api pathType: Prefix backend: service: name: springboot-svc port: number: 80这个Ingress规则告诉Nginx Ingress Controller当访问根路径/时把流量导向前端的vue-svc当访问/api或任何以/api/开头的路径时把流量导向后端的springboot-svc。重要提示这里后端Service的端口是80而不是SpringBoot容器的8080。这是因为我们在springboot-svc的定义中将port: 80映射到了targetPort: 8080。Ingress规则中引用的是Service的端口。5. 部署上线与验证YAML文件准备就绪现在将它们应用到K3S集群。# 应用所有配置 kubectl apply -f deploy-springboot.yaml kubectl apply -f deploy-vue.yaml kubectl apply -f ingress.yaml应用后使用以下命令观察部署状态# 查看所有Pod是否都进入Running状态 kubectl get pods -w # 查看Service kubectl get svc # 查看Ingress kubectl get ingress如果一切顺利你现在可以通过两种方式访问你的应用通过Ingress Controller的NodePort还记得安装Ingress时记下的端口吗例如32688在浏览器访问http://你的服务器IP:32688应该能看到Vue前端页面并且前端发起的API请求如http://你的服务器IP:32688/api/hello能正确到达SpringBoot后端并返回结果。配置域名推荐修改你本地机器的hosts文件C:\Windows\System32\drivers\etc\hosts或/etc/hosts将某个域名如myapp.local指向你的K3S服务器IP。然后修改ingress.yaml在spec.rules下添加host: myapp.local字段。重新应用Ingress后就可以通过http://myapp.local访问了如果Ingress Controller配置了LoadBalancer或有了公网IP则可以直接用真实域名。验证要点前端页面能否正常加载静态资源JS CSS前端页面发起的API请求打开浏览器开发者工具查看Network标签是否返回200状态码和正确数据尝试kubectl scale deployment springboot-deployment --replicas3将后端扩容到3个实例观察请求是否被均匀分配可以在SpringBoot的日志中打印Pod主机名来验证。模拟一个Pod故障kubectl delete pod springboot-pod-nameK3S是否会立即创建一个新的Pod来替代6. 进阶配置与优化技巧基础部署跑通后我们可以让它更健壮、更专业。6.1 配置管理告别硬编码永远不要将数据库连接字符串、API密钥等写死在代码或镜像里。使用ConfigMap和Secret。# configmap.yaml apiVersion: v1 kind: ConfigMap metadata: name: app-config data: application.yml: | spring: datasource: url: jdbc:mysql://${DB_HOST}:${DB_PORT}/mydb username: ${DB_USER} app: api-key: placeholder # 真实值从环境变量或Secret注入 --- # secret.yaml (注意数据需要是base64编码) apiVersion: v1 kind: Secret metadata: name: app-secret type: Opaque data: db-password: c3VwZXJzZWNyZXRwYXNzd29yZA # 示例supersecretpassword然后在SpringBoot的Deployment中通过环境变量或卷挂载的方式使用它们env: - name: DB_PASSWORD valueFrom: secretKeyRef: name: app-secret key: db-password - name: SPRING_DATASOURCE_PASSWORD value: $(DB_PASSWORD) # 在容器内引用环境变量6.2 资源限制与健康检查不给Pod设置资源限制就像开车不系安全带。在Deployment的spec.template.spec.containers下添加resources: requests: memory: 256Mi cpu: 100m limits: memory: 512Mi cpu: 200m livenessProbe: httpGet: path: /actuator/health/liveness # Spring Boot Actuator端点 port: 8080 initialDelaySeconds: 60 periodSeconds: 10 readinessProbe: httpGet: path: /actuator/health/readiness port: 8080 initialDelaySeconds: 30 periodSeconds: 5requests是调度依据limits是硬性上限。健康检查能让K3S知道你的应用是否活着liveness以及是否准备好接收流量readiness。6.3 日志与监控K3S默认使用containerd容器日志在/var/log/containers/目录下。对于集中式日志收集可以考虑部署轻量的EFKElasticsearch, Fluentd, Kibana栈或Loki。监控方面可以安装Prometheus Stack通过Helm安装kube-prometheus-stack它能自动发现并监控K3S集群和所有Pod的性能指标。6.4 持久化存储如果SpringBoot应用需要上传文件或者你决定在集群内部署MySQL就需要持久化存储。K3S自带一个本地路径存储类local-path。# pvc.yaml apiVersion: v1 kind: PersistentVolumeClaim metadata: name: mysql-pvc spec: storageClassName: local-path accessModes: - ReadWriteOnce resources: requests: storage: 5Gi然后在Deployment中挂载这个PVC即可。注意local-path通常只适用于单节点集群多节点集群需要配置网络存储如NFS。7. 常见问题与故障排查实录在实际操作中你几乎一定会遇到下面这些问题。我把踩过的坑和解决方法整理出来希望能帮你节省几个小时甚至几天的时间。7.1 镜像拉取失败ImagePullBackOff这是最常见的问题。Pod状态显示ImagePullBackOff或ErrImagePull。原因1镜像名称或标签错误。仔细检查YAML文件中的image字段确保和Docker Hub或你的私有仓库里的完全一致包括大小写。原因2私有仓库需要认证。你需要创建一个docker-registry类型的Secret。kubectl create secret docker-registry regcred \ --docker-server你的仓库地址 \ --docker-username用户名 \ --docker-password密码 \ --docker-email邮箱然后在Deployment的spec.template.spec下添加imagePullSecrets: - name: regcred原因3网络问题。如果是海外镜像如gcr.io国内网络可能无法拉取。可以尝试寻找国内镜像源或者先docker pull到本地再docker tag和docker push到你能访问的仓库。7.2 Pod启动失败CrashLoopBackOffPod反复启动又崩溃。排查日志这是最重要的手段。kubectl logs pod-name查看当前容器的日志如果Pod之前有崩溃过的容器加-p参数查看上一个容器的日志kubectl logs pod-name -p。日志里通常会直接告诉你错误原因比如“数据库连接失败”、“配置文件找不到”、“端口被占用”等。常见原因应用配置错误检查通过ConfigMap或环境变量传入的配置是否正确。特别是数据库连接字符串、Redis地址等。依赖服务未就绪比如SpringBoot应用启动时需要连接MySQL但MySQL的Pod还没启动好。可以通过在Deployment中配置readinessProbe并在依赖项上使用initContainers或调整启动顺序来缓解。资源不足检查Pod是否因为内存或CPU超出limits而被系统杀死OOMKilled。kubectl describe pod pod-name查看事件。7.3 Ingress访问404或502前端能打开但API请求报错。检查Ingress规则kubectl describe ingress app-ingress查看Events事件和Rules规则是否正确映射到了后端Service。检查Service和Pod的Selector确保Service的selector如app: springboot-app和Pod的labels完全匹配。一个字母都不能差。检查后端服务本身直接通过kubectl port-forward命令将后端服务的端口映射到本地测试。kubectl port-forward svc/springboot-svc 8080:80然后在本地访问http://localhost:8080/actuator/health看服务是否正常。如果这里都不通问题就在后端应用或Service配置上。检查Nginx配置回顾前端Nginx配置文件中的proxy_pass地址必须是K3S内部的Service名和端口如http://springboot-svc:8080。在Vue的Pod里执行kubectl exec -it vue-pod-name -- sh然后cat /etc/nginx/conf.d/default.conf确认配置已正确注入。7.4 节点资源紧张Pod处于Pending状态执行kubectl get pods发现Pod状态一直是Pending。查看详情kubectl describe pod pod-name在Events部分通常会看到提示“Insufficient cpu”或“Insufficient memory”。解决方法给节点增加资源加内存、CPU。优化Pod的资源requests降低其申请值。清理不必要的Pod或部署。使用kubectl top nodes和kubectl top pods查看资源使用情况。7.5 如何更新应用最简单的更新方式是修改Deployment的镜像标签然后重新应用。kubectl set image deployment/springboot-deployment springboot-containeryourdockerhub/springboot-app:v1.1.0K3S会启动新的Pod等待其通过readinessProbe后逐步替换旧的Pod实现零停机更新。你可以通过kubectl rollout status deployment/springboot-deployment来观察更新过程。如果新版本有问题可以快速回滚kubectl rollout undo deployment/springboot-deployment。部署完成后别急着关终端。养成习惯先看Pod状态再查服务端点最后通过Ingress访问。日志kubectl logs和事件kubectl describe是你最好的朋友。对于复杂问题学会使用kubectl get events --sort-by.metadata.creationTimestamp来按时间顺序查看集群事件往往能发现问题的根源。