僕もマストドンを立てた系Qiita記事が書きたかった（AWS使用・Docker不使用）


最近、マストドンが各所で立っていますね。
[tootsuite/mastodon](https://github.com/tootsuite/mastodon)

[たていすのメモ - Mastodonを設置したときのメモ](http://d.hatena.ne.jp/tateisu/20170416/1492341633)
[PixivInside - 実際に運用してみてわかった、大規模Mastodonインスタンスを運用するコツ](http://inside.pixiv.blog/harukasan/1284)
[Qiita - Dockerを利用しないでマストドン(Mastodon)をCentOSにインストールする](http://qiita.com/bezeklik/items/1a8530d530613acd665c)

これらの記事を読んでるとなんだか羨ましくなったので、手探りで自分のAWS上にも立ててみました。
この記事ではその作業の顛末をご紹介します。

【注意！】下記は、絶対にベストプラクティスではありません。コピペは自己責任でお願いします。

## 要件
- とりあえず動くマストドンがどんなものか見たい
- 外部からのアクセス制限をかけたい
- ユーザー数は最大20人程度

## 構成
![Untitled Diagram (3).png](https://qiita-image-store.s3.amazonaws.com/0/51625/617e9ef1-1b63-3962-1f3f-5a3b5b1d6300.png "Untitled Diagram (3).png")

シンプルですね。

## 手順
### ドメイン取得
今回はサブドメインを使用したため新規には取得しませんでしたが、実運用の際には必要になるかと思います。
（japari.com は当然ながらすでに取られたあとでした）

### EC2 インスタンス
とりあえず動けばいいので適当に t2.micro で AWS Linux を起動しましたが、Pawoo 規模になると他インスタンス（記事によるとc4）への変更が必須みたいですね。
また、セキュリティグループなどの設定は各々の環境によると思いますので省略します。
ec2-user でログインし、sudo できるユーザーを追加するなどします。
ここでは `miko` ユーザーとします。
ありふれた手順ですが、わわわと書いておきます。

```sh
sudo useradd miko
sudo passwd miko

# wheel に miko を追加
sudo vi /etc/group

# wheel を sudoers に追加（コメントを外す）
sudo vi /etc/sudoers

# ssh ログイン設定
sudo mkdir /home/miko/.ssh
sudo vi /home/miko/.ssh/authorized_keys
sudo chmod 700 /home/miko/.ssh
sudo chmod 600 /home/miko/.ssh/authorized_keys

# miko ユーザーでログインする
```

### Route53
こちらも普通に設定します。
先程起動したインスタンスのパブリックIPを、自家用マストドンのサブドメインに紐付けましょう。

### マストドンをひっぱってくる
#### git

```sh
sudo yum install -y git
```

#### git clone

```sh
cd /home/miko/
git clone https://github.com/tootsuite/mastodon.git
```

### マストドンに必要なものをインストール
#### ffmpeg

```sh
sudo yum install http://li.nux.ro/download/nux/dextop/el7/x86_64/nux-dextop-release-0-5.el7.nux.noarch.rpm
sudo yum install -y ffmpeg
```

#### ImageMagick

```sh
sudo yum install -y ImageMagick
```

#### ruby
##### install libs

```sh
sudo yum install -y gcc gcc-c++ make
sudo yum install -y openssl-devel readline-devel zlib-devel

# こちらはEC2からRDS上のpostgresにログインするために使います
sudo yum install -y libpq-fe libpq postgresql-devel
```

##### rbenv
[rbenv](https://github.com/rbenv/rbenv)
とくにrubyを使い分ける必要がなければ、rbenv をインストールする必要はないです。

```sh
git clone https://github.com/sstephenson/rbenv.git ~/.rbenv
git clone https://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build

echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile
echo 'eval "$(rbenv init -)"' >> ~/.bash_profile
. ~/.bash_profile
```

##### install 2.4.1
```sh
cd /home/miko/mastodon
rbenv install 2.4.1
rbenv versions  # エイリアス確認
```

#### bundle
```sh
gem install bundle
RAILS_ENV=production bundle install
```

### node
#### install nvm
```sh
git clone https://github.com/creationix/nvm.git ~/.nvm
nvm install 6.10.2
```

#### npm install
```sh
npm install .
```

### ElastiCache Redis
マストドンはメール送信のキューイングに sidekiq を使っているので、Redis を用意する必要があります。
AWS の ElastiCache でとくにハマりどころもなく立てました。
Status が available になったら Primary Endpoint が表示されるので、そちらを控えておきましょう（マストドンの設定に使用します）。

### RDS PostgreSQL
AWS RDS 上に PostgreSQL のインスタンスを立てます。
起動したら、エンドポイントを控えておきましょう。

#### DB の用意
```sh
psql -h #{エンドポイント} -p 5432 -U #{作成時に設定したスーパーユーザー名}
```

[Qiita - PostgreSql コマンドの覚え書き](http://qiita.com/mm36/items/1801573a478cb2865242)

```psql
create database mastodon;  # 変更可能です
CREATE USER #{マストドンに使用させるユーザー名} ENCRYPTED PASSWORD '#{パスワード}';
GRANT ALL ON DATABASE mastodon TO #{マストドンに使用させるユーザー名};
```

### SES
実際のメール送信に使用するSMTPサーバに、AWS SESを利用することにしました。
設定手順はこちらが詳しいです。
http://dev.classmethod.jp/cloud/aws/amazon-ses-build-and-practice/

加えて、AWS SES 管理画面左端メニューから「SMTP Settings」を選び、「create my STMP credentials」ボタンを押して、AMIロールを追加、対応するログインIDとパスワードを発行してもらってください。

### .env.production に設定を追加する

#### .env.production の雛形をコピー
```sh
cd /home/miko/mastodon
cp .env.production.sample .env.production
```

#### rake secret
`PAPERCLIP_SECRET` `SECRET_KEY_BASE` `OTP_SECRET` に使用するランダムな文字列を発行する。

```sh
# 3回実行し、それぞれ控えておく。
rake secret
rake secret
rake secret
```

#### .env.production を書き換える
```sh
vi ./.env.production
```

```sh:shell/.env.production
# Service dependencies
REDIS_HOST=#{hoge}.cache.amazonaws.com  # ElastiCache で立てた Redis の Endpoint
REDIS_PORT=6379
# REDIS_DB=0
DB_HOST=#{hoge}.rds.amazonaws.com  # RDS に立てた PostgreSQL の Endpoint
DB_USER=miko  # mastodon に使用させる PostgreSQL のユーザー
DB_NAME=mastodon  # mastodon に使用させる PostgreSQL のデータベース
DB_PASS=#{hoge}  # DB_USER のパスワード
DB_PORT=5432

# Federation
LOCAL_DOMAIN=hogehoge.com  # mastodon へのアクセスに使用するドメインとパス
LOCAL_HTTPS=true

# Use this only if you need to run mastodon on a different domain than the one used for federation.
# 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 secret` if you use docker compose)
PAPERCLIP_SECRET=#{hoge}  # さきほど発行した secret
SECRET_KEY_BASE=#{hoge}  # さきほど発行した secret
OTP_SECRET=#{hoge}  # さきほど発行した secret

# Registrations
# Single user mode will disable registrations and redirect frontpage to the first profile
# SINGLE_USER_MODE=true
# Prevent registrations with following e-mail domains
# EMAIL_DOMAIN_BLACKLIST=example1.com|example2.de|etc
# Only allow registrations with the following e-mail domains
# EMAIL_DOMAIN_WHITELIST=example1.com|example2.de|etc

# Optionally change default language
# DEFAULT_LOCALE=de

# E-mail configuration
# Note: Mailgun and SparkPost (https://sparkpo.st/smtp) each have good free tiers
SMTP_SERVER=email-smtp.#{hoge}.amazonaws.com  # SES の SMTP サーバホスト
SMTP_PORT=587
SMTP_LOGIN=#{hoge}  # SES で発行した credential のログインID
SMTP_PASSWORD=#{hoge}  # SES で発行した credential のパスワード
SMTP_FROM_ADDRESS=hoge@hoge.com  # 送信元メールアドレス
#SMTP_DELIVERY_METHOD=smtp # delivery method can also be sendmail
#SMTP_AUTH_METHOD=plain
...
...
(以下略)
```

### migration
```sh
RAILS_ENV=production bundle exec rails db:migrate

# 確認
psql -h #{エンドポイント} -p 5432 -d #{データベース名} -U #{ユーザー名} -W
\dn  # スキーマ一覧が表示される
```

### assets precompile
```sh
RAILS_ENV=production bundle exec rails assets:precompile
```

### unicorn
systemd でなく unicorn を使用しています。
単に慣れているからそうしたというだけで、systemd を採用する場合は Pawoo の事例などをご参考ください。

nginx+unicorn+rails の設定は以下が詳しいです。
[Qiita - 【CentOS 7】Nginx + Unicorn で Rails アプリケーションを本番環境で立ち上げる方法](http://qiita.com/noraworld/items/c8acf4bb981c8d8535df)

#### install unicorn
```sh
cd /home/miko/mastodon
vi ./Gemfile
```

最下行の `group :production do 〜 end` の中に `gem 'unicorn'` を追加。

```sh
RAILS_ENV=production bundle install
```

#### 設定ファイル、起動スクリプトの用意など
上記記事を参照のこと。

### nginx
#### install nginx

```sh
sudo yum install nginx
sudo chmod a+rw /var/log/nginx
```

#### update nginx.conf
```nginx:/etc/nginx/nginx.conf
（前略）
    #root         /usr/share/nginx/html;
    root         /var/www/html;  # お好みでどうぞ

（中略）
        # コメントアウトする
        #location / {
        #    auth_basic "Restricted";
        #    auth_basic_user_file /var/www/html/.htpasswd;
        #}

（後略）
```

#### add mastodon.conf

こちらを参考にさせていただきました。

[Qiita - Dockerを利用しないでマストドン(Mastodon)をCentOSにインストールする](http://qiita.com/bezeklik/items/1a8530d530613acd665c)

```nginx:/etc/nginx/conf.d/mastodon.conf
upstream rails-unicorn {
    server unix:/tmp/unicorn.sock;
}

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

server {
  listen 80;
  listen [::]:80;
  server_name #{hoge};  # ホスト名

  keepalive_timeout    70;
  sendfile             on;
  client_max_body_size 0;

  root /var/www/html/mastodon;  # お好みでどうぞ

  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 / {
    # アクセスを特定IPに制限したかったので入れています。
    set_real_ip_from 172.16.0.0/12;
    real_ip_header  X-Forwarded-For;
    allow XXX.XXX.XXX.XXX;  
    deny all;
    
    # 制限しない場合はここだけでよい。
    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://rails-unicorn;
    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://localhost: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;
}
```

#### run nginx
```sh
nginx

#確認
ps aux | grep nginx

# 再起動は nginx -s reload
```

### Application Load Balancer
EC2 のロードバランサーメニューから、アプリケーションロードバランサーを追加します。
SSL の設定など詳細は省略しますが、今回の場合は ALB で SSL を処理させて、EC2 インスタンスには80番のアクセスだけが行くようにしています。

### sidekiq
#### 起動
```sh
nohup RAILS_ENV=production  bundle exec sidekiq -q default -q mailers -q pull -q push > out.log 2> err.log < /dev/null &

# とりあえずなのであまりにも男らしい起動方法を取っていますが、本番で運用される場合はちゃんとしてあげてください（丸投げ）。
```

#### キューの確認
```sh
# redic-cli のインストール
sudo yum install -y redis --enablerepo=epel

# アクセス
redis-cli -h #{hoge}.cache.amazonaws.com -p 6379
```

### 起動
```sh
cd /home/miko/mastodon
./script/unicorn.sh start

# 確認
ps aux | grep mastodon

# 終了は ./script/unicorn.sh stop
```

これで、Route53 に指定したアドレスへアクセスすれば、以下の /about の画面が表示されるでしょう。

![スクリーンショット 2017-04-15 18.15.25.png](https://qiita-image-store.s3.amazonaws.com/0/51625/bc48f36f-8b53-e400-e03a-610f02e52bf3.png "スクリーンショット 2017-04-15 18.15.25.png")



## 所感
### S3 をマウントすればよかった
マストドンは、デフォルトで `public/system/media_attachments/files/` 配下にディレクトリを掘って、ユーザーのアップロードした共有ファイルを保存するようなのですが、 AWS でやるのであればそういったファイルたちは S3 に保存したいものです。
Pawoo は S3 に保存するようにしているようなので、こちらを参考にすればよかったなと思いました。
S3 を EC2 にマウントするには適切な IAM ロールの追加と、EC2 インスタンス作成時の設定が必要とのことで、インスタンス起動前に作業しておく必要がありそうです。

余談ですが、配置するのはユーザーの投稿ファイルなので、ファイル添付のある投稿数が増加すればするほど、S3 の使用容量が増加していきます。
ある程度アンコントローラブルになってしまうので、お財布に不安のある場合は S3 を使用せず、冒頭の記事にあるような Minio を使用する運用を検討してみてもいいかもしれません。

### PostgreSQL に慣れていないので MySQL にしたい
自分はもっぱらの MySQL ユーザーで、久々に [psql コマンド] とかでググりました。
正直なところ、実運用するときにはよりテンパりにくく他の人にも知見のある MySQL での運用を行いたいなあ、と思ってしまいます。

### unicorn じゃなく systemd のほうがいいのだろうか
たまたま慣れているので unicorn を使ってしまいましたが、systemd での運用のほうが楽だったりするんでしょうか。
こちらもやってみて、比較してみたいなと思います。