Azure KubernetesとAzure Database for MySQLを使ってWordPress Containerを動かす

#はじめに
Azure Kubernetes Service(AKS)が2018年6月26日付けで日本リージョンで利用可能になりました。
まずはお試しとして、WordPressのDocker ContainerをAKS上で動かしました。
データベースであるMySQLはAzureのDatabase as a Service(DaaS)であるAzure Database for MySQLを利用します。

「別にApp ServicesとAzure Database for MySQLでいいじゃん」というツッコミもあるかと存じますが、そういう方はこちらの記事を参考にしてください。

[App ServiceとAzure Database for MySQLで簡単Wordpressサイト構築](https://qiita.com/yotan/items/b34fdf901df62193805c)

#構成の説明

##AKS
- Docker Hubで提供されている「wordpress」コンテナをそのまま利用。
- wordpressコンテナを動かすPodは1つ。
- 外部ロードバランサを作成して(今回はとりあえず) http(TCP/80)によるアクセスを可能にする
- 永続化領域(/var/www/html)はAKSが提供しているストレージクラスを利用
- Azure Database for MySQLに繋げられるようにExternalNameも作成する

##Azure Database for MySQL
- AKSからの接続にはサービスエンドポイント(Preview)を使って内部経路でアクセスする
- 内部経路でアクセスするため、とりあえず今回はSSL接続は強制せずにTCP/3306でWordPressから繋げる

#AzureでKubernetesを利用する際のポイント
特にAKSを活用する上で、Azureに特化した構築ポイントとしては以下のものが挙げられます。

##ストレージ
AKSにはデフォルトで以下のストレージクラスが定義されており、簡単にAzure Managed Diskが利用可能です。

| ストレージクラス名|Managed Diskの種類|
|:-----------------|:------------------|
|default|Standard Storage|
|managed-premium|Premium Storage|

参考 : [Azure ディスクを含む永続ボリューム](https://docs.microsoft.com/ja-jp/azure/aks/azure-disks-dynamic-pv?WT.mc_id=AZ-MVP-5002499)

##パブリックIPアドレス
AKSの各種リソースがAzure上に実際にデプロイされているリソースグループ(MC_\<Resource Group Name\>_\<AKS Name\>_\<Region\>)配下にパブリックIPアドレスのリソースを作成するだけで、AKSのロードバランサから利用可能になります。

参考 : [Azure Kubernetes Service (AKS) ロード バランサーで静的 IP アドレスを使用する](https://docs.microsoft.com/ja-jp/azure/aks/static-ip?WT.mc_id=AZ-MVP-5002499)

##Azure Database for MySQLへはプライベート接続
まだプレビュー段階ですが、サービスエンドポイントの機能を使って、AKSの仮想ネットワークからのみアクセスできるように接続を制限します。

参考 : [Azure portal を使用して、Azure Database for MySQL VNet のサービス エンドポイントと VNet ルールを作成および管理する](https://docs.microsoft.com/ja-jp/azure/mysql/howto-manage-vnet-using-portal?WT.mc_id=AZ-MVP-5002499)

MySQLでサービスエンドポイントを利用する時は、汎用サーバーとメモリ最適化サーバーでのみサポートされるため、Azure利用料としては泣けてきますｗ

#AKSのデプロイ
それでは早速構築していきましょう。
Azureポータルにサインイン後、Marketplaceから「kubernetes Service」を探して作成します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/137436/25cba6b9-8f90-6981-2c64-4eb51d900556.png)

今回は「基本」タブで必要な情報だけ設定して、後の項目はデフォルトのままで作成します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/137436/e11a5c65-27c3-1ad2-2a69-d0017d932cd1.png)

ノード数はデフォルトの「3」のままだとAzureの課金が心配なので、今回は検証目的であるため「1」に変更しました。
他の必須項目はご自身の環境に併せて適宜入力します。
(ここではAzureポータルの詳細な操作説明は除きます)

#Azure Database for MySQLのデプロイ
Azureポータルにサインイン後、Marketplaceから「Azure Database for MySQL」を探して作成します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/137436/64438c15-a9f9-4ee4-ecba-869c163fd2f5.png)

設定項目の注意点としては価格レベルはデフォルトのまま、「汎用目的」を選んでおきます。こちらを「Basic」にするとサービスエンドポイントが利用できません。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/137436/144c251b-6ea2-8eb4-029a-c99c6c030f7d.png)
(ここではAzureポータルの詳細な操作説明は除きます)

作成されたMySQLの「サーバー名」「サーバー管理者ログイン名」と作成時に指定したパスワードは後からAKSのyamlファイル編集時に使用するため、メモしておきます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/137436/6f3d5e4b-690f-a39f-faf7-1081a47b81ee.png)


続いてサービスエンドポイントを有効にするため、AzureポータルからAKSの各種リソースが実際にAzure上にデプロイされているリソースグループ(MC_\<Resource Group Name\>_\<AKS Name\>_\<Region\>)配下にある「aks-vnet-」から始まる仮想ネットワークを選択します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/137436/42f3a72e-6a99-0888-ce78-d6cb389eaafe.png)

「サービスエンドポイント」のメニューから「＋追加」をクリックして、「サービスエンドポイントの追加」画面でサービスに「Microsoft.Sql」を選択、サブネットに「aks-subnet」を選択後「追加」をクリックします。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/137436/d7389adb-9fa9-c050-50d9-d84205c27c4f.png)

続いてMySQLのリソース画面に戻り「接続のセキュリティ」メニューから「＋既存の仮想ネットワークを追加」をクリック。
「名前」欄は適当に、サブスクリプション、仮想ネットワーク、サブネット名は実際にAKSのリソースがデプロイされている仮想ネットワークの「aks-subnet」の情報を設定して、最後に「OK」をクリックします。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/137436/23a0b6e0-b2c5-1094-e1a9-daa8475dd01c.png)

今回の検証ではWordPressからMySQLへのアクセスはプライベートネットワークを使用するため、暗号化は行わずに接続します。
「SSL設定」の「SSL接続を強制する」を「無効」に変更後、「保存」をクリックしてMySQLの準備は完了です。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/137436/6a58a2c2-7b23-621f-9f10-b295ab22381c.png)

#Public IP Addressの作成
AzureポータルからAKSの各種リソースが実際にAzure上にデプロイされているリソースグループ(MC_\<Resource Group Name\>_\<AKS Name\>_\<Region\>)配下にPulic IP Addressを作成します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/137436/bbc99203-369c-3d75-0a48-e83897869f4e.png)

リソース名(名前)は適当に。IPアドレスの割り当ては一応「静的」にします。
後からWordPressへのアクセスも楽になるようにDNS名ラベルも付けておきます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/137436/a6d0fba9-9404-7b1d-0ca8-87ddc50b91b8.png)

Public IP Addressの作成が完了したらリソースの概要を確認して「IPアドレス」の値をメモしておきます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/137436/837b0eda-f160-8de5-8f15-58cc29960b42.png)


#AKSのデプロイ準備
Kubernetesを制御するのには基本「kubectl」コマンドを使用します。
クライアントPC上にインストールするのは追加手順として多いため、今回はAzureポータルから起動できるCloud Shellを利用します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/137436/b1c33e57-45e3-1fa5-21c1-a384ecc9c78e.png)

Cloud Shell(bash)のコンソールが開いたら、以下のコマンドを入力することでkubectlが実行できるようになります。

```sh
$ az aks get-credentials --resource-group <Resource Group Name> --name <AKS Name>
Merged "<AKS Name>" as current context in /home/yoshimasa_katakura/.kube/config
```

#AKS上各種サービスのデプロイ

##シークレットの作成
最初にMySQLのデプロイ時に指定した管理者パスワードをKubernetesのシークレットで作成します。
これによって、yamlファイル上にパスワードを直に記述しないですみます。

```sh
$ kubectl apply secret generic wp-secrets --from-literal=WORDPRESS_DB_PASSWORD=<PASSWORD>
```
##MySQLのExternalName作成
MySQLのエンドポイント名も環境依存になるため、WordPress本体のyamlファイルに記載はせずに、ExternalNameとして最初に作成します。

以下のyamlファイルを準備します。
externalNameには先に作成したMySQLのサーバー名を設定します。

```yaml:mysql-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: azure-mysql
spec:
  type: ExternalName
  externalName: XXX.mysql.database.azure.com (ここは適宜変更)
```

以下のコマンドで作成します。

```sh
$ kubectl apply -f mysql-service.yaml
```

##WordPressに渡す環境変数の作成(ConfigMap)
上でシークレットで作成したWORDPRESS_DB_PASSWORDの他に、WordPress Containerにわたす必要がある環境変数を準備します。このような環境依存の値は別のyamlファイルにしておいたほうが後々のメンテ性も高くなります。
WORDPRESS_DB_USERには先に作成したMySQLのサーバー管理者ログイン名を指定します。

```yaml:wp-configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: wp-configmap
data:
  WORDPRESS_DB_NAME: wordpress
  WORDPRESS_DB_USER: dbadmin@aks-mysql (ここは適宜変更)
  WORDPRESS_DB_HOST: azure-mysql:3306
```

以下のコマンドで作成します。

```sh
$ kubectl apply -f wp-configmap.yaml
```

##永続化ストレージの作成
今回利用する永続化ストレージはAzureのStandard Diskを使用するため、storage-classには「default」を指定します。
サイズはとりあえず5Giを確保しましたが、適宜変更してください。
/var/www/html配下にマウントされるため、本格的にWordPressを利用してメディアファイルもたくさんアップロードする際にはもっと大きくする必要があるでしょう。

```yaml:wp-storage.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: wp-azure-disk
  annotations:
    volume.beta.kubernetes.io/storage-class: default
spec:
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 5Gi
```

以下のコマンドで作成します。

```sh
$ kubectl apply -f wp-storage.yaml
```

#External Load Balancerの作成
WordPressにインターネット側からアクセス可能にするため、外部ロードバランサーを作成します。
先に作成したAzure上のPublic IPアドレスのIPアドレスと同じ値をyamlファイル内に設定します。

```yaml:wp-lb.yaml
apiVersion: v1
kind: Service
metadata:
  name: wp-external-lb
spec:
  loadBalancerIP: XX.XX.XX.XX (ここは適宜変更)
  type: LoadBalancer
  ports:
  - port: 80
  selector:
    app: wordpress
```

以下のコマンドで作成します。

```sh
$ kubectl apply -f wp-lb.yaml
```

余談ですがこのコマンドを実行すると、Azure上のロードバランサーリソース「kubernetes」の構成が変更となり、Kubernetesのノードへ指定したパブリックIPアドレスからロードバランスされる設定が追加されます。

##WordPressの作成
以下が作成完了したら、後は最後にWordPressのDeploymentを作成します。

- シークレット(Secrets)「wp-secrets」
- 環境変数(ConfigMap)「wp-configmap」
- 永続化ストレージ(PersistentVolumeClaim)「wp-azure-disk」
- MySqlのExternalName「mysql-service」
- ロードバランサー(LoadBalancer)「wp-external-lb」

このyamlファイル内には環境に依存した記述が一切ありませんね。

```yaml:wp-service.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: wordpress
  labels:
    app: wordpress
spec:
  replicas: 1
  selector:
    matchLabels:
      app: wordpress
  template:
    metadata:
      labels:
        app: wordpress
    spec:
      containers:
      - name: wordpress
        image: wordpress
        envFrom:
        - configMapRef:
            name: wp-configmap
        - secretRef:
            name: wp-secrets
        ports:
        - containerPort: 80
          name: wordpress
        volumeMounts:
        - name: wp-azure-disk
          mountPath: /var/www/html
      volumes:
        - name: wp-azure-disk
          persistentVolumeClaim:
            claimName: wp-azure-disk
```

以下のコマンドで作成します。

```sh
$ kubectl apply -f wp-service.yaml
```

#動作確認

##AKS上のデプロイ状態確認
もっと前のタイミングでも良いのですが、AKS上に作成した各種ワークロードの確認をします。

```sh
# シークレットの状態確認
$ kubectl get secrets
NAME                                       TYPE                                  DATA      AGE
azure-storage-account-ktkraksst01-secret   Opaque                                2         4d
default-token-bjsjg                        kubernetes.io/service-account-token   3         8d
wp-secrets                                 Opaque                                1         2h

# 環境変数の状態確認
$ kubectl get configmap
NAME           DATA      AGE
wp-configmap   3         11h

# 永続化ストレージの状態確認
$ kubectl get persistentVolumeClaim
NAME            STATUS    VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
wp-azure-disk   Bound     pvc-e4f26c86-8c02-11e8-b0bf-da69366a1397   5Gi        RWO            default        1d

# ExternalNameとLoadbalancer(両方共service)の状態確認
$ kubectl get service
NAME             TYPE           CLUSTER-IP    EXTERNAL-IP                          PORT(S)        AGE
azure-mysql      ExternalName   <none>        aks-mysql.mysql.database.azure.com   <none>         1d
kubernetes       ClusterIP      10.0.0.1      <none>                               443/TCP        8d
wp-external-lb   LoadBalancer   10.0.145.26   XX.XX.XX.XX                          80:31367/TCP   25m

# WordPressのPodの状態確認
$ kubectl get pods
NAME                         READY     STATUS    RESTARTS   AGE
wordpress-5fbc6d9cc8-mhd8h   1/1       Running   0          2h
```

WordPressのPodの詳細情報を表示するには以下のコマンドを入力します。

```sh
$ kubectl describe pod wordpress-5fbc6d9cc8-mhd8h
Name:           wordpress-5fbc6d9cc8-mhd8h
Namespace:      default
Node:           aks-agentpool-10122857-1/10.240.0.4
Start Time:     Sun, 22 Jul 2018 10:15:26 +0900
Labels:         app=wordpress
                pod-template-hash=1967285774
Annotations:    <none>
Status:         Running
IP:             XX.XX.XX.XX
Controlled By:  ReplicaSet/wordpress-5fbc6d9cc8
Containers:
  wordpress:
    Container ID:   docker://2e39cffc356eba2b9e634d143dd2887df7d2da4255b10835876732b0bd45d30a
    Image:          wordpress
    Image ID:       docker-pullable://wordpress@sha256:1958b7258b1e1b09bbec1c6269e2e150bcc85c18383aa2420d95cb95a30d107f
    Port:           80/TCP
    Host Port:      0/TCP
    State:          Running
      Started:      Sun, 22 Jul 2018 10:16:10 +0900
    Ready:          True
    Restart Count:  0
    Environment Variables from:
      wp-configmap  ConfigMap  Optional: false
      wp-secrets    Secret     Optional: false
    Environment:    <none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from default-token-bjsjg (ro)
      /var/www/html from wp-azure-disk (rw)
Conditions:
  Type           Status
  Initialized    True
  Ready          True
  PodScheduled   True
Volumes:
  wp-azure-disk:
    Type:       PersistentVolumeClaim (a reference to a PersistentVolumeClaim in the same namespace)
    ClaimName:  wp-azure-disk
    ReadOnly:   false
  default-token-bjsjg:
    Type:        Secret (a volume populated by a Secret)
    SecretName:  default-token-bjsjg
    Optional:    false
QoS Class:       BestEffort
Node-Selectors:  <none>
Tolerations:     node.kubernetes.io/not-ready:NoExecute for 300s
                 node.kubernetes.io/unreachable:NoExecute for 300s
Events:          <none>
```

ConditionsのReadyが「True」になっているので準備完了です。

##実際にWordPressサイトに接続
前に作成したPublic IP Adoressを直接ブラウザで入力するか、DNS名で指定したFQDN「XXX.japanease.cloudapp.azure.com」を入力してWordPressサイトに接続します。

見事接続できたら、最初に言語設定を済ませます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/137436/6cfc589d-226c-0a6b-9a70-8d1c95456433.png)

「ようこそ」画面ではWordPressの管理者情報、サイト名などを入力して先に進みます。
これらの詳細な設定内容は本記事では割愛します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/137436/0bd7cd56-7a8b-52d4-fce0-26c6d1c7d7a6.png)

無事管理画面にログインできました。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/137436/cbc5d583-718e-defd-e23a-411df6e6ca8a.png)


トップページもちゃんと表示されました。後はお好きなようにWordPressのカスタマイズを行ってくださいね！
![image.png](https://qiita-image-store.s3.amazonaws.com/0/137436/09bcd89e-214a-dbb8-8afc-02b5245fddcf.png)

#おわりに
今回の記事ではKubernetesそのもののベストプラクティスについての話ではなく、Azure Kubernetes Service(AKS)の概要を中心に記載しました。

Azure Kubernetes Serviceを利用していく上でAzureの関連サービスをうまく連携して活用していく上で参考になればと思います。

最後に今回利用したyamlファイル一式をGitHubに上げておきましたのでご利用くださいね。

[https://github.com/katakura/aks-wordpress](https://github.com/katakura/aks-wordpress)