GKE上にMySQL/Redisを乗せて永続化させる

GKE上にMySQLとかRedisをわざわざ乗っけるならCloud SQLとかMemoryStoreとか使っといた方が耐障害性たけーよなとか思うんですが料金がお手軽じゃないです。
なので試しにGKEにのせてみることにします。 replicaは1台です。

Secret / ConfigMap の登録

---
apiVersion: v1
kind: Secret
metadata:
  name: mysql-secret
data:
  MYSQL_ROOT_PASSWORD: <BASE64_ENCODED_ROOT_PASSWORD>
  MYSQL_DATABASE: <BASE64_ENCODED_DATABASE>
  MYSQL_USER: <BASE64_ENCODED_USER>
  MYSQL_PASSWORD: <BASE64_ENCODED_PASSWORD>
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: mysql-config-file
data:
  custom.cnf: |
    [mysqld]
    default_authentication_plugin=mysql_native_password
    character-set-server=utf8mb4

    [client]
    default-character-set=utf8mb4
$ kubectl apply -f mysql-config.yml

まずMySQLの設定に必要なConfigMapとSecretをapplyしておきます。
Secretの値にはbase64エンコードされた値を入れておく必要があります。
(一応エンコードが必要ないstringDataなるフィールドもある)

$ echo -n "<TEXT>" | openssl enc -e -base64

これでエンコードできる。

Mysql Serviceの登録

---
apiVersion: v1
kind: Service
metadata:
  name: mysql
spec:
  ports:
  - port: 3306
  selector:
    app: mysql
  clusterIP: None
---
apiVersion: apps/v1 # for versions before 1.9.0 use apps/v1beta2
kind: StatefulSet
metadata:
  name: mysql
spec:
  selector:
    matchLabels:
      app: mysql
  replicas: 1
  serviceName: mysql
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
      - image: mysql:8.0.15
        name: mysql
        ports:
        - containerPort: 3306
          name: mysql
        envFrom:
        - configMapRef:
            name: mysql-config-file
        - secretRef:
            name: mysql-config
        volumeMounts:
        - name: mysql-persistent-storage
          mountPath: /var/lib/mysql
          subPath: mysql
        - name: mysql-config-volume
          mountPath: /etc/mysql/conf.d/custom.cnf
          subPath: custom.cnf
      volumes:
      - name: mysql-config-volume
        configMap:
          name: mysql-config-file
  volumeClaimTemplates:
  - metadata:
      name: mysql-persistent-storage
    spec:
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 20Gi
$ kubectl apply -f mysql.yml

volumeClaimTemplatesにClaimの情報を書き込んだ上で、そのClaimをvolumeMountsで指定してやります。 これで自動的にPersistentVolumeが立つようになります。
GKEではPersistent Volumeのデフォルトとして GCEPersistentDisk が作成されます。 デフォルトではHDDなので、SSDで作成したい場合はStorageClassを追加してspec.storageClassNameを指定する必要があります。

MySQLのデプロイでちょっとハマったんですが、マウントするストレージのディレクトリにはいくつかファイル(ディレクトリ)があるようで、MySQLは初回起動時にデータを保管するディレクトリが空でない場合にエラーを吐くため、Podが立ちませんでした。
対策として、subPath: mysql を指定することでPersistentVolumeに mysql/ ディレクトリを作成して、そのディレクトリを /var/lib/mysql にマウントするようにしています。

Redis Serviceのデプロイ

apiVersion: v1
kind: Service
metadata:
  name: redis
spec:
  ports:
    - port: 6379
  clusterIP: None
  selector:
    app: redis
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: redis
spec:
  selector:
    matchLabels:
      app: redis
  serviceName: redis
  replicas: 1
  template:
    metadata:
      labels:
        app: redis
    spec:
      containers:
      - name: master
        image: redis:alpine
        ports:
        - containerPort: 6379
        volumeMounts:
        - name: redis-persistent-storage
          mountPath: /data
          subPath: redis
  volumeClaimTemplates:
  - metadata:
      name: redis-persistent-storage
    spec:
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 1Gi
$ kubectl apply -f redis.yml

MySQLとあんまり変わらないです。 確認してみます。

# keyをセットする
$ kubectl exec -it redis-0 redis-cli
127.0.0.1:6379> set key redis
OK
127.0.0.1:6379> exit

# podを落として再度作成されるまで待つ
$ kubectl delete pod redis-0
pod "redis-0" deleted

# 新しくできたpodに入ってkeyをgetしてみる
$ kubectl exec -it redis-0 redis-cli
127.0.0.1:6379> get key
"redis"

大丈夫そうです。
このままだとスケールしませんが節約にはなります。
でも節約考えるならGKE使わなくてもいいんだよなぁ…