上一篇【第23篇】SkyWalking与单体应用——老系统也能焕发新活力
下一篇【第25篇】SkyWalking的内存消息队列DataCarrier——高性能数据中转站


1. 引言:从"单机部署"到"集群编排"

上一篇文章我们聊了SkyWalking在单体应用中的价值。但是,当你的系统从一台服务器变成几十个Pod在Kubernetes集群中飘来飘去的时候,问题来了:

  • Agent怎么装到每个Pod里?总不能让人手动SSH进容器吧?
  • Pod的IP是动态变化的,OAP Server怎么找到Agent?
  • Pod重启后IP变了,Trace会不会断?
  • 滚动更新时,新旧Pod共存,监控数据会不会乱?

这篇文章就是要回答这些问题。我们将完整走一遍SkyWalking在Kubernetes上的部署实战。

2. K8s上的SkyWalking架构设计

2.1 整体部署架构

+------------------------------------------------------------------+
|           SkyWalking on Kubernetes 部署架构                        |
+------------------------------------------------------------------+
|                                                                    |
|  ┌─────────────────── Kubernetes Cluster ──────────────────────┐  |
|  │                                                              │  |
|  │  ┌──────────────────── Namespace: monitoring ───────────┐   │  |
|  │  │                                                       │   │  |
|  │  │  ┌─────────────┐    ┌─────────────┐                   │   │  |
|  │  │  │ OAP Pod-1   │    │ OAP Pod-2   │  StatefulSet      │   │  |
|  │  │  │ gRPC:11800  │    │ gRPC:11800  │                   │   │  |
|  │  │  │ HTTP:12800  │    │ HTTP:12800  │                   │   │  |
|  │  │  └──────┬──────┘    └──────┬──────┘                   │   │  |
|  │  │         └────────┬────────┘                           │   │  |
|  │  │                  ▼                                     │   │  |
|  │  │         ┌──────────────┐                               │   │  |
|  │  │         │   UI Pod     │  Deployment                   │   │  |
|  │  │         │   Port:8080  │                               │   │  |
|  │  │         └──────────────┘                               │   │  |
|  │  │                                                       │   │  |
|  │  │  ┌──────────────────────────────────────────────────┐ │   │  |
|  │  │  │          Elasticsearch Cluster (可选)              │ │   │  |
|  │  │  │          Storage: 指标 + Trace数据                  │ │   │  |
|  │  │  └──────────────────────────────────────────────────┘ │   │  |
|  │  └───────────────────────────────────────────────────────┘   │  |
|  │                                                              │  |
|  │  ┌─────────── Namespace: production ─────────────────────┐   │  |
|  │  │                                                        │   │  |
|  │  │  ┌──────────┐  ┌──────────┐  ┌──────────┐             │   │  |
|  │  │  │ App Pod-1│  │ App Pod-2│  │ App Pod-3│             │   │  |
|  │  │  │ ┌──────┐ │  │ ┌──────┐ │  │ ┌──────┐ │             │   │  |
|  │  │  │ │ Agent│ │  │ │ Agent│ │  │ │ Agent│ │             │   │  |
|  │  │  │ └──┬───┘ │  │ └──┬───┘ │  │ └──┬───┘ │             │   │  |
|  │  │  └────┼─────┘  └────┼─────┘  └────┼─────┘             │   │  |
|  │  │       └──────────────┼─────────────┘                   │   │  |
|  │  │                      │ gRPC 11800                      │   │  |
|  │  └──────────────────────┼─────────────────────────────────┘   │  |
|  │                         │                                     │  |
|  └─────────────────────────┼─────────────────────────────────────┘  |
|                            │                                        |
|                   ┌────────▼────────┐                               |
|                   │  Service: OAP   │                               |
|                   │  ClusterIP:None │                               |
|                   └─────────────────┘                               |
+------------------------------------------------------------------+

2.2 核心组件说明

组件 K8s资源类型 说明
OAP Server StatefulSet 有状态服务,需要稳定的网络标识和存储
UI Deployment 无状态Web服务,可水平扩展
Elasticsearch StatefulSet 持久化存储Trace、指标数据
Agent InitContainer/Sidecar 与应用容器共享卷,注入探针

3. Helm Chart一键部署

3.1 添加Helm仓库

# 添加SkyWalking官方Helm仓库
helm repo add skywalking https://apache.jfrog.io/artifactory/skywalking-helm
helm repo update

# 查看可用版本
helm search repo skywalking -l

3.2 自定义values.yaml

# skywalking-values.yaml
oap:
  image:
    tag: 9.7.0
  replicas: 2
  resources:
    requests:
      cpu: 2
      memory: 4Gi
    limits:
      cpu: 4
      memory: 8Gi
  
  # 使用Elasticsearch作为存储
  storageType: elasticsearch
  env:
    SW_STORAGE_ES_CLUSTER_NODES: "elasticsearch-master:9200"
    SW_STORAGE_ES_USER: "elastic"
    SW_STORAGE_ES_PASSWORD: "changeme"
  
  # 暴露gRPC和HTTP端口
  ports:
    grpc: 11800
    http: 12800

ui:
  image:
    tag: 9.7.0
  service:
    type: ClusterIP
    port: 80
  # 如果需要外部访问,可以改为NodePort或LoadBalancer

elasticsearch:
  enabled: true
  replicas: 3
  minimumMasterNodes: 2
  volumeClaimTemplate:
    resources:
      requests:
        storage: 100Gi

3.3 安装部署

# 创建命名空间
kubectl create namespace skywalking

# 安装(将命名改为skywalking)
helm install skywalking skywalking/skywalking \
  -n skywalking \
  -f skywalking-values.yaml

# 查看部署状态
kubectl get pods -n skywalking

# 输出示例:
# NAME                                    READY   STATUS    RESTARTS
# skywalking-oap-0                        1/1     Running   0
# skywalking-oap-1                        1/1     Running   0
# skywalking-ui-7d8f9b5c-abc12            1/1     Running   0
# skywalking-elasticsearch-master-0       1/1     Running   0
# skywalking-elasticsearch-master-1       1/1     Running   0
# skywalking-elasticsearch-master-2       1/1     Running   0

# 端口转发访问UI
kubectl port-forward svc/skywalking-ui 8080:80 -n skywalking

4. InitContainer方式挂载Agent

4.1 为什么要用InitContainer

在K8s中给应用注入Agent有三种主流方式:

+------------------------------------------------------------------+
|        Agent注入方式对比                                          |
+------------------------------------------------------------------+
|                                                                    |
|  方式             |  原理              |  优点         |  缺点      |
+------------------------------------------------------------------+
|  InitContainer    | 先用init容器拷贝  |  镜像无侵入   |  启动稍慢  |
|  (推荐)           | Agent到共享卷     |  易于维护     |            |
+------------------------------------------------------------------+
|  基础镜像预装     | Dockerfile中      |  启动快       |  镜像耦合  |
|                   | 直接COPY Agent    |               |  Agent升级 |
|                   |                   |               |  需重构建   |
+------------------------------------------------------------------+
|  Sidecar容器      | 同Pod的另一个     |  完全解耦     |  资源开销  |
|                   | 容器运行Agent     |               |  大        |
+------------------------------------------------------------------+

4.2 InitContainer配置示例

# app-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: order-service
  namespace: production
spec:
  replicas: 3
  selector:
    matchLabels:
      app: order-service
  template:
    metadata:
      labels:
        app: order-service
    spec:
      # ① InitContainer:拷贝Agent到共享卷
      initContainers:
      - name: skywalking-agent-init
        image: apache/skywalking-java-agent:9.3.0
        imagePullPolicy: IfNotPresent
        command:
        - sh
        - -c
        - |
          set -ex
          # 拷贝Agent到共享挂载卷
          cp -r /skywalking/agent/* /skywalking-agent/
          echo "SkyWalking Agent copied successfully"
        volumeMounts:
        - name: skywalking-agent
          mountPath: /skywalking-agent
      
      # ② 应用容器
      containers:
      - name: order-service
        image: myregistry/order-service:v1.2.3
        ports:
        - containerPort: 8080
        env:
        # ③ 环境变量传递配置
        - name: SW_AGENT_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
        - name: SW_AGENT_COLLECTOR_BACKEND_SERVICES
          value: "skywalking-oap.skywalking.svc.cluster.local:11800"
        - name: SW_AGENT_INSTANCE_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
        - name: JAVA_TOOL_OPTIONS
          value: "-javaagent:/skywalking-agent/skywalking-agent.jar"
        volumeMounts:
        - name: skywalking-agent
          mountPath: /skywalking-agent
      
      # ④ 共享卷
      volumes:
      - name: skywalking-agent
        emptyDir: {}

4.3 工作流程图

+------------------------------------------------------------------+
|          InitContainer Agent注入流程                               |
+------------------------------------------------------------------+
|                                                                    |
|  时间线:                                                           |
|  ──────────────────────────────────────────────────────────────→  |
|                                                                    |
|  ① InitContainer启动                                               |
|  ┌──────────────────────────────────────────┐                      |
|  │ apache/skywalking-java-agent:9.3.0       │                      |
|  │ cp -r /skywalking/agent/* /agent-volume/ │                      |
|  └───────────────────┬──────────────────────┘                      |
|                      │ 拷贝完成                                     |
|          ┌───────────▼───────────┐                                 |
|          │  共享卷 (emptyDir)     │                                 |
|          │  ┌──────────────────┐ │                                 |
|          │  │ skywalking-agent │ │                                 |
|          │  │  .jar            │ │                                 |
|          │  └────────┬─────────┘ │                                 |
|          └───────────┼───────────┘                                 |
|                      │                                             |
|  ② 应用容器启动      │                                             |
|  ┌───────────────────▼──────────────────────┐                      |
|  │ JAVA_TOOL_OPTIONS=                       │                      |
|  │   -javaagent:/skywalking-agent/          │                      |
|  │              skywalking-agent.jar        │                      |
|  │                                          │                      |
|  │ SW_AGENT_NAME=order-service-pod-xxx      │                      |
|  │ SW_AGENT_COLLECTOR_BACKEND_SERVICES=     │                      |
|  │    skywalking-oap:11800                  │                      |
|  └──────────────────────────────────────────┘                      |
+------------------------------------------------------------------+

5. Environment Variables传递配置

5.1 Agent配置环境变量对照表

SkyWalking Java Agent支持通过环境变量覆盖配置(SW_前缀):

agent.config 配置项 环境变量 说明
agent.service_name SW_AGENT_NAME 服务名称
collector.backend_service SW_AGENT_COLLECTOR_BACKEND_SERVICES OAP地址
agent.instance_name SW_AGENT_INSTANCE_NAME 实例名称
agent.sample_n_per_3_secs SW_AGENT_SAMPLE_N_PER_3_SECS 采样率
logging.level SW_LOGGING_LEVEL 日志级别
plugin.peer_max_length SW_PLUGIN_PEER_MAX_LENGTH 对端地址最大长度

5.2 K8s环境变量注入最佳实践

apiVersion: apps/v1
kind: Deployment
metadata:
  name: order-service
spec:
  template:
    spec:
      containers:
      - name: order-service
        env:
        # 基础配置
        - name: SW_AGENT_NAME
          value: "order-service"
        
        # OAP服务发现(使用K8s Service DNS)
        - name: SW_AGENT_COLLECTOR_BACKEND_SERVICES
          value: "skywalking-oap.skywalking:11800"
        
        # 实例名使用Pod名(方便定位)
        - name: SW_AGENT_INSTANCE_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
        
        # 日志级别(生产环境用WARN)
        - name: SW_LOGGING_LEVEL
          value: "WARN"
        
        # 采样率控制
        - name: SW_AGENT_SAMPLE_N_PER_3_SECS
          value: "1"
        
        # 忽略健康检查端点
        - name: SW_TRACE_IGNORE_PATH
          value: "/actuator/**,/health,/metrics/**"

5.3 ConfigMap统一管理配置

# skywalking-agent-configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: skywalking-agent-config
  namespace: production
data:
  agent.config: |
    # 统一Agent配置
    agent.service_name=${SW_AGENT_NAME:unknown}
    collector.backend_service=${SW_AGENT_COLLECTOR_BACKEND_SERVICES:skywalking-oap:11800}
    agent.sample_n_per_3_secs=${SW_AGENT_SAMPLE_N_PER_3_SECS:1}
    logging.level=${SW_LOGGING_LEVEL:WARN}
    plugin.tomcat.collect_http_params=true
    plugin.springmvc.collect_http_params=true
    # K8s下关闭服务端服务发现(使用Agent端配置)
    plugin.kubernetes.enabled=false

6. 与K8s服务发现结合

6.1 OAP Serivce的Headless Service设计

# oap-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: skywalking-oap
  namespace: skywalking
spec:
  type: ClusterIP
  clusterIP: None  # Headless Service
  ports:
  - name: grpc
    port: 11800
    targetPort: 11800
  - name: http
    port: 12800
    targetPort: 12800
  selector:
    app: skywalking-oap

6.2 K8s环境下的服务发现流程

+------------------------------------------------------------------+
|          K8s服务发现 + SkyWalking                                  |
+------------------------------------------------------------------+
|                                                                    |
|  Application Pod                                                   |
|  ┌──────────────────────────────────────────────┐                  |
|  │ SW_AGENT_COLLECTOR_BACKEND_SERVICES=          │                  |
|  │   skywalking-oap.skywalking:11800            │                  |
|  └───────────────────┬──────────────────────────┘                  |
|                      │                                             |
|                      │ DNS解析                                     |
|                      ▼                                             |
|  ┌──────────────────────────────────────────────┐                  |
|  │ CoreDNS                                       │                  |
|  │ skywalking-oap.skywalking → Headless Service  │                  |
|  │ → 返回所有OAP Pod IP                          │                  |
|  │ → 192.168.1.10, 192.168.1.11                 │                  |
|  └───────────────────┬──────────────────────────┘                  |
|                      │                                             |
|          ┌───────────┼───────────┐                                 |
|          ▼                       ▼                                 |
|  ┌──────────────┐       ┌──────────────┐                            |
|  │ OAP Pod-1    │       │ OAP Pod-2    │                            |
|  │ 192.168.1.10 │       │ 192.168.1.11 │                            |
|  └──────────────┘       └──────────────┘                            |
|                                                                    |
|  SkyWalking Agent内置的gRPC负载均衡会自动选择可用OAP                 |
|  如果OAP Pod-1挂了,Agent会自动切换到Pod-2                          |
+------------------------------------------------------------------+

6.3 使用Kubernetes Downward API

env:
# 注入Pod所在节点信息
- name: SW_AGENT_INSTANCE_PROPERTIES
  value: |
    node_name=$(NODE_NAME)
    namespace=$(POD_NAMESPACE)
    pod_ip=$(POD_IP)
- name: NODE_NAME
  valueFrom:
    fieldRef:
      fieldPath: spec.nodeName
- name: POD_NAMESPACE
  valueFrom:
    fieldRef:
      fieldPath: metadata.namespace
- name: POD_IP
  valueFrom:
    fieldRef:
      fieldPath: status.podIP

7. Pod重启与滚动更新时的追踪连续性

7.1 Pod重启对Trace的影响

+------------------------------------------------------------------+
|        Pod重启场景分析                                             |
+------------------------------------------------------------------+
|                                                                    |
|  场景1: 优雅关闭(Graceful Shutdown)                               |
|  ┌────────────────────────────────────────────────┐                |
|  │ PreStop Hook → Drain流量 → Agent flush数据     │                |
|  │ → 关闭gRPC连接 → Pod终止                      │                |
|  │ → 效果: Trace不丢失 ✅                          │                |
|  └────────────────────────────────────────────────┘                |
|                                                                    |
|  场景2: 强制终止(SIGKILL)                                        |
|  ┌────────────────────────────────────────────────┐                |
|  │ kubelet SIGTERM → 等待gracePeriod → SIGKILL   │                |
|  │ → 数据未flush → 少量Trace丢失 ⚠️               │                |
|  └────────────────────────────────────────────────┘                |
|                                                                    |
|  场景3: OOM Kill                                                    |
|  ┌────────────────────────────────────────────────┐                |
|  │ 内存超限 → 内核直接kill → 数据100%丢失 🔴       │                |
|  └────────────────────────────────────────────────┘                |
+------------------------------------------------------------------+

7.2 优雅关闭配置

apiVersion: apps/v1
kind: Deployment
metadata:
  name: order-service
spec:
  template:
    spec:
      terminationGracePeriodSeconds: 60  # 给够60秒用于flush
      containers:
      - name: order-service
        lifecycle:
          preStop:
            exec:
              command:
              - sh
              - -c
              - |
                # 1. 等待Agent flush数据
                sleep 10
                # 2. 应用正常关闭
                curl -X POST http://localhost:8080/actuator/shutdown
                sleep 5

7.3 滚动更新时的追踪连续性

apiVersion: apps/v1
kind: Deployment
spec:
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1          # 最多多启动1个Pod
      maxUnavailable: 0    # 滚动期间不允许不可用Pod
  
  # 配合readinessProbe确保新旧Pod追踪连续
  template:
    spec:
      containers:
      - name: order-service
        readinessProbe:
          httpGet:
            path: /actuator/health/readiness
            port: 8080
          initialDelaySeconds: 30
          periodSeconds: 10
          failureThreshold: 3

滚动更新时的状态变化:

+------------------------------------------------------------------+
|        滚动更新中的Pod与Agent状态                                  |
+------------------------------------------------------------------+
|                                                                    |
|  时间:  T0          T1          T2          T3          T4        |
|  ──────────────────────────────────────────────────────────────→  |
|                                                                    |
|  Pod-A1 (旧)  [在线]  [在线]  [在线]  [PreStop] [已终止]          |
|  Agent-A1     [上报]  [上报]  [上报]  [Flush]  [断开]            |
|                                                                    |
|  Pod-A2 (旧)  [在线]  [在线]  [PreStop] [已终止]                  |
|  Agent-A2     [上报]  [上报]  [Flush]  [断开]                     |
|                                                                    |
|  Pod-B1 (新)          [启动]  [在线]  [在线]  [在线]              |
|  Agent-B1             [连接]  [上报]  [上报]  [上报]              |
|                                                                    |
|  Pod-B2 (新)                   [启动]  [在线]  [在线]             |
|  Agent-B2                      [连接]  [上报]  [上报]             |
|                                                                    |
|  追踪状态:  正常   正常   正常   正常   正常                       |
|  (只要同Service的Pod至少有一个在线,Trace不会中断)                  |
+------------------------------------------------------------------+

8. 监控自举——SkyWalking监控SkyWalking

部署好之后别忘了——OAP Server本身也值得监控:

# OAP自身的监控暴露
oap:
  env:
    # 启用自监控
    SW_PROMETHEUS_FETCHER_ENABLE: "true"
    # 暴露metrics端点
    SW_TELEMETRY: "prometheus"
    # 暴露端口
    SW_PROMETHEUS_FETCHER_PORT: "1234"

然后就可以用Prometheus + Grafana监控OAP自身的CPU、内存、gRPC吞吐量等指标。

9. 总结

SkyWalking在Kubernetes上的部署,核心是三个字:“解耦适配”:

  1. InitContainer注入Agent:应用镜像和Agent镜像解耦,Agent升级不需要重打应用镜像
  2. 环境变量传递配置:告别配置文件,配置随Pod走
  3. K8s Service发现:利用DNS和Headless Service,Agent自动发现OAP节点
  4. 优雅关闭:PreStop Hook确保Agent数据不丢失

Kubernetes不是监控的"敌人",而是最佳拍档——它提供了统一的部署、服务发现、滚动更新机制,搭配SkyWalking的自动探针注入,真正做到了"零配置接入"。

下一篇文章,我们将深入SkyWalking的内部机制,解析它的内存消息队列——DataCarrier,看看它是如何在Agent端实现高性能数据上报的。


上一篇【第23篇】SkyWalking与单体应用——老系统也能焕发新活力
下一篇【第25篇】SkyWalking的内存消息队列DataCarrier——高性能数据中转站


更多推荐