2021.9.16
Kubernetes入門/コンテナ実行に関するハンズオン
はじめに
この記事は Kubernetes のコアコンセプトである「Reconciliation Loop」挙動をローカルで確認する内容です。
事前知識がないとよくわからないと思うので、冒頭はハンズオンを進めるにあたっての Kubernetes の全体像やコンテナ実行に関連する知識に絞って纏めてみました。
※すでに基礎的な知識をお持ちの方は「ハンズオン」の章まで読み飛ばしていただいて大丈夫です。
Kubernetesとは
コンテナ化したアプリケーションのデプロイ、スケーリング、および管理を行うプラットフォームであり、今やコンテナオーケストレーションではデファクトスタンダードになっています。
アーキテクチャ
Kubernetes クラスタは8つのコンポーネント(+1つのオプション)から構成され、kube-apiserver を中心とした分散システムです。
- コントロールプレーン(Master)
- etcd
- kube-apiserver
- kube-scheduler
- kube-controller-manager
- kube-dns (CoreDNS)
- [option] cloud-controller-manager
- データプレーン(Node)
- kubeket (+コンテナランタイム)
- kube-proxy
- CNI Plugin
Kubernetes の各コンポーネントについては公式ページをご参照ください。
Reconciliation Loop(調整ループ)とは
Kubernetes では、さまざまなリソースのあるべき状態を宣言的に定義しリソースの作成を行うと、それらのリソースの状態は常に監視され、あるべき状態(Desired State)と現在の状態(Current State)に差が生じている事を検出すると、あるべき状態に戻そうという動きをします。
このようなメカニズムを Reconsiliation Loop (調整ループ)と呼び、コントロールプレーンコンポーネントによって実現されています。コントロールプレーンのコントローラーマネージャーはクラスター全体を目的の状態に駆動する調整ループを構成し、一連のコントローラーを管理、制御しています。
さまざまなリソース
Kubernetes のリソースは大きく分けて5つのカテゴリに分類されます。
種別 | 概要 |
---|---|
Workloads APIs カテゴリ | コンテナの実行に関するリソース |
Service APIs カテゴリ | コンテナを外部公開するようなエンドポイントを提供するリソース |
Config & Storage APIs カテゴリ | 設定/機密情報/永続化ボリュームなどに関するリソース |
Cluster APIs カテゴリ | セキュリティやクォータなどに関するリソース |
Metadata APIs カテゴリ | クラスタ内の他のリソースを操作するためのリソース |
その中でも Workloads APIs カテゴリに分類されるリソースは、クラスタ上にコンテナを起動させるのに利用するリソースです。
利用者が直接利用するものとしては全部で8種類のリソースがあります。
- Pod
- ReplicationController
- ReplicaSet
- Deployment
- DaemonSet
- StatefulSet
- Job
- CronJob
Podを最小単位として、それらを管理する上位リソースの親子関係は以下の通りです。
- Pod <- ReplicationController
- Pod <- ReplicaSet <- Deployment
- Pod <- DaemonSet
- Pod <- StatefulSet
- Pod <- Job <- CronJob
ハンズオン
構築方法
Kubernetes を構築する方法としては以下がありますが、今回は「Docker Desktop for Mac」を使用して進めて行きます。
- ローカル Kubernetes
ユーザの手元のマシン1台に構築して使用する。- Minikube
- Docker Desktop for Mac / Windows
- kind (Kubernetes in Docker)
- Kubernetes 構築ツール
ツールを利用して、任意の環境(オンプレミス/クラウド)にクラスタを構築して使用する。- kubeadm
- Rancher
- マネージド Kubernetes サービス
パブリッククラウド上のマネージドサービスとして提供されるクラスタを使用する。- Google Kubernetes Engine (GKE)
- Azure Kubernetes Service (AKS)
- Elastic Kubernetes Service (EKS)
ローカル環境
- MacOs High Sierra 10.13.6
- zsh 5.3 (x86_64-apple-darwin17.0)
- docker-desktop community 2.4.0.0(48506)
- docker engine 19.03.13
- kubernetes v1.18.8
事前準備
docker-desktop のインストールと設定
Docker Desktop for Mac インストール後に [Preferences] で kubernetes を有効化します。Kubernetes が running になるまで少し時間がかかります。(5分くらい)
ステータスが running に変わらない場合は、一度ウィンドウを閉じて再度開いて確認してみてください。
また、docker のステータスメニューやダッシュボードなどからも確認できます。
kubectl コマンドの自動補完
kubectl は頻繁に利用するコマンドなので自動補完を有効にしておきます。
❯ source <(kubectl completion zsh)
Contextの切り替え
kubectl から Docker Desktop のクラスタを操作するために、以下のコマンドで Context を切り替えます。
❯ kubectl config use-context docker-desktop
Switched to context "docker-desktop".
Deployment の概要
Deployment は複数の ReplicaSet を管理することで、ローリングアップデートやロールバックなどを実現するリソースです。Kubernetes では最も推奨されているコンテナの起動方法とされています。
仕組みとしては以下のようなフローをとります。
- 新しい ReplicaSet を作成。
- 新しい ReplicaSet 上のレプリカ数(Pod 数)を徐々に増やす。
- 古い ReplicaSet 上のレプリカ数(Pod 数)を徐々に減らす。
- (2、3 を繰り返す)
- 古い ReplicaSet はレプリカ数0で保持する。
Deployment の作成
最初にマニフェストを用意し、実際に Deployment を作成します。
適用するマニフェスト(sample-deployment.yaml)
apiVersion: apps/v1
kind: Deployment
metadata:
name: sample-deployment
spec:
replicas: 3
selector:
matchLabels:
app: sample-app
template:
metadata:
labels:
app: sample-app
spec:
containers:
- name: nginx-container
image: nginx:1.16
apply コマンドで sample-deployment.yaml を指定して Deployment を起動します。
今回、「–record」オプションを利用してどのようなコマンドでアップデートを行ったのかの履歴を残しておきます。
❯ kubectl apply -f sample-deployment.yaml --record
deployment.apps/sample-deployment created
関連リソースが作成されているか確認します。
# Deploymentの確認
❯ kubectl get deployments
NAME READY UP-TO-DATE AVAILABLE AGE
sample-deployment 3/3 3 3 13s
# ReplicaSetの確認
❯ kubectl get replicasets
NAME DESIRED CURRENT READY AGE
sample-deployment-7bf986f9cf 3 3 3 30s
# Podの確認
❯ kubectl get pods
NAME READY STATUS RESTARTS AGE
sample-deployment-7bf986f9cf-9hsjt 1/1 Running 0 47s
sample-deployment-7bf986f9cf-q75vq 1/1 Running 0 47s
sample-deployment-7bf986f9cf-xt5l7 1/1 Running 0 47s
sample-deploymentに対して、一つの ReplicaSet とそれに対する3つの Pod が作成されました。
ここで一つ Pod を削除して、セルフヒーリング(自己修復)の挙動を確認してみます。
# Podの停止(削除)
# Pod名は実際に起動しているものを指定
❯ kubectl delete pod sample-deployment-7bf986f9cf-9hsjt
pod "sample-deployment-7bf986f9cf-9hsjt" deleted
# Podの確認
❯ kubectl get pods
NAME READY STATUS RESTARTS AGE
sample-deployment-7bf986f9cf-9hsjt 1/1 Running 0 104s
sample-deployment-7bf986f9cf-q75vq 1/1 Running 0 104s
sample-deployment-7bf986f9cf-xt5l7 1/1 Running 0 104s
sample-deployment-7bf986f9cf-sgcpg 1/1 Running 0 27s
# ReplicaSetの詳細を確認
❯ kubectl describe replicasets sample-deployment
Name: sample-deployment-7bf986f9cf
Namespace: default
Selector: app=sample-app,pod-template-hash=7bf986f9cf
Labels: app=sample-app
pod-template-hash=7bf986f9cf
Annotations: deployment.kubernetes.io/desired-replicas: 3
deployment.kubernetes.io/max-replicas: 4
deployment.kubernetes.io/revision: 1
kubernetes.io/change-cause: kubectl apply --filename=sample-deployment.yaml --record=true
Controlled By: Deployment/sample-deployment
Replicas: 3 current / 3 desired
Pods Status: 3 Running / 0 Waiting / 0 Succeeded / 0 Failed
Pod Template:
Labels: app=sample-app
pod-template-hash=7bf986f9cf
Containers:
nginx-container:
Image: nginx:1.16
Port: <none>
Host Port: <none>
Environment: <none>
Mounts: <none>
Volumes: <none>
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal SuccessfulCreate 104s replicaset-controller Created pod: sample-deployment-7bf986f9cf-9hsjt
Normal SuccessfulCreate 104s replicaset-controller Created pod: sample-deployment-7bf986f9cf-q75vq
Normal SuccessfulCreate 104s replicaset-controller Created pod: sample-deployment-7bf986f9cf-xt5l7
Normal SuccessfulCreate 27s replicaset-controller Created pod: sample-deployment-7bf986f9cf-sgcpg
指定した Pod (7bf986f9cf-9hsjt) が削除され、新しい Pod (7bf986f9cf-sgcpg)が作成されました。宣言的に指定されているレプリカ数に対して自動的に調整されていることがわかると思います。
ローリングアップデート
試しに Deployment で利用しているコンテナイメージを、nginx:1.16 から nginx:1.17 にアップデートしてみます。
# コンテナイメージの更新
❯ kubectl set image deployment sample-deployment nginx-container=nginx:1.17 --record
deployment.apps/sample-deployment image updated
# Deploymentのアップデート状況を確認
❯ kubectl rollout status deployment sample-deployment
deployment "sample-deployment" successfully rolled out
❯ kubectl describe replicasets sample-deployment
Name: sample-deployment-5988b689cb
Namespace: default
Selector: app=sample-app,pod-template-hash=5988b689cb
Labels: app=sample-app
pod-template-hash=5988b689cb
Annotations: deployment.kubernetes.io/desired-replicas: 3
deployment.kubernetes.io/max-replicas: 4
deployment.kubernetes.io/revision: 2
kubernetes.io/change-cause: kubectl set image deployment sample-deployment nginx-container=nginx:1.17 --record=true
Controlled By: Deployment/sample-deployment
Replicas: 3 current / 3 desired
Pods Status: 3 Running / 0 Waiting / 0 Succeeded / 0 Failed
Pod Template:
Labels: app=sample-app
pod-template-hash=5988b689cb
Containers:
nginx-container:
Image: nginx:1.17
Port: <none>
Host Port: <none>
Environment: <none>
Mounts: <none>
Volumes: <none>
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal SuccessfulCreate 19s replicaset-controller Created pod: sample-deployment-5988b689cb-ddm84
Normal SuccessfulCreate 17s replicaset-controller Created pod: sample-deployment-5988b689cb-j7h76
Normal SuccessfulCreate 15s replicaset-controller Created pod: sample-deployment-5988b689cb-vdpss
Name: sample-deployment-7bf986f9cf
Namespace: default
Selector: app=sample-app,pod-template-hash=7bf986f9cf
Labels: app=sample-app
pod-template-hash=7bf986f9cf
Annotations: deployment.kubernetes.io/desired-replicas: 3
deployment.kubernetes.io/max-replicas: 4
deployment.kubernetes.io/revision: 1
kubernetes.io/change-cause: kubectl apply --filename=sample-deployment.yaml --record=true
Controlled By: Deployment/sample-deployment
Replicas: 0 current / 0 desired
Pods Status: 0 Running / 0 Waiting / 0 Succeeded / 0 Failed
Pod Template:
Labels: app=sample-app
pod-template-hash=7bf986f9cf
Containers:
nginx-container:
Image: nginx:1.16
Port: <none>
Host Port: <none>
Environment: <none>
Mounts: <none>
Volumes: <none>
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal SuccessfulCreate 3m53s replicaset-controller Created pod: sample-deployment-7bf986f9cf-9hsjt
Normal SuccessfulCreate 3m53s replicaset-controller Created pod: sample-deployment-7bf986f9cf-q75vq
Normal SuccessfulCreate 3m53s replicaset-controller Created pod: sample-deployment-7bf986f9cf-xt5l7
Normal SuccessfulCreate 2m36s replicaset-controller Created pod: sample-deployment-7bf986f9cf-sgcpg
Normal SuccessfulDelete 17s replicaset-controller Deleted pod: sample-deployment-7bf986f9cf-sgcpg
Normal SuccessfulDelete 15s replicaset-controller Deleted pod: sample-deployment-7bf986f9cf-xt5l7
Normal SuccessfulDelete 13s replicaset-controller Deleted pod: sample-deployment-7bf986f9cf-q75vq
sample-deployment-7bf986f9cf-7trfb
Normal SuccessfulDelete 59s replicaset-controller Deleted pod: sample-deployment-7bf986f9cf-gp87t
更新後はローリングアップデートにより、ReplicaSet が新たに作られてそこに紐づく形で Pod が再作成されます。
ローリングアップデートの条件は spec.template の内容に変更があると spec.template 以下の構造体のハッシュ値(Pod Template Hash)が再計算され、それを検知して ReplicaSet が新しく作成されます。
変更のロールバック
例えば、クラッシュループ状態などのように Deployment が不安定な場合においては、Deployment をロールバックしたくなることがあります。Deployment の全てのロールアウト履歴は、いつでもロールバックできるようにデフォルトでシステムに保持されています。
また、リソース変更のコマンド実行時に「–record」 オプションを付与した場合も同様にロールアウト履歴として記録されます。
# 変更履歴を確認
❯ kubectl rollout history deployment sample-deployment
deployment.apps/sample-deployment
REVISION CHANGE-CAUSE
1 kubectl apply --filename=sample-deployment.yaml --record=true
2 kubectl set image deployment sample-deployment nginx-container=nginx:1.17 --record=true
# 詳細な情報を取得する場合は「--revision」オプションを指定
❯ kubectl rollout history deployment sample-deployment --revision 1
deployment.apps/sample-deployment with revision #1
Pod Template:
Labels: app=sample-app
pod-template-hash=7bf986f9cf
Annotations: kubernetes.io/change-cause: kubectl apply --filename=sample-deployment.yaml --record=true
Containers:
nginx-container:
Image: nginx:1.16
Port: <none>
Host Port: <none>
Environment: <none>
Mounts: <none>
Volumes: <none>
❯ kubectl rollout history deployment sample-deployment --revision 2
deployment.apps/sample-deployment with revision #2
Pod Template:
Labels: app=sample-app
pod-template-hash=5988b689cb
Annotations: kubernetes.io/change-cause: kubectl set image deployment sample-deployment nginx-container=nginx:1.17 --record=true
Containers:
nginx-container:
Image: nginx:1.17
Port: <none>
Host Port: <none>
Environment: <none>
Mounts: <none>
Volumes: <none>
ロールバックは以下のコマンドで行います。
# リビジョンを指定してロールバックする場合
❯ kubectl rollout undo deployment sample-deployment --to-revision 1
deployment.apps/sample-deployment rolled back
# 一つ前にロールバックする場合(デフォルトの--to-revision 0と同等)
❯ kubectl rollout undo deployment sample-deployment
deployment.apps/sample-deployment rolled back
# ロールバック後は前のReplicaSetのPodが起動される
❯ kubectl get replicasets
NAME DESIRED CURRENT READY AGE
sample-deployment-5988b689cb 0 0 0 3m57s
sample-deployment-7bf986f9cf 3 3 3 7m31s
Tips
CI/CD パイプラインからロールバックする場合、rollout コマンドよりも古いマニフェストを再度 apply する方が相性が良いようです。(spec.template を同じものに戻した場合には Pod Template Hash が同じになり、もともとあった ReplicaSet の Pod が起動されるため、rollout コマンドと同様の結果が得られる。)
そのため、実運用上では rollout コマンドを利用することがほとんどないため、「–record」オプションも多くの場合で不要です。
更新の一時停止
通常、Deployment に更新処理を加えた場合、即時適用されてアップデート処理が実行されます。即時適用を一時停止したい場合は以下のコマンドで停止します。
❯ kubectl rollout pause deployment sample-deployment
deployment.apps/sample-deployment paused
pause された状態では Deployment の更新は即時反映されません。
# pauseされた状態でのイメージの変更
❯ kubectl set image deployment sample-deployment nginx-container=nginx:1.17
deployment.apps/sample-deployment image updated
# アップデートは待機状態のまま
❯ kubectl rollout status deployment sample-deployment
Waiting for deployment "sample-deployment" rollout to finish: 0 out of 3 new replicas have been updated...
また、pause された状態ではロールバックも行うことはできません。
# pauseされた状態でのロールバック操作
❯ kubectl rollout undo deployment sample-deployment
error: you cannot rollback a paused deployment; resume it first with 'kubectl rollout resume deployment/sample-deployment' and try again
再開する場合は resume を実行します。
❯ kubectl rollout resume deployment sample-deployment
deployment.apps/sample-deployment resumed
# deploymentのロールアウトステータスを確認
❯ kubectl rollout status deployment sample-deployment
deployment "sample-deployment" successfully rolled out
スケーリング
Deployment の管理する ReplicaSet のレプリカ数は、ReplicaSet 単体時と同様の方法で apply、または scale コマンドを使ってスケールさせることが可能です。
# マニフェストを適用してスケールさせる
❯ sed -i -e 's|replicas: 3|replicas: 4|' sample-deployment.yaml
❯ kubectl apply -f sample-deployment.yaml
deployment.apps/sample-deployment configured
❯ kubectl get pods
NAME READY STATUS RESTARTS AGE
sample-deployment-7bf986f9cf-48vx2 1/1 Running 0 41s
sample-deployment-7bf986f9cf-7zhbz 1/1 Running 0 41s
sample-deployment-7bf986f9cf-jfznq 1/1 Running 0 39s
sample-deployment-7bf986f9cf-rgsw2 1/1 Running 0 39s
# scale コマンドを使用してスケールさせる
❯ kubectl scale deployment sample-deployment --replicas=5
deployment.apps/sample-deployment scaled
❯ kubectl get pods
NAME READY STATUS RESTARTS AGE
sample-deployment-7bf986f9cf-48vx2 1/1 Running 0 2m36s
sample-deployment-7bf986f9cf-7zhbz 1/1 Running 0 2m36s
sample-deployment-7bf986f9cf-hgzrd 1/1 Running 0 10s
sample-deployment-7bf986f9cf-jfznq 1/1 Running 0 2m34s
sample-deployment-7bf986f9cf-rgsw2 1/1 Running 0 2m34s
Deployment の削除
最後に作成した Deployment リソースを削除します。
❯ kubectl delete -f sample-deployment.yaml
deployment.apps "sample-deployment" deleted
❯ kubectl get deployments
No resources found in default namespace.
❯ kubectl get replicasets
No resources found in default namespace.
❯ kubectl get pods
No resources found in default namespace.
まとめ
今回は Deployment リソースにおける基本的な操作とセルフヒーリングやローリングアップデート、スケーリングの Reconciliation Loop 挙動に重点を置いて確認してみました。
他にもネットワークやストレージ、クラスタ制御に関連するリソースも含め、クラウドネイティブ領域におけるオーケストレーションとしてやれることは非常に多いです。
また、Kubernetes は拡張性も高く、周辺のエコシステムも豊富で学習コストも高いですが、適切に構築、運用できれば強力に DevOps を進めてくれます。
参考
この記事を書いた人
坊野 豪
メンバーズキャリアカンパニー所属。2019年中途入社。Java, Ruby, Go, JavaScript が守備範囲。前職で国内クラウドプロバイダーのサービス開発をやっていたこともあり、ここ最近はクラウドのアーキテクチャパターンやクラウドネイティブな DevOps が好物。