GKE上にWordpress環境作ってみる【vol.4 永続ボリュームにMySQLのデータを保管する！編】

こんにちは。
株式会社クラスアクト インフラストラクチャ事業部の大塚です。

今回はWordpress+MySQL環境を構築するにあたり、PV/PVCを用意してMySQLのデータをpod外に退避し、podが消えてもデータ自体は消えないようにしたいと思います。

過去の構築ログは以下になります。

https://qiita.com/ohtsuka-shota/items/48545c2b34bb251aa0e7

https://qiita.com/ohtsuka-shota/items/f4796366cfa966b33f4c

https://qiita.com/ohtsuka-shota/items/12a7f7a51e572f3da656


# 構築する環境

以下が、今までに構築した環境です。
ConfigMapとSecretを用いて、環境変数をpodに設定しデプロイしておりました。
![名称未設定ファイル-ページ23のコピーのコピー drawio](https://github.com/ohtsuka-shota/ascii0-python-encrypt-decrypt/assets/127835743/d0e90db4-8ad3-4133-82ea-aae0d1f9c542)

今回はPV/PVCを作成し、それをMySQLのpodにアタッチし、データをpod外に保存するようにします。GKEだけでは構築出来ない為、GCE(Google Compute Engine)においてストレージを用意して、そこにPV/PVCをデプロイしていきます。構築イメージは以下です。
![名称未設定ファイル-pv-pvc drawio](https://github.com/ohtsuka-shota/ascii0-python-encrypt-decrypt/assets/127835743/1f19e905-6a20-42fc-8226-812026ac5afa)

# PV/PVCを用いないとどうなるか？
使用するyamlファイル等は以下とします。

https://qiita.com/ohtsuka-shota/items/12a7f7a51e572f3da656

yamlファイルをデプロイした後のpodとserviceの状態は以下となりました。
LoadBalancerのEXTERNAL-IPとポートを使ってWebブラウザにアクセスします。
```
$ kubectl get pod,svc
NAME                READY   STATUS    RESTARTS   AGE
pod/mysql-pod       1/1     Running   0          4m57s
pod/wordpress-pod   1/1     Running   0          4m19s


NAME                             TYPE           CLUSTER-IP   EXTERNAL-IP    PORT(S)           AGE
service/kubernetes               ClusterIP      10.62.0.1    <none>         443/TCP           15m
service/mysql-clusterip          ClusterIP      10.62.2.95   <none>         3306/TCP          5m5s
service/wordpress-loadbalancer   LoadBalancer   10.62.0.71   34.30.162.91   60000:31028/TCP   4m7s
```

用意したサーバは"not PV/PVC"というタイトルのWebサイトになります。
このデータの一部がMySQLのpodに存在しています。
<img width="1124" alt="2023052104" src="https://github.com/ohtsuka-shota/ascii0-python-encrypt-decrypt/assets/127835743/0c046588-1001-48ee-ab4b-eaa892bcbc00">

<img width="1127" alt="2023052103" src="https://github.com/ohtsuka-shota/ascii0-python-encrypt-decrypt/assets/127835743/4562d3fa-bf50-4ab3-94cf-95444ca3c00a">

この状態でMySQLのpodを削除して再度MySQL podをデプロイします。
MySQL podという点では同じですが中身は全く異なります。
```
$ kubectl delete pod mysql-pod
pod "mysql-pod" deleted
$ kubectl apply -f mysql-pod.yaml 
Warning: Autopilot set default resource requests for Pod default/mysql-pod, as resource requests were not specified. See http://g.co/gke/autopilot-defaults
pod/mysql-pod created

$ kubectl get pod
NAME            READY   STATUS    RESTARTS   AGE
mysql-pod       1/1     Running   0          19s
wordpress-pod   1/1     Running   0          8m52s
```

この状態で再度EXTERNAL-IPとポートでアクセスすると再度初期画面が表示されてしまいます。
MySQLのpodが消えてしまったことでデータも消えてしまったことになります。
黄色枠内にデータが存在し、podが消えるのと同時にデータも消えてしまうのです。

<img width="1124" alt="2023052104" src="https://github.com/ohtsuka-shota/ascii0-python-encrypt-decrypt/assets/127835743/0c046588-1001-48ee-ab4b-eaa892bcbc00">

![名称未設定ファイル-pv-pvcのコピー drawio (1)](https://github.com/ohtsuka-shota/ascii0-python-encrypt-decrypt/assets/127835743/55fda888-5c17-42f6-9691-85dc7d2b6024)

# 用意したファイル一覧

今回、PV/PVCを用いた環境を構築するにあたり用意したyamlファイルは以下となります。
まずvol.3から新規作成・変更点のあるyamlファイルになります。

#### mysql-pv.yaml
PVをデプロイする為のyamlファイルとなります。
GKE独自の設定としては"gcePersistentDisk"となるかと思います。
pdNameのところにPVをデプロイするディスクを指定します。gke-mysql-mydiskとしておりますが、これは後程GUI上で作成します。fsTypeのところはFileSystemのタイプを指定します。今回はext4とします。
```mysql-pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: mysql-vol-pv
spec:
  capacity:
    storage: 100Gi
  accessModes:
  - ReadWriteOnce 
  persistentVolumeReclaimPolicy: Retain
  storageClassName: mysql-storage
  gcePersistentDisk:
    pdName: gke-mysql-mydisk 
    fsType: ext4
```

#### mysql-pvc.yaml
vol.3から新規作成したものになります。PVとは異なり、GKE特異のものはないかと思います。
```mysql-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mysql-vol-pvc
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: mysql-storage
  resources:
    requests:
      storage: 100Gi
```

#### mysql-pod.yaml
podにvolumeをアタッチし、デプロイする為の設定を追加しています。
mountPathを"/var/lib/mysql"としているのはMySQLのデータはデフォルトでここに格納されるためです。
```mysql-pod.yaml
apiVersion: v1   
kind: Pod   
metadata: 
  name: mysql-pod 
  labels: 
    app: mysql    
spec:   
  containers:   
    - name:  mysql-con    
      image: mysql:8.0-debian 
      ports:    
      - containerPort: 3306 
      envFrom:  
      - configMapRef:  
          name: mysql-cm 
      - secretRef:  
          name: mysql-sec
      volumeMounts:
        - mountPath: /var/lib/mysql
          name: mysql-vol-pvc
  volumes:
  - name: mysql-vol-pvc
    persistentVolumeClaim:
      claimName: mysql-vol-pvc
```


以下はvol.3から変更していないyamlファイルとなります。
#### wordpress-pod.yaml
```wordpress-pod.yaml
apiVersion: v1  
kind: Pod  
metadata:  
  name: wordpress-pod  
  labels:  
    app: wordpress 
spec:  
  containers:  
    - name:  wordpress-con  
      image: wordpress:php8.1-apache  
      ports:  
      - containerPort : 80  
      envFrom: 
      - configMapRef: 
          name: wp-cm 
      - secretRef: 
          name: wp-sec
```

#### wordpress-configmap.yaml
```wordpress-configmap.yaml
apiVersion: v1 
kind: ConfigMap 
metadata: 
  name: wp-cm 
data: 
  WORDPRESS_DB_HOST: mysql-clusterip 
  WORDPRESS_DB_NAME : gke-wordpress-database 
  WORDPRESS_DB_USER : devuser
```

#### wordpress-secret.yaml
```wordpress-secret.yaml
apiVersion: v1 
kind: Secret 
metadata: 
  name: wp-sec 
data: 
  WORDPRESS_DB_PASSWORD : cGFzc3dvcmQK
```

#### wordpress-loadbalancer.yaml
```wordpress-loadbalancer.yaml
apiVersion: v1  
kind: Service  
metadata:  
  name: wordpress-loadbalancer  
spec:  
  type: LoadBalancer  
  selector:  
    app: wordpress 
  ports:  
  - protocol: TCP  
    port: 60000 
    targetPort: 80
```

#### mysql-configmap.yaml
```mysql-configmap.yaml
apiVersion: v1 
kind: ConfigMap 
metadata: 
  name: mysql-cm 
data: 
  MYSQL_DATABASE : gke-wordpress-database 
  MYSQL_USER : devuser 
  MYSQL_ROOT_PASSWORD : password
```

#### mysql-secret.yaml
```mysql-secret.yaml
apiVersion: v1 
kind: Secret 
metadata: 
  name: mysql-sec 
data: 
  MYSQL_PASSWORD : cGFzc3dvcmQK
```

#### mysql-clusterip.yaml
```mysql-clusterip.yaml
apiVersion: v1   
kind: Service   
metadata:   
  name: mysql-clusterip  
spec:   
  selector:   
    app: mysql   
  type: ClusterIP   
  ports:   
  - name: mysql   
    port: 3306   
    protocol: TCP   
    targetPort: 3306
```

# 構築

まずGCEでPV/PVC用のディスクを用意します。
なお、以下のWebサイトを参考にしております。

https://tkzo.jp/blog/use-persistent-volumes-in-gke/

まず、GCEを開くと以下のような画面が表示されるため、ストレージという項目の"ディスク"を押下します。
<img width="1119" alt="2023052105" src="https://github.com/ohtsuka-shota/ascii0-python-encrypt-decrypt/assets/127835743/f8a1222c-aab9-4cb9-8edf-19f863ef86ac">

遷移し、以下のような画面が表示されます。
上部に表示されている"ディスクを作成"を押下します。
<img width="1117" alt="2023052106" src="https://github.com/ohtsuka-shota/ascii0-python-encrypt-decrypt/assets/127835743/faa0b26c-5d40-4b56-9447-60bc19d83fd0">

ディスクの名前やどのリージョン・ゾーンに作成するか、容量などを決定していきます。
<img width="821" alt="2023052107" src="https://github.com/ohtsuka-shota/ascii0-python-encrypt-decrypt/assets/127835743/416357c7-8237-4e46-a157-fd83d8c0223a">

<img width="824" alt="2023052108" src="https://github.com/ohtsuka-shota/ascii0-python-encrypt-decrypt/assets/127835743/95b81c84-f2af-436d-bb02-0f0ab04d97a6">

作成が完了した画面が以下となります。
<img width="1122" alt="2023052109" src="https://github.com/ohtsuka-shota/ascii0-python-encrypt-decrypt/assets/127835743/99e7f705-f18d-435c-8dfe-b46544255d4a">

この状態でPV/PVCをyamlをベースにデプロイしていきます。
実行後、PVCのステータスがBoundになっていれば問題ないと思います。
Pendingになっているようであれば、おそらくyamlに記載ミスなどがあるかと思います。
```
$ kubectl apply -f mysql-pv.yaml 
persistentvolume/mysql-vol-pv created
$ kubectl apply -f mysql-pvc.yaml 
persistentvolumeclaim/mysql-vol-pvc created

$ kubectl get pv,pvc -o wide
NAME                            CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                   STORAGECLASS    REASON   AGE   VOLUMEMODE
persistentvolume/mysql-vol-pv   100Gi      RWO            Retain           Bound    default/mysql-vol-pvc   mysql-storage            28s   Filesystem
NAME                                  STATUS   VOLUME         CAPACITY   ACCESS MODES   STORAGECLASS    AGE   VOLUMEMODE
persistentvolumeclaim/mysql-vol-pvc   Bound    mysql-vol-pv   100Gi      RWO            mysql-storage   18s   Filesystem
```

PV/PVCはGKEのGUIでも確認することができます。
<img width="1121" alt="image" src="https://github.com/ohtsuka-shota/ascii0-python-encrypt-decrypt/assets/127835743/adb6106b-5566-4a62-b17a-b4c35fcaf283">

この状態でpod,service,ConfigMap,Secretをyamlファイルでデプロイしていきます。
デプロイ結果が以下となります。
```
$ kubectl get pod,svc,cm,secret,pv,pvc -o wide
NAME                READY   STATUS    RESTARTS   AGE     IP             NODE                          NOMINATED NODE   READINESS GATES
pod/mysql-pod       1/1     Running   0          6m12s   10.112.0.132   gk3-my-pool-1-d33c8819-85t9   <none>           <none>
pod/wordpress-pod   1/1     Running   0          3m12s   10.112.0.133   gk3-my-pool-1-d33c8819-85t9   <none>           <none>

NAME                             TYPE           CLUSTER-IP       EXTERNAL-IP    PORT(S)           AGE   SELECTOR
service/kubernetes               ClusterIP      10.112.128.1     <none>         443/TCP           64m   <none>
service/mysql-clusterip          ClusterIP      10.112.128.253   <none>         3306/TCP          54m   app=mysql
service/wordpress-loadbalancer   LoadBalancer   10.112.130.188   34.30.162.91   60000:30188/TCP   54m   app=wordpress

NAME                         DATA   AGE
configmap/kube-root-ca.crt   1      64m
configmap/mysql-cm           3      55m
configmap/wp-cm              3      55m

NAME               TYPE     DATA   AGE
secret/mysql-sec   Opaque   1      55m
secret/wp-sec      Opaque   1      55m

NAME                            CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                   STORAGECLASS    REASON   AGE   VOLUMEMODE
persistentvolume/mysql-vol-pv   100Gi      RWO            Retain           Bound    default/mysql-vol-pvc   mysql-storage            11m   Filesystem

NAME                                  STATUS   VOLUME         CAPACITY   ACCESS MODES   STORAGECLASS    AGE   VOLUMEMODE
persistentvolumeclaim/mysql-vol-pvc   Bound    mysql-vol-pv   100Gi      RWO            mysql-storage   11m   Filesystem
```

今回LoadBalancerのEXTERNAL-IPが34.30.162.91となっているので、このIPアドレス＋ポート番号60000で接続します。
用意したサーバは"PV/PVC on GKE"というタイトルのWebサイトになります。
<img width="1125" alt="image (1)" src="https://github.com/ohtsuka-shota/ascii0-python-encrypt-decrypt/assets/127835743/a589581c-c203-43ca-bf1c-017838e269e0">

この状態でmysqlのpodを削除し、再度デプロイします。
```
$ kubectl delete pod mysql-pod
pod "mysql-pod" deleted
$ kubectl apply -f mysql-pod.yaml 
Warning: Autopilot set default resource requests for Pod default/mysql-pod, as resource requests were not specified. See http://g.co/gke/autopilot-defaults
pod/mysql-pod created

$ kubectl get pod
NAME            READY   STATUS    RESTARTS   AGE
mysql-pod       1/1     Running   0          93s
wordpress-pod   1/1     Running   0          9m13s
```

再度アクセスします。
PV/PVCを作らない状態と異なり、データが残っていることが確認できますね。
<img width="1125" alt="image (1)" src="https://github.com/ohtsuka-shota/ascii0-python-encrypt-decrypt/assets/127835743/a589581c-c203-43ca-bf1c-017838e269e0">

データが黄色枠に保存されており、MySQLのpodはここを参照しているためデータが残る状態になっているということですね。
![名称未設定ファイル-pv-pvcのコピー drawio](https://github.com/ohtsuka-shota/ascii0-python-encrypt-decrypt/assets/127835743/5eadb2ad-88b3-4d03-87e9-9b7fadc0e17c)