阶段四:微服务项目运维实践基础
项目部署到k8s中的流程
1. 集群服务对外提供访问,需要通过Ingress代理发布域名。 2. 搭建私有镜像仓库,用于存储自己构建的项目镜像。 3. 集群中容器数据持久化,通过PV、PVC、StorageClass实现后端数据存储。 4. 部署项目所需的数据库,并配置好项目连接数据库的信息。 5. 项目运行在什么环境中,需要将运行环境打包到镜像中。 6. 在k8s中部署项目,并通过Ingress发布项目
需要掌握的技能
微服务与容器化架构基础
- 熟悉容器技术(Docker)的原理与实践
- 掌握镜像构建、上传与运行流程
Kubernetes 核心概念与组件
- 掌握 Pod、Deployment、Service、Ingress 等关键资源
- 理解 K8s 控制面与工作节点的协作机制
- 能独立编写并应用 YAML 部署文件
微服务应用部署与运行机制
- 学会将微服务系统容器化并部署到 Kubernetes 集群
- 掌握服务发现、负载均衡、滚动更新等机制
- 掌握 ConfigMap、Secret、持久化卷等配置管理
系统监控与日志管理
- 能使用 Prometheus + Grafana 进行指标监控
- 能搭建日志收集系统ELK
- 能编写简单的告警规则
CI/CD 与自动化运维
- 掌握 Jenkins + GitLab + Harbor 的持续集成发布流程
高可用与故障处理
- 理解 HPA 自动扩缩容原理
- 掌握节点污点与 Pod 调度策略
- 能独立排查常见部署与运行故障
微服务项目部署
传统项目简介
- 传统项目就是把所有的业务功能写在一个项目中,最终打包成一个war/jar/php/python包,然后进行部署运行。如下图:

传统项目是一个进程包含了所有的业务功能,功能越多项目就复杂。随着代码的增多,构建和部署的时间也会增加,每次功能的变更或Bug的修复,甚至添加一个简单的功能,都需要重新打包整个项目代码。某个功能出现问题可能影响整个应用
微服务项目概述
- 微服务是一种将一个完整的项目拆分成一个个独立的小应用(微服务)每个应用运行在自己进程中。如下图:

一个微服务只关注一个特定的业务功能,所以它业务清晰、代码量较少,易于维护,启动更快。 传统项目只要有修改,就得重新部署整个应用,微服务解决了这样的问题。一般来说,对某个微服务进行修改,只需要重新部署这个服务即可。
War包与Jar包的区别:
- WAR包:是用于打包Java Web应用程序的格式。它包含Web应用程序的所有内容,如JSP文件、HTML文件、CSS文件、JavaScript文件、Servlet类、配置文件和依赖的JAR包等,WAR包通常用于将Web应用程序部署到Java Web容器(如Tomcat、Jetty等)中运行。
- JAR包:主要用于打包可独立执行的Java应用程序格式。它可以包含类文件、资源文件、配置文件和其他依赖的JAR包。JAR包通过Java的类路径机制被Java虚拟机(JVM)加载和使用。
微服务架构图
https://www.processon.com/view/5ec290195653bb6f2a18504e

微服务项目介绍
- RuoYi-Cloud 是通过Java开发,基于微服务架构设计的权限管理系统。
- Java程序开发框架介绍:
- Spring:是一个开源的Java开发框架,用于简化Java程序的开发。
- Spring Boot:是基于Spring的全新框架,对比于Spring框架更加的简洁高效。
- Spring Cloud:是一套微服务开发框架。
- Spring Cloud Alibaba:是阿里巴巴推出的微服务开发框架。
文档地址:http://doc.ruoyi.vip/ruoyi-cloud/
代码下载:https://gitee.com/y_project/RuoYi-Cloud
微服务组件介绍
- Gateway网关:在微服务架构中,网关起到了路由的作用,例如,用户需要请求一个特定的微服务,而这个请求会先通过网关,然后网关会将其路由到正确的服务上。
- Auth认证:负责处理身份认证和鉴权,系统需要验证用户的身份,身份被认证后,系统还需要确定用户可以访问哪些资源,执行哪些操作。
- Nacos注册中心:阿里开源的服务发现和服务配置平台,在微服务架构中,起到以下作用:
- 服务发现和服务注册:Nacos提供了一个服务注册中心,可以让微服务在启动时注册自己的信息,例如服务的名称、IP地址、端口号等。同时,Nacos也提供了服务发现的功能,允许微服务找到并调用其他的微服务。
- 配置动态更新:在微服务架构中,微服务的配置信息(如数据库连接参数、外部API接口地址等)需要根据环境或业务需求进行变动。Nacos允许将这些配置集中存储和管理,并在配置发生变动时,动态地推送给相应的微服务,无需重启服务即可更新配置。
部署Nacos注册中心
- 查看ACK集群的存储类

- 创建ruoyi命名空间

- 部署mysql数据库yaml
YAML 保存位置: ~/k8s-manifests/ruoyi/mysql-svc.yaml
先在 Kubernetes 管理节点执行:
mkdir -p ~/k8s-manifests/ruoyi
cd ~/k8s-manifests/ruoyi
vi mysql-svc.yaml把下方 YAML 全部复制进去;红色字段按当前环境修改;按 Esc 后输入 :wq 保存退出。
apiVersion: v1
kind: Service
metadata:
name: mysql-svc
namespace: ruoyi
spec:
clusterIP: None
selector:
app: mysql
ports:
- port: 3306
targetPort: 3306
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql
namespace: ruoyi
spec:
serviceName: "mysql-svc"
replicas: 1
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
containers:
- name: mysql
image: swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/mysql:8.4.6
env:
- name: MYSQL_ROOT_PASSWORD
value: "123456"
- name: MYSQL_DATABASE
value: "ry-config"
- name: TZ
value: "Asia/Shanghai"
ports:
- containerPort: 3306
volumeMounts:
- name: data
mountPath: /var/lib/mysql
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: "alicloud-disk-topology-alltype"
resources:
requests:
storage: 20Gi配置详解:
| 需要改的项 | 为什么改 |
|---|---|
namespace: ruoyi | 与实际命名空间一致;使用默认命名空间时可不写。 |
port: 3306 | 对外暴露的 Service 端口;调用方按此端口访问。 |
targetPort: 3306 | 必须与容器实际监听端口一致。 |
replicas: 1 | 按可用性和资源容量设置副本数。 |
image: swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/mysql:8.4.6 | 替换为实际可拉取的镜像地址和版本。 |
MYSQL_ROOT_PASSWORD: 123456 | 按当前环境的实际值确认。 |
注意事项:
- 修改配置前先备份原文件,尤其是 SSH、Nginx、数据库和 Kubernetes 生产配置。
- YAML 缩进不能乱;同级字段要对齐,子字段要多缩进两个空格。
- 资源名称、命名空间、标签选择器要互相对应,否则资源创建了也可能找不到彼此。
- 镜像地址、版本号和拉取权限要确认,否则 Pod 会进入
ImagePullBackOff。
#克隆项目代码并进入到sql目录
git clone https://gitee.com/y_project/RuoYi-Cloud.git
git checkout v3.6.7
cd /root/RuoYi-Cloud/sql
#数据库复制到容器内部
kubectl cp ry_config_20250902.sql ruoyi/mysql-0:/root
kubectl cp ry_20250523.sql ruoyi/mysql-0:/root
#登录mysql-0数据库
kubectl exec -it mysql-0 -n ruoyi -- /bin/bash
mysql -u root -p
mysql> use ry-config
mysql> source /root/ry_config_20250902.sql;
mysql> create database ruoyi;
mysql> use ruoyi
mysql> source /root/ry_20250523.sql;注意事项:
- 修改配置前先备份原文件,尤其是 SSH、Nginx、数据库和 Kubernetes 生产配置。
YAML 保存位置: ~/k8s-manifests/ruoyi/nacos-cm.yaml
先在 Kubernetes 管理节点执行:
mkdir -p ~/k8s-manifests/ruoyi
cd ~/k8s-manifests/ruoyi
vi nacos-cm.yaml把下方 YAML 全部复制进去;红色字段按当前环境修改;按 Esc 后输入 :wq 保存退出。
apiVersion: v1
kind: ConfigMap
metadata:
namespace: ruoyi
name: nacos-cm
data:
mysql.host: "mysql-svc"
mysql.db.name: "ry-config"
mysql.port: "3306"
mysql.user: "root"
mysql.password: "123456"
#headless service
apiVersion: v1
kind: Service
metadata:
name: nacos-svc
namespace: ruoyi
spec:
clusterIP: None
selector:
app: nacos
ports:
#8848是Nacos服务的默认端口,用于处理客户端的请求
- name: server
port: 8848
targetPort: 8848
#9848端口在Nacos集群模式下用于节点间的心跳检测
- name: client-rpc
port: 9848
targetPort: 9848
#9849端口主要用于Nacos集群模式下内部节点间进行数据复制等操作
- name: raft-rpc
port: 9849
targetPort: 9849
#兼容1.4.x版本的选举端口
- name: old-raft-rpc
port: 7848
targetPort: 7848
#Nacos作为服务注册与配置中心,必须要有一个稳定的地址,通过StatefulSet进行部署最佳
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: nacos
namespace: ruoyi
spec:
serviceName: "nacos-svc"
replicas: 3 #Nacos集群数量
selector:
matchLabels:
app: nacos
template:
metadata:
labels:
app: nacos
spec:
affinity:
podAntiAffinity:
# 设置为“尽量不要在同一节点上”,而不是强制分开
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100 # 优先级权重(1-100,越大优先级越高)
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values: ["nacos"]
topologyKey: "kubernetes.io/hostname"
initContainers:
- name: peer-finder-plugin-install
image: swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/nacos/nacos-peer-finder-plugin:1.1
imagePullPolicy: Always
volumeMounts:
- name: data
mountPath: /home/nacos/plugins/peer-finder
subPath: peer-finder
containers:
- name: nacos
image: swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/nacos/nacos-server:v2.1.1
resources:
requests:
memory: "100Mi"
cpu: "500m"
ports:
- name: client-port
containerPort: 8848
- name: client-rpc
containerPort: 9848
- name: raft-rpc
containerPort: 9849
- name: old-raft-rpc
containerPort: 7848
env:
#设置Nacos的运行模式,"cluster"表示Nacos在集群模式下运行
- name: MODE
value: "cluster"
#表示Nacos的版本号
- name: NACOS_VERSION
value: 2.1.1
#表示Nacos集群的副本数量
- name: NACOS_REPLICAS
value: "3"
#表示Nacos的service名称
- name: SERVICE_NAME
value: "nacos-svc"
#DNS前缀域名
- name: DOMAIN_NAME
value: "cluster.local"
#表示Nacos服务的端口号
- name: NACOS_SERVER_PORT
value: "8848"
#表示Nacos应用的端口号
- name: NACOS_APPLICATION_PORT
value: "8848"
#表示Nacos集群中的节点通信使用主机名而非IP地址
- name: PREFER_HOST_MODE
value: "hostname"
#POD_NAMESPACE环境变量用于设置为当前Pod所在的名称空间
- name: POD_NAMESPACE
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.namespace
#使用MySQL作为数据源
- name: SPRING_DATASOURCE_PLATFORM
value: "mysql"
#JDBC连接数据库后面的参数
- name: MYSQL_SERVICE_DB_PARAM
value: characterEncoding=utf8&connectTimeout=10000&socketTimeout=30000&autoReconnect=true&useSSL=false&allowPublicKeyRetrieval=true
#MySQL主机地址从configMap的key中获取
- name: MYSQL_SERVICE_HOST
valueFrom:
configMapKeyRef:
name: nacos-cm
key: mysql.host
#MySQL的库名称从configMap的key中获取
- name: MYSQL_SERVICE_DB_NAME
valueFrom:
configMapKeyRef:
name: nacos-cm
key: mysql.db.name
#MySQL的端口从configMap的key中获取
- name: MYSQL_SERVICE_PORT
valueFrom:
configMapKeyRef:
name: nacos-cm
key: mysql.port
#MySQL的用户名从configMap的key中获取
- name: MYSQL_SERVICE_USER
valueFrom:
configMapKeyRef:
name: nacos-cm
key: mysql.user
#MySQL的用户密码从configMap的key中获取
- name: MYSQL_SERVICE_PASSWORD
valueFrom:
configMapKeyRef:
name: nacos-cm
key: mysql.password
volumeMounts:
- name: data
mountPath: /home/nacos/plugins/peer-finder
subPath: peer-finder
- name: data
mountPath: /home/nacos/data
subPath: data
- name: data
mountPath: /home/nacos/logs
subPath: logs
#在pvc模板中按照实际情况指定动态存储的名称
volumeClaimTemplates:
- metadata:
name: data
namespace: ruoyi
spec:
storageClassName: "alicloud-disk-topology-alltype"
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 20Gi配置详解:
| 需要改的项 | 为什么改 |
|---|---|
namespace: ruoyi | 与实际命名空间一致;使用默认命名空间时可不写。 |
port: 8848 | 对外暴露的 Service 端口;调用方按此端口访问。 |
targetPort: 8848 | 必须与容器实际监听端口一致。 |
port: 9848 | 对外暴露的 Service 端口;调用方按此端口访问。 |
targetPort: 9848 | 必须与容器实际监听端口一致。 |
port: 9849 | 对外暴露的 Service 端口;调用方按此端口访问。 |
注意事项:
- 修改配置前先备份原文件,尤其是 SSH、Nginx、数据库和 Kubernetes 生产配置。
- YAML 缩进不能乱;同级字段要对齐,子字段要多缩进两个空格。
- 资源名称、命名空间、标签选择器要互相对应,否则资源创建了也可能找不到彼此。
- 镜像地址、版本号和拉取权限要确认,否则 Pod 会进入
ImagePullBackOff。
部署redis缓存服务
YAML 保存位置: ~/k8s-manifests/ruoyi/redis-config.yaml
先在 Kubernetes 管理节点执行:
mkdir -p ~/k8s-manifests/ruoyi
cd ~/k8s-manifests/ruoyi
vi redis-config.yaml把下方 YAML 全部复制进去;红色字段按当前环境修改;按 Esc 后输入 :wq 保存退出。
apiVersion: v1
kind: ConfigMap
metadata:
name: redis-config
namespace: ruoyi
data:
redis.conf: |
bind 0.0.0.0
protected-mode no
appendonly yes
requirepass 123456
apiVersion: v1
kind: Service
metadata:
name: ry-redis-svc
namespace: ruoyi
spec:
# StatefulSet 必须使用 headless service 才能给每个 Pod 分配 DNS 名称
clusterIP: None
selector:
app: ry-redis
ports:
- port: 6379
targetPort: 6379
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: ry-redis
namespace: ruoyi
spec:
serviceName: ry-redis-svc # 必须和上面的 Service 名一致
replicas: 1
selector:
matchLabels:
app: ry-redis
template:
metadata:
labels:
app: ry-redis
spec:
containers:
- name: redis
image: swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/redis:7.2.9
ports:
- containerPort: 6379
env:
- name: TZ
value: "Asia/Shanghai"
volumeMounts:
- name: data
mountPath: /data
- name: redis-config
mountPath: /usr/local/etc/redis/redis.conf
subPath: redis.conf
command: ["redis-server", "/usr/local/etc/redis/redis.conf"]
volumes:
- name: redis-config
configMap:
name: redis-config
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: "alicloud-disk-topology-alltype"
resources:
requests:
storage: 20Gi配置详解:
| 需要改的项 | 为什么改 |
|---|---|
namespace: ruoyi | 与实际命名空间一致;使用默认命名空间时可不写。 |
port: 6379 | 对外暴露的 Service 端口;调用方按此端口访问。 |
targetPort: 6379 | 必须与容器实际监听端口一致。 |
replicas: 1 | 按可用性和资源容量设置副本数。 |
image: swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/redis:7.2.9 | 替换为实际可拉取的镜像地址和版本。 |
accessModes: ["ReadWriteOnce"] | 按存储类型与读写需求确认。 |
注意事项:
- 修改配置前先备份原文件,尤其是 SSH、Nginx、数据库和 Kubernetes 生产配置。
- YAML 缩进不能乱;同级字段要对齐,子字段要多缩进两个空格。
- 资源名称、命名空间、标签选择器要互相对应,否则资源创建了也可能找不到彼此。
- 镜像地址、版本号和拉取权限要确认,否则 Pod 会进入
ImagePullBackOff。