[ 言語選択: English ]

1. PVとPVC

Persistent Volume(PV)はHDD/SSDのように永続化された記憶領域を提供するK8sの機能です。

コンテナべースの仮想化環境ではプロセスが終了すると、それまで内部に保存されていた内容が破棄され、初期化されます。

これはウィルスなどにより外部から汚染された場合でも、再起動によりリセットすれば汚染前の状態の戻るため、健全性を確保できる点で優れています。 しかし、データベースサーバーのように、データを保存することを目的とした用途では、この特徴は困ることになります。

このような仮想化環境では、永続化された(電源を入れ直しても消えない)記憶領域が何かしらの形で準備されていて、KubernetesではPVと呼ばれます。

Kubernetesでは、PVはシステムが準備します。 ユーザーは準備されたPVを利用し、PVC (PV Claim)を定義することで、システム(k8s)に永続化領域を要求し、その割り当てられた領域に必要なデータを保存します。

このSCCPで利用するk8sクラスターでは、大きく2つの方法(Rook/CephとNFS)でPVを準備しています。 現時点ではNFSは古いので、Rook/CephのBlockStorageとFileStorageを利用する例を紹介します。

1.1. BlockStorageを利用したPVC定義 (専有タイプ)

storageClassName: rook-ceph-block を利用することで、任意の容量のPVCを定義することができます。

サンプルの定義ファイルは次のようなものです。

---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: nginx-block-pvc
spec:
  accessModes:
    - ReadWriteOnce
  volumeMode: Filesystem
  resources:
    requests:
      storage: 1Gi
  storageClassName: rook-ceph-block
  • name: には、任意の分かりやすい名前を指定します。

  • storage: には、任意の必要なサイズを指定します。

  • Rookを利用する場合、accessModes:には"ReadWriteOnce"だけが指定できます。

  • Rookを利用する場合、一つのPodからのみアクセスできるためDatabase Serverなどに向いています。

1.2. FileStorageを利用したPVC定義 (共有タイプ)

BlockStorageは1つのPodだけがアクセスできるプライベート領域ですが、FileStorageは複数のPodから同時にアクセスできる共有領域です。 以前はnamespace毎にFileStorageを作成することができませんでしたが、Rook/Cephのバージョンが上がり安定して利用できるようになりました。

---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: nginx-file-pvc
spec:
  accessModes: [ "ReadWriteMany" ]
  storageClassName: rook-cephfs
  resources:
    requests:
      storage: 1Gi
  • name: には、BlockStorageと同様に任意の分かりやすい名前を指定します。

  • storage: には、BlockStorageと同様に任意の必要なサイズを指定します。

  • accessModes:には、"ReadWriteOnce"も指定できますが、通常は "ReadWriteMany" を指定するために、FileStorageを利用しますので、このままです。

  • 複数のPod(プロセス、サーバー)からアクセスできるので、単一コンテンツを共有したいWebサーバーでの利用に向いています。

2. BlockStorageを利用するWebサーバー

BlockStorageを利用する、Webサーバー(nginx-ceph-block)を準備します。

まず、下記の2つのYAMLファイルをダウンロードしたり、コピー&ペーストするなどして、準備してください。

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-block-storage
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx-block-storage
  template:
    metadata:
      labels:
       app: nginx-block-storage
    spec:
      containers:
      - name: nginx-block-storage
        image: nginx:latest
        imagePullPolicy: "Always"
        ports:
        - containerPort: 80
        volumeMounts:
        - name: nginx-data-storage
          mountPath: /usr/share/nginx/html
      volumes:
      - name: nginx-data-storage
        persistentVolumeClaim:
          claimName: nginx-block-pvc

このPodにアクセスするには、別途Service定義が必要です。

---
apiVersion: v1
kind: Service
metadata:
  name: nginx-block-storage
spec:
  type: LoadBalancer
  ports:
     -  port: 80
        protocol: TCP
        targetPort: 80
  selector:
    app: nginx-block-storage

最初のBlockStorageの定義ファイルと、この2つのファイルをダウンロードして実行するための手順は次のとおりです。

## あらかじめファイルをダウンロードしている場合は、この3行はスキップ
$ wget https://web-int.u-aizu.ac.jp/~yasu-abe/ja/sccp/manual/volumes.nginx-block-pvc.yaml.txt
$ wget https://web-int.u-aizu.ac.jp/~yasu-abe/ja/sccp/manual/volumes.deploy-nginx-blockstorage.yaml.txt
$ wget https://web-int.u-aizu.ac.jp/~yasu-abe/ja/sccp/manual/volumes.svc-nginx-blockstorage.yaml.txt

## ダウンロードファイルしたファイルをkubectlコマンドで実行する
$ kubectl -n $(id -un) apply -f volumes.nginx-block-pvc.yaml.txt
$ kubectl -n $(id -un) apply -f volumes.deploy-nginx-blockstorage.yaml.txt
$ kubectl -n $(id -un) apply -f volumes.svc-nginx-blockstorage.yaml.txt

ここまで実行したら、Webブラウザで構成したWebサーバーに接続します。

## Webブラウザを開き、作成したWebサーバーに接続する
$ browse http://$(kubectl -n $(id -un) get svc nginx-block-storage -o=jsonpath='{.status.loadBalancer.ingress[0].ip}')

まだコンテンツ(index.html)ファイルが配置されていないので、下記のコマンドでファイルを配置します。

$ kubectl -n $(id -un) exec -it "$(kubectl -n $(id -un) get pod -l app=nginx-block-storage -o jsonpath={.items[0].metadata.name})" -- bash -c "echo Hello World, $(id -un) at $(date) > /usr/share/nginx/html/index.html"

またWebブラウザに戻って、ページが表示されることを確認してください。

3. FileStorageを利用するWebサーバー

次に、FileStorageを利用するWebサーバーを準備します。

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-file-storage
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nginx-file-storage
  template:
    metadata:
      labels:
       app: nginx-file-storage
    spec:
      containers:
      - name: nginx-file-storage
        image: nginx:latest
        imagePullPolicy: "Always"
        ports:
        - containerPort: 80
        volumeMounts:
        - name: nginx-data-storage
          mountPath: /usr/share/nginx/html
      volumes:
      - name: nginx-data-storage
        persistentVolumeClaim:
          claimName: nginx-file-pvc

次にPodにアクセスするためのServiceを定義します。

---
apiVersion: v1
kind: Service
metadata:
  name: nginx-file-storage
spec:
  type: LoadBalancer
  ports:
     -  port: 80
        protocol: TCP
        targetPort: 80
  selector:
    app: nginx-file-storage

BlockStorageの例と同様に、YAMLファイルをダウンロードして実行するための手順は次のとおりです。

## あらかじめファイルをダウンロードしている場合は、この3行はスキップ
$ wget https://web-int.u-aizu.ac.jp/~yasu-abe/ja/sccp/manual/volumes.nginx-file-pvc.yaml.txt
$ wget https://web-int.u-aizu.ac.jp/~yasu-abe/ja/sccp/manual/volumes.deploy-nginx-filestorage.yaml.txt
$ wget https://web-int.u-aizu.ac.jp/~yasu-abe/ja/sccp/manual/volumes.svc-nginx-filestorage.yaml.txt

## ダウンロードファイルしたファイルをkubectlコマンドで実行する
$ kubectl -n $(id -un) apply -f volumes.nginx-file-pvc.yaml.txt
$ kubectl -n $(id -un) apply -f volumes.deploy-nginx-filestorage.yaml.txt
$ kubectl -n $(id -un) apply -f volumes.svc-nginx-filestorage.yaml.txt

ここまで実行したら、Webブラウザで構成したWebサーバーに接続します。

$ browse http://$(kubectl -n $(id -un) get svc nginx-file-storage -o=jsonpath='{.status.loadBalancer.ingress[0].ip}')

まだコンテンツ(index.html)ファイルが配置されていないので、下記のコマンドでファイルを配置します。

$ kubectl -n $(id -un) exec -it "$(kubectl -n $(id -un) get pod -l app=nginx-file-storage -o jsonpath={.items[0].metadata.name})" -- bash -c "echo Hello World, $(id -un) at $(date) > /usr/share/nginx/html/index.html"

またWebブラウザに戻って、ページが表示されることを確認してください。

4. それぞれのWebサーバーの特徴

ここでは2種類のWebサーバー(nginx)を構成しました。

それぞれの設定ファイルは、"block"と"file" 違いはありますが、ほぼ同じです。 ここではその微妙な違いについて説明します。

4.1. 【復習】Webページの開き方

## Block Storage を利用するWebサーバーへの接続
$ browse http://$(kubectl -n $(id -un) get svc nginx-block-storage -o=jsonpath='{.status.loadBalancer.ingress[0].ip}')

## File Storage を利用するWebサーバーへの接続
$ browse http://$(kubectl -n $(id -un) get svc nginx-file-storage -o=jsonpath='{.status.loadBalancer.ingress[0].ip}')

4.2. replicas: 設定の違い

よくみると、2つのDeployment YAMLファイル(deploy-nginx-blockstorage.yaml.txt, deploy-nginx-filestorage.yaml.txt) の中に、replicas: と書かれている行があります。(.spec.replicas)

これは同時に動作する nginx の Pod(プロセス) の数を指定するものです。複数動作した方が、より多くのアクセスに耐えられるので好ましいですが、BlockStorageの 1 を複数にしてもうまく動きません。

この動きを確認するため、replicasの数を 1 から 2 に変更してみます。

次のコマンドを実行すると、emacsが立ち上がりますので、replicas:の数字を変更します。

$ env EDITOR=emacs kubectl -n $(id -un) edit deploy nginx-block-storage
## 編集が終ったらemacsを終了する

この変更をしても特に動作に変化はありません。

現在の状況を get コマンドから確認してみます。

$ kubectl -n $(id -un) get pod

すると、次のように表示されます。

NAME                                   READY   STATUS              RESTARTS   AGE
nginx-block-storage-5f47458575-g7wvm   1/1     Running             0          84s
nginx-block-storage-5f47458575-nkbg4   0/1     ContainerCreating   0          7s
nginx-file-storage-7976586c4b-4p4xj    1/1     Running             0          26m
nginx-file-storage-7976586c4b-lbnw9    1/1     Running             0          26m

上から3行目のSTATUSが ContainerCreating となっているPodは永遠に動作しません。

こん現任は describe コマンドで分かります。

$ kubectl -n $(id -un) describe pod -l app=nginx-block-storage

すると、次のようなメッセージが表示され、最後が次のようになるはずです。

 Warning  FailedMount         15s                   kubelet                  Unable to attach or mount volumes:
     unmounted volumes=[nginx-data-storage], unattached volumes=[kube-api-access-kq479 nginx-data-storage]:
     timed out waiting for the condition

これは、nginx-data-storageで要求した記憶領域(ファイルシステム、PVC)をマウントしようとして失敗している様子を表しています。

BlockStorageの特徴は、1つのPodからしか利用できない専有タイプであるためです。

FileStorageの特徴は、複数のPodと共有できることです。 1回index.htmlを変更した後に、何回Webブラウザからアクセスしても同じ内容が確認できるのは、複数のWebサーバーから同じファイルを参照していることを示しています。

課題: 余裕があれば、deploy/nginx-file-storage の replica: 数を2より増やしてみてください。

5. 現在定義されているPVCを確認する

kubectl の get コマンドを応用すれば、現在自分が利用しているPVCの状態を確認することもできます。

$ kubectl -n $(id -un) get pvc

表示される内容は各自の状態によって変化します。下記は1つの出力例です。

NAME              STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS      AGE
nginx-block-pvc   Bound    pvc-b37bf308-310b-4679-90c6-174bcc0dcf6e   1Gi        RWO            rook-ceph-block   77m
nginx-file-pvc    Bound    pvc-c2ad02e3-ca2d-4c88-98b5-3999371f804d   1Gi        RWX            rook-cephfs       44m

6. 応用問題

6.1. Q1. 【任意の名前のBlock Storageを作成】

Block Storage の YAMLファイル(volumes.nginx-block-pvc.yaml.txtダウンロード ) をダウンロードし、エディタなどで name: を変更して、異なる名称のPVCを作成してください。

$ kubectl -n $(id -un) get pvc で前後の変化を確認すること。

A1.解答例

## 現在の状態を確認

$ kubectl -n $(id -un) get pvc

## 存在しないファイル名 (ここでは、new-block-pvc.yaml) でファイルを作成

## name を新しい名前にエディタなどで変更する。ここではsedコマンドを利用する

$ sed -i -e 's/name: .*$/name: new-block-pvc/g' new-block-pvc.yaml

## new-block-pvc.yaml の内容を反映させる

$ kubectl -n $(id -un) apply -f new-block-pvc.yaml

## 変更後の状態を確認

$ kubectl -n $(id -un) get pvc

6.2. Q2. 【Block Storageの削除】

Q1.で作成したBlock Storageを削除してください。

$ kubectl -n $(id -un) get pvc で前後の変化を確認すること。

A2.解答例

## 方法1. 準備したYAMLファイルを利用して削除する

$ kubectl -n $(id -un) delete -f new-block-pvc.yaml

## 方法2. command::get TYPE::pvc を利用して、対象のNAMEを指定して削除する

$ kubectl -n $(id -un) get pvc

$ kubectl -n $(id -un) delete pvc new-block-pvc

6.3. Q3. 【BlockStorageの拡張】

ストレージの容量は、storage:により大きな値を指定することで拡張できます。 Block Storageで作成した、nginx-block-pvc のサイズを 2Gi に変更してください。

A3.解答例

## 方法1. nginx-block-pvc を作成する時に利用したYAMLファイルを編集します

$ sed -i -e 's/storage: .*$/storage: 2Gi/' nginx-block-pvc.yaml

$ kubectl -n $(id -un) apply -f nginx-block-pvc.yaml`

## 方法2. command::editを利用して、storage: のサイズを変更します。

$ env EDITOR=emacs kubectl -n $(id -un) edit pvc nginx-block-pvc

注意点

作成済みのPVCのサイズ(storage:)だけを変更しても即座には反映されません。 実際に利用した時に、始めて変更したサイズで利用できるようになります。 このためQ1, Q2で作成したPVCではなく、既にPodと接続しているPVCを利用しています。

6.4. Q4.【MySQLサーバーへの応用】

公式MySQLサーバーイメージ(DockerHub公式MySQLコンテナイメージ) を利用することを前提としています。

次の要領で、データベースサーバーを動作させてください。

  1. BlockStorageを作成する (volumes.nginx-block-pvc.yaml.txtを書き換える)

  2. MySQLサーバーを作成するためのDeployment定義する (volumes.deploy-nginx-blockstorage.yaml.txtを書き換える)

  3. MySQLサーバーに接続するためのServiceを定義する (volumes.svc-nginx-blockstorage.yaml.txt)

  4. mysqlコマンドから接続できることを確認する

方法は一通りではありません。ヒントや解答例は、変更点の少ない最も一般的と思われる構成を前提としています。

ヒント

volumes.nginx-block-pvc.yaml.txt を書き換える行は、以下の項目です。

・name: 行

volumes.deploy-nginx-blockstorage.yaml.txt を書き換える行は以下の項目です。

・ app: 行 (複数)

・ image: 行

・ containerPort: 行

・ mountPath: 行

それぞれに指定する値は、公式MySQLイメージ (mysql:8.0) では、次のような構成になっています。 参考情報 - Dockerfile mysql:8

・接続用ポート番号(containerPort): 3306

・データ保存先(mountPath): /var/lib/mysql

また、新たに、env: 設定 (name: MYSQL_ROOT_PASSWORD, value: "secret") を加える必要があります。(secretの部分は任意で書き換えてください)

設定方法については、k8s公式ガイド - MySQLをデプロイする を参考にしてください。

volumes.svc-nginx-blockstorage.yaml.txt を書き換える行は以下のとおりです。

・name: 行

・port:/targetPort: 行 (Deploymentに記述したポート番号と同じ)

・app: 行 (Deploymentに記述したmatchLabels:の指定と同じ)

Serviceの変更箇所は、name:を除くと、Deploymentに記述した内容と整合性が取れていないと、ネットワーク接続ができないので注意してください。

稼動確認の方法は、各ThinkPadの端末上に、mysqlコマンドがインストールされていますので、次のコマンドが実行できることを確認してください。

## Service名(name:)が "mysql-svc" だという前提でコマンドラインを組み立てています。
$ mysql -u root -p -h $(kubectl -n $(id -un) get svc mysql-svc -o=jsonpath='{.status.loadBalancer.ingress[0].ip}')

## パスワードは、Deploymentで MYSQL_ROOT_USER環境変数 に設定した値 (secret 等)
Enter password:

## 次のSQLでUser情報が表示されれば成功です
mysql> select host,user from mysql.user;

+-----------+------------------+
| host      | user             |
+-----------+------------------+
| %         | root             |
| localhost | mysql.infoschema |
| localhost | mysql.session    |
| localhost | mysql.sys        |
| localhost | root             |
+-----------+------------------+
5 rows in set (0.00 sec)
A4.解答例 (各YAMLファイルへのリンク)