この記事は、「オンプレミス、Kubernetesで、マイクロサービスを意識したWebアプリをデプロイするまでの軌跡」の一部です。
はじめに
DockerをはじめとするコンテナにはImmutable(不変)であるべき、という思想があり、各インスタンスは立ち上がるごとに初期化され、綺麗な状態に戻ります。
ここで一点困るのが、ファイルサーバーやデータベースなど、永続的に保持しておきたい情報をどうしておくか?という事です。
KubernetesにはPersistentVolumeという機能があって、これを使う事でコンテナとは独立したボリュームを作ることが出来ます。
今回は、このPersistentVolumeの使い方を紹介します。
NFSサーバーをセットアップする
PersistentVolumeで使えるボリュームの形態はいくつかあるようなのですが、今回は手軽そうなNFSを使う事にします。
まずは任意のサーバーにNFSをインストールする必要があるので、用意します。専用であれば良いでしょうが、僕は以前の記事で構築したKubernetesのWorkerノードをNFSサーバーとしてセットアップしました。
Ubuntuであれば、以下のコマンドでインストール可能です。
$ sudo apt-get install nfs-kernel-server
共有するフォルダを作成し、権限を設定します。
$ sudo mkdir -p /export/nfs
$ sudo chmod 1777 /export/nfs/.
$ sudo nano /etc/exports
...略
/export/nfs xxx.xxx.xxx.0/24(rw,async,no_root_squash)
$ sudo service nfs-server restart
$ sudo service rpcbind restart
参考サイトにあるように、複数のサービスで同じフォルダを共有する事は難しいようです。
必要に応じて、サブフォルダを作成しておきます。
$ sudo mkdir -p /export/nfs/serviceA
$ sudo mkdir -p /export/nfs/serviceB
今回はテストのため、serviceA
の方に以下のようなファイルを仕込んでおきます。
$ nano /export/nfs/serviceA/index.html
hello world!
NFSマウント準備
クライアント側も準備が必要です。
全てのノードで以下を実行して、NFSが使えるようにしておきます。
sudo apt install nfs-common
PeersistentVolumeを設定する
NFSサーバーは設定できたので、PeersistentVolumeを設定します。
apiVersion: v1
kind: PersistentVolume
metadata:
name: service-a-pv
namespace: some-name-space
annotations:
volume.beta.kubernetes.io/storage-class: "slow"
spec:
capacity:
storage: 5Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
nfs:
server: 192.168.0.xxx <-- 実際のipアドレス
path: /export/nfs/serviceA <-- 共有したいNFSサーバー上のフォルダ
設定の中でname
、namespace
、storage
、accessMode
等、必要であれば適宜変更してください。
設定を元に、PersistentVolumeを作成します。
kubectl apply -f service-a-pv.yaml
これで作成できました。
確認します。
$ kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
service-a-pv 5Gi RWO Retain Available slow 1h
問題がある場合はStatusがAvailableにならないので、設定を見直してください。
PersistentVolumeClaimを設定する
間違い探しみたいですが、PersistentVolume Claim です。
この設定で、PersistentVolumeを実際のアプリに紐づけます。
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: service-a-pvc
namespace: some-name-space
labels:
app: service-a <-- ボリュームを紐づけるデプロイメント
annotations:
"volume.beta.kubernetes.io/storage-class": "slow"
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
これで作成する準備は出来ましたが、今回はnamespaceを指定しているので、namespaceが無い状態ではエラーが出ます。
$ kubectl create namespace some-name-space
namespaceを作成したら、pvcを作成しましょう。
$ kubectl apply -f service-a-pvc.yaml
改めて状態を確認します。
$ kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
service-a-pv 5Gi RWO Retain Bound service-a-pvc slow 32h
service-a-pvcにバインドされていますね。
PersistentVolumeClaimの状態も確認します。
$ kubectl get pvc -n some-name-space
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
service-a-pvc Bound service-a-pv 5Gi RWO slow 32h
これで、ボリュームの準備は完了しました。
一点注意ですが、今回の設定ではnamespaceを分けているので単にkubectl get pvc
ではNo resources found.
となってしまいます。オプションでnamespaceを指定してください。
コンテナに紐づける
いよいよ、実際のコンテナに紐づけていきます。
以下のようなyamlファイルで、サービスとデプロイメントを定義します。
apiVersion: v1
kind: Service
metadata:
name: service-a
namespace: some-name-space
labels:
app: service-a
spec:
type: NodePort
ports:
- port: 80
targetPort: 80
protocol: TCP
selector:
app: service-a
---
apiVersion: apps/v1beta1
kind: Deployment
metadata:
name: service-a
namespace: some-name-space
spec:
replicas: 1
revisionHistoryLimit: 3
template:
metadata:
namespace: some-name-space
labels:
app: service-a
version: latest
spec:
containers:
- name: service-a
image: nginx:latest
ports:
- containerPort: 80
volumeMounts:
- name: service-a-storage <-- ※ 同じ名前を指定する
mountPath: /usr/share/nginx/html
volumes:
app: service-a
- name: service-a-storage <-- ※ 同じ名前を指定する
persistentVolumeClaim:
claimName: service-a-pvc
この例ではnginxのコンテナを起動して、デフォルトの公開ディレクトリである/usr/share/nginx/html
に先ほど作ったボリュームをマウントしています。
これにより、サービスにアクセスした際に先ほど作ったindex.html
が見えるようになります。
$ kubectl apply -f service-a.yaml
デプロイ後、このサービスが起動しているポートを調べます。
$ kubectl get services -n some-name-space
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service-a NodePort 10.107.212.223 <none> 80:30380/TCP 71m
この例では30380で起動しているようなので、以下のコマンドで内容を確認できます。
$ curl <サーバーのip>:30380
hello world!
無事に確認できれば、全てうまく設定できています。
お疲れさまでした!
注意
今回はデモのためにindex.html
をPersistentVolumeの中にいれてnginxで参照しましたが、この使い方では、ポッドをたくさん作ってスケールしても結局単一のファイルを見に行くので、Kubernetesの恩恵もあまり受けられないと思われます。検証等はしていないですが、ご注意ください。
参考
「Docker」を全く知らない人のために「Docker」の魅力を伝えるための「Docker」入門
KubernetesでNFSを利用してPersistentStorageを準備する