EKS(on Fargate)でEFSにデータを永続化

EKSでは積極的にRDS等のマネージドサービスでデータを保存したいところですが、種々の事情により永続ボリュームを用意しなければならないこともあります。
先日、PostgreSQLコンテナのデータをEFSを用いて永続化してみたので以降に手順をまとめます。

Amazon EFS CSI driverのデプロイ

リンク先の「Option B」を参考にAmazon EFS CSI driverをデプロイします。

Use persistent storage in Amazon EKS

この手順を終えると以下のようにEFSファイルシステムが出来上がります。

f:id:hiroki-sawano:20201101170114p:plain

永続ストレージの設定

次のマニフェストStorageClassPersistentVolumeおよびPersistentVolumeClaimを作成します。
PVの spec.sci.volumeHandle には前述の手順で作成したEFSのファイルシステムIDを指定します。
PVの spec.capacity.storage やPVCの spec.resources.requests.storageKubernetesの必須項目であるため設定が必要ですが、EFSは伸縮自在なファイルシステムであるため実際に容量は制限されません。

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: efs-sc
provisioner: efs.csi.aws.com
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: efs-pv
spec:
  capacity:
    storage: 5Gi
  volumeMode: Filesystem
  accessModes:
    - ReadWriteMany
  persistentVolumeReclaimPolicy: Retain
  storageClassName: efs-sc
  csi:
    driver: efs.csi.aws.com
    volumeHandle: fs-xxxxxxxx
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: efs-claim
spec:
  accessModes:
    - ReadWriteMany
  storageClassName: efs-sc
  resources:
    requests:
      storage: 5Gi
$ kubectl apply -f efs.yml
storageclass.storage.k8s.io/efs-sc created
persistentvolume/efs-pv created
persistentvolumeclaim/efs-claim created

EFSをマウントするDeploymentの作成

EFSでデータを永続化するPostgreSQLコンテナを用意します。
使用するコンテナイメージは以下のエントリで紹介したCentOS7ベースのPostgreSQLです(バージョンは9.6に変更)。
起動時にエントリポイントでDBクラスタを作成( initdb )します。

hiroki-sawano.hatenablog.com

DBクラスタディレクト/var/lib/pgsql/9.6/data (以降、PGDATA)に persistent-storage と名付けたボリュームをマウントします。

apiVersion: apps/v1
kind: Deployment
metadata:
  ...
spec:
  ...
  template:
    ...
    spec:
      containers:
      - name: db
        ...
        volumeMounts:
        - mountPath: /var/lib/pgsql/9.6/data
          name: persistent-storage
      volumes:
      - name: persistent-storage
        persistentVolumeClaim:
          claimName: efs-claim
$ kubectl apply -f db.yml
deployment.apps/db created

PGDATAの書き込みに失敗

ログを確認してみるとPGDATAを initdb で初期化する際、権限不足でエラーとなっていました。
ボリュームはrootでマウントされるので、実行ユーザであるpostgresが書き込み権限を持っていなかったわけです。

$ kubectl logs <db-pod-name>
...
initdb: could not change permissions of directory "/var/lib/pgsql/9.6/data": Operation not permitted
...

initContainersで所有者変更

fsGroupでいける気がしましたが、どうにもうまくいかなかったのでinitContainersを使います。

stackoverflow.com

db コンテナが起動する前に change-data-dir-ownership コンテナでPGDATAの所有者を 26:26 ( postgres:postgres )に変更します。

注意: postgresのUIDとGIDは環境により異なる場合があります(PostgreSQL: Re: Default UID for postgres user in linux

apiVersion: apps/v1
kind: Deployment
metadata:
  ...
spec:
  ...
  template:
    ...
    spec:
      initContainers:
      - name: change-data-dir-ownership
        image: alpine:3
        command:
        - chown
        - -R
        - 26:26
        - /var/lib/pgsql/9.6/data
        volumeMounts:
        - name: persistent-storage
          mountPath: /var/lib/pgsql/9.6/data
      containers:
      ...
$ kubectl delete -f db.yml
$ kubectl apply -f db.yml

正常起動の確認

これでデータベースの初期化に成功し、コンテナが正常稼働しました。

$ kubectl logs -f  <db-pod-name>
...

fixing permissions on existing directory /var/lib/pgsql/9.6/data ... ok
creating subdirectories ... ok
selecting default max_connections ... 100
selecting default shared_buffers ... 128MB
selecting default timezone ... UTC
selecting dynamic shared memory implementation ... posix
creating configuration files ... ok
running bootstrap script ... ok
performing post-bootstrap initialization ... ok
syncing data to disk ... ok

Success. You can now start the database server using:

    pg_ctl -D /var/lib/pgsql/9.6/data -l logfile start


WARNING: enabling "trust" authentication for local connections
You can change this by editing pg_hba.conf or using the option -A, or
--auth-local and --auth-host, the next time you run initdb.
waiting for server to start....< 2020-10-27 05:01:48.580 UTC >LOG:  redirecting log output to logging collector process
< 2020-10-27 05:01:48.580 UTC >HINT:  Future log output will appear in directory "pg_log".
 done
server started
CREATE DATABASE
...
waiting for server to shut down.... done
server stopped
< 2020-10-27 05:02:24.104 UTC >LOG:  redirecting log output to logging collector process
< 2020-10-27 05:02:24.104 UTC >HINT:  Future log output will appear in directory "pg_log".

データベースの一覧を確認します。

$ kubectl exec -it <db-pod-name> -- psql -l
                             List of databases
   Name    |  Owner   | Encoding | Collate | Ctype |   Access privileges   
-----------+----------+----------+---------+-------+-----------------------
 postgres  | postgres | UTF8     | C       | C     | 
 template0 | postgres | UTF8     | C       | C     | =c/postgres          +
           |          |          |         |       | postgres=CTc/postgres
 template1 | postgres | UTF8     | C       | C     | =c/postgres          +
           |          |          |         |       | postgres=CTc/postgres
 your_db   | postgres | UTF8     | C       | C     | 
(4 rows)

PGDATA以下のファイルとディレクトリも確認しておきます。

$ kubectl exec -it  <db-pod-name> -- ls /var/lib/pgsql/9.6/data
PG_VERSION    pg_dynshmem    pg_multixact  pg_stat  pg_xlog
base          pg_hba.conf    pg_notify     pg_stat_tmp  postgresql.auto.conf
global        pg_ident.conf  pg_replslot   pg_subtrans  postgresql.conf
pg_clog       pg_log         pg_serial     pg_tblspc    postmaster.opts
pg_commit_ts  pg_logical     pg_snapshots  pg_twophase  postmaster.pid

再デプロイし、DB初期化処理がスキップされ、かつ前回作成したデータベースが存在することを確認します。

$ kubectl delete -f db.yml
$ kubectl apply -f db.yml
$ kubectl logs <db-pod-name>
< 2020-10-27 05:18:17.340 UTC >LOG:  redirecting log output to logging collector process
< 2020-10-27 05:18:17.340 UTC >HINT:  Future log output will appear in directory "pg_log".

$ kubectl exec -it <db-pod-name> -- psql -l
                             List of databases
   Name    |  Owner   | Encoding | Collate | Ctype |   Access privileges   
-----------+----------+----------+---------+-------+-----------------------
 postgres  | postgres | UTF8     | C       | C     | 
 template0 | postgres | UTF8     | C       | C     | =c/postgres          +
           |          |          |         |       | postgres=CTc/postgres
 template1 | postgres | UTF8     | C       | C     | =c/postgres          +
           |          |          |         |       | postgres=CTc/postgres
 your_db   | postgres | UTF8     | C       | C     | 
(4 rows)