Amazon Web Services ブログ
AWS のサイトですか? AWS Lambda を使用したドメインの識別
以下のゲスト投稿で、私の同僚である Tim Bray は IsItOnAWS.com を構築した方法について説明しています。このサイトは、AWS の IP アドレスレンジのリストと Tim が記述した AWS Lambda 関数を使用して、お気に入りのウェブサイトが AWS で実行されているかどうかを調べることを目的としています。
— Jeff;
AWS のサイトですか?
クリスマスの時期に遊び半分でプログラミングをしていたら、おもしろい Lambda 関数ができました。きっと気に入ってもらえると思います。指定したドメイン名 (または IP アドレス) (IPv6 でも可能) が、公表されている AWS IP アドレスレンジ に含まれているかどうかを調べてくれます。IsItOnAWS.com で実際に試してみることができます。構築の過程では、1 つの Lambda 関数で別の Lambda 関数を作成しています。JSON 形式の IPv4 および IPv6 CIDR で提供されているレンジのリストはここです。説明書はここで、Jeff Barr のブログ もあります。以下は、JSON 形式の IP レンジの例です。
これを見た瞬間、「IsItOnAWS.com ができるのではないか」と思いました。可能だと分かったので、これを構築しました。以下のようなサイトにしたいと思いました。
- サーバーレス (クールな人たちはみんなそうしているので)
- シンプル (数字の範囲の中で数値を調べるというシンプルな課題なので)
- 高速。当然ですよね。
データベースか否か
構築はとても明快に思えました。IP レンジを表にまとめ、アドレスを調べるということです。テーブルはどこに配置すればいいでしょうか。Amazon DynamoDB にしようかと思いましたが、実質的に数値のレンジから検索するのは明快ではありませんでした。明快な所で SQL データベースも考えましたが、上記の 2 番目に当てはまりません。Redis やそれに似た方法を考えましたが、インスタンスのプロビジョンが必要で、上記の 1 番目に当てはまりません。数日間この問題を考え続けて行き詰ってしまいました。その時、1 つの疑問が浮かびました。レンジのリストはどれほど大きいのだろう。エントリの数は 1000 未満であることが分かりました。そうであれば、そもそもデータベースは必要ないのではないでしょうか。JSON を配列して、バイナリサーチすることにします。そうすると、配列はどこにすればいいでしょうか。Amazon S3 なら簡単でしょう。でも、上記の 3 番目を見てください。S3 は高速ですが、すべてのリクエストのループにそれが必要でしょうか。それで、配列リテラルとしてレンジを含む小さなファイルを生成し、それを IsItOnAWS Lambda 関数自体の中に含めることにしました。この方法だと、IP アドレスが変更されるたびに関数を再構築してアップロードする必要があります。アドレスを気にするのであれば、Amazon Simple Notification Service (SNS) トピックへの受信登録をし、変更されるたびに通知が届くようにできます (最近の私の経験によると、週に 1、2 回の頻度です)。そして、サブスクリプションを Lambda 関数に結び付けておきます。これで、だれもが必要とする道具をそろえることができたと感じました。Lambda 関数は 2 つあります。1 つめは、 newranges.js
で、変更通知を取得し、IP レンジのデータから JavaScript を生成して、2 つめの Lambda 関数である isitonaws.js
へアップロードします。これには JavaScript が含まれています。注意深い読者の方は、これはすべて Node ランタイムに関係するとお分かりでしょう。典型的な async または waterfall である新しいレンジ関数は、当初思っていたよりはやや複雑です。
Postmodern IP アドレス
最初のタスクは IP レンジの取得で、直接的な HTTP GET です。その後、JSON をより検索しやすい形にします。当然ですが、IPv4 と IPv6 の両方のレンジがあります。作業を容易にするため、単純な文字列や数字のマッチングで検索できる 1 つの配列にまとめようと思いました。IPv6 アドレスは JavaScript の数値で扱うには大きすぎるので、文字列にする必要があります。IPv4 のスペースが IPv6 の ("::ffff:0:0/96"
) に組み込まれている方法はやや驚きでした。BMP マッピングが低ビットの Unicode に組み込まれているようなものだと思っていました。このような方法になっているのはなぜかと漠然と考えはしましたが、調査はしていません。すべての CIDR をクラッシュして整然とした検索可能な配列にするコードは煩雑になりましたが、目的は達成できます。
Lambda の中に Lambda を構築する
次に、実際に IsItOnAWS
リクエストを扱う Lambda を構築します。zip ファイルである必要があり、NPM に作成に必要なツールがあります。後は、圧縮されたバイトを S3 に詰め込んでアップロードすれば、新しい Lambda 関数を作成できます。鋭い方はお気づきでしょうが、いったん zip を作成すれば、直接 Lambda にアップロードできます。フローを精錬するために後で生成された「レンジ」データ構造をダウンロードして確認したかったので、S3 を中間のステップとして使用しました。実際の IsItOnAWS
ランタイムは、名前でアドレスを探すために DNS を「たたく」ことと、レンジ配列に使用したのと同じ形式にするために多少工夫すること以外は、かなりシンプルです。HTML テンプレートの作成などはせず、zip の中のファイルを読み込み、表示されない <div>
があれば、それを結果に置き換えただけです。ただ、バイナリサーチの手法をコードにする必要はありました。10 年に 1 度あるかないかの作業でしたが、楽しくできました。ピースをまとめるすべてのコードを実行できるようになったら、Amazon API Gateway を使用して世界につなげてみたくなりました。以前これを実行したときは複雑な作業でしたが、今回は「プロキシリソースを通じて Lambda プロキシと統合された API を作成する」を参照していたので、比較的単純で予想外の事態はほとんど生じないと思えました。ただ、その説明は主に API の構築 (例:JSON in/out など) についてで、実際の使用例にはあまり触れられていません。実際に HTML を人に送信してブラウザに読み込ませる方法などは書かれていませんでしたが、その方法を考えるのは難しくありませんでした。以下はその方法です (Node より)。
一度すべてを API Gateway に接続できたら、最後のステップは isitonaws.com をそこに向かわせることです。そのため、このコードを記述したのは 12 月から 1 月にかけてでしたが、ブログにまとめるのは今になっています。当時は、Amazon Certificate Manager (ACM) 証明書は API Gateway で使用できませんでした。そして、時は 2017 年になり、証明書を承認して接続するのに古い学校の式典のようなことをする暇はありません。ACM により、証明書のプロセスはまったく思考を要しないものになっています。ACM と Let’s Encrypt が世に出たおかげで、今や非 HTTPS サイトを作成する理由は何もなくなりました。両方ともすばらしいツールですが、私のように API Gateway や CloudFront など AWS のサービスを使用している方であれば、ACM のほうがなじみやすいでしょう。自動更新もされる点は、きっと気に入るはずです。それで今や、ドメイン名を HTTPS と CloudFront 経由で API Gateway API に接続するのはとても簡単にできます。「API Gateway API のホスト名としてカスタムドメイン名を使用する」を参照してください。初めて試して、私の場合はうまくいきましたが、注意点があります (2017 年 3 月時点の話ですが)。最後のステップで ACM 証明書を API に接続する時、数分間、接続のために待機しなければなりませんが、これは普通のことです。幸い、私はあまり気にしていなかったため、あせって更新やキャンセルその他の操作はしませんでしたが、もしそうしていたら問題が生じていたかもしれません。ところで、API Gateway を使用する副次的な効果として、CloudFront を通してすべて実行されます。そのため、データベースを使わないので、高速な処理を期待できます。実際、ここバンクーバーでの話ですが、処理は高速に行われています。速度を計測する必要がないほど高速です。「IP レンジの変更」 SNS トピックに E メールで受信登録もしているので、変更があれば随時 E メールが送信されます。私はその通知を見てうれしくなります。自分の Lambda が新しい Lambda を記述して、すべてが自動で、手を煩わされずに、スムーズかつ高速に行われていると分かるからです。
— Tim Bray、シニアプリンシパルエンジニア