Docker・k8sでデータベースを実運用

MySQL・PostgreSQLなどのDBMSは、1OSインスタンスに1つだけセットアップする構成が一般的なため、複数のDB環境を切り替えたい場合や、別バージョンの動作検証をしたい場合などにコンテナを活用すると便利です。

dockerやkubernetes上でDBMSを運用する際、以下の点には注意が必要です。

  • あらかじめvolume設定をしておかないとデータは永続化せずインスタンスとともに揮発する
  • dockerイメージのバージョンを明示的に指定しないと、意識していないタイミングでlatestが指す最新版にどんどん置き換わっていく。メジャーバージョンが切り替わると起動しない可能性が高い

volumeの指定

DBMSのデータは、各ソフトのデータディレクトリに格納されるため、このディレクトリをdockerのvolumeに指定しておくことで、ホストOSのファイルシステムにオンラインデータを保持できます。

MySQL・PostgreSQL公式イメージのデータディレクトリはそれぞれ以下のPathです。

  • MySQL: /var/lib/mysql
  • PostgreSQL: /var/lib/postgresql/data

DBコンテナの初回起動時に、ホストOS上に空ディレクトリを作成し、volume設定でデータディレクトリにマップしてコンテナ起動すれば初期化されます。

このデータディレクトリは物理バックアップのターゲットです。ファイル取得はOS側のファイル操作が可能ですが、データリカバリなど前後の手順は各DBMSの手順に従います。

PostgreSQLのvolume構成に注意

PostgreSQLの公式イメージは、データディレクトリ(PGDATA)として/var/lib/postgresql/dataが指定されていますが、このPATHにvolumeをmountすると起動失敗したり再起動時にデータ消失したりといったケースがあります。ext4のlost+found/ディレクトリが干渉したり、インスタンス終了時に/var/lib/postgresql/data/が消去されるという事象のようです。

適切に動作した構成としては、/var/lib/postgresqlにディスクをmountし、PGDATAに/var/lib/postgresql/data/pgdataを指定すると期待どおりにデータ保持する動作となりました。

データディレクトリに別デバイスを利用する場合は、PGDATAにサブディレクトリを追加する方が安全なようです。

論理バックアップリストア

論理バックアップを利用する場合、データをホストOSとコンテナ間で転送する必要があり、以下の3つの方法があります。

  • A: ホストOSにDBクライアントツールをインストールして、コンテナ上のDBサーバーに接続
  • B: volume指定したディレクトリに論理バックアップファイルを置き、コンテナ上のDBクライアントでバックアップ/リストア
  • C: Bと同様、コンテナ上のDBクライアントでバックアップ/リストアするが、ホスト=コンテナ間のファイル転送に docker container cpを利用
  • D: docker execを利用して、パイプで転送

このうち、一番手軽なのはDのdocker exec経由で転送する方式です。

以下のように標準入出力を接続することで、ホスト上のファイルをコンテナに転送できます。

リストアの際には、-iオプション(標準入力を接続)が必要です。

docker exec pg\_container pg\_dump -Upostgres target_db > backup.sql 
cat backup.sql | docker exec -i pg_container psql -Upostgres

kubernetesの場合にもkubectlコマンドで同様の操作が可能です。

kubectl exec pg\_pod pg\_dump -Upostgres target_db > backup.sql 
cat backup.sql | kubectl exec -i pg_pod psql -Upostgres

コンテナ上の論理バックアップ・リストア方式

なお、AのホストOS上のDBクライアントから接続する場合、ファイル転送を考える必要がない点はメリットですが、サーバーとクライアントのバージョンが合わずに実行できないケースがあります。

逆に、B, CのようにDBMSサーバー同梱のコマンドを利用する場合には、互換性の問題はなくなりますが、論理バックアップのファイル転送が別途必要になります。DBコンテナにログインする手順は、 docker exec を使いこなすを参照してください。

バージョンアップの基本的な手順

DBMSのメジャーバージョンアップ時には、旧バージョンのデータディレクトリを新しいインスタンスが読めず起動時に異常終了するケースがよくあります。

このケースでは、コンテナが起動しなかった場合にdocker logs some_containerでログを確認すると、データに互換性がないという趣旨のメッセージが残されています。

PostgreSQL・MySQLとも、一番確実性の高いバージョンアップ手段は、論理バックアップ・リストアを経由して再構築することです。

  1. 従来バージョンのdockerイメージでDBインスタンスを起動。docker runコマンドのイメージ名にmysql:5.6やpostgres:9.4などのバージョンタグを指定することにより旧版で起動できる
  2. mysqldumpコマンドやpg_dumpコマンドで論理バックアップを取得。旧版のコンテナは使わなくなるので、ダンプファイルはコンテナ外(ホスト上)に保存する必要あり。
  3. 旧バージョンのdockerコンテナを終了
  4. MySQL・PostgreSQL新バージョンのdockerコンテナを起動。データディレクトリを新たに指定してDBを新規作成する
  5. mysqlコマンドやpsqlコマンドでバックアップデータをリストアする

場合によっては論理バックアップを取得しなくても、データディレクトリを新バージョンでから読むだけでupgradeできる場合もあります。この手法が使えるケースでも、前提として直前のインスタンス停止が異常終了でないことが求められる場合が多いため、起動できない場合は旧版のコンテナで一度起動→正常終了する必要があります。

また、エクスポート→インポートする際にはバックアップデータを確保できているため、メジャーバージョンアップ直後に動作確認できたらコンテナ再起動してデータが継続しているか確認した方が安全です。

前述のように、わかりづらいvolume構成ミスによりデータロストが起きるケースがあります。コンテナは揮発性の挙動をとるため、データ消失リスクが潜在したまま運用してしまうと被害が拡大しかねません。

中馬崇尋
Chuma Takahiro