ARM64 Kubernetes にデータベース Pod をデプロイ

# はじめに
この手順を辿れば、ARM64 Kubernetes 上で Docker Official Image を使ったデータベース Pod を起動できます。

# この記事について
実践的に Kubernetes を学習できればと思い、[2時間でRaspberry Piを使ったKubernetes クラスタを作る](https://qiita.com/vfuji/items/f575673b83f78dd6e4ba)という記事を書いたのですが、Pod の作成手順もあった方が良いと思い、この記事を書きました。

# 前提条件
この記事を書く時に使った環境は下記の通りです。

- [K3s: v1.21.1+k3s1](https://k3s.io)
- Raspberry Pi 4B 4GB Memory
- インターネットアクセス環境
- LinuxをCLIのみで触る程度の知識

## ソフトウェア
ARM64対応のOfficial Imageは以下のバージョン以降
- MySQL: 8.0.29以降
- MongoDB: 3.4.20以降

# 事前確認事項の整理
## ハードウェアのシステムアーキテクチャを確認
Pod をデプロイする対象が X86-64(AMD64) なのか、ARM64 なのか確認します。
Intel CPU や AMD CPU の場合は AMD64、Raspberry Pi の場合は ARM64 となります。
*注意: コンテナイメージが対応していないシステムアーキテクチャの Pod は起動しません*

## Official Docker Imageの対応状況を確認
目的とするコンテナイメージが自分の持っているKubernetesシステムアーキテクチャに対応することを確認

[docker hub で mysql を検索](https://hub.docker.com/search?q=mysql)すると、x86−64 と ARM64 の両方に対応していることが確認できます。
![docker_hub_mysql.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1557529/1d88390d-3501-3727-a2be-0e73cac014aa.png)

同じバージョンで、複数のアーキテクチャに対応したイメージを提供していることが確認できます。
![docker_hub_mysql_8028.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1557529/53adc080-f847-0e61-4d16-76d3c5c58fc6.png)

こちらは、AMD64　のみのイメージとなります。よって、Raspberry Pi　を使った Kubernetes では動作しません。
![docker_hub_mysql_8027.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1557529/5fcd5506-8de8-115e-c63f-47f380e12baa.png)


同じく[docker hub で mongo を検索](https://hub.docker.com/search?q=mongodb)すると、x86-64 と ARM64 の両方に対応していることが確認できました。
![docker_hub_mongodb.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1557529/9b490d8e-d29b-5835-a12c-320edd06034d.png)

MongoDB　は、3.4.20 以降、AMD64 と ARM64 の両方が提供されています。
![docker_hub_mongodb_3420.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1557529/2591f272-eaa4-ea7a-dc42-6dcd3f5ee87d.png)

# デプロイの準備と実行
## 各コンテナイメージの環境変数(Environment Variables)を確認
コンテナイメージを起動する際に初期値として渡す環境変数の条件を確認します。コンテナイメージを作った時に決まる要素なので、都度確認する必要があります。そうでなければ、コンテナ(Pod)起動後に個別設定が必要となってしまうでしょうから、必須の確認項目となるかと。
Docker Official Imagesでは、以下のような形で環境変数が説明されています。これは提供されているイメージの説明ページによって記載方法が違うため、都度確認が必要となります。

Docker Official Images: MySQLの環境に関する説明
![スクリーンショット 2021-05-23 17.20.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1557529/74f19ddb-4e0e-caef-7e91-481937035b41.png)


Docker Official Images: Mongoの環境変数に関する説明
![スクリーンショット 2021-05-23 17.20.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1557529/3e80f3b2-0ca9-bf68-f8d8-3c255b631df2.png)

## Pod 作成準備
Pod 作成の準備を行います。ちなみに、こちらの例はセキュアではありませんので、ご注意下さい。
パスワードは都度個別にsecretで運用すべきですが、説明用としてsecretを使うものと直書きの両方を案内します。
下記のように設定するとMySQL ROOTのパスワードが`rootpass01`となります。適宜変更して使って下さい。

```shell:コマンド
kubectl create secret generic mysql-pass --from-literal=password=rootpass01
```

Pod作成用yamlの用意

```yaml:mysql-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: watashino-mysql
  labels:
    name: label-mysql
spec:
  containers:
  - name: mysql
    image: mysql:8.0.29   # このように指定するとdocker hubのMySQL 8.0.29 となります
    env:
    - name: MYSQL_USER       # 環境変数を直接指定
      value: user01          # ユーザー名
    - name: MYSQL_PASSWORD   # 環境変数を直接指定
      value: password01      # 上記にて設定するユーザーのパスワード
    - name: MYSQL_DATABASE   # 環境変数を直接指定
      value: database01      # データベース名称
    - name: MYSQL_ROOT_PASSWORD # secretを使った環境変数の指定
      valueFrom:                #
        secretKeyRef:           # secretsを参照
          name: mysql-pass      # secretsの名称
          key: password         # secretsのkey
    ports:
    - name: mysql
      containerPort: 3306    # コンテナのポート番号
      protocol: TCP
    volumeMounts:
    - name: watashino-mysql-storage
      mountPath: /var/lib/mysql
  volumes:
  - name: watashino-mysql-storage
    emptyDir: {}
```

## Pod のデプロイと確認
上記で作ったsecretとyamlを使ってPodを作成します。

```shell:デプロイ
kubectl apply -f mysql-pod.yaml
```

確認しましょう。

```shell:起動の確認
kubectl get pods
NAME                                READY   STATUS      RESTARTS   AGE
watashino-mysql                     1/1     Running     0          3m
```

Podが起動したら中身を確認します。

```shell:動作確認
kubectl exec -it pods/watashino-mysql -- /bin/sh
sh-4.4# mysql -u user01 -h localhost -p
Enter password: 
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 9
Server version: 8.0.29 MySQL Community Server - GPL

Copyright (c) 2000, 2022, Oracle and/or its affiliates.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| database01         |
| information_schema |
+--------------------+
2 rows in set (0.02 sec)

mysql> \q
Bye
sh-4.4#
```

## Pod の起動失敗の例

AMD64 対応の 8.0.28 のイメージをRaspberry Pi の Kubernetes 環境で実行起動しようとすると、起動に失敗します。

```shell:起動失敗
kubectl get pods
NAME              READY   STATUS              RESTARTS   AGE
watashino-mysql   0/1     ErrImagePull        0          29s
```

MySQL 8.0.28 をデプロイすると`no match for platform in manifest`というエラーが出力されます。これは、ARM64 プラットフォームに対応した適切なイメージが pull できない、というエラーになります。

```shell:describeして詳細確認
kubectl describe pods/watashino-mysql
Name:         watashino-mysql
Namespace:    default
Priority:     0
Node:         worker1/192.168.1.21
Start Time:   Mon, 21 Nov 2022 15:21:29 +0000
Labels:       name=label-mysql
Annotations:  <none>
Status:       Pending
IP:           10.42.1.23
IPs:
  IP:  10.42.1.23
Containers:
  mysql:
    Container ID:   
    Image:          mysql:8.0.28
    Image ID:       
    Port:           3306/TCP
    Host Port:      0/TCP
    State:          Waiting
      Reason:       ImagePullBackOff
    Ready:          False
    Restart Count:  0
    Environment:
      MYSQL_USER:           user01
      MYSQL_PASSWORD:       password01
      MYSQL_DATABASE:       database01
      MYSQL_ROOT_PASSWORD:  <set to the key 'password' in secret 'mysql-pass'>  Optional: false
    Mounts:
      /var/lib/mysql from watashino-mysql-storage (rw)
      /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-pzb75 (ro)
Conditions:
  Type              Status
  Initialized       True 
  Ready             False 
  ContainersReady   False 
  PodScheduled      True 
Volumes:
  watashino-mysql-storage:
    Type:       EmptyDir (a temporary directory that shares a pod's lifetime)
    Medium:     
    SizeLimit:  <unset>
  kube-api-access-pzb75:
    Type:                    Projected (a volume that contains injected data from multiple sources)
    TokenExpirationSeconds:  3607
    ConfigMapName:           kube-root-ca.crt
    ConfigMapOptional:       <nil>
    DownwardAPI:             true
QoS Class:                   BestEffort
Node-Selectors:              <none>
Tolerations:                 node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
                             node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
  Type     Reason     Age               From               Message
  ----     ------     ----              ----               -------
  Normal   Scheduled  46s               default-scheduler  Successfully assigned default/watashino-mysql to worker1
  Warning  Failed     21s               kubelet            Failed to pull image "mysql:8.0.28": rpc error: code = NotFound desc = failed to pull and unpack image "docker.io/library/mysql:8.0.28": no match for platform in manifest: not found
  Warning  Failed     21s               kubelet            Error: ErrImagePull
  Normal   BackOff    21s               kubelet            Back-off pulling image "mysql:8.0.28"
  Warning  Failed     21s               kubelet            Error: ImagePullBackOff
  Normal   Pulling    6s (x2 over 45s)  kubelet            Pulling image "mysql:8.0.28"
```

# 備考
## Podのアンインストール方法

```shell:コマンド
kubectl delete pods/watashino-mysql
```

## Secretのアンインストール方法

```shell:コマンド
kubectl delete secrets/mysql-pass
```