wataメモ

日々のメモをつらつらと書くだけ

Ruby on Railsでサービスを作ってみたpart6

 今回で6回目「Ruby on Railsでサービスを作ってみた」記事投稿。過去記事も増えてきましたが読みたい方はこちらからどうぞ。(part1part2part3part4part5)今回のインフラ周りの話でこのシリーズは一応完結。(挨拶)

サーバ構成

 AWSをインフラとしては利用しているが、CloudFormationやTerraformは使わず、せっせと自分で色々構築している。VisualOpsで出力した結果は以下。

f:id:wata_htn:20151001172336p:plain

サブネット

 Multi-AZにするためにap-northeast-1b、ap-northeast-1cにそれぞれprivateサブネットとpublicサブネットを作成。第3オクテット目で分けていて0-9はpublicサブネット、10-19はprivateサブネットとした。

AZ publicサブネット privateサブネット
ap-northeast-1b 10.0.0.0/24 10.0.10.0/24
ap-northeast-1c 10.0.1.0/24 10.0.11.0/24

踏み台サーバ

 sshは直接サーバへはさせず、必ず踏み台サーバを経由するようにしている。SecurityGroupを更新するWebアプリを作成し、クライアント証明書が入っているブラウザからならどこからでも接続出来るようにしている。かつ、scriptコマンドによるコマンドをログに必ず残すようにもなっている。以前開発合宿で作った資料から抜粋。

f:id:wata_htn:20151001173855p:plain

f:id:wata_htn:20151002000116j:plain

 ただ、アカウント管理周りはLDAP等は用意していないので、この辺りは改善点ではある。

NATサーバ

 特に語ることもないAWSが用意しているAMIを使って構築。privateサブネットからの通信は内部IP以外はNATを通す様にルーティングを設定しておく。まあ、強いて上げれば「送信元/送信先の変更チェック」を外しておくぐらい。

Webサーバ

 Auto Scalingは設定していない。privateサブネットに配置。外からアクセスを受け付けるのと、sslの解除はELBに任せている。基本的にはWebサーバはhttpのプロトコルで受けることになる。なので場合によってはリダイレクトの設定はnginxに入れないと行けない場合がある。
 インスタンスを再起動するとELBから上手く繋がらなくなるので、復帰スクリプトも仕込んでおく。
 プロビジョニングについてはitamaeを使って、設定を反映させている。

画像配信サーバ

 S3からの画像をsmall_lightを通してリサイズするリバースプロキシとなっている。CDNを挟むことを基本イメージしている。本来はサブドメインでやりたかったがが、今回のサービスとしてはサブドメインでグループ分けを行う想定の為、Route53は使わずCloudFrontのドメインそのままを利用することにした。別ドメインなら無駄なCookieとかの送信もなくなるので良いかなということで今回は総判断した。
 画像に関しては、アップロードされた時にリサイズ処理を掛けるのと、アクセスが来た時にリサイズするパターンがある。アップロードされた時にリサイズをすると、画像の表示サイズを変えたい、追加したい場合にすべての画像を変換し直す必要が出てきてしまう。その為にアクセス時にリサイズし、そのサイズがあればキャッシュを返すパターンが望ましい。ただ、キャッシュがない場合に同時にアクセスが来た場合の処理を気にしないとエラーになってしまうこともあるので注意が必要。

f:id:wata_htn:20151002131522j:plain

 というかローカル開発ではDockerで画像配信サーバをまかなっているので、本番ではEC2 Container Serviceを試して見るのもありだったかも。

RDS

 個人で作っているのも有り今回はMulti-AZにはせず。文字コードを設定した以外は特筆すべき事も無い。

Circle CI

 本番環境リリース用にCircle CIからCodeDeployへ連携の設定をしている。bundle installはキャッシュしてかつ並列実行指定。一人プロジェクトだったのもあり、Slackは今回使っていない。なのでCircle CIの通知はCCMenuを使って通知を受けるようにしている。Slackが無いとChatOpsが出来ないのもやはり面倒なので、一人でも導入すべきだったかも知れない。

circle.yml

dependencies:
  cache_directories:
    - "vendor/bundle"
  override:
    - bundle -j4 --path=vendor/bundle

CodeDeploy

 今回はCapistranoは使わず、CodeDeployにしてみた。当たり前の話だがデメリットとしてAWS以外の環境にリリースすることになったら、ある程度script下のシェルが使えるがちょっと面倒そう。今回のリリースを図にするとこんな感じ。CodeDeployが東京リージョンに来たので、S3→Lambda→EC2からS3ファイルを取得→リリースということをやらなくて良くなったのは便利。
 ビルドに関してはzipファイルでS3にあげているが、古いビルドの削除はバケットのライフサイクルの設定を入れて任せている。

f:id:wata_htn:20151002130359j:plain 

 appspec.ymlの内容としては/opt/survey下にリリースして、Lifecycleでシェルをキックしてunicornを再起動しているだけだ。AfterInstallでridgepoleのapplyをしているが本来はこれはしない方が良いだろう。複数台サーバがある場合は何回も走ってしまうし、何よりDB反映のタイミングは遡及とかもありコントロールしたいだろう。

version: 0.0
os: linux
files:
  - source: /
    destination: /opt/survey
permissions:
  - object: /opt/survey
    pattern: "**"
    owner: webservice
    group: webservice
    mode: 755
hooks:
  AfterInstall:
    - location: script/codedeploy_bundle
      timeout: 300
      runas: root
  ApplicationStart:
    - location: script/codedeploy_start
      timeout: 300
      runas: root

NewRelic

 何かトラブルがあった場合にリソース情報の取得、可視化しておくことは非常に重要。かつ、開発時も常に自分のサービスのリソース使用状況は把握しておくのが良い。特にメモリやディスクは日頃から監視しておかないとサービス停止にも繋がりかねない。その為にNewRelicを入れている。無料でも基本的なリソースの監視はできるが、有料にすれば、アプリの細かいところまで見れるようになる。パフォーマンスチューニングの際に役に立つだろう。アプリでスループットをきちんと出すには、リソースを監視しながらやらないと、勘で修正してもなかなかうまくいかない。「推測するな、観測せよ」は鉄則。

script

 インフラというわけではないが、プロジェクト毎のコマンド等をscriptディレクトリにまとめるというGitHub Engineeringブログを見て、これはいいかもと思って適用している。特に決まったルールは無いが、GitHub:github/sripts-to-rule-them-allから持ってくるのもいいだろう。

やっていないこと

 やっていない事としては、プロセス監視、ログ監視、プロビジョニングの自動化(今は踏み台サーバから各サーバをitamaeしている)等だ。プロビジョニングの自動化についてはAMI化して、新しく再リリースがクラウドっぽいがCodeDeployを使ってみるのもありかと一瞬思ったがやはりそれは無いな。リソース監視および可視化もまだまだしていない部分もある。バックアップもきちんとしていないので、このままデータが吹き飛ぶと復旧には手間がかかることになる。

まとめ

 part6まで掛けて「Ruby on Railsでサービスを作ってみた」のメモを書いてみた。手順をすべて記載しているわけではないのでハウツーにはならないが、本ブログをきっかけに調べなければいけないこと、勉強しなければいけないことのとっかかりには出来るかもしれない。これから初めて「一からサービスを作ってみよう」と思う人の参考になれば幸いだ。

次回について

 冒頭に「一応」完結と書いたのは、実装周りでもう少し補足したいことがあるのでおまけ的に書いてみようと思ったからだ。興味があればお付き合い頂きたい。