Edited at

Docker🐳でMastodon🐘のインスタンスを立てるドン (リバースプロキシにnginx-proxy + letsencrypt-nginx-proxy-companionを使う)

More than 1 year has passed since last update.

Mastodonが流行っているので、自分のVPSでインスタンスを立ち上げてみました。こちらです。

mstdn.brdr.jp - BORDER/mstdn

サーバにDockerとNginxをインストールして、という感じのやり方をされている人が多い印象ですが、NginxもDockerで構築してみます。

また、nginx-proxyとletsencrypt-nginx-proxy-companionを使ったリバースプロキシを別に立ち上げ、1つのサーバで複数のサービスを運用できるようにしました。

メールの配信サービスはSparkPostを利用してます。


ネットワークを作成する

色々試したんですが、frontとback-mstdnという2種類のネットワークをあらかじめ作っておくといい感じに動作しました。



  • front: リバースプロキシと、Mastodon(や他のサービス)を繋ぐためのネットワーク


  • back-mstdn: Mastodonの各コンテナを繋ぐためのネットワーク

サービスが増えるとback-hoge、back-fugaのようにbackが増えていくことを想定してみました。

# ネットワークを作成する

$ docker network create --driver bridge front
$ docker network create --driver bridge back-mstdn


リバースプロキシのdocker-compose.ymlを作成する


proxy/docker-compose.yml

version: '2'

services:
proxy:
image: jwilder/nginx-proxy:alpine
container_name: proxy-nginx
ports:
- 80:80
- 443:443
restart: always
tty: false
privileged: true
volumes:
- ./certs:/etc/nginx/certs:ro
- /var/run/docker.sock:/tmp/docker.sock:ro
- /etc/nginx/vhost.d
- /usr/share/nginx/html
networks:
- front

letsencrypt:
image: jrcs/letsencrypt-nginx-proxy-companion
container_name: proxy-letsencrypt
restart: always
tty: false
privileged: true
volumes:
- ./certs:/etc/nginx/certs:rw
- /var/run/docker.sock:/var/run/docker.sock:ro
volumes_from:
- proxy
networks:
- front

networks:
front:
external: true


リバースプロキシの方はこれだけです。

letsencrypt-nginx-proxy-companionが、Let's Encryptを使ったSSL証明書の発行や更新をいい感じにおこなってくれます。

発行されたSSL証明書などは、docker-compose.ymlと同じ階層のcertsディレクトリ以下に保存されます。


Mastodonのdocker-compose.ymlを編集

Mastodonのリポジトリをcloneして、docker-compose.ymlを編集していきます。

何を編集するかというと、


  • Nginxコンテナを新たに追加する


    • ポートは適当に9090番で



  • 各コンテナをback-mstdnネットワークで繋ぐ


    • Nginxはリバースプロキシとやり取りするので、frontとback-mstdnの両方のネットワークに繋ぐ



  • データを永続化するために、dbとredisのvolumesのコメントアウトを解除

という感じです。


service/mastodon/docker-compose.yml

version: '2'

services:
nginx:
image: nginx:1.11.10-alpine
container_name: mstdn-nginx
ports:
- 9090:9090
restart: always
tty: false
env_file: .env.production
links:
- web
- streaming
volumes:
- ./setting/nginx/conf.d:/etc/nginx/conf.d:ro
- ./setting/nginx/conf:/etc/nginx/conf/:ro
volumes_from:
- container:proxy-nginx
networks:
- front
- back-mstdn

db:
restart: always
image: postgres:alpine
container_name: mstdn-db
volumes:
- ./postgres:/var/lib/postgresql/data
networks:
- back-mstdn

redis:
restart: always
image: redis:alpine
container_name: mstdn-redis
volumes:
- ./redis:/data
networks:
- back-mstdn

web:
restart: always
build: .
image: gargron/mastodon
container_name: mstdn-web
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/system:/mastodon/public/system
networks:
- back-mstdn

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

sidekiq:
restart: always
build: .
image: gargron/mastodon
container_name: mstdn-sidekick
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
networks:
- back-mstdn

networks:
front:
external: true
back-mstdn:
external: true



Mastodonに追加したNginxの、volumes_fromの設定

volumes_from:

- container:proxy-nginx

こんな設定がMastodonのNginxのところにあります。これは、リバースプロキシで作成したSSLの証明書と秘密鍵をMastodon Nginxからもアクセスできるようにするための設定です。

Mastodon Nginxの中に入って確認すると、/etc/nginx/certsというディレクトリができていて、リバースプロキシのcertsディレクトリと内容が同期されるようになります。


Nginxの設定ファイルを追加

mastodon/setting/nginx/conf.d/default.confを作成して、Production guideにあるような感じで、Nginxの設定をやっていきます。

SSLの設定をやっておかないとリモートフォローが動かないので、ちゃんと設定していきます。

Production Guideの例では、HTTPでのアクセス(80番ポート)をHTTPS(443番ポート)にリダイレクトするような記述があります。ただ、今回はそういうのはリバースプロキシのnginx-proxyが全部やってくれるので、9090番ポートにいきなりHTTPSでのアクセスが飛んでくることを想定して設定を変更していきます。


mastodon/setting/nginx/conf.d/default.conf

map $http_upgrade $connection_upgrade {

default upgrade;
'' close;
}

server {
listen 9090 ssl;
server_name mstdn.brdr.jp;

ssl_protocols TLSv1.2;
ssl_ciphers EECDH+AESGCM:EECDH+AES;
ssl_ecdh_curve prime256v1;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;

ssl_certificate /etc/nginx/certs/mstdn.brdr.jp/fullchain.pem;
ssl_certificate_key /etc/nginx/certs/mstdn.brdr.jp/key.pem;

# 以下省略


ssl_certificate     /etc/nginx/certs/mstdn.brdr.jp/fullchain.pem;

ssl_certificate_key /etc/nginx/certs/mstdn.brdr.jp/key.pem;

ssl_certificateとssl_certificate_keyは、リバースプロキシのNginxに保存されている証明書と秘密鍵をそれぞれ指定します。


.env.productionを編集

.env.production.sampleをコピー&リネームした、.env.productionを編集していきます。Mastodonの設定もなんですが、リバースプロキシ用の環境変数も追記していきます。

編集すべきところを抜粋していきます。


mastodon/.env.production

# リバースプロキシ用の設定

# 一番上とかに追記
VIRTUAL_HOST=mstdn.brdr.jp
VIRTUAL_PORT=9090
VIRTUAL_PROTO=https
LETSENCRYPT_HOST=mstdn.brdr.jp
LETSENCRYPT_EMAIL=mail@example.com
LETSENCRYPT_TEST=false

# 探して編集
LOCAL_DOMAIN=example.com
LOCAL_HTTPS=true

# 探して編集
# `docker-compose run --rm web rake secret`を3回実行して、出力された文字列をそれぞれ入れる
PAPERCLIP_SECRET=
SECRET_KEY_BASE=
OTP_SECRET=

# 探して編集
# SparkPostの場合です
SMTP_SERVER=smtp.sparkpostmail.com
SMTP_PORT=587
SMTP_LOGIN=SMTP_Injection
SMTP_PASSWORD=<API Key>
SMTP_FROM_ADDRESS=<好きなアドレス>@mail.example.com


こんな感じです。

nginx-proxyは超便利で、VIRTUAL_HOSTに設定したドメインへのアクセスがあった場合、自動でVIRTUAL_PORTで設定したポート番号へのアクセスを待ち受けているサーバに転送してくれます。

VIRTUAL_PROTO=httpsの設定はとても大事で、リバースプロキシからバックエンドへの接続をHTTPSでやってくれるようになります。ここに説明があります。

VIRTUAL_PROTO=httpsを設定しないと、MastodonへのアクセスがHTTP(9090番ポート)になってしまうので、先程のNginxの設定ではうまく処理できず、「400 Bad Request The plain HTTP request was sent to HTTPS」みたいなエラーが出てしまいます。

「じゃあProduction Guideの通りリダイレクトすればええやん」という感じなんですけど、うまく設定できずリダイレクトループが発生してしまったので、この形に落ち着きました。


SparkPostの設定

下記の投稿の通りにやると、ばっちりです!(力尽きた)

文系非エンジニアでも立てられるマストドン(Mastodon) CentOS7+Docker+SparkPost


リバースプロキシとMastodonを起動するドン

以上で設定ができたので、リバースプロキシとMastodonを起動しましょう。

# リバースプロキシの起動

$ cd ~/proxy
$ docker-compose up -d

# Mastodonの起動
$ cd ~/service/mastodon
$ docker-compose up -d

nginx-proxyは最高なので、今後、Mastodon以外のサービスを作って起動した場合も、VIRTUAL_HOSTとVIRTUAL_PORTをバックエンドの環境変数に設定しておくだけで、起動や変更を検知して自動でいい感じにアクセスを振り分けてくれます。


以上

サーバサイドの知識が全然ない僕ですが、なんとかMastodonのインスタンスを立ち上げることができました!

ドメインでアイデンティティを確立していきたいと思います。ちなみに僕のアカウントは以下なので、良かったらフォローしてください。

Ryo Nakae - BORDER/mstdn

DockerやNginxのセキュリティに対する知見がないので、おいおいその辺りは勉強していこうと思います。


参考