http://prepan.org/ というサービスを運営している。このサービスはインフラ周りにAWSのEC2とRDSを使っている。このサービスが立ち上がったのが2011年あたりなのだけど、この時EC2とRDSはまだVPCがない時代(EC2-Classic)で、VPC非対応なEC2/RDS(t1.micro)で動き続けていた。
しかし、VPC非対応時代のt1.microは、今のt2.microと比べてそれぞれ月額$10ずつ程度高い。つまり、同じ構成でVPC対応のEC2/RDS(t2.micro)を使う場合と比べると、月額$20も余計にインフラコストがかかっていた。
これは辛いと思い、EC2/RDSをVPC内に移行した。手順を踏んでやることで、全部でダウンタイム20分位で出来た。そこで今回はその手順について書いておきたいと思う。
元々の構成
- prepan.orgはRoute53で管理されていて、EIPを向いている
- EIPはWebサーバや非同期ワーカーが動くEC2を向いている
- EC2はSSH/HTTPだけを通すセキュリティグループ(app-sgとする)に入っている
- EC2はデータが入っているRDSを参照している
- RDSはapp-sgからのみアクセスを許可する。つまり上記EC2からのアクセスのみ許可する
これを最終的に以下のようにしたい。
作戦検討
画像からだけだと、単にEC2とRDSがVPCに入っただけじゃんと思うかもしれないが、これをするのは結構大変だった。なぜなら
- VPCの概念を理解するのが、最初は大変
- EC2-ClassicとVPCは、そもそも完全に仕組みが分離されていて、EIPだったりEC2だったりセキュリティグループだったり全部作り直す必要がある
- ダウンタイム少なくするためには、一気に全部移すのではなく、EC2を移して、次にRDSを移して、と順序だてて移行する必要がある
などの理由からだ。
そこで次の手順を一つずつ行っていき、ダウンタイムを出来る限り少なく移行できるようにした。
- 1. Webサーバ(EC2)を完全にまっさらな状況から作れるように、元々のchefを動くように
- 2. Webサーバを入れられるVPCネットワークを設定する
- 3. WebサーバをVPC内に新しく立て、prepan.orgがそちらを参照するように
ここまででWebサーバのEC2をVPC内に移行している。ここまではダウンタイムなしでいけるはず。続いて
これで完了。5番の手順では頑張ればダウンタイムなしでもいけそうだったが、まあ多少は許容して良いだろうということで、20分程度のダウンタイムで乗り切った。
それでは以下1つずつ流れを載せていく。
1. Webサーバ(EC2)を完全にまっさらな状況から作れるように、元々のchefを動くように
もともとchefのcookbookが https://github.com/metacpan/prepan-cookbooks においてあった。けれどこれが結構古くに作ったもので、今そのまま適用しようとしたら、エラーで動かなかった。
まっさらな状態からWebサーバを作るためには、chefが動かないと厳しいので、ひとまずこれを動くようにした。PRは
- https://github.com/metacpan/prepan-cookbooks/pull/11/files
- https://github.com/metacpan/prepan-cookbooks/pull/12/files
- https://github.com/metacpan/prepan-cookbooks/pull/13/files
- https://github.com/metacpan/prepan-cookbooks/pull/15/files
あんまり面白いところはないのだけど、chefのアップグレードをしたり、berkshelfでインストールしているcookbookを更新したり、それで動かないところを直したりというのを地道にやっていった。
あとはローカルテスト用にvagrantを使っていたのだけど、それが参照するイメージはamazon linux 1のものにして、最終的に本番で適用するのと同じ環境になるようにした。
これでひとまずローカルで通るようになったら、このフェーズは終了。
2. Webサーバを入れられるVPCネットワークを設定する
続いて、Webサーバを入れられるVPCネットワークを設定する。ここで構築するVPCネットワークに最終的にRDSも入ることになる。
VPCネットワーク、最初いまいち分からなかったので、以下の本を見て、学習をしていった。
また、新しい技術を獲得しようと欲を持って、CloudFormationでネットワーク構築することにした。この勉強の様子は
- CloudFormationでVPCを構築し、CloudFormationに入門する方法を知った - $shibayu36->blog;
- CloudFormationでEC2の立ち上げをした - $shibayu36->blog;
- CloudFormationで立ち上げたEC2に自動でApacheをインストールして起動するようにした - $shibayu36->blog;
- 「Amazon Web Services 基礎からのネットワーク&サーバー構築 改訂版」でAWSサーバ構築とCloudFormationに入門した - $shibayu36->blog;
- CloudFormationのクロススタック参照を利用してネットワーク設定とWebサーバ設定を分ける - $shibayu36->blog;
実際にVPCネットワークをCloudFormationで実装したPRは https://github.com/metacpan/prepan-cookbooks/pull/16/files 。CloudFormationの設定ファイルは https://github.com/metacpan/prepan-cookbooks/blob/18a777f55e8d3d5fd00454880ab2871cb9ad310d/cloudformation/network.yml 。この設定ファイルを使ってCloudFormationでスタック作成をすれば、VPCネットワークが一気に完成する。
ここでやったことは
の三つ。
ここまででWebサーバを入れられるVPCネットワークが完成した。イメージは以下のとおり。
3. WebサーバをVPC内に新しく立て、prepan.orgがそちらを参照するように
さて、ようやくここから今本番で動いているWebサーバを、VPC内のEC2に置き換える作業をする。この作業により、VPC内のEC2からVPC外のRDSにつなぐようになる。このようなつなぎ方の時のシナリオについては VPC の DB インスタンスにアクセスするシナリオ - Amazon Relational Database Service が参考になる。
以下の流れで作業した。
- 新しくVPC内にEC2立てて、chef適用
- Cloudformationでやっても良かったけど、動的に設定いろいろ変えるので、スタックでの管理が面倒そうだった。ということでEC2の起動テンプレートを作成しておき、手動で立てることに
- EC2の起動テンプレートについては [新機能]Launch Templates for Amazon EC2 instancesを試してみた #reinvent | DevelopersIO 参照
- 新しくElastic IPを取得し、作成したEC2に割り当て
- EC2-Classic時のElastic IPは、VPC内のEC2に割り当てできなかったので、新規作成する必要がある
- RDSのセキュリティグループで、新規作成したEIPを通すように設定
- Webサーバ起動して、EC2にSSHして
curl http://localhost:8000/
してアクセスできること確認- これでアクセス出来れば、正しくWebサーバが起動しており、RDSにも接続できていることが分かる
- VPC内、VPC外両方のaccesslogをtailしておく
- Route53で prepan.org のDNSの向き先を新しいEIPに変える
- 大体アクセスログが古い方に来なくなったので、一応昔のEC2のAMIを作り、インスタンスを削除
- 完
図にするとこんな感じ。VPC外部に接続するときはインターネットゲートウェイを通っているはずだけど、図にするの大変だったので、簡易的に書いている。
DNS切り替えでいけるため、ダウンタイムは0で完了できた。ここまででまずはWeb用のサーバーをVPC内のEC2インスタンスに置き換え、t2.microを使うようにした。ここでコストが大体$10/month下がった。
4. RDSを入れるVPCネットワークを設定する
この調子でRDSも移行していく。実はRDSは使っていたインスタンスの設定で、VPC外のRDSをVPC内にマイグレーションすることができる(!)。参考は https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/UserGuide/USER_VPC.WorkingWithRDSInstanceinaVPC.html#USER_VPC.Non-VPC2VPC 。
VPC内でRDSを使おうとするとサブネットグループみたいな概念が出てきて戸惑うと思う。前提として、VPC内でRDSを使う手順や概念を知りたければこちらを読んでいくと良い。ここで抑えておきたいポイントは
- VPC内でRDSを設定するためには、リージョンの異なるサブネットが最低二つ必要で、それらをサブネットグループとしてまとめる必要がある
- Multi-AZ配置のためかと思うが、Multi-AZ配置を使わなくても、この設定が必要
- これはMulti-AZ対応をボタン一発でできるようにするためかと思われる
それではネットワークの設定をしていく。https://github.com/metacpan/prepan-cookbooks/pull/17/files のPRでCloudFormationの設定を作成し、適用した。やっていることとしては
- RDSを入れるためのプライベートサブネットを二つ作る
- サブネットグループ設定のためにリージョンは異なるようにする
- 作ったプライベートサブネット二つをRDSのサブネットグループとして設定する
- RDS用のVPCセキュリティグループを作る
- アプリケーションサーバのセキュリティグループからの通信だけ通すようにする
という感じ。
ネットワークだけを図に表すとこんな感じになる。
5. RDSの設定変更でVPC内に移行する
最後にRDSをVPC内に移行する。本番作業の前に、RDSインスタンスをスナップショットから新しく立てて、同じ構成を作り、WebサーバのEC2からアクセス可能かを確認しておくと良い。それが終わったら実際に本番のRDSをVPC内に移行していく。
作業手順をメモしていたので、それを以下に貼り付ける。
まず、EIPの関連付けをやめて、アクセス遮断。
RDSの設定で、VPC内に移す https://us-west-1.console.aws.amazon.com/rds/home?region=us-west-1
- この時点ではインスタンスタイプをt2.microに出来ない
- サブネットグループとセキュリティグループを選択
- すぐに適用する
- 「すぐに適用する」を選択すると、RDSにダウンタイムが発生する
移ったらSSHしてアクセスできるか試す。
curl localhost:8000
続いてそのままインスタンスタイプの変更
- https://us-west-1.console.aws.amazon.com/rds/home?region=us-west-1 からインスタンスタイプを変更し、t2.microに
- すぐに適用
- 「すぐに適用する」を選択すると、RDSにダウンタイムが発生する
アクセスできるか確認。
curl localhost:8000
最後にEIPをもう一度割り当てて終わり!ここまでの作業が20分なので、大体ダウンタイムは20分くらいだった。これでRDSのインフラコストが$10/monthほど下がった。めでたい。
これにより構成が最初に示した以下のものになった(簡単のためサブネットグループなどは書いていない)。
ちなみにRDSのメンテナンスウインドウに設定を適用するようにすれば、おそらくダウンタイム0でRDSも移行出来たと思う。しかし、設定がミスってていきなり仕事中とかに対応しないといけなくなるとかは嫌だったので、今回はダウンタイムを受け入れて移行を行った。
まとめ
今回はVPCがなかったEC2-Classic時代のEC2/RDSを、VPCのEC2/RDSへ移行した話について書いてみた。今回の作業でVPCのネットワーク設定やCloudFormationに詳しくなれたので非常に良かった。
今回の作業の前提知識として以下の本が最適だったのでおすすめです。
参考
- http://prepan.org/
- https://github.com/metacpan/prepan-cookbooks
- CloudFormationでVPCを構築し、CloudFormationに入門する方法を知った - $shibayu36->blog;
- CloudFormationでEC2の立ち上げをした - $shibayu36->blog;
- CloudFormationで立ち上げたEC2に自動でApacheをインストールして起動するようにした - $shibayu36->blog;
- 「Amazon Web Services 基礎からのネットワーク&サーバー構築 改訂版」でAWSサーバ構築とCloudFormationに入門した - $shibayu36->blog;
- CloudFormationのクロススタック参照を利用してネットワーク設定とWebサーバ設定を分ける - $shibayu36->blog;
- VPC の DB インスタンスにアクセスするシナリオ - Amazon Relational Database Service
- [新機能]Launch Templates for Amazon EC2 instancesを試してみた #reinvent | DevelopersIO
- https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/UserGuide/USER_VPC.WorkingWithRDSInstanceinaVPC.html#USER_VPC.Non-VPC2VPC
- https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/UserGuide/USER_VPC.WorkingWithRDSInstanceinaVPC.html