AnosideをさくらのVPSからHerokuに移した話


さくらのVPS上でNginx + Unicorn + MySQL + Railsな環境で動いていた[Anoside](http://anoside.com)を、Herokuに移動しました。この記事はそのレポート的なものになります。


## Anosideってなに?

http://anoside.com Anoside | 匿名で、気にせず、つぶやこう。

匿名でつぶやきが投稿できるWebサービスです。2ちゃんねるとTwitterを足して2で割ったようなサービスです。僕が1人で細々と開発、運営をしています。

最近のソーシャルネットワークな世界は現実世界の人間関係に近づきすぎていて、自分の発言が自分を知る誰かに読まれることが日常的になっています。そのせいで、周りの人に見られると困るような発言がしづらいという雰囲気になっています(少なくとも僕の中では)。

そういう発言しづらい内容を、周りを気にせずつぶやいてみんなに見てもらえたら、それはとっても嬉しいなって思い、Anosideを作りました。よかったら使ってみてくださいー。


## サーバ移行のモチベーション

### サービス開発に集中したいので、サーバ周りに時間をかけたくない

Anosideをリリースするとき、サーバのセットアップをしました。僕がサーバ周りにあまり詳しくないということもあるのですが、結構時間がかかりました。データのバックアップやエラー検知、プロセスの監視などなど。「この作業時間をサービス開発に当てられたら、もっと良いプロダクトになるだろうなあ」と思いながら、AnsibleのPlaybooksを書いたりしてました。
例えAnsibleでサーバ設定を自動化したとしても、ソフトウェアのバージョンを上げる作業など、運用中にサーバ周りで時間をかけることはたくさんありそうです。僕はサービスを作るのが好きみたいで、いろいろと作りたいサービスがあるので、サーバ周りには極力時間をかけず、サービス開発に時間をかけるほうが有意義に思えました。


### 悲しいけど、Herokuでもサーバ費がそんなに高くならない

[Anoside](http://anoside.com)、残念ながらあまり人がいません。

アクセス数がほとんどないので、1X dynoを2台(webとworkerで1台ずつ)動かせば十分そうです。[1X dynoは1台分無料になる](https://devcenter.heroku.com/articles/usage-and-billing#750-free-dyno-hours-per-app)ので、実質もう1台分の料金だけで済みます。データベースも[HobbyのBasic ($9/1 month)](https://www.heroku.com/pricing)プランでしばらく問題なさそう。万が一アクセス数が急増したときは、泣きながら笑いたいと思います。泣きながら笑えたら良いなあ…。


## サーバ移行時にやったこと

### 既存のRailsアプリケーションをHerokuで動くようにする

AnosideはRails 4で作られたアプリケーションなので、[Getting Started with Rails 4.x on Heroku](https://devcenter.heroku.com/articles/getting-started-with-rails4)を参考にしました。


### 設定情報をFigaroで管理するようにする

今までアプリケーション内の設定情報を[RailsConfig](https://github.com/railsjedi/rails_config)で管理していましたが、今回からHerokuの環境変数を利用した設定情報管理方法と相性の良い[Figaro](https://github.com/laserlemon/figaro)を利用することにしました。


### SidekiqがHeroku上で動くようにする

メールの送信処理をバックグラウンドで実行するために[Sidekiq](http://sidekiq.org/)を使用しているので、Herokuで動くようにします。SidekiqはRedisを使用するので、まずはRedisの設定から。


#### Redis Cloudを使う

[Redis Cloud | Add-ons | Heroku](https://addons.heroku.com/rediscloud)

よく使われているのは[Redis To Go](https://addons.heroku.com/redistogo)みたいですが、Redis Cloudのほうが無料プランの容量が大きかったので、こちらを利用することにしました。

```
$ heroku addons:add rediscloud
```

#### ProcfileにSidekiqの起動コマンドを追加する

以下を `Procfile` に追記して、Sidekiqの処理をWorker Dyno上で実行するようにします。

```Procfile
worker: bundle exec sidekiq
```


#### Herokuに環境変数 `REDIS_PROVIDER` をセットする

下記コマンドで `REDIS_PROVIDER` という環境変数をセットします。[Sidekiqはこの環境変数に設定されたRedisサーバにキューを保存します](https://github.com/mperham/sidekiq/blob/master/lib/sidekiq/redis_connection.rb)。

```
$ heroku config:set REDIS_PROVIDER=REDISCLOUD_URL
```

Redis Cloudを使用すると、自動で `REDISCLOUD_URL` という環境変数がHerokuに設定されます。この環境変数にサーバ情報が保存されているので、`REDIS_PROVIDER` はこの環境変数を見るようにします。


### メールサーバにMandrillを使用する

[Mandrill by MailChimp | Add-ons | Heroku](https://addons.heroku.com/mandrill)

さくらのVPSでAnosideを運用していた頃から[Mandrill](http://mandrill.com/)を利用していたのですが、Herokuのアドオンとしても提供されていたので、こちらを使用することにしました。

```
$ heroku addons:add mandrill
```

環境変数として `MANDRILL_APIKEY` と `MANDRILL_USERNAME` が設定されるので、この情報をRailsが読めるようにします。

```config/environments/production.rb
config.action_mailer.smtp_settings = {
  address:        ENV['SMTP_HOST'],
  port:           ENV['SMTP_PORT'],
  user_name:      ENV['MANDRILL_USERNAME'],
  password:       ENV['MANDRILL_APIKEY'],
  authentication: :plain
}
```

`$ heroku config:set SMTP_USERNAME=MANDRILL_USERNAME` とかすれば `ENV['SMTP_USERNAME']` で `MANDRILL_USERNAME` の値が見れるようになるかと思ったのですが、`Net::SMTPAuthenticationError` という例外が発生してメールサーバの認証ができませんでした…。


### ログの管理にLogentriesを使用する

[Logentries | Add-ons | Heroku](https://addons.heroku.com/logentries)

[Papertrail](https://addons.heroku.com/papertrail)という選択肢もあってどちらにするか迷ったんですが、1日に保存できるログの量がLogentriesのほうが多いみたいなので、こちらを選びました。

```
$ heroku addons:add logentries
```


### データベースのバックアップにPG Backupsを使用する

[PG Backups | Add-ons | Heroku](https://addons.heroku.com/pgbackups)

2014/02/01現在、3プランとも全て無料なので、一番高機能そうな「Auto - One Month Retention」プランを使用することにしました。

```
$ heroku addons:add pgbackups:auto-month
```

### アプリケーションのエラー監視にSentryを使用する

[Sentry | Add-ons | Heroku](https://addons.heroku.com/sentry)

他にも[Honeybadger](https://addons.heroku.com/honeybadger)や[Airbrake](https://addons.heroku.com/airbrake)という聞いたことのあるプロダクトがあったんですが、無料プランがあるのがSentryだけだったので、とりあえずSentryを使うことにしました。

```
$ heroku addons:add sentry
```

アドオンのインストール後、Sentryの管理画面にアクセスし、プロジェクト詳細の設定ページで「Platform」を「Rails 4 (Ruby)」にしました。


### その他サーバの監視にNew Relicを使用する

[New Relic | Add-ons | Heroku](https://addons.heroku.com/newrelic)

2014/02/01現在、無料プランが2種類あり、「Maximum monthly average dynos」によって受けられるサービスに差があるみたいです。Starkプランだと平均1.5 dynosなら全ての機能が使えるみたいなので、こちらを選択しました。どうせ平均1.5 dynosもいかないだろうし…。

```
$ heroku addons:add newrelic:stark
```

管理画面にアクセスし、タイムゾーンとPing URLの設定をしました。


### さくらのVPS側の本番サーバをメンテナンスモードにする

Heroku側の設定がひと通り終わった気がするので、実際にサーバ移行作業に入りました。Nginxを使用していたので、[ngx_http_rewrite_module](http://nginx.org/en/docs/http/ngx_http_rewrite_module.html)でanoside.comへのリクエストをメンテナンス告知ページにリダイレクトして、データベースへのアクセスを一時的に遮断しました。


### データベースのデータをHerokuに移す

#### MySQLのデータをPostgreSQLのデータにコンバートする

(今回の移行作業では、後述する[taps-taps](https://rubygems.org/gems/taps-taps)というツールを使用してデータをコンバートしたんですが、[Herokuの公式ドキュメントにモダンそうな別の方法が紹介されている](https://devcenter.heroku.com/articles/heroku-mysql)ので、この方法を試したほうが良かったかも知れません。もう少し早くこのドキュメントを見つけていれば…。)

今までMySQLにデータを格納していたので、Herokuで使用するPostgreSQL向けにデータをコンバートする必要がありました。適当にググったら [taps-taps](https://rubygems.org/gems/taps-taps) というRubyのツールが見つかったので、これを使用しました。
(taps-tapsは[taps](https://github.com/ricardochimal/taps)からフォークされたものです。本家のtapsは、Ruby 2.1で実行すると[内部で使用している OkJson というgem周りでエラーが起こすバグ](https://github.com/ricardochimal/taps/pull/133)があるようで、使用できませんでした。)

```
$ gem install taps-taps
```

使い方は「[Ruby - taps で簡単に MySQL から PostgreSQL へデータを移行する - Qiita [キータ]](http://qiita.com/Dominion525/items/90e1e6311159e9276221)」を参考にしました。
以下は実際に実行したコマンドです。

```
$ taps server mysql://root@localhost/anoside_tmp httpuser httppass
$ taps pull postgres://bojovs@localhost/anoside_tmp http://httpuser:httppass@localhost:5000
```

`taps server` コマンドでMySQLのデータベースを読むSinatraアプリを起動し、次の `taps pull` コマンドでそのアプリケーションにアクセスして、データをPostgreSQLに移すという流れになります。


#### コンバートしたデータをダンプする

データをHerokuのデータベースにインポートするため、ローカルのPostgreSQLに格納されたコンバート済みのデータをダンプする必要があります。下記コマンドでダンプしました。

```
$ pg_dump -Fc --no-acl --no-owner -h localhost -U bojovs anoside_tmp > anoside_tmp.dump
```


#### ダンプデータをAmazon S3にアップロードする

Herokuにデータをインポートするために用意されている `heroku pgbackups:restore` というコマンドは、[HTTPでアクセスできる場所にダンプデータが無いと動作しない](https://devcenter.heroku.com/articles/heroku-postgres-import-export#import)らしいので、今回はAmazon S3にアップロードしました。(なんでサーバに上げる必要があるんだろう…?)


#### データをインポートする

S3にアップロード後、下記コマンドを実行すると、Herokuのデータベースにダンプデータがインポートされます。

```
$ heroku pgbackups:restore DATABASE_URL https://s3-ap-northeast-1.amazonaws.com/path/to/anoside_tmp.dump
```

`DATABASE_URL` はHerokuのPostgreSQLデータベースへのURLがセットされている環境変数になります。この環境変数は初めから設定されています。


### `anoside.com` がHerokuを向くようにする

最初に下記コマンドでドメインをHerokuに追加します。

```
$ heroku domains:add anoside.com
```

次にドメインのDNS設定に以下を追記します。

```
cname @ anoside.herokuapp.com.
```

今回は`anoside.com`というルートドメインに`CNAME`レコードを使用しましたが、例えばもし「info@anoside.com」のようなメールアドレスを作りたい場合は、[`ALIAS `や`ANAME`レコードを使用する必要があります](https://devcenter.heroku.com/articles/custom-domains#root-domain)。
最近だと[Amazon Route 53](http://aws.amazon.com/jp/route53/)が`ALIAS`レコードに対応しているので、こちらを利用すると良いかも知れません (未検証なのでうまくいかないかも…)。

このあとDNSの設定が反映されて、無事Herokuへの移行が完了しました。


### we love herokuに登録する

Herokuを利用しているWebサービスをまとめている「[we love heroku](http://welove.herokuapp.com/)」というサービスがあったので、[登録してみました](http://welove.herokuapp.com/sites/317)。みんな結構Heroku上にサイトを作っているんだなあ。


## 作業完了

データベースの移行がちょっと面倒くさかったくらいで、他の作業はサクサクと終わりました。よーし、おじさんサービス開発に集中しちゃうぞー。

「このHerokuのアドオンがオススメだよ!」とか、「あの設定しなくていいの?」みたいなことがありましたら、ぜひ教えてもらえると嬉しいです。

あとしつこいですが、[Anoside](http://anoside.com)もよろしくお願いします! 目指せユーザ数100人!

http://anoside.com