Kubernetes(k8s)を学ぼう2! ~Volume編~

こんにちは。KOUKIです。

とある企業でWeb系のシステム開発業務に従事しています。

今回は、前回学習したKubernetesの学習の続きを記事にしています。今回は、Volumeについて触れていきたいと思います。

ワークスペースの準備

アプリケーションの準備

検証で使うアプリケーションは、Go言語で実装します。

簡単に説明すると、GET/POSTリクエストの受付を可能にしたEchoサーバーを作成しています。このEchoサーバーは/storyパスからのリクエストを裁くことが可能であり、GETリクエストの場合はローカルに保存されたデータを返却し、POSTリクエストの場合はローカルに新しいデータを追加保存します。

Dockerの準備

Dockerfileとdocker-compose.ymlファイルを用意しましょう。

volumesを作成することで、データの永続化を可能にしています。

アプリケーションの挙動

以下のコマンドにて、アプリケーションを起動しましょう。

アプリケーションが立ち上がったら「http://localhost/story」に対してGETリクエストを送ってみましょう。筆者は、ChromeのTalend API Testerを使ってテストしてます。

リクエストが成功(200)して、空文字が返却されました。

続いて、「http://localhost/story」に対して、POSTリクエストでJSON文字列を送ってみましょう。

OKですね。もう一度、GETリクエストを送信します。

今度は、返り値にHello Worldが表示されています。

書き込んだデータは、ローカルPCに永続保存できるようにしたので、コンテナが削除されても消えないはずです。確かめてみましょう。

先ほどと同じように「http://localhost/story」に対してGETリクエストを送ってみましょう。

以下のように「Hello World」が表示されたら成功です。

Kubernetes(k8s)環境構築

DeploymentとServiceを作成

先ほど作成したアプリケーションを元にDeploymentとServiceを作成しましょう。

Docker Hub上でレポジトリ作成

次に、先ほど作成したコンテナイメージをDocker HubにPushします。

そのためには、最初にDocker Hub上で新しいレポジトリを作成しましょう。

筆者の場合は「kub-data-demo」レポジトリを作成しました。これはdeployment.ymlに指定した「image: アカウント名/kub-data-demo」と同じものです。※アカウントには、ご自身のアカウント名を入れてください

Docker HubにイメージをPush

レポジトリ作成が完了したら下記のコマンドで、Docker HubにイメージをPushします。

デプロイ

色々事前準備が多くて大変でしたが、いよいよDocker HubにPushしたイメージをminikube(k8s クラスタ)上にデプロイします。

「minikube service story-service」コマンドを実行するとアプリケーションが起動します。※Podを作るまで多少時間がかかるかもしれません

アプリケーションの挙動

筆者の環境では、「http://192.168.99.100:30800/」にてアプリケーションが立ち上がったので、Dockerコンテナ時に確認した時と同じ方法で、アプリケーションが動作するか確認します。

http://192.168.99.100:30800/storyにGETでアクセス
http://192.168.99.100:30800/storyにPOSTでアクセス
http://192.168.99.100:30800/storyにGETでアクセス

アプリケーションは、問題なく動いていますね。しかし、これには一つ欠点があります。

以下のコマンドで、deploymentとserviceを一度削除してから再度立ち上げてください。

アプリケーションが立ち上がったら、「http://192.168.99.100:32170/story」にGETリクエストを送信します。

すると「story: “”」がかえってきました。データが永続化されていないのです

Kubernetes Volumes

docker コンテナの時は、docker-compose.ymlにvolumesを指定してデータを永続化しました。

k8sにもvolumesの概念があります。

今回もvolumeを使ってデータの永続化をしましょう。

errorエンドポイントを追加

データの永続化の前にやることがあります。

現状、Podを削除しないと動作(データの存在有無)確認ができないので、アプリケーションにerrorエンドポイントを追加して、GetリクエストでPodを破壊できるようにしましょう。

破壊しても大丈夫なの?と思われるかもしれませんが、k8sが自動で再作成されるのでOKです。

ファイルを修正したのでイメージを作り直して、Docker HubにPushする必要があります。

次にdeployment.ymlのimageの指定を変更します。

最後に以下のコマンドでアプリケーションを更新します。※既存のPodは削除しなくて大丈夫です。更新してくれます。

アプリが立ち上がったら「/error」にアクセスして、Podが破壊されるか確認しましょう。

OKですね。しばらくすると自動修復されます。

emptyDir Volume

k8sのvolumesには色々なTypeがありますが、最初にemptyDirを試してみましょう。

このvolumeは、WorkerNodeにPodが割り当てられた時に最初に作成されます。

emptyDirは空volumeを作成するので、/storyにアクセスした時は、「”Failed to open file”」が表示されますが、データを格納すると回避できるので問題ありません。

データを格納する
再表示

それでは、/errorにアクセスしてPodを破壊後、データが永続化されるか確認しましょう。

/errorにアクセスする

podが立ち上がったら/storyにアクセスします。

OKですね。よかった!

emptyDirは、Pod内に指定したマウント先に空のvolumeを作成します。その際に、マウント先に格納されたファイル類は削除されるようです。

このアプリケーションの例で言えば、storyディレクトリ配下のtext.txtは一旦削除されます。

しかし、emptyDirは、Pod数を増やすと期待した動きをしてくれません。例えばPodを2つにしてみましょう。

コンテナが立ち上がったら/errorにアクセスします。この操作でもともと立ち上がっていたPodがエラー落ちします。

このあと、再度/storyにアクセスします。

はい、「Failed to open file」と表示されました。

この動作で2つのことがわかります。

1つは、replicasで増やしたPodに対してリクエストが飛んだこと。
もう一つは、EmptyDirはPod毎に作られる->新しく立ち上げたPodにはtext.txtファイルが無いということです。

k8sのメリットの一つはオートスケールができることなので、EmptyDirの使い所には注意が必要ですね。

hostPath Volume

hostPath Volumeを試してみましょう。

このドライバーは、ホストマシン上のディレクトリパスをPod内にセットすることができるようです。ちょうどdocker-compose.ymlのvolumesみたいなものですね。

ホストマシン上のパスをPodと共有するので、Podが増えたとしてもデータを共有することができるはずです。

deployment.ymlのvolumesを書き換えましょう。

emptyDirからhostPathに変更しました。pathには/dataを指定しており、podの中でdataフォルダが生成されます。

そして、dataフォルダに「/app/story 」がマウントされるはずです。

アプリが立ち上がったら、先ほどと同様にPOSTリクエストでデータ投入->/errorでアプリケーションの遮断->GETリクエストでデータ表示をしてみてください。データが表示できるはずです。

データの永続化

次は、データの永続化について検証していきましょう。

先程までの設定は、PodとNodeがVolumeに依存している状態でしたが、今度は、それらの依存関係を切り離すようにVolumeを設定したいと思います。

まずは、永続Volumeを設定するためのファイルを用意します。

PersistentVolume

host-pv.ymlには、PersistentVolumeを実装します。

PersistentVolume (PV)はストレージクラスを使って管理者もしくは動的にプロビジョニングされるクラスターのストレージの一部です。これはNodeと同じようにクラスターリソースの一部です。PVはVolumeのようなボリュームプラグインですが、PVを使う個別のPodとは独立したライフサイクルを持っています。このAPIオブジェクトはNFS、iSCSIやクラウドプロバイダー固有のストレージシステムの実装の詳細を捕捉します。

公式サイトより

PersistentVolumeClaim

さらにClaimの設定を行います。

このファイルには、PersistentVolumeClaimの設定を書きます。

PersistentVolumeClaim (PVC)はユーザーによって要求されるストレージです。これはPodと似ています。PodはNodeリソースを消費し、PVCはPVリソースを消費します。Podは特定レベルのCPUとメモリーリソースを要求することができます。クレームは特定のサイズやアクセスモード(例えば、ReadWriteOnce、ReadOnlyMany、ReadWriteManyにマウントできます。詳しくはアクセスモードを参照してください)を要求することができます。

公式サイトより

プロビジョニング

先ほどのVolume設定をdeployment.ymlに反映します。

volumesの設定に、pvcを指定しました。

設定の反映

先ほど設定したVolumeをapplyコマンドで反映しましょう。

普通に起動すれば、OKです。

環境変数を利用する

次は、環境変数を利用してみましょう。

main.goのファイル読み込み処理で、環境変数からファイルパスを読み込みます。

STORY_FOLDERは、deployment.ymlに設定する環境変数名です。

続いて、イメージを更新し、DockerHubにPush後、deploymentをapplyします。

「Hello, World]」が表示されているので、、環境変数名からパスを読み込めたようですね。

ConfigMap

最後に、ConfigMapを学んで終わりにしましょう。

ConfigMapは環境変数などをKey Valueペアとして保存するリソースです。設定は、yamlファイルに書きます。

このファイルをapplyします。

ConfigMapの設定をdeployment.ymlに追加します。

deployment.ymlファイルをapplyします。

OKですね。ConfigMapは結構使えそうですね。

おわりに

長くなりましたが、volume編は以上です。

ローカルでアプリ開発ならDockerだけで十分ですが、運用面も踏まえてポートフォリをを作りたいという人は、k8sを検討するのも良いと思います。

ガンガン使いこなしていきましょう!

記事まとめ