Kubernetes完全ガイドをちゃんと理解するための個人雑メモ
3章 kubernetes環境の選択肢
Minikube
- シングルノード構成
- ハイパーバイザーを必要とし、各ハイパーバイザーに応じたDriverを使用して操作する
# Minikubeの起動 driverの指定をしないと使用可能なDriverを選択する? -> デフォルトがdockerのよう
minikube start
docker desktop
- kubernetesの設定を有効化することで使用できそう
kind
- minikubeはシングルノードのためマルチノードクラスターをローカルで構築するにはkindを使用する。
playground
以下のリンクでブラウザからkubernetesの構築が試せるよう。
4章 APIリソースとkubectl
- kubectlはバージョンを気にしなければ以下brewでインストールできる。
brew install kubernetes-cli
- シェル補完
echo 'source <(kubectl completion zsh)' >> ~/.zshrc
- kubernetesはマスターノードとワーカーノードからなるマルチノードで構成されている。
- マスターノードはAPIエンドポイントの提供、コンテナのスケジューリングやスケーリングを担うノード。
- ワーカーノードは実際にコンテナが起動するノード。
- kubernetesクラスタにはNamespaceが設定できるようだが、一つのクラスタを複数の目的で利用したり複雑なクラスタでなければデフォルトのNamespaceでリソースを作成して良さそう。
- kubernetesの操作は全てマスターノードが提供するAPIを通して実行し、実際にはkubectlを使用する。
- kubectlはkubeconfig(~/.kube/config)の情報をもとにkubernetesに接続する。
- リソースの作成は
kubectl create
でも可能だがkubectl apply
を使用したほうが良い。 - 理由としては差分検知がうまく働かないことがあるため。applyを使用すれば確実。
- kubectl createはgenerateNameを使用することでランダムな命名をするのに使えることもある。
- kubectlの操作は全て非同期のため操作が完了してから何かしたい場合は--waitオプションを使用する。
- アノテーションはEKSやGCEなどクラウドサービスごとの拡張機能などを使うための目印的な使い方がある
- マニフェストから削除されたリソースをapply時に自動で削除するための--pruneオプションがある。
kubectl apply -f sample.yaml --prune
- kubectl setコマンドでマニフェストを更新しなくてもリソースの変更ができるがマニフェストとリソースに差異が生まれるためあまり利用すべきでない。
-
kubectl diff -f sample-pod.yaml
でマニフェストファイルと現状の変更差分を表示できる -
kubectl api-resources
でリソース一覧見れる - リソース情報の取得はyaml, json形式の他にカスタムで表示もできる
kubectl get pods -o custom-columns="IMAGE:{.spec.containers[0].image}" sample-pod
IMAGE
nginx:1.7.9
- 全てのリソース一覧の取得
kubectl get $(kubectl api-resources --namespaced=true --verbs=list -o name | tr '\n' ',' | sed -e 's|,$||g')
- pod内で実行されているコンテナ上でコマンドを実行するには以下のような感じ
kubectl exec -it sample-pod -- /bin/bash
- kubectl debugでpod内にデバッグ用のコンテナを起動させて任意のコマンドを実行したりもできる
- kubectlを利用してpodにポートフォワードするには以下のような感じ
kubectl port-forward sample-pod 8080:80 // 8080 -> 80
- kubectl logsでリソースのログを確認できる
- OSSのSternを利用すると複数Podのログがより見やすい
brew install stern
- ファイルのコピーはkubectl cpでできる
- kubectlはサブコマンドを拡張することができ、プラグインの追加はkrewというプラグインマネージャーを使用することができる。インストールは以下。
https://krew.sigs.k8s.io/docs/user-guide/setup/install/
kube-ps1
プロンプトに現在選択中のkubernetesクラスタとNamespaceを表示する。
brew update
brew install kube-ps1
# .zshrcに追加
source "/usr/local/opt/kube-ps1/share/kube-ps1.sh"
PS1='$(kube_ps1)'$PS1
source ~/.zshrc
[user@mba:~/work/kubernetes-lessones/4]
$
(⎈|minikube:default)
server side apply
kubectlによる差分計算によるリソース作成はclient applyであり、server side applyを使用することで変更の衝突を検知することができる。
# podの作成
kubectl apply -f sample-pod.yaml
# podの内容を変更
kubectl set image pod sample-pod sample-pod=nginx:1.17
# マニフェストの内容と現状の内容が相違しているのでエラーになる
kubectl apply -f sample-pod.yaml --server-side
# 強制適用
kubectl apply -f sample-pod.yaml --server-side --force-conflicts
変更の検知にはフィールドを管理することで実現し、各フィールドにはmanagerがkubectlでデフォルト設定される。もし、CIからkubectlで変更がある場合、ローカルでの変更と衝突してもmanager名がデフォルトのままだと検知できないため変更することが可能。
# マニフェストを変更
# manager名を変更してapplyするとエラーになる
kubectl apply -f sample-pod.yaml --server-side --field-manager ci-tool
5章 Workloads APIs
クラスタ上にコンテナを起動させるのに利用するリソース。全8種
- Pod
- ReplicationController
- ReplicaSet
- Deployment
- DaemonSet
- StatefulSet
- Job
- CronJob
Pod
- Podは1つ以上のコンテナを含む。
- コンテナはPodに割り当てられたIPアドレスを共有する。
- コンテナ同士はlocalhost宛で通信することができる。
デザインパターン
-
サイドカーパターン
-
アンバサダーパターン
-
アダプターパターン
-
Dockerfileで言うところのENTRYPOINTをcommand, CMDをargsに記述できる。
-
デフォルトのdnsPolicyはクラスタ内のDNSに問い合わせを行い、解決できなかった場合はアップストリームに問い合わせをおこなう。
ReplicaSet/ReplicationController
指定のレプリカ数のPodを維持し続けるリソース。ReplicationControllerは今後廃止予定のためReplicaSetを使用しておけばok。ReplicaSetはPodがたとえ停止してしまったとしても指定されたレプリカ数を維持しようとする、いわゆるセルフヒーリングが働いている。レプリカ数の維持はPodのラベルを見ていていい感じにPodを削除・作成し数を調整してくれる。
Deployment
ReplicaSetの親リソース。ReplicaSetを管理することでローリングアップデートやロールバックを実現するためのリソース。ロールバックは古いマニフェストをapplyすることで戻すことができるのであまりロールバックコマンド自体は使わないかもしれない。アップデート戦略はローリングアップデート以外にもRecreate
が選べたり、アップデート時にPodをどのように更新していくかは細かく設定することができる。
DaemonSet
ReplicaSetはどのノードにいくつPodが配置されるかがわからないが、DaemonSetは各ノードに必ず1つPodを作成する。そのため、レプリカ数の指定などはなく、Datadogなどのモニタリング用のPodなど必ず各ノード上に配置したいリソースの作成に使用される。
アップデート戦略はRollingUpdate
とOnDelete
の2種類がある。
StatefulSet
データを永続化することができる。
PersistentVolume
永続化領域。これはクラスタ外にある。
PersistentVolumeClaim
永続化要求。この要求によりクラス外のPersistentVolumeをPodにアタッチすることができる。
StatefulSetを削除するときは、PersistentVolumeは削除されないためもし永続化領域の削除をしたい場合はPersitentVolumeClaimとPersitentVolumeの削除が必要。
Job
処理を実行したら停止するPodを作成する。ReplicaSetなどとの違いは停止することを前提に作られること。restartPolicy
を設定することができNever
とOnFailure
がある。違いは再起動時にPodを作り直すか再度同じPodを使用するかの違い。
Oneshot Task
1回のみ実行するタスク。completions=1 parallelism=1 backoffLimit=0
Multi Task
並列実行。例えば、completions=5 parallelism=3 backoffLimit=0 だと5回成功するまで3並列でタスクを実行する。3並列とはPodが3つ作成されるということ。
Multi WorkQueue
completionsを指定しないことで、処理を実行し続けることが可能。並列を指定することで大きな処理を並列で実行し続けるようなWorkQueue型のJobを実行することができる。一つでもPodが成功終了した場合、それ以降はPodは作成されない。Podが成功終了した時点で起動しているPodは正常終了されるまで停止されることはない。
WorkQueue型のJobを作成する場合なんらかのメッセージキューを使用することになる。
Single Worker Queue
上記Multi WorkerQueueの並列数を1にすることで1つずつWorkQueueを実行することができる。
CronJob
cronのようにスケジューリングすることができるJob。
ジョブの停止
kubectl patch cronjob sample-cronjob -p '{"spec": {"suspend": true}}'
任意のタイミングでの実行
kubectl create job sample-job-from-cronjob --from cronjob/sample-cronjob
同時実行に関するポリシー
- Arrow(default) 同時実行に関して制御を行わない
- Forbid 前のJobが終了していない場合、次のJobを実行しない(同時実行しない)
- Replace 前のJobをキャンセルし、次のJobを実行する
6章 Service APIs
クラスタ上のコンテナに対するエンドポイントの提供や、ラベルに一致するコンテナのディスカバリに利用されるリソース。
-
Service
- ClusterIP
- ExternalIP
- NodePort
- LoadBalancer
- Headless
- ExternalName
- None-Selector
-
Ingerss
-
kubernetesクラスター内の各ノードはNode Networkでつながっている。
-
そして、ノード内の各PodはCNI(Container Network Interface)でつながっているためPod間の通信ができる。
-
なのでServiceがなくてもPod間通信はなりたつがServiceを利用することでロードバランシングやクラスタ内DNSなどのメリットがある。
-
Podに割り当てられるIPは作成するたびに変化する。
-
kubernetesではDNSを利用して名前解決をすることができるため変化するIPではなくドメイン名で解決するようにしたほうがよい。
ClusterIP
クラスター内のみ疎通性のあるサービス。マニフェストでラベルを指定することで適切に紐づいたPodへのロードバランシングを実施する。ClusterIPの名前解決はDNS名ですべきだが、ClusterIPを指定することも可能。
ExternalIP
ClusterIPは外部疎通性がないが、ExternalIPはkubernetesノードのIPをを使用して外部疎通を確立することができるサービス。ただし、特別な理由がなければNode Port Serviceを検討したほうがよい。
また、ExternalIPはマニフェストにtype: ClusterIPを指定し、ExternalIPというtypeがあるわけではない。当然、ClusterIPが作成されるためマニフェストで指定したノードIPに外部からアクセスすると各Podに均等に割り振られる。
NodePort Service
ExternalIPは指定したnodeのIPとの外部疎通を確立していたが、NodePort Serviceは全てのノード(厳密には0.0.0.0:Portで外部との疎通を確立する)で受け付けることができる。typeはNodePortで指定。
マニフェストでは「全てのノードで受け付けるポート」「クラスターIPのポート」「割り振るPodのポート」を指定する。
NodePortで指定できるポートは30000~32767の範囲。
LoadBalancer Service
クラスタ外部にロードバランサー用のIPを払い出す。外部のロードバランサーを利用するのでGCPやAWSのロードバランサーに対して外部疎通性のあるIPを払い出すことになる。プロダクションとしてkubernetesを使用する時に一番実用的なサービス。
ExternalIPやNodePortはkubernetesノードのIPを指定するためそのノードIPが単一障害点になってしまう。一方、LoadBalancer Serviceは外部ロードバランサーを使用するためそのような心配はない。
マニフェストのtypeにはLoadBalancerを指定。
実際の運用ではロードバランサーのIPにドメインを割り当てる関係でIPを固定したい場合があると思うがその場合にはマニフェストでIPを指定することができる。
特に指定なく作成したロードバランサーは全公開になっているためアクセスを制限したい場合はマニフェストで指定することができる。
Headless Service(None)
対象となる個々のPodのIPが直接返ってくるサービス。サービス自体はIPを持たずDNSラウンドロビンを使ったエンドポイントを提供するため、複数のIPが返ってくる。
このサービスを作成するにはマニフェストでtypeにNoneを指定する。
ExternalName Service
サービスのドメインに外部ドメインを使用することができるサービス。外部ドメインを使用することでkubernetesへのアクセスに使用するドメインを疎結合に保つことができる。作成するにはマニフェストのtypeにExternalNameを指定する。
Non-Selector Service
ExternalName Serviceは外部のドメインに転送されていたが、Non-Selector Serviceの場合、サービス名で名前解決をすると自分で指定したメンバに対してロードバランシングを行う。基本的にはClusterIPによるクラスタ内ロードバランサで使用するのが大半。
Ingress
ロードバランシングを提供するサービスリソースがL4相当のロードバランサーであったのに対し、L7相当のロードバンサーとなるのがIngressである。Ingressはクラス外のロードバランサを利用したIngress(GKE Ingressなど)とクラスタ内にIngress用のPodをデプロイするIngress(Nginx Ingressなど)がある。
GKE Ingressを構築する場合は、クラスタ外部のL7相当ロードバランサからクラスタ内のNodePort Serviceを経由してPodに到達する。
Nginx Ingressの場合はL4相当のロードバランササービスを経由してNginx Ingress Podを経由してPodに到達する。
Ingressの作成はマニフェストのkindにIngressを指定して作成。GKE Ingressの場合は普通にルーティングルールを記述するだけでよく、Nginx Ingressの場合はアノテーションにNginxを指定する。
その他
- セッションアフィニティ
- 外部ロードバランサーを使用する場合、ノードに到達した際にもロードバランシングが行われノードをまたいで振り分けされるため、二段階振り分けみたいになる。これはマニフェストで無効にすることも可能。
- リージョンやゾーンなどのトポロジを意識した振り分けも可能。
Config & Storage
- 機密情報はSecretリソース
- 単純なKey-Valueの設定はConfigMapリソース
環境変数
- マニフェストのenvに指定することでコンテナに環境変数を設定することができる。
- Podに関する情報を参照する場合はfieldRefを使用することができる。
- コンテナに関する情報はresourceFieldRefで参照することができる。
- マニフェストのargsとcommandで環境変数を使用するには
()を使用する{}ではなく - マニフェストのargsとcommandで使用できる環境変数はマニフェスト内で定義した環境変数のみ。OSに設定された環境変数などは参照できない。
Secret
Secretのtypeは以下の通り。
- Opaque 一般的な汎用用途
- kubernetes.io/tls TLS証明書
- kubernetes.io/basic-auth Basic認証
- kubernetes.io/dockerconfigjson Dockerレジストリの認証情報
- kubernetes.io/ssh-auth SSHの認証情報
- kubernetes.io/service-account-tokne Service Account のトークン
- kubernetes.io/token Bootstrapトークン
Opaque
- 作成したSecretの値はbase64で難読化されている
- Secretの作成は以下の4つの方法がある
- --from-file kubectlでファイルから値を参照する
- --from-env-file kubectlでenvファイルから値を参照する
- --from-literal kubectlで直接値を渡す
- マニフェストファイルを使用する
Secretの利用
containerからSecretを利用するには
- 環境変数として渡す
- Volumeとしてマウントする
のどちらか
環境変数として渡す
secretKeyRefを使用して指定したSecretのKeyに対応する値を環境変数として設定する。Secret全てを渡す場合はenvFrom.secretRefでSecret名を指定することでSecretの全てのKey-Valueを環境変数として渡すことができる。また、Secret全てを渡す場合Keyが衝突する可能性があるのでprefixを設定することもできる。
Valueとしてマウントする
volumeとしてSecetをマニフェストで指定することでSecretの指定のKeyに対応した値をファイルとしてコンテナからマウントして参照することができる。環境変数として渡す時同様、Secret全てをマウントすることもできる。
動的にSecretを渡す
環境変数として渡す場合は、コンテナ起動時に環境変数として設定するため動的な対応はできないがValueとしてマウントしている場合は一定間隔で変更を検知しファイルを置き換えるため動的にSecretをコンテナに渡すことができる。
ConfigMap
Secret同様、ConfigMapの作成にはkubectlでファイルを渡す方法、kubectlで直接値を渡す方法、マニフェストで作成する方法がある。ConfigMapはKey-Valueの形式だがnginx.confのようなファイルをそのまま渡すこともできる。また、binaryDataを指定することでUTF-8以外のデータを含むバイナリデータを保存することもできる。つまり、画像データなどを保存しておける。保存した場合はbase64でエンコードされた状態で保存される。
ConfigMapの値をコンテナから使用するにはSecret同様、環境変数として渡すかValueとしてマウントする方法があり、個別で渡すこともConfigMapの全ての値を渡すこともできる。ただし、ConfigMapの値を全て環境変数として渡す場合は「.」や「-」みたいな記号を含むKeyの場合、うまく参照できない可能性があるのでファイルとしてマウントしたほうがよい。
Permissionについて
ConfigMapもSecretも値として保存したスクリプトをファイルとして実行する場合、適切な権限を設定することができる。デフォルトは644の権限でマウントされるので実行権限がない。なのでファイルとして実行したい場合などは適切な権限を付与する必要がある。また、マニフェストファイルでの指定は8進数表記の権限を10進数表記に変換したものを指定する。
設定値の変更を不可にする
ConfigMapとSecretの値はimmutable=trueにすることで後から変更不可にすることができる。
VolumeとPersistentVolumeとPersistentVolumeClaim
Volumeはホストなどにあらかじめ用意されてある利用可能なvolumeをマニフェストに指定することで使用する。PersistentVolumeは外部の永続ボリュームを提供するシステムと連携する。PersistentVolumeClaimはその名の通りPersistentVolumeをマニフェストに指定しても登録しか行われないので実際に要求するためのもの。
Volume
kubernetesではVolumeプラグインとして下記のようなものが用意されておりPodに対して静的に領域を指定する。
- emptyDir
- hostPath
- downwardAPI
- projected
- nfs
- iscsi
- cephfs
emptyDir
Podの一時的なディスク領域として利用可能。PodがTerminateされると削除される。ホスト上の任意の領域をマウントできるわけではないし、ホスト上のファイルを参照することもできない。あくまで一時的な領域として使用される。
emptyDirはマニフェストでmedium=Memoryを指定することで高速なtmpfs領域を割り当てすることもできる。
hostPath
kubernetesノード上の領域をコンテナにマッピングすることができる。emptyDirとは異なりホスト上の任意の領域をマウントすることができるためPodがTerminateされても消えない。
downwardAPI
Podの情報などをファイルとして配置するためのプラグイン。環境変数のところで使用したfieldRefとresourceFieldRefと同じ使い方。
projected
Secret, ConfigMap, downwardAPI, serviceAccountTokenのボリュームマウントを1ヶ所のディレクトリに集約するプラグイン。
PersistentVolume
VolumeはPodの定義内に直接書き込む形で接続をしていたが、PersistentVolumeは完全に外部にリソースとして作成する。また、PersistentVolumeは厳密にはConfig & Storage リソースではなくClusterリソースに分類。
PersistentVolumeとして実際に使用できるのは
- GCE Persistent Disk
- AWS Elastic Block Store
- Container Storage Interface (CSI)
などがある。
Container Storage Interface(CSI)
このプラグインはkubernetesとストレージエンジンをつなぐためのインターフェイス仕様です。従来はさまざまなプラグインがkubernetes内に実装されてきたが、プロバイダ側のアップデートのたびにkubernetes内部のプラグイン実装まで変更する必要があるため、kubernetes側では抽象化したインターフェイスを参照するようにすることでさまざまなプラグインが疎結合に使用することができる。
PersistentVolumeClaim
永続化領域の要求を行うリソース。前述のPersistentVolumeリソースを作成しただけでは使用することはできないためこのPersistentVolumeClaim経由で利用することになる。PersistentVolumeClaimは指定された容量、ラベルなどの条件に当てはまるPersistenVolumeを割り当てる。
Dynamic Provisioning
上述のPersistentVolumeCaimの説明では事前にPersistentVolumeリソースを作成している必要が当然ある。また、要求のVolume容量に一番近いVolumeが割り当てられるため3GiBの要求なのに7GiBも多い10GiBのVolumeが割り当てられるみたいなこともある。
これらの問題を解決するのがDynamic Provisioning。これを使用するとPersistentVolumeClaimが作成されたタイミングで動的にPersistentVolumeを作成して、割り当てる。そのため事前にPersistentVolumeを作成する必要もないし、割り当ても無駄がない。
Dynamic Provisioninを利用するには事前にどういったPersistentVolumeを作成するかを定義したStorage Classリソースを作成する。
8章 Cluster APIs & Metadata APIs
Cluster APIsに分類されるリソースはセキュリティ周りの設定などクラスタの挙動を制御するためのリソースで内部的に利用されているものを除いて利用者が直接利用するものは全部で10種類。
- Node
- Namespace
- Persistent Volume
- Resource Quota
- ServiceAccount
- Role
- ClusterRole
- RoleBinding
- ClusterRoleBinding
- NetworkPolicy
Metadata APIsに分類されるリソースはクラスタ上にコンテナを起動させるのに利用され、以下の4種類がある。
- LimitRange
- HorizontalPodAutoscaler
- PodDisruptionBudget
- CustomResourceDefinition
9章 リソース管理とオートスケーリング
- コンテナのリソースを制限することが可能。マニフェストで下限はrequestsに上限はlimitsにmemoryとcpuを設定できる。下限のみを設定すると上限に使用可能なリソースを名一杯使ってしまい、逆に上限のみの設定だと上限と同じ値が下限にも設定されてしまうので片方のみの設定は避け両方設定したほうがよい。
- Ephemeral Storageの容量が圧迫された時PodはEvictされる。Ephemeral Storageとしてカウントしているのは以下の3つ。
- コンテナが出力するログ
- emptyDirに書き込まれたデータ
- コンテナの書き込み可能なレイヤに書き込まれたデータ
- kubernetesクラスタ自体のオートスケーリング機能(Cluster Autoscaler)もあり、需要に応じてkubernetesノードを自動で追加してくれる。nodeの追加はPending状態のPodが作成されたタイミングで行われる。
- これは、リソースの下限が高すぎると負荷がそうでもなにのにnodeの追加が行われてしまったり、逆にnodeの追加が行われないなどの予期せぬ挙動をしてしまう可能性がある。
- ポイントは加減と上限に顕著な差をつけないことと下限を高くしすぎないこと。
- リソースのRequestとLimitを設定しないと、無限にリソースを作り続けてしまうのでデフォルトのLimitRangeが用意されている環境もある。
- LimitRangeリソースはtypeにcontainer, Pod, PersitentVolumeClaimなどを指定してそれらのリソースが作成されるときに制限をかけることができる。
QoS Class
PodにはRequest/Limitsの設定に応じて自動的にQoS Classの値が設定されるようになっている。QoS Classの値は手動で設定するものではなく自動で設定される。
割り当てられるQoS Class
- BestEffort
- Guaranteed
- Burstable
このQoS Classはkubernetesがコンテナにたいしてoom scoreを設定するのに使用される。oom scoreはOOM Killerによってプロセスを停止される際の優先度の値。
ResourceQuota
リソース。作成可能なリソースの制限とリソース使用量の制限ができる。
HorizontalPodAutoscaler(HPA)
Deployment, ReplicaSetなどのオートスケーリング。HorizontalPodAutoscalerは30秒に1回オートスケーリングすべきかチェックしている。
VerticalPodAutoscaler(VPA)
リソース。PodのCPU/メモリのオートスケーリング。
参考
MinikubeのTips。LoadBalancerをMinikubeで作れないと思ってたので助かりました。
Minikubeでhpaのテストできなくてたどりついた
10章 ヘルスチェック
Prove | 役割 | 失敗時の挙動 |
---|---|---|
Liveness Prove | Pod内のコンテナが正常に起動しているどうか | Podは再起動する |
Readiness Prove | Podがリクエストを受け付けられるかどうか | トラフィックを流さない(Podは再起動しない |
Startup Prove | Podの初期起動が完了したかどうか | 他のProveは起動しない |
コンテナの初期起動に時間がかかる場合に十分に時間を確保していないとヘルスチェックが失敗し続けてしまうなどの問題があったためStartup Proveが登場した。Startup ProveはPod起動時に1回だけ実行され、問題なかった場合に他のProveを実施する。
ヘルスチェックの方式は以下の3種
- exec コマンドを実行し終了コード0でなければ失敗
- httpGet ステータスコード200 ~ 399でなければ失敗
- tcpSocket TCPセッションが確立できなければ失敗
gRPCのヘルスチェックにはgRPC用のヘルスチェックがもともと用意されており、grpc_health_proveが使用できる。事前にコンテナイメージにgrpc_health_proveを用意しておきコマンド実行でヘルスチェックする。
ライフサイクル
Init Containers
Pod内でメインとなるコンテナを起動する前に、初期化処理を別のコンテナを起動して実行することで初期化処理を分離することができる。Init Containersは複数設定することができ、マニフェストに書いた順番通りに実行されるため順序性が必要な処理なども実行できる。
postStart / preStop
コンテナの起動後、終了直前に処理を実行することができる。注意点としてpostStartとpreStopは複数回実行される可能性があるため、1回のみ実行してほしい処理は書かないほうがいい。
11章 メンテナンスとノードの停止
- ノードにはSchedulingEnabledとScheduleDisabledのステータスがあり、ScheduleDisabledのステータスのノードでは新たにPodの作成は行われない。既に実行中のpodには影響はない。ステータスの変更は
kubectl cordon
とkubectl uncordon
で変更する。 - ノード上で起動しているPodを退避させるには
kubectl drain
を使用する。 - kubernetesではあるノードにPodが偏って起動している状況などもありえるため、その状態でPodの退避を実行するとサービスの瞬断が発生してしまう。そのため、対象のPodを最低限起動させておく制約などを
PodDistributionBudget(PDB)
リソースで作成することができる。大規模なkubernetesの運用の場合、PDBなしにPodの退避をするのは難しい。
12章 高度で柔軟なスケジューリング
- マニフェストで指定できるスケジューリングには「配置したいnodeを指定する方法」と「配置したくないnodeを指定する方法」の2種類がある。
配置したいノードを選択する
-
nodeSelector 簡易的なNodeAffinity
-
NodeAffinity 特定なNode上だけで実行する
-
Node Anti-Affinity 特定のNode以外で実行
-
Inter-Pod Affinity 特定のPodがいるドメイン上で実行する
-
Inter-Pod Affinity 特定のPodがいないドメイン上で実行する
-
トポロジ均衡
-
TaintsとTolerations
kubernetesのスケジューラーは基本なもので十分だが機能が不足しているときに自作できるようになっている。
(後半の既存のPodに影響与えるスケジューリングのところだいぶ流し読みしてしまった.)
13章 セキュリティ
ServiceAccount
- kubernetesにはUserAccountとServiceAccountの2種類がある。
- UserAccountはGKEではGoogleアカウントだったり、EKSではIAMだったりと紐づいていてkubernetesの管理対象ではない。
- また、UserAccountはクラスタレベルのものなのでNamespaceの影響は受けない。
- 一方、ServiceAccountはPodで実行されるプロセスのために割り当てるものでNamespaceに紐づく。
- 指定しなかった場合はdefaultのServiceAccountが割り当てられる。
- 例えば、Dockerレジストリの認証情報を指定したServiceAccountを作成し、containerに割り当てるなどして使う。
- 作成したServiceAccountの認証情報はSecretリソースとしてPodに割り当てられる。
RBAC(Role Based Access Control)
- どういった操作を許可するのかを定めたRoleをServiceAccountなどのUserに対して紐づけることで権限管理する。
- 複数のRoleを集約したRole(AggregationRole)を利用することもできる。
- RoleとRoleBindingにはNamespaceとClusterレベルの2種類がある。
SecurityContext
SecurityContextは個々のコンテナに対するセキュリティ設定。
- privileged 特権コンテナ。コンテナ内で起動するプロセスのLinux Capabilitiesがホストと同等の権限。
- capabilities より細かく権限を付与。
- readOnlyRootFilesystem コンテナイメージに含まれているファイルなどをReadOnlyに
PodSecurityContext
Podに対するセキュリティ設定。
- runAsUser/runAsGroup/runAsNonRoot 実行ユーザー、グループを変更する。セキュリティの観点からrootユーザー以外で実行するように変更したほうがよい。
- fsGroup 通常マウントしたボリュームのオーナーとグループはroot:rootになっているため、コンテナんの事項ユーザを変更しているとマウントしたボリュームに対する権限がない場合がある。そういった時にマウントするボリュームのグループを変更できる。
- sysctl カーネルパラメーターを変更。カーネルパラメーターにはsafeとunsafeなものがありたいていunsafe。unsafeなパラメーターを変更した場合、kubernetesクラスタの提供者が明示的に許可していないとPodの起動に失敗する。マネージドなクラスタの場合変更するのは難しいかもしれない。InitContaienrsで特権コンテナとして変更する方法もある。
PodSecurityPolicy
kubernetesクラスタに対してセキュリティーポリシーによる制限を行うリソース。PodSecurityPolicyはホワイトリスト形式のため適切なポリシーを作成しないとリソースが作成できなくなる。
ReplicaSetなどを実行するとき、ReplicaSetの例で言うとReplicaSet Controllerと呼ばれるシステムコンポーネントがPodを作成するためPodを作成するときの権限はReplicaSetを作成したときの権限は使用されない。kube-system内にある権限を変更したくなるがPodに割り当てたServiceAccountが適切なPodSecurityPolicyを持っていればPodを作成できるようになっている。
NetworkPolicy
kubernetesクラスタ内でPod同士が通信する際のトラフィックルールを規定するもの。NetworkPolicyを利用しない場合、クラスタ内のすべてのPod同士で通信できる。Namespaceごとにトラフィックを転送しないようにしたり、全ての通信を遮断してホワイトリスト形式で利用したりできる。
NetworkPolicyはインバウンドのIngressとアウトバウンドのEgressで構成される。
認証/認可 Admission Control
Admission Controlはkubernetes APIサーバーにリクエスト制御を追加で行う仕組み。kubernetesではAuthentication/Authorization/Admission Controlの3つのフェーズを通ってリソースが登録される。
Authenticationは認証。Authorizationは認可。Admission Controlは別途そのリクエストの内容を許可するかどうかの判断や追加の柔軟なチェックなどが行える。Admission Controlはプラグイン形式になっている。
PodPreset
Admission Controllerの1種。リソース。Podの起動前に環境変数を割り当てたり、volumeやPersistentVolumeを割り当てたりといった設定を追加できるため、Podの定義と疎結合に分離できる。また、PodPresetが衝突すると適用されなくなってしまうためなるべくPodPresetは分割して局所的に管理したほうがよい。
Secret
マニフェストは通常GitHubなどでバージョン管理するがSecretの値はbase64で難読化されているだけなのでセキュリティ的にそのままあげることはできない。
kubesec
GCPのKMSやAWSのKMSを使用して安全にSecretを管理するためのOSS。kubesecはファイル全体を暗号化するのではなくSecretの構造を保ったまま値だけ暗号化するので可読性に優れている。
インストール
curl -sSL https://github.com/shyiko/kubesec/releases/download/0.9.2/kubesec-0.9.2-darwin-amd64 \
-o kubesec && chmod a+x kubesec && sudo mv kubesec /usr/local/bin/
補完機能
source <(kubesec completion bash)
GCPのKMSの例で言うと、生成した共通鍵を利用してSecretの内容を暗号化・複合化をすることができ、その共通鍵をKMSで厳重に管理するようなフロー。
SealedSecret
kubesecとは異なり暗号化された独自のSealedSecretリソースをクラスタ上に作成し、クラスタ内部でSealedSecretからSecretリソースを作成する。秘密鍵も公開鍵もクラスタ内部に閉じ込められており、クラスタ内部の鍵を使用して暗号化、複合化する。
インストール
brew install kubeseal
// コントローラーのインストール
ExternalSecret
SealedSecret同様、リソースを作成するタイプ。ExternalSecretリソースをクラスタに登録しておくとクラスタ内部でExternalSecretからSecretに変換することができる。SealedSecretではクラスタ内で秘密鍵などを管理していたが、ExternalSecretでは外部のSecretManager(AWSやGCPなど)の参照情報を保持している。
インストール
Helm
インストール
brew install helm
補完
source <(helm completation bash)
Helmではチャートと呼ばれるパッケージが多く存在している。
チャートリポジトリを初期化
helm repo add stable https://charts.helm.sh/stable
チャートを検索
helm search repo wordpress
NAME CHART VERSION APP VERSION DESCRIPTION
stable/wordpress 9.0.3 5.3.2 DEPRECATED Web publishing platform for building...
HelmリポジトリだけでなくHelm Hubからチャートを検索することもできる
helm search hub wordpress
チャートのインストール
インストール時に指定するパラメーターは直接コマンドオプションで指定するかvaluesファイルを作成し読み込むかのどちらか。
テンプレート
helm templateを利用することでテンプレートファイルからマニフェストファイルを出力することのみが可能。これを利用してGitOpsのようなCI/CDでhelm templateでマニフェストを出力し、applyするといったフローも可能。
独自チャートの作成
独自のチャートを作成するにはhelm createを実行する。
kustomize
マニフェストファイルを組み合わせたり、値やコンテナイメージを上書きしたりすることができる。
15章 モニタリング
DataDog
helmでチャートが用意されているので使用するとよさげ。
Prometheus
16章 コンテナログの集約
Fluented
kubernetesでは標準出力、エラー出力でログを出力するのが推奨されているが、Fluentedを使用することでクラスタ外部に転送するような構成が作れる。
DataDog
Datadogのエージェント経由でも可能。
Grafana Loki
Prometheusと相性がいい。
何にせよDamonSetでPodに常駐させてログを取集する。
17章 CI/CD
GitOps
Gitを用いたCI/CDの手法の一つ。アプリケーションのCI/CDでよく見るようなCIツールを使用してkubectlでapplyする方法もあるがCIツールがkubernetesクラスタに対して強い権限を持ってしまったり依存関係が強くなってしまったりといった問題もあるがGitOpsを採用することでこういった問題を軽減できる。
CIツール
Tekton + Kanikoを組み合わせて実現したりできる。以下のCIで実行したいようなツールたちをパイプラインに乗せるのに導入する必要がありそう。
kubeval
マニフェストファイルのYAML構造がAPIバージョンに準拠しているかをチェックできるためCIに組み込んで使用できる。
Conftest
マニフェストファイルのユニットテストを実施できるため、これもCIに組み込んで使用できる。
Open Policy Agent
ポリシーチェックを実行できる。kubernetestと連携するGatekeeperと組み合わせることでマニフェストが適用されたポリシーチェックを実施することができる。kubernetesのAdmin Controllerとして動作するのでリソース作成時に検知する感じになる。
CDツール
前述したようにGitOpsを実現するにはCIツールではなくCDツールを採用したほうがええ。とりあえずArgoCDを使うのがベターそう。
ArgoCD
- 指定したリポジトリを監視し、マニフェストをクラスタに適用する。
- GitOpsで言うところのDeploy Agentに該当する。
install
kubectl create namespace argocd
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
- ArgoCDではApplicationリソースを作成することで特定のリポジトリの特定のパスにあるファイルをkubernetestクラスタに適用する。
- KustomizeやHelmにも対応しているため現状GitOpsを始めるのに最も適したツール。
- ArgoCDは WebUIが用意されている。
開発環境を整えるツール
Telepresence
リモートのkubernetesクラスタを利用してローカル開発を実現するもの。大規模なkubernetesクラスタになると全てのコンポーネントをローカルで起動させるのは難しい。
Telepresenceはリモートで動いているkubernetesコンポーネントを使用し、一部をローカルで動作させる。ローカルのコンポーネントとkubernetesコンポーネントは疎通性があるので手元で起動したコンテナからリモートのkubernetes上のPodネットワークに接続することができる。
Skaffold
dockerおよびkubernetes向けのビルドとデプロイを自動化する。利用にはYAML形式のConfigリソースを作成して設定する。
19章 kubernetesのアーキテクチャ
kubernetesクラスタは下記8つのコンポーネントから構成されている。
etcd
分散Key-Value Store。etcdにはkubernetesクラスタに登録されるすべての情報が保存されるためetcdのデータを守ることが最も重要。そのため、単一障害点とならないようクラスタ構成を組む必要がある。
kube-apiserver
kubernetes APIを提供するコンポーネント。kubectlでリクエストを送ることでリソースの操作を行う。kube-scheduler, kube-controller-manager, kubeletなどもこのkube-apiserverに対してリクエストを送る。
Podの例で言うとkubectlでPodの作成を実行するとkube-apiserverでリクエストを受け取り、Pod情報をetcdに登録する。kube-schedulerはノード未割り当てのPodのノード情報をkube-apiserverにリクエストすることで割り当てるノードを登録する。実際に起動するのはkubeletの役目。このようにkube-apiserver以外のすべてのコンポーネントはkube-apiserverを中心にして分散システムとして動作している。
kube-scheduler
上述したようにkube-schedulerはノード未割り当てのPodを検知し割り当てするノード情報を登録する。
kube-controller-manager
さまざまなコントローラーを実行するコンポーネント。Deployment ControllerやReplicaSet Controllerはリソースの情報を監視し、必要な操作をkube-apiserverに対して実行する。
kubelet
上述したようにkubeletはkubernetesノード上に存在し、Podの起動、停止の操作を実行する。
kubeletはCRIによってcontainerdなどの高レイヤコンテナランタイムに命令を送り、高レイヤコンテナランタイムはOCIの形式に準拠した形でruncのような低レイヤコンテナランタイムに命令を送るようになってる。
このようにCRIやOCIといった標準化が進んでいるためその形式に準拠した形であれば何でもしようできるようになっている。
kube-proxy
kube-proxyもkubelet同様、ノード上で動作する。Serviceリソースが作られた際にClusterIPやNodePort宛のトラフィックがPodに正常に転送されるようにする。
転送方式
- userspaceモード
- iptablesモード
- ipvsモード
CNI(Container Network Interface) Plugin
複数のkubernetesノードはPod間の疎通性を確保するためにクラスタ内に分散配置されたPodが相互に疎通う可能なネットワークを作成する必要があり、この部分を担当するのがCNI。
kube-dns(CoreDNS)
kubernetesクラスタ内部の名前解決をする。
cloud-controller-manager
kubernetesクラスタが各種クラウドプロバイダと連携するためのコンポーネント。
CustomResourceDefinitionとOperator
kubernetesでは独自リソースを容易に追加して、kubernetesを拡張することができるようになっている。独自リソースを定義するにはCustomResourceDefinitionリソースを定義し作成するだけでよい。実際は独自リソースが作成された際に何かしらの処理をおこなうOperator(カスタムコントローラー)を実装する必要がある。
もし、カスタムコントローラーを実装するのであればkubebuilder
やOperator Framework
などを使用すると楽に作成できる。