最近の砂場活動その1: CloudFormation/RDS/ElastiCache編

機械学習っぽいエントリばっかり書いているけど、普段はWebアプリケーションエンジニアをやっている。そちらの方面の勉強もちょびちょび進めていく。圧倒的に知識と経験が足りなくて、危機感しかない。

アプリケーションエンジニアがインフラオーナーシップを高めていく機運が社内で高まっていたり、ミドルウェアやインフラの知識を高めたかったり、コンテナ関連の手頃な練習台が欲しいといったあたりがモチベーション。直近の業務での必要性も高まっているので、正直あと半年早くやっておけばよかったという気持ちがある…。とにかく気軽に変更してぶっ壊して障害起こして復帰させられるような砂場を作るのが目標。砂場ができればそこで遊べる。

お題

wordpressアプリなどがよくお手軽事例で取り上げられるが、あれこれやるのに愛着がないアプリを触っていても面白くない。三日で飽きる。ちょっと前に自分が作っていたアプリがあったので、それを題材にする。

cliの機械学習アプリで、教師データはテキストファイル、キャッシュはバイナリファイルに読み書きという素朴すぎる作りになっていた。この時はgoでcliアプリを練習をするのが目的だったので何も間違ってはいないのだが。これをひとまず

  • cliアプリをwebアプリ化
  • テキストファイルで管理していた教師データをPostgresで管理
  • バイナリファイルで管理していたキャッシュをRedisで管理

するようにした。砂場活動の本題とは直接関係ないものではあるが、少しずつ書き換えている。ORMやWebフレームワークのようなものは使わず、なるべくgoの標準ライブラリの範囲で収まるようにシンプルになるようにしてる。

CloudFormation

あれこれサーバーの設定を(後述する)ansibleでいじって壊れたらEC2のサーバーを新しく立てなおして、というのを手でやっていた。いつどういう目的で作ったのかよく分からないVPCやセキュテイィグループがとっちらかっていて治安が悪かった…。何度もやっているとさすがにAWS Consoleをぽちぽちやっているのもダルくなってきたので、CloudFormationをやってみることにした。仕事でもCloudFormationを触るので、ymlファイルを見てあれこれ分かるようになると助かる。

第一段階

最初は素朴に1つのEC2インスタンスに

  • アプリサーバー
  • バッチサーバー
  • Nginx
  • Redis
  • Postgres

が同居している構成にした。素朴ではあるものの、CloudFormation以前にAWSでのネットワークやセキュティグループへの理解が不足していたので、本で勉強した。

Amazon Web Services 基礎からのネットワーク&サーバー構築 改訂版

Amazon Web Services 基礎からのネットワーク&サーバー構築 改訂版

かなり分かりやすかったが、コンソールの画面で設定していく形式だったので、それをCloudFormationのコード(yml)に落としていく必要がある。だるいなーと思っていたが、CloudFormation化したものをgithubに上げている先人がいたので助かりまくった!!!

両方とも同僚で同じチームだし、Mackerelチームのアウトプット最高ですね、負けられない。

a-knowさんのコードは各セクション毎にPull Requestが分かれていて分かりやすかったし、shibayuさんのエントリはクロススタックをどう扱えばいいかが書かれていて助かった。これらを参考にMakefileにコマンドを書いておいて、VPCおよびセキュリティグループのスタック、EC2インスタンスのスタックを作れるようにした。makeは最高のタスクランナーです。

make create-vpc
make create-ec2

一台のEC2インスタンスで済ませるために、アプリケーションサーバーとDBサーバーが同一のpublic subnetにいる。セキュリティ的によろしくない作りですね。

第二段階(今ココ)

1つのEC2インスタンスに全部入りのCloudFormationだと、例えばアプリサーバーの台数を増やそうと思ったときにDBやCacheも別々になってしまうのでダメ。次の構成では

  • アプリサーバー兼バッチサーバー兼Nginxが同居しているEC2サーバー
  • ElastiCache(Redis)
  • RDS(Postgres)

を目指した。今後、コンテナ化していくことを考えると、状態をRDSなど外に逃がせられるようにしたのでよかった(ref: The Twelve-Factor App (日本語訳))。構成変化に合わせ、subnetの構成も変えていく。

  • private subnetにRDSとElastiCscheを配置
  • public subnetにEC2を配置。このサブネットからのみRDS/ElastiCscheが接続できるセキュリティグループを作る
    • 80版のportを公開、sshは自宅のIPからのみに制限

RDSなどのマネージドサービスを使わない場合、セキュリティ関係のパッチを当てるためにNATサーバーが必要だった気がする。

第三段階(これから)

EC2で動いているアプリサーバーをECSで動くようにする。ひとまずコンテナ化する必要がある。サーバーと別個でデーモン的に動いていたプロセスをコンテナでどう扱うのがいいかよく分かってない…。別個でコンテナ立ち上げるのが定石なんだろうなと思いつつも、本当にちょっとしたものなので、そこでお金ならべくケチりたい。

コンテナ化したらECRにリポジトリを作って、イメージをpush。ここはやるだけ。

セキュリティグループやsubnetはEC2でやってたときのを使いまわせそう。オートスケールはとりあえず必要ないけど、いくつかコンテナ立ち上げてロードバランサでいい感じにやる方法を調べる。

Webサーバ以外にもバッチジョブ(クローラー)を走らせたいけど、その方法も調べる必要がある。常時走らせるわけではないので、この辺調べる必要がある。

コンテナ動かすのはEC2じゃなくてFargateで試してもいいけど、tokyo regionにまだきてないからひとまずいいか。EC2はsshログインして最悪色々チェックできるけど、Fargateになるとそうも行かなくなりそうなので、うまく行かないときのデバッグ方法も模索して行きたい。ログをどこに置くのかも考える必要がある。

Ansible

CloudFormationでインスタンスが立ったら、インスタンスの設定をAnsibleでやっていく。

  • Webアプリをデーモン化するためにsupervisorを入れる
  • Webアプリはgoで書いているので、シングルバイナリに固めてscpでサーバーに送る手抜きdeploy
    • リクエスト送るのは大体自分なので送られていたリクエストがあっても叩き落とす
    • graceful restartとかはあとで考える…
  • ログ取り目的でnginxを入れる
  • モニタリング用途にmackerel-agentと入れる
    • accesslog/check_disk/check_uptimeあたりをプラグインで入れておく
    • postgres/redisとの疎通もcheck監視しておく
    • APIKEYはどうやって渡すのがいいかな…
      • 暗号化するのかなー

docker化して、ECSで動かすようになったらansible捨てられそう。捨てたい。

困ったこと

  • データベースはアプリと切り離したり、RDSなどマネージドサービスを使うことでいい感じになったものの、自分しかアクセスしない割にお金がかかるようになった
  • 手元とciと本番環境でそれぞれきちんとうまく動くようにするのがちょっと面倒
    • 手元はmacのpostgres、ciサーバでの設定、本番はRDSなど
  • RDSをcloudformationで作るとちょっとしたことで作り直されてしまって、デバッグに時間がかかってしまった
  • 可視化目的でCloudFormationのデザイナーを使っていたけど、クロススタックを使っている場合の可視化が分からない
  • アクセスキーとかその辺の管理
  • アクセスするのが自分とcronだけなので、緊張感がない

今後やる予定のこと

  • EC2で動いているアプリサーバーをECSに乗せる
    • 複数台配置してロードバランサー置く
  • RDSの複数台構成/Multi-AZ化、レプリケーション、Failover、バックアップ、リストアなどを試す
    • ミドルウェア関係で障害を意図的に起こして、そこからの復帰方法を学ぶ
  • Nginxの前にALBを配置
    • ヘルスチェック
  • public ipにRoute 53で名前付ける
  • パフォーマンスチェック。ボトルネックの把握