ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

K3S实战:SpringBoot+Vue全栈应用容器化部署与Kubernetes编排指南

K3S实战:SpringBoot+Vue全栈应用容器化部署与Kubernetes编排指南 1. 项目概述与核心价值最近在整理技术栈发现很多朋友在从单体应用转向微服务或云原生架构时面对容器编排这一关总有些发怵。尤其是中小团队或个人开发者既想享受Kubernetes带来的声明式部署、服务发现、弹性伸缩等现代化能力又担心它那庞大的体量和复杂的运维成本。这不我手头正好有一个典型的SpringBoot后端Vue前端组成的全栈项目需要部署上线这次我决定不直接用庞大的K8s而是选用它的轻量级发行版——K3S来一次从零到一的完整实践。K3S可以理解为Kubernetes的“精简优化版”它保留了K8s的核心API和功能但通过去除历史包袱、集成轻量级组件如用SQLite替代etcd、将二进制文件打包为单个小于100MB的包等方式极大地降低了资源消耗和部署复杂度。对于资源有限的边缘计算场景、开发测试环境或者像我这样只想快速搭建一个稳定可用的生产级容器编排平台的人来说K3S简直是“福音”。本次部署的目标就是将一套标准的SpringBoot后端服务提供RESTful API和一个Vue.js构建的前端静态应用通过K3S集群进行容器化部署实现服务的高可用、便捷管理和自动化运维。整个流程涉及Docker镜像制作、K3S集群搭建、Kubernetes资源配置文件YAML编写、服务暴露等关键环节我会把每一步的原理、操作和踩过的坑都详细记录下来。2. 环境与工具准备构建可复现的基础工欲善其事必先利其器。在开始动手之前我们需要准备好一个干净、一致的实验环境。我选择在一台配置为2核4GB的云服务器CentOS 7.9上进行这模拟了大多数个人项目或初创团队的最小化生产环境。当然你也可以在本地虚拟机如VirtualBox Ubuntu或多台服务器上搭建集群原理相通。2.1 基础系统配置首先确保系统环境干净。更新系统包并安装一些基础工具# 更新系统包 yum update -y # 安装常用工具wget用于下载vim用于编辑net-tools用于网络诊断 yum install -y wget vim net-tools接下来是关键一步关闭Swap。Kubernetes及其衍生品包括K3S为了确保调度和运行的稳定性默认要求禁用Swap内存。这是因为Swap的I/O操作会引入不可预测的延迟影响Pod容器组的调度和性能。# 临时关闭Swap swapoff -a # 永久关闭Swap注释掉/etc/fstab中swap相关的行 sed -i / swap / s/^\(.*\)$/#\1/g /etc/fstab然后配置Linux内核参数以允许iptables正确处理桥接网络流量这是容器网络正常工作的基础。# 加载br_netfilter模块 modprobe br_netfilter # 确保重启后依然加载 echo br_netfilter /etc/modules-load.d/k8s.conf # 设置sysctl参数 cat EOF | sudo tee /etc/sysctl.d/k8s.conf net.bridge.bridge-nf-call-ip6tables 1 net.bridge.bridge-nf-call-iptables 1 EOF # 使配置生效 sysctl --system2.2 Docker安装与配置虽然K3S默认集成了containerd作为容器运行时且从1.21版本开始甚至可以直接通过--docker参数使用Docker但考虑到我们后续镜像构建和调试的便利性Docker CLI工具链更丰富我选择先安装Docker并配置K3S使用Docker作为运行时。# 1. 卸载旧版本如有 yum remove docker docker-client docker-client-latest docker-common docker-latest docker-latest-logrotate docker-logrotate docker-engine # 2. 安装yum工具集并添加Docker仓库 yum install -y yum-utils yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo # 3. 安装Docker引擎 yum install -y docker-ce docker-ce-cli containerd.io # 4. 启动并设置开机自启 systemctl start docker systemctl enable docker # 5. 验证安装 docker run hello-world安装成功后需要配置Docker的镜像加速器国内环境必备并修改Cgroup驱动为systemd以与K3S保持一致避免潜在的兼容性问题。# 配置daemon.json cat /etc/docker/daemon.json EOF { registry-mirrors: [https://your-mirror.mirror.aliyuncs.com], # 替换为你的加速器地址 exec-opts: [native.cgroupdriversystemd], log-driver: json-file, log-opts: { max-size: 100m }, storage-driver: overlay2 } EOF # 重启Docker systemctl daemon-reload systemctl restart docker2.3 K3S Server节点安装K3S的安装简单到令人发指。官方提供了一键安装脚本。我们将当前服务器作为集群的Server节点即Master节点。# 使用国内镜像加速安装避免网络问题 curl -sfL https://rancher-mirror.rancher.cn/k3s/k3s-install.sh | INSTALL_K3S_MIRRORcn sh -s - --docker这个命令做了几件事下载K3S安装脚本、指定使用中国镜像源、并通过--docker参数告诉K3S使用我们刚安装的Docker作为容器运行时。安装完成后K3S服务会自动启动。关键验证与文件定位# 检查K3S服务状态 systemctl status k3s # 获取node节点信息此时应该只有一个节点角色为control-plane,master k3s kubectl get node # 获取集群所有Pod状态确认核心组件coredns, metrics-server等运行正常 k3s kubectl get pods -A安装成功后K3S的kubeconfig文件集群管理员凭证位于/etc/rancher/k3s/k3s.yaml。我们需要将其权限设置正确并复制到本地~/.kube/config以便使用常规的kubectl命令。sudo chmod 644 /etc/rancher/k3s/k3s.yaml mkdir -p ~/.kube sudo cp /etc/rancher/k3s/k3s.yaml ~/.kube/config sudo chown $(id -u):$(id -g) ~/.kube/config # 验证kubectl kubectl cluster-info3. 应用容器化从代码到镜像有了K3S集群接下来就需要将我们的SpringBoot和Vue应用打包成Docker镜像。这是将任何应用交付到Kubernetes生态的第一步也是至关重要的一步。3.1 SpringBoot后端应用Docker化对于SpringBoot应用通常我们采用多阶段构建Multi-stage Build来制作镜像目的是得到一个仅包含运行所需JRE和JAR包的、体积尽可能小的生产镜像。假设你的SpringBoot项目使用Maven构建项目根目录结构如下springboot-app/ ├── src/ ├── pom.xml └── Dockerfile对应的Dockerfile内容如下# 第一阶段构建阶段使用Maven镜像 FROM maven:3.8.6-openjdk-11-slim AS builder WORKDIR /app # 复制pom文件利用Docker缓存层避免依赖重复下载 COPY pom.xml . RUN mvn dependency:go-offline -B # 复制源码并打包 COPY src ./src RUN mvn clean package -DskipTests # 第二阶段运行阶段使用轻量级JRE镜像 FROM openjdk:11-jre-slim # 设置时区避免容器内日志时间不对 RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime echo Asia/Shanghai /etc/timezone # 创建一个非root用户运行应用增强安全性 RUN useradd -m -u 1000 appuser USER appuser WORKDIR /app # 从构建阶段复制打好的jar包 COPY --frombuilder /app/target/*.jar app.jar # 暴露应用端口与SpringBoot配置文件中的server.port一致 EXPOSE 8080 # 使用exec形式启动确保能接收SIGTERM等信号实现优雅关闭 ENTRYPOINT [java, -jar, app.jar]构建与推送镜像# 在项目根目录执行构建并打上标签 docker build -t your-dockerhub-username/springboot-app:1.0.0 . # 登录Docker Hub或其他镜像仓库 docker login # 推送镜像到仓库 docker push your-dockerhub-username/springboot-app:1.0.0注意如果你的应用需要连接数据库、Redis等不要在Dockerfile里写死配置。应该通过环境变量在K8s的YAML中配置或外部的配置文件通过ConfigMap挂载来注入保证镜像的通用性。3.2 Vue前端应用Docker化Vue项目是纯静态资源HTML, CSS, JS。标准的做法是使用Node.js环境进行构建npm run build然后将生成的dist目录放到一个Nginx或Apache镜像中提供服务。假设Vue项目结构如下vue-app/ ├── src/ ├── public/ ├── package.json ├── vue.config.js └── Dockerfile对应的Dockerfile也采用多阶段构建# 第一阶段构建阶段使用Node镜像 FROM node:16-alpine AS builder WORKDIR /app # 复制依赖文件并安装 COPY package*.json ./ RUN npm install --registryhttps://registry.npmmirror.com # 复制源码并构建 COPY . . RUN npm run build # 第二阶段运行阶段使用Nginx镜像 FROM nginx:alpine # 将构建好的静态文件复制到Nginx的默认服务目录 COPY --frombuilder /app/dist /usr/share/nginx/html # 如果需要可以复制自定义的Nginx配置文件 # COPY nginx.conf /etc/nginx/conf.d/default.conf # 暴露80端口 EXPOSE 80 # Nginx镜像默认已启动Nginx无需额外ENTRYPOINT构建与推送docker build -t your-dockerhub-username/vue-app:1.0.0 . docker push your-dockerhub-username/vue-app:1.0.0实操心得Vue项目构建时经常需要配置生产环境API地址。绝对不要写死在vue.config.js里。正确做法是在Dockerfile构建阶段通过构建参数--build-arg传入或者更常见的在Nginx配置中设置一个变量让前端JS在运行时从window.env或通过请求特定接口来获取。在K8s中我们通常采用后者结合ConfigMap来管理Nginx配置。4. K3S集群部署实战编写Kubernetes清单镜像准备就绪后就到了最核心的环节编写Kubernetes的资源清单文件YAML。这些文件描述了我们的应用最终在集群中应该以何种形态运行。我会为前后端分别创建Deployment定义Pod副本和Service定义网络访问。4.1 部署SpringBoot后端服务创建文件springboot-deployment.yaml:apiVersion: apps/v1 kind: Deployment metadata: name: springboot-backend namespace: default # 不指定则默认为default spec: replicas: 2 # 启动2个Pod副本实现高可用 selector: matchLabels: app: springboot-backend template: metadata: labels: app: springboot-backend spec: containers: - name: app image: your-dockerhub-username/springboot-app:1.0.0 # 替换为你的镜像 ports: - containerPort: 8080 # 容器内端口 env: # 通过环境变量注入配置这是12-Factor App的最佳实践 - name: SPRING_PROFILES_ACTIVE value: prod - name: DB_HOST valueFrom: configMapKeyRef: # 假设数据库地址通过ConfigMap管理 name: app-config key: database.host - name: REDIS_HOST valueFrom: configMapKeyRef: name: app-config key: redis.host resources: # 资源请求与限制防止单个Pod占用过多资源也帮助调度器决策 requests: memory: 512Mi cpu: 250m limits: memory: 1Gi cpu: 500m livenessProbe: # 存活探针检查应用是否“活着” httpGet: path: /actuator/health/liveness # Spring Boot Actuator端点 port: 8080 initialDelaySeconds: 60 # 容器启动后60秒开始探测 periodSeconds: 10 # 每10秒探测一次 readinessProbe: # 就绪探针检查应用是否“准备好”接收流量 httpGet: path: /actuator/health/readiness port: 8080 initialDelaySeconds: 30 periodSeconds: 5 --- apiVersion: v1 kind: Service metadata: name: springboot-backend-service spec: selector: app: springboot-backend ports: - port: 80 # Service对集群内暴露的端口 targetPort: 8080 # 转发到Pod的哪个端口 type: ClusterIP # 默认类型仅在集群内部可访问关键点解析Deployment: 定义了Pod的副本数、更新策略等。replicas: 2意味着任何时候都至少有两个Pod在运行一个挂了另一个还能服务。环境变量env: 将配置信息如数据库连接串从代码中分离。这里演示了从ConfigMap引用实际还可以用Secret管理密码。资源resources:requests是调度依据limits是硬性限制。设置合理值能提高集群稳定性。探针Probe: 这是生产部署的灵魂。livenessProbe失败K8s会重启PodreadinessProbe失败K8s会将该Pod从Service的负载均衡池中移除直到它恢复。这实现了应用的自我修复和零停机部署。Service: 类型为ClusterIP为后端Pod集合提供了一个稳定的内部域名springboot-backend-service.default.svc.cluster.local和IP前端应用在集群内通过这个域名访问后端。4.2 部署Vue前端服务创建文件vue-frontend-deployment.yaml:apiVersion: apps/v1 kind: Deployment metadata: name: vue-frontend spec: replicas: 2 selector: matchLabels: app: vue-frontend template: metadata: labels: app: vue-frontend spec: containers: - name: nginx image: your-dockerhub-username/vue-app:1.0.0 ports: - containerPort: 80 resources: requests: memory: 128Mi cpu: 100m limits: memory: 256Mi cpu: 200m # 静态资源服务通常不需要复杂的探针一个简单的HTTP GET即可 livenessProbe: httpGet: path: /index.html port: 80 initialDelaySeconds: 10 periodSeconds: 10 readinessProbe: httpGet: path: /index.html port: 80 initialDelaySeconds: 5 periodSeconds: 5 --- apiVersion: v1 kind: Service metadata: name: vue-frontend-service spec: selector: app: vue-frontend ports: - port: 80 targetPort: 80 type: NodePort # 修改为NodePort便于从集群外部访问关键点解析NodePort Service: 这是与后端Service最大的不同。type: NodePort会在集群每个节点的某个固定端口范围30000-32767上暴露服务。这样用户就可以通过节点IP:NodePort来访问前端页面。这是最简单的外部访问方式适合演示和测试。生产环境通常会使用LoadBalancer如果云厂商支持或更常用的Ingress控制器。4.3 应用配置管理ConfigMap示例将可变配置与镜像分离。创建app-configmap.yaml:apiVersion: v1 kind: ConfigMap metadata: name: app-config data: # 后端应用配置 database.host: mysql-service.default.svc.cluster.local redis.host: redis-service.default.svc.cluster.local # 前端API基础地址可供前端运行时获取 api.base.url: http://springboot-backend-service这个ConfigMap被上面的SpringBoot Deployment通过env.valueFrom.configMapKeyRef引用。4.4 执行部署与验证将以上YAML文件保存到服务器然后使用kubectl apply命令部署# 应用所有配置 kubectl apply -f app-configmap.yaml kubectl apply -f springboot-deployment.yaml kubectl apply -f vue-frontend-deployment.yaml # 查看部署状态 kubectl get deployments kubectl get pods -o wide # 查看Pod详情及所在节点 kubectl get services # 查看Service注意vue-frontend-service的NodePort端口 # 查看Pod日志排查问题 kubectl logs -f pod-name如果一切顺利你会看到两个springboot-backend-xxxxx和两个vue-frontend-xxxxx的Pod状态都是Running。通过kubectl get svc命令找到vue-frontend-service对应的NodePort例如32345然后在浏览器访问http://你的服务器IP:32345应该就能看到Vue前端页面了。前端页面发起的API请求会通过配置的api.base.url指向springboot-backend-service访问到后端服务。5. 进阶配置与生产级考量基础的部署完成后我们可以进一步优化让这个部署更接近生产环境的要求。5.1 使用Ingress暴露服务替代NodePortNodePort不适合生产端口范围有限且不安全。更优雅的方式是使用Ingress它类似于一个7层HTTP/HTTPS负载均衡器可以根据域名和路径将流量路由到不同的后端Service。首先在K3S上安装一个Ingress Controller。K3S默认集成了Traefik作为Ingress Controller但也可以安装更流行的Nginx Ingress Controller。这里以安装Nginx Ingress为例# 使用Helm安装K3S默认安装了Helm helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx helm repo update helm install ingress-nginx ingress-nginx/ingress-nginx \ --namespace ingress-nginx \ --create-namespace \ --set controller.service.typeNodePort # 为Ingress Controller自身创建一个NodePort Service安装后创建一个Ingress资源定义文件ingress.yaml:apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: fullstack-ingress annotations: nginx.ingress.kubernetes.io/rewrite-target: / # 可能需要根据前端路由配置重写规则 spec: rules: - host: app.yourdomain.com # 你的域名需要配置DNS指向集群节点IP或负载均衡器IP http: paths: - path: / pathType: Prefix backend: service: name: vue-frontend-service port: number: 80 - path: /api pathType: Prefix backend: service: name: springboot-backend-service port: number: 80应用这个Ingresskubectl apply -f ingress.yaml现在访问http://app.yourdomain.com会看到前端而前端所有以/api开头的请求会被转发到后端SpringBoot服务。你需要将域名app.yourdomain.com解析到安装了Ingress Controller的节点IP或云负载均衡器IP。5.2 持久化存储与数据库部署有状态服务如MySQL、Redis需要持久化存储。在K3S中可以方便地使用Local Path ProvisionerK3S默认安装来提供动态的本地存储或者对接云存储。以部署MySQL为例需要创建一个PersistentVolumeClaim (PVC)来申请存储然后在Deployment中挂载。这里是一个简化的示例apiVersion: v1 kind: PersistentVolumeClaim metadata: name: mysql-pvc spec: accessModes: - ReadWriteOnce resources: requests: storage: 5Gi storageClassName: local-path # 使用K3S自带的local-path存储类 --- apiVersion: apps/v1 kind: Deployment metadata: name: mysql spec: selector: matchLabels: app: mysql template: metadata: labels: app: mysql spec: containers: - name: mysql image: mysql:8.0 env: - name: MYSQL_ROOT_PASSWORD valueFrom: secretKeyRef: # 密码等敏感信息务必使用Secret name: mysql-secret key: rootPassword volumeMounts: - name: mysql-data mountPath: /var/lib/mysql volumes: - name: mysql-data persistentVolumeClaim: claimName: mysql-pvc # 挂载PVC注意生产环境强烈建议将数据库部署在集群外或者使用云托管的数据库服务RDS以获得更好的可靠性、可维护性和备份能力。5.3 日志与监控K3S默认集成了CoreDNS和Metrics Server。Metrics Server为Kubernetes Dashboard或HPAHorizontal Pod Autoscaler自动水平扩缩容提供资源指标。对于应用日志标准做法是让应用将日志输出到标准输出stdout和标准错误stderrKubernetes会自动捕获并可以通过kubectl logs查看。对于集中式日志收集可以部署EFKElasticsearch, Fluentd, Kibana或Loki栈。对于监控可以部署Prometheus Grafana。K3S社区有相关的Helm Chart可以一键部署用于监控集群节点、Pod、Service等各项指标。6. 常见问题与故障排查实录在实际操作中你几乎一定会遇到一些问题。下面是我在部署过程中遇到的一些典型问题及解决方法。6.1 镜像拉取失败现象Pod状态一直为ImagePullBackOff或ErrImagePull。排查kubectl describe pod pod-name查看Pod详情在Events部分会有详细错误信息。常见原因镜像名错误或不存在检查YAML文件中的image字段确保拼写正确且镜像已推送到仓库。私有仓库无权限如果使用私有仓库如阿里云容器镜像服务需要创建docker-registry类型的Secret并在Deployment的spec.template.spec中添加imagePullSecrets字段引用它。# 创建docker-registry secret kubectl create secret docker-registry regcred \ --docker-serveryour-registry-server \ --docker-usernameyour-name \ --docker-passwordyour-password \ --docker-emailyour-email然后在Deployment YAML中添加spec: template: spec: imagePullSecrets: - name: regcred containers: - ...6.2 Pod启动后立即CrashLoopBackOff现象Pod反复重启状态为CrashLoopBackOff。排查kubectl logs pod-name --previous查看上一个容器的日志这通常能直接定位到应用启动失败的原因如数据库连接不上、配置文件错误、端口冲突等。检查应用的健康检查liveness/readiness probe配置是否合理。如果探针检查的路径不对或启动延迟initialDelaySeconds设置太短可能导致K8s认为应用不健康而不断重启。可以临时将探针注释掉看应用是否能正常启动并运行。6.3 Service无法访问现象前端页面能打开但所有API请求都失败网络错误。排查在集群内部进行调试。进入一个运行中的Pod比如前端Pod使用curl命令测试后端Service的域名。kubectl exec -it vue-pod-name -- sh # 在Pod内部执行 curl -v http://springboot-backend-service:80/actuator/health如果内部能通说明Service配置和网络策略没问题问题可能出在前端代码配置的API地址上。检查前端构建时或运行时配置的API地址是否正确指向了后端Service的集群内域名springboot-backend-service或带命名空间的springboot-backend-service.default.svc.cluster.local。如果内部也不通检查Service的selector是否与Pod的labels匹配。Pod的containerPort是否与Service的targetPort一致。后端Pod是否真的在运行且就绪kubectl get pods查看READY列。6.4 NodePort无法从外部访问现象浏览器访问节点IP:NodePort无法打开页面。排查确认NodePort端口号kubectl get svc vue-frontend-service查看PORT(S)列例如80:32345/TCP则NodePort是32345。检查服务器安全组/防火墙规则是否放行了该NodePort端口30000-32767范围。在服务器本机用curl测试curl http://localhost:32345如果本机能通外部不通基本就是防火墙或安全组的问题。6.5 K3S特定问题coredns Pod pending现象kubectl get pods -A发现corednsPod状态为Pending。排查这通常是因为节点资源特别是CPU/内存不足或者local-path存储类的默认存储路径权限问题。查看Pod详情kubectl describe pod -n kube-system coredns-xxxx。如果是资源不足考虑增加节点资源或优化其他Pod的资源限制。如果是存储问题可以尝试手动创建存储目录并赋予权限谨慎操作# 查看local-path的配置路径 kubectl get storageclass local-path -o yaml | grep -A 5 -B 5 path # 通常默认路径在/var/lib/rancher/k3s/storage下 sudo mkdir -p /var/lib/rancher/k3s/storage sudo chmod 777 /var/lib/rancher/k3s/storage # 仅为示例生产环境应设置更严格的权限然后删除Pending的Pod让其重建kubectl delete pod -n kube-system coredns-xxxx。整个部署过程从环境准备到应用上线再到问题排查其实是一个不断加深对Kubernetes对象模型理解的过程。K3S极大地降低了入门门槛但它背后运行的是完整的、生产就绪的Kubernetes能力。把SpringBoot和Vue这样的经典组合放上去不仅仅是完成一次部署更是为应用拥抱弹性伸缩、蓝绿部署、服务网格等更高级的云原生特性铺平了道路。当你熟悉了这些YAML文件和kubectl命令之后你会发现管理应用的生命周期变得前所未有的清晰和高效。
返回列表