CKA模擬試験A
模擬試験です
実際のCKAは、120分で課題15–20個を解き、66%を超えると合格です。このセットも、課題17個・120分・合格ライン66%に合わせました。部分点制なので、すべて正解する必要はありません。17個のうち12個を通過すると、完了として扱われます。
ヒントを見ずに、まず最後まで解いてみてください。実際の試験にはヒントがありません。詰まった課題は飛ばして、時間が余ったら戻ってくるほうが、点数に有利です。ヒントと正解は、試験を一度終えたあとの復習用に使ってください。
実際の試験会場で初めて知ると時間を失うこと
- 試験はリモートデスクトップで、課題ごとに指定されたホストに
sshして作業します。ネストしたsshはサポートされていません。作業が終わったら、exitで必ず元の場所に戻ってください。 kエイリアスとbashの自動補完がすでに設定されています。開始してすぐにaliasを作れというアドバイスは、今の環境に合いません。このラボのPodも、同じように設定してあります。yq・curl・wget・manも、すでにインストールされています。- ターミナルのコピーは
Ctrl+Shift+C、貼り付けはCtrl+Shift+Vです。 Ctrl+Wはブラウザーのタブを閉じてしまいます。単語を消すときは、Ctrl+Alt+Wを使ってください。- INSERTキーが無効化されているので、vimは
iで入力モードに入る必要があります。 - 問題ごとに配点が異なり、正解に至る経路は複数が許容されます。
ドメイン配分
| ドメイン | 実際の配点 | このセットの課題 |
|---|---|---|
| Cluster Architecture, Installation and Configuration | 25% | 課題1–4 |
| Workloads and Scheduling | 15% | 課題5–7 |
| Services and Networking | 20% | 課題8–10 |
| Storage | 10% | 課題11–12 |
| Troubleshooting | 30% | 課題13–17 |
課題13から17まで
実際の試験では、壊れたリソースがあらかじめ用意されています。この環境にはその仕組みがないので、各課題の指示文に、問題のマニフェストを入れておきました。まずそのまま適用してから、症状を診断して直してください。適用せずに最初から正しく作っても採点は通過しますが、そうすると診断の練習になりません。
この環境で異なる点
Pod内のクラスターは、kwokctlが起動した1人用のクラスターです。コントロールプレーンは本物なので、マニフェスト・RBAC・スケジューリング・クォータ・CRD・PVのバインド・ドレインは、すべて実際に動作します。ただし、ワークロードのコンテナは実行されないので、kubectl exec・logs・port-forwardは使えません。課題も、それを要求しません。
開始前に、kubectl get nodesでノード3つがReadyかを確認してください。まだなら、クラスターが起動している最中です(2分ほどかかります)。
採点
各課題の確認ボタンを押すと、採点ツールが稼働中のクラスターで値を再計算して照合します。ファイルに何を書いたかではなく、クラスターがどんな状態かを見ます。正解に至る経路は、複数が許容されます。
opsネームスペースのデプロイ権限
ネームスペースopsを作成し、サービスアカウントdeployerを置いてください。
Roledeployerは、appsグループのdeploymentsに対するget・list・watch・create・updateと、coreグループのpodsに対するget・listを許可します。それ以外の権限はない必要があります。RoleBindingdeployerで、そのサービスアカウントに結び付けてください。
coreグループは、apiGroupsに空文字列で書きます。権限が広がるミスは、許可されたものだけを確認しても表に出ないので、kubectl auth can-i ... --as=system:serviceaccount:ops:deployerで、許可されてはいけない動詞も一緒に尋ねてください。
集約ClusterRole
ClusterRolemonitoring-viewを作成してください。ルールを直接書かず、ラベルrbac.example.com/aggregate-to-monitoring: "true"が付いたClusterRoleを集めるように、aggregationRuleを使います。
そのラベルが付いたClusterRolemonitoring-metricsは、coreグループのpodsとnodesに対するget・list・watchを許可します。
集約はコントローラーが行います。monitoring-viewのrulesを空にして作成すると、しばらくしてルールが埋まります。埋まらないなら、セレクターのラベルと実際のラベルが、1文字でも違っています。
Backup CRDとカスタムリソース
CRDbackups.ops.example.comを作成してください。グループはops.example.com、バージョンはv1(served・storage)、スコープはNamespaced、kindはBackup、複数形はbackups、短い名前はbkです。スキーマで、spec.scheduleは必須の文字列で、spec.retentionは整数です。
続けて、ネームスペースopsにBackupnightlyを作成し、scheduleを0 2 * * *、retentionを7にしてください。
CRDが登録された直後は、まだ提供されていません。kubectl wait --for=condition=Established crd/<이름>で待ってから(プレースホルダーはCRD名です)、カスタムリソースを作成してください。スキーマに型を書かないと、どんな値でも入ってしまいます。
team-aのクォータとデフォルトのリソース値
ネームスペースteam-aにResourceQuotateam-a-quotaを置いてください。上限は、pods 10、requests.cpu 2、requests.memory 4Gi、limits.cpu 4、limits.memory 8Giです。
同じネームスペースにLimitRangeteam-a-limitsを置き、リソースを書いていないコンテナに、limits cpu 500m・memory 512Miと、requests cpu 100m・memory 128Miが補完されるようにしてください。
LimitRangeのdefaultはlimitsを、defaultRequestはrequestsを補完します。実際に補完されるかを見るには、kubectl run ... --dry-run=server -o yamlでサーバーに尋ねればよいです。
frontendのDeploymentとローリング戦略
ネームスペースwebにDeploymentfrontendを作成してください。イメージはnginx:1.27、レプリカは4個、Podラベルはapp=frontendとtier=webです。
戦略はRollingUpdateで、maxSurge 1・maxUnavailable 0とし、リビジョン履歴は3個だけ残します。
maxUnavailable 0は、「更新中も、準備のできたPod数を決して減らさない」という意味です。数値でも百分率でも書けますが、採点ツールは、数値の1と0を期待します。
バッチ専用ノードとトレラレーション
ノードlab-node-batchを新規に作成してください。kwokが管理するように、アノテーションkwok.x-k8s.io/node: fakeを付け、ラベルworkload=batchとテイントworkload=batch:NoScheduleを設定します。
続けて、ネームスペースbatchにDeploymentcruncherを作成して、busybox:1.36をレプリカ3個で動かし、3つのPodがすべて、そのノードにだけ起動するようにしてください。
トレラレーションは、「行ける」とだけ言います。「そこへ行け」は、nodeSelectorかrequiredのnodeAffinityが言います。両方があってはじめて、配置が決まります。
すべてのノードで起動するエージェント
ネームスペースwebにDaemonSetnode-agentを作成してください。イメージはbusybox:1.36で、テイントが設定されたノードを含めて、クラスターのすべてのノードで起動する必要があります。
DaemonSetもスケジューラーを経由します。テイントが設定されたノードで起動するには、そのテイントに耐えるトレラレーションが必要です。どんなテイントにも耐えさせるには、キーを書かずに、operatorだけをExistsにすればよいです。
名前付きポートを使うapiサービス
ネームスペースwebにDeploymentapiを作成してください。イメージはnginx:1.27、レプリカ2個、コンテナポートは8080で、そのポートの名前はhttpです。
Serviceapiは、ClusterIPでポート80を受けて、数値ではなく、ポートの名前httpで転送する必要があります。
Serviceは、セレクターが間違っていても問題なく作成され、エンドポイントだけが空になります。kubectl get endpointslice -n web -l kubernetes.io/service-name=apiで、アドレスが付いているかを確認してください。
shop.example.comのIngress
IngressClassnginxを作成してください。controllerはk8s.io/ingress-nginxです。
ネームスペースwebに、frontendDeploymentを指すServicefrontend(ポート80)を作成し、Ingressshopで、ホストshop.example.comの/はfrontend:80に、/apiはapi:80に送ってください。どちらのパスも、pathTypeはPrefixです。
Ingressは、バックエンドのサービスがなくても作成されます。サービス名を間違えて書いてもエラーが出ないので、自分で確認してください。v1では、バックエンドをservice.nameとservice.port.numberで書きます。
webネームスペースのネットワークポリシー
ネームスペースwebのすべてのPodに対して、入ってくるトラフィックをデフォルトで遮断するNetworkPolicydefault-deny-ingressを作成してください。
続けて、NetworkPolicyapi-allowで、app=apiのPodに限り、同じネームスペースのapp=frontendのPodと、ネームスペースmonitoringから来るTCP 8080だけを許可してください。
全面遮断は、podSelectorを空のオブジェクトにして、ingressルールを1つも書かないことです。fromのリストの項目を分けて書けばORで、1つの項目の中にpodSelectorとnamespaceSelectorを一緒に書けばANDです。この問題はORです。
静的ボリュームのバインド
StorageClasslocal-fastを作成してください。provisionerはkubernetes.io/no-provisioner、reclaimPolicyはRetain、volumeBindingModeはImmediateです。
PVpv-fast-1は、2Gi・ReadWriteOnce・local-fast・hostPath/mnt/fast1です。ネームスペースstorageのPVCdataを、1Gi・ReadWriteOnce・local-fastで作成して、そのPVにバインドされるようにしてください。
PVCがPendingから抜け出せないなら、クラス名・アクセスモード・容量の3つのうち、どれかがPVと合っていません。要求容量はPVの容量より小さくてもかまいませんが、アクセスモードは、正確に含まれている必要があります。
PVCと一時ボリュームを一緒に使うワークロード
ネームスペースstorageにDeploymentwriterを作成してください。イメージはnginx:1.27、レプリカは1個です。
PVCdataを、ボリューム名dataで/dataにマウントし、emptyDirボリュームcacheを/cacheにマウントしてください。
ボリュームは、Podのスペックのvolumesに宣言し、コンテナのvolumeMountsで、名前で持ってきて使います。どちらか一方だけだと、Podが作成されないか、マウントが抜けます。
エンドポイントが空のサービス
まず、次のマニフェストをそのまま適用してください。
apiVersion: apps/v1
kind: Deployment
metadata: {name: shop, namespace: broken}
spec:
replicas: 3
selector: {matchLabels: {app: shop}}
template:
metadata: {labels: {app: shop}}
spec:
containers:
- name: shop
image: nginx:1.27
ports: [{containerPort: 8080}]
---
apiVersion: v1
kind: Service
metadata: {name: shop, namespace: broken}
spec:
selector: {app: shop-svc}
ports: [{port: 80, targetPort: 80, protocol: TCP}]
Podは3個とも起動するのに、Serviceshopにエンドポイントがまったく付きません。Deploymentには手を触れず、Serviceだけを直して、3つのPodがポート8080でつながるようにしてください。
直すべき場所は2か所です。エンドポイントがそもそも空になる理由と、ポートが見当違いの場所に行く理由は、それぞれ別のフィールドにあります。kubectl get svc shop -n broken -o yamlとPodのラベルを、並べて見てください。
Pendingから抜け出せないワークロード
まず、次のマニフェストをそのまま適用してください。
apiVersion: apps/v1
kind: Deployment
metadata: {name: report, namespace: broken}
spec:
replicas: 2
selector: {matchLabels: {app: report}}
template:
metadata: {labels: {app: report}}
spec:
containers:
- name: report
image: nginx:1.27
resources:
requests: {cpu: "16", memory: 64Mi}
PodがPendingから抜け出せません。レプリカ数とイメージはそのままにして、原因を取り除き、2つのPodがどちらもRunningになるようにしてください。
kubectl describe podのEventsや、PodのPodScheduled条件に、スケジューラーが理由を書いています。ノード1つが提供できる量は、kubectl describe nodeのAllocatableにあります。
広すぎる権限を絞る
まず、次のマニフェストをそのまま適用してください。
apiVersion: v1
kind: ServiceAccount
metadata: {name: ci, namespace: broken}
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata: {name: ci-admin}
roleRef: {apiGroup: rbac.authorization.k8s.io, kind: ClusterRole, name: cluster-admin}
subjects:
- {kind: ServiceAccount, name: ci, namespace: broken}
監査で、このバインディングが指摘されました。サービスアカウントciは削除せずに、権限だけを絞ってください。ciは、ネームスペースbroken内で、podsをget・list・watchし、deploymentsをget・list・watch・create・updateできる必要があり、それ以外は、クラスターのどこでも何もできない必要があります。
cluster-adminのバインディングを削除するだけでは終わりではありません。削除するとciは何の権限もなくなるので、必要な分を、ネームスペースの範囲で、改めて与える必要があります。確認は、kubectl auth can-i --as=system:serviceaccount:broken:ciで行います。
クォータに引っかかって起動できないワークロード
まず、次のマニフェストをそのまま適用してください。
apiVersion: v1
kind: ResourceQuota
metadata: {name: tight, namespace: quota-broken}
spec:
hard:
requests.cpu: 500m
limits.memory: 512Mi
---
apiVersion: apps/v1
kind: Deployment
metadata: {name: worker, namespace: quota-broken}
spec:
replicas: 3
selector: {matchLabels: {app: worker}}
template:
metadata: {labels: {app: worker}}
spec:
containers:
- name: worker
image: nginx:1.27
Podが1つも起動しません。クォータには手を触れず、ワークロード側だけを直して、レプリカ3個がすべてRunningになるようにしてください。
ReplicaSetの状態条件に、アドミッションが拒否した理由が、そのまま書かれています(kubectl describe rs -n quota-broken)。クォータにあるリソースが書かれていれば、そのネームスペースのすべてのPodが、そのリソースを必ず宣言する必要があります。
終わらないドレイン
まず、次のマニフェストをそのまま適用してください。
apiVersion: apps/v1
kind: Deployment
metadata: {name: edge, namespace: broken}
spec:
replicas: 2
selector: {matchLabels: {app: edge}}
template:
metadata: {labels: {app: edge}}
spec:
nodeSelector: {kubernetes.io/hostname: lab-node-0}
containers: [{name: edge, image: nginx:1.27}]
---
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata: {name: edge-pdb, namespace: broken}
spec:
minAvailable: 2
selector: {matchLabels: {app: edge}}
メンテナンスのために、ノードlab-node-0を空にする必要があります。ところが、ドレインが終わりません。PodDisruptionBudgetは削除せず、レプリカ2個を維持したまま、ドレインを終えてください。終わったあと、lab-node-0にはedgeのPodが1つもなく、2つのPodは別のノードでRunningである必要があります。
塞いでいるものが2つあります。1つはPodを空にできなくし、もう1つは、空にしても行き場がないようにします。kubectl get pdb -n brokenのALLOWED DISRUPTIONS列と、PodのnodeSelectorを、一緒に見てください。