Tech Do | メディアドゥの技術ブログ 

株式会社メディアドゥのエンジニアによるブログです。

Kubernetesについてまとめてみた

はじめに

こんにちは!2021年メディアドゥに中途入社した鈴木です!
入社後、SRE部で全社で利用しているパブリッククラウド環境(AWS)のインフラ改善やIaC、パフォーマンスチューニングを担当しておりました。 2023年3月から小説投稿サービス「エブリスタ(*1)」チームに異動し新規開発・運用改善を担当しております。

エブリスタはKubernetesを利用したインフラ構成になっております。
私自身Kubernetesの経験が全くないのですが、今後適切なKubernetesの運用ができるように、まずは基本的な仕組みを理解したいと思い本記事を執筆しました。

はじめに

Kubernetesとは

Kubernetesとはハードウェアを抽象化し、コンテナ化されたアプリケーションのデプロイ、スケーリングなどの管理を自動化するためのプラットフォームです。

Kubernetesを利用することで以下のメリットがあります。

・サービスへの影響を最小限に抑えつつデプロイのローリングアップデートとロールバックすることが可能
・コンテナの異常監視だけでなくアプリケーションが正常にサービス提供しているかまで監視することが可能
・コンテナのメトリクスをトリガーとした柔軟なスケーリングを自動化することが可能
・etc …

Dockerとは

Dockerとはコンテナを実行するための実行環境およびツールキットのことです。 KubernetesはDocker以外のコンテナランタイムにも対応しておりますが、今回はDockerを用いて解説させていただきます。

コンテナを利用することで一般的には以下のような恩恵を受けることができます。

・リソースの効率的な利用
・環境の共通化・統一化
・アプリの起動高速化
・etc…

上記のように様々な恩恵を受けることができるコンテナですが、規模の大きいサービスはコンテナの数が増えるため、運用していく中で以下のような課題が生じます。

・アプリケーションのリリース時に大量のコンテナへどのようにデプロイするか
・大量のコンテナの中からアプリやコンテナの異常をどのように検知/修復するか
・一定の負荷が生じた時にどのようにスケーリングするか

Kubernetesは上記のような課題の解消に役立ちます。

クラスタとPodの作成

実際に挙動を確認できる環境を用意した方がKubernetesへの理解が深まるため、いくつかサーバーを用意して以下構成のクラスタを構築してみました。

クラスタ作成にはクラスタ内にノードを組み込む設定など様々な設定が必要なのですが、今回は割愛させていただきます。 上記の図の通り、Kubernetesには大きく分けて2種類のノードが存在します。

ワーカーノード
実際に動かしたいアプリケーションのコンポーネントであるPodをホストするためのノード

マスターノード
クラスター内のワーカーノードとPodを管理するためのノード
参考 : Kubernetesのコンポーネント

そして、ノード上でPodと呼ばれるKubernetes内で作成・管理できるコンピューティングの最小のデプロイ可能なユニットを動かすことでサービスの提供が可能になります。

Pod
1つまたは複数のコンテナのグループであり、ストレージやネットワークの共有リソースを持ち、コンテナの実行方法に関する仕様を持つ

PodなどのコンポーネントをKubernetesクラスタ上にリソースとして作成する際には、マニフェストと呼ばれるYAML形式(またはJSON形式)のファイルを用います。 マニフェストを利用することでデプロイするコンテナや周辺リソースをコードで管理することができます。

マニフェストを元にPodが作成されるフローは大まかに以下になります。

① リソースを定義したマニフェストをkubectlコマンドでmasterノード上のAPIサーバーに送信
② APIサーバーに送られたマニフェストの情報をetcdと呼ばれるキーバリューストアに保存
③ Schedulerが新規コンテナを動作させるワーカーノードを決定しAPIサーバー経由で決定した情報をetcdに格納
④ kubeletがAPIサーバー経由でetcdから自身のワーカーノードに新規Podの作成がスケジューリングされていることを確認しPodを作成

AWSのEKSやGCPのGKEなどのマネージドサービスを利用しているとマスターノードの挙動を細かく理解しなくてもKubernetesクラスタを簡単に構築できてしまいます。 クラスタを一から作成することでKubernetesの各種コンポーネントの役割やデプロイの仕組みを大まかに把握できるためおすすめです。

アプリケーションのデプロイについて

本記事の冒頭でKubernetesのメリットの1つとしてデプロイ管理の容易さについて軽く触れました。
アプリケーションを稼働させるコンテナを作成するためには、事前にDockerイメージを準備する必要があります。
作成したDockerイメージはDocker HubやGCRなどのレジストリにイメージを保管しておくことができます。

レジストリ上に保管しているコンテナのイメージをマニフェストで指定することでワーカーノード上に指定したコンテナをデプロイすることができます。
※下記のマニフェストはGCRに保管したweb-appというDockerイメージを指定する例

しかし、クラスタ上に作成されたアプリケーションにはクラスタの外からアクセスすることができません。 Kubernetesの特性上スケールアウトやスケールインをした際にPodは作成/削除が繰り返され、PodのIPやDNSドメイン名が頻繁に変更されてしまうためです。

KubernetesにはServiceと呼ばれるクラスター内で1つ以上のPodとして実行されているネットワークアプリケーションを公開してくれるオブジェクトが存在します。 Serviceにはいくつかのタイプが存在しクラスタ内のみ疎通できたり、外部からのアクセスを許可するタイプなど用途によって設定を変更することができます。
参考 : Serviceの公開 (Serviceのタイプ)

Serviceも先ほどのPodと同じくマニフェストで定義することができます。 Serviceを利用することで外部からPod上のアプリケーションにアクセスすることができるようになります。

しかし、上記構成のように単一のPodを作成するだけではKubernetesの恩恵をあまり受けることができません。 サービスの可用性を上げるために複数のPodを用いることが一般的なKubernetesの使い方です。

複数台のPodを管理する際はKubernetesのコントローラーと呼ばれるオブジェクトが役立ちます。 コントローラーはクラスターの状態を監視し、必要に応じて構成を変更してくれるオブジェクトです。 コントローラーにはいくつか種類があるのですが、今回はReplicaSetとDeploymentについて簡単に解説します。

ReplicaSet

ReplicaSetと呼ばれるコントローラーを利用すると実行中のPodを常に監視し、Podの数がマニフェストで指定したレプリカ数と常に一致していることを確認してくれます。 マニフェストで指定したレプリカ数と一致していない場合、Podを新規作成/削除してくれます。 また、ノード障害時には正常に稼働するノードにPodを再作成する機能も存在します。

Deployment

Deploymentは複数のReplicaSetを管理することでローリングアップデートやロールバックを実現するコントローラーになります。 DeploymentはReplicaSetの特性を持ちながら、ローリングアップデートやロールバックといったデプロイ管理ができるコントローラーの一種です。 Kubernetesの公式サイトでもReplicaSetではなくDeploymentを利用するように推奨しております。
参考 : ReplicaSetを使うとき

アプリケーションをアップデートする際に新しいReplicaSetを作成して新旧全体のPod数を調整しながら新しいPodを段階的に作成していきます。 また、ローリングアップデートが終わった後も旧バージョンのReplicaSetは一定数保持されるため、ロールバックも簡単にできます。

Deploymentを利用することで一時的にPod数が増えるため使用するリソース量は増えますが、ダウンタイムなしでアプリケーションをアップデートできるメリットがあります。 また、リリース後に問題が生じても簡単にロールバックができるため、開発や改善業務に集中することができます。

上記以外にも様々な機能や特徴がありますが、今回は割愛させていただきます。
上記で紹介しただけでもKubernetesのReplicaSetとDeploymentを利用することでデプロイ管理が非常に楽になることをご認識いただけたかと思います。

Podの監視について

Kubernetesクラスターはワーカーノード上のkubeletと呼ばれるプロセスによってPod上のメインプロセスが正常に稼働しているかどうかデフォルトで監視してくれます。 しかし、kubeletはメインプロセスの監視のみであり、アプリケーションが意図した挙動をしているかまではチェックしてくれません。
(無限ループやデッドロックなど発生してもメインプロセスは生きているため、異常として検知してくれない)

上記より詳細のKubernetesにはPodの正常性を判断するためのヘルスチェック機能が以下3種類存在します。

Liveness Probe
Pod内のコンテナが正常に動作しているか確認する
失敗時はコンテナを再起動する

Readiness Probe
Podがリクエストを受け付けることができるか確認する
失敗時はトラフィックを流さない

Startup Probe
Podの初回起動が完了したか確認する
失敗時は他のProbeを実行し始めない

上記3種類のヘルスチェックの方式と、失敗と判定される条件は以下になります。

httpGet
・HTTP GETリクエストを実行し、StatusCodeが200~399でなければ失敗
tcpSocket
・TCPセッションが確立できなければ失敗
exec
・コンテナで任意のコマンドを実行し、終了コードが0でなければ失敗

上記ヘルスチェックの方式と間隔などをマニフェストに定義することでPod上で稼働するアプリケーションの特性に合わせて柔軟にヘルスチェックができるようになります。 ヘルスチェック失敗時にはPodの再作成やトラフィックを受信しないようにしてくれるため、サービスの可用性向上に役立ちます。

スケーリングについて

KubernetesにはPodの負荷が高くなった時に、NodeやPodの数を自動的にスケーリングしてくれる機能が存在します。 Metrics Serverをクラスタ内に構築することでコンテナのCPU使用率やメモリ使用率を収集し、特定のメトリクスをトリガーにスケーリングすることができます。
(クラウドサービスプロバイダを利用している場合、クラスタ作成時に自動的にMetrics Serverが導入されることが多い)

Kubernetesが提供するスケーリングの機能は3種類存在します。

・Cluster Auto Scaling
Podの負荷に応じてクラウドプロバイダが提供するAPIを通してNodeを作成/削除する

・Horizontal Pod Autoscaler (HPA)
CPUやメモリ使用率などのメトリクスが設定した閾値を超えた時にPodのレプリカ数を増減する

・Vertical Pod Autoscaler (VPA)
CPUやメモリ使用率などのメトリクスが設定した閾値を超えた時にPodに割り当てられているリソースを自動的に増減する

Kubernetesのスケーリング機能を利用することで負荷に応じてNodeやPodの数やリソースを自動的に調整してくれるため、突発的なサーバー負荷に耐えることができるうえに、コストの最適化に役立ちます。

まとめ

Kubernetesを利用したプロジェクトへアサインされるにあたり、公式ドキュメントを読みながら実際に動かしてみることでKubernetesの仕様を大まかに把握することができました。 Kubernetesは把握しておくべき仕様が多いですが、使いこなせばサービスの安定稼働や開発速度の向上に繋がる強力なツールだと思っております。

今回学んだことを活かすことで現状のエブリスタのKubernetesの構成の問題点を整理し、リソースの最適化によるサービス安定化やコストの削減に努めていきたいと思います。

ここまで長い記事を最後まで読んでくださりありがとうございました。


※参考文献※
青山 真也 (著).Kubernetes完全ガイド 第2版 .インプレス,2020,668p.

book.impress.co.jp