AWSの無料SSLを使ってmastodonインスタンスを立てる手順

サーバー周りをAWSで固めてmastodonインスタンスを立てたので、その手順と資料をまとめました。

SSLは無料で使えますが、サーバーの使用料とかは普通にかかります。とはいえ、お１人〜１０人鯖ぐらいならEC2のmicroでも動かせるので、そんなにお金はかからなそう。
立て終わった後に書いたので、記憶違いなどあるかもしれません。コメント・編集リクエストしてください :pray: 

# なぜAWSなのか

人数が少ないときは安く済ませられるし、リソースが足りなくなってもお金を突っ込めばスケールできるから（要出典）。

# 必要なもの

- ドメイン
- メールを配信する手段
- AWSのアカウント
- docker・Web・Linux・AWSなどについての基本的な知識

# 構成

- mastodonの公式レポジトリに出てくるdocker-compose.ymlを使う
- 無料SSLを使うためにRoute53とEastic Load Balancerを使う
- Redisを外に出すためにElastic Cacheを使う(Optional)
- Postgresを外に出すためにRDSを使う(Optional)
- メール送信サービスとしてはSendGridを使った
    - 本筋とは関係ないので今回は省略
    - SMTPが使えればなんでもいい

# 手順

1. 設定ファイルを埋める
2. SSL接続を可能にする
3. HTTPでのアクセスを禁止する
4. 画像や動画などをS3に保存するようにする
5. RedisとPostgresを外に出す(Optional)

## 設定ファイルを埋める

まずはEC2のインスタンスを立てましょう。OSはubuntu、インスタンスタイプは`medium`でいいと思います。`micro`にすると、CPUだかメモリが足りなくてAssetsのコンパイルでコケます。
しかし、サーバー自体は`micro`でもそこそこ快適に動くので、デプロイが終わったら`micro`にしましょう。
Security Groupの設定については、とりあえず80番と22番が任意の場所から接続できるようになってれば良いです。

そしてEC2上で、mastodon本体をcloneしてきます：

```shell-session
$ git clone https://github.com/tootsuite/mastodon.git
```

[公式のREADME](https://github.com/tootsuite/mastodon#running-with-docker-and-docker-compose)を参考に、mastodonの設定をします。この時、`LOCAL_HTTPS`については、**falseにしてください**。

`sudo docker-compose up` まで行けたら、EC2インスタンスの80番ポートを3000番ポートにリダイレクトします：

```shell-session
$ sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 3000
```

これにて `http://yourdomain.com/`から、mastodonインスタンスにアクセスできるようになりました！！！！

## SSL接続を可能にする

とはいえhttpでログインするのは怖いので、SSLの設定をしましょう。
Elastic Load Balancerの無料SSLを使います。

### ELBの作成

Elastic Load Balancerを作ります。
**EC2のダッシュボード > ロードバランサー ＞ ロードバランサーの作成**から、ロードバランサーを作成します：

- *リスナー*
    - **HTTPSとHTTPの両方を選んでください**
- *証明書の選択*
    - 「ACMから新しい証明書をリクエスト」で、SSLの証明書を生成する
    - 参考： [[AWS Certificate Manager]東京でも無料でSSL証明書が使用可能になりました！ ｜ Developers.IO](http://dev.classmethod.jp/cloud/aws/acm-available-in-tokyo/)
- *セキュリティグループの設定*
    - 任意のIPアドレスからHTTPSが受けられるようなセキュリティグループを作成して設定する
- *ターゲットグループ*
    - **HTTPだけ**を追加する
- *ヘルスチェック*
    - HTTPで`/about`にする
- *ターゲットの登録*
    - 先ほど作成したEC2のインスタンスを選択する
        - ポートは**80番**を選択する

※作成されたELBのarnはメモっておきましょう。

### ELBとインスタンスの接続

**EC2ダッシュボード ＞ ターゲットグループ**から、先ほど作成したターゲットグループを選び、「ターゲット」に登録してあるEC2インスタンスの状態が`healthy`であることを確認する。

もし`unhealthy`であれば、[ドキュメント](http://docs.aws.amazon.com/ja_jp/elasticloadbalancing/latest/classic/ts-elb-healthcheck.html)等を参考にして解決しましょう。

### ドメインとRoute 53を連携する

ドメインをELBで使えるようにします。
**Route 53 > HostedZones ＞ Create Hosted Zones**からHostedZonesを作成します。
HostedZoneを作成したら、ドメインのNSをHostedZonesのNSの値で置き換えます（参考： [お名前.comのドメインをAWSで使用する4つの方法](http://qiita.com/sadayuki-matsuno/items/4c371ba984d9b22b3737#2%E3%81%8A%E5%90%8D%E5%89%8Dcom%E3%81%ABroute53%E3%81%AEdns%E3%82%92%E7%99%BB%E9%8C%B2%E3%81%97%E3%81%A6%E3%82%B5%E3%83%96%E3%83%89%E3%83%A1%E3%82%A4%E3%83%B3%E3%82%92%E5%A7%94%E4%BB%BB%E3%81%99%E3%82%8B)
）。
そして、**Create Record Set**から、`Type A`の`Record Set`を作成します。この時、ALIASをYesにして、Alias Targetを先ほどのELBのarnに設定します。

### Congrats!

ここまでの手順で、`https://yourdomain.com`から自分のmastodonインスタンスにアクセスできるようになったはずです！やったね！

もし繋がらなかった場合は、ELBのセキュリティグループやターゲットグループの設定を見直してみてください。

最終的な状態では、

- ELBにはHTTPSかHTTPでアクセスできる
    - ELBのリスナーに80番ポートと443番ポートが設定されている
- ELBに入ってきたHTTPSまたはHTTPのアクセスは、EC2の80番ポートに転送される
    - ターゲットグループの「ターゲット」のEC2インスタンスの「ポート」の欄が80である
- EC2のインスタンスは80番ポートでアクセスを受ける
- 以上を妨げないようなセキュリティグループの設定である
    - ELBとEC2やその他のAWSのサービスが、すべて別のセキュリティグループを持つ

が満たされているはずです。

## HTTPでのアクセスを禁止する

前節まででSSLでのアクセスが実現されましたが、依然HTTPでのアクセスも可能です。
EC2インスタンスへのアクセスがELB経由で行われるようにし、また、HTTPでアクセスされたときはHTTPSにリダイレクトするようにしましょう。

まず、EC2インスタンスのセキュリティグループのインバウンドルールについて、HTTPの行の「送信元」の欄に、ELBのセキュリティグループのID（sg-********）を入力して保存します。
これによって、ELBからのみEC2インスタンスにHTTPSでアクセスできるようになりました。

次に、EC2インスタンスの中で、以下のような設定ファイルでnginxを起動します：

```nginx
server {
  listen 80;

  location / {
    if ($http_x_forwarded_proto != 'https') {
      rewrite ^ https://$host$request_uri? permanent;
    }
  }
}
```

これにより、HTTPアクセスをHTTPS付きのURLにリダイレクトすることができます。

## 画像や動画などをS3に保存するようにする

この状態だと、アップロードされた画像・動画やユーザーのアイコン画像は、すべてdockerコンテナの中に保存されます。
そのため、`sudo docker-compose down`するたびに、画像と動画が消えてしまうし、アイコン画像もなくなってしまいます。
これではうまくないので、メディアファイルのストレージをS3に設定しましょう。

まず、S3のバケットを作成します。バケットの名前とリージョンは覚えておきましょう。
次に、AWSの**IAMコンソール**からユーザーを作成して、アクセスキーを取得します。このとき、**既存のポリシーを直接アタッチ**から、**AmazonS3FullAccess**を追加しておきましょう。

そして、`.env.production.sample`のコメントアウトしてある設定を利用して、以下のように設定を書き加えます：

```ini
S3_ENABLED=true
S3_BUCKET=${your-bucket-name}
AWS_ACCESS_KEY_ID=...
AWS_SECRET_ACCESS_KEY=...
S3_REGION=${your-bucket-region}
```

## RedisとPostgresを外に出す(Optional)

前節までで、所望のmastodonインスタンスが手に入ったと思います。
この構成では、中央のEC2インスタンスの中で、Rails・Redis・Postgres・Sidekiq・nodejsの５つのDockerコンテナが動いていることになり、負荷が集中しています。

ところで、AWSはRedis専用のインスンタンスを提供していますし(ElasticCache)、postgres専用のインスタンス(RDS)も提供しています。
実際どのぐらい効果があるのかわかりませんが、これらのコンテナを中央のEC2から切り離してみましょう。

この辺からは適当です。

### インスタンスを立てる

PostgresとRedisのインスタンスを立てます。

このとき、Postgresの設定は、mastodonのレポジトリ内の設定ファイル（`.env.production`）の`DB_*`変数に合わせて適当に変えましょう。
`DB_HOST`の値は後で取得します。Redisも同様です。

次に、PostgresとRedisのセキュリティグループを編集して、EC2のインスタンスからアクセスできるように設定します。
「HTTPでのアクセスを禁止する」の節と同じ要領でできます。

Postgresのインスタンスタイプについてですが、無料枠に収まるように`micro`にしましたが、ちゃんと動いてるようです。Redisのインスタンスは、無料枠が無いようなので、適当に`micro`にしました。今のところ大丈夫そうです。

PostgresとRedisのEndpointをメモっておきます

### `.env.production`を編集

`.env.production`の`REDIS_HOST`と`DB_HOST`を、先程メモったEndpointに書き換えます。

### `docker-compose.yml`を編集

mastodonの`docker-compose.yml`を修正して、`db`コンテナと`redis`コンテナの部分をコメントアウトしましょう。また、他のコンテナの`depend_on`の部分もそれに合わせてコメントアウトします。

### 再起動

```shell-session
$ sudo docker-compose build
$ sudo docker-compose up -d
```

にてmastodonを再起動します。
これによって、RedisとPostgresがAWSのサービスを利用する形で切り出されたことになります。

# その他

- 運営ガイドがあった： [documentation/Administration-guide.md at master · tootsuite/documentation](https://github.com/tootsuite/documentation/blob/master/Running-Mastodon/Administration-guide.md)
- 実際のところ、RedisとかPostgresを外に出したほうがパフォーマンスは出るのか、あるいは料金は安く抑えられるのか、というところは検証したい（誰か検証してほしい）。
- Sidekiq, streamingも外に出したほうがいいのか誰か検証してほしい（他力本願）
- 結局インスタンスは人が集まらなかったので閉鎖した。アカウント作ってくれた人ごめんなさい。