Docker構成のMastodonに後からpgBouncerを組み込む

背景

mastodon.juggler.jp はここ数日はサーバの人数1100〜1050、1日間のアクティブユーザ270人くらいで人数はあまり変わってません。しかしユーザ間のフォローが増えるにつれて、発言、ブースト、お気に入りで発生するFan-outが大量にsidekiqのキューに積まれるのが目立ってきました。特にdefaultキューに処理が溜まるとユーザへの応答性が低下します。

Push通知は計算よりも通信待機が多いのでキューを処理するスレッドを増やすのが効果的です。
それに必要なのがCPU、メモリ、そしてDBサーバへの接続数です。

今回はDBサーバへの接続数を稼ぐために、DBサーバとアプリケーションの間にpgBouncerを設置してみます。

期待される効果

Docker構成のMastodon で使われているDBサーバの初期設定では max_connectionが100 になっていますが、この接続一つごとにプロセスを一つ使ってしまいます。

pgBouncerを使ってDB接続の中継を行うと、アプリ側に必要な接続数を増やしつつもDBサーバ側のプロセス数を節約することができます。

pgbouncerイメージの取得

pgbouncerイメージの取得と設定ファイルの雛型の抽出を行います。

docker run --rm --name test-pgbouncer -it gavinmroy/alpine-pgbouncer sh
でコンテナ中にログインできたら、以下の作業を行います。

Mastodonの停止とコンテナの削除

あらかじめdbサーバはデータをホスト側のフォルダに出して永続化しておいてください。
このあたりは前回の記事で説明しています。 http://d.hatena.ne.jp/tateisu/20170416/1492341633
また、データベースにはMastodonに必要なデータが既に一通り定義されているものとします。

docker-compose down

Mastodonの停止と関連コンテナの削除が行われます。

データベースのサービス定義の名前変更

docker-compose.ymlを編集します。サービス定義の内、名前が「db:」となっているものを「db_backend:」という名前に変更します。

pgbouncer設定ファイルの用意

.env.production にある DB_で始まる設定値をテキストエディタにコピーしておきます。

docker-compose.ymlがあるフォルダの下に pgbouncerフォルダを作成します。
その中に pgbouncer.ini と userlist.txt を作成します。
パーミッションと内容は先ほどメモしたものに合わせてください。

userlist.txt には「"bouncer" "(新パスワード)" 」という行を記述します。パスワードは適当に生成してください。

pgbouncer.iniには以下の編集を行います。

[database]セクションにバックエンドへの接続を書きます。
「postgres = host=db_backend port=5432 dbname=postgres user=postgres password=(DBパスワード)」

  • hostに指定するのは、データベースサーバのサービス定義の名前です。先ほどdb_backendに変更しましたね?
  • port,dbname,user,passwordに指定するのは、これまでDB接続に使っていたのと同じ情報です。先ほど .env.production からメモしましたね?

[pgbouncer]セクションの以下の情報を編集します。

listen_addr = 0.0.0.0
listen_port = 5432
auth_type = plain
auth_file = /etc/pgbouncer/userlist.txt
pool_mode = transaction
max_client_conn = 900
default_pool_size = 20

pgBouncerのサービス定義を追加

docker-compose.ymlを編集します。以下のようなサービス定義を追加します。

  db:
    restart: always
    image: gavinmroy/alpine-pgbouncer
    volumes:
      - ./pgbouncer:/etc/pgbouncer
    expose:
      - "5432"
    depends_on:
      - db_backend

pgbouncer.ini のlisten_post とdocker-compose.ymlの exposeの指定が同じポート番号か確認しましょう。

.env.production の編集

.env.production の設定値を、bouncerへの接続に適したものに変更します。

  • DB_HOSTに指定する名前は、docker-compose.ymlのpgBouncer用サービス定義の名前と一致させます。
  • DB_NAMEに指定する名前はpgbouncer.ini の[database]セクションに指定した接続情報の左端の名前と一致させます。
  • DB_USERに指定する名前はuserlist.txt に指定したユーザと一致させます。
  • DB_PASSに指定するパスワードはuserlist.txt に指定した新パスワードと一致させます。
  • 末尾にある PREPARED_STATEMENTS=false をアンコメントします。

データベースを利用するアプリはこれらの環境変数を使って接続するようになります。

動作確認

docker-compose up -d して問題がなければ成功です。

うまくいかない場合は以下をチェックしてください

  • docker ps -a で出るポート指定は期待通りになっているか。expose漏れはないか。
  • docker-compose logs -f db でログイン周りのエラーが出ていないか
  • docker-compose logs -f db_backend でログイン周りのエラーが出ていないか
  • docker-compose logs -f で prepared statement に関するエラーが出ていないか。PREPARED_STATEMENTS=false を設定し忘れてないか

関連記事