これの続き。Part3
第9章 リソース管理とオートスケーリング
Kubernetes ではコンテナ単位にリソース制限を行うことができる。
CPU / メモリリソース制限
Kubernetes のリソース種別とその単位
- CPU : 1 = 1000m = 1vCPU
- メモリ : 1G = 1000M ( 1Gi = 1024 Mi )
リソース制限は Pod 定義内の各コンテナ定義部分に記載
- Requests は使用するリソースの下限を指定する
- 空き Node に Requests で指定した量のリソースが存在しない場合にはスケジュールされない
- Limits は使用するリソースの上限を指定する
- Requests とは異なり Node に Limits で指定したリソースが残っていなくてもスケジュールされる
「Requests のリソース量 < Limits のリソース量」となる。Requests と Limits の差が大きいコンテナで負荷が上がると、実際に使用したいリソース量と乖離するために注意が必要。
Requests のみ設定を行った場合、Limits は自動的に設定されず、ホスト側の負荷の上限までリソースを消費し続けるのでこのような Pod がたくさん稼働している Node 上ではリソースの奪い合いが発生し、メモリの場合は OOM(Out of Memory)が発生する。
Limits のみ設定を行った場合、Limits と同じ値が Requests に設定されるようになっている。オーバーコミットをさせる場合は、Limits だけでなく明示的に Requests も設定する
Ephemeral Storage
一般的にストレージは PersisitentVolume を用いるが、コンテナ再起動時に破棄されても良いデータはコンテナ内のディスク領域を利用することもある
- コンテナが出力するログ( kubectl logs で確認できるログ)
- emptyDir に書き込まれたデータ
- コンテナの書き込み可能なレイヤに書き込まれたデータ
システムに割り当てられるリソースと Eviction Manager
Kubernetes Node のリソース不足で全体に影響を及ぼしてしまうことを防止するために、各Node に 2種類のリソースがシステム向けに確保されている。
- kube-reserved : Kubernetes のシステムコンポーネントやコンテナランタイムに確保されるリソース
- system-reserved : OS に深く関わるデーモンなどに確保されるリソース
実際の Pod に割当可能なリソースは Node に存在するリソースの総量から上の2つを除いた量になる
オーバーコミットとリソース不足
Requests がリソースを超過するとスケジュールされずに Pending の Pod が出てくる
複数コンテナ利用時のリソース割り当て
Pod には複数のコンテナと複数の Initコンテナが内包されている
- Pod 単位でスケジュールされるので、必要なリソースはこれらの複数のコンテナを元に計算する
- 必要なリソース量は「すべてのコンテナのリソースの合計値」または「すべての Initコンテナのリソースの最大値」のうち大きい方を利用する
Cluster Autoscaler とリソース不足
Kubernetes クラスタ自体のオートスケーリングで、需要に応じて Kubernetes Node を自動的に追加していく機能
- ※ クラスタ全体や Node のロードアベレージが高まったタイミングでスケールさせるものではない
- Pending の Pod が出てきたタイミングで発動する
- Requests と Limits が適切に設定できていないと、実際のロードアベレージは低いのにスケールアウトしたり、ロードアベレージが高いのにスケールアウトされないことが起こりえる
- 基本的にリソースによるスケジュールは Requests(下限) が基準
- Requests と Limits に顕著な差をつけない
- Requests を大きくし過ぎないこと
LimitRange によるリソース制限
LimitRange を使用すると Pod に対して CPU やメモリのリソースの最小値 / 最大値、デフォルト値を設定できる( Namespace に対して )
ResourceQuota による Namespace のリソースクォータ制限
ResourceQuota を使用すると各 Namespace ごとに利用可能なリソースを制限できる。
既に作成されているリソースへは影響しない点に注意が必要。
`kubectl describe resourcequota` にて、現在の使用状況とクォータが確認できる。
- 作成可能なリソース数の制限
- リソース使用量の制限
HorizontalPodAutoscaler(HPA) : 水平スケール
Deployment / Replicaset / ReplicationController のレプリカ数をCPU負荷などに応じて Pod を自動的にスケールさせる。
- 負荷が高くなったらスケールアウト、負荷が低くなったらスケールイン
- Pod に Resource Request が設定されていない場合は動作しない
- 30秒に1回の頻度でオートスケーリングするべきかのチェックを行っている
VerticalPodAutoscaler(VPA) : 垂直スケール
コンテナに割り当てる CPU / メモリのリソースの割り当てを自動的にスケールさせる。
Pod に割り当てられているリソースの Requests と Limits を負荷に応じて、自動的にスケールさせる。
第10章 ヘルスチェックとコンテナのライフサイクル
Kubernetes Pod の正常性を判断をするためのヘルスチェック機構であり、異常終了した場合には Pod に設定された spec.restartPolicy に従って Pod を再起動する
Liveness / Readiness / Startup Probe 3種類のヘルスチェック機構
- Liveness Probe : Pod内のコンテナが正常に動作しているかの確認(失敗時はコンテナを再起動)
- Readiness Probe : Pod がリクエストを受け付けることができるかの確認(失敗時はトラフィックを流さない、Pod を再起動しない)
- Startup Readiness : Pod の初回起動が完了したかの確認(失敗時は他の Probe を実行し始めない)
3種類のヘルスチェック方式
- exec : コマンドを実行し、終了コードが 0 でなければ失敗
- httpGet : HTTP GET リクエストを実行し、Status Code が 200 - 399 でなければ失敗
- tcpSocket : TCP セッションが確立できなければ失敗
ヘルスチェックの間隔
- initialDelaySeconds : 初回ヘルスチェック開始までの遅延(最大で periodSecods の分だけ延長)
- periodSeconds : ヘルスチェック間隔の秒数
- timeoutSeconds : タイムアウトまでの秒数
- successThreshold : 成功と判断するまでのチェック回数
- failureThreshold : 失敗と判断するまでのチェック回数
コンテナのライフサイクルと再起動(restartPolicy)
- Always : Pod が停止すると、常に Pod を再起動
- OnFailure : Pod の停止が予期せぬ停止(終了コード 0以外)の場合に再起動
- Never : Pod が停止しても、Pod を再起動させない
Init Containers
Pod 内でメインとなるコンテナを起動する前に別のコンテナを起動させるための機能。
これを使うことでセットアップ用のスクリプトなどをメインコンテナに含まない状態を保つことができる。
起動直後と終了直前にコマンドを実行する(postStart / preStop)
コンテナの起動後と停止直前に任意のコマンドを実行することが可能。
- PostStart : コンテナの作成直後に実行
- PreStop : コンテナ停止前に実行
これらが可能となる。
- exec : 任意のコマンドを実行
- httpGet : HTTP GET リクエストを送信
Pod の安全な停止とタイミング
Pod の削除が行われる際には、安全に停止するために何段階に分けて処理が実行される。
- Pod の削除要求が Kubernetes APIサーバーに届くと非同期に「preStop + SIGTERM」と Service からの除外設定が行われる
第11章 メンテナンスとノード停止
ノードの停止と Pod の停止
Kubernetes Node を安全に停止する際には、いくつかのステップを踏む必要がある。
現時点で Docker コンテナのライブマイグレーションのような機能はない。
- メンテナンス等でノードを停止させる場合には Pod を停止する必要がある
- SIGTERM, SIGKILL に対応したアプリケーションを作ることと terminationGracePeriodSeconds を適切に設定することが必要
スケジューリングの対象からの除外と復帰(cordon / uncordon)
Kubernetes Node は SchedulingEnabled と SchedulingDisabled のどちらかのステータスを持つ。( デフォルトは SchedulingEnabled )
- Node を SchedulingDisabled に変更し、スケジューラーの候補から外す場合は `kubectl cordon` を使う
- Node を SchedulingEnabled に変更し、スケジューラーの候補に戻す場合は `kubectl uncordon` を使う
ノードの排出処理による Pod の退避(drain)
- Node のステータスを SchedulingDisabled に変更しても、以降のスケジューリング候補から外れるだけで、既に実行されている Pod は停止されない
- Node 上で実行しているすべての Pod を退避させる処理を行うには `kubectl drain` コマンドを使う
- 処理が開始されると Node を SchedulingDisabled に変更し、スケジューリング候補から外して、各 Pod に対して SIGTERM シグナルを送信し、Pod の退避を行う
# Node上で実行しているPodをすべて退避する(DaemonSet以外)
$ kubectl drain < Node名 > --force --ignore-daemonsets
# drain が実行されると、停止可能なPodは退避処理(eviction)が行われる
`kubectl drain` コマンドの実行時に下記のエラーが出ることがある
- Deployment などで管理されていない Pod を削除しようとした場合
- local storage を使っている Pod を削除しようとした場合(--delete-local--data)
- DaemonSet の管理下にある Pod を削除しようとした場合(--ignore-daemonsets)
AWS の EKS のノードグループのバージョンアップの際は drain され、Node が入れ替わっていた。
※ Node のメンテナンスが気軽にできるように、各 Pod に停止処理が実行された際に安全に止まるように適切な設定を行っておく( preStop, gracefulPeriod, アプリケーション側の SIGTERM など )
PodDisruptionBudget(PDB)
Node が排出処理を行う際にPod を停止することのできる数を制限するリソース
Node 上の Pod を追い出す際に同一種別の Pod が同じタイミングで停止しないように担保することが可能。
第12章 高度で柔軟なスケジューリング
フィルタリングとスコアリング
Kubernetes のスケジューリングには「フィルタリング」と「スコアリング」の過程がある。
- フィルタリングでは、スケジューリングする Pod が割当可能な Node を計算する
- Pod をスケジューリングするのに十分なリソースがあるか
- 必須条件として指定されたラベルを持つ Node かどうか ...etc
- スコアリングでは、フィルタリングされた後の Node の一覧を順位付けし、最も適したノードを計算する
- Deployment をできるだけ分散して Node に配置したり
- 既に利用するコンテナイメージが Pull してあるノードを優先したり
- 優先条件として指定された Label をもつ Node を優先したりする
マニフェストで指定するスケジューリング
フィルタリングやスコアリングは、特に何もしなくても Kubernetes によって計算されてスケジューリングに利用される。
一部の設定はマニフェストに追加指定することで、スケジューリングに影響を与えることができる。
2種類ある
- Kubernetes 利用者が配置したい Node を選択する方法
- nodeSelector(Simple Node Affinity) : 簡易的な Node Affinity 機能
- Node Affinity : 特定の Node 上だけで実行する
-
- Node Anti-Affinity : 特定の Node 以外で実行する
- 厳密には Node Affinity の spec.affinity.nodeAffinity の条件部分に否定系のオペレータを指定
- Inter-Pod Affinity : 特定の Pod がいるドメイン上で実行する
- Pod 定義内の spec.affinity.podAffinity で指定することができる
Pod 同士を近づけて配置できるので Pod間通信のレイテンシを下げることができる
- Pod 定義内の spec.affinity.podAffinity で指定することができる
- Inter-Pod Anti-Affinity : 特定の Pod がいないドメイン上で実行する
- Pod 定義内の spec.affinity.podAntiAffinity で指定することができる
- Node Anti-Affinity : 特定の Node 以外で実行する
- Kubernetes 管理者が配置してほしくない Node を指定する方法
- Taints / Tolerations : Node に対して汚れ(Taints)を付けておき、それを許容(Tolerations)できる Pod のみスケジューリングを許可するポリシー
- 指定しない限り、その Node にスケジューリングされることはない
- 条件に合致しない Pod をその Node 上から追い出すことも可能になる
- 対象 Node を特定の用途に向けて専用 Node にする場合などに利用する
Taints / Tolerations で利用できる Effect
# 特定NodeにTaintsを付与
$ kubectl taint node <Node名> <Key>=<Value>:<Effect>
# 特定Labelを持つ全NodeにTaintsを付与
$ kubectl taint node -l kubernetes.io/os=linux <Key>=<Value>:<Effect>
- PreferNoSchedule : 可能な限りスケジューリングしない
- NoSchedule : スケジューリングしない
- 既にスケジューリングされている Pod はそのまま
- NoExecute : 実行を許可しない
- 既にスケジューリングされている Pod は停止される
PriorityClass による Pod の優先度と退避
Pod に対して、優先度を指定することができる。
既にスケジューリングされている優先度の低い Pod を停止して、優先度の高い Pod を起動することも可能。
その他、独自でスケジューラーを作成することも可能
Kubernetes のスケジューラは未割り当ての Pod を確認し、どの Node に割り当てるかが決定されると API 越しに Node を指定する仕組みになっている。
定期的に Kubernetes Master の API サーバーに確認を行い、割り当てを行う独自のスケジューラプログラムを実装すれば置き換えることができる。
※ 基本的には標準のスケジューラで十分。
第13章 セキュリティ
ServiceAccount
Kubernetes はユーザーに似たコンセプトとして UserAccount と ServiceAccount がある。
- UserAccount は AWS の EKS では IAM とリンクしており Kubernetes の管理対象ではなく Namespace の影響は受けない。
- ServiceAccount は Kubernetes の世界で完結する。Pod で実行されるプロセスのために割り当てられ Namespace に紐づく。Pod 起動時には必ず 1つ割り当てる必要があり、ベースで認証 / 許可 を行っている。
- 指定しない場合は default Service Account が割り当てられる
RBAC (Role Based Access Control)
どういった操作を許可するのかを定めた Role を作成し ServiceAccount などの User に対して Role も紐付ける(RoleBinding) ことで権限を付与する。Role と RoleBinding、ClusterRole と ClusterRoleBinding がある。
AggregationRule を利用することで、複数 Role を元に集約した Role を作成し Role の管理性を高めることもできる。
Role と ClusterRole
Role と ClusterRole のいずれも Namespace スコープのリソースを対象とした許可設定を行う。作成時には apiGroups, resources, verbs の 3つを指定する。
Role に指定可能な実行できる操作(verbs)
- * : すべての処理
- create : 作成
- delete : 削除
- get : 取得
- list : 一覧取得
- patch : 一部変更
- update : 更新
- watch : 変更の追従
Kubernetes が作成する ClusterRole で用意されているプリセット
- cluster-admin : すべてのリソースを管理可能
- admin : ClusterRoleの編集 + Namespace レベルのRBAC
- edit : ReadWrite
- view : ReadOnly
Kubernetes のシステムコンポーネントが利用している ClusterRole の一例
- system:controller:attachdetach-controller
- system:controller:certificate-controller
- system:controller:clusterrole-aggregation-controller
- system:controller:cronjob-controller
- system:controller:daemon-set-controller
- system:controller:deployment-controller
SecurityContext
個々のコンテナに対してセキュリティの設定ができる。
- privileged : 特権コンテナとして実行
- capabilities : Capabilities の追加と削除
- allowPrivilegeEscalation : コンテナ実行時に親プロセス以上の権限を与えるかどうか
- readOnlyRootFilesystem : root ファイルシステムを ReadOnly にするかどうか
- runAsUser : 実行するユーザー
- runAsGroup : 実行するグループ
- runAsNonRoot : root での実行を拒否する
- seLinuxOptions : SELinux のオプション
PodSecurityContext
Pod(すべてのコンテナ)に対してセキュリティの設定ができる。
- runAsUser : 実行するユーザー
- runAsGroup : 実行するグループ
- runAsNonRoot : root での実行を拒否する
- supplementalGroups : プライマリGIDに追加で付与するGIDのリストを指定
- fsGroup : ファイルシステムのグループを指定
- sysctls : 上書きするカーネルパラメータを指定
- seLinuxOptions : SELinux のオプションを指定
PodSecurityPolicy
Kubernetes クラスタに対してセキュリティポリシーによる制限を行うリソース
Kubernetes in v1.26 を使っているのでもう削除されている。
> Removed feature
> PodSecurityPolicy was deprecated in Kubernetes v1.21, and removed from Kubernetes in v1.25.
NetworkPolicy
Kubernetes クラスタ内で Pod 同士が通信する際のトラフィックルールを規定する。
- 利用しない場合、クラスタ内のすべての Pod 同士で通信を行うことが可能。
- 利用できれば、Namespace ごとにトラフィックを転送しないようにしたり、基本的にすべての Pod 間の通信を断絶して特定の Pod 間だけ通信を許可することが可能。
種類と制限範囲(Policy:Ingress:Egress)
-
podSelector : 特定の Pod からの通信を許可:特定の Pod への通信を許可
-
namespaceSelector : 特定の Namespace上の Pod からの通信を許可 : 特定の Namespace上の Pod への通信を許可
-
ipBlock : 特定の CIDR からの通信を許可 : 特定の CIDR への通信を許可
認証 / 認可と Admission Control
Kubernetes API サーバーにリクエスト制限を追加で行う仕組み。
Kubernetes では Authentication(認証) / Authorization(認可) / Admission Control の 3つのフェーズを通ってリソースが登録される。
Secret リソースの暗号化
GitOps ではマニフェストを Git リポジトリに配置する。
Secret リソースは Base64 で暗号化されているだけなので、そのままでは Git リポジトリに配置することは望ましくない。そのため、別途暗号化を行う必要がある。
- kubesec
- AWS KMS などを利用して Secret の暗号化が可能
- ファイル全体を暗号化するのではなく Secret の構造を保ったまま値だけを暗号化するので可読性が優れている。
- SealedSecret
- 暗号化された独自の SealedSecret リソースを作成し、クラスタに登録すると、クラスタの内部で SealedSecret から Secret リソースに変換される仕組み
- Secret リソースにとして公開鍵と秘密鍵のペアを作り、Secret リソースを SealedSecret リソースに暗号化する際は、クラスタにある 1番新しい公開鍵を用いて `kubeseal` コマンドで行う
- SealedSecret Conroller が秘密鍵を用いて Secret リソースに復号する
- (すべての秘密鍵を使って復号を試みる、暗号化するキーをローテーションできるようになっている、秘密鍵はクラスタ外部に出ないのでセキュア)
- ExternalSecret
(Part4 に続く...)