4.7. 微服务商城实战
单体应用出错,看一个日志文件就够;微服务出错,你面对的是注册中心、配置中心、消息队列、缓存、搜索、对象存储和一条跨十个服务的调用链。本章按依赖顺序把这些组件装起来,再用 SkyWalking 把一次请求还原出来。
- 层级:
L4 · 实战
- 难度:
实战
- 预计:
90 分钟
- 前置:
StatefulSet、Ingress、存储、可观测性
4.7.1. 你将学到
mall 商城的组件拓扑与启动依赖,为什么顺序不能乱
Nacos 集群 + MySQL 主从的部署方式与注册/配置地址
Redis、RabbitMQ、MongoDB、MinIO、ElasticStack 的部署与健康检查
用 Cilium Ingress 与 MetalLB 暴露商家端
4.7.2. 为什么需要它
这套案例的价值不在「装八个中间件」,而在于让你体会依赖顺序和跨命名空间寻址。mall 的每个服务启动时都要连 Nacos 拿配置、注册自己,再连 MySQL/Redis/Mongo/ES/MinIO 干活。任何一个地址写错,表现都是 CrashLoopBackOff 或启动后 500,日志里往往只有一句 Connection refused。
组件 |
命名空间 |
工作负载 |
端口 |
存储 |
|---|---|---|---|---|
MySQL + Nacos |
|
StatefulSet ×2 与 ×3 |
3306 / 8848 |
10Gi/副本 |
Elasticsearch + Kibana |
|
StatefulSet ×3 / Deployment |
9200 / 5601 |
10Gi/副本 |
Redis + Sentinel |
|
StatefulSet ×3 |
6379 / 5000 |
10Gi/副本 |
RabbitMQ |
|
StatefulSet ×3 |
5672 / 15672 |
|
MongoDB / MinIO |
|
StatefulSet ×3 / ×4 |
27017 / 9000 |
10Gi/副本 |
SkyWalking |
|
Deployment |
11800 / 12800 / 8080 |
无(存 ES) |
mall 应用 |
|
Deployment ×7 |
8201 等 |
无 |
图:请求从网关进入业务服务,横切依赖是 Nacos、MySQL、Redis、MQ、ES、MinIO,链路数据汇入 OAP。
4.7.3. 核心概念
4.7.3.1. 依赖关系决定部署顺序
目录编号就是顺序:00-Prometheus → 01-Nacos → 02-ElasticStack → 03-Redis → 04-RabbitMQ → 05-MongoDB → 06-MinIO → 07-Skywalking,最后才是 mall-and-skywalking。两条硬依赖必须记住:
Nacos 依赖 MySQL。配置与注册信息落在
nacosdb,MySQL 没起来 Nacos 会一直重连。仓库把 MySQL 做成主从:mysql-0写、mysql-read读,Nacos 连mysql-0.mysql。SkyWalking OAP 依赖 Elasticsearch。
SW_STORAGE=elasticsearch、SW_STORAGE_ES_CLUSTER_NODES=elasticsearch.elastic.svc:9200,ES 未就绪时 OAP 的 readiness 一直失败。
4.7.3.2. 集群外的四项能力
清单假定集群已具备 Cilium(启用 Cilium Ingress)、MetalLB 与 OpenEBS 的 openebs-hostpath,可选 nfs-csi 和 Metrics Server。缺 Cilium 则 ingressClassName: cilium 不生效、域名 404;缺 MetalLB 则 LoadBalancer Service 永远 <pending>;缺 OpenEBS 则 PVC Pending、Pod 卡在 ContainerCreating;缺 Metrics Server 则 kubectl top 与 HPA 都不可用。
4.7.3.3. 地址与 Ingress 的两个坑
跨命名空间访问有两种写法,仓库里都出现过:nacos.nacos.svc:8848(省略 cluster.local,合法)和 mysql-0.mysql(只在同命名空间成立,跨命名空间要写全 mysql-0.mysql.nacos.svc.cluster.local)。
警告
同一个案例里混用了两套 Ingress
01-Nacos/06-nacos-ingress.yaml 同时写了 Cilium 注解和 ingressClassName: nginx;06-MinIO/04-ingress-minio.yaml 用 cilium;07-Skywalking 又回到 nginx。部署前用 kubectl get ingressclass 核对,把注解和 class 统一,否则会出现「Ingress 创建成功但不通」。
4.7.3.4. SkyWalking Agent 怎么进去
每个 Java 服务都用同一个套路:initContainer(apache/skywalking-java-agent:9.0.0-java8)把 /skywalking/agent 拷到 emptyDir;主容器挂载到 /skywalking;JAVA_TOOL_OPTIONS 加 -javaagent,SW_AGENT_COLLECTOR_BACKEND_SERVICES 指向 skywalking-oap.tracing.svc:11800。镜像不用改造,Agent 版本由 initContainer 控制。
4.7.3.5. 资源规划与观测串联
资源上先看 requests:Nacos 每副本 2Gi 内存、ES 每副本 1Gi heap、MinIO 四副本各 10Gi 卷,再加 Redis 与 Mongo,完整环境建议 3 台 4C8G 起步,否则 Pod 会大面积 Pending。
观测上,00-Prometheus 靠 prometheus.io/scrape 注解抓 Nacos actuator、redis-exporter(9121)、RabbitMQ(15692)、MongoDB(9216)、ES(9114) 与 MinIO;SkyWalking 负责 trace,Grafana 用 763、10991 等面板。指标看趋势、trace 看单次请求。
4.7.4. 动手实验
实验:按顺序部署基础服务,再让 mall 跑起来
目标:每个服务都有可用的 Endpoint,SkyWalking 能看到调用链,商家端能通过 Ingress 打开。
步骤 1 · 前置检查
kubectl get ingressclass,storageclass
kubectl get pods -n metallb-system
至少要看到 cilium 或 nginx 之一、openebs-hostpath,以及 MetalLB 的 controller/speaker Running。
步骤 2 · 部署 Nacos 与 MySQL
kubectl create namespace nacos
kubectl apply -f infra-services-with-prometheus/01-Nacos/01-secrets-mysql.yaml \
-f infra-services-with-prometheus/01-Nacos/02-mysql-persistent.yaml -n nacos
kubectl get pods -n nacos -w
MySQL 主从就绪后创建 Nacos 专用账号(Secret 中密码为 magedu.com),再部署 Nacos 三副本:
kubectl exec -n nacos mysql-0 -- mysql -uroot -e \
"GRANT ALL ON nacosdb.* TO 'nacos'@'%' IDENTIFIED BY 'magedu.com'; FLUSH PRIVILEGES;"
kubectl apply -f infra-services-with-prometheus/01-Nacos/03-nacos-cfg.yaml \
-f infra-services-with-prometheus/01-Nacos/04-nacos-persistent.yaml \
-f infra-services-with-prometheus/01-Nacos/05-nacos-service.yaml -n nacos
kubectl get endpoints nacos -n nacos
NAME ENDPOINTS AGE
nacos 10.244.1.37:8848,10.244.2.21:8848,10.244.3.18:8848 3m
三个地址说明 3 副本都通过了 readiness。若只有一两个,先看 kubectl logs nacos-1 -n nacos 里的数据库连接错误。
步骤 3 · 部署 ElasticStack
kubectl create namespace elastic
kubectl apply -f infra-services-with-prometheus/02-ElasticStack/01-elasticsearch-cluster-persistent.yaml -n elastic
kubectl rollout status statefulset/es-cluster -n elastic --timeout=600s
kubectl apply -f infra-services-with-prometheus/02-ElasticStack/02-fluentbit.yaml \
-f infra-services-with-prometheus/02-ElasticStack/03-kibana.yaml -n elastic
ES 的 initContainer 会 chown 数据目录并改 vm.max_map_count,需要 privileged;PodSecurity 为 restricted 的集群要先改节点级 sysctl。
步骤 4 · 部署 Redis 与 RabbitMQ
kubectl create namespace redis
kubectl apply -f infra-services-with-prometheus/03-Redis/ -n redis
kubectl get endpoints redis -n redis
NAME ENDPOINTS AGE
redis 10.244.1.42:6379,10.244.2.33:6379,10.244.3.27:6379 4m
Redis 的 master 是 redis-0.redis.redis.svc。接着部署 RabbitMQ,管理台默认 admin/magedu.com:
kubectl create namespace rabbit
kubectl apply -f infra-services-with-prometheus/04-RabbitMQ/ -n rabbit
kubectl get pods -n rabbit
mall 需要独立账号与 vhost:
kubectl exec -n rabbit rabbitmq-0 -- rabbitmqctl add_user malladmin magedu.com
kubectl exec -n rabbit rabbitmq-0 -- rabbitmqctl add_vhost /mall
kubectl exec -n rabbit rabbitmq-0 -- rabbitmqctl set_permissions -p /mall malladmin ".*" ".*" ".*"
步骤 5 · 部署 MongoDB 与 MinIO
kubectl create namespace mongo && kubectl apply -f infra-services-with-prometheus/05-MongoDB/ -n mongo
kubectl exec -n mongo mongodb-0 -- mongo --quiet --eval 'rs.status().ok'
返回 1 表示副本集已选主。再部署 MinIO:4 副本、纠删码模式,必须四个都就绪才能提供读写:
kubectl create namespace minio && kubectl apply -f infra-services-with-prometheus/06-MinIO/ -n minio
kubectl get pods -n minio
步骤 6 · 部署 SkyWalking
kubectl create namespace tracing
kubectl apply -f infra-services-with-prometheus/07-Skywalking/ -n tracing
kubectl get endpoints skywalking-oap skywalking-ui -n tracing
步骤 7 · 部署 mall 应用
kubectl create namespace mall
kubectl apply -f mall-and-skywalking/ -n mall
kubectl get pods -n mall
七个 Deployment 都应 1/1 Running。若某个服务反复重启,先看 spring.cloud.nacos.config.server-addr:示例写的是 nacos.nacos.svc:8848,只有 Nacos 在 nacos 命名空间时才成立。
这批清单的 Ingress 写的是 ingressClassName: cilium,而本环境只有 nginx。用一份补丁文件批量改:
spec:
ingressClassName: nginx
kubectl get ingress -n mall -o name | xargs -I{} kubectl patch {} -n mall --patch-file ingress-class-patch.yaml
步骤 8 · 用 MetalLB + Cilium Ingress 暴露商家端
kubectl get ingress -n mall
curl -I http://mall-admin-web.magedu.com
Service 的 EXTERNAL-IP 为 <none> 不影响访问:Cilium 会用自管的 LB IP 做入口,把域名解析过去即可。
步骤 9 · 在 SkyWalking UI 里看链路
访问 http://skywalking.magedu.com,服务列表里应出现 mall-gateway、mall-auth。点一次商家端登录再看拓扑与 Trace;没数据先查 OAP Endpoint、SW_AGENT_COLLECTOR_BACKEND_SERVICES 和 ES 里的 sw_* 索引。
4.7.4.1. 验证清单
kubectl get endpoints -n nacos nacos有三个地址kubectl get endpoints redis -n redis三个地址,redis-0为 masterkubectl get pods -n mall全部1/1 Running且无重启curl -I http://mall-admin-web.magedu.com返回 200
4.7.5. 常见问题与排错
现象 |
可能原因 |
处理方式 |
|---|---|---|
Nacos |
MySQL 未就绪; |
先建库建用户,再看日志里的 JDBC 报错 |
Nacos 只有 1 个副本就绪 |
peer-finder 初始化失败;PVC 不足 |
看 |
SkyWalking 无数据 |
Agent 未注入;OAP 地址错;ES 磁盘满 |
查启动日志里的 |
OAP readiness 一直失败 |
Elasticsearch 未就绪或索引创建失败 |
先 |
备注
旧 API 检查
本目录的 apiVersion 基本都能在 v1.31 直接用:StatefulSet apps/v1、Ingress networking.k8s.io/v1、RBAC rbac.authorization.k8s.io/v1。若你从旧教程抄到 extensions/v1beta1 的 Ingress 或 apps/v1beta1 的 StatefulSet,API Server 会直接拒绝;batch/v1beta1 的 CronJob 也已移除,应改为 batch/v1。
4.7.6. 进阶挑战
给 Nacos 加 Prometheus 抓取(
/nacos/actuator/prometheus),在 Grafana 做一个「注册实例数」面板。把 mall-gateway 扩到 3 副本,观察 SkyWalking 拓扑里同一服务的多实例是否被正确聚合。
4.7.7. 小结
顺序不是形式:MySQL 先于 Nacos,ES 先于 OAP,基础服务先于 mall。
SkyWalking 的接入点是 initContainer +
-javaagent,链路能否出现取决于 OAP、Agent 地址与 ES 三者。