Kubernetesアプリケーションの快適リリースとGitOps
はじめに
さて、前回までの記事で、Kubernetesアプリケーションを構築して動作させるための必要な知識は揃いました。このあと、実際にアプリケーションを運用するにあたって考えなければいけないのがアップデートです。そもそもKubernetesのようなプラットフォームを選択する大きな理由のひとつが、このアップデートを自動化して高頻度なリリースサイクルを実現することにあります。
今回は、Kubernetes上のアプリケーションのアップデートの仕組みと、CI/CDを含むリリースサイクルを管理するためのベストプラクティスとされているGitOpsについて、実際にアプリケーションへ適用しながら紹介します。
Deploymentによるローリングアップデート
Kubernetes上のアプリケーションのアップデートは、稼働しているPodのイメージ更新や設定変更になることがほとんどです。そしてこれらのPodの多くは、第3回で紹介したDeploymentリソースで管理されています。このDeploymentリソースを使ってPodが立ち上がっていると、Podのアップデート時にPodをひとつずつ通信断のないように更新してくれるローリングアップデートが動作します。
このローリングアップデートは、Deploymentのspec.template配下が変更されると自動的に実行されます。古いバージョンのPodの削除と、新しいバージョンのPodの作成が順次行われることで、Serviceを通してアプリケーションにアクセスできる状態を保ちながらアップデートされます。このときのPodの削除と作成の挙動をdeployment.spec.strategy.rollingUpdate配下の2つの設定値で変更できます。下記にマニフェストのサンプルを示します。
spec: replicas: 3 selector: matchLabels: app: nginx strategy: rollingUpdate: maxSurge: 25% maxUnavailable: 25% type: RollingUpdate template: metadata: labels: app: nginx spec: containers: - image: nginx
maxSurge | Deploymentのレプリカ数を超過して存在できるPodの最大数。個数あるいはパーセンテージで設定可能。レプリカ数=5、maxSurge=2 (または40%)の場合、ローリングアップデート開始時に即座に2つの新しいPodが作成される。以降は、古いPodが完全に削除された段階で総数7を超えないように新しいPodを作成していく |
maxUnavailable | Deploymentのレプリカ数を下回って存在できるReadyでないPodの最大数。個数あるいはパーセンテージで設定可能。レプリカ数=5、maxUnavailable=2 (または40%)の場合、ローリングアップデート開始時に即座に2つのPodを削除する。以降は、新しいPodがReadyになった段階で総数3を下回らないように古いPodを削除していく |
余剰なリソースの作成を抑制したい場合はmaxSurgeの値を小さくする、サービスへのパフォーマンス影響を小さくしたい場合はmaxUnavailableの値を小さくする、早くUpdateを完了したい場合はそれぞれの値を大きくする、と理解しておきましょう。
また、このローリングアップデートでPodが削除される際は、Podの削除とServiceからの除外がほぼ同時に発生してしまいます。ちょうどPodが削除されるタイミングでアクセスが発生すると、削除処理が開始したPodに通信が振り分けられてPodが応答できないことがあります。これを回避するためには、アプリケーションへのアクセスを即座に停止させない(Graceful Shutdown)ようにコンテナを作るか、pod.spec.containers.lifecycle.preStopに何らかのコンテナ停止前の処理を記述しておく必要があります。最もシンプルな方法は、下記のサンプルのように後者でsleep 5などにしてコンテナの停止を5秒程度待つことです。もちろん、コンテナ内にsleepコマンドが含まれている必要があります。
spec: replicas: 3 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - image: nginx lifecycle: preStop: exec: command: ["sleep", "5"]
ここからは、実際にアプリケーションをローリングアップデートして動作を見てみましょう。第4回で構築したニュースアプリケーションを更新していきます。単にヘッダーの色を黒から青に変更しています。
では、この変更を適用してみます。ここでは、変更を反映した新たなコンテナイメージにblueというタグを付けています。kustomize.yaml を編集し、コンテナイメージのタグを更新してkubectl apply -k overlays/develop/で変更を適用します。なお、事前にdeploymentのレプリカ数を3に増やしています。
# kustomization.yaml images: - name: website newName: registry.gitlab.com/thinkit-kubernetes-sample1/website newTag: blue
しばらくアクセスを続けると、変更が反映されていることが確認できました。このときのDeploymentの動きを見てみましょう。下記のログは、kubectl get deploymentに-wオプションを付けてDeploymentの動きを観測したものです。
$ kubectl get deployment website -o wide -w NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR website 3/3 3 3 3m11s website registry.gitlab.com/creationline/thinkit-kubernetes-sample1/website:dev app=website website 3/3 3 3 3m58s website registry.gitlab.com/creationline/thinkit-kubernetes-sample1/website:blue app=website website 3/3 0 3 3m58s website registry.gitlab.com/creationline/thinkit-kubernetes-sample1/website:blue app=website website 3/3 1 3 3m58s website registry.gitlab.com/creationline/thinkit-kubernetes-sample1/website:blue app=website website 4/3 1 4 4m10s website registry.gitlab.com/creationline/thinkit-kubernetes-sample1/website:blue app=website website 3/3 1 3 4m10s website registry.gitlab.com/creationline/thinkit-kubernetes-sample1/website:blue app=website website 3/3 2 3 4m10s website registry.gitlab.com/creationline/thinkit-kubernetes-sample1/website:blue app=website website 4/3 2 4 4m25s website registry.gitlab.com/creationline/thinkit-kubernetes-sample1/website:blue app=website website 3/3 2 3 4m25s website registry.gitlab.com/creationline/thinkit-kubernetes-sample1/website:blue app=website website 3/3 3 3 4m25s website registry.gitlab.com/creationline/thinkit-kubernetes-sample1/website:blue app=website website 4/3 3 4 4m27s website registry.gitlab.com/creationline/thinkit-kubernetes-sample1/website:blue app=website website 3/3 3 3 4m27s website registry.gitlab.com/creationline/thinkit-kubernetes-sample1/website:blue app=website
READYとAVAILABLEはREADY状態のPod数、UP-TO-DATEは最新のPODでDeployされているPod数です。はじめにimageタグが書き換わり、UP-TO-DATEの値が一度0になっているのがわかります。そこから1つずつPodを増やし、READYのPodが1つ増えた段階で古いPodを削除し、再びREADYの数を3に戻す動きをしています。最終的に、3つすべてのPodがUP-TO-DATEでREADYの状態になりました。
CI/CDとGitOps
これまで見てきたように、kubernetesではアプリケーションの構成情報をマニフェストファイルの形で表現しています。これらのマニフェストファイルは多くの場合で、アプリケーションのソースコードと同様にGitで管理されます。本連載のレポジトリでもそうしていますね。そしてGit管理されているマニフェストファイルをもとに、変更を自動的にkubernetesクラスタにデプロイするようなCI/CDパイプラインを構築できます。
このパイプラインには大きく2通りの方式があります。1つ目はGitの更新をトリガーに対象のkubernetesクラスタへ外部からアクセスし、kubectl applyのコマンド等でクラスタを更新するpush型の方式です。後述するGitOpsと対比して、CIOpsと呼ばれることがあります。2つ目はkubernetesクラスタ内部からGitレポジトリを監視し、更新を検知した場合に適用するpull型の方式です。push型の方式と異なり、kubernetesクラスタへのアクセスポイントを外部に晒すことなくデプロイできます。GitOpsは、Weaveworks社が提唱するプラクティスで、このpull型の方式を採用するデプロイパターンのベストプラクティスです。
それぞれ、実際に試してみましょう。
CI Ops
今回はGitレポジトリとしてGitLabを採用しているので、GitLab CI/CDを利用します。GitLab CI/CDの設定ファイル.gitlab-ci.yml を下記のように記述します。ここでは詳細な説明を省きますが、単純にkubectl applyコマンドを発行していることがわかります。
deploy: image: gcr.io/google.com/cloudsdktool/cloud-sdk stage: deploy script: - kubectl apply -k ./overlays/develop
非常にシンプルですね。また、クラスタに接続するためのkubeconfigファイルをCI/CDの環境変数として渡しています。このファイルは通常 $HOME/.kube/configに配置されており、オプションなくkubectlコマンドを実行した場合にはそのパスが参照されます。今回はCI/CD側で設定したconfigを環境変数で利用するように設定しています。認証情報もこのファイルに含まれているため、取り扱いには十分注意してください。
このとき、CI/CDの実行環境であるGitLab RunnerからKubernetesのAPI Serverまで通信が到達する必要があります。外部からの通信を拒否している、あるいはプライベートアドレスで稼働しているクラスタの場合は、kubernetes内部でGitLab Runnerを動かさなくてはいけません。GitLab特有の設定になるため、ここでは詳細を解説しませんが、使用するCI/CDツールに応じて設定してください。
さて、実行結果は下記のようになります。GitLabのUIでも確認できます。
通常の端末からkubectlを実行した場合と同様ですね。websiteイメージを利用しているwebsite deploymentが更新されていることがわかります。これがgit pushをトリガーに発生するようになります。実際にアプリケーションにアクセスしてみても、ヘッダーの色がblueになって更新されていることがわかります。
GitOps
続いて、GitOpsを採用した場合のパターンを見ていきます。Weaveworks社が出しているガイドラインによると、GitOpsでは下記の4つの原則が満たされている必要があります。
- システムの全体が宣言的に記述されていること
- システムの正しい状態がGitで管理されていること
- システムの変更が承認されたら、自動的に適用されること
- システムが正しく構成されていることを保証し、相違があれば知らせること
先ほど実践したCI Opsでは3つ目の原則までは満たせますが、4つ目の原則を満たしていると言えません。クラスタをkubectlコマンドで直接操作してしまった場合や、誤って過去に流したパイプラインを再度実行してしまった場合には、システムの状態がGit上で管理されている状態と相違してしまいます。この4つ目の原則を満たすために、GitOpsではKubernetesクラスタを監視するソフトウェアが必要となります。
GitOpsを実現するソフトウェアはWeaveworks社が開発したfluxをはじめいくつかありますが、ここでは現在よく使われているArgoCDを利用します。まず、クラスタにArgoCDをインストールしましょう。インストール手順の説明は割愛します。公式ドキュメントのインスントール手順に従ってインストールしてください。
さて、インストールができたら、ArgoCD上にアプリケーションを登録していきます。WebUIから設定を進めていきましょう。
WebUI上のNEW APPボタンをクリックし、下記の情報を入力してCreateボタンをクリックします。
- アプリケーション名
- gitレポジトリとそのパス、ブランチやタグ
- デプロイ先のKubernetesクラスタ
- 同期設定(自動同期/手動同期)
アプリケーションが登録されると、ArgoCDは自動的に同期を行います。各リソースの同期状況をUIで確認できます。
それでは、ここから実際にアプリケーションを更新していきましょう。新たにブランチchapter7を作成し、ArgoCDが参照するブランチを chapter7に変更します。現在、website Podのコンテナイメージは:devとなっています。
これを:blueに変更します。kustomization.yamlを同じように変更してGitLab上でcommitします。コミットログはこちらです。しばらくするとArgoUIのStatusがOut of Syncになり、その後再びSynced状態になります。履歴を確認すると、先ほどのcommitに反応してアプリケーションがアップデートされていることがわかります。イメージタグも:blueになっていますね。
こちらも実際にアプリケーションにアクセスしてみると、ヘッダーの色が青色になって更新されていることがわかります。このようにArgoCDを利用すると、Gitレポジトリ上のマニフェストとアプリケーションの間で常に同期を取ることができます。Git上の変更だけでリリースが完了すれば開発者体験として非常に快適ですし、クラスタの状態が監視されていることはとても安心感があります。
おわりに
今回は、kubernetes上でアプリケーションを更新する際の挙動と、CI/CDパイプラインの適用について紹介しました。GitOpsの導入まで進めば、開発者がyamlファイルを書き換えるだけでアプリケーションのリリースが完了してしまいます。第1回、第2回で触れたKubernetesを利用するメリットを大いに享受できる状態になっていると言えるでしょう。前回までの記事は、本連載の記事一覧から読むことができます。
さて、次回はこちらも実運用する上で欠かせない、ロギングと監視について紹介します。お楽しみに!
連載バックナンバー
Think ITメルマガ会員登録受付中
全文検索エンジンによるおすすめ記事
- kustomizeで復数環境のマニフェストファイルを簡単整理
- kustomizeやSecretを利用してJavaアプリケーションをデプロイする
- KubernetesのWorkloadsリソース(その1)
- Kubernetes上のコンテナをIngressでインターネットに公開するまで
- Kubernetesアプリケーションのモニタリングことはじめ
- StatefulSetとPersistentVolumeを使ってステートフルアプリケーションを動かす
- KubernetesのConfig&Storageリソース(その1)
- Kubernetesアプリケーションのトレーシング
- Kubernetes上のアプリケーション開発を加速させるツール(2) Telepresence
- KubernetesのDiscovery&LBリソース(その1)