特殊环境 Agent 部署

创建时间:2023-10-04 最近修改时间:2024-06-24

#1. 特殊的 K8s CNI

在常见 K8s 环境中,DeepFlow Agent 可以采集到全栈观测信号,如下图左上角所示:

  • 同一个 Node 上的两个 Pod 互访时,可采集到 eBPF Syscall 和 cBPF Pod NIC 两种位置的数据
  • 不同的 Node 上的两个 Pod 互访时,可采集到 eBPF Syscall、cBPF Pod NIC 以及 cBPF Node NIC 三种位置的数据

不同 K8s CNI 下的数据采集能力(Pod 与 Pod 通信场景)

不同 K8s CNI 下的数据采集能力(Pod 与 Pod 通信场景)

但是,在某些 CNI 下,由于流量路径的特殊性,DeepFlow Agent 采集到的数据会有差异:

  • 在 Cilium CNI 环境中(上图右上角):
    • Cilium 使用 XDP (opens new window) 将网络绕过了 TCP/IP 协议栈,导致名为 lxc-xxx 的 Pod NIC 上只能看到单向流量
    • 同一个 Node 上的两个 Pod 互访时,可采集到 eBPF Syscall 一种位置的数据
    • 不同的 Node 上的两个 Pod 互访时,可采集到 eBPF Syscall 和 cBPF Node NIC 两种位置的数据,后者采集自 Node eth0
  • 在 MACVlan、华为云 CCE Turbo (opens new window) 等 CNI 环境中(上图左下角):
    • 使用 MACVlan 子接口而非 Veth-Pair + Bridge,此时在 Root Netns 中没有对应的 Pod NIC,但是能在 Node eth0 上看到所有 Pod 的所有流量
    • 此时,DeepFlow Agent 可参照下文配置 tap_mode = 1 (virtual mirror),将 Node NIC 上的流量等同于视为是在 Pod NIC 上采集到的
    • 同一个 Node 上的两个 Pod 互访时,可采集到 eBPF Syscall 和 cBPF Pod NIC 两种位置的数据,后者采集自 Node eth0
      • 不过,由于 eth0 上只有一份通信流量,因此客户端、服务端共享一份 cBPF Pod NIC 位置的数据
    • 不同的 Node 上的两个 Pod 互访时,可采集到 eBPF Syscall 和 cBPF Pod NIC 两种位置的数据,后者采集自 Node eth0
  • 在 IPVlan CNI 环境中(上图右下角):
    • 使用 IPVlan 子接口而非 Veth-Pair + Bridge,此时在 Root Netns 中没有对应的 Pod NIC,且仅能在 Node eth0 上看到 Pod 进出 Node 的流量
    • 同一个 Node 上的两个 Pod 互访时,可采集到 eBPF Syscall 一种位置的数据
    • 不同的 Node 上的两个 Pod 互访时,可采集到 eBPF Syscall 和 cBPF Node NIC 两种位置的数据,后者采集自 Node eth0

另外,eBPF XDP 还可以与 IPVlan 混合使用(例如阿里云的 Terway CNI (opens new window)),此时的流量采集能力等同于 Cilium 或 IPVlan。

一些参考资料:

#1.1 MACVlan

K8s 使用 macvlan CNI 时,在 rootns 下只能看到所有 POD 共用的一个虚拟网卡,此时需要对 deepflow-agent 进行额外的配置:

  1. 创建 agent-group 和 agent-group-config:

    deepflow-ctl agent-group create macvlan
    deepflow-ctl agent-group-config create macvlan
    
    1
    2
  2. 获取 macvlan agent-group ID:

    deepflow-ctl agent-group list  | grep macvlan
    
    1
  3. 新建 agent-group-config 配置文件 macvlan-agent-group-config.yaml:

    vtap_group_id: g-xxxxxx
    ## Regular Expression for TAP (Traffic Access Point)
    ## Length: [0, 65535]
    ## Default:
    ##   Localhost:   lo
    ##   Common NIC:  eth.*|en[osipx].*
    ##   QEMU VM NIC: tap.*
    ##   Flannel:     veth.*
    ##   Calico:      cali.*
    ##   Cilium:      lxc.*
    ##   Kube-OVN:    [0-9a-f]+_h$
    ## Note: Regular expression of NIC name for collecting traffic
    tap_interface_regex: eth0
    ## Traffic Tap Mode
    ## Default: 0, means local.
    ## Options: 0, 1 (virtual mirror), 2 (physical mirror, aka. analyzer mode)
    ## Note: Mirror mode is used when deepflow-agent cannot directly capture the
    ##   traffic from the source. For example:
    ##   - in the K8s macvlan environment, capture the Pod traffic through the Node NIC
    ##   - in the Hyper-V environment, capture the VM traffic through the Hypervisor NIC
    ##   - in the ESXi environment, capture traffic through VDS/VSS local SPAN
    ##   - in the DPDK environment, capture traffic through DPDK ring buffer
    ##   Use Analyzer mode when deepflow-agent captures traffic through physical switch
    ##   mirroring.
    tap_mode: 1
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
  4. 创建 agent-group-config:

    deepflow-ctl agent-group-config create -f macvlan-agent-group-config.yaml
    
    1
  5. 修改 deepflow-agent 的 agent-group:

    kubectl edit cm -n deepflow deepflow-agent
    
    1

    添加配置:

    vtap-group-id-request: g-xxxxx
    
    1

    停止 deepflow-agent:

    kubectl -n deepflow  patch daemonset deepflow-agent  -p '{"spec": {"template": {"spec": {"nodeSelector": {"non-existing": "true"}}}}}'
    
    1

    通过 deepflow-ctl 删除 macvlan 的 agent:

    deepflow-ctl agent delete <agent name>
    
    1

    启动 deepflow-agent:

    kubectl -n deepflow  patch daemonset deepflow-agent --type json -p='[{"op": "remove", "path": "/spec/template/spec/nodeSelector/non-existing"}]'
    
    1

    查看 deepflow agent list, 确保 agent 加入了 macvlan group:

    deepflow-ctl agent list
    
    1

#1.2 华为云 CCE Turbo

参考 MACVlan 配置方法即可。

#1.3 IPVlan

唯一需要注意的是,采集器的 tap_interface_regex 只需配置为 Node NIC 列表。

#1.4 Cilium eBPF

唯一需要注意的是,采集器的 tap_interface_regex 只需配置为 Node NIC 列表。

#2. 特殊 K8s 资源或 CRD

这类场景需要进行以下操作:

  • Agent 高级配置中打开和关闭对应的资源
  • 配置 Kubernetes API 权限

#2.1 OpenShift

该场景需要关闭默认的 Ingress 资源获取,打开 Route 资源获取。

Agent 高级配置如下:

static_config:
  kubernetes-resources:
    - name: ingresses
      disabled: true
    - name: routes
1
2
3
4
5

ClusterRole 配置增加:

rules:
  - apiGroups:
      - route.openshift.io
    resources:
      - routes
    verbs:
      - get
      - list
      - watch
1
2
3
4
5
6
7
8
9

#2.2 OpenKruise

该场景下需要从 API 获取 CloneSetapps.kruise.io/StatefulSet 资源。

Agent 高级配置如下:

static_config:
  kubernetes-resources:
    - name: clonesets
      group: apps.kruise.io
    - name: statefulsets
      group: apps
    - name: statefulsets
      group: apps.kruise.io
1
2
3
4
5
6
7
8

注意这里需要加上 Kubernetes 的 apps/StatefulSet

ClusterRole 配置增加:

- apiGroups:
    - apps.kruise.io
  resources:
    - clonesets
    - statefulsets
  verbs:
    - get
    - list
    - watch
1
2
3
4
5
6
7
8
9

#3. K8s 运行 Agent 权限受限

#3.1 无 K8s Daemonset 部署权限

当没有在 Kubernetes 集群中运行 Daemonset 的权限、但可在 K8s Node 上直接运行普通进程时,可使用该方法实现 Agent 部署。

  • 以 deployment 形态部署一个 deepflow-agent
    • 通过设置环境变量 ONLY_WATCH_K8S_RESOURCE,该 agent 仅实现对 K8s 资源的 list-watch 及上送控制器的功能
    • 这个 agent 的其他所有功能均会自动关闭
    • agent 请求 server 时告知自己在 watch-k8s,server 会将此信息更新到 MySQL 数据库中
    • 这个仅用做 Watcher 的 Agent 将不会出现在 Agent 列表中
  • 在这个 K8s 集群中,以 Linux 进程的形态在每个 K8s Node 上运行一个常规功能的 deepflow-agent
    • 由于这些 agent 没有 IN_CONTAINER 环境变量,不会 list-watch K8s 资源
    • 这些 agent 依然会获取 POD 的 IP 和 MAC 地址并同步到 server
    • 这些 agent 将完成所有的观测数据采集功能
    • server 向这些 agent 下发的 Agent 类型为 K8s

#3.1.1 部署 deployment 模式 DeepFlow Agent

cat << EOF > values-custom.yaml
deployComponent:
- "watcher"
clusterNAME: your-cluster-name
EOF

helm install deepflow -n deepflow deepflow/deepflow-agent --create-namespace \
  -f values-custom.yaml
1
2
3
4
5
6
7
8

部署后,将自动创建 Domain(对应此 K8s 集群),通过deepflow-ctl domain list中获取 your-cluster-name cluster 的 kubernetes-cluster-id,再继续下面的操作。

#3.1.2 部署普通进程形式的 DeepFlow Agent

  • 参考传统服务器部署 DeepFlow Agent,但无需创建 Domain
  • 修改 agent 配置文件 /etc/deepflow-agent/deepflow-agent.yamlkubernetes-cluster-id 填写上一步获取的集群 ID

#3.2 不允许 Daemonset 请求 apiserver

默认情况下 DeepFlow Agent 在 K8s 中以 Daemonset 方式运行。但有些情况下为了保护 apiserver 避免过载,Daemonset 不允许对 apiserver 请求。此时也可使用本文中「无 Daemonset 部署权限」的类似方式进行部署:

  • 部署一个 deepflow-agent deployment,仅负责 list-watch apiserver、同步 K8s 资源信息
  • 部署一个 deepflow-agent daemonset,任何 Pod 都不会 list-watch apiserver

#3.3 不允许 deepflow-agent 请求 apiserver

deepflow-server 依赖 deepflow-agent 上报的 K8s 资源信息来实现 AutoTagging 能力,当你的环境不允许 deepflow-agent 直接 Watch K8s apiserver 时,你可以自己实现一个专门用于同步 K8s 资源的 pseudo-deepflow-gent。这个 pseudo-deepflow-agent 需要实现的功能包括:

  • 周期性的 List-Watch K8s apiserver 以获取最新的 K8s 资源信息
  • 调用 deepflow-server 的 gRPC 接口上报 K8s 资源信息

#3.3.1 gRPC 接口

上报 K8s 资源信息的接口为(GitHub 代码链接 (opens new window)):

rpc KubernetesAPISync(KubernetesAPISyncRequest) returns (KubernetesAPISyncResponse) {}
1

pseudo-deepflow-agent 上报消息的结构体说明(GitHub 代码链接同上):

message KubernetesAPISyncRequest {
    // 如无特殊说明,以下字段均必须携带。

    // K8s 集群标识。
    // 请使用同一个 K8s 集群中 deepflow-agent.yaml 所配置的值。
    optional string cluster_id = 1;

    // 资源信息的版本号。
    // 当 K8s 资源信息发生变化时,请确保此版本号也要进行改变,通常可使用 Linux Epoch。
    // 当 K8s 资源信息没有变化时,携带上一次的版本号,此时 entries 无需传输。
    // 即使资源信息没有变化,也要周期性请求 deepflow-server,保证两次请求间隔不要超过 24 小时。
    optional uint64 version = 2;

    // 异常信息。
    // 当调用 K8s API 异常,或者发生其他错误时,可通过此字段告知 deepflow-server。
    // 当存在 error_msg 时,建议携带上一次请求使用的 version 和 entries 字段。
    optional string error_msg = 3;

    // Source IP 地址。
    // 通常可填写为 pseudo-deepflow-agent 请求 gRPC 时使用的客户端 IP 地址。
    // 使用有代表性、区分性的 source_ip 能够方便查阅 deepflow-server 日志,定位请求者。
    optional string source_ip = 5;

    // 各类 K8s 资源的所有信息。
    // 请注意需要包括所有类型的所有资源,未出现的资源将会被 deepflow-server 认为已删除。
    repeated common.KubernetesAPIInfo entries = 10;
}

message KubernetesAPIInfo {
    // K8s 资源类型,当前支持的资源类型有:
    // - *v1.Node
    // - *v1.Namespace
    // - *v1.Deployment
    // - *v1.StatefulSet
    // - *v1.DaemonSet
    // - *v1.ReplicationController
    // - *v1.ReplicaSet
    // - *v1.Pod
    // - *v1.Service
    // - *v1beta1.Ingress
    optional string type = 1;

    // 该类型的资源列表。
    // 注意:请使用 JSON 序列化,之后使用 zlib 进行压缩,传输压缩后的字节流即可。
    optional bytes compressed_info = 3;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46

deepflow-server 回复消息的结构体说明(GitHub 代码链接同上):

message KubernetesAPISyncResponse {
    // deepflow-server 已经接受的资源信息版本号,通常等于最近一次请求中的 version。
    optional uint64 version = 1;
}
1
2
3
4

#3.3.2 KubernetesAPIInfo

注意,deepflow-server 要求某些 K8s 资源类型必须上报包括:

  • *v1.Node
  • *v1.Namespace
  • *v1.Pod
  • *v1.Deployment*v1.StatefulSet*v1.DaemonSet*v1.ReplicationController*v1.ReplicaSet:根据 Pod 的工作负载类型按需上报即可

除此之外的其他资源可以不上报:

  • *v1.Service
  • *v1beta1.Ingress

对于上述资源,pseudo-deepflow-agent 需要上报的信息可参考此处的文档

#4. 云服务器运行 Agent 权限受限

#4.1 使用非 root 用户运行 deepflow-agent

假设我们希望使用普通用户 deepflow 来运行安装于 /usr/sbin/deepflow-agent 的 Agent,我们必须先通过 root 用户添加 deepflow 所需的权限:

## Use the root user to grant execution permissions to the deepflow-agent
setcap cap_sys_ptrace,cap_net_raw,cap_net_admin,cap_ipc_lock,cap_sys_admin=eip /usr/sbin/deepflow-agent
mkdir /sys/fs/cgroup/cpu/deepflow-agent
mkdir /sys/fs/cgroup/memory/deepflow-agent
chown -R deepflow:deepflow /sys/fs/cgroup/cpu/deepflow-agent
chown -R deepflow:deepflow /sys/fs/cgroup/memory/deepflow-agent
chown -R deepflow:deepflow /usr/sbin/deepflow-agent
1
2
3
4
5
6
7

使用非 root 用户运行 deepflow-agent,例如:

systemctl start deepflow-agent
1

若希望卸载 deepflow-agent,注意删除对应的权限:

## Use the root user to revoke execution permissions from the deepflow-agent
setcap -r /usr/sbin/deepflow-agent
rmdir /sys/fs/cgroup/cpu/deepflow-agent
rmdir /sys/fs/cgroup/memory/deepflow-agent
1
2
3
4