Posted at

Kubernetes: Deployment の仕組み

More than 1 year has passed since last update.

この記事は Kubernetes 1.5.2 で確認した情報を元に記載しています。


Deployment

Deploymentはローリングアップデートやロールバックといったデプロイ管理の仕組みを提供するものです。


Deployment の仕組み

下記の図のようにDeploymentReplicaSetを生成・管理し、ReplicaSetPodを生成・管理します。

ReplicaSet(ReplicationControllerの後継)はPodTemplateと呼ばれるPodのテンプレートをもとに、Podを指定された数(レプリカ数)に調整・管理を行う仕組みです。Podがレプリカ数より足りない場合はPodを追加し、多い場合はをPodを削除します。この仕組みによってノードの障害やアプリケーションのクラッシュでPodが足りなくなった際も自動的にPodが追加され、セルフヒーリングが実現されています。

Deploymentはコンテナイメージのバージョンアップなどアップデートがあった場合、新しい仕様のReplicaSetを作成し、新旧の全体Pod数を調整しながら新しい仕様のPodに置き換えていきます。これによりローリングアップデートが実現されます。ただし、レプリカ数のみ変更の場合は新しいReplicaSetは作成せず、現在のReplicaSetのレプリカ数を変更します。新旧のPodともに共通するラベルを持っておりローリングアップデート中はServiceを通して新旧のバージョンが混ざってサービス提供される形になります。

リソース
役割

Deployment

ReplicaSetを生成、管理しローリングアップデートやロールバックといったデプロイ管理を行います。

ReplicaSet
同じ仕様のPodのレプリカ数を管理します。ReplicationControllerの後継に当たるものです。

Pod
アプリケーションを動かすための最小単位。1つ以上のコンテナと共有されたボリュームで構成されます。

ローリングアップデートが終わった後もアップデート前のReplicaSetは一定数保持されます。これによりロールバックが可能になっています。


Deployment の定義

以下はDeploymentの定義例になります。下記の内容をdeployment-example.yamlという名前で保存したとします。

apiVersion: extensions/v1beta1

kind: Deployment
metadata:
# Deploymentの名前。Namespace内ではユニークである必要があります
name: deployment-example
spec:
# レプリカ数の指定
replicas: 2
# Podのテンプレート(PodTemplate)
template:
metadata:
labels:
# ラベル指定は必須
app: deployment-example
spec:
containers:
- name: nginx
image: nginx:1.10
ports:
- containerPort: 80

.spec.templatePodTemplateと呼ばれるPodのテンプレートになります。このPodTemplateをもとにReplicaSetがレプリカ数分のPodを生成・管理します。またDeploymentが配下のReplicaSetReplicaSetが管理するPodを管理するためにラベルが必要となり、.spec.template.metadata.labelsで指定する必要があります。


Deployment のデプロイ

定義したDeploymentをデプロイをしてみます。ここでは操作履歴を残すために--recordオプションを指定してkubectlのコマンドをアノテーションに保存するように指定しています。

$ kubectl create -f deployment-example.yaml --record

deployment "deployment-example" created

デプロイできたか確認してみます。

$ kubectl get deployments

NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
deployment-example 2 2 2 2 22m

--selectorオプションで設定したラベルを指定して関連リソースを見てます。

$ kubectl get deployments,replicasets,pods --selector app=deployment-example

NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
deploy/deployment-example 2 2 2 2 30s

NAME DESIRED CURRENT READY AGE
rs/deployment-example-2748039730 2 2 2 30s

NAME READY STATUS RESTARTS AGE
po/deployment-example-2748039730-k5c5j 1/1 Running 0 30s
po/deployment-example-2748039730-p85gg 1/1 Running 0 30s


Deployment の変更

次にDeploymentの情報を変更してみます。ここでは nginx のバージョンを変更してみます。先程のdeployment-example.yamlを編集し、nginx のバージョンを 1.11 にしてみます。

# 省略...

spec:
containers:
- name: nginx
image: nginx:1.11 # 1.10から1.11に変更
ports:
- containerPort: 80

変更を反映させます。

$ kubectl apply -f deployment-example.yaml --record

deployment "deployment-example" configured

変更されたか見てみます。ローリングアップデートで新旧のPodの数を調整しながらアップデートされていきます。

$ kubectl get deployments,replicasets,pods --selector app=deployment-example

NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
deploy/deployment-example 2 2 2 2 7m

NAME DESIRED CURRENT READY AGE
rs/deployment-example-2748039730 0 0 0 7m # 古いバージョンのReplicaSet。レプリカ数が0に
rs/deployment-example-2829304371 2 2 2 27s # 新しいバージョンのReplicaset。レプリカ数が2に

NAME READY STATUS RESTARTS AGE
po/deployment-example-2829304371-33x2x 1/1 Running 0 27s # Podは再生成される
po/deployment-example-2829304371-szbxm 1/1 Running 0 27s

アップデート中は下記の図のような状態になっています。アップデートが終わっても古いバージョンのReplicaSetは履歴管理のために残ります。

Podのコンテナのバージョンが新しくなっていることを確認してみます。

$ kubectl describe pods --selector=app=deployment-example

...
Controllers: ReplicaSet/deployment-example-2829304371
Containers:
nginx:
Container ID: docker://a7adf603e653826d4092d8b1340f985584767080c47a5d23895c438ed5d4d813
Image: nginx:1.11 # コンテナのバージョンが新しくなっていれば成功
Image ID: docker://sha256:01f818af747d88b4ebca7cdabd0c581e406e0e790be72678d257735fad84a15f
Port: 80/TCP
...


ロールバック

Deploymentの特徴にロールバックがあります。デプロイの履歴を見てみます。

$  kubectl rollout history deployment deployment-example

deployments "deployment-example"
REVISION CHANGE-CAUSE
1 kubectl create -f deployment-example.yaml --record
2 kubectl apply -f deployment-example.yaml --record

リビジョンの詳細は下記のコマンドで確認できます。

$ kubectl rollout history deployment deployment-example --revision=2

deployments "deployment-example" with revision #2
Labels: app=deployment-example
pod-template-hash=2829304371
Annotations: kubernetes.io/change-cause=kubectl apply -f deployment-example.yaml --record
Containers:
nginx:
Image: nginx:1.11
Port: 80/TCP
Volume Mounts: <none>
Environment Variables: <none>
No volumes.

リビジョンを指定してロールバックしてみます。--to-revisionオプションを指定しない場合は直前のリビジョンになります。

$ kubectl rollout undo deployment deployment-example --to-revision=1

deployment "deployment-example" rolled back

nginx コンテナのバージョンが 1.11 から 1.10 にロールバックされていることを確認します。ロールバックまで少し時間がかかる場合があります。

$ kubectl describe pods --selector=app=deployment-example

...
Containers:
nginx:
Container ID: docker://fae4d04753243119bf5840fefd820353766eddfd9dd75a628ea1e56673cf3828
Image: nginx:1.10 # 1.10に戻っている
Image ID: docker://sha256:c2d83d8cde8d6b13e774170f85d87829736e6614a8be33479bb7f8bd7f12dd06
Port: 80/TCP

デプロイの履歴を見てみます。リビジョン1の行がなくなり、リビジョン3(最新)になっていることがわかります。

$ kubectl rollout history deployment deployment-example

deployments "deployment-example"
REVISION CHANGE-CAUSE
2 kubectl apply -f deployment-example.yaml --record
3 kubectl create -f deployment-example.yaml --record # 1から3(最新)に変更された

なおデプロイの履歴は内部的には過去のReplicaSetで管理されており、ロールバックは過去のReplicaSetの切り替えることで行われています。リビジョンやコマンドの履歴といった情報はReplicaSetのアノテーションとして保存されています。


補足: Deployment のラベル

Deploymentではspec.template.metadata.labels設定したラベルが自動的にDeploymentのセレクタ(.spec.selector.matchLabels)に設定されるようになっています。

$ kubectl get deployment deployment-example -o yaml

apiVersion: extensions/v1beta1
kind: Deployment
...
spec:
replicas: 2
selector:
# 自動的に設定される
matchLabels:
app: deployment-example
...
template:
metadata:
creationTimestamp: null
labels:
# マニフェストで設定した値
app: deployment-example
...

Deploymentspec.template.metadata.labelsの値はReplicaSetのラベルとセレクタの一部とPodのラベルに使われています。またReplicaSetごとに配下のPodを識別するためにpod-template-hashというPodTemplateから生成したハッシュ値を持っています。

$ kubectl get replicaset -l app=deployment-example -o yaml

...
- apiVersion: extensions/v1beta1
kind: ReplicaSet
spec:
replicas: 2
selector:
# このセレクタでPodを選択する
matchLabels:
# deploymentで指定したラベル
app: deployment-example
# Podを識別するためのPodTemplateから作られたハッシュ値
pod-template-hash: "2748039730"
template:
metadata:
creationTimestamp: null
labels:
# セレクタと同じ値
app: deployment-example
pod-template-hash: "2748039730"

PodReplicaSetが持つセレクタと同じラベルが設定されています。

$ kubectl get pods -l app=deployment-example -o yaml

...
- apiVersion: v1
kind: Pod
metadata:
...
labels:
app: deployment-example
pod-template-hash: "2748039730"

下記の図ではDeploymentspec.template.metadata.labelsapp=myappだった場合のラベルの付け方を説明しています。新旧のPodともに共通のapp=myappというラベルがあるためローリングアップデート中もServiceを通して断続なくサービス提供でき、新旧で異なるpod-template-hashというラベルがあるため個別にも管理できる仕組みになっています。


まとめ



  • Deploymentはローリングアップデートやロールバックといったデプロイ管理の仕組みを提供します

  • 同じ仕様のPodを管理するReplicaSetとアプリケーションを動作させる最小単位のPodで構成されています

  • 履歴が管理されておりロールバックが可能です


参考