こんにちは、kymmt90です。EC事業部カラーミーリピートチームでRails、OpenAPI、GraphQLなどを使ったWeb APIの開発を主に担当しています。だいたい半期に1回のペースでペパボテックブログに記事を書いていて、前回はRails Developers Meetup 2017の登壇レポートを書きました。
私が所属するチームではカラーミーリピートというWebサービスを開発/運用しています。カラーミーリピートは、2017年11月末のサービス開始から7か月ほど、Web APIサーバー (Rails) とフロントエンドサーバー (Express) の両方をHeroku上で運用しています。この記事では、カラーミーリピートのHeroku上での運用に伴って得た知見について共有します。
カラーミーリピートとは
カラーミーリピートは事業者様が手間をかけずに定期販売を始められるECプラットフォームです。ランディングページ作成機能やすぐ使えるクレジットカード決済、別ページへ決済用モーダルを表示するボタンを埋め込める機能などにより、Web上で定期販売するための準備をスピーディに完了できるように設計してあり、事業者様のビジネス拡大をサポートしています。
なぜHerokuか
ペパボの主要サービスはプライベートクラウドNyahなどの上で運用しているものが多いのですが、カラーミーリピートがHerokuを選んだのは次のようなメリットによります。
まず、チームの構成とサービスの状況です。エンジニアに話を限定すると、チーム構成はリリース当初から現在にかけて多少の変化はありますが、フロントエンド担当1〜2人かつWeb API担当2〜3人という小さめのチームです。また、スモールスタートで始めたサービスなので、現在も機能拡充に集中して取り組んでいます。このようなケースでは、ミドルウェアから下の層はマネージドな環境に任せておいて、アプリケーションをよくしていくことに全力を注ぐことができるHerokuは大変便利です。
次に、利用する技術との相性のよさです。RailsやExpressで作ったアプリケーションであれば、Herokuへ git push
するだけでデプロイが完了し、アドオンを追加するだけでRDBやKVS、監視ツールまで導入できます。また、先ほども述べたとおりアドオン自体のアップデートなどはHeroku側ですべて受け持ってくれるので大変助かっています。
Herokuを使っているペパボのサービス
SUZURIはHeroku上で運用されているサービスであり、昨年にはSUZURIのエンジニア@shimojuがHeroku上での本番運用の知見について社内勉強会で発表した資料を公開しています。こちらもぜひご覧ください。
Herokuはスケーラブルなアプリ養成ギプス - shimoju.diary
Heroku上での開発/運用
それでは、スモールスタートで始めたカラーミーリピートにおいて、Herokuをどのように使っているか、また、そのなかで得た知見について紹介していきます。
活用しているアドオン
Web APIサーバーには次のアドオンを追加しています。
- Heroku Postgres
- 本番用とfollower DB用
- Heroku Redis
- SendGrid
- New Relic APM
- Papertrail
- Proximo
フロントエンドサーバーには監視系アドオンであるNew Relic APM, Sentry, Papertrailを追加しています。また、CDNだけはペパボの他サービスと同様にHerokuアドオンではないAkamaiを利用しています。
Heroku Postgresの話題などはWeb上でもわりと見かけるところなので、ここではもう少しマイナーな話題について書きます。
Slackとの連携によるパフォーマンス/エラー監視
New Relic APMやSentryのアドオンでWeb API/フロントエンドサーバー両方のパフォーマンスやエラーを監視しています。とくにNew RelicではApdexの低下とエラーレートの上昇が閾値を超えたときにSlackに次のような通知を送るようにしています。この通知はNew RelicのAlertsを使っています。
ただし、AlertsをSlack連携させるためにNew Relicのadmin権限が必要なのですが、Herokuのアドオンだと権限を取得するのに一手間かかるのが難点です。具体的にはカスタマーサポートに連絡してadmin権限をもらう必要があります。詳しくは次の記事を参照してください。
固定IPアドレスをかんたんに取得して利用
大人の事情でIPアドレス制限のかけられた環境に対して、こちらのIPアドレスを申請して利用する必要に迫られることが往々にしてあることと思います。しかし、HerokuのdynoのIPアドレスは変動する可能性があります。そんなときに便利なのが固定IPアドレスを取得するためのアドオンです。このようなアドオンとしては次の候補があります。
カラーミーリピートでは、固定IPアドレスでの接続が必要なのはSidekiqで非同期処理を実行するworker dynoだけでした。Proximoは Procfile
に次のとおり記述するだけでworkerから外部への通信をお手軽にIP固定化用のプロキシ経由にできるので、今回はProximoを選びました。
worker: bin/proximo bundle exec sidekiq
PROXIMO_MASK
というサブネットマスクを指定する環境変数と併用すると、一部のレンジの送信先だけProximo経由にすることもできます。
Pipelinesを用いた複数のデプロイ環境の活用
Herokuといえば git push heroku master
するだけでデプロイできる機能が有名です。さらに、いまはHeroku Pipelinesという継続的デリバリーのための機能でdevelopment/staging/productionの各デプロイ環境を活用することができます。
カラーミーリピートでは次のようにデプロイ環境を運用しています。
- Heroku上でWeb APIとフロントエンドを結合して確認したいときや、開発者以外の人に新機能を事前確認してもらうときはdevelopment環境を使う
- masterマージ時にCI環境からstaging環境にデプロイする
- ペパボではCI環境としてDroneを利用
- drone-herokuを利用してHerokuへデプロイ
- リリースする機能についてstaging環境で十分に動作確認したら、Heroku Pipelinesのpromoting機能を使ってproduction環境にデプロイする
上記リスト最後の項目で述べたpromoting機能を使うと、staging環境デプロイ時に生成される成果物 (slug) をproduction環境にコピーするだけでデプロイが完了します。slugのコピーだけでビルドが走らないので高速にデプロイできますし、stagingとproductionでリリース成果物にずれが生じるようなこともありません。
$ heroku pipelines:promote -r staging # rオプションにpromote元のremoteブランチ名を指定
Release Phaseを使うと、promote時にDBのマイグレーションを実行することもできます。Railsの場合、Procfile
に次のとおり記述すればOKです。
release: bin/rails db:migrate
slugに環境依存の設定値が含まれていると、staging環境で生成したslugをそのままproduction環境にコピーするだけでは問題が発生する可能性があります。そうならないように、slugの生成方法には気をつける必要があります。
Dev/Prod Parityにしたがった一貫性のあるアプリケーション動作環境
Rails特有の話になりますが、巷ではよくstaging環境で動かすRailsアプリケーションに RAILS_ENV=staging
と設定する記事を見かけます。しかし、基本的に RAILS_ENV
には development
、test
、production
のいずれかの値を設定する規約になっており、かつ、Herokuは RAILS_ENV=staging
とする方法をおすすめしていません。
Deploying to a Custom Rails Environment - Heroku Dev Center
要約すると次のことが書いてあります。
- 本番で動くことをできるだけ保証するために、できるだけ本番に近い開発環境が必要になる
- いわゆるDev/Prod Parity
- The Twelve-Factor AppのDev/Prod Parityの説明
- stagingというのは本番と同じ動作をするかチェックするための環境である
- だから、Railsではstaging環境でも本番と同じ環境変数
RAILS_ENV=production
で動かすべきである
つまり、RAILS_ENV
はRailsが開発モード (development
)、テストモード (test
)、本番モード (production
) のどれで実行されるかを決める環境変数であって、どのデプロイ環境で動かすかを表す環境変数ではない、ということです。
具体例としては、本番モードだけで実行したいロジックの分岐を表現するのに if Rails.env.production?
とするのが自然ですが、RAILS_ENV=staging
を導入してしまうと if Rails.env.staging? || Rails.env.production?
としないと不具合を招くという点が挙げられています。
また、Herokuに RAILS_ENV=staging
で git push
したときには次のような警告が表示されるようになっています。
###### WARNING:
You are deploying to a non-production environment: "staging".
This is not recommended.
See https://devcenter.heroku.com/articles/deploying-to-a-custom-rails-environment for more information.
ということで、カラーミーリピートのWeb APIはDev/Prod Parityを意識して次のように運用しています。
Herokuのdevelopment/staging環境
RAILS_ENV=production
と設定しています。また、production環境と同じミドルウェア構成にしています。
また、production環境と異なる設定にしたいときは、環境変数を通じて切り替えています。たとえば、ストレージや決済などの外部サービスについて、development/stagingではそれら外部サービスのテスト環境を使いたいということがあります。この場合、それぞれの環境の環境変数に外部サービスのURLや認証情報を入れています。
ローカル開発環境
Herokuのproduction環境と同じミドルウェア(PostgreSQL, Redis)を使った構成で動かすようにしています。これは、Docker Composeを使うことで開発者ごとのマシンの状況によらずかんたんに実現できます。
また、上でHerokuのdevelopment/staging環境では環境変数を用いてproduction環境と秘匿情報の設定を分けるという話を書きました。一方、ローカル開発環境では次のような候補があります。
- Heroku Localを使う
.env
を使って環境変数を読み込む
- Railsのencrypted secrets/credentialsを使う
- Railsの機能。暗号化したYAMLで秘匿情報を扱う
Heroku LocalはHeroku CLIを使えるので便利です。しかし、秘匿情報の暗号化機能などはなく、Docker Composeを使う方法と合わないというところもあります。そこで、カラーミーリピートではRailsのencrypted secrets/credentialsをローカル開発環境で利用してアプリケーションに秘匿情報を渡しています。この方法では設定ファイル自体が暗号化されるのでリポジトリにコミットしても問題ないこと、次のように ENV.fetch
を使うことでHeroku上では環境変数を優先するように書けることから、ある程度取り回しやすい方法だと考えています。
# 例: 環境変数があればそちらを、なければencrypted secrets/credentialsを読み込む
PaymentClient.configure do |config|
config.id = ENV.fetch('PAYMENT_ID', Rails.application.secrets.payment_id)
config.password = ENV.fetch('PAYMENT_PASSWORD', Rails.application.secrets.payment_password)
end
今後の展望
今後の展望としては、GitHub連携機能であるPreview Apps相当の機能を楽に実現したいという点があります。ペパボではGitHub Enterpriseを利用しているので、Preview Appsをまったく利用できません。この問題に関しては、Herokuが
Heroku's GitHub sync features are hard-coded to connect to and use github.com.
と回答しており、GitHub Enterpriseで使えるようになるのは厳しそうという印象です。
Can I use Github Enterprise with Pipeline Review apps? - Heroku Help
一方、カラーミーリピートでは、同時に複数のUI改善の検証を進めることが多いです。そして、プロダクトマネージャーなど開発者以外がUIを確認するときにHerokuのdevelopment環境を使っていますが、development環境にある一つのHerokuアプリに対してしかデプロイしない運用だったので、デプロイ待ちの行列が発生しがちでした。
現在はこの問題をPreview Appsを参考にして解決しています。フロントエンドエンジニアの@tsuchikazuが、development環境でのHerokuアプリの作成とアプリのデプロイまでを実行するHeroku CLIを利用したコマンドをシェルスクリプトで作成しました。このコマンドでブランチごとにHerokuアプリをdevelopment環境へデプロイ可能にして、擬似Preview Apps環境を実現しています。
次の図は疑似Preview Appsがデプロイされている様子です。ブランチ名の一部がアプリ名になっています。
現在はこのコマンドに使い終わったdevelopment環境のアプリを手動削除できる機能を持たせています。今後は一定時間で自動削除したりできるようにしていく予定です。
おわりに
スモールスタートで始めたWebサービスをHeroku上で運用するなかで得た知見について共有しました。カラーミーリピートではHerokuの数ある機能を活用しつつ、アプリケーションの改善に力を注ぐことができています。
もっとおもしろくサービス開発/運用したい!というかたは、ぜひこの下にあるリンクからご応募ください。