AWSからあの有名なAWSへ移行した話

## 概要
会社も大きくなってきたので、弊社も今流行りのAWSへ移行することになりました。元々弊社は世間でよく使用されているAWSというクラウドにサービスを展開していました。そこで他の方も同じ状況に遭遇すると思い、AWSからAWSへの移行とそこで得られた知見を参考までにお話します。
主な特徴はダウンタイムを抑えるため、VPCピアリングを使い段階的に移行したことであり、その部分について特に詳細に記述しました。
タイトルはへんてこさんに考えてもらいました。

## 移行内容
### 用意したもの
- 移行元のAWS環境
- 移行先のAWS環境
    - 新たに移行先のAWSルートアカウントを作成し、まっさらな環境を用意しました。

### 移行元構成 (Old)
微妙にマイクロサービスの様相を呈している（アカウント管理を司る内部APIがあり、各サービスは基本的に独立している）アーキテクチャです。アプリケーションサーバはELBを通すもの（EC2 Instance サービスA, B, C...）と通さないもの（EC2 Instance サービスE, F, G...）があります。詳しくは以下をご覧ください。
![旧環境](https://qiita-image-store.s3.amazonaws.com/0/18606/7d28319d-5fba-783c-7233-d26ced81356e.png)

### 今回移行するもの
- ドメイン as Route 53
- マシン as EC2
- データベース as RDS
- オブジェクトストレージ as S3

### 移行先構成 (New)
移行元とほぼ変わりませんが、全てのウェブサーバやAPIサーバをプライベートサブネットに置き、ウェブからのアクセスには必ずELBを通し、外に出る際はNATインスタンスを経由するようにしました。
図に書き忘れていましたが旧も新もSSHで繋ぐための踏み台インスタンスがあります。
![新環境](https://qiita-image-store.s3.amazonaws.com/0/18606/cf951bc4-99d8-d34d-dd1f-6fd1f3e061a4.png)

## 移行手順
|No|作業|ウェブサーバ|内部API|DNS|データベース|サービス停止|
|:-:|:--|:-:|:-:|:-:|:-:|:-:|
|1|ウェブサーバの移行|New|Old|Old|Old|無|
|2|内部APIの移行|New|New|Old|Old|無|
|3|DNSの移行|New|New|New|Old|無|
|4|データベースの移行|New|New|New|New|有|

## 事前に行ったこと
### VPCピアリングで旧環境と新環境を繋いだ
移行手順にあるように移行は段階的に行いたかったので、新環境から旧環境の内部APIやデータベースにアクセスできるよう、旧環境と新環境間でVPCピアリングを行いました。これにより、アプリケーションサーバは新環境に置き、そのアプリケーションサーバからアクセスする内部APIは旧環境に置くという構成が取れるようになりました。VPCピアリング越しにRDSへアクセスすることも出来るので便利です。VPCピアリングさまさま。

### 内部APIのエンドポイントを内部DNSで管理
元々内部APIのエンドポイントはAWSから発行されるInternalなホスト名か、もしくは/etc/hotsに内部APIとホスト名の対応を記述し、そのホスト名をアプリケーション内の設定ファイルに記述していました。しかしこれには不都合があり、前者であれば運用するインスタンスを変更したり一つのインスタンス内に複数のAPIを持たせ辛く、後者は内部APIへアクセスするインスタンス全ての/etc/hostsについて同期を取らなければならないという問題がありました。
これらの問題を一掃するため、移行前に内部APIのエンドポイントをRoute 53を使用した内部DNSで管理するようにしました。これによる副次的な効果として、これまではステージングと本番で内部APIのエンドポイントを設定ファイルで切り替えていたのですが、ステージングと本番のそれぞれの内部DNSで内部APIのエンドポイントとホストの対応付けを定義することにより、設定ファイルを使用した切り替えが必要無くなりました。

### Chefのレシピを作成
今回、秘伝のタレが存在しているということもあり、AMIやrsyncでの移行は見送り全て一からChefで環境を構築するようにしました。これにより暗黙知が少なくなるとともに、本番とステージングを同じようにデプロイ出来るようになりました。簡単に構成をご紹介します。

- 本番とステージングの切替はenviromentsで行う。以下を管理
    - サービスのドメイン
    - デプロイブランチ
    - アプリケーションの設定ファイル
    - アクセス時の認証
- nodesに各インスタンスで使用するレシピを記述
- templatesにnginx confの各種雛形を定義
- definitionにサービスデプロイの雛形を定義
- recipeに基本環境を整えるレシピ、definitionを使用した各サービスのデプロイレシピ（10以上）を定義
- data_bagsに暗号化した秘密鍵を保存

### NATインスタンスを用意
弊社では外部APIをいくつか使用しており、その内のいくつかはアクセス元のIPアドレスを申請する必要がありました（申請しないとアクセスできない）。しかしそのAPIを使用するインスタンスは増減することがあり、アクセス元のインスタンスが固定ではありませんでした。増減の度に毎回アクセス元IP申請するのはしんどいので、NATインスタンスを立て静的IPを振り、外に出る際は必ずそのインスタンスを通るようにしました。これにより外部APIに申請するIPは一つで済み、そのAPIを使用するインスタンスが増減する場合にも再申請の必要はなくなりました。

## 実際の移行
### 1. ウェブサーバの移行
永続化データについては旧環境のものを見に行くようにし、ウェブサーバのみ移行しました。

![1. ウェブサーバの移行](https://qiita-image-store.s3.amazonaws.com/0/18606/fac53e47-b50e-373f-e3a2-6d039d3c3ea7.png)

### 2. 内部APIの移行
アカウント管理の内部APIを移行しました。

![image](https://qiita-image-store.s3.amazonaws.com/0/18606/0da4041d-9a6f-d375-4597-01189ed5d990.png)

### 3. DNSの移行
まず新環境の動作確認をしました。そして、これまで一般ユーザからのアクセスはOldに流れていたのですが、それをNewへ流すようにしました。

### 4. データベースの移行
サービスを一時停止し、データベースを移行しました。動作確認をしました。

![image](https://qiita-image-store.s3.amazonaws.com/0/18606/e39c08ab-957a-7773-5967-0e368dc9765e.png)

## 終わりに
数十のサービスを抱えるにも関わらず、サービス停止は数時間で済みました。
微妙に図が間違っている（S3には別にVPC Peeringを通してアクセスしてない、EBSにはVPC Peering越しにアクセス出来ない）ですが、力尽きたので聖夜が来る前に投稿とさせて頂きます。