$shibayu36->blog;

クラスター株式会社のソフトウェアエンジニアです。エンジニアリングや読書などについて書いています。

AWSのVPC非対応なEC2/RDSをVPC内に移行した手順 - PrePANのインフラマイグレーション

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からのアクセスのみ許可する

これを最終的に以下のようにしたい。


  • prepan.orgはRoute53で管理されていて、EIPを向いている
  • EC2/RDS全体を覆うVPCがあり、パブリックサブネットとプライベートサブネットがある
  • パブリックサブネットにはWebサーバや非同期ワーカーが動くEC2が入っていて、EC2はSSH/HTTPだけを通すVPCのセキュリティグループ(app-sgとする)に入っている
  • プライベートサブネットにはRDSが入っていて、RDSにはMySQLのポート番号かつapp-sgからのアクセスを許可するVPCセキュリティグループに入っている
  • EC2はプライベートサブネットのRDSを参照する

作戦検討

画像からだけだと、単に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内に移行している。ここまではダウンタイムなしでいけるはず。続いて

  • 4. RDSを入れるVPCネットワークを設定する
  • 5. RDSの設定変更でVPC内に移行する

これで完了。5番の手順では頑張ればダウンタイムなしでもいけそうだったが、まあ多少は許容して良いだろうということで、20分程度のダウンタイムで乗り切った。


それでは以下1つずつ流れを載せていく。

1. Webサーバ(EC2)を完全にまっさらな状況から作れるように、元々のchefを動くように

もともとchefのcookbookが https://github.com/metacpan/prepan-cookbooks においてあった。けれどこれが結構古くに作ったもので、今そのまま適用しようとしたら、エラーで動かなかった。

まっさらな状態からWebサーバを作るためには、chefが動かないと厳しいので、ひとまずこれを動くようにした。PRは

あんまり面白いところはないのだけど、chefのアップグレードをしたり、berkshelfでインストールしているcookbookを更新したり、それで動かないところを直したりというのを地道にやっていった。

あとはローカルテスト用にvagrantを使っていたのだけど、それが参照するイメージはamazon linux 1のものにして、最終的に本番で適用するのと同じ環境になるようにした。


これでひとまずローカルで通るようになったら、このフェーズは終了。

2. Webサーバを入れられるVPCネットワークを設定する

続いて、Webサーバを入れられるVPCネットワークを設定する。ここで構築するVPCネットワークに最終的にRDSも入ることになる。


VPCネットワーク、最初いまいち分からなかったので、以下の本を見て、学習をしていった。

また、新しい技術を獲得しようと欲を持って、CloudFormationでネットワーク構築することにした。この勉強の様子は


実際に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ネットワークが一気に完成する。

ここでやったことは

  • VPCを作り
  • インターネットと接続されたパブリックサブネット(Webサーバ用)を一つ作り
  • Webサーバ用にSSHとHTTPの接続を許可したVPCセキュリティグループを作る

の三つ。


ここまででWebサーバを入れられるVPCネットワークが完成した。イメージは以下のとおり。

f:id:shiba_yu36:20180428143142p:plain

3. WebサーバをVPC内に新しく立て、prepan.orgがそちらを参照するように

さて、ようやくここから今本番で動いているWebサーバを、VPC内のEC2に置き換える作業をする。この作業により、VPC内のEC2からVPC外のRDSにつなぐようになる。このようなつなぎ方の時のシナリオについては VPC の DB インスタンスにアクセスするシナリオ - Amazon Relational Database Service が参考になる。

以下の流れで作業した。

  • 新しくVPC内にEC2立てて、chef適用
  • 新しくElastic IPを取得し、作成したEC2に割り当て
    • EC2-Classic時のElastic IPは、VPC内のEC2に割り当てできなかったので、新規作成する必要がある
  • RDSのセキュリティグループで、新規作成したEIPを通すように設定
    • RDSがVPC外にある場合、VPCの特定セキュリティグループからのアクセス許可はできない
    • そのため、CIDR/IP範囲の指定が必要
    • 例えば203.0.113.25/32からのアクセスを許可みたいにすれば、203.0.113.25のElastic IPが振られているVPC内EC2インスタンスからRDSに繋ぐことができるようになる
  • Webサーバ起動して、EC2にSSHしてcurl http://localhost:8000/ してアクセスできること確認
    • これでアクセス出来れば、正しくWebサーバが起動しており、RDSにも接続できていることが分かる
  • VPC内、VPC外両方のaccesslogをtailしておく
  • Route53で prepan.org のDNSの向き先を新しいEIPに変える
    • DNSttlは2分だったので2分待つ
  • 大体アクセスログが古い方に来なくなったので、一応昔のEC2のAMIを作り、インスタンスを削除

図にするとこんな感じ。VPC外部に接続するときはインターネットゲートウェイを通っているはずだけど、図にするの大変だったので、簡易的に書いている。

f:id:shiba_yu36:20180428151547p:plain


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セキュリティグループを作る

という感じ。


ネットワークだけを図に表すとこんな感じになる。

f:id:shiba_yu36:20180428155833p:plain

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

続いてそのままインスタンスタイプの変更

アクセスできるか確認。

curl localhost:8000

最後にEIPをもう一度割り当てて終わり!ここまでの作業が20分なので、大体ダウンタイムは20分くらいだった。これでRDSのインフラコストが$10/monthほど下がった。めでたい。


これにより構成が最初に示した以下のものになった(簡単のためサブネットグループなどは書いていない)。


ちなみにRDSのメンテナンスウインドウに設定を適用するようにすれば、おそらくダウンタイム0でRDSも移行出来たと思う。しかし、設定がミスってていきなり仕事中とかに対応しないといけなくなるとかは嫌だったので、今回はダウンタイムを受け入れて移行を行った。

まとめ

今回はVPCがなかったEC2-Classic時代のEC2/RDSを、VPCのEC2/RDSへ移行した話について書いてみた。今回の作業でVPCのネットワーク設定やCloudFormationに詳しくなれたので非常に良かった。

今回の作業の前提知識として以下の本が最適だったのでおすすめです。