上一篇【第16篇】SkyWalking UI使用手册:学会阅读指挥中心的作战地图
下一篇【第18篇】用SkyWalking观察微服务的各个维度:从宏观到微观的侦察术


前16篇我们学会了SkyWalking的所有理论——基因改造针、指挥中心、档案馆、作战地图。但理论再好,不上战场都是纸上谈兵。

今天我们要打造一个真实的微服务沙场——用Spring Cloud搭建5个微服务,配上SkyWalking的完整监控链路,让所有理论在实践中运转起来。

1. 实战集群架构设计

1.1 微服务架构

我们模拟一个电商系统,包含5个核心服务:

┌──────────────────────────────────────────────────────────────────┐
│              实战环境 - 电商微服务架构                               │
│                                                                  │
│  ┌─────── User ────────┐                                         │
│  │  user-gateway       │ ← Spring Cloud Gateway (网关)          │
│  │  端口: 8080         │                                         │
│  └─────────────────────┘                                         │
│          │                                                       │
│          │ 路由分发                                              │
│          ▼                                                       │
│  ┌──── user-service ────┐  ← 用户服务                           │
│  │  端口: 8081          │                                        │
│  │  功能: 注册/登录/查询 │                                        │
│  └──────────────────────┘                                        │
│          │                                                       │
│          ├──→ ┌─── order-service ──┐  ← 订单服务                 │
│          │    │ 端口: 8082         │                              │
│          │    │ 功能: 下单/查询     │                              │
│          │    └────────────────────┘                              │
│          │         │                                              │
│          │         ├──→ ┌── payment-service ──┐ ← 支付服务      │
│          │         │    │ 端口: 8083           │                 │
│          │         │    │ 功能: 支付/退款       │                 │
│          │         │    └─────────────────────┘                 │
│          │         │                                              │
│          │         └──→ ┌── inventory-service ──┐ ← 库存服务    │
│          │              │ 端口: 8084             │               │
│          │              │ 功能: 库存查询/扣减     │               │
│          │              └────────────────────────┘               │
│                                                                  │
│  ┌─── 基础设施 ────────────────────────────────────────┐        │
│  │  Nacos: 服务注册与发现 (8848)                        │        │
│  │  MySQL: 业务数据库 (3306)                            │        │
│  │  Redis: 缓存 (6379)                                 │        │
│  │  RocketMQ: 消息队列 (9876)                          │        │
│  └──────────────────────────────────────────────────────┘        │
│                                                                  │
│  ┌─── SkyWalking ──────────────────────────────────────┐        │
│  │  OAP Server: 数据分析 (11800/12800)                  │        │
│  │  ES: 数据存储 (9200)                                │        │
│  │  UI: 可视化 (8080-sw)                               │        │
│  └──────────────────────────────────────────────────────┘        │
└──────────────────────────────────────────────────────────────────┘

1.2 调用链路设计

一个下单请求的典型链路:

用户下单请求的完整链路:

user-gateway (路由)
  → user-service (验证用户身份)
    → order-service (创建订单)
      → inventory-service (扣减库存)
      → payment-service (发起支付)
        → MySQL (写入支付记录)
      → RocketMQ (发送订单通知)

1.3 技术选型

组件技术版本
网关Spring Cloud Gateway3.1.x
服务框架Spring Boot2.7.x
注册中心Nacos2.2.x
数据库MySQL8.0
缓存Redis7.x
消息队列RocketMQ5.x
监控SkyWalking9.3.0
存储Elasticsearch8.12

2. 各服务的Agent配置

2.1 服务名规划

服务名是Agent最重要的配置——它决定了应用在SkyWalking中的身份标识。规划原则:

┌──────────────────────────────────────────────────────────────────┐
│              服务名规划原则                                         │
│                                                                  │
│  1. 使用业务语义名,不使用技术名                                   │
│     好的: user-service, order-center                             │
│     差的: springboot-app1, svc-8082                              │
│                                                                  │
│  2. 同一服务的多个实例用相同名字                                   │
│     实例1: user-service                                          │
│     实例2: user-service (相同名字,SkyWalking自动区分实例)         │
│                                                                  │
│  3. 不同层次用不同命名风格                                       │
│     网关层: xxx-gateway                                          │
│     业务层: xxx-service                                          │
│     基础层: xxx-center                                           │
│                                                                  │
│  4. 避免特殊字符和空格                                           │
│     user-service ✓                                               │
│     User Service ×                                               │
│     user.service ×                                               │
└──────────────────────────────────────────────────────────────────┘

我们的服务名规划:

服务Agent服务名端口
user-gatewayecommerce-gateway8080
user-serviceuser-service8081
order-serviceorder-service8082
payment-servicepayment-service8083
inventory-serviceinventory-service8084

2.2 各服务Agent配置

每个服务的Agent配置核心参数:

# ==================== user-gateway ====================
agent.service_name=ecommerce-gateway
collector.backend_service=skywalking-oap:11800
agent.sample_n_per_3_secs=-1
logging.level=WARN

# ==================== user-service ====================
agent.service_name=user-service
collector.backend_service=skywalking-oap:11800
agent.sample_n_per_3_secs=-1
logging.level=WARN

# ==================== order-service ====================
agent.service_name=order-service
collector.backend_service=skywalking-oap:11800
agent.sample_n_per_3_secs=-1
logging.level=WARN

# ==================== payment-service ====================
agent.service_name=payment-service
collector.backend_service=skywalking-oap:11800
agent.sample_n_per_3_secs=-1
logging.level=WARN

# ==================== inventory-service ====================
agent.service_name=inventory-service
collector.backend_service=skywalking-oap:11800
agent.sample_n_per_3_secs=-1
logging.level=WARN

2.3 网关服务的特殊处理

Spring Cloud Gateway是基于WebFlux的响应式框架,需要额外激活Gateway插件:

# 激活Gateway插件
mv optional-plugins/apm-spring-cloud-gateway-2.x-plugin.jar plugins/

# 同时需要激活WebFlux插件(如果Gateway使用WebFlux)
mv optional-plugins/apm-spring-webflux-5.x-plugin.jar plugins/

2.4 Agent配置的统一管理

在Docker Compose环境中,使用共享Agent卷 + 环境变量覆盖:

# docker-compose.yml - Agent配置
services:
  user-service:
    image: ecommerce/user-service:latest
    environment:
      - SW_AGENT_NAME=user-service
      - SW_AGENT_COLLECTOR_BACKEND_SERVICES=skywalking-oap:11800
      - SW_LOGGING_LEVEL=WARN
    volumes:
      - skywalking-agent:/skywalking-agent:ro
    command: >
      java -javaagent:/skywalking-agent/skywalking-agent.jar
      -jar /app/user-service.jar

3. Kubernetes部署时Agent挂载方式

3.1 为什么K8s需要特殊处理

在Docker Compose中,我们可以直接把Agent目录挂载进去。但在K8s中,应用镜像通常不包含Agent——需要一种优雅的方式把Agent注入容器。

3.2 initContainer方式(推荐)

initContainer是K8s的标准机制——在主容器启动前,先运行一个初始化容器,把Agent文件复制到共享卷中:

┌──────────────────────────────────────────────────────────────────┐
│              K8s initContainer Agent挂载流程                        │
│                                                                  │
│  Pod启动顺序:                                                    │
│                                                                  │
│  1. initContainer运行                                             │
│     ┌───────────────────────────────────────────┐                │
│     │  skywalking-agent-init                    │                │
│     │  - 从Agent镜像复制文件到共享卷              │                │
│     │  - cp -r /skywalking-agent /agent-volume  │                │
│     │  - 容器退出                                │                │
│     └───────────────────────────────────────────┘                │
│         │                                                         │
│         │ 文件已复制到共享卷                                       │
│         ▼                                                         │
│  2. 主容器运行                                                    │
│     ┌───────────────────────────────────────────┐                │
│     │  user-service                              │                │
│     │  - 挂载共享卷到/skywalking-agent            │                │
│     │  - -javaagent:/skywalking-agent/agent.jar  │                │
│     │  - 应用启动                                │                │
│     └───────────────────────────────────────────┘                │
│                                                                  │
│  共享卷 (emptyDir):                                               │
│  initContainer写入 → 主容器读取 → Agent文件在两个容器间共享        │
└──────────────────────────────────────────────────────────────────┘

3.3 完整K8s Deployment示例

# Kubernetes Deployment - user-service with SkyWalking Agent
apiVersion: apps/v1
kind: Deployment
metadata:
  name: user-service
  labels:
    app: user-service
spec:
  replicas: 2
  selector:
    matchLabels:
      app: user-service
  template:
    metadata:
      labels:
        app: user-service
    spec:
      # initContainer: 复制Agent文件
      initContainers:
      - name: skywalking-agent-init
        image: apache/skywalking-java-agent:9.3.0-jdk11
        command: ["sh", "-c", "cp -r /skywalking-agent /agent"]
        volumeMounts:
        - name: agent-volume
          mountPath: /agent
      
      # 主容器: 运行应用
      containers:
      - name: user-service
        image: ecommerce/user-service:latest
        env:
        - name: SW_AGENT_NAME
          value: "user-service"
        - name: SW_AGENT_COLLECTOR_BACKEND_SERVICES
          value: "skywalking-oap:11800"
        - name: SW_LOGGING_LEVEL
          value: "WARN"
        volumeMounts:
        - name: agent-volume
          mountPath: /skywalking-agent
        command:
        - java
        - -javaagent:/skywalking-agent/skywalking-agent.jar
        - -jar
        - /app/user-service.jar
        ports:
        - containerPort: 8081
      
      # 共享卷
      volumes:
      - name: agent-volume
        emptyDir: {}

3.4 ConfigMap管理公共配置

# SkyWalking公共配置 ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: skywalking-agent-config
data:
  SW_AGENT_COLLECTOR_BACKEND_SERVICES: "skywalking-oap:11800"
  SW_LOGGING_LEVEL: "WARN"
  SW_AGENT_SAMPLE_N_PER_3_SECS: "-1"

所有服务都可以引用这个ConfigMap:

# Deployment中引用ConfigMap
envFrom:
- configMapRef:
    name: skywalking-agent-config
env:
- name: SW_AGENT_NAME
  value: "user-service"  # 每个服务单独设置

3.5 Sidecar方式(备选)

除了initContainer,还可以用Sidecar方式挂载Agent——一个Pod中运行两个容器:

# Sidecar方式(不推荐,资源浪费)
spec:
  containers:
  - name: user-service
    image: ecommerce/user-service:latest
    command: ["java", "-javaagent:/shared/skywalking-agent.jar", "-jar", "app.jar"]
    volumeMounts:
    - name: shared
      mountPath: /shared
  - name: skywalking-agent-sidecar
    image: apache/skywalking-java-agent:9.3.0-jdk11
    # Sidecar容器只提供Agent文件
    volumeMounts:
    - name: shared
      mountPath: /shared
  volumes:
  - name: shared
    emptyDir: {}

Sidecar方式的问题是——Sidecar容器会一直运行,浪费资源。initContainer方式只在启动时运行一次,更高效。

4. Docker Compose完整实战环境

4.1 完整docker-compose.yml

# docker-compose.yml - 完整实战环境
version: '3.8'
services:
  # ==================== 基础设施 ====================
  
  # Nacos注册中心
  nacos:
    image: nacos/nacos-server:v2.2.3
    environment:
      MODE: standalone
    ports:
      - "8848:8848"
  
  # MySQL数据库
  mysql:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: root123
      MYSQL_DATABASE: ecommerce
    ports:
      - "3306:3306"
    volumes:
      - mysql-data:/var/lib/mysql
  
  # Redis缓存
  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"
  
  # Elasticsearch
  elasticsearch:
    image: elasticsearch:8.12.0
    environment:
      - discovery.type=single-node
      - "ES_JAVA_OPTS=-Xms1g -Xmx1g"
      - xpack.security.enabled=false
    ports:
      - "9200:9200"
    volumes:
      - es-data:/usr/share/elasticsearch/data
  
  # ==================== SkyWalking ====================
  
  # OAP Server
  skywalking-oap:
    image: apache/skywalking-oap-server:9.3.0
    environment:
      - SW_STORAGE=elasticsearch
      - SW_CLUSTER_NODES=elasticsearch:9200
      - SW_CORE_RECORD_DATA_TTL=3
      - SW_CORE_OTHER_METRICS_DATA_TTL=7
    depends_on:
      - elasticsearch
    ports:
      - "11800:11800"
      - "12800:12800"
  
  # SkyWalking UI
  skywalking-ui:
    image: apache/skywalking-ui:9.3.0
    environment:
      - SW_OAP_ADDRESS=skywalking-oap:12800
    depends_on:
      - skywalking-oap
    ports:
      - "9090:8080"
  
  # ==================== 业务服务 ====================
  
  # 用户网关
  user-gateway:
    build:
      context: ./user-gateway
      dockerfile: Dockerfile
    environment:
      - SW_AGENT_NAME=ecommerce-gateway
      - SW_AGENT_COLLECTOR_BACKEND_SERVICES=skywalking-oap:11800
      - SW_LOGGING_LEVEL=WARN
      - NACOS_ADDR=nacos:8848
    depends_on:
      - nacos
      - skywalking-oap
    ports:
      - "8080:8080"
  
  # 用户服务
  user-service:
    build:
      context: ./user-service
      dockerfile: Dockerfile
    environment:
      - SW_AGENT_NAME=user-service
      - SW_AGENT_COLLECTOR_BACKEND_SERVICES=skywalking-oap:11800
      - SW_LOGGING_LEVEL=WARN
      - NACOS_ADDR=nacos:8848
      - MYSQL_HOST=mysql
      - REDIS_HOST=redis
    depends_on:
      - nacos
      - mysql
      - redis
      - skywalking-oap
    ports:
      - "8081:8081"
  
  # 订单服务
  order-service:
    build:
      context: ./order-service
      dockerfile: Dockerfile
    environment:
      - SW_AGENT_NAME=order-service
      - SW_AGENT_COLLECTOR_BACKEND_SERVICES=skywalking-oap:11800
      - SW_LOGGING_LEVEL=WARN
      - NACOS_ADDR=nacos:8848
      - MYSQL_HOST=mysql
    depends_on:
      - nacos
      - mysql
      - skywalking-oap
    ports:
      - "8082:8082"
  
  # 支付服务
  payment-service:
    build:
      context: ./payment-service
      dockerfile: Dockerfile
    environment:
      - SW_AGENT_NAME=payment-service
      - SW_AGENT_COLLECTOR_BACKEND_SERVICES=skywalking-oap:11800
      - SW_LOGGING_LEVEL=WARN
      - NACOS_ADDR=nacos:8848
      - MYSQL_HOST=mysql
    depends_on:
      - nacos
      - mysql
      - skywalking-oap
    ports:
      - "8083:8083"
  
  # 库存服务
  inventory-service:
    build:
      context: ./inventory-service
      dockerfile: Dockerfile
    environment:
      - SW_AGENT_NAME=inventory-service
      - SW_AGENT_COLLECTOR_BACKEND_SERVICES=skywalking-oap:11800
      - SW_LOGGING_LEVEL=WARN
      - NACOS_ADDR=nacos:8848
      - MYSQL_HOST=mysql
      - REDIS_HOST=redis
    depends_on:
      - nacos
      - mysql
      - redis
      - skywalking-oap
    ports:
      - "8084:8084"

volumes:
  mysql-data:
  es-data:

4.2 业务服务的Dockerfile模板

每个服务的Dockerfile需要包含Agent挂载:

# user-service/Dockerfile
FROM openjdk:11-jre-slim

# 复制应用jar包
COPY target/user-service.jar /app/user-service.jar

# 复制SkyWalking Agent
COPY skywalking-agent /skywalking-agent

# 激活Gateway插件(如果需要)
# RUN mv /skywalking-agent/optional-plugins/apm-xxx-plugin.jar /skywalking-agent/plugins/

ENTRYPOINT ["java", \
            "-javaagent:/skywalking-agent/skywalking-agent.jar", \
            "-jar", "/app/user-service.jar"]

5. 验证追踪数据正常上报

5.1 启动环境

# 启动所有服务
docker-compose up -d

# 查看服务状态
docker-compose ps

# 查看OAP日志
docker-compose logs skywalking-oap | tail -20

# 查看Agent日志
docker-compose logs user-service | grep "skywalking"

5.2 验证步骤

┌──────────────────────────────────────────────────────────────────┐
│              追踪数据验证流程                                       │
│                                                                  │
│  步骤一:验证OAP正常启动                                           │
│  └──→ curl http://localhost:12800/api/status                    │
│  └──→ 返回200表示OAP正常                                          │
│                                                                  │
│  步骤二:验证UI可访问                                              │
│  └──→ 打开 http://localhost:9090                                 │
│  └──→ 能看到Dashboard表示UI正常                                   │
│                                                                  │
│  步骤三:发送测试请求                                              │
│  └──→ curl http://localhost:8080/api/users/1                    │
│  └──→ curl http://localhost:8080/api/orders/create              │
│  └──→ 多发几个请求,确保有足够数据                                  │
│                                                                  │
│  步骤四:查看UI中的拓扑图                                          │
│  └──→ 打开UI → General → Service                                 │
│  └──→ 拓扑图应显示5个服务节点和它们的调用关系                       │
│                                                                  │
│  步骤五:查看Trace数据                                             │
│  └──→ 打开UI → Trace                                             │
│  └──→ 应能看到刚才发送的请求的Trace                                │
│  └──→ Trace中应包含多个Span(网关→用户→订单→库存→支付)            │
│                                                                  │
│  步骤六:查看Agent日志                                             │
│  └──→ docker-compose logs user-service                           │
│  └──→ 日志中应包含TraceID信息                                      │
│  └──→ 格式: TID:xxx PROC:xxx                                     │
└──────────────────────────────────────────────────────────────────┘

5.3 常见问题排查

问题检查方法常见原因
UI看不到服务检查OAP日志Agent没连上OAP(检查backend_service地址)
拓扑图不显示链路检查调用是否经过服务之间没有互相调用
Trace不完整检查Agent插件缺少必要的插件(如Gateway插件)
数据延迟显示检查OAP聚合周期聚合周期是500ms,数据不会立即出现
只有部分Span检查采样率采样率过低导致部分请求未被追踪

5.4 应用日志中的TraceID

如果Agent正常工作,应用日志中会自动输出TraceID:

# 查看user-service日志中的TraceID
docker-compose logs user-service | grep "TID"

# 日志格式示例:
# 2026-07-02 14:30:00 INFO [TID:a3b2c1d4e5] UserService.getUser - userId=123
# 2026-07-02 14:30:00 INFO [TID:a3b2c1d4e5] UserService.queryDB - SQL=SELECT...

在Spring Boot中,需要添加logback的TraceID输出格式:

<!-- logback-spring.xml -->
<configuration>
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <!-- %tid输出TraceID -->
            <pattern>%d{yyyy-MM-dd HH:mm:ss} [%tid] %-5level %logger - %msg%n</pattern>
        </encoder>
    </appender>
    
    <root level="INFO">
        <appender-ref ref="CONSOLE"/>
    </root>
</configuration>

6. 小结

实战环境搭建是把理论变成实践的关键一步:

  • 集群架构:5个微服务 + 基础设施 + SkyWalking全链路
  • 服务名规划:业务语义命名,同服务多实例同名
  • Agent配置:环境变量覆盖方式,统一管理
  • K8s挂载:initContainer方式是标准做法,比Sidecar更高效
  • Docker Compose:完整的本地实战环境
  • 数据验证:OAP状态 → UI访问 → 发请求 → 看拓扑 → 看Trace → 看日志

下一篇文章,我们将用这个实战环境来观察微服务的各个维度——从告警出发,一步步定位问题。


上一篇【第16篇】SkyWalking UI使用手册:学会阅读指挥中心的作战地图
下一篇【第18篇】用SkyWalking观察微服务的各个维度:从宏观到微观的侦察术


更多推荐