ASK を使いこなす: SQL Server 2017 を AKS で使う - ストレージの準備

この記事では [Azure Kubernetes Services (AKS) を使用して Kubernetes に SQL Server コンテナーを配置する](https://docs.microsoft.com/ja-jp/sql/linux/tutorial-sql-server-containers-kubernetes?view=sql-server-2017) を深堀します。既に [AKS チュートリアルの深堀](https://qiita.com/kenakamu/items/e155740aa60c85a3d867#aks-%E3%83%81%E3%83%A5%E3%83%BC%E3%83%88%E3%83%AA%E3%82%A2%E3%83%AB%E3%81%AE%E6%B7%B1%E5%A0%80) で紹介した内容についての理解がある前提とします。

今回はまずストレージの作成についてみていきます。次回シークレットの作成や SQL Server 2017 の配置を行います。

# データの保存場所について

ポッドが作成される場合、コンテナは毎回イメージからインスタンスを作成するため、ローカルに保存していたデータはすべて消えます。そのためストレージは別途用意します。

##  ポッドと同じライフサイクルのボリューム

ライフサイクルがポッドと同じ場合、ポッドがスケールダウンや再起動などで再作成された際、データが失われます。早速動作を確認しましょう。

1\. Redis のイメージに対して、emptyDir と呼ばれる一時領域をマウントするマニフェストを作成。ここではファイル名を volume-test.yaml とした。

- volumes にて emptyDir を指定
- containers.volumeMount にてボリュームを /cache にマウント

```yaml:volume-test.yaml
apiVersion: apps/v1beta1
kind: Deployment
metadata:
  name: volume-test
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: volume-test
    spec:
      nodeSelector:
        "beta.kubernetes.io/os": linux
      containers:
      - name: volume-test
        image: redis
        ports:
        - containerPort: 6379
          name: redis
        volumeMounts:
        - mountPath: /cache
          name: cache-volume
      volumes:
      - name: cache-volume
        emptyDir: {}
```

2\. マニフェストを適用してポッドを展開

```shell
>  kubectl apply -f .\volume-test.yaml
```

3\. ポッドにアタッチして、フォルダとファイルを作成。

```shell
> kubectl get pods -l app=volume-test
NAME                           READY   STATUS    RESTARTS   AGE
volume-test-6d9595775d-kwqxx   1/1     Running   0          31s
> kubectl exec -it volume-test-6d9595775d-kwqxx /bin/bash
```
```bash
root@volume-test-6d9595775d-kwqxx:/data# mkdir mydata
root@volume-test-6d9595775d-kwqxx:/data# echo hello > mydata/test.txt
root@volume-test-6d9595775d-kwqxx:/data# cat mydata/test.txt
hello
root@volume-test-6d9595775d-kwqxx:/data# exit
```

4\. ポッドを削除。

```shell
> kubectl delete pods volume-test-6d9595775d-kwqxx
pod "volume-test-6d9595775d-kwqxx" deleted
```

5\. レプリカセットによって自動で再作成されるので、確認して再度アタッチ。先ほどのフォルダもファイルも存在しないことを確認。

```shell
> kubectl get pods -l app=volume-test
NAME                           READY   STATUS    RESTARTS   AGE
volume-test-6d9595775d-rtqrq   1/1     Running   0          17s
> kubectl exec -it volume-test-6d9595775d-rtqrq /bin/bash
```
```bash
root@volume-test-6d9595775d-rtqrq:/data# cat mydata/test.txt
cat: mydata/test.txt: No such file or directory
```

6\. デプロイメントを削除。

```shell
kubectl delete deployments -l app=volume-test
```

これでポッドを再作成するとデータが消えることがわかりました。

## 永続ボリューム

データベースのように書き出したデータはポッドの再起動に関係なく維持したい場合、永続ボリュームを使います。これによりポッドとストレージを分離し、ポッド起動時に同じ永続ボリュームにアタッチすることでデータの維持ができます。

AKS では永続ボリュームとして以下のサービスが利用できます。

- Azure ディスク : 1 つのポッドのみがアクセスする場合
- Azure Files : 複数のポッドからアクセスする場合

![](https://docs.microsoft.com/ja-jp/azure/aks/media/concepts-storage/persistent-volumes.png)

### ストレージクラスと永続ボリューム要求

永続ボリュームは、ストレージクラスと永続ボリューム要求の組み合わせて設定します。

- ストレージの種類を決めるストレージクラスを作成
- 永続ボリュームの容量など要求を決める、永続ボリューム要求を作成、ポッドに関連付け
- ポッド要求時、永続ボリュームを作成して割り当て

![](https://docs.microsoft.com/ja-jp/azure/aks/media/concepts-storage/persistent-volume-claims.png)
※永続ボリューム要求と永続ボリュームは 1 対 1 の関係

## 永続ボリューム要求と永続ボリューム

上記で説明した通り永続ボリューム要求と永続ボリュームは 1 対 1 の関係ですが、ポッドに紐づく永続ボリューム要求を削除した場合、永続ボリュームも削除するかは、ReclaimPolicy で決めることができます。ReclaimPolicy を含めたストレージに関するパラメーターはストレーククラスから引き継ぎます。

このチュートリアルでは Azure ディスクを使います。

# ボリュームとは

k8s におけるボリュームは、ポッド内のコンテナがデータを読み書きできるディスク領域で、ポッドに紐づきます。ボリュームには様々な種類や用途があります。

- ポッド内のコンテナが利用するデータの一時領域
- ポッド内の複数コンテナでのデータ共有
- ボリュームを通したアプリケーション設定やシークレットの割り当て
- ポッドが削除されると同時に削除される領域
- ポッドの削除時でもデータを永続化する領域

# 高可用性

SQL 本体にも Always On などの HA 機能がありますが、このチュートリアルでは k8s のレプリカセットによるポッドの自動復旧機能を使っています。障害発生時のイメージは以下の通りです。

1\. あるノードに展開された SQL ポッドが永続ボリュームに接続している。
![](https://docs.microsoft.com/ja-jp/sql/linux/media/tutorial-sql-server-containers-kubernetes/kubernetes-sql.png?view=sql-server-2017)

2\. ポッドが落ちた場合は、レプリカセットによって新規ポッドが展開されるが、同じ永続ボリュームにアタッチするため継続してサービスできる。
![](https://docs.microsoft.com/ja-jp/sql/linux/media/tutorial-sql-server-containers-kubernetes/kubernetes-sql-after-pod-fail.png?view=sql-server-2017)

3\. ノードが落ちた場合も、レプリカセットによって他のノードにポッドが展開されるが、同じ永続ボリュームにアタッチするため継続してサービスできる。
![](https://docs.microsoft.com/ja-jp/sql/linux/media/tutorial-sql-server-containers-kubernetes/kubernetes-sql-after-node-fail.png?view=sql-server-2017)

# チュートリアルの深堀

今回はチュートリアルのうち、ストレージに関わる部分の作業を見ていきます。
またチュートリアルでは Standard ディスクを使っていますが、SQL は高速はディスクが通常必要なため、ここでは Premium を使っています。

## ストレージを作成する

この手順では永続ボリュームと永続ボリューム要求を構成しています。またストレージクラスを使った動的作成を利用しているため、事前に Azure 側で Azure ディスクを用意する必要はありません。

1\. ストレージクラスおよび永続ボリューム要求を展開する yaml を mssql-storage.yaml として作成。

- 前半はストレージクラス (StorageClass) 用。名前は azure-disk 
- provisioner: ストレージ作成の実行サービス。ここでは Azure Disk を指定
- parameter: provisioner に渡すパラメータを指定。ストレージの種類をして Premium_LRS を指定
- ReclaimPolicy の設定がないため、既定の Delete が適用
- 後半は永続ボリューム要求 (PersistentVolumeClaim) 用。名前は mssql-data
- annotations: ストレージクラスとして azure-disk を指定
- spec: ボリュームに対するアクセス。ここでは ReadWriteOnce のため 1 ポッドからのみ読み書き。また容量として 8GB を指定

```yaml:mssql-storage.yaml
kind: StorageClass
apiVersion: storage.k8s.io/v1beta1
metadata:
     name: azure-disk
provisioner: kubernetes.io/azure-disk
parameters:
  storageaccounttype: Premium_LRS
  kind: Managed
---
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: mssql-data
  annotations:
    volume.beta.kubernetes.io/storage-class: azure-disk
spec:
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 8Gi
```

2\. ```apply``` で yaml を適用。

```shell
kubectl apply -f mssql-storage.yaml
```
3\. 作成された永続ボリューム要求を確認。

- pvc: persistentvolumeclaims の省略形

```shell
> kubectl get pvc                                                  NAME         STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
mssql-data   Bound    pvc-49cbb6c9-e0f8-11e9-869b-d2cecd57d4bb   8Gi        RWO            azure-disk     118s
```

4\. ```describe``` で詳細確認。

- Volume として　pvc-9021c795-e0dd-11e9-869b-d2cecd57d4bb が割り当てらている

```shell
> kubectl describe pvc mssql-data                                  Name:          mssql-data
Namespace:     default
StorageClass:  azure-disk
Status:        Bound
Volume:        pvc-49cbb6c9-e0f8-11e9-869b-d2cecd57d4bb
Labels:        <none>
Annotations:   kubectl.kubernetes.io/last-applied-configuration:
                 {"apiVersion":"v1","kind":"PersistentVolumeClaim","metadata":{"annotations":{"volume.beta.kubernetes.io/storage-class":"azure-disk"},"name...
               pv.kubernetes.io/bind-completed: yes
               pv.kubernetes.io/bound-by-controller: yes
               volume.beta.kubernetes.io/storage-class: azure-disk
               volume.beta.kubernetes.io/storage-provisioner: kubernetes.io/azure-disk
Finalizers:    [kubernetes.io/pvc-protection]
Capacity:      8Gi
Access Modes:  RWO
VolumeMode:    Filesystem
Events:
  Type       Reason                 Age    From                         Message
  ----       ------                 ----   ----                         -------
  Normal     ProvisioningSucceeded  2m27s  persistentvolume-controller  Successfully provisioned volume pvc-49cbb6c9-e0f8-11e9-869b-d2cecd57d4bb using kubernetes.io/azure-disk
Mounted By:  mssql-deployment-5b74bdb6f7-tqz8q
```

5\. 上記結果より割り当てられている Volume が「pvc-49cbb6c9-e0f8-11e9-869b-d2cecd57d4bb」となっているが、Azure 側に同名の Azure ディスクが作成されている。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/96bf1712-afaf-80f5-9eea-38954aa25ba1.png)

6\. ストレージクラスを確認。今回作成したもの以外に、既定のもの存在。

- sc: storageclass の省略形

```shell
> kubectl get sc -o wide
NAME                PROVISIONER                AGE
azure-disk          kubernetes.io/azure-disk   17m
default (default)   kubernetes.io/azure-disk   31h
managed-premium     kubernetes.io/azure-disk   31h
```

7\. ```describe``` で詳細確認。

- ReclaimPolicy が Delete

```shell
> kubectl describe sc azure-disk                                   
Name:            azure-disk
IsDefaultClass:  No
Annotations:     kubectl.kubernetes.io/last-applied-configuration={"apiVersion":"storage.k8s.io/v1beta1","kind":"StorageClass","metadata":{"annotations":{},"name":"azure-disk"},"parameters":{"kind":"Managed","storageaccounttype":"Premium_LRS"},"provisioner":"kubernetes.io/azure-disk"}

Provisioner:           kubernetes.io/azure-disk
Parameters:            kind=Managed,storageaccounttype=Premium_LRS
AllowVolumeExpansion:  <unset>
MountOptions:          <none>
ReclaimPolicy:         Delete
VolumeBindingMode:     Immediate
Events:                <none>
```

8\. 永続ボリューム要求とセットで作成される永続ボリュームを確認。

- pv: persistentvolume の省略形
- Reclaim Policy: ストレージクラスから継承。永続ボリューム要求が削除された場合の永続ボリュームの挙動。ここでは Delete のため一緒に削除される

```shell
> kubectl get pv                                                   NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                STORAGECLASS   REASON   AGE
pvc-49cbb6c9-e0f8-11e9-869b-d2cecd57d4bb   8Gi        RWO            Delete           Bound    default/mssql-data   azure-disk              5m
```

これでディスクの準備は完了です。

# 動作の深堀

では実際に永続ボリューム要求を消したらどうなるか試してみましょう。

1\. 以下コマンドで永続ボリューム要求を削除。

```shell
kubectl delete pvc mssql-data
```

2\. 永続ボリュームが削除されていることを確認。

```shell
> kubectl get pv
No resources found.
```

3\. ポータルでも先ほどのディスクが削除されたことを確認。
<img width="500" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/69139cb4-481c-8646-0e44-c8e5ecd06356.png"/>

要求が消えると関連するボリュームも消えることが確認できました。次のステップのために再度要求を作成します。

4\. yaml を適用。

```shell
kubectl apply -f .\mssql-storage.yaml
```
5\. ポータルでも確認。
<img width="500" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/ec318455-f62c-72bf-ca71-b88e10ebd573.png"/>

# まとめ

基本的なポッドの性質として、ファイルの作成や変更は永続ボリュームがないと維持できない事と、動的にストレージを作成できることがわかりました。

ボリュームは動的以外にも静的に割り当てることもでますし、複数ポッドから同じボリュームにアクセスしたい場合は Azure Files を使うなど、まだまだ他のオプションもありますが、これらはまたの機会に紹介します。

[次の記事へ](https://qiita.com/kenakamu/items/a05843d8e5efd13eb009)
[目次へ戻る](https://qiita.com/kenakamu/items/e155740aa60c85a3d867)

# 参照

[Azure で利用できるディスクの種類](https://docs.microsoft.com/ja-jp/azure/virtual-machines/windows/disks-types)
[Azure Kubernetes Service (AKS) でのアプリケーションのストレージ オプション]
(https://docs.microsoft.com/ja-jp/azure/aks/concepts-storage)
[Persistent Volumes](https://kubernetes.io/docs/concepts/storage/persistent-volumes/)