ServiceとPodだけでほぼダウンタイムゼロのリリースを実現する
ラベルを使ってPodを世代管理しておくと、Serviceを更新するだけでリリース(トラフィックルーティングを切り替える)を行なうことができて便利です。
要約
- 実装
- メリット
- アプリケーションのデプロイとサービス公開の作業を分離できる
- Service(L4レイヤの設定)を切り替えるだけなので影響範囲・切り替えコストを最小化できる
- Deploymentの再applyによるダウンタイムを極力ゼロにできる
- デメリット
- 一時的にクラスタのリソースを過分に利用してしまう
- 古いバージョンのお掃除が大変
詳細
全体像
扱うKubernetesリソースが2つ(ServiceとDeployment)だけなので最初にmanifestの全体像を貼ります。
この設定では hoge-0.0.1
と hoge-0.0.2
という2つのDeploymentが用意されており、現在Serviceは hoge-0.0.1
に紐づけられているPod( label['version']: v0.0.1
) に向けられています。
apiVersion: v1 kind: Service metadata: name: hoge labels: app: hoge spec: type: ClusterIP ports: - name: http protocol: TCP port: 80 targetPort: 8080 selector: app: hoge version: 0.0.1 --- apiVersion: apps/v1 kind: Deployment metadata: name: hoge-0.0.1 labels: app: hoge version: 0.0.1 spec: replicas: 10 selector: matchLabels: app: hoge template: metadata: labels: app: hoge version: 0.0.1 spec: containers: - name: hoge image: tacumaigei/hoge:0.0.1 ports: - containerPort: 8080 --- apiVersion: apps/v1 kind: Deployment metadata: name: hoge-0.0.2 labels: app: hoge version: 0.0.2 spec: replicas: 10 selector: matchLabels: app: hoge template: metadata: labels: app: hoge version: 0.0.2 spec: containers: - name: hoge image: tacumaigei/hoge:0.0.2 ports: - containerPort: 8080
現在のPodの状況はこう(以降、すべて namespaceはdefaultとします)
kubectl get deployment NAME READY UP-TO-DATE AVAILABLE AGE hoge-0.0.1 10/10 10 10 8h hoge-0.0.2 10/10 10 10 8h kubectl get pods NAME READY STATUS RESTARTS AGE hoge-0.0.1-5d45d5845d-7lqpw 1/1 Running 1 8h hoge-0.0.1-5d45d5845d-85lxj 1/1 Running 1 8h hoge-0.0.1-5d45d5845d-98cnl 1/1 Running 1 8h hoge-0.0.1-5d45d5845d-b96ff 1/1 Running 1 8h hoge-0.0.1-5d45d5845d-jx7tc 1/1 Running 1 8h hoge-0.0.1-5d45d5845d-knjng 1/1 Running 1 8h hoge-0.0.1-5d45d5845d-mzwrq 1/1 Running 1 8h hoge-0.0.1-5d45d5845d-t2h84 1/1 Running 1 8h hoge-0.0.1-5d45d5845d-wxpd6 1/1 Running 1 8h hoge-0.0.1-5d45d5845d-zgnq6 1/1 Running 1 8h hoge-0.0.2-f579c9474-49bx5 1/1 Running 1 8h hoge-0.0.2-f579c9474-7lkfz 1/1 Running 1 8h hoge-0.0.2-f579c9474-9bxd7 1/1 Running 1 8h hoge-0.0.2-f579c9474-b7qmg 1/1 Running 1 8h hoge-0.0.2-f579c9474-ctn6v 1/1 Running 1 8h hoge-0.0.2-f579c9474-jc4x6 1/1 Running 1 8h hoge-0.0.2-f579c9474-jkfrd 1/1 Running 1 8h hoge-0.0.2-f579c9474-lrj42 1/1 Running 1 8h hoge-0.0.2-f579c9474-p2427 1/1 Running 1 8h hoge-0.0.2-f579c9474-rzv6q 1/1 Running 1 8h kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE hoge ClusterIP 10.96.222.54 <none> 80/TCP 8h kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 10h
利用しているアプリケーション(dockerfile)は適宜ご自身のアプリケーションと思ってください。この例で利用しているアプリケーションは /
にリクエストすると HOGE: Hello World!
が返ってくるだけの簡易アプリケーションです(リポジトリ))。
ここで重要なことが2つあります。
- Deploymentの名前はバージョンごとに設定すること(今回は
hoge-0.0.1
hoge-0.0.2
としています) - ServiceのSelectorはPodに対して行われる
1.の設定は複数アプリケーションをバージョン違いで共存させるために必要です。Deploymentに紐づいているPodのlabelだけバージョンを上げてDeployment名を同じままにしておくとDeploymentを更新してしまうことになるため注意です。
2.の設定は理解に注意が必要です。ServiceはあくまでPodを認知する機構であり、Deploymentを認知するわけではないということです。Selectorの設定で Deploymentのラベルであるapp: hoge-0.0.1
を設定してもこの名前のPodを探しにいってしまい、見つかりません。 なのでPodのラベルを使って、適切にPodを認知させてください。
Serviceを hoge-v0.0.2
に切り替え
ここが本題。
現在 ラベル app:hoge
version: 0.0.1
と設定されているPodに向けられているServiceの設定を version: 0.0.2
へ変更します。
apiVersion: v1 kind: Service metadata: name: hoge labels: app: hoge spec: type: ClusterIP ports: - name: http protocol: TCP port: 80 targetPort: 8080 selector: app: hoge version: 0.0.2 # 変更箇所
kubectl apply -f service.yaml
これでDeployment hoge-0.0.2
に切り替わりました。
めちゃくちゃ楽ですね。
アプリケーションのdockerimageが作成されたらdeploymentファイルをクラスタにapply、という一連の処理を自動化しておけば、リリース時にはServiceの切り替え作業だけにすることができ、かつダウンタイムがほぼゼロに収まる運用を実現できます。
検証結果
念のため、ローカルでkindを立ち上げて検証しました。
同じnamespace内にcurlコマンドを叩くためのPodを用意し、Service hoge
にリクエストを流し続けている間にcurlを叩き続けた結果がこちらです。
# hogeと同じnamespace内に立てたPodの中 $ for i in {1...100000}; do curl hoge:8080/; done HOGE: Hello,World!HOGE: Hello,World!HOGE: Hello,World!HOGE: Hello,World!HOGE: Hello,World!HOGE: Hello,World!HOGE: Hello,World!HOGE 2!!!!!!!!: Hello,World!HOGE 2!!!!!!!!: Hello,World!HOGE 2!!!!!!!!: Hello,World!HOGE 2!!!!!!!!: Hello,World!HOGE 2!!!!!!!!: Hello,World!HOGE 2!!!!!!!!:
検証した限りはリクエストの欠損なく切り替えられていることが確認できました。 これはクラスタの負荷状況やリクエスト数によっても変わるところなのでダウンタイムゼロ!と断言することはできない部分ですが、一番コスト低く新しいPodをサービス公開する手順として覚えておくとよさそうです。