Open8

k8s に関する知見をメモしていく

nissy-devnissy-dev

Pod を安全に終了する方法

このドキュメントがわかりやすかった
https://christina04.hatenablog.com/entry/kubernetes-pod-graceful-shutdown

k8s 側で設定すること

  • prestop: コンテナを終了する直前にコマンドを実行する
    • Pod が Service から切り離されるまでの時間に受け付けるリクエストをさばけるくらいの時間は必要
  • terminationGracePeriodSeconds: Pod を graceful に終了するための時間
    • prestop + gracefulshutdown 以上の時間を設定する、デフォルト 30s

アプリケーション側でやること

SIGTERM や SIGINT を受け取ったらライブラリで実装されている server.close メソッドを実行すれば良い。Node.js, fastify, express など基本的なライブラリではデフォルトで graceful shutdown に対応している。

nissy-devnissy-dev

Secrets の扱い

https://kubernetes.io/ja/docs/concepts/configuration/secret/

基本的には、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 というリソースで定義する。

https://kubernetes.io/ja/docs/concepts/configuration/configmap/

ここまでの説明でわかる通り、k8s の secret は値をbase64でエンコードしているだけなので、実際には sealed secret を利用してエンコードする

https://developers.cyberagent.co.jp/blog/archives/40766/

nissy-devnissy-dev

アクセス権限まわり

k8s のアクセス権限の管理には、RBAC (Role Based Access Conrtol) という方針をとっている。これは、ロールと権限を対応させて、ユーザーにはロールを割り当てる方法である。

RBAC とよく比較される方針として、ABAC (Attribute Based Acess Control) という方針がある。ここでの属性は、以下のように非常に柔軟に解釈される。このため柔軟な権限の付与ができる一方で、管理が複雑になるという問題もある。

ユーザーの特性(役職やセキュリティクリアランスなど)、実行するアクションの属性、またはデータをアクセスしている現在の時刻や物理的な場所といった「実世界」の特性など、ほとんどすべてのものが属性になり得ます。

https://www.cloudflare.com/learning/access-management/role-based-access-control-rbac/

なので、基本的に k8s では Role と ユーザー (ServiceAccount) を作成し、それを RoleBinding で紐づけるという方針でアクセス権限を管理する

Role

次の感じで各リソースに対する操作 (get, watch,....) を許可方式で定義できる。

https://kubernetes.io/ja/docs/reference/access-authn-authz/rbac/

k8s のリソースに関する API はいくつかの Group に分類されていて、そのことを apiGroup と呼ぶ。

https://qiita.com/tkusumi/items/300c566a74b6b64e7e89#api-group

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 を使って他のリソースを操作するための認証に使用されるアカウントのこと。

https://kubernetes.io/docs/concepts/security/service-accounts/

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を使う。

nissy-devnissy-dev

Pod の Eviction と PodDisruptionBudget

退避(eviction)とは、リソース不足のノードで1つ以上のPodを積極的に終了させるプロセスです。

機材の交換などで意図的に API 経由で Eviction する場合 (API Eviction) と k8s 側がノードのリソース逼迫により再度リソースの要求をするために Eviction する場合 (Node-Pressure Evication) がある。Node-Pressure Evication は、指定した Disk や Memory などの容量を超えて使用しているときなどに起こる。

https://kubernetes.io/docs/concepts/scheduling-eviction/node-pressure-eviction/

https://kubernetes.io/ja/docs/concepts/scheduling-eviction/api-eviction/

アプリケーションの可用性を高めるために、Eviction 時に指定のレプリカ数を維持しながら行うというルールを課すリソースが PodDisruptionBudget である。minAvailable: 1 のように設定すると、少なくとも1つのレプリカは稼働しながら Eviction が実行される。

https://kubernetes.io/docs/tasks/run-application/configure-pdb/

https://engineering.mercari.com/blog/entry/20231204-k8s-understanding-pdb/

nissy-devnissy-dev

Pod のノード分散

すべての Pod が同じノードで動いていると、そのノードが故障した際にすべての Pod が終了してしまう。アプリケーションの可用性を高めるために Pod のノード分散を行うのが好ましい。Pod 間で異なるノードに配置させる場合は PodAntiAffinity を、リージョン・ゾーン・ノードなどを考慮して分散させる場合は TopologySpreadConstraints を利用する。
https://kubernetes.io/ja/docs/concepts/scheduling-eviction/assign-pod-node

nissy-devnissy-dev

Pod の readiness / liveness probe の違い

  • liveness probe: コンテナの再起動を判断するために使われる
  • readiness probe : コンテナがトラフィックを受け入れる準備ができたかを判断する

service は readiness probe で ready になっていない pod を切り離す。rolling update は、作成したPodが readiness probe で ready と判定されてから古いPodの削除を行う。

Readiness Probeが失敗状態を返す場合、KubernetesはそのPodをすべての一致するサービスエンドポイントから取り除きます。

https://kubernetes.io/ja/docs/concepts/configuration/liveness-readiness-startup-probes/

Java などの JVM では、コンテナが ready になった直後は JIT コンパイルを行ったりするため、通常より性能が低くいきなり大量のリクエストが来てもさばけない場合がある。(liveness probe に失敗してしまう場合がある。) そうしたケースでは、Startup Probe でコンテナの wrap up が終わり通常の性能を獲得できるまで、liveness probe を遅延させ pod の再起動を防ぐことができる。

https://kubernetes.io/ja/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/#define-startup-probes