DockerでMastodonのインスタンスを立てる。とりあえずメールサーバーはホストOSで。

VPSでMastodonのインスタンスを立ち上げてみました。
Qiitaの記事を参考に構築したのですが一部想定どおりに動作しなかったので他のサイトを参考に変更しました。
メールの配信は、とりあえずホストOS上にPostfixを構築してSMTP認証なしで行っています。

#ネットワークを作成する
メールの配信はホストOS上のPostfixで行い、かつ、Postfixの構築を簡単にするためにSMTP認証なしにします。
ホストOSのPostfixから見るとコンテナ上で動作するMastodonは他サーバーで動作しているように見えるので、サブネットを指定してDockerのネットワークを作成してPostfixのmynetworksに設定します。
リバースプロキシとWebサーバー間のネットワークを172.20.0.0/16、Mastodon内のネットワークを172.21.0.0/16とします。

```
docker network create --driver bridge --subnet 172.20.0.0/16 front
docker network create --driver bridge --subnet 172.21.0.0/16 mstdn
```

#リバースプロキシの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ディレクトリ以下に保存されます。

proxyのdocker-compose.ymlのあるディレクトリで

```
docker-compose build
docker-compose up -d
```

で、proxyの各コンテナをビルドして起動します。

#Mastodonのdocker-compose.ymlを編集

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

1. Nginxコンテナを追加する
1. 画像等をNginxで返すようにするためNginxのコンテナでassets,packs,systemをマウントする
1. Nginxのポート番号は9091とする
1. 各コンテナをmstdnネットワークで繋ぐ
1. Nginxはリバースプロキシとやり取りするので、frontとmstdnの両方のネットワークに繋ぐ
1. データを永続化するために、dbとredisのvolumesのコメントアウトを解除
1. コンテナのTimeZoneをAsia/Tokyoにするために/etc/localtimeをマウントする

```
version: '2'
services:

  nginx:
    image: nginx:1.11.10-alpine
    container_name: mstdn-nginx
    ports:
      - 9091:9091
    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
      - ./public/assets:/mastodon/public/assets
      - ./public/packs:/maastodon/public/packs
      - ./public/system:/mastodon/public/system
      - /etc/localtime:/etc/localtime:ro

    volumes_from:
      - container:proxy-nginx
    networks:
      - front
      - mstdn

  db:
    restart: always
    image: postgres:alpine
    container_name: mstdn-db
    volumes:
      - ./postgres:/var/lib/postgresql/data
      - /etc/localtime:/etc/localtime:ro

    networks:
      - mstdn

  redis:
    restart: always
    image: redis:alpine
    container_name: mstdn-redis
    volumes:
      - ./redis:/data
      -/etc/localtime:/etc/localtime:ro

    networks:
      - mstdn

  app:
    build: .
    image: gargron/mastodon
    container_name: mstdn-app
    restart: always
    env_file: .env.production
    volumes:
      - /etc/localtime:/etc/localtime:ro
    networks:
      - mstdn

  web:
    extends:
      service: app
    container_name: mstdn-web
    restart: always
    env_file: .env.production
    command: bundle exec rails s -p 3000 -b '0.0.0.0'
    depends_on:
      - db
      - redis
    volumes:
      - ./public/assets:/mastodon/public/assets
      - ./public/packs:/mastodon/public/packs
      - ./public/system:/mastodon/public/system
    networks:
      - mstdn

  streaming:
    extends:
      service: app
    container_name: mstdn-streaming
    restart: always
    env_file: .env.production
    command: npm run start
    depends_on:
      - db
      - redis
    networks:
      - mstdn

  sidekiq:
    extends:
      service: app
    container_name: mstdn-sidekiq
    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
    networks:
      - mstdn

networks:
  front:
    external: true
  mstdn:
    external: true
```

#mstdn-nginxコンテナのNginxの設定ファイルを追加

mastodonのdocker-compose.ymlがあるディレクトリにsetting/nginx/conf.d/default.confを作成します。
※sample.localは実際のサイトのドメインに置き換えてください。

```default.conf
map $http_upgrade $connection_upgrade {
  default upgrade;
  ''      close;
}

server {
  listen 9091 ssl;
  server_name mastodon.sample.local;

  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/mastodon.sample.local/fullchain.pem;
  ssl_certificate_key /etc/nginx/certs/mastodon.sample.local/key.pem;

  keepalive_timeout    70;
  sendfile             on;
  client_max_body_size 0;

　root /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;

  location / {
    try_files $uri @proxy;
  }

  location ~ ^/(assets|system/media_attachments/files|system/accounts/avatars) {
    add_header Cache-Control "public, max-age=31536000, immutable";
  }

  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://web: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://streaming: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;
}
```

#.env.productionを編集
.env.production.sampleをコピー&リネームした、.env.productionを編集していきます。Mastodonの設定とリバースプロキシ用の環境変数も追記します。
smtpサーバーはホストOS上に構築したpostfixを使用するのでMastodon内のネットワークに指定したサブネット172.21.0.0/16の172.21.0.1を設定します。また、smtp認証はしないのでユーザー名とパスワードは設定しません。
編集するところを抜粋します。

```
# リバースプロキシ用の設定
VIRTUAL_HOST=mastodon.sample.local
VIRTUAL_PORT=9091
VIRTUAL_PROTO=https
LETSENCRYPT_HOST=mastodon.sample.local
LETSENCRYPT_EMAIL=mail@sample.local
LETSENCRYPT_TEST=false

# Service dependencies
# You may set REDIS_URL instead for more advanced options
REDIS_HOST=redis
REDIS_PORT=6379
# You may set DATABASE_URL instead for more advanced options
DB_HOST=db
DB_USER=postgres
DB_NAME=postgres
DB_PASS=
DB_PORT=5432
# Federation
LOCAL_DOMAIN=mastodon.sample.local
LOCAL_HTTPS=true

# Use this only if you need to run mastodon on a different domain than the one u
sed for federation.
# You can read more about this option on https://github.com/tootsuite/documentat
ion/blob/master/Running-Mastodon/Serving_a_different_domain.md
# DO *NOT* USE THIS UNLESS YOU KNOW *EXACTLY* WHAT YOU ARE DOING.
# WEB_DOMAIN=mastodon.example.com

# Application secrets
# Generate each with the `rake secret` task (`docker-compose run --rm web rake s
ecret` if you use docker compose)
PAPERCLIP_SECRET=`docker-compose run --rm web rake secret`を実行して出力された文字列を設定する
SECRET_KEY_BASE=`docker-compose run --rm web rake secret`を実行して出力された文字列を設定する
OTP_SECRET=`docker-compose run --rm web rake secret`を実行して出力された文字列を設定する
                        ・
                        ・
# Leaving them blank is not enough for authentication method 'none'.
SMTP_SERVER=172.21.0.1
SMTP_PORT=25
SMTP_LOGIN=
SMTP_PASSWORD=
SMTP_FROM_ADDRESS=mstdn@sample.local
#SMTP_DOMAIN= # defaults to LOCAL_DOMAIN
#SMTP_DELIVERY_METHOD=smtp # delivery method can also be sendmail
SMTP_AUTH_METHOD=plain
#SMTP_CA_FILE=/etc/ssl/certs/ca-certificates.crt
SMTP_OPENSSL_VERIFY_MODE=none
SMTP_ENABLE_STARTTLS_AUTO=false
```

#config/environments/production.rbの修正
.env.productionにSMTP_LOGINとSMTP_PASSWORDを設定していなくてもSMTPサーバーにuser_nameとpasswordを送信してエラーになるのでuser_nameとpasswordをコメントアウトします。

```
　　　　　　　　　　 ・
　　　　　　　　　　 ・
  # E-mails
  config.action_mailer.smtp_settings = {
    :port           => ENV['SMTP_PORT'],
    :address        => ENV['SMTP_SERVER'],
#    :user_name      => ENV['SMTP_LOGIN'],
#    :password       => ENV['SMTP_PASSWORD'],
    :domain         => ENV['SMTP_DOMAIN'] || config.x.local_domain,
    :authentication => :plain,
  }
　　　　　　　　　　 ・
　　　　　　　　　　 ・
```

#Google Analyticsを導入します
[Google Analyticsを導入します](http://qiita.com/gorillab/items/79b0d64d86e10a02085a,"Google Analyticsを導入します")を参照してGoogle Analyticsを導入します。

#次回アップデートのために修正した「app/views/home/index.html.haml」をリポジトリに追加してコミットする。

```
git add app/views/home/index.html.haml
git commit -m "V1.4.1" ...　とりあえず現在のバージョンをコメントにする。
```

#Postfixの設定
main.cfを変更します。
1. mynetworksに172.21.0.0/16を追加します。
2. 受信先で迷惑メールに振り分けられないようにsmtpd_use_tlsとsmtp_use_tlsをyesにします。

```
                 ・
                 ・

# TLS parameters
smtpd_tls_cert_file=/etc/ssl/certs/ssl-cert-snakeoil.pem
smtpd_tls_key_file=/etc/ssl/private/ssl-cert-snakeoil.key
smtpd_use_tls=yes
smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache
smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache
smtp_use_tls=yes
                 ・
                 ・
mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128 172.0.0.0/8 172.21.0.0/16
```

#SPFレコードの設定
受信先で迷惑メールに振り分けられないようにDNSにSPFレコードを設定します。
※xxx.xxx.xxx.xxxはホストOSのip4アドレス,yyyy:yyyy:yyyy:yyyy::yyyy:yyyyはホストOSのip6アドレスです。

```
sample.local.      IN TXT "v=spf1 +a:sample.local +ip4:xxx.xxx.xxx.xxx ip6:yyyy:yyyy:yyyy:yyyy::yyyy:yyyy +mx -all"
```

以上で設定は終了です。

#mastodonをビルドしてデータベースを初期設定する

mastodonのdocker-compose.ymlのあるディレクトリで

```
docker-compose build
docker-compose run --rm web rails db:migrate
docker-compose run --rm web rails assets:precompile
docker-compose up -d
```

を実行することで、

1. mastodonの各コンテナをビルド
2. データベースを初期化
3. アセットをプリコンパイル
4. mastodonの各コンテナを起動

起動まで時間がかかりますので、ログを参照します。画面のスクロールが止まるまで待ちます。

```
docker-compose logs -f
```

ctrl-fで中断できます。

#管理者ユーザーを設定する
管理者ユーザーとして使うユーザーを登録して、ログインした後に、管理者権限を設定する。

```
docker-compose run --rm web rails mastodon:make_admin USERNAME=管理者ユーザーとするユーザーID
```

ログインしてサイトの設定をします。

#更新情報
##mstdn-nginxコンテナのNginxの設定ファイルの誤りを訂正

```default.conf
  location /api/v1/streaming {
         ・
         ・
    proxy_pass http://web:4000;
                 ↓
    proxy_pass http://streaming:4000;
```

##次回アップデートのために修正した「app/views/home/index.html.haml」をリポジトリに追加してコミットする。

```
git add app/views/home/index.html.haml
git commit -m "V1.4.1" ...　とりあえず現在のバージンをコメントにする。
```


#参考にした情報
- [Docker🐳でMastodon🐘のインスタンスを立てるドン (リバースプロキシにnginx-proxy + letsencrypt-nginx-proxy-companionを使う)](http://qiita.com/ryo_dg/items/e0cc93e6a8688e5116c8,"Docker🐳でMastodon🐘のインスタンスを立てるドン (リバースプロキシにnginx-proxy + letsencrypt-nginx-proxy-companionを使う)")
- [GitHub](https://github.com/tootsuite/documentation/blob/master/Running-Mastodon/Production-guide.md,"GitHub")
- [Mastodon(マストドン)の確認メールを自前メールサーバー(postfix)で送信する](http://scriptlife.hacca.jp/contents/programming/2017/04/26/post-2278/,"Mastodon(マストドン)の確認メールを自前メールサーバー(postfix)で送信する")
- []()
- [Dockerでホストと同じタイムゾーンをコンテナ起動時に設定する](http://qiita.com/ganta/items/a0f34866c994ebaeef69,"Dockerでホストと同じタイムゾーンをコンテナ起動時に設定する")
- [Mastodon のインスタンス作成時につまづいたところメモ](http://qiita.com/rerofumi/items/025f458089d462846a5f,"Mastodon のインスタンス作成時につまづいたところメモ")