Certified Kubernetes Application Developer (CKAD) #21 実技模擬試験: 18 のタスク + 解説
#1 試験環境 から #20 試験のコツ まで、全ドメインを一周しました。このシリーズの最終回は読む記事ではなく 解く記事 です。実際の CKAD と同じく、全ドメインを統合した 18 のタスクを一か所に集めました。択一式ではなく、空のターミナルに直接リソースを作り出す実技シナリオで、各タスクには配点が付いています。
推奨される制限時間は実際の試験と同じ 2 時間 です。合格ラインは 66% で、18 のタスクの配点を合算して採点します。あるタスクで詰まったら印を付けて飛ばし、簡単なタスクから点数を積み上げる運用が合格ラインを越える道です。
各タスクは まず自力で最後まで解いてから 正解を開いてください。先に正解を読むと手が慣れません。正解には kubectl コマンドと必要な YAML、そしてなぜそう解くのかと、よく陥る落とし穴を押さえる解説を併記しています。
解き方 #
- minikube や kind でローカルクラスターを立ち上げます。NetworkPolicy のタスクまで正確に採点するには、Calico のような CNI がインストールされた環境が望ましいです (
minikube start --cni=calico)。 - タスクごとに指定された コンテキストとネームスペースを先に切り替えます。このシリーズで繰り返したとおり、コンテキストやネームスペースの設定ミスは正解を書いても 0 点です。
k config use-context <問題で指定されたコンテキスト>
k config set-context --current --namespace=<問題のネームスペース>- タスクに必要なネームスペースは開始前にあらかじめ作っておきます。
for ns in web build batch data sec net rbac probe cfg deploy; do
kubectl create namespace $ns 2>/dev/null
done- 18 個を 最後まで解いてから 正解を開いて一度に採点します。途中で正解を見ると実際の試験感覚が薄れます。#1 で整えた
alias k=kubectlとexport do="--dry-run=client -o yaml"のセットアップを先に適用すると時間を節約できます。
ドメイン分布 #
実際の CKAD のドメイン比重に合わせて 18 のタスクを配置しました。
| # | ドメイン | タスク数 | タスク番号 |
|---|---|---|---|
| 1 | Application Design and Build | 4 | 1, 2, 3, 4 |
| 2 | Application Deployment | 4 | 5, 6, 7, 8 |
| 3 | Observability and Maintenance | 3 | 9, 10, 11 |
| 4 | Environment·Configuration·Security | 4 | 12, 13, 14, 15 |
| 5 | Services and Networking | 3 | 16, 17, 18 |
配点はドメイン比重とタスクの難易度を反映し、合計 100 点としました。採点基準は記事末にまとめています。
タスク 1 (5 点): Application Design and Build #
ネームスペース web に nginx イメージで replicas が 3 の Deployment front を作成してください。続いてこの Deployment を 80 ポートで受けてコンテナの 80 ポートに転送する ClusterIP Service front-svc を作成してください。
正解
k -n web create deploy front --image=nginx --replicas=3
k -n web expose deploy front --name=front-svc --port=80 --target-port=80解説: 単純な作成タスクはマニフェストを手で書かず、命令型の generator で終えるのが最も速いです。expose は Deployment の selector を自動的に取得するため、Service の selector を自分で書く必要はありません。--target-port を省略すると --port と同じ値に設定されるので、2 つが等しいときは省略できます。
タスク 2 (6 点): Application Design and Build #
ネームスペース web に Pod cache-loader を作成してください。この Pod はメインコンテナとして redis イメージを実行し、その前に init container wait-dns が busybox イメージで until nslookup front-svc.web.svc.cluster.local; do sleep 2; done コマンドを実行して Service DNS が準備されるまで待機し、その後メインコンテナが起動するようにしてください。
正解
k -n web run cache-loader --image=redis $do > cache.yaml生成した骨組みに init container を追加します。
apiVersion: v1
kind: Pod
metadata:
name: cache-loader
namespace: web
spec:
initContainers:
- name: wait-dns
image: busybox
command: ["sh", "-c", "until nslookup front-svc.web.svc.cluster.local; do sleep 2; done"]
containers:
- name: cache-loader
image: redisk apply -f cache.yaml解説: init container はメインコンテナの起動前に順に実行され、成功して初めて次の段階に進みます。落とし穴は、command をシェル形式で与えるときに ["sh", "-c", "..."] で包まないと until のようなシェル構文が動作しない点です。タスク 1 の Service があって初めて init container が終了するため、2 つのタスクを一緒に検証できます。
タスク 3 (6 点): Application Design and Build #
ネームスペース web に Pod printer を作成してください。busybox イメージを使いますが、コンテナが起動したら echo CKAD-2026 && sleep 3600 コマンドを実行する必要があります。イメージのデフォルトの entrypoint ではなく、このコマンドで上書きしてください。
正解
k -n web run printer --image=busybox --command -- sh -c "echo CKAD-2026 && sleep 3600"生成されたマニフェストは次のような形です。
spec:
containers:
- name: printer
image: busybox
command: ["sh", "-c", "echo CKAD-2026 && sleep 3600"]解説: kubectl run で --command を付けると、-- の後ろのトークンがコンテナの command (Docker の entrypoint) に入ります。--command を外すと同じトークンが args に入ってデフォルトの entrypoint の引数として動作するため、意図と異なります。コマンド自体に && があるので、sh -c で包んでシェルに解釈させます。
タスク 4 (5 点): Application Design and Build #
ネームスペース build にコンテナイメージ httpd:2.4 を使う Pod web-static を作成し、ラベル tier=frontend と env=staging を付けてください。続いてラベル env=staging を持つ Pod だけを照会するコマンドを書いてください。
正解
k -n build run web-static --image=httpd:2.4 --labels="tier=frontend,env=staging"
k -n build get pods -l env=staging解説: --labels はカンマで複数のラベルを一度に付けます。ラベルセレクタ照会は -l key=value の形式で、-l env のように値を省略すると当該キーが存在するすべての Pod を、-l '!env' は当該キーがない Pod を選びます。ラベルは CKAD 全般で採点スクリプトがリソースを探す基準になるため、誤字があってはいけません。
タスク 5 (6 点): Application Deployment #
ネームスペース deploy に nginx:1.25 イメージで Deployment api を replicas 4 で作成してください。続いてイメージを nginx:1.26 にローリングアップデートし、ロールアウトが終わったら以前のバージョンにロールバックしてください。最後に現在のイメージが nginx:1.25 かを確認するコマンドを書いてください。
正解
k -n deploy create deploy api --image=nginx:1.25 --replicas=4
k -n deploy set image deploy/api nginx=nginx:1.26
k -n deploy rollout status deploy/api
k -n deploy rollout undo deploy/api
k -n deploy rollout status deploy/api
k -n deploy get deploy api -o jsonpath='{.spec.template.spec.containers[0].image}'解説: set image のコンテナ名は create deploy で作ったデフォルトのコンテナ名と一致する必要があります。ここでは Deployment 名と同じ nginx ではなく、generator が付けたコンテナ名を確認しますが、kubectl create deploy api --image=nginx:1.25 のコンテナ名はイメージの最初のトークンである nginx です。rollout undo は直前のリビジョンに戻し、特定のリビジョンを指定するには --to-revision=N を使います。
タスク 6 (6 点): Application Deployment #
ネームスペース batch に Job pi-calc を作成してください。perl イメージで perl -Mbignum=bpi -wle "print bpi(200)" コマンドを実行し、完了が 4 回起こる必要があり (completions)、同時に 2 個まで実行されるように (parallelism) 設定してください。再試行は最大 3 回に制限してください。
正解
k -n batch create job pi-calc --image=perl $do \
-- perl -Mbignum=bpi -wle "print bpi(200)" > pi.yaml生成した骨組みに completions、parallelism、backoffLimit を追加します。
apiVersion: batch/v1
kind: Job
metadata:
name: pi-calc
namespace: batch
spec:
completions: 4
parallelism: 2
backoffLimit: 3
template:
spec:
restartPolicy: Never
containers:
- name: pi-calc
image: perl
command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(200)"]k apply -f pi.yaml解説: completions は成功する必要がある Pod 数、parallelism は同時に立ち上げる数、backoffLimit は失敗時の再試行の上限です。generator はこの 3 つのフィールドを作ってくれないため、dry-run の骨組みに直接追加します。Job の restartPolicy は Never または OnFailure のみ許可され、Always は拒否されます。
タスク 7 (5 点): Application Deployment #
ネームスペース batch に CronJob cleanup を作成してください。busybox イメージで 5 分ごとに date; echo cleanup done コマンドを実行し、成功した Job 記録は 3 個、失敗した Job 記録は 1 個までだけ保存してください。
正解
k -n batch create cronjob cleanup --image=busybox \
--schedule="*/5 * * * *" $do -- sh -c "date; echo cleanup done" > cj.yaml生成した骨組みに history limit を追加します。
apiVersion: batch/v1
kind: CronJob
metadata:
name: cleanup
namespace: batch
spec:
schedule: "*/5 * * * *"
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 1
jobTemplate:
spec:
template:
spec:
restartPolicy: OnFailure
containers:
- name: cleanup
image: busybox
command: ["sh", "-c", "date; echo cleanup done"]k apply -f cj.yaml解説: successfulJobsHistoryLimit と failedJobsHistoryLimit は CronJob.spec の直下に置き、jobTemplate の中には置きません。スケジュール式は分・時・日・月・曜日の 5 フィールドで、*/5 は 5 分間隔を意味します。コマンドにセミコロンがあるので sh -c で包みました。
タスク 8 (5 点): Application Deployment #
ネームスペース deploy に次の 2 つのファイルを持つ kustomize オーバーレイを構成してください。base/deployment.yaml は nginx イメージの Deployment site で、オーバーレイは共通ラベル app=site とイメージタグ nginx:1.27 を適用してから kubectl apply -k でデプロイする必要があります。
正解
base/deployment.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: site
spec:
replicas: 1
selector:
matchLabels:
app: site
template:
metadata:
labels:
app: site
spec:
containers:
- name: nginx
image: nginxbase/kustomization.yaml:
resources:
- deployment.yamloverlay/kustomization.yaml:
resources:
- ../base
labels:
- pairs:
app: site
includeSelectors: false
images:
- name: nginx
newTag: "1.27"k -n deploy apply -k overlay解説: kustomize の images 変換器はマニフェストを直接修正せず、名前が一致するイメージのタグだけを変えます。ラベル変換は最新の kustomize では commonLabels の代わりに labels 項目を推奨し、selector まで変えたくないときは includeSelectors: false を置きます。apply -k はディレクトリから kustomization.yaml を探してビルド結果を適用します。
タスク 9 (6 点): Observability and Maintenance #
ネームスペース probe に Pod health を作成してください。nginx イメージを使い、コンテナの 80 ポートへ 5 秒ごとに HTTP GET / を送る liveness probe と、起動後 5 秒経ってから 3 秒ごとに同じパスを確認する readiness probe を付けてください。
正解
k -n probe run health --image=nginx $do > health.yaml生成した骨組みに 2 つの probe を追加します。
spec:
containers:
- name: health
image: nginx
ports:
- containerPort: 80
livenessProbe:
httpGet:
path: /
port: 80
periodSeconds: 5
readinessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 5
periodSeconds: 3k apply -f health.yaml解説: liveness は失敗するとコンテナを再起動し、readiness は失敗すると Service エンドポイントから除外します。initialDelaySeconds は最初の点検までの待機、periodSeconds は点検間隔です。probe の種類は httpGet・exec・tcpSocket の 3 つのうち 1 つで、フィールドパスが紛らわしいときは k explain pod.spec.containers.livenessProbe で確認します。
タスク 10 (5 点): Observability and Maintenance #
ネームスペース probe にすでに動作中の Pod broken が CrashLoopBackOff 状態です。原因を診断し、最も最近に終了したコンテナの直前のログをファイル /tmp/broken.log に保存してください。
正解
k -n probe describe pod broken
k -n probe logs broken --previous > /tmp/broken.log解説: CrashLoopBackOff はコンテナが繰り返し終了する状態なので、現在実行中のログが空のことがあります。--previous (-p) フラグは直前に終了したコンテナのログを取得する要です。describe の Events と Last State の Reason・Exit Code が原因を分け、OOMKilled・終了コード 1 などをここで読み取ります。
タスク 11 (5 点): Observability and Maintenance #
クラスター全体で CPU 使用量が最も高い Pod を探そうとしています。すべてのネームスペースの Pod のリソース使用量を CPU 基準で降順に並べて出力するコマンドを書いてください。metrics-server がインストールされていると仮定します。
正解
k top pod -A --sort-by=cpu解説: kubectl top は metrics-server のデータを読んでリアルタイムの使用量を表示します。-A は --all-namespaces の短縮で、--sort-by=cpu または --sort-by=memory で並び替えの基準を決めます。metrics-server がないと error: Metrics API not available が出るので、ローカルクラスターでは minikube addons enable metrics-server のような事前インストールが必要です。
タスク 12 (7 点): Environment·Configuration·Security #
ネームスペース cfg に ConfigMap app-cfg をキー APP_MODE=production、LOG_LEVEL=info で作成してください。続いて Pod reader (busybox、sleep 3600) を作成し、APP_MODE は環境変数として注入し、ConfigMap 全体は /etc/config パスに volume としてマウントしてください。
正解
k -n cfg create configmap app-cfg \
--from-literal=APP_MODE=production --from-literal=LOG_LEVEL=info
k -n cfg run reader --image=busybox $do --command -- sleep 3600 > reader.yaml生成した骨組みに env と volume を追加します。
spec:
containers:
- name: reader
image: busybox
command: ["sleep", "3600"]
env:
- name: APP_MODE
valueFrom:
configMapKeyRef:
name: app-cfg
key: APP_MODE
volumeMounts:
- name: cfg-vol
mountPath: /etc/config
volumes:
- name: cfg-vol
configMap:
name: app-cfgk apply -f reader.yaml解説: 単一キーを env で与えるときは configMapKeyRef、全体を env で与えるときは envFrom.configMapRef、ファイルとしてマウントするときは volumes.configMap を使います。volume としてマウントすると各キーが /etc/config/APP_MODE のようにファイルになり、env と違って ConfigMap の更新が自動反映されます。この違いは #13 の核心です。
タスク 13 (6 点): Environment·Configuration·Security #
ネームスペース sec に Secret db-cred をキー username=admin、password=s3cr3t で作成してください。続いて Pod db-client (busybox、sleep 3600) で password を環境変数 DB_PASSWORD として注入してください。
正解
k -n sec create secret generic db-cred \
--from-literal=username=admin --from-literal=password=s3cr3t
k -n sec run db-client --image=busybox $do --command -- sleep 3600 > db.yaml生成した骨組みに env を追加します。
spec:
containers:
- name: db-client
image: busybox
command: ["sleep", "3600"]
env:
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-cred
key: passwordk apply -f db.yaml解説: Secret の単一キーは secretKeyRef で取得し、ConfigMap の configMapKeyRef と構造が同じで参照の種類だけが異なります。kubectl create secret は値を自動的に base64 エンコードするため、自分でエンコードしません。値を検証するときは k -n sec get secret db-cred -o jsonpath='{.data.password}' | base64 -d でデコードします。
タスク 14 (6 点): Environment·Configuration·Security #
ネームスペース sec に Pod hardened を作成してください。nginx イメージを使いますが、コンテナを UID 1000、GID 3000 で実行し、権限昇格を禁止し (allowPrivilegeEscalation: false)、ルートファイルシステムを読み取り専用にしてください。
正解
k -n sec run hardened --image=nginx $do > hardened.yaml生成した骨組みに securityContext を追加します。
spec:
containers:
- name: hardened
image: nginx
securityContext:
runAsUser: 1000
runAsGroup: 3000
allowPrivilegeEscalation: false
readOnlyRootFilesystem: truek apply -f hardened.yaml解説: runAsUser・runAsGroup は Pod レベルとコンテナレベルの両方に置くことができ、コンテナレベルが優先されます。一方 allowPrivilegeEscalation と readOnlyRootFilesystem はコンテナレベルの securityContext にのみあります。nginx は読み取り専用のルートでは一時パスへの書き込みが塞がれて起動に失敗することがあるので、実際の運用では emptyDir で書き込みパスを別途マウントします。採点はフィールドの設定有無だけを見る場合が多いです。
タスク 15 (6 点): Environment·Configuration·Security #
ネームスペース data に Pod sized を作成してください。nginx イメージを使い、CPU 要求 100m・制限 200m、メモリ要求 128Mi・制限 256Mi を設定してください。この Pod の QoS class を確認するコマンドも書いてください。
正解
k -n data run sized --image=nginx $do > sized.yaml生成した骨組みに resources を追加します。
spec:
containers:
- name: sized
image: nginx
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "200m"
memory: "256Mi"k apply -f sized.yaml
k -n data get pod sized -o jsonpath='{.status.qosClass}'解説: requests と limits の両方が設定されていますが値が互いに異なるため、この Pod の QoS class は Burstable です。requests と limits がすべてのリソースで正確に同じなら Guaranteed、どの値もなければ BestEffort になります。CPU の m はミリコア、メモリの Mi はメビバイトの単位です。
タスク 16 (6 点): Services and Networking #
ネームスペース net に nginx イメージの Deployment shop を replicas 2 で作成し、クラスター外部からノードの 30080 ポートでアクセスできる NodePort Service shop-np で公開してください。Service のポートは 80、ターゲットポートは 80 です。
正解
k -n net create deploy shop --image=nginx --replicas=2
k -n net expose deploy shop --name=shop-np --type=NodePort --port=80 --target-port=80nodePort 値を 30080 に固定します。
k -n net patch svc shop-np --type='json' \
-p='[{"op":"replace","path":"/spec/ports/0/nodePort","value":30080}]'解説: expose で NodePort を作ると nodePort は 30000〜32767 の範囲で自動割り当てされます。特定の値に固定するには、マニフェストの spec.ports[0].nodePort を直接指定するか、上記のように patch します。dry-run で YAML を取り出して nodePort: 30080 を直接書いて apply する方法のほうが単純なこともあります。
タスク 17 (6 点): Services and Networking #
ネームスペース net に Ingress shop-ing を作成してください。ホスト shop.example.com のパス / に入るトラフィックを、タスク 16 で作った Service shop-np の 80 ポートにルーティングしてください。pathType は Prefix を使ってください。
正解
k -n net create ingress shop-ing \
--rule="shop.example.com/*=shop-np:80" $do > ing.yaml生成されたマニフェストは次のとおりです。
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: shop-ing
namespace: net
spec:
rules:
- host: shop.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: shop-np
port:
number: 80k apply -f ing.yaml解説: create ingress の --rule では host/path=service:port の形式を使い、パス末尾の /* は pathType: Prefix に変換されます。backend の port は number または name で指定します。実際にルーティングが動作するには ingress-nginx のような Ingress コントローラがクラスターにインストールされている必要がありますが、採点はリソース定義が正確かを見ます。
タスク 18 (4 点): Services and Networking #
ネームスペース net に NetworkPolicy db-allow を作成してください。ラベル app=db を持つ Pod への ingress トラフィックを、同じネームスペースでラベル role=api を持つ Pod が TCP 5432 ポートでアクセスするときだけ許可してください。それ以外の ingress はすべて遮断します。
正解
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: db-allow
namespace: net
spec:
podSelector:
matchLabels:
app: db
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
role: api
ports:
- protocol: TCP
port: 5432k apply -f db-allow.yaml解説: podSelector はポリシーが適用される対象 Pod (app=db) を選び、ingress.from は許可する出発地 (role=api) を選びます。NetworkPolicy は 1 つの Pod に 1 つでも適用されると、明示されていないトラフィックはすべて遮断されるため、別途の deny-all ポリシーなしでもそれ以外の ingress が塞がれます。from と ports を同じルールの中に置くと、2 つの条件の AND になります。NetworkPolicy はこれをサポートする CNI でのみ強制されます。
採点基準 #
各タスクの配点を合算して採点します。合計は 100 点で、66 点以上が合格圏 です。
| ドメイン | タスク・配点 | 小計 |
|---|---|---|
| Application Design and Build | 1(5) · 2(6) · 3(6) · 4(5) | 22 |
| Application Deployment | 5(6) · 6(6) · 7(5) · 8(5) | 22 |
| Observability and Maintenance | 9(6) · 10(5) · 11(5) | 16 |
| Environment·Configuration·Security | 12(7) · 13(6) · 14(6) · 15(6) | 25 |
| Services and Networking | 16(6) · 17(6) · 18(4) | 16 |
| 合計 | (合計) | 100 |
採点は実際の試験と同じく 成果物基準 です。コマンドをどう打ったかではなく、作られたリソースが要件と一致するかを見ます。1 つのタスクの中でもラベル・ポート・要求量のような項目別に部分点が分かれるため、詰まる項目があっても埋められる部分は最後まで埋めるほうが点数に有利です。
ドメイン別の弱点復習 #
採点後、点数が低いドメインを下表の該当記事に戻って復習してください。
| ドメイン | 関連タスク | 復習する記事 |
|---|---|---|
| Application Design and Build | 1, 2, 3, 4 | #2 · #3 · #4 |
| Application Deployment | 5, 6, 7, 8 | #5 · #7 · #9 · #10 |
| Observability and Maintenance | 9, 10, 11 | #11 · #12 |
| Environment·Configuration·Security | 12, 13, 14, 15 | #13 · #14 · #15 · #16 |
| Services and Networking | 16, 17, 18 | #18 · #19 |
特定のタスクで時間が足りなかったなら、ドメイン知識より手の速さの問題かもしれません。その場合は #1 kubectl セットアップ と #20 時間管理 を読み直し、同じ 18 のタスクを時間を計ってもう一度解いてください。CKAD は同じタスクを 2〜3 回繰り返すと、タスクあたりの所要時間が目に見えて減ります。
シリーズを終えて #
#1 の kubectl セットアップから出発し、Pod・マルチコンテナ・イメージ・ワークロード・デプロイ戦略・Helm・kustomize・probe・観測・ConfigMap・Secret・RBAC・SecurityContext・リソース・ボリューム・Service・Ingress・NetworkPolicy まで、CKAD の全ドメインを 21 編で通過しました。この模擬試験で 66 点を越えたなら、実際の試験会場でも十分に合格ラインを越えられる手ができています。おめでとうございます。
CKAD がアプリ開発者の最初の実技だったとすれば、次の段階はクラスター自体を運用する CKA です。ここで磨いた kubectl の速さと dry-run の習慣、マニフェスト感覚がそのまま足がかりになるので、合格の勢いをそのまま次の実技に引き継いでいくことをおすすめします。