【ITニュース解説】Building a Complete AWS VPC with Load Balancer: A Step-by-Step Journey
2025年10月05日に「Dev.to」が公開したITニュース「Building a Complete AWS VPC with Load Balancer: A Step-by-Step Journey」について初心者にもわかりやすく解説しています。
ITニュース概要
AWSでVPCとロードバランサーを用いた本番環境インフラ構築の手順を解説。構築中に遭遇するDNSや接続、ALB関連のトラブルとその解決策を実例で示し、初心者が実践的な知識とトラブルシューティングスキルを習得できるガイド。
ITニュース解説
AWSでWebアプリケーションのインフラを構築する際、仮想プライベートクラウド(VPC)やロードバランサーの設定は非常に重要だが、多くの人が途中でつまずくポイントでもある。この記事では、VPCの構築から、アプリケーションロードバランサー(ALB)を使ったWebサーバーの公開までを、具体的なトラブルと解決策を交えながら解説していく。アイルランドのオンラインストア「Dublin Delights」を想定した環境構築を例に、実際に手を動かしながら学べる内容となっている。
最終的に目指すのは、公開サブネットとプライベートサブネットを持つVPC、インターネットゲートウェイ(IGW)、NAT Gateway、ALB、そして合計3台のEC2インスタンス(1台は公開用、2台はプライベート用)を組み合わせた、本番環境でも通用する堅牢なシステムである。さらに、これらのリソース間の通信を制御するセキュリティグループやルーティングの設定も詳細に見ていく。
まず、AWSのクラウド上に自分専用の仮想的なネットワーク空間であるVPCを構築する。VPCコンソールから「VPCとその他」を選択すると、必要な基本的なコンポーネントが一度にセットアップされるため、初心者には特におすすめだ。VPCの名前を「dublin-delights-vpc」とし、IPv4 CIDRブロックには「10.0.0.0/16」を指定する。これは、このVPC内で利用できるIPアドレスの範囲を定義し、約65,000個のIPアドレスが使用可能になることを意味する。可用性ゾーン(AZ)は複数(例えばUS-east-1aとUS-east-1bの2つ)選び、公開サブネットを1つ、プライベートサブネットを2つ作成する。各サブネットにはそれぞれ異なるCIDRブロック(例: 公開サブネットに10.0.1.0/24、プライベートサブネットに10.0.2.0/24と10.0.3.0/24)を割り当てる。NAT Gatewayは公開サブネットに1つ配置する設定でVPCを作成する。
この一括作成によって、指定したCIDRブロックを持つVPC、公開サブネットとプライベートサブネット、VPCとインターネットをつなぐインターネットゲートウェイ(IGW)、そしてプライベートサブネット内のサーバーがインターネットに出ていくためのNAT Gatewayが自動的に生成される。また、これらの通信経路を定義するルートテーブルも作成される。
ここで最初の問題に直面した。VPC作成後にEC2インスタンスを起動した際、DNS(Domain Name System)の名前解決ができないというエラーが発生したのだ。これにより、インスタンスがインターネット上のドメイン名(例えばGoogleのサイトなど)をIPアドレスに変換できず、「sudo apt update」のようなコマンドも失敗する事態となった。この問題は、VPCの設定で「DNSホスト名」と「DNS解決」の両方を有効にすることで解決した。VPCコンソールから該当のVPCを選択し、「アクション」→「VPC設定の編集」でこれらのチェックボックスをオンにするだけで、インスタンスがインターネット上のサービスにアクセスできるようになる。
次に、アプリケーションを動かす仮想サーバーであるEC2インスタンスを3台起動する。1台はインターネットから直接アクセス可能な公開インスタンス(public-web-1)、残り2台はロードバランサーの背後で動作するプライベートインスタンス(private-web-2, private-web-3)とする。
公開インスタンスはUbuntu Server 22.04 LTSのAMI(Amazon マシンイメージ)を使用し、t2.microタイプで、先ほど作成した「dublin-delights-vpc」の公開サブネットに配置する。「パブリックIPの自動割り当て」を有効にすることで、インターネットから直接アクセスするためのパブリックIPアドレスが自動で付与される。このインスタンスには「public-sg」という新しいセキュリティグループを割り当てる。
プライベートインスタンスも同様にUbuntu Server 22.04 LTS、t2.microタイプだが、それぞれ異なるプライベートサブネット(private-subnet-1とprivate-subnet-2)に配置する。「パブリックIPの自動割り当て」は無効にし、インターネットから直接アクセスできないようにする。これらのインスタンスには「private-sg」という新しいセキュリティグループを割り当てる。
インスタンスを起動後、すぐにSSH(Secure Shell)で接続しようとしたが、「Connection timed out」という接続タイムアウトエラーが発生した。この原因はセキュリティグループの設定ミスだった。セキュリティグループは仮想的なファイアウォールであり、インスタンスへの通信を許可・制限する役割を果たす。
この問題を解決するため、セキュリティグループのルールを修正した。「public-sg」では、SSH(ポート22)を自分のPCのIPアドレスからのみ許可し、HTTP(ポート80)はインターネット上のあらゆるIPアドレス(0.0.0.0/0)から許可する設定とした。アウトバウンド(インスタンスから外部への通信)は「すべてのトラフィック」を許可した。「private-sg」では、SSH(ポート22)を「public-sg」からのアクセスのみ許可し、HTTP(ポート80)は後で作成するALBのセキュリティグループからのみ許可するように設定する。アウトバウンドは同様に「すべてのトラフィック」を許可した。これらの設定を適用することで、公開インスタンスへのSSH接続が可能になった。
さらに、AWS Session Manager(インスタンスにSSHキーを使わずに安全に接続するサービス)を使おうとしたところ、「SSM Agent is not online」というエラーが出た。これは、公開サブネットが実際にはインターネットに接続されていなかったことが原因だった。公開サブネットは名前の通りインターネットに公開されている必要があるが、そのためのルーティング設定が不足していたのだ。VPCコンソールで公開サブネットのルートテーブルを探し、宛先「0.0.0.0/0」(すべてのIPアドレス)に対するターゲットとして、作成済みのインターネットゲートウェイ(dublin-igw)を追加することで、公開サブネット内のインスタンスがインターネットにアクセスできるようになり、Session Managerも正常に動作するようになった。
次に、Webアプリケーションへのトラフィックを複数のインスタンスに分散させるアプリケーションロードバランサー(ALB)を設定する。ALBはEC2コンソールから作成し、「dublin-alb」という名前を付ける。インターネットからアクセスできるように「Internet-facing」とし、VPCには「dublin-delights-vpc」を選択、可用性ゾーンは公開サブネットのある両方のAZを選択する。ALB専用のセキュリティグループ「alb-sg」を新たに作成する。
ALBがトラフィックを転送する先のサーバーの集合をターゲットグループと呼ぶ。EC2コンソールで「web-targets」という名前のターゲットグループを作成し、ターゲットタイプは「インスタンス」を選択、プロトコルはHTTP、ポートは80とする。ヘルスチェックパスは「/」に設定し、作成した3台のEC2インスタンス(public-web-1, private-web-2, private-web-3)をこのターゲットグループに登録する。
ALBが作成され、ターゲットグループにインスタンスが登録された後、ALBのDNS名にアクセスすると「504 Gateway Time-out」というエラーが発生し、ターゲットグループのインスタンスも「Unhealthy(不健康)」と表示された。これには二つの根本原因があった。
一つ目は、EC2インスタンス上にWebサーバーがまだインストールされていなかったことだ。インスタンスにSSHで接続し、Apache Webサーバーをインストールし起動することで解決した。また、簡単なHTMLファイル(例: 「<h1>Web Server - [ホスト名]</h1>」)を「/var/www/html/index.html」に作成し、Webサーバーが正しく動作することを確認した。
二つ目の原因は、ALBに割り当てられたセキュリティグループ「alb-sg」にアウトバウンド(外向き)のルールが設定されていなかったことだった。ALBは、ターゲットインスタンスのヘルスチェックを実行したり、ユーザーからのリクエストをインスタンスに転送したりするために、外部への通信を許可する必要がある。この「alb-sg」のアウトバウンドルールに「すべてのトラフィック」(0.0.0.0/0)を許可する設定を追加することで、ALBがターゲットインスタンスと正常に通信できるようになり、Webサーバーへのアクセスも可能になった。
ここまでの設定を終えた後、各セキュリティグループのルールを最終的に確認し、本番環境にふさわしい適切な設定にする。「alb-sg」は、インバウンドにHTTP(ポート80)をインターネット全体(0.0.0.0/0)から許可し、アウトバウンドはすべてのトラフィックを許可する。「public-sg」は、インバウンドにSSH(ポート22)を自分のIPアドレスから、HTTP(ポート80)を「alb-sg」から許可し、アウトバウンドはすべてのトラフィックを許可する。「private-sg」は、インバウンドにSSH(ポート22)を「public-sg」から、HTTP(ポート80)を「alb-sg」から許可し、アウトバウンドはすべてのトラフィックを許可する。この設定により、セキュリティを保ちつつ必要な通信のみを許可する堅牢な構成が実現する。
構築が完了したら、実際に動作するかをテストする。まず、公開インスタンスからプライベートインスタンスのWebサーバーに直接アクセスできるかを確認する。次に、ALBのDNS名にWebブラウザやcurlコマンドでアクセスし、Webページが表示され、リクエストが複数のインスタンスに分散されているかを確認する。最後に、プライベートインスタンスから「curl ifconfig.me」などのコマンドを実行し、インターネットにアクセスできること、その際にNAT GatewayのIPアドレスが表示されることで、NAT Gatewayが正しく機能していることを確認する。
AWSのインフラ構築ではいくつかの共通の問題が発生しやすい。「インスタンスがパブリックサブネットにない」というエラーは、該当サブネットのルートテーブルにインターネットゲートウェイへのルートが設定されていない場合に起こる。「参照されたグループID」に関するエラーは、既存のセキュリティグループルールを直接編集するのではなく、新規ルールとして追加することで解決できる場合がある。プライベートインスタンスからインターネットにアクセスできない場合は、プライベートサブネットのルートテーブルにNAT Gatewayへのルートが設定されているかを確認する。ALBのヘルスチェックが失敗する「Target.Timeout」エラーは、Webサーバーが起動しているか、セキュリティグループのルールが適切か、Webサーバーが正しいコンテンツを提供しているか(例: /var/www/html/index.html)を確認することが重要である。
これらの経験から得られた重要な教訓としては、セキュリティグループがステートフルである(インバウンド許可があれば、それに対するリターン通信はアウトバウンドルールなしでも許可される)こと、サブネットが「公開」であるかどうかは名前ではなくルートテーブルの設定で決まること、ALBはアウトバウンドルールがないと正常に機能しないこと、NAT Gatewayは必ず公開サブネットに配置する必要があること、そしてヘルスチェックがアプリケーションの健全性を維持するために不可欠であること、などが挙げられる。
この構成にかかるコストは、t2.microインスタンス3台、NAT Gateway、ALBを合わせた場合、およそ月額73ドル程度となる。コストを最適化するには、NAT Gatewayの代わりにNATインスタンスを使用する、使用しないインスタンスを停止する、またはリザーブドインスタンスを購入するなどの方法がある。
本番環境で利用するには、さらにセキュリティ強化(SSHアクセス制限、VPCフローログ、CloudTrail、IAMロール、暗号化)、監視体制の確立(CloudWatchアラーム、ALBログ、SNS通知)、高可用性(マルチAZ配置、オートスケーリング、ヘルスチェック)、パフォーマンス最適化(適切なインスタンスサイズ、ALBスティッキネス、CloudFront CDN)などを考慮する必要がある。
構築の過程で発生する問題は、AWSの仕組みを深く理解するための貴重な機会となる。エラーメッセージを注意深く読み、一つずつ解決していくことで、確かな知識とトラブルシューティング能力を身につけることができる。