跳转至

微服务商城实战

单体应用出错,看一个日志文件就够;微服务出错,你面对的是注册中心、配置中心、消息队列、缓存、搜索、对象存储和一条跨十个服务的调用链。本章按依赖顺序把这些组件装起来,再用 SkyWalking 把一次请求还原出来。

难度 · 实战 预计 90 分钟 前置:StatefulSet、Ingress、存储、可观测性

你将学到

  • mall 商城的组件拓扑与启动依赖,为什么顺序不能乱
  • Nacos 集群 + MySQL 主从的部署方式与注册/配置地址
  • Redis、RabbitMQ、MongoDB、MinIO、ElasticStack 的部署与健康检查
  • 用 Cilium Ingress 与 MetalLB 暴露商家端

为什么需要它

这套案例的价值不在「装八个中间件」,而在于让你体会依赖顺序和跨命名空间寻址。mall 的每个服务启动时都要连 Nacos 拿配置、注册自己,再连 MySQL/Redis/Mongo/ES/MinIO 干活。任何一个地址写错,表现都是 CrashLoopBackOff 或启动后 500,日志里往往只有一句 Connection refused。

组件 命名空间 工作负载 端口 存储
MySQL + Nacos nacos StatefulSet ×2 与 ×3 3306 / 8848 10Gi/副本
Elasticsearch + Kibana elastic StatefulSet ×3 / Deployment 9200 / 5601 10Gi/副本
Redis + Sentinel redis StatefulSet ×3 6379 / 5000 10Gi/副本
RabbitMQ rabbit StatefulSet ×3 5672 / 15672 emptyDir
MongoDB / MinIO mongo / minio StatefulSet ×3 / ×4 27017 / 9000 10Gi/副本
SkyWalking tracing Deployment 11800 / 12800 / 8080 无(存 ES)
mall 应用 mall Deployment ×7 8201 等 无
flowchart LR
  U[商家端浏览器] --> G[mall-gateway]
  G --> B[mall-auth portal admin search]
  B --> N[Nacos]
  B --> M[(MySQL)]
  B --> R[(Redis)]
  B --> Q[RabbitMQ]
  B --> E[(Elasticsearch)]
  B --> O[(MinIO)]
  B -.agent.-> OAP[SkyWalking OAP]
  OAP --> E

图:请求从网关进入业务服务,横切依赖是 Nacos、MySQL、Redis、MQ、ES、MinIO,链路数据汇入 OAP。

核心概念

依赖关系决定部署顺序

目录编号就是顺序:00-Prometheus → 01-Nacos → 02-ElasticStack → 03-Redis → 04-RabbitMQ → 05-MongoDB → 06-MinIO → 07-Skywalking,最后才是 mall-and-skywalking。两条硬依赖必须记住:

  1. Nacos 依赖 MySQL。配置与注册信息落在 nacosdb,MySQL 没起来 Nacos 会一直重连。仓库把 MySQL 做成主从:mysql-0 写、mysql-read 读,Nacos 连 mysql-0.mysql。
  2. SkyWalking OAP 依赖 Elasticsearch。SW_STORAGE=elasticsearch、SW_STORAGE_ES_CLUSTER_NODES=elasticsearch.elastic.svc:9200,ES 未就绪时 OAP 的 readiness 一直失败。

集群外的四项能力

清单假定集群已具备 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 都不可用。

地址与 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 创建成功但不通」。

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 控制。

资源规划与观测串联

资源上先看 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 看单次请求。

动手实验

实验:按顺序部署基础服务,再让 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。用一份补丁文件批量改:

ingress-class-patch.yaml
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_* 索引。

验证清单

  • kubectl get endpoints -n nacos nacos 有三个地址
  • kubectl get endpoints redis -n redis 三个地址,redis-0 为 master
  • kubectl get pods -n mall 全部 1/1 Running 且无重启
  • curl -I http://mall-admin-web.magedu.com 返回 200

常见问题与排错

现象 可能原因 处理方式
Nacos CrashLoopBackOff MySQL 未就绪;nacosdb 或账号未创建 先建库建用户,再看日志里的 JDBC 报错
Nacos 只有 1 个副本就绪 peer-finder 初始化失败;PVC 不足 看 kubectl logs nacos-1 -n nacos;确认 SC 可动态置备
SkyWalking 无数据 Agent 未注入;OAP 地址错;ES 磁盘满 查启动日志里的 SkyWalking agent started
OAP readiness 一直失败 Elasticsearch 未就绪或索引创建失败 先 curl es-cluster-0.elasticsearch.elastic.svc:9200

旧 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。

进阶挑战

  1. 给 Nacos 加 Prometheus 抓取(/nacos/actuator/prometheus),在 Grafana 做一个「注册实例数」面板。
  2. 把 mall-gateway 扩到 3 副本,观察 SkyWalking 拓扑里同一服务的多实例是否被正确聚合。

小结

  • 顺序不是形式:MySQL 先于 Nacos,ES 先于 OAP,基础服务先于 mall。
  • SkyWalking 的接入点是 initContainer + -javaagent,链路能否出现取决于 OAP、Agent 地址与 ES 三者。

延伸阅读