この記事は私が過去 3 年ほど Kubernetes に携わる中で学んだ、ちょっと見つけにくい知識をまとめたものです。 特にカスタムコントローラーを開発するような人に必要となる知識群です。
感想とか指摘とかあれば Twitter までお寄せください。
更新履歴
- 2021-03-05: "コンテナの resources.limits と resources.requests の違いについて" の項を補足しました (thanks to @superbrothers)
API
kube-apiserver が備える拡張機構を列挙しなさい
回答例
- Custom resources: OpenAPI スキーマで独自のリソース型を追加できる
- Aggregation layer: kube-apiserver からリバースプロキシでリクエストを処理する
- Admission webhooks: etcd 保存前にリソースを編集もしくは検証できる
- Authentication webhook: token 認証を webhook で外部処理できる
- Authorization webhook: 認可を webhook で外部処理できる。
Admission コントロールの動作シーケンスについて説明しなさい
回答例
- 認証・認可
- Mutating webhook
- Object schema validation
- Validating webhook
- etcd に保存
複数の mutating webhook が同一リソースを編集する際の問題と解決方法を説明しなさい
回答例
ひとつの webhook が Pod 内の全てのコンテナにボリュームを追加し、別の webhook が Pod にコンテナを追加するとした場合、前者は後者の後に呼び出される必要がある。 しかし kube-apiserver に webhook の呼び出し順序を設定する方法はない。 解決方法として、前者の webhook の reinvocation policy をIfNeeded
にすれば良い。Admission webhook の呼び出しに失敗したときの挙動について説明しなさい
Kubernetes API の排他制御方式について説明しなさい
回答例
kube-apiserver に保存されているリソースはすべて resource version が与えられ、書き換えられる都度変化する。 Replace (PUT
) 操作の場合、Read (GET
) してきた時点の resource version と異なると操作に失敗する楽観ロックで排他制御される。リソースの一部を書き換える Patch 操作の場合は排他制御されない。Patch の種類を説明しなさい
回答例
- JSON Patch: 内蔵・カスタムどちらのリソースにも使える
- JSON Merge Patch: 同上。違いについては http://erosb.github.io/post/json-patch-vs-merge-patch/ を参照。
- Strategic Merge Patch: 内蔵リソースにしか使えない
- Server Side Apply: 内蔵・カスタムどちらのリソースにも使える。フィールドや要素毎に owner が管理される。フィールドを占有する場合は便利だが、
metadata.finalizers
のような共有フィールドでの使用は避ける。
cf. PATCH operations
サブリソースとは何か説明しなさい
回答例
リソース本体とは別個に REST API のエンドポイントが用意されるリソースの一部要素のこと。 典型的にはstatus
要素がサブリソースとして扱われる。サブリソースはメインリソースの編集操作では編集できない。 操作が分かれるため、RBAC による権限管理も別個に指定が必要となる。cf. Types (Kinds)
Kubernetes API のストレージバージョンとは何か説明しなさい
回答例
API リソースは互換性がない形で Kind が更新されることがあり、このような場合バージョンが変わる。 しかしながら、etcd に保存される形式は特定のバージョンのみで、REST API で要求されたバージョンに必要に応じて変換(convert)される。 etcd に保存されるバージョンのことを API のストレージバージョンと呼ぶ。リソース API のバージョンを更新する正しい進め方を説明しなさい
回答例
- 新しいバージョンを追加する。ストレージバージョンはまだ変えない。
- 新しいバージョンが安定したらストレージバージョンを新しいものに変更する。
- 上記の時点では、保存済みのリソースはまだ古いバージョンで保存されているため、replace して保存形式を新しいバージョンに変える。
- 古いバージョンを deprecate する。
- etcd の保存データがすべて新しいバージョンに変更した後、古いバージョンを完全に廃止する。
Conversion webhook はなぜ round-trip conversion を実装する必要があるか説明しなさい
回答例
ストレージバージョンが v1 で、REST API では v2 で Create するとする。 この際、v2 → v1 に変換しなければならない。次に保存したリソースを v2 で Get するとする。この際には v1 → v2 に変換する必要がある。 Create したものが Get できるようにするため、conversion webhook は変換の過程で情報が欠落しないよう実装しなければいけない。
Round-trip conversion で情報の欠落を防ぐにはどうするか説明しなさい
回答例
annotation に欠落する情報を保存するのが一般的。 例えば HPA v2 で増えたフィールドの情報は v1 では annotation として保存されている。Aggregation API server と kube-apiserver はどのように認証・認可しているか説明しなさい
回答例
Aggregation API server と kube-apiserver は相互に TLS で認証している。 認証の詳細については Authentication Flow を参照すること。Aggregation API server は kube-apiserver に SubjectAccessReview というリソースを作る権限を持つ必要がある。 権限を与えるには
kube-system
Namespace にあるextension-apiserver-authentication-reader
という組み込みの Role を Aggregation API server を動かしている ServiceAccount に RoleBinding で与える。
コントローラー実装
Event
リソースについて用途と生存期間を説明しなさい回答例
Event
リソースは他のリソースに起きた変化を記録するために作る。kubectl describe pods NAME
とすると指定の Pod に対応するEvent
が読みやすく表示される。標準では一時間しか保存されないため、必要に応じて吸いだす必要がある。
cf. Emitting, Consuming, and Presenting: The Event Lifecycle
Node
などのクラスタリソースのEvent
はどの namespace に作るべきか答えなさい回答例
default
namespaceReconcile とはどのような処理か説明しなさい
回答例
宣言的(Declarative) API を実装する要となる仕組みで、現在の状態を取得し、宣言された状態に適宜修正する処理のことを指す。kube-apiserver の watch API について説明しなさい
回答例
Watch API は、指定したリビジョン以降の変更を抜け漏れなくストリームで受信できる API である。 例えば Pod の作成を監視して動作するコントローラーを作成する場合、都度 GET で polling するより watch API で監視するほうが圧倒的に効率が良い。kube-apiserver の delete API について説明しなさい
回答例
Delete API は、指定したリソースの削除を開始する API である。 REST API としての DELETE リクエストが完了して成功しても、即座にリソースが kube-apiserver および etcd から削除されるとは限らない。kubectl delete
がリソースの削除が完了するまで待機するのは、kubectl
が kube-apiserver を監視して待機しているだけである。kubectl delete --wait=false
とすれば、削除の完了を待たない。コントローラーを実装する観点では、Delete API の呼び出しを受け付けた直後は Delete イベントではなく Update イベントが来ることがあるので注意すること。
metadata.deletionTimestamp
とは何か、どのように動作するかを説明しなさい回答例
Delete API を呼び出されたときに、即座に当該リソースを消せないときにセットされるフィールドである。 Pod を graceful に削除する場合や後述のmetadata.finalizers
が空でないときが該当する。 Update などで直接設定することはできない。Pod の場合
metadata.deletionTimestamp
の設定時刻を過ぎると Terminating という状態にあると判断され、コンテナプロセスには SIGKILL が送られる。 なお時刻を過ぎたからと言って API サーバーから削除されるわけではない。Pod 以外のリソースで graceful termination が可能なものがあるかは不明(ServiceAccount では指定できなかった)。
cf. Metadata
metadata.finalizers
とは何か、どのように動作するかを説明しなさい回答例
finalizers が空でない場合、kube-apiserver は DELETE リクエストを受けたリソースのmetadata.deletionTimpstamp
をセットするだけで etcd からは削除しない。 リソースを監視しているコントローラーは、deletionTimestamp がセットされている場合必要な後始末をしたのち、自身が管理する finalizers の要素を取り除く。finalizers が空になり次第 kube-apiserver は etcd からリソースを削除する。
cf. Using Finalizers
client-go が提供する k8s.io/client-go/tools/leaderelection パッケージについて説明しなさい
回答例
kube-apiserver 上のリソースを利用してリーダー選出する。 coordination.k8s.io のLease
を使うことが推奨されている。 このパッケージは古い leader が動き続けていないことを保証(fencing)はしてくれないので注意。実装例: https://github.com/kubernetes/client-go/blob/master/examples/leader-election/main.go
metadata.ownerReferences
とは何か、どのように動作するかを説明しなさい回答例
リソースを削除するときに、そのリソースの子となるリソースもまとめて消したいことが良くある。 そのような仕組みを実装するのがmetadata.ownerReference
で、親リソースを指定しておくと親が消えたら当該リソースも連鎖的に削除される。ownerReferences には controller 一つと controller でないものを複数指定可能だが、controller-runtime には簡単に設定できるユーティリティが存在する。 ただし、複数の owner がいる場合そのすべてが削除されてからリソースが GC されるので、controller でないものを指定するケースは滅多にない。
StatefulSet を削除すると VolumeClaimTemplate から作成された PVC/PV はどうなるか説明しなさい
回答例
それらの PVC/PV は放置される。PVC を自動で削除したい場合、コントローラーが PVC の ownerReference を所有者である StatefulSet ないしさらに親のカスタムリソースに設定すると良い。 Elastic Cloud on Kubernetes (ECK) では、Elasticsearch カスタムリソースが PVC の owner となっている。
プログラムと連携動作
以下のコンポーネントの役割を説明しなさい
- etcd
- kube-apiserver
- kube-controller-manager
- kube-scheduler
- kubelet
- kube-proxy
- containerd
- CoreDNS
回答例
- etcd: Kubernetes の永続データを保存する。ログや PersistentVolume のデータは対象外。
- kube-apiserver: etcd にアクセスする唯一のコンポーネント。様々な API を提供する。
- kube-controller-manager: kube-apiserver を見張り、各種の調整動作を行う。
- kube-scheduler: kube-apiserver を見張り、Node にスケジュールされていない Pod をスケジュールする。
- kubelet: 各 Node 上で動作し、Pod を動作させる。
- kube-proxy: 各 Node 上で動作し、Service のロードバランサを実装する。
- containerd: CRI という共通インタフェースで kubelet からコンテナ実行を請け負う。
- CoreDNS: Service の ClusterIP を DNS で解決するクラスタ内部の DNS サービスを実装する。
Pod リソースを作成して内部のコンテナが実際に動作するまでの各コンポーネントの動作を説明しなさい
回答例
- kube-apiserver が etcd に Pod リソースを保存する
- kube-scheduler が Node がまだ割り当てられていない Pod を見つける
- kube-scheduler は Node の空きリソースや affinity 条件を元に Node を割り当てる
- API サーバーを監視している kubelet が自 Node に割り当てられた Pod を見つける
- Pod 内で共有される Linux namespace (network など)のための infrastructure コンテナを作る
- CRI を通じて containerd などのコンテナランタイムにコンテナを作らせる
- CNI プラグインに network namespace を初期化させる
- Pod で定義されている initContainers を CRI で順番に作成し実行する
- Pod で定義されている containers を CRI で並列に作成し実行する
各 namespace にある
default
ServiceAccount は誰がいつ作るか説明しなさい回答例
namespace 毎に必ずあるdefault
ServiceAccount だが、実は namespace 作成直後には存在しない。namespace を監視している kube-controller-manager が少々遅れて作成する。 ServiceAccount が kube-apiserver にアクセスするトークンの Secret は、さらに遅れて作成される。
namespace 作成直後はまだ
default
ServiceAccount やトークンの Secret がなくて Pod を作れないということがありえる。 そのため、自動試験などでは Pod の作成が成功するまでリトライするか、Deployment を作るのが安全。kubelet または Node が応答不能の時に Pod に何が起こるか説明しなさい
回答例
kubelet が停止すると、kube-apiserver に heart beat が送られなくなり、速やかに taint が付けられる。 Pod には以下の toleration が付いているので標準で 300 秒は耐える。tolerations: - effect: NoExecute key: node.kubernetes.io/not-ready operator: Exists tolerationSeconds: 300 - effect: NoExecute key: node.kubernetes.io/unreachable operator: Exists tolerationSeconds: 300
300 秒を経過すると graceful termination が開始される。 Pod の
spec.terminationGracePeriodSeconds
は標準で 30 秒なので、metadata.deletionTimestamp
が今から 30 秒後に設定される。さらに 30 秒経過すると Pod の Status が Terminating 状態となる。 しかし、プロセスを管理する kubelet は停止しているのでそれ以上何もできず放置される。 つまり Pod は消えずに残る。
Pod が Terminating のときの ReplicaSet コントローラーの動作を説明しなさい
回答例
ReplicaSet コントローラーは通常、速やかに追加の Pod を新規に作成する。Pod が Terminating のときの StatefulSet コントローラーの動作を説明しなさい
回答例
StatefulSet コントローラーは Pod 名に stable network ID を付ける都合上、追加の Pod を新規に作成できない。Node の fencing 手順を説明しなさい
回答例
上記の通り、Node が応答不能になると Pod は Terminating のまま消えない。 Node リソースを消せば当該 Node 上の Pod もまとめて消えるが、Node と API サーバー間の通信障害で分断されていただけの場合、StatefulSet の同一 ID の Pod が二つクラスタ内で実行されて split brain を引き起こす恐れがある。それを避けるため、応答不能ノードは BMC 経由で電源を切る STONITH などで確実に活動停止させたのち、Node リソースを削除する。
資源管理
コンテナの resources.limits と resources.requests の違いについて説明しなさい
回答例
resources.limits は Linux カーネルの cgroups 機能を使い上限を越えて利用できないようにするのに使われる。resources.requests は kube-scheduler がどの Node に配置するかを計算するために使われる。 resources.requests.cpu は CFS share の設定にも使われており指定した CPU 時間を割り合いとして確保できるようになっている。
コンテナに resources.limits.memory だけ指定した場合の振る舞いを説明しなさい
回答例
limits だけ指定した場合、同じ値で requests が指定されたものとして振る舞う。 memory だけでなく cpu も同様。cf. Create a Pod that gets assigned a QoS class of Guaranteed
コンテナが request した以上のメモリを使うとどうなるか説明しなさい
回答例
request した以上にメモリを使っている Pod は、Node のメモリが不足した際に eviction の候補になる。 逆に、request 量未満であれば下記 QoS クラスや Priority クラスに関わらず通常は eviction されない。cf. Interactions between Pod priority and quality of service
Pod の QoS クラスについて説明しなさい
回答例
QoS クラスには Guaranteed, Burstable, BestEffort の 3 つがある。 CPU とメモリ両方の requests/limits を指定し、requests==limits である場合 Guaranteed となる。 Guaranteed 条件は満たさないが CPU かメモリを request していれば Burstable となる。 それ以外の Pod は BestEffort に分類される。QoS クラスは Node のメモリが不足した際に、eviction 対象の Pod を決めるために使われる。 まず BestEffort, 次に Burstable, 最後に Guaranteed となる。
といっても Guaranteed は requests==limits なので eviction 対象には通常ならない。 例外的な状況下でのみ evict される。
Pod の PriorityClass について説明しなさい
回答例
kube-scheduler が priority の低い Pod を削除し、高い優先度の Pod をスケジュールする preemption 動作をするのに使われる。Node の CPU リソースが不足するとき Pod が evict されるか答えなさい
回答例
evict されない。そのため、requests を適切に指定しておくことは重要といえる。 CPU リソースの requests がない場合、際限なく Node に Pod が詰め込まれてしまうため。
ネットワーク
Service の ClusterIP, NodePort, LoadBalancer タイプをそれぞれ説明しなさい
回答例
ClusterIP タイプの Service は ClusterIP と呼ばれるクラスタ内部でのみルーティング可能な仮想 IP アドレスを持つ。 ClusterIP 仮想アドレスは、他のタイプの Service にも必ずある。 ClusterIP は kube-proxy により、Node 上で複数の宛先 Pod の IP アドレスに DNAT される。DNAT には iptables や IPVS が利用される。NodePort タイプの Service はクラスタ内で一意なポート番号を持つ。すべてのクラスタ Node は当該ポート番号に送られたパケットを宛先 Pod に転送する。
LoadBalancer タイプの Service は ClusterIP および NodePort の機能に加えて、外部のロードバランサが割り当てる IP アドレスにパケットが届いたら宛先 Pod に転送する機能を持つ。
Service と Endpoints (EndpointSlices) の関係を説明しなさい
回答例
Service には同名の対となる Endpoint(Slice)s リソースがあり、Endpoint(Slice)s には宛先 Pod が列挙される。 Service が Pod のセレクタを持っていれば Endpoint(Slice)s は自動管理され、そうでない場合は他の手段で用意する。Pod の
spec.containers.ports
や DockerfileEXPOSE
の効果を説明しなさい回答例
spec.containers.ports
はname
をキーとしてlivenessProbe
や Service のtargetPort
で利用できる。 Dockerfile のEXPOSE
はもっぱらドキュメントとしての意味しかない。ports/EXPOSE に書いていないポート番号で TCP/UDP で listen することができる。 逆に、ports/EXPOSE に書いてあっても listen しているとは限らない。
Service の
spec.externalTrafficPolicy
が Local の場合、外部からのパケットはどう Pod に届くか説明しなさい回答例
externalTrafficPolicy は主に type=LoadBalancer の Service と併用される。 このフィールドを指定しないかデフォルトの Cluster の場合、kube-proxy は外部から届いたパケットのソースアドレスを Node のものに書き換え、Pod に転送する。 このフィールドが Local の場合、ソースアドレスは書き換えず、Node 上で動作している Pod にのみ転送する。Local の場合、Pod が動いていない Node に外部からのパケットが届くと行き先がなく通信に失敗する。 外部ロードバランサはそうならないよう、Pod がある Node にのみパケットを届ける。 例えば MetalLB の場合、Pod がある Node でのみロードバランサの IP アドレスを BGP で広告する。
モニタリング
readinessProbe とは何か、失敗すると何が起こるか説明しなさい
回答例
readinessProbe が失敗すると Pod は ready ではなくなる。 ready でない Pod は Service (Endpoints) の振り分け先から外される。livenessProbe とは何か、失敗すると何が起こるか説明しなさい
回答例
livenessProbe に失敗したコンテナは再起動される。
アクセスコントロール
クラスタワイドリソースの権限を Role で管理できるか?
回答例
できない。名前空間リソースの権限を ClusterRole で管理できるか?
回答例
できる。どの名前空間にあるリソースにもアクセスさせたい場合に設定する。default
サービスアカウントの権限はどうあるべきか説明しなさい回答例
default
サービスアカウントは、ServiceAccount を指定しない Pod が広く利用するため余計な権限を与えるべきでない。kube-apiserver の privilege escalation prevention について説明しなさい
回答例
ユーザーや ServiceAccount が (Cluster)RoleBinding を操作して他のエンティティに権限を許可する際に、kube-apiserver は操作しているユーザー/SerivceAccountが当該の権限を有しているかチェックする。 もし権限を有していない場合、RoleBinding の操作は失敗する。例えばコントローラーが管理下の ServiceAccount に権限を動的に追加しようとするなら、あらかじめコントローラーの ServiceAccount にも当該権限を持たせておかねばならない。
impersonate とはどういう権限か説明しなさい
回答例
任意の他のユーザーやグループになりすますことができる。cluster-admin にのみ許可されるべき権限。特権ユーザーが一般ユーザーの動作を確認するため
kubectl --as=USER --as-group=GROUP
で impersonate することができる。 この際--as-group=system:authenticated
も足さないといろいろ不都合があるので注意。標準で用意される
view
,edit
,admin
ClusterRole について説明しなさい回答例
これらは Aggregated ClusterRoles と呼ばれ、特定のラベルを持つ他の ClusterRole の権限を合成した権限を与える。- view: Secret など秘密情報を除き、幅広くリソースを閲覧する権限を与える。Pod のログも見れる。
- edit: 名前空間内の Role/RoleBinding を除くリソースを作成・閲覧できる。
- admin: Role/RoleBinding も含め名前空間内のリソースを作成・閲覧できる。