Kibotos

链路追踪

本页介绍怎样接入 trace 存储,让一次 Workspace 命令在 Grafana 里显示为一条时间线,以及各服务怎样配置、怎样排查。

概述

Trace 让一次 Workspace 命令在 Grafana 里显示为一条时间线:从网关或 kibo 进入 ctrl,经执行器的每次尝试、node 的每个 RPC,到 guest 的调用,每一跳的耗时与失败点都在上面。它是可选的外部依赖,由部署方运营。不接入时各服务不产生 span,行为与没有 trace 时相同。

  • ctrl、node(包括它的数据面进程)、E2B 网关与 Exposure 网关各自产生 span,用 OTLP/HTTP 批量推送到 trace 存储,不落盘。
  • guest 内的代理不产生 span。它从 node 的调用里读出 trace 上下文,写进自己的日志记录。
  • 每条日志记录带当时的 trace_id 与 span_id,从 trace 能查到日志,从日志能查到 trace,见日志字段。

trace 只用于排障,不是用量数据。存储不可达、队列满或凭证到期时,span 直接丢弃,之后不会补推,丢弃数量见 kibotos_traces_dropped_total。

一次命令怎样连成 trace

一次 Workspace 命令的全部 span 共用一个由命令 ID 算出的 trace ID。 命令可能跨多次执行尝试、多个 ctrl 进程与多台 node,这样不用在任何地方保存 trace 上下文,就能把它们连在一起。

span属于父
调用方发起命令的那次 RPC,例如网关的 CreateWorkspace 调用调用方自己的 trace调用方的 span
命令旅程的根 command CreateWorkspace命令旅程无。结算时一次性发出,从受理时刻跨到结算时刻
执行器的一次尝试 command.attempt命令旅程旅程根
这次尝试里 ctrl 对 node 的调用、node 对 guest 的调用命令旅程尝试
  • 调用方的那次 RPC 的 span 带一个 link,指向命令旅程的根。Grafana 里在这个 span 的 References 处能直接打开旅程。
  • 命令旅程的 trace ID 是命令 ID 的 SHA-256 的前 16 字节。知道命令 ID 就能直接算出 trace ID;按 command_id 标签搜索也能找到。
  • 命令还没有结算时,旅程根还不存在,尝试 span 照样能按 trace ID 打开。

采样

采样只在入口决定一次,同一个请求内的后续调用跟随入口的决定。

入口采样
ctrl 的 Operator API 与 ctrl API全部
E2B 网关的控制 API全部
E2B 网关的 envd 与应用端口、Exposure 网关、node 数据面按比例,默认 0.01
ctrl 对 node、node 对 guest 的调用跟随调用方
周期对账、凭证续取这类后台调用不采样
  • 按比例采样按 trace ID 判定,比例相同的入口对同一条 trace 得出同一结论,所以网关采样的请求,node 数据面也会采样。
  • 调用方请求里的 traceparent 只提供 trace ID 与父 span,采样标志不读:租户不能决定 Kibotos 记录多少 trace。tracestate 与 baggage 不读也不转发。
  • 网关与 node 数据面的每个响应带 traceresponse 头,值是 00-<trace_id>-<span_id>-<flags>,flags 为 01 表示这次请求被采样。拿着它就能在 Grafana 里找到这次请求。

凭证

进程地址与凭证从哪里来
ctrl配置文件 [traces] 段。ctrl 用签名私钥给自己签 token
node向 ctrl 取得地址、CA、token 与采样比例,只放在内存里。node 没有 trace 相关的配置
两个网关环境变量,token 从部署方给的文件读取
  • ctrl 签的 token 与实例指标存储的同形:ES256 签名的 JWT,sub 是 node ID 或 ctrl,kibotos_role 是 node 或 ctrl,有效期 1 小时,剩余不足一半时换新。多一个声明 kibotos_sink: traces,kid 以 trc- 开头。
  • 可以与实例指标共用一把私钥。追踪的前置代理同时校验 kibotos_role 与 kibotos_sink,实例指标的前置代理拒绝带 kibotos_sink 的 token,所以两种 token 互相不能代用。
  • ctrl 没有 [traces] 段时告诉 node 不产生 span。node 之后每 5 分钟再问一次,所以 ctrl 加上 [traces] 并重启后,node 最多 5 分钟后开始推送。
  • ctrl 不可达期间,node 用手里的 token 推送到到期为止,之后结束的 span 按 no_credential 丢弃。

参考部署:VictoriaTraces 与 vmauth

VictoriaTraces 单节点版只让 vmauth 访问,自身不设口令;vmauth 对外提供推送与查询入口,并负责认证。

用途地址
ctrl 的 [traces].endpoint,node 从 ctrl 取得同一个地址;网关的 *_TRACES_ENDPOINThttps://traces.example.com:8427/insert/opentelemetry/v1/traces
Grafana 的 Jaeger 数据源https://traces.example.com:8427/select/jaeger

vmauth 至少用 v1.152.0,参考部署与实例指标存储同一版本。它的配置有三类用户:

用户认证能访问
kibotos-node、kibotos-ctrlctrl 签的 JWT,kibotos_role 与 kibotos_sink 都要匹配/insert/opentelemetry/v1/traces
每个网关一个静态 bearer token/insert/opentelemetry/v1/traces
每个看板一个basic auth/select/jaeger/api/...

下面几节在宿主上或用 Helm chart 部署存储,两者都要用到下一节的私钥与 token。

签名私钥与 token

在 ctrl 宿主上生成签名私钥并导出公钥,私钥只给 kibotos-ctrl 读取。也可以直接用实例指标的那把,共用的含义见凭证:

sudo install -o kibotos-ctrl -g kibotos-ctrl -m 0600 /dev/null /etc/kibotos/ctrl/traces-signing-key.pem
sudo openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-256 -out /etc/kibotos/ctrl/traces-signing-key.pem
sudo openssl pkey -in /etc/kibotos/ctrl/traces-signing-key.pem -pubout > traces-signing-public.pem

再为每个网关生成一个 bearer token,为 Grafana 生成一个口令。文件不能带换行:vmauth 把内容原样比较:

set -eu
umask 077
for name in e2b gw; do openssl rand -hex 32 | tr -d '\n' > traces-$name-token; done
openssl rand -hex 24 | tr -d '\n' > traces-grafana-password

traces-e2b-token 与 traces-gw-token 分别放到两个网关宿主上,作为它们的 *_TRACES_TOKEN_FILE 指向的文件,见网关的配置。

宿主

VictoriaTraces 单节点版的启动参数:

参数示例说明
-storageDataPath/var/lib/victoria-traces数据目录,放在本地磁盘
-retentionPeriod7d保留期,最短 1d
-httpListenAddr127.0.0.1:10428只让同机的 vmauth 访问

vmauth 的启动参数与实例指标存储的相同,-auth.config 指向下面的文件。把 traces-signing-public.pem 的内容写进两个 JWT 用户。网关的 token 与 Grafana 的口令从 vmauth 进程的环境变量 KIBOTOS_TRACES_WRITER_E2B_TOKEN、KIBOTOS_TRACES_WRITER_GW_TOKEN 与 KIBOTOS_TRACES_READER_GRAFANA_PASSWORD 读取:

users:
- name: kibotos-node
  jwt:
    default_vm_access_claim: {}
    public_keys:
    - |
      -----BEGIN PUBLIC KEY-----
      MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE...
      -----END PUBLIC KEY-----
    match_claims:
      kibotos_role: node
      kibotos_sink: traces
  url_map:
  - src_paths: ["/insert/opentelemetry/v1/traces"]
    url_prefix: "http://127.0.0.1:10428/"
- name: kibotos-ctrl
  jwt:
    default_vm_access_claim: {}
    public_keys:
    - |
      -----BEGIN PUBLIC KEY-----
      MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE...
      -----END PUBLIC KEY-----
    match_claims:
      kibotos_role: ctrl
      kibotos_sink: traces
  url_map:
  - src_paths: ["/insert/opentelemetry/v1/traces"]
    url_prefix: "http://127.0.0.1:10428/"
- name: e2b
  bearer_token: "%{KIBOTOS_TRACES_WRITER_E2B_TOKEN}"
  url_map:
  - src_paths: ["/insert/opentelemetry/v1/traces"]
    url_prefix: "http://127.0.0.1:10428/"
- name: gw
  bearer_token: "%{KIBOTOS_TRACES_WRITER_GW_TOKEN}"
  url_map:
  - src_paths: ["/insert/opentelemetry/v1/traces"]
    url_prefix: "http://127.0.0.1:10428/"
- name: grafana
  username: grafana
  password: "%{KIBOTOS_TRACES_READER_GRAFANA_PASSWORD}"
  url_map:
  - src_paths: ["/select/jaeger/api/.+"]
    url_prefix: "http://127.0.0.1:10428/"
  • 两个 JWT 用户都要有 default_vm_access_claim: {},原因与实例指标存储相同。两个声明都要匹配,所以用同一把私钥签的指标 token 不带 kibotos_sink,会被拒绝。
  • 公钥直接写在配置文件里:vmauth 只在配置文件本身变化时重新加载。

Kubernetes

deploy/helm/kibotos-traces/ 装出 VictoriaTraces(StatefulSet 与持久卷,只在集群内可达,NetworkPolicy 只放行 vmauth)、vmauth 与它的 Service。结构与实例指标存储的 chart相同。

前提:集群的 CNI 执行 NetworkPolicy。

用签名私钥与 token 生成的文件创建 Secret,然后安装。示例用命名空间 kibotos-traces:

set -eu
kubectl create namespace kibotos-traces
kubectl -n kibotos-traces create secret generic kibotos-traces-e2b --from-file=token=traces-e2b-token
kubectl -n kibotos-traces create secret generic kibotos-traces-gw --from-file=token=traces-gw-token
kubectl -n kibotos-traces create secret generic kibotos-traces-grafana --from-file=password=traces-grafana-password
cat > traces.yaml <<EOF
vmauth:
  publicKeys:
    - |
$(sed 's/^/      /' traces-signing-public.pem)
  writers:
    - name: e2b
      secretName: kibotos-traces-e2b
    - name: gw
      secretName: kibotos-traces-gw
  readers:
    - name: grafana
      secretName: kibotos-traces-grafana
  service:
    type: LoadBalancer
    annotations:
      service.beta.kubernetes.io/alibaba-cloud-loadbalancer-address-type: intranet
EOF
helm install kibotos-traces deploy/helm/kibotos-traces -n kibotos-traces -f traces.yaml --wait
kubectl -n kibotos-traces get service kibotos-traces-vmauth

最后一条输出的 EXTERNAL-IP 就是存储的地址。

三个存储 chart 共有的键见 chart 的通用 values,本 chart 里存储的键以 victoriaTraces. 开头。本 chart 特有的键与默认值:

键说明
vmauth.publicKeys公钥 PEM 的列表,必填。轮换私钥期间新旧并存
vmauth.writers网关的推送用户,每项 name 与 secretName(键 token)
victoriaTraces.retentionPeriod默认 7d,最短 1d
victoriaTraces.storage.size默认 20Gi
  • 设了 vmauth.tls.secretName、证书由私有 CA 签发时,CA 交给 ctrl 的 ca_file 与网关的 *_TRACES_CA_FILE。
  • vmauth 自己的 /metrics、/flags、pprof 与配置重载用 chart 生成的 Secret <release>-vmauth-admin 的键 key 作 authKey,不对网络敞开。
  • 配置变化后 vmauth 重启的几秒里推送失败,这批 span 丢弃。

容量

  • 每次命令约有一个根 span、每次尝试一个 span、每个 node RPC 两个 span(ctrl 的客户端与 node 的服务端),外加 node 对 guest 的调用。
  • 数据面请求按比例采样,默认每 100 个请求记 1 个。调高比例前先估算数据面请求量。
  • 每个进程最多排队 2048 个 span,每 5 秒或攒满 512 个推送一次。

ctrl 的配置

只有 ctrl 有 [traces] 段。没有这个段时 ctrl 不产生 span,node 也不产生。每个键的取值范围与默认值见 ctrl 配置参考的 [traces]。signing_key_file 可以与 [metrics] 用同一个文件;sample_ratio 要与网关的 *_TRACES_SAMPLE_RATIO 相同。

在 /etc/kibotos/ctrl/config.toml 末尾加上下面一段,然后重启 ctrl。node 不需要改配置,也不需要重启。完整示例见 ctrl.example.toml 末尾。

[traces]
endpoint = "https://traces.example.com:8427/insert/opentelemetry/v1/traces"
signing_key_file = "/etc/kibotos/ctrl/traces-signing-key.pem"
  • init、enrollment create、check-upgrade 与服务本身都校验 [traces] 并读取私钥与 CA 文件,文件读不到或格式不对时都会失败。
  • 启用后,ctrl 日志有一行 trace export enabled,每台 node 日志有一行 node tracing enabled。

网关的配置

两个网关的变量同形,前缀分别是 KIBOTOS_E2B_ 与 KIBOTOS_GW_。示例见 e2b.example.env 与 gw.example.env 末尾。

变量说明
*_TRACES_ENDPOINT完整的 OTLP/HTTP trace 地址。不设时网关不产生 span
*_TRACES_TOKEN_FILE设了地址时必填。文件内容是 bearer token,去掉首尾空白后非空,不超过 8 KiB;网关账户要能读取
*_TRACES_CA_FILE可选。替换系统根证书的 PEM 文件
*_TRACES_SAMPLE_RATIO可选。envd、应用端口与 Exposure 代理的采样比例,0 到 1,默认 0.01。与 ctrl 的 sample_ratio 取同一个值,网关采样的请求在 node 上也会被采样
  • 没有设置地址、却设置了 token、CA 或比例时,网关拒绝启动。
  • token 只在启动时读一次,换 token 后重启网关。
  • 启用后,网关日志有一行 trace export enabled。

Grafana

在 Grafana 里加一个 Jaeger 类型的数据源,URL 填 vmauth 地址加 /select/jaeger(集群内是 http://kibotos-traces-vmauth.<命名空间>.svc:8427/select/jaeger),Basic auth 填一个只读用户:宿主上的配置里的 grafana,或 Kubernetes 上 vmauth.readers 里的用户。

deploy/traces/grafana-command-journey.json「Kibotos 命令旅程」:

  • 变量「标签过滤」用 logfmt 写法缩小范围,例如 workspace_id=ws-… 或 command_id=…。
  • 上面两张表分别列 ctrl 与 E2B 网关最近的 trace,点 Trace ID 在下方打开整条 trace。
  • 右上角的链接「同一 trace_id 的日志」打开日志看板,按当前的 Trace ID 过滤。

span 推送后要过几秒才能查到。

日志字段

处理请求或执行尝试时写出的日志记录带 trace_id 与 span_id,都是小写十六进制。没有 trace 上下文的记录不带这两个字段。

  • 在 VictoriaLogs 里按 trace_id:<ID> 查询,能看到同一次请求在 ctrl、node 与 guest 写下的记录。guest 的记录经 node 转写,保留这两个字段。
  • 不采样的请求照常续接 trace ID,它们的记录同样带 trace_id,日志之间照样能关联,只是存储里没有这条 trace。
  • 不接入 trace 存储时,日志不带这两个字段。

不会出现在 trace 里的内容

span 的属性只有下面几类,其余一律在推送前去掉:

属性内容
rpc.system.name、rpc.method、rpc.response.status_code、error.typeConnect 调用的方法与结果码
http.request.method、http.route、http.response.status_codeHTTP 请求的方法、路由模式与状态码
workspace_id、instance_id、volume_id、transfer_id、template_id、exposure_id、command_id请求涉及的对象
kibotos.attempt、kibotos.settlement_code执行器的尝试序号与命令的结算码
  • 请求路径、查询串、主机名、端口、对端地址、User-Agent、Workspace 的 labels 与 annotations、文件名、凭证、请求与响应体都不进入 span。
  • span 的事件与错误文本也不推送:错误文本可能带用户的路径,结果码已经在属性里。
  • 资源属性只有 service.name、service.version,node 另有 kibotos.node_id。

安全

  • ctrl、node 与网关要能访问 vmauth。租户不能访问它:guest 网络与公网都不应能连到它的地址。
  • 能推送的人可以伪造 span,搅乱排障时看到的时间线,但不能读。能读的人看到的是对象 ID、方法名与耗时,没有用户数据。
  • 签名私钥泄露后,持有者能以任何 node 的身份推送;与实例指标共用私钥时,两边一起轮换。轮换步骤与实例指标的私钥轮换相同,公钥改在本 chart 的 vmauth.publicKeys。

排查

现象原因处理
Grafana 里没有任何 tracectrl 没有 [traces],或网关没有设置 *_TRACES_ENDPOINT补上后重启 ctrl 或网关
只有 ctrl 与网关的 span,没有 node 的node 还没有问到凭证(最多 5 分钟),或连不上 vmauth查看 node 日志里的 node tracing enabled 与 trace export failed
日志有 trace export failed,code 为 401vmauth 的公钥与签名私钥不匹配,或网关的 token 与 Secret 里的不同重新导出公钥,或核对 token 文件与 Secret 的内容,注意换行
日志有 trace export failed,code 为 0连不上 vmauth检查地址与网络
kibotos_traces_dropped_total{reason="queue_full"} 增长span 产生得比推送快降低数据面的采样比例,或检查存储是否变慢
只有 E2B 网关的 span,命令旅程打不开命令还没有结算,或 ctrl 没有启用 trace按命令 ID 搜索尝试 span;确认 ctrl 日志有 trace export enabled

本页内容