AWSから外部へ通信する時のGlobal-IPを固定させたい


1.ユースケース

やりたいことはざっくり以下の図のとおりです。

で、細かい要件が以下になります。

・AWS内のWebサーバからAWS外部のアプリケーションへアクセスする機能がある
・その外部アプリケーションサーバとは専用線接続などはできない(Private-ip通信ができない)
・外部アプリケーションでは、IPアドレスによるアクセス制限を行っている
・外部アプリケーション側は、IP制限設定を動的に処理させることができない(運用制約上)

IGWはいじれないので、EIPでなんとかする必要があります。。。

よくあるプラクティスは、「インスタンス起動時に新規でEIPをallocateしてassociateする」という方式になりますが、外部アプリケーション側に動的に設定を反映できないので、これは採用できませんでした。。。

2.実現方式の候補

ではどうするか。。。やり方としては大きく3方式考えられると思います。
 ①:NATサーバを立て、そこにEIPを割り当てる。
   Webサーバからのインターネットアクセスは、NATサーバを経由させる
 ②:Direct Connectを使ってオンプレ側のデータセンターとつないでいるのなら、
   インターネットアクセスは全てオンプレ側のProxyを経由させる
 ③:Webサーバ台数分のEIPをストックしておき、起動時にassociateするスクリプトを書く

①はNATサーバのインスタンスコストがかかるし、SPOF対策をすると、コストは2台分になり、動的ルーティング切換え処理も必要になってきます。このためだけにNATを立てるということなら、無しですね。
②は既存のオンプレ側のルーティング設定をいじることになるので、色々政治的な問題が発生したりしてしまいます。。。
③もEIPを遊ばせておくコストがかかってきますが、インスタンスコストに比べれば安いもんでしょう。

ということで今回は③のやり方を採用し、そのやり方をメモしておきます。

3.予め取得しておいたEIPから、空きを自動でassociateするスクリプト

次のスクリプトをインスタンス起動時に自動実行するように仕込んでやります。

#EIP割り当て処理
associate_eip()
{
  #インスタンスに割り当てられていないEIPを取得する
  ALLOC_ID_LIST=(`aws ec2 describe-addresses --query 'Addresses[?InstanceId=='null'].[AllocationId]' --output text`)

  #割り当て可能なEIPのひとつ(先頭)のAllocate-IDを取得
  ALLOC_ID=${ALLOC_ID_LIST[0]}

  #EIP割り当て(no-allow-reassociationを指定すれば、上書きされることがないので、衝突した場合はエラーが返される)
  `aws ec2 associate-address --instance-id <自分のInstance-Id> --allocation-id ${ALLOC_ID}  --no-allow-reassociation`
  if [ $? != 0 ]; then
    return 1
  fi
  return 0
}


# main

#EIP割り当て処理が成功するまで繰り返し(同時起動による衝突を考慮するため)
STATUS="failed"
while [ "${STATUS}" = "failed" ]; do
  #EIP取得処理の呼び出し
  associate_eip
  #成功したらSTATUSを更新してループを抜ける
  if [ "$?" -eq 0 ]; then
    STATUS="success"
  fi
done


空いているEIPを取得し、そのEIPをassociateします。
ただ、オートスケールで複数台同時に起動したような場合、複数のインスタンスで同じEIPを取得してしまう可能性があります。
このあたりの排他制御を厳密にやろうとすると面倒なので、とりあえず取得したEIPを割り当てようとします。
もし割り当てに失敗したら、空いているEIPを再度取得し直してassociateを試みる、
この繰り返しをしています。



エラー処理は別途必要ですが、こんなロジックで対応してみました。
もっと良いやり方があれば教えてください。