内部実装
コミット
65b0dd9aeのソースを読んだもの。ここでの主張はすべてファイルと行を指す。
コードマップ
| パス | 責務 |
|---|---|
cmd/busyambassador/main.go | os.Args[0] による BusyBox 方式ディスパッチで entrypoint / kubestatus / version に振り分け |
cmd/entrypoint/ | Go のプロセスマネージャ、クラスタ watcher、notify ロジック |
cmd/apiext/ | emissary-apiext CRD 変換 webhook |
pkg/ambex/ | Envoy に xDS で設定を流す ADS サーバ |
pkg/snapshot/v1/ | watcher が組み立てる Snapshot / KubernetesSnapshot 型 |
pkg/api/getambassador.io/ | v1 / v2 / v3alpha1 の CRD Go 型 |
python/ambassador/src/ambassador/ir/ | 中間表現とそのファクトリ群 |
python/ambassador/src/ambassador/envoy/v3/ | Envoy v3 設定ジェネレータ |
python/ambassador-diag/src/ambassador_diag/diagd.py | diagd の Flask サービスとコンパイルパイプライン |
中核データ構造
snapshot.SnapshotとKubernetesSnapshot(Go) は watcher が組み立てる世界の状態一式。KubernetesSnapshotは Mappings・Hosts・Listeners・Services・Endpoints・Secrets・Gateway API オブジェクトを抱える (pkg/snapshot/v1/types.go:23、pkg/snapshot/v1/types.go:57、pkg/snapshot/v1/types.go:84-87)。IR(Python) は中間表現。clusters: Dict[str, IRCluster]・groups: Dict[str, IRBaseMappingGroup]・listeners: Dict[str, IRListener]などの辞書群を持つ (python/ambassador/src/ambassador/ir/ir.py:90-118)。EnvoyConfig(Python) はIRから生成され、split_config()で bootstrap 設定・ADS 設定・clustermap に割れる (python/ambassador-diag/src/ambassador_diag/diagd.py:1628、python/ambassador-diag/src/ambassador_diag/diagd.py:1635)。FastpathSnapshot(Go) は Python を飛ばすパス用にecp_v3_cache.Snapshotと*Endpointsを運ぶ (pkg/ambex/fastpath.go:7-11)。MappingSpec(Go,v3alpha1) はユーザ向けのルーティングリソース (pkg/api/getambassador.io/v3alpha1/crd_mapping.go:27)。
追う価値のあるパス
Mapping リソースが Envoy のライブなルートになるまでを追う。
- watcher が snapshot を組み立てる。
WatchAllTheThings(cmd/entrypoint/watcher.go:26) がクラスタを watch しsnapshot.Snapshotを組み立てる (cmd/entrypoint/watcher.go:201)。 SnapshotReadyで (cmd/entrypoint/watcher.go:106-118)notifyクロージャがnotifyReconfigWebhooksを発火する (cmd/entrypoint/watcher.go:62-67)。- それが diagd に POST する。
notifyWebhookUrlがGetEventUrlの URL に POST し (cmd/entrypoint/notify.go:42、cmd/entrypoint/env.go:244)、ポート 8004 の_internal/v0/watt?url=<snapshot>に解決される (cmd/entrypoint/env.go:153)。 - diagd が受信する。
handle_watt_updateが?url=を読み("watt", url)をキューに入れる (python/ambassador-diag/src/ambassador_diag/diagd.py:916)。 - diagd がパースする。
load_config_wattが snapshot を fetch しディスクに保存しConfigに流し込む (python/ambassador-diag/src/ambassador_diag/diagd.py:1539)。 - diagd がコンパイルする。
_load_irがIR.check_deltasで complete か incremental かを判定し、IRを構築し、EnvoyConfig.generateに続けてsplit_config()を呼ぶ (python/ambassador-diag/src/ambassador_diag/diagd.py:1585-1635)。Mapping はMappingFactory.load_allでIRHTTPMapping群になる (python/ambassador/src/ambassador/ir/irmappingfactory.py:28)。 - diagd が検証する。
validate_envoy_configがenvoy --mode validateを走らせ、invalid ならエラーを返して現行設定を維持する (python/ambassador-diag/src/ambassador_diag/diagd.py:1652)。 - ambex が配信する。検証済み設定がディスクに書かれ、ambex が go-control-plane の
SnapshotCacheに読み込み、127.0.0.1:8003の ADS で Envoy に push する (pkg/ambex/main.go:6-40、cmd/entrypoint/entrypoint.go:164-167)。
text
WatchAllTheThings -> SnapshotReady -> notifyReconfigWebhooks
-> POST _internal/v0/watt?url= -> handle_watt_update
-> load_config_watt -> _load_ir (IR -> EnvoyConfig -> split_config)
-> validate_envoy_config (envoy --mode validate)
-> ambex SnapshotCache -> ADS push -> envoy読んで驚いた点
- endpoint ファストパスは Python を丸ごと飛ばす。 Pod churn は EDS の endpoint を変えるがルーティング構造は変えないので、entrypoint は endpoint だけの更新を
fastpathCh chan *ambex.FastpathSnapshotで直接 ambex に渡し (cmd/entrypoint/entrypoint.go:163-167)、ambex は EDS データだけを差し替える (pkg/ambex/fastpath.go:7)。激しい Pod churn 下でのリコンフィグ嵐を避けるためである。 - Emissary は swap 前に本物の Envoy で設定を検証する。 生成した ADS 設定を
envoy --mode validateに通し、失敗時はライブ設定を触らない (python/ambassador-diag/src/ambassador_diag/diagd.py:1652)。Emissary 自身の生成バグがユーザのトラフィックを落とせない。 - CRD のバージョン差はエンジンから隠蔽される。 複数バージョン (
v1・v2・v3alpha1) が併存するが、in-cluster のemissary-apiext変換 webhook (cmd/apiext/main.go、pkg/apiext) のおかげで Python エンジンが見るのは常にv3alpha1だけで、バージョン差を意識する必要がない。