PostgreSQL
docker
docker-compose
mastodon
マストドン

Docker で稼働しているマストドンを Docker を使用しない環境に移行する方法

はじめに

先日、僕が運営しているマストドンインスタンス (mastodon.noraworld.jp) を Docker 環境から Docker を使わない環境 (non-Docker 環境) に移行しました。

そのときの作業が僕にとってかなり大変で、特にデータベースの移行で苦労しました。ネットで調べると、Docker でマストドンインスタンスを立てる方法や、non-Docker で立てる方法はたくさん情報が出てくるのですが、Docker で稼働しているインスタンスを non-Docker に移行する記事は、そんなにありませんでした。

今回は、僕と同じように環境の移行を検討されている人で、ネットの情報が少なく困っている人の参考になればと思い、僕が行った作業をここにまとめようと思います。参考になれば幸いです。

Docker をやめた理由

Docker は、環境を構築するのは簡単ですが、パフォーマンスやリソースの面でネックになるということが、約 4 ヶ月半 Docker でマストドンを運用していてわかったことです。

Docker は、パフォーマンスをあまり下げずに、複数の環境でほぼ同じ環境を構築することができるのが特徴です。しかし、数ヶ月マストドンインスタンスを運用していて、やはり Docker だと応答が少し遅くて、だんだん重くなってくるのが体感でわかりました。mastodon.noraworld.jp は、4 月下旬から運用を続けていましたが、8 月の下旬辺りから、気になるほど応答が遅くなってきたので、non-Docker への以降を検討しました。

また、Docker はかなり容量を食います。コンテナの中に必要なバイナリやライブラリをすべてインストールするので、サーバ直下にインストールされているものでも、別途コンテナ内に必要です。当然サーバ直下にインストールするより容量の面での効率が悪いです。コンテナの中をいくら綺麗にしても、マストドンだけで 2GB くらいは食ってしまいます。

運用していて唯一良かった点は、マストドン自体のアップデートや、各ライブラリのアップデート (セキュリティアップデート含む) などがすべて、docker-compose pulldocker-compose build だけで完結する点ですが、マストドンをアップデートするだけなら、non-Docker でも作業量は変わらないです。

マストドンの運用でわかった、僕の中での結論としては、Docker はあくまで開発環境を簡単に構築できて、しかも削除して再構築するなどが非常に簡単にできるだけで、本番環境では、最初の環境構築以外はほとんど利点がないということです。

この記事を読む前に

この記事では、以下のことを想定して書かれています。

  • 作業はすべてマストドンのディレクトリ以下で行っていること
  • バックアップを取る際のバックアップ先は ~/backup としていること
  • マストドン以外のアプリケーションで Redis を使用していないこと

これらの事柄は特に注意書きなしで説明しています。バックアップ先のディレクトリは状況に応じて変更してください。

エラーに困ったら

エラーが発生してどのように解決すれば良いかわからないときは、一番下のトラブルシューティングを確認してみてください。

またトラブルシューティングに記載されていないエラーが出た場合は、まず最初にデーモン (サービス) が起動または停止しているかをよく確認してください。

たとえば、PostgreSQL にログインしたり、Rails でマイグレーションするときなどは当然 PostgreSQL サーバが起動していないとエラーになります。その逆に、マストドンの Docker コンテナが起動しているときに PostgreSQL サーバを起動しようとしたりするとエラーになります。PostgreSQL サーバが起動している状態で、PostgreSQL 9.6 サーバを起動しようとしていた、なんていうミスもあるかもしれません。

このように、デーモンの起動/停止がエラーの原因になっていることは意外に多いです。

デーモンやコンテナの起動状況の確認

デーモンが起動しているかどうかを確認するには以下のコマンドを実行します。

$ systemctl status [サービス名]

たとえば Redis サーバが起動しているか確認したい場合は [サービス名] の箇所に redis と入れます。

マストドンの Docker コンテナが起動しているか確認するには以下のコマンドを実行します。

$ docker-compose ps

トラブルシューティングを見たり、デーモンやコンテナの状況を確認しても解決しない場合は、エラーメッセージで検索してみてください。

環境

  • CentOS 7
  • Ruby 2.4.1
  • Node.js 6.7.0
  • PostgreSQL 9.6.5
  • Redis 4.0.1
  • Mastodon v1.6.0rc1

Ruby と Bundler のインストール

サーバ直下に、まだ Ruby がインストールされていない、もしくはインストールされていても、Docker 内と同じバージョンの Ruby がインストールされていない場合はインストールします。Docker 内と違うバージョンの Ruby を使用しても大きな問題は出ないかもしれませんが、あまり古いとエラーになる可能性があるので同じバージョンのインストールをおすすめします。

Docker 内の Ruby のバージョンを確認します。

$ cat .ruby-version
2.4.1

上記の例では 2.4.1 なので、Ruby 2.4.1 をインストールします。

Ruby のインストールには rbenv を利用します。rbenv の導入および必要なライブラリ等に関しては「【CentOS7(+Ubuntu16)】Ruby / Rails のインストールから Rails サーバの起動までの(ほぼ)完全ガイド」を参考にしてください。

$ rbenv install 2.4.1
$ rbenv rehash

Ruby がインストールできたら Bundler をインストールします。

$ gem install bundler

Yarn のインストール

Yarn がインストールされていない場合はインストールします。Yarn をインストールするためにはまず Node.js をインストールする必要があります。

Node.js のインストールに関しては ndenv を使用することを個人的におすすめします。ndenv 以外のインストール方法でも構いませんが、以降の説明では ndenv を使用してインストールしたと想定して説明を進めます。その際は各々読み替えてください。ndenv の導入および Node.js のインストールに関しては「ndenv を使用して複数のバージョンの Node.js を管理する方法と基本的な使い方」を参考にしてください。

ちなみに Node.js のバージョンについては特に指定はないですが、v6.7.0 を使用したところ、問題なく動作しました。

Node.js がインストールされると、一緒に npm もインストールされます。npm を使って Yarn をインストールします。

$ npm install -g yarn

bundle install

Bundler で、必要な Gem をインストールします。その前に、必要なネイティブライブラリをインストールします。

$ sudo yum -y install libidn-devel protobuf-lite-devel postgresql-contrib

今回僕が bundle install のためにインストールしたのは上記だけですが、おそらく他にもインストールが必要なライブラリがあります。bundle install で失敗したときに、どの Gem をインストールしようとして失敗したのかが表示されるので、その Gem のインストール方法を検索して、各自必要なネイティブライブラリをインストールしてください。この辺は、すでに僕が別の機会にインストールしてしまって、ここで何が必要なのかをすべて把握仕切れないので、投げやりな感じになってしまって申し訳ないです。

bundle install を実行します。

$ bundle install --deployment --without development test

production 環境なので、developmenttest のみで必要な Gem はスキップします。

yarn install

yarn install を実行します。

$ yarn install --pure-lockfile

Redis のインストール

Redis は以下のようにインストールすることはできます。

# 個人的に非推奨
$ sudo yum -y install epel-release
$ sudo yum -y install redis

ところがこの方法でインストールしたところ、Docker で使用していた Redis のダンプファイルを適用してデーモンを起動することができませんでした。エラーメッセージが、バージョンに関することだったことから、Docker 内で使用していた Redis と互換性が保たれないためにデーモンが起動できなかったと思われます。また、上記のコマンドでは、CentOS のバージョンによっては、パッケージが見つからないと言われインストールできません。

そのため、REMI リポジトリを利用してインストールします。

$ sudo yum -y install epel-release
$ wget http://rpms.famillecollet.com/enterprise/remi-release-7.rpm
$ sudo rpm -Uvh remi-release-7*.rpm
$ sudo yum -y --enablerepo=remi,remi-test,epel install redis

参考: CentOS7.xに最新版のRedisをインストールする最も簡単な方法

Redis のインストールが完了したら、remi-release-7.rpm は削除して大丈夫です。

PostgreSQL のインストール

重要: Docker 内の PostgreSQL と同じバージョンものを使用することを強く推奨します

ここはかなり重要なことなので、慎重に判断してください。PostgreSQL のバージョンが、Docker 内のものと異なる場合、互換性の理由で、バックアップをリストアする際にエラーが発生することがあります。エラーを無視しても最低限重要なデータはリストアできるのですが、一部のデータが消失してしまう可能性があります。僕が試したときには、全ユーザのお気に入り一覧、認証済みアプリ一覧、画像リンクの参照データなどが消失してしまいました。

もちろんデータベースに詳しい人なら、PostgreSQL のバージョンアップ、バージョンダウンによる互換性の問題を修正する術をご存知かと思いますが、そうでない人にとってはかなりリスキーな手段です。ちなみに僕は上記の問題を自力で解決することができませんでした。

なので、PostgreSQL は Docker 内と同じバージョンのものをインストールすることを強く推奨します。この記事では、Docker 内の PostgreSQL のバージョンを確認して、それと同じバージョンのものをインストールする方法を説明します。

バージョンの確認

Docker 内で使用されている PostgreSQL のバージョンを確認するには以下のファイルを見ます。

$ sudo cat postgres/PG_VERSION
9.6

上記の例では PostgreSQL 9.6 が使用されていることがわかりました。

以降の説明では、PostgreSQL 9.6 を使用することを前提としています。もし、表示されたバージョンが 9.6 でない場合は 9.696 となっている箇所を適宜読み替えてください

postgres/PG_VERSION が見つからない場合

もし、マストドンのディレクトリの中に postgres というディレクトリがない場合は、Docker コンテナ内のデータの永続化がされていないと考えられます。その場合は、以下のように、Docker コンテナ内に入って、ファイルを確認します。

# PG_VERSION が見つからない場合のみ
$ docker-compose up -d
$ docker exec -it mastodon_db_1 /bin/bash
$ su - postgres
$ cat data/PG_VERSION
9.6
$ exit
$ exit

該当するバージョンの PostgreSQL をインストール

上記の例では 9.6 だったので、PostgreSQL 9.6 をインストールします。9.6 のインストールに必要なリポジトリを追加し、インストールします。

$ sudo rpm -Uvh https://yum.postgresql.org/9.6/redhat/rhel-7-x86_64/pgdg-centos96-9.6-3.noarch.rpm
$ sudo yum -y install postgresql96 postgresql96-devel postgresql96-contrib postgresql96-server

参考: How to Install PostgreSQL 9.6 on CentOS/RHEL 7/6/5 and Fedora 25/24/23

9.6 ではない場合は、9.696 となっている部分を、該当する数字に置き換えてください。うまくいかない場合は、該当バージョンのインストール方法を各自調べてください。

PostgreSQL 関連のコマンドのバージョンが異なる場合

上記の例では PostgreSQL 9.6 をインストールしたので、関連するコマンドも 9.6 であるべきですが、そうなっていない場合があります。それは、古いバージョンまたは、別のバージョンの PostgreSQL がすでにインストールされていたからです。

これを確認するにはたとえば psql コマンドのバージョンを調べます。

$ psql --version
psql (PostgreSQL) 9.2.18

9.6 をインストールしたにもかかわらず 9.2 となっています。このような状態になっていると、今後いろいろと不都合が生じるので、修正します。以下のコマンドを実行して、バージョン 9.6 のコマンドがインストールされたバイナリのパスを通します。

# バージョンが異なる場合のみ
$ echo 'export PATH="/usr/pgsql-9.6/bin:$PATH"' >> ~/.bash_profile

通したパスを反映させます。

$ exec -l $SHELL

これでもう一度、psql コマンドのバージョンを確認します。インストールした PostgreSQL のバージョンになっていることを確認してください。

$ psql --version
psql (PostgreSQL) 9.6.5

他にも、pg_restore を使用するので、バージョンがそろっているか念のため確認してください。

$ pg_restore --version

マストドンサービスファイルの作成

マストドンを起動するのに必要なサービスファイルを作成します。作成するサービスは以下の 3 つです。

  • mastodon-web.service
  • mastodon-sidekiq.service
  • mastodon-streaming.service

/etc/systemd/system/ 以下に上記の 3 つのサービスファイルを作成します。

/etc/systemd/system/mastodon-web.service
[Unit]
Description=mastodon-web
After=network.target

[Service]
Type=simple
User=mastodon
WorkingDirectory=/home/mastodon/live
Environment="RAILS_ENV=production"
Environment="PORT=3000"
ExecStart=/bin/bash -lc 'bundle exec puma -C config/puma.rb'
TimeoutSec=15
Restart=always

[Install]
WantedBy=multi-user.target
/etc/systemd/system/mastodon-sidekiq.service
[Unit]
Description=mastodon-sidekiq
After=network.target

[Service]
Type=simple
User=mastodon
WorkingDirectory=/home/mastodon/live
Environment="RAILS_ENV=production"
Environment="DB_POOL=5"
ExecStart=/bin/bash -lc 'bundle exec sidekiq -c 5 -q default -q mailers -q pull -q push'
TimeoutSec=15
Restart=always

[Install]
WantedBy=multi-user.target
/etc/systemd/system/mastodon-streaming.service
[Unit]
Description=mastodon-streaming
After=network.target

[Service]
Type=simple
User=mastodon
WorkingDirectory=/home/mastodon/live
Environment="NODE_ENV=production"
Environment="PORT=4000"
ExecStart=/home/mastodon/.ndenv/shims/npm run start
TimeoutSec=15
Restart=always

[Install]
WantedBy=multi-user.target

以下の 3 つを環境に合わせて変更してください。

実行ユーザが mastodon ではない場合

User の箇所を該当ユーザに書き換えてください

マストドンのあるディレクトリが /home/mastodon/live ではない場合

WorkingDirectory の箇所を、マストドンのディレクトリになるようにフルパスで指定してください。

起動する npm のパスが異なる場合

ndenv を使用した場合は特に気にする必要はありません。しかしそれ以外の方法で Node.js (npm) をインストールした場合は、適切なパスに書き替える必要があります。

まずは npm のパスを確認してください。

$ which npm
~/.ndenv/shims/npm

mastodon-streaming.service の ExecStart のパスは、ここで表示されたパスに置き換えてください。ただし、~/home/ユーザ名 と読み替えてください。

途中でサービスファイルを変更する場合

ここで定義したサービスファイルを使って起動し、後からタイプミス等があったとしてファイルの内容を変更した場合は、デーモンをリロードする必要があります。

$ sudo systemctl daemon-reload

苦労話

上記のファイルは、マストドン公式リポジトリのプロダクションガイドに書かれていたものを参考にしているのですが、なぜかこのガイドのサービスファイルだと、エラーが発生してデーモンが起動できませんでした。(厳密には起動するのですが、その数秒後に謎のエラーが出て落ちてしまいます)

エラーログを細かく見ながらひたすら調べていたら、やっと解決策が見つかりました。

Doesn't seem to work with production mode on DigitalOcean

この通りにファイルを変更したら問題なく動作しました。なぜこれでうまくいくのか、そしてなぜプロダクションガイドの例ではうまくいかないのかはわかりませんが、この解決策にたどり着くまで数時間かかりました…

PostgreSQL と Redis のデータのバックアップとリストア

【重要】PostgreSQL と Redis のデータは、必ず同時に、同時期のデータのバックアップを取ること

これは PostgreSQL のバージョンに次いで重要なことです。Docker 内で使用していた PostgreSQL と Redis のデータをサーバ直下に置くわけですが、このとき、稼働していた時期がそれぞれ別々の状態のデータをリストアしてしまうと予期せぬ問題が発生します。僕のインスタンスで起こった問題は、ホームタイムラインが空になり、何も表示されない状態になってしまったことです。

たとえば、9 月 5 日に取った PostgreSQL のバックアップデータと、9 月 6 日に取った Redis のバックアップデータを同時に使用してはならないということです。必ず同時期に稼働していた状態のデータを使用してください。具体的には、Docker で稼働している PostgreSQL と Redis のデータは、一緒にバックアップを取らないといけません。

Docker が稼働している状態で、PostgreSQL のデータをリストアして、しばらく経ったあとに Redis のデータをリストアしてしまうと、PostgreSQL のリストアをしてから Redis のリストアをする間にこれらのデータが書き換わってしまうので、同時期のデータではなくなってしまいます。

そのため、PostgreSQL と Redis のデータをリストアする際には、まずマストドンの Docker コンテナを停止させてから、同じタイミングで行う必要があります。

【重要】9 月 11 日より前に記載していたバックアップとリストアに関して

この記事では、PostgreSQL のバックアップとリストアの推奨方法として、マストドンディレクトリ以下の postgres と redis ディレクトリの中身をそのままコピーする、という紹介をしていました。

しかし、これは結果的にバッドケースでした。この方法で mastodon.noraworld.jp の移行メンテナンスを行って、この記事を書いたのですが、その後、このインスタンス内で、一部のユーザアカウントが分裂したり、特定のユーザのトゥートが流れず、検索したりメンションを送ったりできなくなるという不具合が発生してしまいました。

この不具合の原因は、マストドンの開発コアメンバーに診てもらったところ、データベース内のインデックスが壊れているということがわかりました。インデックスが壊れたことにより、アカウントのスクリーンネームとドメインが全く同じレコードは存在してはいけないというユニーク制約を無視してレコードが追加されたことで、アカウントが 2 つに分裂するなどの不具合が起こりました。

参考: Something is wrong with DB of my instance #4856

本来このようなことは起こるはずがないのですが、Docker 内で使われていたデータベースファイルをそのままコピーすると、このような不具合を招く可能性が出てきてしまいます。そのため、そのようなバックアップ方法はおすすめしません。併せて、この記事内のその説明を削除しました。ファイルをそのままコピーしてバックアップを取るのではなく、以降の説明で紹介する pg_dumppg_restore を利用したバックアップとリストアを行ってください。

マストドンへのアクセスを拒否

以降の説明で pg_dump コマンドを使用します。このコマンドを使用した PostgreSQL のバックアップ方法を「論理バックアップ」と呼びます。論理バックアップは、PostgreSQL サーバが起動している最中に実行しなければなりません。なので、バックアップをしている最中にデータベースが書き換わって、整合性が失われることがないように、ここでマストドンへの外部アクセスを止める必要があります。

アクセスを止めるには、Web サーバ (Nginx) を停止するのが一番手っ取り早いですが、すべてのサービスを止めたくない場合は、マストドンの server コンテキストに deny all; を追加して、マストドンに対するすべてのアクセスを一時的に拒否してください。

server {
    server_name マストドンで使用しているドメイン;

+   deny all;
}

deny all; を追加したら、シンタックスエラーがないか確認して、リロードします。

$ sudo nginx -t
$ sudo nginx -s reload

PostgreSQL の論理バックアップ

Docker 内のデータベースのバックアップを取ります。マストドンの Docker コンテナが起動していない場合は、起動します。

$ docker-compose up -d

DB コンテナに接続します。

$ docker exec -it mastodon_db_1 /bin/bash

postgres ユーザでログインします。

$ su - postgres

論理バックアップを行います。バックアップファイル名を mastodon_docker.dump とします。

$ pg_dump -Fc -U username dbname > mastodon_docker.dump

usernamedbname にはそれぞれユーザ名と DB 名を入れます。ユーザ名と DB 名を確認するには、.env.production ファイル内の DB_USERDB_NAME を見てください。

生成されたバックアップファイルはカスタムアーカイブ形式と呼ばれます。他にもスクリプト形式や tar 形式などがありますが、カスタムアーカイブ形式は、ファイルサイズを小さくできる上に、後からスクリプト形式に変化させることもできるので、汎用性が高いです。カスタムアーカイブ形式が個人的におすすめです。

参考: 論理バックアップ(pg_dump と pg_dumpall)
参考: PostgresSQLのbackup, restore方法まとめ

バックアップを取ったら、Docker コンテナからログアウトします。

$ exit
$ exit

Docker コンテナにあるバックアップファイルを、サーバ直下に移動させます。

$ docker cp mastodon_db_1:/var/lib/postgresql/mastodon_docker.dump ~/backup

Redis のバックアップ

PostgreSQL の次は Redis のデータをバックアップします。Redis コンテナに接続します。

$ docker exec -it mastodon_redis_1 /bin/sh

データのバックアップを取るのですが、接続した直後のディレクトリ (/data) にはすでに dump.rdb があるかと思います。もしこのファイルがある場合は別の名前に変更します。

# dump.rdb が存在する場合
$ mv dump.rdb dump.rdb.old

redis-cli コマンドでバックアップを取ります。

$ redis-cli save

すると、dump.rdb というファイルが生成されます。生成されていたら、Docker コンテナからログアウトします。

$ exit

先ほどと同じように、Docker コンテナにあるバックアップファイルを、サーバ直下に移動させます。

$ docker cp mastodon_redis_1:/data/dump.rdb ~/backup

mastodon_docker.dumpdump.rdb をバックアップしたら、Docker コンテナを停止させます。

$ docker-compose stop

PostgreSQL のリストア

もし、今回はじめて PostgreSQL 9.6 をインストールした場合は、初期化を行う必要があります。以前から PostgreSQL 9.6 を使用していた場合は実行する必要はありませんし、実行するとエラーになります。

# はじめて PostgreSQL 9.6 を使用する場合
$ sudo /usr/pgsql-9.6/bin/postgresql96-setup initdb

また、マストドンのアプリケーションからアクセスできるように、/var/lib/pgsql/9.6/data/pg_hba.conf を変更します。これは PostgreSQL 9.6 をすでに使用していた場合も、念のため確認してください。ファイルを編集する際は root 権限が必要です。

/var/lib/pgsql/9.6/data/pg_hba.conf
# TYPE  DATABASE        USER            ADDRESS                 METHOD

# "local" is for Unix domain socket connections only
- local   all             all                                     peer 
+ local   all             all                                     trust
# IPv4 local connections:
- host    all             all             127.0.0.1/32            ident
+ host    all             all             127.0.0.1/32            trust
# IPv6 local connections:
- host    all             all             ::1/128                 ident
+ host    all             all             ::1/128                 trust

参考: PostgreSQLのerror FATAL: Ident authentication failed for user

この状態で、PostgreSQL 9.6 サーバを起動して、状態を確認します。

$ sudo systemctl start postgresql-9.6
$ systemctl status postgresql-9.6

active となっていれば起動しています。

先ほどバックアップしたファイルを PostgreSQL のデータがあるディレクトリにコピーします。

$ sudo cp ~/backup/mastodon_docker.dump /var/lib/pgsql

バックアップファイルの所有者を postgres に変更します。

$ sudo chown postgres:postgres /var/lib/pgsql/mastodon_docker.dump

postgres ユーザにログインして、psql で SQL が実行できる状態にします。

$ sudo -u postgres psql

以下の SQL 文を実行します。

sql> CREATE USER username CREATEDB;
sql> CREATE DATABASE dbname WITH TEMPLATE = template0 ENCODING = 'UTF8' LC_COLLATE = 'en_US.utf8' LC_CTYPE = 'en_US.utf8';
sql> ALTER DATABASE dbname OWNER TO username;
sql> \q

usernamedbname は変更してください。

再び、postgres ユーザでログインします。

$ sudo su - postgres

バックアップファイルをリストアします。

$ pg_restore -U username -C -d dbname mastodon_docker.dump --schema=public

参考: pg_restore で --table オプションを指定しないほうがよい
参考: PostgreSQL: 非特権ユーザーによる pg_dump と pg_restore

usernamedbname は変更してください。

色々なオプションをつけていますが、このオプションの通りに実行すれば、おそらくエラーは出ないはずです。PostgreSQL のバージョンが、Docker 内とこことで異なる場合は、互換性の問題によりエラーが出力されるかもしれません。これが、同じバージョンの PostgreSQL をインストールすることを推奨する理由です。

特にエラーがなければ、これでマストドンのデータを復帰させることができました。postgres からログアウトします。

$ exit

一旦、PostgreSQL 9.6 サーバを停止させます。

$ sudo systemctl stop postgresql-9.6

Redis のリストア

【重要】リストアを行う前に、もし Redis サーバがすでに起動していた場合は、必ず停止してからリストアしてください。

$ sudo systemctl stop redis

Redis のバックアップをリストアします。こちらは PostgreSQL のようにリストアするためのコマンドはありません。単にファイルを移すだけです。

$ sudo cp ~/backup/dump.rdb /var/lib/redis

所有者を redis に変更します。

$ sudo chown redis:redis /var/lib/redis/dump.rdb

これだけかと心配になりますが、これで大丈夫です。簡単ですね。

ちなみにサーバ直下で、他のアプリケーションで、すでに Redis を使用していた場合は、これだと他のアプリケーションのデータが消えてしまいます。Docker で使用していたマストドンの Redis データと、すでにサーバ直下で別のアプリケーション用に使われていた Redis データをどのようにリストアするかは、他の記事を調べてください。

マストドンの環境設定ファイルを編集

Docker から non-Docker に移行したことで、マストドンの環境設定ファイルを変更する必要があります。具体的には、REDIS_HOSTDB_HOSTlocalhost に変更します。

.env.production
- REDIS_HOST=redis
+ REDIS_HOST=localhost
- DB_HOST=db
+ DB_HOST=localhost

今後、何かしらの事情でマストドン Docker コンテナを一時的に起動しないといけない場合は、サーバ直下の PostgreSQL, Redis, mastodon-web, mastodon-sidekiq, mastodon-streaming を停止させたあとに、必ず上記のファイルを Docker の環境のものに書き換えるのを忘れないでください。

マイグレーション

PostgreSQL サーバを起動してマイグレーションを実行します。

$ sudo systemctl start postgresql-9.6
$ RAILS_ENV=production bundle exec rails db:migrate
$ sudo systemctl stop postgresql-9.6

プリコンパイル

Docker で運用していた場合は、public/assetspublic/packspublic/system の所有者、所有グループが Docker のものになっているので、まずは所有者を変更します。その後、プリコンパイルを実行します。

$ sudo chown -R $(whoami):$(whoami) public/assets public/packs public/system
$ RAILS_ENV=production bundle exec rails assets:precompile

今後、何かしらの事情でマストドン Docker コンテナを一時的に起動しないといけない場合は、サーバ直下の PostgreSQL, Redis, mastodon-web, mastodon-sidekiq, mastodon-streaming を停止させたあとに、必ず public/assetspublic/packspublic/system の所有者、所有グループの変更を忘れないでください。

デーモンの起動

マストドンの Docker コンテナが起動している場合は、デーモンを起動することができないので、コンテナが起動している場合は停止します。

$ docker-compose stop

コンテナを停止させたら、いよいよサービスを起動させます。

$ sudo systemctl start postgresql-9.6 redis mastodon-web mastodon-sidekiq mastodon-streaming

デーモンの状態を確認

すべてのデーモンが起動しているか確認します。

$ systemctl status postgresql-9.6 redis mastodon-web mastodon-sidekiq mastodon-streaming

すべてのサービスの状態が active になっていれば OK です。

デーモンの自動起動を有効

すべてのデーモンが正しく起動していることを確認したら、サーバをリブートしたときにこれらのサービスが自動で起動されるように設定を有効にしましょう。

$ sudo systemctl enable postgresql-9.6 redis mastodon-web mastodon-sidekiq mastodon-streaming

マストドンへのアクセスを許可

拒否していたマストドンへのアクセスを許可します。

server {
    server_name マストドンで使用しているドメイン;

-   deny all;
}

シンタックスエラーがないか確認して、再起動します。

$ sudo nginx -t
$ sudo nginx -s reload

正しく起動しているかどうか、またデータがちゃんとリストアできているかどうかを、実際に Web にアクセスして確認してみてください。個人のトゥートやタイムライン、通知一覧、お気に入り一覧、設定、自己紹介文、サイト説明文、画像、認証済みアプリの一覧など、すべてのデータが正しく読み取れれば成功です! お疲れさまでした!

後日談

不要になった Docker コンテナを削除

non-Docker 環境で問題なく稼働したことを確認したら、もうマストドンの Docker コンテナは必要ありません。もし、もう一度 Docker で稼働させたくなったとしても、データの永続化をしていれば、またいつでも Docker 稼働に戻すことができます。

現状、マストドンの Docker コンテナが動いていなければ空き容量を増やすために削除してしまったほうが良いでしょう。以下のコマンドで、現在稼働していないコンテナ、現在使われていないイメージ、ボリュームを削除することができます。

$ docker system prune -a

これで、マストドンで使用していた Docker コンテナが削除されます。ちなみに僕のサーバの環境では約 3GB ほど開放されました。これは結構大きいです。

さらに数日経った 9 月 13 日の話ですが、サーバ内で Docker コンテナで稼働させていたすべての Web サービスを non-Docker 環境に移行しました。今回の件で、Docker は本番環境には向いていないなと思い、もうサーバでは Docker を使うことはないだろうと思ったので、Docker そのものをアンインストールしました。すると、マストドンの Docker コンテナを削除する前から累計して、約 15GB も空き容量ができました。Docker はとにかく容量を食うようです。

cronjob の修正

もし、Docker でマストドンを稼働させていたときに、何かしらのタスクを、docker または docker-compose コマンドで定期的に実行していた場合は、それを Docker を使わないコマンドで置き換える必要があります。

たとえば

$ docker-compose run --rm web rails mastodon:remove_remote

というコマンドを cron で実行していた場合は

$ RAILS_ENV=production bundle exec rails mastodon:remove_remote

に変更する必要があります。また、注意すべきなのは、RAILS_ENV=production をつけないといけないことです。Docker のコマンドのときにはつけていなかったので意外に忘れるポイントです。

トラブルシューティング

この記事の手順を進めていくに当たって、エラーが発生したときには、まずこちらを参考にしてください。それでも解決しなければ、他の記事をあたってください。

FATAL: Ident authentication failed for user

マイグレーションや、その他データベースに関する処理を行った際にこのようなエラーが出る場合があります。

このエラーが起きた場合は /var/lib/pgsql/9.6/data/pg_hba.conf を開き、以下の該当する箇所を変更してください。

/var/lib/pgsql/9.6/data/pg_hba.conf
# TYPE  DATABASE        USER            ADDRESS                 METHOD

# "local" is for Unix domain socket connections only
- local   all             all                                     peer 
+ local   all             all                                     trust
# IPv4 local connections:
- host    all             all             127.0.0.1/32            ident
+ host    all             all             127.0.0.1/32            trust
# IPv6 local connections:
- host    all             all             ::1/128                 ident
+ host    all             all             ::1/128                 trust

すべて trust に変更します。

変更したら、PostgreSQL を再起動して、変更を反映させます。

$ sudo systemctl stop postgresql-9.6
$ sudo systemctl start postgresql-9.6

参考: PostgreSQLのerror FATAL: Ident authentication failed for user

Data directory is not empty!

$ sudo /usr/pgsql-9.6/bin/postgresql96-setup initdb

上記コマンドを実行したときに起こる可能性のあるエラーです。これは PostgreSQL を初期化しようとしたときに、すでにデータが入っていて初期化できないことを意味します。なので、このエラーが表示されたということは、このコマンドを実行する必要はなかった、ということになります。

それでも、PostgreSQL サーバが起動できず、どうしてもこのコマンドを実行したい場合は、先に PostgreSQL のデータを削除または別の場所に退避する必要があります。

$ sudo mv /var/lib/pgsql/9.6/data ~/backup

その後、該当のコマンドを実行してください。

参考: Postgres CentOS and Amazon Linux initdb error: Data directory is not empty! [FAILED]

ERROR: permission denied to create database

おそらくこのエラーに遭遇することはないと思います。もしこのエラーに当たった場合は、何かを間違えている (勘違いやコマンドミス、実行順序ミス等の) 可能性が高いので、実行したコマンドの履歴を見ておかしなところがないか確認してください。

FATAL: role "username" is not permitted to log in

"username" の箇所にはデータベースのロールが入ります。

これはエラーの内容通り、ログインする権限がないということです。まずは PostgreSQL に接続します。

$ sudo systemctl start postgresql-9.6
$ sudo -u postgres psql

以下のコマンドで権限を与えます。

sql> ALTER ROLE "username" WITH LOGIN;
sql> \q

"username" は読み替えてください。

参考: Superuser is not permitted to login, PostgreSQL: role is not permitted to log in

We're sorry, but something went wrong

サービスを立ち上げて Web にアクセスしたときにこのエラーページが出たら、以下の 4 点を確認してください。

Docker コンテナが立ち上がっていないか

原因はわからないのですが、docker-compose stop して止めたはずなのに、non-Docker への移行をしているときの何らかのタイミングで勝手にマストドンの Docker コンテナが立ち上がるときがあります。原因はよくわかりませんが、このときはマストドン Docker コンテナを止めましょう。

$ docker-compose stop

.env.production を書き換え忘れていないか

これはよくあるミスです。うっかり忘れてしまいます。REDIS_HOSTDB_HOST を、ともに localhost に変更しましょう。

- REDIS_HOST=redis
+ REDIS_HOST=localhost
- DB_HOST=db
+ DB_HOST=localhost

assets, packs, system の所有者が docker になっていないか

public/assetspublic/packs の所有者、所有グループが Docker のものになっていると、CSS や JS ファイルを読み取ることができないので、このエラーページが出力されます。また、public/system が読み取れないと、画像を読取ることができません。所有者、所有グループを変更して、読み取れる状態にしましょう。

$ sudo chown -R $(whoami):$(whoami) public/assets public/packs public/system

マイグレーションやプリコンパイルをし忘れていないか

マイグレーションやプリコンパイルを忘れるとこのエラーページが表示されることがあります。マイグレーションに関しては、データベースを移行する前にすでに行われていた場合はする必要はないですが、不安なら念のためやっておくと良いでしょう。

$ RAILS_ENV=production bundle exec rails db:migrate
$ RAILS_ENV=production bundle exec rails assets:precompile

上記の 4 点を確認したら、PostgreSQL, Redis, mastodon-web, mastodon-sidekiq, mastodon-streaming を再起動します。

$ sudo systemctl stop postgresql-9.6 redis mastodon-web mastodon-sidekiq mastodon-streaming
$ sudo systemctl start postgresql-9.6 redis mastodon-web mastodon-sidekiq mastodon-streaming

失敗歴 (おまけ)

今回の環境の移行は、僕の知識が浅かったせいで、様々な不具合に直面しました。その失敗歴を個人的な戒めとして残しておきます。

1 回目

標準 Yum リポジトリで PostgreSQL をインストールしたところ、Docker 内よりも古いバージョンがインストールされてしまった。その状態で、Docker 内の PostgreSQL のデータをスクリプト形式で論理バックアップして、サーバ直下にリストアしたところ、一部の SQL が互換性の問題でエラーになり、全ユーザのお気に入り一覧、認証済みアプリ一覧、画像リンクの参照データなどが復元できない不具合が発生した。

2 回目

Docker 内と同じバージョンの PostgreSQL をインストールした。今回は論理バックアップではなく、Docker 内で使用されていた PostgreSQL のデータファイルを直接移行し、データを復元した。ところが、その際に Redis のダンプファイルを、1 回目でリストアしたものをそのまま使用していたためデータの不整合が生じ、ホームタイムラインが空になり、何も表示されない不具合が発生した。

3 回目

PostgreSQL と Redis のデータの不整合が生じないように、同時期に使用していたデータを移行した。しかし、2 回目と同じく、PostgreSQL の論理バックアップをせず、データファイルをそのまま移行したことが原因で、データベース内のインデックスが壊れ、一部のユーザアカウントが分裂したり、特定のユーザのトゥートが流れず、検索したりメンションを送ったりできなくなるという不具合が発生した。

  • PostgreSQL と Redis のデータのバックアップとリストア
  • マストドンの環境設定ファイルを編集
  • マイグレーション
  • プリコンパイル
  • デーモンの起動
  • 後日談
  • トラブルシューティング
  • 失敗歴 (おまけ)