0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

KubernetesのDeploymentとは何か?Podの自己修復とローリングアップデートを理解する

0
Posted at

はじめに

前回は、Podを実際に動かすコンピューターであるNodeについて学びました。

ここまでの関係は、次のようになります。

Node
└── Pod
    └── Container

しかし、実際の運用ではPodを1つずつ直接作成し、手作業で管理することはあまりありません。

Podが停止したときに作り直したり、アクセス増加に合わせて個数を増やしたり、新しいバージョンへ安全に入れ替えたりする仕組みが必要だからです。

そこで登場するのが Deployment(デプロイメント) です。

この記事では、次の内容を整理します。

  • Deploymentとは何か
  • Podを直接作成する場合との違い
  • Deployment・ReplicaSet・Podの関係
  • Podの個数が自動的に維持される仕組み
  • Podを増減する方法
  • ローリングアップデートとロールバック
  • DeploymentのYAMLの読み方
  • DeploymentとServiceの役割の違い

結論

Deploymentとは、どのようなPodを何個動かし、どのように更新するかを管理するKubernetesのリソースです。

たとえば、Rails Podを3個動かしたい場合は、Deploymentに次のような希望状態を定義します。

Deployment
「Rails Podを3個動かしたい」
        ↓
現在の状態を確認
        ↓
Rails Podを3個維持する

1個のPodが停止して2個になると、Deploymentの管理下では新しいPodが作られ、再び3個へ戻ります。

希望する状態:3個
現在の状態 :2個
        ↓
新しいPodを1個作成
        ↓
現在の状態 :3個

さらに、Deploymentは新しいコンテナイメージへのローリングアップデートや、問題発生時のロールバックも管理します。

Podを直接作成する場合の問題

PodはYAMLから直接作成できます。

apiVersion: v1
kind: Pod
metadata:
  name: rails-pod
spec:
  containers:
    - name: rails
      image: example/rails-app:v1

しかし、このPodを直接作成した場合、Podが削除されても同じPodを維持してくれる上位のコントローラーがありません。

Podを直接作成
        ↓
Podが削除される
        ↓
Podは0個のまま

また、Podを3個動かしたい場合に、ほぼ同じYAMLを3個用意して個別に管理するのは大変です。

rails-pod-1.yaml
rails-pod-2.yaml
rails-pod-3.yaml

Deploymentを使えば、「この定義のPodを3個動かす」という希望状態を1つのYAMLで宣言できます。

Deployment
└── 同じ定義のRails Podを3個維持する

Deploymentとは

Deploymentは、アプリケーションのPodを宣言的に管理するためのリソースです。

「この処理を1回実行してください」という命令ではなく、「この状態を維持してください」という希望状態をKubernetesへ伝えます。

命令的な考え方
「Podを3個作ってください」

宣言的な考え方
「Podが常に3個存在する状態にしてください」

Deploymentは、主に次のことを管理します。

  • Podの作成
  • 指定したPod数の維持
  • Pod数の増減
  • コンテナイメージやPod設定の更新
  • ローリングアップデート
  • 更新状況の記録
  • 問題発生時のロールバック

Deployment・ReplicaSet・Podの関係

DeploymentがPodを管理すると説明されることが多いですが、厳密にはDeploymentとPodの間に ReplicaSet があります。

Deployment
└── ReplicaSet
    ├── Pod 1
    ├── Pod 2
    └── Pod 3

それぞれの役割は次のとおりです。

要素 役割
Deployment ReplicaSetの作成・更新履歴・切り替えを管理する
ReplicaSet 指定された個数のPodを維持する
Pod コンテナを実際に動かす単位
Container RailsやSidekiqなどのアプリケーションを実行する

普段はReplicaSetを直接作成・更新するのではなく、Deploymentを操作します。

Deploymentを作成すると、DeploymentがReplicaSetを作り、そのReplicaSetがPodを作成します。

Deploymentを作成
        ↓
ReplicaSetを作成
        ↓
指定された個数のPodを作成
        ↓
Pod内でContainerを起動

なぜReplicaSetが間にあるのか

ReplicaSetの基本的な役割は、指定された個数のPodを維持することです。

ReplicaSet
「Podを3個維持する」
        ↓
Pod 1
Pod 2
Pod 3

一方、DeploymentはReplicaSetを利用して、アプリケーションのバージョンを管理します。

コンテナイメージをv1からv2へ変更すると、Deploymentは新しいPod定義のためのReplicaSetを作ります。

Deployment
├── 古いReplicaSet
│   └── image: v1のPod
│
└── 新しいReplicaSet
    └── image: v2のPod

新しいReplicaSetのPodを増やしながら、古いReplicaSetのPodを減らすことで、段階的な更新が可能になります。

この仕組みがローリングアップデートです。

希望する状態と現在の状態

Kubernetesでは、YAMLに書いた希望する状態と、実際の現在の状態を比較し続けます。

Deploymentで次のように指定したとします。

spec:
  replicas: 3

これは、「Podを作る処理を3回実行する」という意味ではありません。

「対象のPodが3個存在する状態を維持する」という意味です。

希望する状態:Pod 3個

現在の状態:Pod 3個
→ 何もしない

現在の状態:Pod 2個
→ Podを1個作る

現在の状態:Pod 4個
→ Podを1個減らす

このように、現在の状態を希望する状態へ近づけ続ける処理を Reconciliation(調整ループ) と呼びます。

Podが停止したときの自己修復

Deploymentの管理下にあるPodが失われると、ReplicaSetがPod数の不足を検知し、新しいPodを作成します。

障害発生前
├── Rails Pod A
├── Rails Pod B
└── Rails Pod C

Rails Pod Bが削除される
        ↓
現在は2個しかない
        ↓
新しいRails Pod Dを作成
        ↓
再び3個になる

ここで重要なのは、消えたPod Bが復活するわけではないことです。

別の名前やIPアドレスを持つ、新しいPod Dが作成されます。

削除前:rails-abc-123
削除後:rails-abc-789

そのため、Podは同じ個体を長期間使い続けるサーバーではなく、必要に応じて作り直される一時的な存在として扱います。

Deploymentを定義するYAML

以下は、Railsの代わりにNginxを3個動かす基本的なDeploymentです。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: sample-nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: sample-nginx
  template:
    metadata:
      labels:
        app: sample-nginx
    spec:
      containers:
        - name: nginx
          image: nginx:1.27
          ports:
            - containerPort: 80
          resources:
            requests:
              cpu: "100m"
              memory: "128Mi"
            limits:
              cpu: "200m"
              memory: "256Mi"

大きく分けると、次の構造になっています。

Deploymentの設定
├── Podを何個動かすか
├── どのPodを管理対象にするか
└── どのようなPodを作るか
    └── どのContainerを動かすか

YAMLの各項目

apiVersionとkind

apiVersion: apps/v1
kind: Deployment
項目 意味
apiVersion: apps/v1 Deploymentを扱うKubernetes APIのバージョン
kind: Deployment 作成するリソースがDeploymentであることを示す

metadata

metadata:
  name: sample-nginx

Deployment自体の名前です。

作成後は、sample-nginxという名前でDeploymentを指定できます。

replicas

replicas: 3

動かしたいPodの個数です。

この値を5へ変更して適用すると、Podが5個になるように調整されます。

selector

selector:
  matchLabels:
    app: sample-nginx

Deploymentが管理対象を識別するための条件です。

この例では、app: sample-nginxというラベルを持つPodを対象にします。

template

template:
  metadata:
    labels:
      app: sample-nginx

templateは、Deploymentが作成するPodの設計図です。

template.metadata.labelsには、作成されるPodへ付けるラベルを定義します。

selector.matchLabelsとtemplate.metadata.labelsは対応している必要があります。

selector.matchLabels
app: sample-nginx
        ↕ 一致させる
template.metadata.labels
app: sample-nginx

一致していない場合、Deploymentを正しく作成できません。

template内のspec

template:
  spec:
    containers:
      - name: nginx
        image: nginx:1.27

これは、作成するPodの中で動かすコンテナの定義です。

Podを直接定義するYAMLのspecに相当する部分が、Deploymentではspec.template.specの中に入ります。

Deployment
└── spec
    └── template
        └── spec
            └── containers

Deploymentを作成する

YAMLをdeployment.yamlとして保存した場合、次のコマンドで適用できます。

kubectl apply -f deployment.yaml

Deploymentの状態を確認します。

kubectl get deployments

作成されたReplicaSetを確認します。

kubectl get replicasets

作成されたPodを確認します。

kubectl get pods

関係をまとめると、次のようになります。

kubectl apply -f deployment.yaml
        ↓
Deploymentを作成
        ↓
ReplicaSetを作成
        ↓
Podを3個作成

Deploymentの詳細やイベントを確認する場合は、次のコマンドを使用します。

kubectl describe deployment sample-nginx

Podを増減する

Pod数を3個から5個へ増やす場合は、YAMLのreplicasを変更します。

spec:
  replicas: 5

その後、再び適用します。

kubectl apply -f deployment.yaml

一時的な操作であれば、次のコマンドでもPod数を変更できます。

kubectl scale deployment sample-nginx --replicas=5

ただし、GitなどでYAMLを管理している場合は、コマンドだけで変更するとYAMLに書かれた値とクラスターの状態が食い違います。

そのため、継続して使う設定はYAML側も更新します。

PodはどのNodeへ配置されるのか

Deploymentは「Podを何個作るか」を管理しますが、PodをどのNodeへ配置するかは直接決めません。

作成された各Podの配置先は、基本的にkube-schedulerが決定します。

Deployment
「Rails Podを3個必要としています」
        ↓
ReplicaSetがPodを作成
        ↓
kube-schedulerが配置先を決定
        ↓
Node 1:Rails Pod A
Node 2:Rails Pod B
Node 3:Rails Pod C

ただし、Podが必ず異なるNodeへ1個ずつ分散されるとは限りません。

何も条件を設定していなければ、複数のPodが同じNodeへ配置されることもあります。

厳密に分散させたい場合は、Pod anti-affinityやtopology spread constraintsなどの配置条件を使用します。

ローリングアップデートとは

DeploymentでコンテナイメージなどのPodテンプレートを変更すると、DeploymentはPodを段階的に新しいバージョンへ入れ替えます。

たとえば、Railsアプリケーションをv1からv2へ更新するとします。

containers:
  - name: rails
    image: example/rails-app:v2

Deploymentは、新しいReplicaSetを作成します。

更新前
Deployment
└── ReplicaSet v1
    ├── Pod v1
    ├── Pod v1
    └── Pod v1

更新開始
Deployment
├── ReplicaSet v1
│   ├── Pod v1
│   └── Pod v1
│
└── ReplicaSet v2
    └── Pod v2

その後、新しいPodを増やしながら古いPodを減らします。

途中
├── Pod v1
├── Pod v2
└── Pod v2

更新完了
├── Pod v2
├── Pod v2
└── Pod v2

すべてのPodを一度に停止してから新しいPodを作るのではないため、アプリケーションを提供し続けながら更新しやすくなります。

ただし、無停止を実現するには、アプリケーション側の互換性、Readiness Probe、十分なNodeリソースなども必要です。

Deploymentを使うだけで、どのようなアプリケーションでも必ず無停止になるわけではありません。

RollingUpdateの設定

Deploymentの標準的な更新方式はRollingUpdateです。

spec:
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 1
      maxSurge: 1

各項目の意味は次のとおりです。

項目 意味
maxUnavailable 更新中に利用できなくてもよいPodの最大数
maxSurge 希望するPod数を超えて一時的に作成できるPodの最大数

Podを3個動かしている場合、上記の設定ではおおむね次のように更新できます。

通常:3個
        ↓
新しいPodを一時的に1個追加:最大4個
        ↓
古いPodを1個ずつ減らす
        ↓
最終的に新しいPodが3個

値には個数だけでなく、25%のような割合も指定できます。

更新状況を確認する

Deploymentの更新状況は、次のコマンドで確認できます。

kubectl rollout status deployment/sample-nginx

更新履歴は次のコマンドで確認できます。

kubectl rollout history deployment/sample-nginx

PodやReplicaSetも合わせて確認すると、古いPodから新しいPodへ切り替わる様子を把握できます。

kubectl get replicasets
kubectl get pods

ロールバックとは

新しいバージョンで問題が起きた場合、Deploymentを以前のリビジョンへ戻せます。

v1:正常
        ↓
v2へ更新
        ↓
不具合を検知
        ↓
v1相当のPodテンプレートへロールバック

直前のリビジョンへ戻す場合は、次のコマンドを使用します。

kubectl rollout undo deployment/sample-nginx

特定のリビジョンへ戻す場合は、次のように指定できます。

kubectl rollout undo deployment/sample-nginx --to-revision=2

ロールバックは、以前のPodそのものを復活させる処理ではありません。

保存されている以前のPodテンプレートを使い、再びローリングアップデートを行う処理です。

また、データベース変更など、Deploymentの外で行われた変更まで自動的に元へ戻すわけではありません。

Podを再起動したい場合

Deploymentに「同じPodをその場で再起動する」という操作はありません。

管理下のPodを順番に作り直したい場合は、次のコマンドを利用できます。

kubectl rollout restart deployment/sample-nginx

これは既存コンテナ内のプロセスだけを再起動するのではなく、ローリングアップデートの仕組みを使ってPodを新しく作り直します。

DeploymentとServiceの関係

DeploymentとServiceは、どちらも複数のPodに関係しますが、役割が異なります。

Deployment
└── Podを3個作成・維持する

Service
└── 対象のPodへ通信を届ける
要素 役割
Deployment Podの個数・定義・更新を管理する
Service Podに対する安定した接続先を提供する

全体の関係は次のようになります。

Deployment
└── ReplicaSet
    ├── Rails Pod 1 ←┐
    ├── Rails Pod 2 ←┼─ Serviceから通信
    └── Rails Pod 3 ←┘

DeploymentとServiceは、Podへ付けられたラベルをそれぞれの目的で利用します。

labels:
  app: rails

ただし、DeploymentがServiceへ通信を渡すわけではありません。

DeploymentはPodの管理、Serviceは通信の入口を担当します。

RailsとSidekiqは別のDeploymentにする

RailsとSidekiqは、同じコンテナイメージを使う場合でも役割や増減のタイミングが異なります。

Rails Deployment
└── Rails Podを3個維持

Sidekiq Deployment
└── Sidekiq Podを2個維持

RailsへのHTTPリクエストが増えた場合はRails Podを増やし、バックグラウンドジョブが増えた場合はSidekiq Podを増やせます。

Webアクセス増加
→ Rails Deploymentをスケール

ジョブ増加
→ Sidekiq Deploymentをスケール

同じDeploymentへまとめると、Railsだけ増やしたい場合でもSidekiqまで同じ単位で増えてしまいます。

そのため、独立して更新・拡張したいアプリケーションは別のDeploymentにします。

EKSにおけるDeployment

DeploymentはKubernetesのリソースであり、EKS専用の機能ではありません。

EKS上でも、基本的な関係は同じです。

Amazon EKSクラスター
└── Deployment
    └── ReplicaSet
        └── Pod
            └── Container

EC2をNodeとして使う構成では、作成されたPodが各EC2 Nodeへ配置されます。

EKSクラスター
├── Node 1(EC2)
│   ├── Rails Pod 1
│   └── Sidekiq Pod 1
│
└── Node 2(EC2)
    ├── Rails Pod 2
    └── Rails Pod 3

DeploymentのYAMLはKubernetes APIへ送られ、EKSのControl Planeによって処理されます。

EKSがDeploymentそのものの代わりになるわけではありません。

Deploymentが向いているアプリケーション

Deploymentは、基本的に個々のPodの名前や順番を固定する必要がないアプリケーションに向いています。

代表例は次のとおりです。

  • RailsなどのWebアプリケーション
  • APIサーバー
  • NginxやEnvoyなどのプロキシ
  • Sidekiqなどのワーカー

一方、Podごとに固定した名前や永続ストレージとの対応関係が必要なアプリケーションでは、StatefulSetが適する場合があります。

Deployment
└── 入れ替わってもよい同一役割のPodを管理

StatefulSet
└── 固定した識別子や順序が必要なPodを管理

たとえば、データベースをKubernetes上で構築する場合は、DeploymentだけでなくStatefulSetや永続ストレージについて検討する必要があります。

よくある勘違い

DeploymentはPodそのもの?

違います。

DeploymentはPodを実行する場所ではなく、Podの個数や更新状態を管理するリソースです。

実際にコンテナを動かす単位はPodです。

DeploymentがPodを直接管理する?

説明を簡略化するとそのように表現できますが、実際にはDeploymentがReplicaSetを管理し、ReplicaSetがPod数を維持します。

Deployment → ReplicaSet → Pod

replicasを3にすると、必ず3台のNodeへ分散される?

必ずしも分散されません。

replicas: 3はPod数を3個にする指定であり、配置先Nodeを3台に分ける指定ではありません。

Podが落ちると、同じPodが再起動する?

コンテナだけが異常終了した場合は、Pod内でコンテナが再起動されることがあります。

一方、Pod自体が削除・消失した場合は、同じPodが戻るのではなく、新しいPodが作成されます。

Deploymentを作れば外部からアクセスできる?

Deploymentを作成しただけでは、外部からアクセスするための入口は提供されません。

通信の入口には、ServiceやIngressなど別のリソースが必要です。

Deploymentを使えば必ず無停止で更新できる?

必ずしも無停止になるわけではありません。

ローリングアップデートに加えて、Readiness Probe、十分なリソース、アプリケーションやDBの後方互換性などが必要です。

Deploymentはデータベースにも使える?

技術的にPodを起動することはできますが、固定された識別子や永続ストレージが重要なデータベースでは、StatefulSetやRDSなどを検討します。

まとめ

Deploymentについて、重要な点をまとめます。

  • Deploymentは、どのようなPodを何個動かすか管理する
  • 希望する状態と現在の状態を比較し、Pod数を維持する
  • DeploymentとPodの間にはReplicaSetがある
  • ReplicaSetが指定された個数のPodを維持する
  • Podが失われると、同じPodではなく新しいPodが作成される
  • replicasでPod数を指定する
  • templateに作成するPodの設計図を書く
  • selector.matchLabelsとtemplate.metadata.labelsを対応させる
  • Podテンプレートの変更時は、新しいReplicaSetが作成される
  • ローリングアップデートでは、Podを段階的に新しいバージョンへ入れ替える
  • 問題があれば、以前のリビジョンへロールバックできる
  • DeploymentはPodを管理し、ServiceはPodへの通信を担当する
  • RailsとSidekiqのように別々に増減・更新したい処理は、別のDeploymentにする
  • EKSでもDeploymentはKubernetesリソースとして動作する

最も簡単に覚えるなら、次の関係です。

Deployment
└── ReplicaSet
    └── Pod
        └── Container

それぞれの役割を一言で表すと、次のようになります。

Deployment:Podの定義・個数・更新を管理する
ReplicaSet:指定された個数のPodを維持する
Pod       :Containerを動かす
Node      :Podを動かす
Service   :Podへの通信を受け付ける

PodとNodeを理解したあとにDeploymentを学ぶと、Kubernetesが「Podを作るだけでなく、必要な個数を維持し、安全に更新する仕組み」が見えやすくなります。

参考資料

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?