ICA模擬試験A
ICA模擬試験Aです。制限時間は120分、課題は17個、合格ラインは68%です。 部分点制なので、行き詰まった課題は飛ばして、あとで戻ってくるほうがよいです。
模擬試験なので、ヒントと正解を見ずに、まず最後まで解いてみてください。 時間が足りなくて解けなかったことと、分からなくて解けなかったことは別の問題で、その2つを区別して初めて、 次に何を勉強するかが決まります。すべて解いて採点したあとに開いてください。
実際の試験環境
試験中に、istio.io/docs、istio.io/blog、kubernetes.io/docsを見られます。
istio.io/search/でドキュメントサイトの中を検索することは許可されますが、外部の検索
結果をクリックしてはいけません。kエイリアスとbashの自動補完、そしてistioctlの
自動補完は、すでに設定されています。課題ごとに指定されたホストへsshして
作業し、入れ子のsshはサポートされません。ターミナルのコピーはCtrl+Shift+C、
貼り付けはCtrl+Shift+Vです。
この模擬試験の環境
Podの中の1人用クラスターで、そのまま作業します(sshは不要です)。 kube-apiserverは本物なので、誤ったマニフェストは実際に拒否されますが、サイドカーは 起動せず、トラフィックも流れません。採点は、クラスターに残された設定を 読み直して行います。
課題1が、残りのほとんどの前提です。Istioのリソースの種類が登録される前は、
kubectl applyがVirtualServiceを受け付けてくれません。まず終わらせてください。
1. デフォルトプロファイルのIstioのインストールマニフェストを作成して、/root/ica/install/manifest.yaml
に保存してください。そして、そのマニフェストの中のCustomResourceDefinitionだけを選んで、
クラスターに登録してください。この環境ではistiodのPodが起動しないので、必要なのは
APIの型だけです。
2. ネームスペースを3つ作成して、サイドカー注入のラベルを付けてください。
ica-shopにistio-injection=enabledica-legacyにistio-injection=disabledica-revにistio.io/rev=1-24。このネームスペースには、istio-injectionラベルを残さないでください。
3. Podのレベルで、ネームスペースの決定を覆してください。2つのDeploymentとも、
レプリカ1つ、イメージnginx:1.27-alpineです。
ica-shopのbatch-runner: Podテンプレートのラベルsidecar.istio.io/inject: "false"ica-legacyのlegacy-api: Podテンプレートのラベルapp: legacy-apiとsidecar.istio.io/inject: "true"
4. ica-shopにレビューのサービスを3つのバージョンで立ち上げて、subsetを定義してください。
- Deployment
reviews-v1・reviews-v2・reviews-v3、それぞれレプリカ1つ。Podのラベルはapp=reviewsとversion=v1/v2/v3。イメージnginx:1.27-alpine、コンテナポート9080。 - Service
reviews: ポート9080、ポート名http。selectorはapp=reviewsだけを置きます。 - DestinationRule
reviews:spec.hostはreviews.ica-shop.svc.cluster.local。subsetv1・v2・v3をそれぞれのversionラベルで定義し、trafficPolicy.loadBalancer.simpleをLEAST_REQUESTにします。
5. ica-shopにVirtualService reviewsを作成してください。ホストは
reviews.ica-shop.svc.cluster.localで、条件のないルール1つで、subset v1に
80、subset v2に20を分けます。
6. 課題5のVirtualService reviewsの前に、ルールを2つ追加して、合計3つに
してください。順序が採点の対象です。
- ヘッダー
end-userがちょうどtesterならsubsetv3 uriのプレフィックスが/api/v2で、かつメソッドがGETならsubsetv2- 課題5の重みのルールが、条件のない最後のルールとして残ります
7. 同じVirtualService reviewsの最後(条件のない)のルールに、レジリエンスの設定を
付けてください。全体のタイムアウト2s、リトライ3回、試行ごとのタイムアウト500ms、リトライの
条件は5xx・reset・connect-failureです。
8. DestinationRule reviewsのtrafficPolicyに、上限と外れ値の検知を
追加してください。
connectionPool.tcp.maxConnectionsは100connectionPool.http.http1MaxPendingRequestsは10connectionPool.http.maxRequestsPerConnectionは1outlierDetectionはconsecutive5xxErrors5、interval10s、baseEjectionTime30s、maxEjectionPercent50
9. ica-shopにIngressを立ててください。
- Gateway
shop-gateway: selectorはistio: ingressgateway。80ポート(名前http、プロトコルHTTP)はホストshop.example.comを受けて、HTTPSへ渡します。443ポート(名前https、プロトコルHTTPS)は、同じホストをSIMPLEモードで終端し、証明書のSecret名はshop-certです。 - VirtualService
shop-edge: ホストshop.example.com、ゲートウェイshop-gatewayに結び付け、uriのプレフィックス/reviewsを、reviews.ica-shop.svc.cluster.localの9080ポートへ送ります。
10. mTLSをメッシュ全体で強制しつつ、古いネームスペースだけを例外にしてください。
istio-systemネームスペースを作成し、その中にPeerAuthenticationdefaultをSTRICTで置きます。selectorは付けないでください。ica-legacyにPeerAuthenticationdefaultをPERMISSIVEで置きます。ここにもselectorは付けないでください。
11. ica-legacyのlegacy-apiワークロードだけをSTRICTに上げて、8080ポートは
例外にしてください。PeerAuthenticationの名前はlegacy-api、selectorは
app: legacy-api、mtls.modeはSTRICT、portLevelMtlsの8080は
DISABLEです。
12. ica-shopに認可ポリシーを2つ作成してください。どちらもselectorは
app: reviewsです。
reviews-allow:ALLOW。送信元のprincipalはcluster.local/ns/ica-shop/sa/productpage、許可する動作は、メソッドGETとパス/reviews*です。reviews-deny-admin:DENY。パス/admin*をブロックします。
13. ica-shopのapp: reviewsワークロードに、JWT検証を付けてください。
- RequestAuthentication
shop-jwt: issuerはhttps://auth.shop.example.com、jwksUriはhttps://auth.shop.example.com/.well-known/jwks.json - AuthorizationPolicy
require-jwt:ALLOW。requestPrincipalsはhttps://auth.shop.example.com/*
14. あるチームが、ica-fixネームスペースに書き込むと言って、次のVirtualServiceを
1つだけ渡してきました。このままでは、istioctl analyzeがエラーを出します。何が抜けているかを
診断して補い、istioctl analyze -n ica-fixがErrorを1つも出さないように
してください。
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
name: payments
namespace: ica-fix
spec:
hosts:
- payments.ica-fix.svc.cluster.local
http:
- route:
- destination:
host: payments.ica-fix.svc.cluster.local
subset: stable
補うものの仕様は次のとおりです。Deployment paymentsはPodのラベル
app=paymentsとversion=v1、Service paymentsはポート9090にポート名
http、DestinationRule paymentsは上と同じFQDNをhostに置き、subset
stableをversion: v1で定義します。このネームスペースに、課題と無関係な
リソースを残さないでください。残ると、analyzeがそれまで拾います。
15. ica-triageネームスペースに次のVirtualServiceを適用したところ、
x-beta: trueヘッダーを付けても、常にstableへ行きます。原因を診断して直したものを
適用してください。ルールは2つのままにします。
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
name: checkout
namespace: ica-triage
spec:
hosts:
- checkout.ica-triage.svc.cluster.local
http:
- route:
- destination:
host: checkout.ica-triage.svc.cluster.local
subset: stable
- match:
- headers:
x-beta:
exact: "true"
route:
- destination:
host: checkout.ica-triage.svc.cluster.local
subset: beta
16. 次のポリシーを入れたあと、ica-triageのpaymentsワークロードが、すべてのリクエストを
拒否します。原因を診断し、cluster.local/ns/ica-triage/sa/frontendが
POSTで/pay*にアクセスすることだけを許可するように直してください。
apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata:
name: payments-allow
namespace: ica-triage
spec:
selector:
matchLabels:
app: payments
action: ALLOW
rules: []
17. ica-triageに次の2つのリソースを置いたところ、ordersを呼び出す側がすべて
失敗します。サーバー側の要求はそのままにして、クライアント側を直してください。
apiVersion: security.istio.io/v1
kind: PeerAuthentication
metadata:
name: default
namespace: ica-triage
spec:
mtls:
mode: STRICT
---
apiVersion: networking.istio.io/v1
kind: DestinationRule
metadata:
name: orders
namespace: ica-triage
spec:
host: orders.ica-triage.svc.cluster.local
trafficPolicy:
tls:
mode: DISABLE
課題15から17までのica-triageには、ワークロードを作る必要はありません。直すべき
ものは設定です。
メッシュのAPIの型をクラスターに登録する
istioctl manifest generateは、チャートをバイナリの中に抱えているので、インターネットなしでも動作します。その結果にはistiodとServiceまで入っていますが、この環境で必要なのはCRDだけなので、kindで絞り込んでください。適用するときは、--server-sideを使ってください。Istio CRDはアノテーションが大きく、通常のapplyのlast-applied-configurationの上限(262144バイト)を超えます。
ネームスペースごとにサイドカー注入のラベルを付ける
リビジョンのラベルとistio-injectionラベルが1つのネームスペースに一緒にあると、istio-injectionが勝ちます。そのため、リビジョンに移す途中で以前のラベルを消さないと、新しいリビジョンが静かに無視されます。消すときは、ラベル名のあとにマイナス記号を付けます。
Podのレベルで注入の有無を覆す
sidecar.istio.io/injectは、Podテンプレートのラベルに付けます。Deployment自体のラベルやアノテーションではありません。値は、必ず引用符で囲んだ文字列である必要があります。Kubernetesのラベルの値は文字列しか受け付けないので、引用符なしで書くと、apiserverが拒否します。
バージョンごとのワークロードとsubsetを作成する
Serviceのselectorにversionを入れると、1つのバージョンだけが対象になり、重みによる分配がそもそも不可能になります。バージョンを分けるのは、DestinationRuleのsubsetの役割です。subsetのlabelsは、subset名ではなく、Podのラベルと文字どおりに同じである必要があり、spec.hostは、短い名前の代わりにFQDNで書いてください。Serviceのポート名は、Istioがプロトコルを判別する根拠です。
重みで80対20に分ける
重みは、route配列の各destinationに付き、合計が100である必要があります。このルールにはmatchを置かないでください。あとの課題で、条件の付いたルールがこの前に入ってきて、条件のないルールは、常に一番最後にある必要があります。
具体的なルールをcatch-allの前に置く
ルールは上から下へ評価され、最初にマッチしたものが勝ちます。条件のないルールが一番前にあると、あとのルールは永遠に評価されません。そして、2つの条件をANDで結ぶには、同じmatchブロックの中に並べて書く必要があります。ブロックを2つに分けると、ORになります。
タイムアウトとリトライの関係を合わせる
timeoutは、リトライを含む全体のデッドラインです。perTryTimeout掛ける試行回数がtimeoutを超えると、最後の試行は開始もできずに打ち切られます。ここでは、500ms掛ける3が1.5秒なので、2秒以内に収まります。retryOnは、カンマでつないだ文字列で、空にしておくと、どの失敗でリトライするかが決まりません。
コネクションプールの上限と外れ値の検知を設定する
2つは同じtrafficPolicyの中にありますが、行うことが違います。connectionPoolは、こちらから出ていく負荷を防ぎ、outlierDetectionは、相手のインスタンスが5xxを出し続けると、しばらく外します。maxEjectionPercentを書かないと、フィールドがそもそも保存されないので、値を明示してください。前の課題で作ったloadBalancerを、消さないように気をつけてください。
Ingressゲートウェイを立てる
Gatewayは、ポートを開けるだけで、ルーティングは行いません。VirtualServiceのspec.gatewaysに名前を書いて結び付けないと、そのルールはメッシュ内部にだけ適用され、外部のリクエストは404を受け取ります。httpsRedirectは、80ポートのserverのtlsの下に置きます。証明書は、ゲートウェイ用Podのネームスペースで、credentialNameで探されます。
メッシュ全体のSTRICTとネームスペースの例外
PeerAuthenticationは、3つの層でかかります。selectorがなく、ルートネームスペース(istio-system)にあればメッシュ全体、selectorなしで別のネームスペースにあれば、そのネームスペース全体、selectorがあればワークロードです。狭い範囲が、広い範囲に勝ちます。全体のポリシーにselectorを付けた瞬間に、それはもう全体ではありません。
ワークロードのSTRICTにポートの例外を置く
portLevelMtlsは、selectorがあるワークロードのポリシーでのみ動作します。selectorなしで書くと、apiserverが拒否します。そして、ここに書くポート番号は、Serviceのポートではなく、Podのコンテナポートです。Serviceのポートを書くと、例外がどこにもかからず、その事実はエラーなしで静かに見過ごされます。
ALLOWとDENYのポリシーを使い分ける
DENYはALLOWより先に評価され、1つでもかかればそこで終わりです。評価の順序はCUSTOM、DENY、ALLOWです。principalはServiceAccountのSPIFFEアイデンティティで、形式はcluster.local/ns/<네임스페이스>/sa/<서비스계정>です(プレースホルダーはネームスペースとServiceAccountです)。パスの末尾のアスタリスクは、プレフィックスのマッチです。
JWT検証とトークンの要求を一緒にかける
RequestAuthenticationは、トークンがあれば検証するだけで、トークンのないリクエストは、そのまま通します。トークンを要求するには、requestPrincipalsを使うAuthorizationPolicyを一緒にかける必要があります。requestPrincipalsの形式は、issuerとsubjectをスラッシュでつないだもので、発行者だけを制限したいなら、後ろをアスタリスクにします。
analyzeが指摘するものを補う
istioctl analyze -n ica-fixをまず実行して、何を見つけられないと言っているかを読んでください。IST0101は、参照したhost、またはhostとsubsetの組が見つからなかったという意味です。Serviceがなくても、DestinationRuleにそのsubsetがなくても、同じコードが出ます。1つずつ補ってもう一度実行すると、残りが減っていくのが分かります。
到達しないルールを生き返らせる
ルールは上から下へ評価され、最初にマッチしたものが勝ちます。条件のないルールは、すべてのリクエストにマッチするので、それが一番前にあると、あとのルールは、どんなリクエストでも評価されません。エラーは何も出ず、設定は有効なまま残るので、目で見つけるしかありません。
すべてのリクエストをブロックする、空のALLOWポリシーを直す
AuthorizationPolicyがあるワークロードに1つでもかかると、そのワークロードはデフォルト拒否に変わります。その状態でALLOWのルールが空だと、どのリクエストもルールにかからず、すべて拒否されます。ポリシーがないことと、空のポリシーがあることは、正反対の結果を出します。
サーバーの要求と食い違ったクライアントのTLSを直す
PeerAuthenticationは、サーバーが何を受け入れるかを決め、DestinationRuleのtrafficPolicy.tlsは、クライアントがどう接続するかを決めます。サーバーがSTRICTなのにクライアントがDISABLEだと、平文で接続して切断されます。どちらも有効な設定なので、どちらもエラーを出さないという点が、この障害を難しくします。サイドカーが代わりに証明書を扱ってくれるモードを選んでください。