Kubernetesディストリビューション — 自分で立てる
認定ロゴ付きの k3s が適合性テストに落ちた
目標
バージョンを固定したk3sで、Kubernetes公式の適合性テストを実際にいくつか選んで実行し、JUnitの結果を読んで、合格・失敗・タイムアウトを区別します。 適合性テストが何を確認し、何を確認しないかを、結果で説明できるようになります。
なぜ重要なのか
ディストリビューションを選ぶとき、「Certified Kubernetes」のロゴは強いシグナルに見えます。しかしその認定は、GAで必須のAPIと動作がアップストリームと同じであることを、テストスイート1つで確認した結果であり、テストにはノード2台以上のような前提があります。 性能・可用性・セキュリティ設定・オプション機能は、範囲外です。ロゴを正しく読むには、テストが何でできていて、失敗がどんな形で記録されるかを自分で見る必要があります。また、テストバイナリとサーバーのバージョンがずれると結果そのものが無意味になるので、バージョンを合わせることから始めます。最後に、わざとDNSを壊して、同じテストが不合格になり、復旧後に再び通過するのを見ると、適合性テストを「復旧の確認ツール」としても使えることがわかります。
ステップ
- 次のフィールドを書いてください(書き込み先:
/root/conformance/versions.json)。server(APIサーバーのgitVersion)、server_minor(1.36の形式)、e2e_test(e2e.test --versionの出力)、ginkgo(ginkgo versionのバージョン番号だけ。例:2.0.0)、same_minor(サーバーとe2e.testのマイナーバージョンが同じかどうか、ブール値)です。 /usr/local/conformance/conformance.yaml(v1.36.4タグの一覧)を読み取り、次のフィールドを書いてください(書き込み先:/root/conformance/catalog.json)。total(項目数)、sig_network(codenameが[sig-network]で始まる項目の数)、dns_tests(codenameが[sig-network] DNSで始まるcodenameをソートした配列)、dns_cluster_release([sig-network] DNS should provide DNS for the cluster [Conformance]のreleaseの値)です。e2e.testを--ginkgo.dry-runで2回実行し、次のフィールドを書いてください(書き込み先:/root/conformance/dryrun.json)。conformance_will_run(focusが\[Conformance\]のときに実行されるspecの数)、total_specs(全体のspecの数)、dns_will_run(focusが\[sig-network\] DNS.*\[Conformance\]のときに実行される数)、matches_catalog(conformance_will_runがステップ2のtotalと同じかどうか、ブール値)です。[sig-network] DNS should provide DNS for the cluster [Conformance]だけをfocusにして実行し、JUnitの結果を残し(--report-dir、保存先:/root/conformance/dns-pass/junit_01.xml)、標準出力とエラー出力も残してください(保存先:/root/conformance/dns-pass/e2e.log)。通過する必要があります。[sig-architecture] Conformance Tests should have at least two untainted nodes [Conformance]を実行して、結果を残し(保存先:/root/conformance/two-nodes/junit_01.xml、/root/conformance/two-nodes/e2e.log)、次のフィールドを書いてください(書き込み先:/root/conformance/two-nodes.json)。status(JUnitのそのtestcaseのstatus)、reason(e2e.logの[FAILED]の行のうち、ソースの位置(in [It] - ...)ではなく理由の文が書かれた行の文)、schedulable_nodes(現在テイントのないReadyノードの数、数値)です。- kube-systemのcoredns Deploymentを0に減らし、Podが消えたことを確認してから、ステップ4と同じDNSテストを
--ginkgo.timeout=45sで実行して、結果を残してください(保存先:/root/conformance/dns-broken/junit_01.xml、/root/conformance/dns-broken/e2e.log)。次のフィールドを書きます(書き込み先:/root/conformance/broken.json)。coredns_replicas(実行直前のspec.replicas)、coredns_pods(実行直前のCoreDNSのPodの数)、status(JUnitのそのtestcaseのstatus)です。復旧は次のステップで行います。 - corednsを1に戻してAvailableにしてから、同じDNSテストをもう一度実行して、結果を残してください(保存先:
/root/conformance/dns-restored/junit_01.xml、/root/conformance/dns-restored/e2e.log)。通過する必要があります。 - 次のフィールドを書いてください(書き込み先:
/root/conformance/report.json)。server(サーバーのgitVersion)、catalog_total(ステップ2のtotal)、passed(ステップ4と7で合格したtestcase名を、[It]なしで、重複なしにソートした配列)、failed_single_node(ステップ5で不合格になったテストのcodename)、timed_out_when_broken(ステップ6の結果がタイムアウトだったかどうか、ブール値)、submission_files(CNCFの認定PRに入れるファイル名4つをソートした配列)、certified_focus(認定のための実行で求められるE2E_FOCUSの値をそのまま)、skip_allowed(認定のための実行でE2E_SKIPを指定できるかどうか、ブール値)です。
参考
- VMの中にk3s v1.36.4+k3s1が1台と、同じバージョンの
e2e.test・ginkgo・conformance.yamlが/usr/local/conformanceにあります。テスト用のイメージはあらかじめ取得してあります。 - 基本の実行形式:
e2e.test --kubeconfig $KUBECONFIG --provider skeleton --ginkgo.no-color --report-dir <디렉터리> --ginkgo.focus='<정규식>'(プレースホルダーはディレクトリと正規表現です) - よくあるミス: focusの角括弧をエスケープしないこと。正規表現の文字集合として解釈されて、無関係なテストが数百個選ばれます。先に
--ginkgo.dry-runで個数を確認してください。 - よくあるミス: ステップ6を時間制限なしで実行することです。DNSの結果を600秒待ちます。
- 全体の適合性の実行(446個)は、このラボでは行いません。k3sの認定の提出は、2台構成で約2時間54分かかりました。
- ドキュメント: cncf/k8s-conformance・instructions.md・Conformance Testing in Kubernetes
テストバイナリとサーバーのバージョンを合わせる
次のフィールドを書いてください(書き込み先: /root/conformance/versions.json)。server(APIサーバーのgitVersion)、server_minor(1.36の形式)、e2e_test(e2e.test --versionの出力)、ginkgo(ginkgo versionのバージョン番号だけ。例: 2.0.0)、same_minor(サーバーとe2e.testのマイナーバージョンが同じかどうか、ブール値)です。
テストバイナリは/usr/local/conformanceにあります。k3sのバージョンには、+k3s1のような末尾の印が付きます。適合性テストはバージョンごとに一覧が変わるので、クラスターのバージョンと同じリリースブランチから作ったテストを使わないと、結果に意味がありません。
適合性テストの一覧を読み取る
/usr/local/conformance/conformance.yaml(v1.36.4タグの一覧)を読み取り、次のフィールドを書いてください(書き込み先: /root/conformance/catalog.json)。total(項目数)、sig_network(codenameが[sig-network]で始まる項目の数)、dns_tests(codenameが[sig-network] DNSで始まるcodenameをソートした配列)、dns_cluster_release([sig-network] DNS should provide DNS for the cluster [Conformance]のreleaseの値)です。
YAMLなので、codenameが複数行に折りたたまれている項目があります。grepで行を数えると間違うので、python3のyamlモジュールで読み取ってください。項目ごとに、testname・codename・description・release・fileのキーがあります。releaseは、そのテストが適合性に入ったバージョンです。
実行する前に範囲を数える
e2e.testを--ginkgo.dry-runで2回実行し、次のフィールドを書いてください(書き込み先: /root/conformance/dryrun.json)。conformance_will_run(focusが\[Conformance\]のときに実行されるspecの数)、total_specs(全体のspecの数)、dns_will_run(focusが\[sig-network\] DNS.*\[Conformance\]のときに実行される数)、matches_catalog(conformance_will_runがステップ2のtotalと同じかどうか、ブール値)です。
dry-runは、クラスターに何も作らずに、どのspecが選ばれるかだけを表示します。出力のWill run N of M specsの行を読み取ります。--ginkgo.no-colorを指定すると、色のコードが混ざりません。
適合性テストを1つ実際に実行する
[sig-network] DNS should provide DNS for the cluster [Conformance]だけをfocusにして実行し、JUnitの結果を残し(--report-dir、保存先: /root/conformance/dns-pass/junit_01.xml)、標準出力とエラー出力も残してください(保存先: /root/conformance/dns-pass/e2e.log)。通過する必要があります。
focusは正規表現なので、角括弧をエスケープする必要があります。名前の一部だけを書くと、似た名前のテストが一緒に選ばれることがあるので、dry-runで1個であることを先に確認してください。JUnitには、スキップしたspecもskippedとしてすべて入ります。
1台構成のクラスターが不合格になる適合性テスト
[sig-architecture] Conformance Tests should have at least two untainted nodes [Conformance]を実行して、結果を残し(保存先: /root/conformance/two-nodes/junit_01.xml、/root/conformance/two-nodes/e2e.log)、次のフィールドを書いてください(書き込み先: /root/conformance/two-nodes.json)。status(JUnitのそのtestcaseのstatus)、reason(e2e.logの[FAILED]の行のうち、ソースの位置(in [It] - ...)ではなく理由の文が書かれた行の文)、schedulable_nodes(現在テイントのないReadyノードの数、数値)です。
このテストは、失敗するのが正常です。適合性の提出で、k3sがどんな構成でテストを実行したか(cncf/k8s-conformanceのREADME)と比べてみてください。JUnitでは、failureの子要素とstatus属性を一緒に見ます。
CoreDNSを減らして同じテストを実行する
kube-systemのcoredns Deploymentを0に減らし、Podが消えたことを確認してから、ステップ4と同じDNSテストを--ginkgo.timeout=45sで実行して、結果を残してください(保存先: /root/conformance/dns-broken/junit_01.xml、/root/conformance/dns-broken/e2e.log)。次のフィールドを書きます(書き込み先: /root/conformance/broken.json)。coredns_replicas(実行直前のspec.replicas)、coredns_pods(実行直前のCoreDNSのPodの数)、status(JUnitのそのtestcaseのstatus)です。復旧は次のステップで行います。
このテストは、DNSの参照結果を600秒間待つので、時間制限なしで実行すると10分間止まったままになります。スイートの制限が終わると、ginkgoは失敗ではなくタイムアウトとして記録します。e2e.logで、どの名前の参照が失敗したかを探してみてください。
元に戻して、同じテストで証明する
corednsを1に戻してAvailableにしてから、同じDNSテストをもう一度実行して、結果を残してください(保存先: /root/conformance/dns-restored/junit_01.xml、/root/conformance/dns-restored/e2e.log)。通過する必要があります。
復旧を確認する最も確実な方法は、壊したときに不合格になったまさにそのテストを、もう一度通過させることです。前の実行のネームスペースがTerminatingのまま残っていても、テストは新しいネームスペースを作ります。
結果を適合性の言葉で解釈する
次のフィールドを書いてください(書き込み先: /root/conformance/report.json)。server(サーバーのgitVersion)、catalog_total(ステップ2のtotal)、passed(ステップ4と7で合格したtestcase名を、[It] なしで、重複なしにソートした配列)、failed_single_node(ステップ5で不合格になったテストのcodename)、timed_out_when_broken(ステップ6の結果がタイムアウトだったかどうか、ブール値)、submission_files(CNCFの認定PRに入れるファイル名4つをソートした配列)、certified_focus(認定のための実行で求められるE2E_FOCUSの値をそのまま)、skip_allowed(認定のための実行でE2E_SKIPを指定できるかどうか、ブール値)です。
前のステップのJUnitファイルから、名前とステータスをもう一度読み取ってください。提出ファイルとfocusおよびskipのルールは、cncf/k8s-conformanceのinstructions.mdにあります。