クラウドネイティブの可観測性に関して言えば、おそらく誰もが OpenTelemetry (OTEL) について言及するでしょう。これは、コミュニティが標準に依存してすべてのクラスター コンポーネントの開発を同じ方向に向けているためです。 OpenTelemetry は、ログ、メトリクス、トレース、その他のコンテキスト情報を 1 つのリソースに結合できます。 クラスター管理者またはソフトウェア エンジニアは、このリソースを使用して、定義された期間にわたってクラスター内で何が起こっているかを把握できますが、Kubernetes 自体はこのテクノロジー スタックをどのように活用するのでしょうか?
Kubernetes は複数のコンポーネントで構成されており、独立したコンポーネントもあれば、積み重ねられたコンポーネントもあります。 コンテナー ランタイムの観点からアーキテクチャを上から下に見てみると、次のようになります。
kube-apiserver: API オブジェクトのデータを確認して構成します。
kubelet: 各ノードで実行されるエージェント。
CRI ランタイム: CRI-O やcontainerd などのコンテナ ランタイム インターフェイス (CRI) 準拠のコンテナ ランタイム。
OCI ランタイム: runc や crun などの下位レベルの Open Container Initiative (OCI) ランタイム。
Linux カーネルまたは Microsoft Windows: 基礎となるオペレーティング システム。
つまり、Kubernetes でコンテナーを実行するときに問題が発生した場合は、コンポーネントの 1 つを検討し始めることになります。 今日のクラスター アーキテクチャは複雑化しているため、問題の根本原因を見つけることは、私たちが直面している最も時間のかかる作業の 1 つです。問題の原因となっている可能性のあるコンポーネントがわかった場合でも、他のコンポーネントを考慮する必要があります。これどうやってやるの?
ほとんどの人はおそらく、ログを取得し、フィルタリングして、コンポーネントの境界で組み立てることに固執するでしょう。メトリクスもありますが、メトリクス値を通常のログに関連付けると、何が起こっているのかを追跡することが難しくなります。また、一部のメトリクスはデバッグ用に定式化されたものでもありません目的。
その結果が OpenTelemetry です。このプロジェクトは、 traces~
トレース、 metrics~
メトリック、 logs~
ログなどの信号を組み合わせて、クラスターの状態の統一されたビューを維持することを目的としています。
Kubernetes における OpenTelemetry トレースの現在の状態は何ですか? API サーバーの観点から見ると、Kubernetes v1.22 以降、トレースのアルファ サポートがあり、今後のリリースのいずれかでベータ版に昇格する予定です。 残念ながら、ベータ版の卒業生は Kubernetes v1.26 を利用できませんでした。設計提案は、API Server Tracing Kubernetes Enhancement Proposal (KEP) で見つけることができ、詳細情報が提供されています。
kubelet トレース部分は、Kubernetes v1.25 のアルファ状態で実装された別の KEP でトレースされます。 この記事の執筆時点ではベータ版の卒業は予定されていませんが、v1.27 のリリース サイクルではさらに多くの卒業が予定されている可能性があります。 2 つの KEP 以外の取り組みもあります。たとえば、klog は、ログ メッセージを既存のトレースにリンクすることで可観測性を向上させる OTEL サポートを検討しています。 SIG Instrumentation と SIG Node では、現在 kubelet と CRI コンテナー ランタイム間の gRPC 呼び出しに焦点を当てているため、kubelet トレースをリンクする方法について説明します。
CRI-O は、v1.23.0 以降 OpenTelemetry トレースをサポートしており、トレースにログを追加したり、アプリケーションの論理部分にスパンを拡張したりするなど、継続的にトレースの改善に取り組んでいます。これにより、トレースのユーザーが同じ情報を取得できるようになりますが、スコープが強化されています。他の OTEL 信号をフィルタリングする機能。 CRI-O のメンテナは、純粋に Rust で書かれた conmon-rs と呼ばれる、conmon に代わるコンテナ監視の開発にも取り組んでいます。 Rust で実装する利点の 1 つは、OpenTelemetry サポートなどの機能を追加できることです。これらのライブラリはすでに存在しているため、CRI-O との緊密な統合が可能になり、消費者はコンテナからの最低レベルのトレース データを確認できるようになります。
containerd は v1.6.0 以降、プラグインを使用して取得できるトレース サポートを追加しました。 runc や crun などの下位レベルの OCI ランタイムは OTEL をまったくサポートしておらず、その計画もないようです。そのため、トレースを収集してデータ シンクにエクスポートする際のパフォーマンスのオーバーヘッドを考慮する必要があります。個人的には、OCI ランタイムでの拡張テレメトリ収集は評価する価値があると考えていますが、Rust OCI ランタイムでは将来的に同様のことを検討していますか?
以下に示すデモでは、runc、conmon-rs、CRI-O、および kubelet の単一のローカル ノード スタックを使用します。kubelet でトレースを有効にするには、KubeletConfiguration で次の設定を行う必要があります。
apiVersion: kubelet. config. k8s. io/ v1beta1
kind: KubeletConfiguration
featureGates:
KubeletTracing : true
tracing:
samplingRatePerMillion: 1000000
samplingRatePerMillion に等しいと、100 万はすべてをサンプリングすることを内部的に変換します。同様の設定を CRI-O に適用する必要があります。バイナリ crio ファイルは、and で開始することも、次のような埋め込み設定で開始することもできます: --enable-tracing`` --tracing - 100 万あたりのサンプリング レート 1000000。
100 万に等しいsamplingRatePerMillion は内部的にすべてのサンプリングに変換され、同様の構成を CRI-O に適用する必要があります。crio バイナリは引数 --enable-tracing および --tracing-sampling-rate-per- で開始できます。 million 1000000 ファイルを使用するか、次のようなドロップイン構成を使用します。
cat / etc/ crio/ crio. conf. d/ 99 - tracing. conf
[ crio. tracing]
enable_tracing = true
tracing_sampling_rate_per_million = 1000000
conmon-rs を使用するように CRI-O を構成するには、少なくとも最新の CRI-O v1.25.x および conmon-rs v0.4.0 が必要です。その後、次のようにプラグインを構成して CRI-O が conmon-rs を使用できるようにします。
cat / etc/ crio/ crio. conf. d/ 99 - runtimes. conf
[ crio. runtime]
default_runtime = "runc"
[ crio. runtime. runtimes. runc]
runtime_type = "pod"
monitor_path = "/path/to/conmonrs" # or will be looked up in $PATH
デフォルト構成は、OpenTelemetry コレクター gRPC エンドポイント: localhost:4317 を指します。これも稼働している必要があります。 ドキュメントで説明されているように、OTLP を実行するには複数の方法がありますが、kubectl プロキシを介して Kubernetes で実行されている既存のインスタンスに侵入することも可能です。 すべてが設定されている場合、コレクターは受信トレースをログに記録する必要があります。
ScopeSpans #0
ScopeSpans SchemaURL :
InstrumentationScope go. opentelemetry. io/ otel/ sdk/ tracer
Span #0
Trace ID : 71896e69f7d337730dfedb6356e74f01
Parent ID : a2a7714534c017e6
ID : 1d27dbaf38b9da8b
Name : github. com/ cri- o/ cri- o/ server. ( * Server ) . filterSandboxList
Kind : SPAN_KIND_INTERNAL
Start time : 2023 - 07 - 07 09 : 50 : 20.060325562 + 0000 UTC
End time : 2023 - 07 - 07 09 : 50 : 20.060326291 + 0000 UTC
Status code : STATUS_CODE_UNSET
Status message :
Span #1
Trace ID : 71896e69f7d337730dfedb6356e74f01
Parent ID : a837a005d4389579
ID : a2a7714534c017e6
Name : github. com/ cri- o/ cri- o/ server. ( * Server ) . ListPodSandbox
Kind : SPAN_KIND_INTERNAL
Start time : 2023 - 07 - 07 09 : 50 : 20.060321973 + 0000 UTC
End time : 2023 - 07 - 07 09 : 50 : 20.060330602 + 0000 UTC
Status code : STATUS_CODE_UNSET
Status message :
Span #2
Trace ID : fae6742709d51a9b6606b6cb9f381b96
Parent ID : 3755d12b32610516
ID : 0492afd26519b4b0
Name : github. com/ cri- o/ cri- o/ server. ( * Server ) . filterContainerList
Kind : SPAN_KIND_INTERNAL
Start time : 2023 - 07 - 07 09 : 50 : 20.0607746 + 0000 UTC
End time : 2023 - 07 - 07 09 : 50 : 20.060795505 + 0000 UTC
Status code : STATUS_CODE_UNSET
Status message :
Events :
SpanEvent #0
-> Name : log
-> Timestamp : 2023 - 07 - 07 09 : 50 : 20.060778668 + 0000 UTC
-> DroppedAttributesCount : 0
-> Attributes ::
-> id: Str ( adf791e5- 2eb8- 4425 - b092- f217923fef93)
-> log. message: Str ( No filters were applied, returning full container list)
-> log. severity: Str ( DEBUG )
-> name: Str ( / runtime. v1. RuntimeService / ListContainers )
スパンにはトレース ID があり、通常は追加のトレース ID が添付されており、ログなどのイベントも出力の一部であることがわかります。 上記の場合、kubelet の Pod Lifecycle Event Generator (PLEG) は定期的に ListPodSandbox をトリガーして CRI-O の RPC を呼び出します。 これらのトレースは、たとえば、Jaeger によって表示できます。トレース スタックをローカルで実行する場合、Jaeger インスタンスのアドレスは、デフォルトで http://localhost:16686 に公開されます。
これらの ListPodSandbox リクエストは、Jaeger UI に直接表示されます。
効果はあまり良くないため、ワークロードは kubectl を通じて直接実行されます。
kubectl run - it - - rm - - restart= Never - - image= alpine alpine - - echo hi
hi
pod "alpine" deleted
ここで、Jaeger を見ると、conmonrs、crio、kubelet を使用した RunPodSandbox と CreateContainer CRI RPC のトレースがわかります。
kubelet と CRI-O スパンは、調査を容易にするために相互接続されています。スパンを詳しく見ると、CRI-O のログに対応する機能が正しく含まれていることがわかります。 たとえば、コンテナ ユーザーは次のようにトレースから抽出できます。
下位レベルの common-rs スパンもこのトレースの一部です。 たとえば、conmon-rs は、コンテナとエンド ユーザー間の IO を処理する内部 read_loop を維持します。読み取りバイトと書き込みバイトのログはスパンの一部です。同じことが wait_for_exit_code スパンにも当てはまります。これは、コンテナに正常に終了するように指示します。 0のコード:
これらすべての情報をJaegerのフィルタリング機能と組み合わせると、スタック全体がコンテナの問題をデバッグするための優れたソリューションになります。また、「スタック全体」について言及すると、モノリシックアプローチの最大の欠点も明らかになります。ログの解析よりもクラスタ上での効率が低いということです。明らかなオーバーヘッドが追加されます。セットアップでは、ユーザーはデータを永続化するために Elasticsearch などのシンクを維持する必要があり、Jaeger UI を公開し、パフォーマンス上の落とし穴を念頭に置いている可能性がありますが、いずれにせよ、これが Kubernetes 1 の可観測性の側面を改善する最良の方法であることに変わりはありません。