MastodonをDockerで構築(S3連携)

はじめに

マストドンの構築手順です。 2017年4月にマストドンがブームになり、さくらVPSでminioと連携で構築しましたが、 新ドメイン取得をきっかけに新たにAWSとS3で作り直したのでその時の手順を公開します。 バージョンが上がっていますので、公式のマニュアルを参考に作成しています。

Dockerをインストール

公式マニュアル: https://docs.docker.com/engine/installation/linux/docker-ce/ubuntu/#install-docker-ce

apt-get update
apt-get -y install apt-transport-https ca-certificates curl software-properties-common
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add -

コマンド解説

  • aptパッケージインデックスを更新します。
  • aptHTTPS経由でリポジトリを使用できるようにパッケージをインストールします。
  • ドッカーの公式GPGキーを追加します。

フィンガープリントを確認します。

apt-key fingerprint 0EBFCD88
pub   4096R/0EBFCD88 2017-02-22
      Key fingerprint = 9DC8 5822 9FC7 DD38 854A  E2D8 8D81 803C 0EBF CD88
uid                  Docker Release (CE deb) <docker@docker.com>
sub   4096R/F273FCD8 2017-02-22

公式レポジトリを追加します。 アーキテクチャの確認

dpkg --print-architecture
amd64

add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
apt-get update
apt-get -y install docker-ce

バージョンを指定しない場合は最新版がインストールされます。 バージョンを指定する場合は以下のようにしてバージョンを指定します。

apt-get install docker-ce=<VERSION>

dockerコマンドでテストします。 このコマンドはテストイメージをダウンロードし、コンテナ内で実行します。コンテナが実行されると情報メッセージが出力され、終了します。

docker run hello-world

インストールしたバージョンを確認します。

docker --version
Docker version 17.09.0-ce, build afdb6d4

DockerComposeのインストール

公式マニュアル:https://docs.docker.com/compose/install/#prerequisites

curl -L https://github.com/docker/compose/releases/download/1.17.0/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose
docker-compose --version
docker-compose version 1.17.0, build ac53b73

Mastodonのインストール

公式マニュアル:https://github.com/tootsuite/documentation/blob/master/Running-Mastodon/Docker-Guide.md

Mastodon一式をgitコマンドで取得

/opt ディレクトリにインストールします。 /opt はアプリケーションをインストールするときに使うディレクトリです。 インストールするバージョンはv2.0.0rc4です。

cd /opt
git clone https://github.com/tootsuite/mastodon
cd mastodon
git tag -l
...
v2.0.0rc4

git checkout refs/tags/v2.0.0rc4

docker-composeでbuild

ymlファイルを編集します。

cp -p docker-compose.yml docker-compose.yml.org
vi docker-compose.yml

このままではDocker上でしたデータが保存されません。 なのでデータの永続化設定をするため、以下の場所のコメントアウトを外します。

#    volumes:
#      - ./postgres:/var/lib/postgresql/data

  redis:
    restart: always
    image: redis:4.0-alpine
### Uncomment to enable REDIS persistance
#    volumes:
#      - ./redis:/data

設定全体は以下のようになります。

version: '3'
services:

  db:
    restart: always
    image: postgres:9.6-alpine
### Uncomment to enable DB persistance
    volumes:
      - ./postgres:/var/lib/postgresql/data

  redis:
    restart: always
    image: redis:4.0-alpine
### Uncomment to enable REDIS persistance
    volumes:
      - ./redis:/data

  web:
    build: .
    image: gargron/mastodon
    restart: always
    env_file: .env.production
    command: bundle exec rails s -p 3000 -b '0.0.0.0'
    ports:
      - "3000:3000"
    depends_on:
      - db
      - redis
    volumes:
      - ./public/assets:/mastodon/public/assets
      - ./public/packs:/mastodon/public/packs
      - ./public/system:/mastodon/public/system

  streaming:
    build: .
    image: gargron/mastodon
    restart: always
    env_file: .env.production
    command: npm run start
    ports:
      - "4000:4000"
    depends_on:
      - db
      - redis

  sidekiq:
    build: .
    image: gargron/mastodon
    restart: always
    env_file: .env.production
    command: bundle exec sidekiq -q default -q mailers -q pull -q push
    depends_on:
      - db
      - redis
    volumes:
      - ./public/system:/mastodon/public/system

ここまで設定が完了したらbuildします。少し時間がかかります。

docker-compose build

Mastdonのセットアップ

以下のコマンドでsecretを3回出力します。コンフィグファイルに記載するのでメモしておきます。

docker-compose run --rm web rake secret
【出力結果をメモ】
docker-compose run --rm web rake secret
【出力結果をメモ】
docker-compose run --rm web rake secret
【出力結果をメモ】

vapid_keyも同様に出力して、結果をメモします。

docker-compose run --rm web rake mastodon:webpush:generate_vapid_key
VAPID_PRIVATE_KEY=【出力結果をメモ】
VAPID_PUBLIC_KEY=【出力結果をメモ】

設定ファイルを編集します。

cp -p .env.production.sample .env.production
vi .env.production

# You may set DATABASE_URL instead for more advanced options
DB_HOST=db
DB_USER=postgres
DB_NAME=postgres
DB_PASS=【DBパスワードを設定】
DB_PORT=5432

# LOCAL_DOMAIN should *NOT* contain the protocol part of the domain e.g https://example.com.
LOCAL_DOMAIN=【MastodonのFQDN】
LOCAL_HTTPS=true

# Generate each with the `RAILS_ENV=production bundle exec rake secret` task (`docker-compose run --rm web rake secret` if you use docker compose)
PAPERCLIP_SECRET=【rake secretの実行結果を貼り付け】
SECRET_KEY_BASE=【rake secretの実行結果を貼り付け】
OTP_SECRET=【rake secretの実行結果を貼り付け】

SMTP_SERVER=email-smtp.us-east-1.amazonaws.com
SMTP_PORT=587
SMTP_LOGIN=【SES権限のIAMアクセスキーID】
SMTP_PASSWORD=【SES権限のIAMアクセスキー】
SMTP_FROM_ADDRESS=通知用メールアドレス(SESは設定時に確認メールが必要ので架空のアドレスは不可)

S3 (optional)
S3_ENABLED=true
S3_BUCKET=【バケット名】
AWS_ACCESS_KEY_ID=【S3権限のIAMアクセスキーID】
AWS_SECRET_ACCESS_KEY=【S3権限のIAMアクセスキー】
S3_REGION=ap-northeast-1
S3_PROTOCOL=https
S3_HOSTNAME=s3-ap-northeast-1.amazonaws.com

S3のバケット名に注意

バケット名は[.]ドットを含まない名前にしてください。例えばhoge.hogeとした場合、画像ファイルパスが 「hoge.hoge.s3-ap-northeast-1.amazonaws.com」となります。 S3のワイルドカード証明書は一つ上のサブドメインまでしか機能しないので、HTTPS接続ができなくなります。 サイト全体がHTTPSにならないので、ブラウザ上で警告が表示されます。

S3のリージョン確認

上記はリージョンが東京の場合です。リージョンの確認は次のURLから確認できます。 http://docs.aws.amazon.com/general/latest/gr/rande.html#s3_region

コンフィグの設定が完了しましたら、Mastodonをセットアップします。

docker-compose run --rm web rake db:migrate
docker-compose run --rm web rake assets:precompile

メモリ不足

syslogに以下のエラーが出力がある場合、メモリ不足でプリコンパイルに失敗しています。 t2.microのメモリ1GBではダメだったので、t2.smallのメモリ2GBに変更しました。 /var/log/syslog level=error msg=”libcontainerd: error restarting containerd: fork/exec /usr/bin/docker-containerd: cannot allocate memory”

Nginxでリバースプロキシのインストール

ここまででMastdonは起動しましたが、ブラウザからだとport3000を指定しないとアクセスできません。 また証明書を導入していないので、HTTPSのアクセスもできません。 なのでNginxでリバースプロキシとして導入します。

公式レポジトリからNginxをインストール

レポジトリを指定するファイルを作成します。

vi /etc/apt/sources.list.d/nginx.list

##対応するUbuntuリリースを$ release から置き換えてください。
deb http://nginx.org/packages/ubuntu/ $ release nginx
deb-src http://nginx.org/packages/ubuntu/ $ release nginx

Ubuntu 16.04 xenialの場合
deb http://nginx.org/packages/ubuntu/ xenial nginx
deb-src http://nginx.org/packages/ubuntu/ xenial nginx

viするのが面倒な方は以下のコマンドでどうぞ

echo "deb http://nginx.org/packages/ubuntu/dists xenial nginx" >> /etc/apt/sources.list.d/nginx.list
echo "deb-src http://nginx.org/packages/ubuntu/dists xenial nginx" >> /etc/apt/sources.list.d/nginx.list

アップデートしてインストールします。

apt-get update
apt-get -y install nginx

下記のエラーが出た場合

「The following signatures couldn’t be verified because the public key is not available: NO_PUBKEY $key」 以下のコマンドで実行してください。 apt-key adv –keyserver keyserver.ubuntu.com –recv-keys $key apt-get update apt-get -y install nginx

サービスの起動と自動起動設定をします。

systemctl start nginx
systemctl enable nginx

証明書の導入 Let’s Encrypt

前回はCentOSで導入しましたが今回はUbuntuなので手順が異なります。 またMastdonなので通常のWebサイトとは異なる手順を踏みます。

Let’sEncryptでWordPressのHTTPS対応

Let’s Encryptをインストールします。

apt-get install letsencrypt

はじめにLetsEncrypt自身のWebサーバを使ってドメイン認証と証明書の取得をする必要があるため、Nginxを停止します。

systemctl stop nginx

ドメイン認証と証明書を取得します。

letsencrypt certonly --standalone -d 【MastodonのFQDN】

Nginxの設定

Nginxに使用するDH鍵交換パラメータを事前に作成します。

mkdir /etc/nginx/ssl
openssl dhparam 2048 -out /etc/nginx/ssl/dhparam.pem

Nginxのコンフィグ設定をします。 Mastodonにてサンプル設定が公開されていますが、暗号化のssl_ciphersやHSTSの設定を変更しています。 また、ドキュメントルートはこのブログでは/opt/mastodon/publicになります。

2017年4月ごろとの違い

ドキュメントルートが変更になっています。当時はmastdon/live/publicだったのですが、 liveがなくなっているようです。

vi /etc/nginx/conf.d/mastdon.conf

map $http_upgrade $connection_upgrade {
  default upgrade;
  ''      close;
}

server {
  listen 80;
  listen [::]:80;
  server_name 【MastodonのFQDN】;
  # Useful for Let's Encrypt
  location /.well-known/acme-challenge/ { allow all; }
  location / { return 301 https://$host$request_uri; }
}

server {
  listen 443 ssl http2;
  listen [::]:443 ssl http2;
  server_name 【MastodonのFQDN】;

  ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
  ssl_prefer_server_ciphers on;
  ssl_ciphers 'kEECDH+ECDSA+AES128 kEECDH+ECDSA+AES256 kEECDH+AES128 kEECDH+AES256 kEDH+AES128 kEDH+AES256 +SHA !aNULL !eNULL !LOW !kECDH !DSS !MD5 !EXP !PSK !SRP !CAMELLIA !SEED';
  ssl_stapling on;
  ssl_session_cache    builtin:1000 shared:SSL:10m;

  ssl_certificate     /etc/letsencrypt/live/【MastodonのFQDN】/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/【MastodonのFQDN】/privkey.pem;
  ssl_dhparam /etc/nginx/ssl/dhparam.pem;

  keepalive_timeout    70;
  sendfile             on;
  client_max_body_size 0;

  root /opt/mastodon/public;

  gzip on;
  gzip_disable "msie6";
  gzip_vary on;
  gzip_proxied any;
  gzip_comp_level 6;
  gzip_buffers 16 8k;
  gzip_http_version 1.1;
  gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;

  add_header Strict-Transport-Security "max-age=31536000";

  location / {
    try_files $uri @proxy;
  }

  location ~ ^/(emoji|packs|system/accounts/avatars|system/media_attachments/files) {
    add_header Cache-Control "public, max-age=31536000, immutable";
    try_files $uri @proxy;
  }

  location /sw.js {
    add_header Cache-Control "public, max-age=0";
    try_files $uri @proxy;
  }

  location @proxy {
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto https;
    proxy_set_header Proxy "";
    proxy_pass_header Server;

    proxy_pass http://127.0.0.1:3000;
    proxy_buffering off;
    proxy_redirect off;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $connection_upgrade;

    tcp_nodelay on;
  }

  location /api/v1/streaming {
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto https;
    proxy_set_header Proxy "";

    proxy_pass http://127.0.0.1:4000;
    proxy_buffering off;
    proxy_redirect off;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $connection_upgrade;

    tcp_nodelay on;
  }

  error_page 500 501 502 503 504 /500.html;
}

設定が完了したらNginxを起動します。

systemctl start nginx

再度Let’sEncryptで証明書を再取得します。今度はドキュメントルートを指定して実行します。

letsencrypt certonly --webroot -d 【MastodonのFQDN】 -w /opt/mastodon/public/
2を選択して上書きします。
2  Renew & replace the cert (limit ~5 per 7 days)

証明書を更新したので、Nginxを再起動して再度読み込みます。

systemctl restart nginx

Mastodonへアクセス

https://【MastodonのFQDN】へアクセスしMastodonに接続します。

自分の管理者アカウントを作成します。Mastodonは管理者アカウントを直接作成するのではなく、一般アカウントを管理者権限に昇格させる手順になります。 作成したアカウントを管理者権限へ昇格する時は以下のコマンドを実行します。

docker-compose run --rm web bundle exec rails mastodon:make_admin USERNAME=【ユーザ名】

Mastodonの運用

以下のコマンドをcronに設定します。3ヶ月で証明書が切れてしまうので、月に1回程度実行すればいいと思います。

0 6 1 * *       /bin/systemctl stop nginx && /usr/bin/letsencrypt renew --force-renewal && /bin/systemctl start nginx > /dev/null 2&>1

cronについて

一度cronが正常に実行されるか必ずテストしましょう。直前になって実行されないという状況は避けましょう。

設定変更などを反映させる時

# DockerのMastodonコンテナの停止
docker-compose stop

# Mastodonのアセットファイルの再作成
docker-compose run --rm web rails assets:precompile

# DockerのMastodonコンテナ起動
docker-compose start

一度Dockerイメージを削除して作り直す時。(DBは永続化しているためそのまま)

# 停止してコンテナ削除
docker-compose down

# Mastodonイメージ作成
docker-compose build

# DBやアセットもリセットする場合は、以下2つ実行
docker-compose run --rm web rails db:migrate
docker-compose run --rm web rails assets:precompile

# 起動
docker-compose up -d

ユーザに管理者権限を付与するコマンド

docker-compose run --rm web bundle exec rails mastodon:make_admin USERNAME=ユーザ名

Mastodonバージョンアップ

cd /opt/mastodon
git fetch # リモートレポジトリをローカルへ持ってきます。
git stash # docker-compose.ymlなど変更しているファイルを退避
git tag   # バージョンタグの確認
git checkout 【バージョン】
git stash pop stash@{0} # 退避したファイルを戻します。
docker-compose pull    # Dockerコンテナの更新
docker-compose build   
docker-compose run --rm web rails db:migrate
docker-compose run --rm web rails assets:precompile
docker-compose stop && docker-compose up -d

git stashができない場合gitのセッティングができていないためです。

*** Please tell me who you are.

Run

  git config --global user.email "you@example.com"
  git config --global user.name "Your Name"

to set your account's default identity.
Omit --global to set the identity only in this repository.

fatal: unable to auto-detect email address (got 'root@xxxxxxxx')
Cannot save the current index state

以下のコマンドを実行します。

git config --global user.email 【メールアドレス】
git config --global user.name 【名前】

Mastodonを構築(2回目)してみての感想

個人的な見解ですが、お一人様などインスタンスなど小規模であればDockerを使うことをオススメします。 私が初めて構築した時はDockerを使わずminioと連携してVPS上に構築しましたが、バージョンアップやデータのバックアップが大変でした。 今はDockerで運用していますがバージョンアップなどDockerなしと比べて運用が楽になりました。

WordPressよりは敷居が上がりますが、MastodonはDockerやWebアプリケーションを学ぶいい学習素材だなぁと感じています。 周りの開発者を見るとどんどんカスタマイズしてるので、私もまだまだ勉強したいと思います。