k8s に関する知見をメモしていく
Pod を安全に終了する方法
このドキュメントがわかりやすかった
k8s 側で設定すること
- prestop: コンテナを終了する直前にコマンドを実行する
- Pod が Service から切り離されるまでの時間に受け付けるリクエストをさばけるくらいの時間は必要
- terminationGracePeriodSeconds: Pod を graceful に終了するための時間
- prestop + gracefulshutdown 以上の時間を設定する、デフォルト 30s
アプリケーション側でやること
SIGTERM や SIGINT を受け取ったらライブラリで実装されている server.close メソッドを実行すれば良い。Node.js, fastify, express など基本的なライブラリではデフォルトで graceful shutdown に対応している。
StatefulSet のつかいどころ
Deployment でも StatefulSet でも Pod から PV を PVC で指定できるんだけど、Pod と PV との対応関係が変わる。
Secrets の扱い
基本的には、Opaque type として定義する使い方になる
apiVersion: v1
kind: Secret
metadata:
name: secret-sample
type: Opaque
data:
# 値は base64 でエンコードした値を入力する
secretkey: YmFyCg==
ドキュメント見ると basic 認証、Docker、TLS など特定のユースケースに沿った type も用意されている。
作成した Secrets は環境変数やファイルとして Container に渡すことができる。環境変数として次の感じで渡せる。
apiVersion: v1
kind: Pod
metadata:
name: secret-env-pod
spec:
containers:
- name: mycontainer
image: ubuntu
env:
- name: SECRET_Kay
valueFrom:
secretKeyRef:
name: secret-sample
key: secretkey
ちなみに secret でない定数 (設定ファイル) は configmap というリソースで定義する。
ここまでの説明でわかる通り、k8s の secret は値をbase64でエンコードしているだけなので、実際には sealed secret を利用してエンコードする
アクセス権限まわり
k8s のアクセス権限の管理には、RBAC (Role Based Access Conrtol) という方針をとっている。これは、ロールと権限を対応させて、ユーザーにはロールを割り当てる方法である。
RBAC とよく比較される方針として、ABAC (Attribute Based Acess Control) という方針がある。ここでの属性は、以下のように非常に柔軟に解釈される。このため柔軟な権限の付与ができる一方で、管理が複雑になるという問題もある。
ユーザーの特性(役職やセキュリティクリアランスなど)、実行するアクションの属性、またはデータをアクセスしている現在の時刻や物理的な場所といった「実世界」の特性など、ほとんどすべてのものが属性になり得ます。
なので、基本的に k8s では Role と ユーザー (ServiceAccount) を作成し、それを RoleBinding で紐づけるという方針でアクセス権限を管理する
Role
次の感じで各リソースに対する操作 (get, watch,....) を許可方式で定義できる。
k8s のリソースに関する API はいくつかの Group に分類されていて、そのことを apiGroup と呼ぶ。
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: sample-rbac]
name: sample-role
rules:
- apiGroups: [""] # "" indicates the core API group
resources: ["pods"]
verbs: ["get", "watch", "list"]
ServiceAccount
k8s のユーザーアカウントとは別で、Pod などが k8s の API を使って他のリソースを操作するための認証に使用されるアカウントのこと。
apiVersion: v1
kind: ServiceAccount
metadata:
name: sample-serviceaccount
namespace: sample-rbac
RoleBinding
今まで見てきた通り、RBAC の方針では ServiceAccount と Role を対応させる必要がある。そのためのリソース。
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
namespace: sample-rbac
roleRef:
kind: Role
name: sample-role
apiGroup: rbac.authorization.k8s.io
subjects:
- kind: ServiceAccount
name: sample-serviceaccount
namespace: sample-rbac
ちなみに今まで見てきたのは、特定の namespace のアクセス権限を定義する話。 クラスター内全体でのアクセス権限を制御するには、ClusterRoleやClusterRoleBindingを使う。
Pod の scheduling と preemption
プリエンプションは、優先度の低いPodを終了させて、より優先度の高いPodがノード上でスケジュールできるようにするプロセス
preemption は必ずしも PDB (PodDisruptionBudget) を尊重するわけではないので注意が必要である。
Pod の Eviction と PodDisruptionBudget
退避(eviction)とは、リソース不足のノードで1つ以上のPodを積極的に終了させるプロセスです。
機材の交換などで意図的に API 経由で Eviction する場合 (API Eviction) と k8s 側がノードのリソース逼迫により再度リソースの要求をするために Eviction する場合 (Node-Pressure Evication) がある。Node-Pressure Evication は、指定した Disk や Memory などの容量を超えて使用しているときなどに起こる。
アプリケーションの可用性を高めるために、Eviction 時に指定のレプリカ数を維持しながら行うというルールを課すリソースが PodDisruptionBudget である。minAvailable: 1 のように設定すると、少なくとも1つのレプリカは稼働しながら Eviction が実行される。
Pod のノード分散
すべての Pod が同じノードで動いていると、そのノードが故障した際にすべての Pod が終了してしまう。アプリケーションの可用性を高めるために Pod のノード分散を行うのが好ましい。Pod 間で異なるノードに配置させる場合は PodAntiAffinity を、リージョン・ゾーン・ノードなどを考慮して分散させる場合は TopologySpreadConstraints を利用する。
Pod の readiness / liveness probe の違い
- liveness probe: コンテナの再起動を判断するために使われる
- readiness probe : コンテナがトラフィックを受け入れる準備ができたかを判断する
service は readiness probe で ready になっていない pod を切り離す。rolling update は、作成したPodが readiness probe で ready と判定されてから古いPodの削除を行う。
Readiness Probeが失敗状態を返す場合、KubernetesはそのPodをすべての一致するサービスエンドポイントから取り除きます。
Java などの JVM では、コンテナが ready になった直後は JIT コンパイルを行ったりするため、通常より性能が低くいきなり大量のリクエストが来てもさばけない場合がある。(liveness probe に失敗してしまう場合がある。) そうしたケースでは、Startup Probe でコンテナの wrap up が終わり通常の性能を獲得できるまで、liveness probe を遅延させ pod の再起動を防ぐことができる。