Slide 1

Slide 1 text

How is Cilium Tested? Yutaro Hayakawa 1

Slide 2

Slide 2 text

Overview ● Ciliumにおけるテストの手法 ・ ツール ・ インフラ ● Ciliumのテストの難しさとその対処方法 2

Slide 3

Slide 3 text

Ciliumのアーキテクチャおさらい 3 Node (Linux) Agent Node (Linux) Agent External Network Node (Linux) Agent Data Path (Linux/eBPF) Data Path (Linux/eBPF) Data Path (Linux/eBPF) Operator

Slide 4

Slide 4 text

Ciliumのアーキテクチャおさらい 4 Node (Linux) Agent Node (Linux) Agent External Network Node (Linux) Agent Data Path (Linux/eBPF) Data Path (Linux/eBPF) Data Path (Linux/eBPF) Operator Cilium Agent 各ノードに常駐. Node-Localな処理 を担うコンポーネント. Data Path (Linux/eBPF) の設定, BGP, DNS Interception, L7 Proxy等.

Slide 5

Slide 5 text

Ciliumのアーキテクチャおさらい 5 Node (Linux) Agent Node (Linux) Agent External Network Node (Linux) Agent Data Path (Linux/eBPF) Data Path (Linux/eBPF) Data Path (Linux/eBPF) Operator Data Path eBPFだけでやっていると思われがち だが実際にはLinuxのNetwork Stack (IP routing, iptables, etc) も組み合わせて機能を作り込ん でいる.

Slide 6

Slide 6 text

Ciliumのアーキテクチャおさらい 6 Node (Linux) Agent Node (Linux) Agent External Network Node (Linux) Agent Data Path (Linux/eBPF) Data Path (Linux/eBPF) Data Path (Linux/eBPF) Operator Cilium Operator Clusterで1つ (HAできるがリーダ選出で1つに見 える). Cluster-Wideな処理を担う (IPAM, Cluster-wideなリソースの生成等)

Slide 7

Slide 7 text

Ciliumのテスト ● CiliumのコードはCPlaneはGo, eBPFの部分だけC ● テストの粒度 ○ Unit Test ○ E2E Test ○ (Integration Test) ○ (Performance Test / Scale Test) 7

Slide 8

Slide 8 text

Unit Test ● 数秒 - 1,2分以内に終わるくらいの小規模なテスト ○ フィードバックループの速さが大事 ● Goのパッケージごとのテスト ○ 普通のGoの単体テスト ○ eBPFのユーザスペースコードやNetlinkを叩くコードのテストはどうする? ■ Privileged Testと呼ばれていて特権がなければスキップされる ○ Kubernetes Controllerのテストはどうする? ■ クラスタを立てて本物のAPIサーバを使ってテストするのはなるべく避けたい ● eBPFのテスト ○ どうやってeBPFを単体テストする? ○ Ciliumを丸ごと立ち上げてパケットを通すテストは避けたい ■ セットアップが難しく, 実行に時間がかかるため 8

Slide 9

Slide 9 text

BPF_PROG_TEST_RUN ● bpf(2)の機能でLoadしたeBPFのプログラム をAttachせずに実行できる (ref) ○ まさにeBPFの単体テストのために作られた機能 ● そのままでも使えるが, ちょっとテスト走らせるま での手続きが面倒くさい ○ Goでパケット作ったりパースしたり ○ eBPFプログラムをロードしたり ○ もう少しテストを書くのに専念したい 9 eBPF Program Test Runner In Pkt Out Pkt bpf(BPF_PROG_TEST_RUN) User Kernel

Slide 10

Slide 10 text

eBPF Unit Test Framework ● eBPFの単体テストを書くための自作フレームワーク (ref) ● PKGENのコードの中でパケット (skb) を作る ● SETUPで実際のコードにskbを通す ● CHECKで出力が意図通りか確認する ○ ちゃんとNATされたか, 返り値 (PASS, DROP, REDIRECT) が意図通りか等 ● eBPFのコードのみでテストを書き切ることができる 10 PKTGEN Program Test Runner Empty InPkt SETUP Program InPkt OutPkt CHECK Program OutPkt Result bpf(BPF_PROG_TEST_RUN)

Slide 11

Slide 11 text

Coverbee ● eBPFのテストカバレッジが計測できるツール (ref) ● ロード前にコード書き換えで動的に収集コードを書き込む ● 自分でカバレッジ収集のコードを書かずに計測ができる 11

Slide 12

Slide 12 text

Kubernetes Controllerのテストどうする? ● 実際のクラスタにデプロイするところまではやりたくない ○ Unit Testはフィードバックループの速さが大事 ○ Kindを使ってもデプロイには2-3分かかる ○ 依存するコンポーネントだけを動かしてテストを書きたい ○ Kubernetes APIは本物を使わない. でもClientのコードはそのまま使いたい. 12

Slide 13

Slide 13 text

Hive/Cell Dependency Injection Framework ● DIフレームワーク (cilium/hive) ○ Go向けのいわゆるConstructor Injection ○ Hive == DIコンテナ, Cell == Constructor (蜂の巣のイメージ) ○ Cilium AgentやOperatorはHiveを中心にしたモジュラーモノリス構造 ● モジュール間の依存解決や初期化をDI任せにできる ○ Ciliumの機能の必要なSubsetだけを集めて動かすことができる ○ FakeClient (本物のClientと同じIfaceを実装している) もDIで本物とすげかえられる ○ 本物のAPIサーバがなくてもControllerのテストができる ● Demo 13

Slide 14

Slide 14 text

E2E Test ● k8sクラスタも含めた実環境に近い環境でのテスト ○ 実行に30分くらいかかる大きなテスト ○ ユーザが実際にCiliumを使う時と同じようなシナリオ ○ 本物のKubernetes環境を作って実際にトラフィックも作ってテストしたい ○ DPlaneのテストは実際にカーネルに設定を入れてパケットを通してテストしたい ○ k8sのRBACやCRDのバリデーションのルールなどを本物を使ってテストしたい ○ 三大クラウドのAPIに依存した機能は本物のクラウドでなければテストできない ● (余談) Kubernetesの標準APIを正しく実装しているかどうかもテストされる ○ NetworkPolicy, Service, Ingress, GatewayAPI等 ○ Conformance Testなどと呼ばれている 14

Slide 15

Slide 15 text

Connectivity Test ● Ciliumが動いているクラスタでCiliumの機能 を一通り動かして疎通確認するテストを走らせら れる ○ 新しい機能を作ったらここにシナリオを増やす ○ どんなk8sクラスタでも動かせる ○ ユーザがプロダクションで動かしてもOK ● CiliumのE2Eテストの基本的な構成はGHAで 環境構築をしてConnectivity Testを走らせ るという流れ ● Demo 15

Slide 16

Slide 16 text

LinuxカーネルバージョンごとのE2Eテスト ● E2Eテストでは複数のLinuxカーネルでテストをしている ○ CiliumはRHELカーネルとアップストリームのLTSカーネルで動作確認をしている ○ 基本的にLinuxは互換性を壊さないのになぜテストするのか? ■ 新しいカーネルのバージョンにしかない機能を使うことがある ■ Verifierへの変更等によってLoadできていたプログラムができなくなることがたまにある ■ Network Stackのバグを引くこともたまにある ○ カーネルがリリースされる前に問題を発見するためにbpf-nextツリーのカーネルもテストしている ● どうやって複数バージョンのカーネルをテストする? 16

Slide 17

Slide 17 text

Little VM Helper (LVH) ● CIに使うためだけの最小構成のVMを動かすQEMUラッパー (ref) ○ OCIフォーマットのイメージに入ったQCoWイメージを動かせる ○ コンテナレジストリにVMイメージをPushして配布できる ○ Dockerのような使用感でVMが動く ● カーネルを自動ビルドしてレジストリにPushするパイプラインが常に動いている (ref) ● Demo 17

Slide 18

Slide 18 text

E2Eテストの課題: テストケースの組み合わせ爆発 ● Ciliumは多機能な上にサポートしている動作環境が多い ○ 機能の組み合わせ * 動作環境 ● クラウド環境 ○ GCP, AWS, Azure, etc… ● Linuxカーネルのバージョン ○ ディストリビューション固有カーネル (RHELカーネルなど), LTSカーネル数バージョン ● Kubernetesのバージョン ○ アップストリームのバージョン3つ ○ クラウドの固有環境 (GKE, EKS, AKS, etc...) ● Cilium自身のバージョン ○ Upgrade/Downgradeテスト ● 最新のUpgrade/DowngradeテストはMatrixの数が25 ○ もちろん並列実行されるが1 Matrix当たり平均25分前後かかる ● 時間的にも経済的にも (CloudやGHAの使用料) コストが高い 18

Slide 19

Slide 19 text

E2Eテストの課題: Flakyなテスト ● テストがFlakyになりがち ○ E2Eテストは様々な要因で失敗する ■ CI環境の問題 ■ 確率的に発現するバグ ■ テストコードそのものの不備 ○ 動いているものが多いので失敗の原因を突き止めるのが非常に難しい ■ Ciliumは非常にたくさんのgoroutine (手元では381あった) が並行・並列に動く ■ 動きは全く決定的ではないのでRace Conditionなどは起きやすいし見逃しやすい 19

Slide 20

Slide 20 text

Flakyなテストの弊害 ● PRを出した時は `Required` とタグされたテストは全て通さなくてはマージできない ● Flakyなテストがあるとそのテストの失敗が自分の変更が原因でなのかそうでないのかわからない ○ 他の開発者が作ってしまったFlakeを自分がたまたま引いているだけかも ○ 逆も然りで, 自分がFlakeを作ってもたまたまテストが成功してマージしてしまうかもしれない ● 現状PRでテストを走らせるとほぼ毎回何かのテストが失敗する ○ テストの失敗はリトライする前にGitHubのIssueで報告することになっている (ref) ○ リトライにも30分以上かかるのでCIを通すのがPRの時間の中で支配的になることも ○ テストを信頼できないと皆がテストの失敗に無頓着になってしまうので本物のエラーが発見しづらくなる 20

Slide 21

Slide 21 text

Flakyテストに対する取り組み ● CI Health Manager ○ Flakyなテストを直すローテーションワーク (ref) ○ テスト実行の統計情報を見て失敗率の高いテストを直す ○ 現状はIsovalentの人がやっている ■ (コミュニティの中でやる人がいてもいいなと個人的には思う) ● E2Eテストの削減 ○ 一般的な話としてE2Eテストは減らした方がいい ○ E2Eテストが充実しているのは安心感があるが, 時間がかかり, Flakyになりやすく金銭的なコ ストも高い ○ なるべくUnit TestやIntegration Testでカバーできるケースを増やす 21

Slide 22

Slide 22 text

おわり 22