MENU

"コンテナ"の世界を知る! 【Vol.02】IBM Cloudのマネージド型Kubernetesサービス環境での
設計〜アプリケーションデプロイまでのポイントを解説!

2020/11/25

前回ご紹介したコラムで、Knowledge Discoveryのコンテナ化まで完了したので、今回は作成したコンテナイメージを動作させるIBM Cloudのマネージド型Kubernetesサービス「IBM Cloud Kubernetes Service(以下、IKS)」の設計・設定、そしてアプリケーションのデプロイについて紹介します。

Kubernetes環境でアプリケーションを動かすために必要な作業とは?

今回稼働環境として採用したIKSは、IBM Watsonやブロックチェーンなどと連携するアプリケーションを短時間で配信するためのマネージド・コンテナ・サービスです。Knowledge DiscoveryはIBM Watsonを利用したアプリケーションであることから、運用面・コスト面も考慮し、IKSを採用しています。

ここでは、① IKS環境の準備/設計、② IKS環境の設定/アプリケーションのデプロイの2つに分けて解説します。

① IKS環境の準備/設計

まずは、IKS環境の準備と設計した内容の一部についてご紹介します。

・Kubernetes ワーカーノード(Kubernetesノード)のデプロイ
IKSでは、マスターノード(Kubernetes Master)はIBM Cloud側で構築・管理され、ユーザーはワーカーノードのスペックや数量を決めてオーダーすることで、Kubernetes環境を利用することができます。マスターノードはワーカーノードやその上で動くコンテナアプリケーションの司令塔となる重要な役割であり、マスターノード自身が高可用性であることが望まれます。可用性を保ちつつマスターノードを運用・保守するのは敷居が高いですが、この辺を意識せずKubernetes環境を使用できるのは「マネージドサービス」の大きなメリットです。

・ネットワーク設計(アクセス制御/ルーティング/負荷分散など)
サービスを公開するにあたり、インターネットからのアクセス制御やコンテナ間の通信制御といった通信要件の整理や、複数のコンテナに対する負荷分散といった「ネットワーク設計」を行いました。

Kubernetesのネットワークは仕様の理解が非常に難しく、目的のネットワーク要件を達成するために調査や検証に多くの時間を費やしました。


・Kubernetesとコンテナレジストリの連携(アクセストークンの設定)
Kubernetesへのアプリケーションのデプロイとは、すなわちコンテナのデプロイのことです。そして、コンテナは「コンテナレジストリ」からデプロイするのが一般的です。一般公開したくないプライベートなコンテナを管理するために、今回はIBM Cloud Container Registryを使用しました。IBM Cloud Container RegistryはOAuthによる認証が必要となるため、アクセストークンを予めService Accountに登録しておく必要があります。


・ログ設計
コンテナは作成や削除が頻繁に発生します。コンテナ障害によるコンテナの作り直しが発生した場合、コンテナ自体が削除されるためコンテナ内のログファイルにアクセスできなくなってしまいます。ログの欠損を回避する、あるいは、監査目的でログの保管が必要な場合など、コンテナで出力されるログを永続的に保管する仕組みを検討する必要があります。

そこで推奨されているのが、標準出力/標準エラー(stdout/stderr)への出力です。標準出力/標準エラーに出力されたログはコンテナの外(ノードのログ領域)に一時的に保存することができます。ただ、この場合もコンテナが削除された場合には、同じタイミングでログが削除されてしまいます。また、ノード上に一時保管されたログはローテーションされないためコンテナの作り直しが発生するまでデータ量が増え続けることになります。

本構成では、「Fluent Bit」を使いノード上に一時保管されたログデータを「Grafana Loki」に転送しています。ログ転送に「Fluentd」、ログ収集に「Elasticsearch」という組み合わせが一般的ですが、より軽量に動作するという理由から、Fluent Bit + Grafana Lokiの組み合わせを採用しました。また、Fluent Bitはログのアーカイブにも利用しており、アーカイブ先として安価に利用できるIBM Cloud Object Storage(ICOS)を選定しました。

・IBM Cloud Object Storage (ICOS)
前述の通り、ログのアーカイブ先としてICOSを利用しましたので、ICOSへのログの保管期間(ライフサイクル)の設定や認証設定、アクセス制御などを行いました。


・外部ストレージ
Knowledge Discoveryのアプリケーションは、Couch DBとPostgreSQLの2つのデータベースを利用しています。コンテナの削除とともにデータが消えてしまうことは許されず、永続的な外部のストレージにデータベースファイルを持つ必要があります。一般的に、データベースをKubernetesで動かすことは、ステートフルなものになるため推奨されていませんが、今回はデータベースのコンテナ化とその運用にも挑戦してみました。

本構成では、各データベース用の永続ボリュームにIBM CloudのEndurance Storageを利用しました。DBのデータをクラスターの外部(永続ボリューム)に持たせることで、コンテナの削除、再作成を行っても、ストレージに再接続させることで、データの永続性を保つことが可能となります。

② IKS環境の設定/アプリケーションのデプロイ

Kubernetesの操作やアプリケーションのデプロイは、「リソース」をマスターノードに登録することで行います。「リソース」には、コンテナの実行に関することから、ネットワーク、セキュリティなど多数の種類があります。これらの「リソース」から利用するものをマニフェストファイルと呼ばれるファイルに定義し、CLIツールの「kubectl」を用いてマスターノードに登録します。
Knowledge DiscoveryのKubernetesへの移行で、マニフェストファイルに定義したリソースの一部をご紹介します。


(1)Workloads:コンテナの実行に関するリソースを定義

・Deployment/ReplicaSet
各アプリケーションをKubernetes環境にデプロイするため、どのNamespaceで何個Podを起動させるかなどの定義を行います。
Namespaceとは、Kubernetesクラスタ上で、仮想的なクラスタを指すものです。今回は、マルチテナント化にあたり、Namespace単位で顧客分離を行っています。Podとは、Kubernetesで扱われる最小単位であり、1つ以上のコンテナからなるグループです。Pod内のコンテナはIPアドレスとデータ領域を共有します。今回は、基本的に1つのPodに1つのコンテナの構成としています。

なお、Deploymentはアプリケーションのアップデートなどの際、ローリングアップデートやロールバックにも利用されます。


・ DaemonSet
各ノードにPodを1つずつ配置するためのもので、全てのノードで必ず動作させたいプロセスに対して利用します。アプリケーションのPodはどのノード上で稼働するかわかりません。各ノード上にあるログの管理や監視が行えるようにするために、Fluent Bitなどの運用監視系のPodに利用しています。

・StatefulSet
データベースなどステートフルなワークロードに対応するための定義です。Couch DBやPostgreSQLはStatefulSetを利用するのが一般的ですが、本検証ではDeploymentを利用しています。StatefulSetはデータ保護の観点からNode障害時に自動復旧がされないという仕様のため、今回はできる限りメンテナンスフリーな構成にするためにNode障害時にも自動復旧されるDeploymentを選択しました。

・CronJob
CronJobとは、Linux OSでいうcrontabのように、指定されたスケジュールで定期的にPodを起動する仕組みです。今回は、ログローテート、アーカイブ処理がKubernetesで標準実装されていないため、CronJobとして独自に用意しました。また、データベースの日次のバックアップもCronJobにて独自に実装しています。


(2)Discovery & LB:コンテナの外部公開やロードバランサーなどに関するリソースを定義

・Service
Serviceとは、インターネットやKubernetesクラスタ外部からPodへの通信やPod間での通信、クラスタ内のDNSによる名前解決、L4ロードバランサー機能などを管理するリソースです。ここでは、各Podに対して、ロードバランシング設定やポートの割り当て設定等を行います。PostgreSQLのようにHTTPサービス監視が難しいものは、このL4ロードバランサー機能を用いて監視を実施します。

・Ingress
Ingressは、上記のServiceで定義されたポリシーに対して、L7ロードバランサーの機能として負荷分散、SSL終端、名前ベースの仮想ホスティングの機能を提供します。


(3) Config & Storage:コンテナに対する設定ファイルや永続ボリュームなどに関するリソースを定義

・ConfigMap
前回紹介したコンテナイメージの作成にあたり、汎用化のため、コンテナイメージ内に含めなかった環境変数や各種設定値をConfigMapに定義します。これは、アプリケーションをKubernetes上にデプロイする際に読み込まれます。

・PV/PVC
PV(PersistentVolume)には、永続ストレージとして領域を確保するための設定を記載します。Kubernetesクラスタに永続ストレージを紐づける処理となります。
PVC(PersistentVolumeClaim)には、PVでKubernetesクラスタに紐づけされた領域を、Podに紐づけるための設定を記載します。

今回は、データベース用の永続ストレージとして、IBM Cloud Endurance Storageから領域を確保するためのPV、そして確保されたPVをデータベース用のPodに紐づけるPVCをそれぞれ作成しています。


(4)Cluster:セキュリティ関連のリソースを定義

・ServiceAccount(Pod単位の認証設定)
ServiceAccountはNamespaceに紐づくリソースで、どのようなアクションを認証・認可するのかを定義するのに利用します。本検証では、コンテナイメージをIBM Cloud Container Registry からKubernetesクラスタ内へPullするために利用しました。

・ClusterRole(権限設定)
上記のServiceAccountが「認証情報の設定」とすると、ClusterRoleは「認可の設定」となります。NamespaceやPod、PVなどのリソースに対しての削除や変更といった操作権限を定義します。


その他、PodごとのCPUやメモリの使用制限を行う定義など、さまざまなリソースの設定があります。

アプリケーションの起動順には “依存関係”がある?!起動失敗からその解決方法を学ぶ

同時に複数のアプリケーションをデプロイした際に、一部のアプリケーション(以降、App1とApp2とします)が正常に動作しないという問題が発生しました。

調査を進めたところ、App1とApp2のアプリケーションは起動の際、PostgreSQLと接続可能であることが条件となっていました。Kubernetesでは、コンテナ(Pod)の起動順序を制御するための仕組みが実装されていないため、どの順序で各コンテナが起動するか分かりませんし、運用中にコンテナが削除、再作成などされる場合もあります。

そこで、以下の仕組みを実装することで起動順序を制御し、問題を解決しました。

【手順】

  • 1. App1とApp2からPostgreSQLの起動状態を監視(死活監視)

  • 2. App1/2がPostgreSQLのダウン状態を検知したら、自分自身を終了(kill)する。

  • 3. App1とApp2はPostgreSQLに接続可能となるまで待機(死活監視を継続)

  • 4. PostgreSQLが復旧

  • 5. App1とApp2はPostgreSQLの復旧(起動)を確認して、自分自身を起動

上記処理として、App1のマニフェストファイルに下記の処理を記載(一部抜粋)

まとめ

  • Kubernetesへの移行で感じたメリット
    • インフラ設定のコード化(IaC)が可能となり、Gitなどと組み合わせることでインフラ構成管理やバージョン管理が容易に
    • アプリケーションのデプロイ手順が容易となり、属人化せずに、素早く安全にデプロイが可能に
    • KubernetesのNamespaceによる分離やネットワークレベルでの分離などを組み合わせることでマルチテナント化を実現
Kubernetesそのものはもちろん、プラグインなど周辺ソフトウェアを含めた仕様の把握が大変で、つまずくことも…。

特に苦労した点は以下の通りです。
・マニフェストファイルによる設定方法
・インターネットとの接続やアクセス制御の方法
・外部ストレージの管理(再利用や削除)の実装方法
・Kubernetesの権限やセキュリティ周りの仕様

巷で言われている通り、かなりの学習コストが必要であると実感しました。

次回は、Kubernetes環境の監視環境について紹介します。