nginx
docker
docker-compose
mastodon

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のセキュリティに対する知見がないので、おいおいその辺りは勉強していこうと思います。

参考