AWS上のコンテナはネットワークをどう利用するのか? ポート番号の扱いとDNSの仕組みを中心に
AWS、そしてネットワークの基礎知識は、なんらかのサービスを開発する際に不可欠なものです。本稿では、コンテナを利用してWebアプリケーションを開発するとき、Webアクセスはどのような技術を用いて成り立っており、どういったことに注意する必要があるのか、といった入門的情報をAWSに務める菊池之裕さんに説明してもらいました。
みなさん、こんにちは。アマゾン ウェブ サービス ジャパン株式会社でシニアソリューションアーキテクト ネットワークスペシャリストを務めております、菊池之裕と申します。私は普段、ネットワークに関連したお客さまの技術的な導入支援や課題解決などの業務に従事しています。
読者のみなさんは、ネットワークについての基礎知識をどれくらい持っていらっしゃるでしょうか? 普段、主にWebアプリケーションを開発されている若手エンジニアには、ネットワークについて詳しく知らない方も多いかもしれません。今回は、コンテナを利用してWebアプリケーションを開発している方に向けて、知っておきたいネットワークの基礎知識を解説していきます。
- Amazon ECSとAmazon VPCによるコンテナ管理の概要
- 仮想マシン、クラウドコンピューティング、そしてコンテナ
- コンテナはポートフォワーディングで外部と通信する
- DNSによってドメインを用いたアクセスを実現する
- おわりに ─ サービスの問題解決にもネットワークの知識を
Amazon ECSとAmazon VPCによるコンテナ管理の概要
今回の記事ではコンテナ利用者を対象としているため、コンテナの実行・管理にAWSの完全マネージド型のコンテナオーケストレーションサービスであるAmazon ECS(Elastic Container Service)を使用していることを想定して、解説を進めていきます。
Amazon ECSを用いてWebアプリをデプロイしている場合、それぞれのコンテナはAmazon ECS用のコンテナエージェントを実行しているAmazon EC2(Elastic Compute Cloud)、もしくはAWS Fargateによって管理された仮想マシン上で動いており、Amazon VPC(Virtual Private Cloud)というユーザーごとに隔離されたプライベートなクラウド環境上にホストされています。
外部との通信を行うために、Amazon VPCのコンテナとインターネットとの間にはインターネット・ゲートウェイというコンポーネントが配置されています。そして、VPC上のロードバランサーであるElastic Load Balancingを経由して、コンテナと通信できているのです1。
ロードバランサーとは、複数のサーバーにかかる負荷を平等に振り分ける装置のことです。仮に、1台のWebサーバーだけで運用を続けていると、アクセス数が増加した場合に負荷に耐えられなくなったり、ハードウェアの故障などが原因でサーバーが急に動かなくなってしまったりすることもあります。
そうした事態に備えて、Webサーバーを複数台用意しておき、ユーザーからのアクセスを各サーバーへと適切に割り振るようなシステム構成をとるのが一般的です。この役割を担うのがロードバランサーになります。アプリケーションへのリクエストをロードバランサーが受け付け、NAPT(Network Address and Port Translation)という仕組みを用いて、各Webサーバーに割り振っていきます。
仮想マシン、クラウドコンピューティング、そしてコンテナ
次に、Amazon EC2のような仮想マシンと、コンテナとの違いについても触れておきたいと思います。
読者の方々は、サーバーの仮想化と呼ばれる技術がどのようなものかをご存じでしょうか? これは、ハイパーバイザと呼ばれるソフトウェアを用いることで、物理サーバーを複数台の仮想的なサーバーのように分割して利用可能にする技術です。それぞれの仮想サーバーを独立したコンピューターのように扱い、個別のOSやアプリケーションを実行できるようになります。
仮想化の歴史は古く、約60年前のメインフレームの時代に遡(さかのぼ)ります。かつては貴重で高価だったコンピューターを、複数人が共同で利用するための手法として発展してきました。
仮想化の技術が広く普及するきっかけとなったのは、インテル社が仮想化支援技術であるIntel VT(Virtualization Technology)を開発したことです。Intel VTはコンピューターの仮想化をハードウェアレベルで支援してくれる拡張機能で、これによってインテルのサーバー上で比較的容易に複数の仮想マシンを立ち上げられるようになりました。仮想化により限られた物理サーバーのリソースを有効活用できるため、コスト削減や運用の効率化に結びつきます。その利便性の高さから、多くの企業に導入されることとなりました。
クラウドコンピューティングと仮想マシン
その後、クラウドコンピューティングという考えが出てきました。クラウドコンピューティングとは従量課金制で提供されるインターネット経由のオンデマンドのITリソースを指します。自前のデータセンターやサーバーを購入、所有そして維持することなく、企業は計算能力やストレージ、データベース、その他のサービスなどの技術を必要に応じて活用することができます。
AWSにおいては、Amazon EC2と呼ばれるコンピューティング環境を提供しており、ユーザーに貸し出している仮想マシンをEC2インスタンスと呼びます。Amazon EC2ではコンピューターの最下層に、ベアメタルと呼ばれる層のサーバーが存在しています。これはOSやソフトウェアなどがインストールされていない物理ホストです。その上にハイパーバイザの層があり、そのさらに上のEC2インスタンス上に仮想マシン用のOSが乗ってアプリケーションを動かしています。
一方、Amazon ECSでコンテナを動かす場合には、そのような仮想マシンのクラスタ上に複数のコンテナプロセスが立ち上がります。
コンテナが干渉しないためのカーネル技術
ここで1つの疑問が浮かび上がります。複数のコンテナでWebアプリケーションを立ち上げて、それぞれHTTPやHTTPSの通信を行うためポート番号の80番や443番を使用しているケースもあるはずです。同一の仮想マシン内で動いているそれぞれのコンテナは、お互いの干渉を受けないのでしょうか?
答えは「受けない」です2。干渉を避ける鍵を握っているのが、namespaceやcgroupというカーネルの機能になります。
namespaceを使うと、プロセスやファイル構造、ユーザーID、グループID、ネットワークなどをOSの中で分離し、それぞれの名前空間ごとに独立させることが可能です。namespaceによる分割を行うと、あるコンテナに存在しているプロセスは別のコンテナから見えなくなります。この技術により、複数のコンテナ上で80番ポートや443番ポートを使用するWebアプリケーションを立ち上げたとしても、それぞれが干渉しあわなくて済むのです。
また、cgroupを用いることでプロセスをグループ化し、特定のプロセスグループが利用できるCPUやメモリ、ディスクI/Oなどのリソースを制限できます。これにより、特定のコンテナがホストOSのリソースを消費し尽くしてしまい、別のコンテナのリソースに影響を与えるようなことがなくなります。
コンテナを利用しやすくしたDockerの先進性
namespaceやcgroupの技術そのものは昔からありましたが、利用するためには複雑な設定が必要であり、それほど多くの方には普及しませんでした。その状況を一変させたのが、Dockerです。Dockerの先進性は利用の容易さにありました。Dockerコマンドを叩くだけで、コンテナを用いるのに必要な手続きを全て完結させられるようになったのです。
Dockerにおいては、コンテナの構成情報を記述するためのファイルであるDockerfileや、コンテナを起動させるためのベースとなるDockerイメージなどの仕組みを用いることで、ユーザーは以下のような利点を享受できるようになりました。
- Dockerfileの記述を読めば、アプリケーションを動かすのに必要なライブラリやパッケージ、環境構築手順などの情報を把握できる
- 同一の実行環境を再現させることが容易であるため、開発やステージング、本番など複数の環境においてアプリケーションが同様の動作をすることを保証できる
- シンプルなコマンドでコンテナを生成できるため、作業省力化やオペレーションミスの低減につながる
また、Dockerイメージを一元管理するためのリポジトリサービスであるDocker Hubでは、各種Linuxディストリビューションの公式イメージや、有志によって作成された各種イメージなどが配布されています。Dockerのエコシステムを多くの方々が支えているのです。こうした利便性の高さやコミュニティとしての成熟度の高さから、Dockerは爆発的に普及しました。
コンテナはポートフォワーディングで外部と通信する
これまで解説してきた知識を前提に、仮想マシン上で動いているコンテナがWebサーバーとして外部と通信するまでに、どのような処理を行っているのかを解説していきます。
外部からのアクセスを受け付ける場合、基本的にはコンテナ自身のIPアドレスではなく、コンテナが動いている仮想マシン(ホスト)のIPアドレスに対してリクエストが送信されます。その後、仮想マシン上で動いている各コンテナに対してアクセスの転送が行われるのですが、ここである疑問が湧き上がります。仮に複数のコンテナが80番ポートを開放してWebアプリケーションを提供している場合、ユーザーはどのようにして各アプリケーションにアクセスすればいいのでしょうか?
ここで用いられるのが、ポートフォワーディングという仕組みです。ポートフォワーディングとは、コンピューターが特定のポート番号で受け付けた通信を、別のアドレスの特定のポート番号へと自動的に転送する機能を指します。
これにより、仮に仮想マシン上で2つのコンテナが起動し、それぞれがWebアプリケーションを立ち上げて80番ポートでリクエストを待ち受けていたとしても「49000番ポートに対するアクセスであれば前者のWebアプリケーションの80番ポートに転送し、49001番ポートに対するアクセスであれば後者のWebアプリケーションの80番ポートに転送する」といった操作が可能になります。
ポート番号のことを詳しく知らない方のために
ポート番号とは、TCP/IP通信においてコンピューターが通信に使用するプログラムを識別するための番号です。
TCP/IP通信では、IPアドレスを用いることで「ネットワーク上のどのコンピューターと通信するか」を識別できます。しかし、コンピューター上ではさまざまなプログラムが動いているため、その情報だけでは「コンピューターで動いているどのプログラムと通信するか」を決定できません。それを見分けるために、ポート番号という仕組みが生まれました。
例えば、HTTPやHTTPSで通信をしたい場合には、80番や443番のポートを用いるのが一般的です。各プロトコルで使われるポート番号の情報は、IANA(Internet Assigned Numbers Authority)という組織によって管理されています。
TCP/IPによる通信で利用されるポート番号のうち、0番から1023番までは著名なサービスやプロトコルが利用するために予約されている番号です。これをウェルノウンポートと呼びます。それ以外にも、MySQLにおける3306番ポートやRuby on Railsでの開発時にデフォルトで用いられる3000番ポートのように、ウェルノウンポートではないものの「多くの場合、このサービスではこのポート番号が使われる」と広く認知されているものもあります。
基本的には、通信を行うアプリケーション同士が「○○のポート番号でやりとりをする」と合意できていれば、何番のポートを使ってもかまいません。極端な話、80番や443番のポート以外でWebアプリケーションを外部に公開してもいいのです。しかし、こうしたルールからの逸脱は混乱を招いてしまうでしょう。基本的なポート番号の決まりを守っておいた方が、サーバーとクライアントとの間で期待された通信がしやすくなります。
Amazon ECSにおけるポートフォワーディングの管理
コンテナを利用する場合に、こうしたポートフォワーディングの情報を人間がすべて管理するのは骨の折れる作業ですし、設定を誤ればトラブルの原因になりかねません。そこでAmazon ECSでは、Amazon ECS用のコンテナエージェントを用いて、これらのマッピング情報を自動的に管理しているのです。
Amazon ECSはこのエージェントによってコンテナインスタンス内で稼働するコンテナの管理を行います。エージェントがネットワーク情報を適切に抽象化し、クライアントが443番ポートや80番ポートに対して送ってきたリクエストをポートフォワーディングして、各コンテナ内のWebアプリケーションに届けてくれます。
そのため、基本的にはAmazon ECSを使用している場合には、こうした複雑な設定についてアプリケーション開発者が考える必要はありません3。
▼ Application Load Balancerを作成し、Amazon ECSタスクを自動的に登録する方法
ローカル環境やオンプレミスサーバーのDockerでの管理
一方で、ローカル環境上でDockerを用いた開発を行っている場合や、オンプレミスのサーバー上でDockerを動かす場合などには、こうしたポートフォワーディングの設定について考慮しておく必要があります。
Dockerにおいて外部との接続にはネットワークドライバーが利用されます。ネットワークの接続方法にはいくつかのモードがありますが、デフォルトではbridgeモードが利用されるため、外部からコンテナに接続するにはポートフォワーディング処理の必要があります。
hostモードではDockerコンテナでアサインしたポートがそのまま使われます。よって同一のポートで動作するコンテナはポートが競合します。他にもbridge、overlayなどのモードがありますが、本稿では割愛します。
詳しくは、Dockerのドキュメントをご参照ください。
DNSによってドメインを用いたアクセスを実現する
ここまで、IPアドレスやポート番号を用いて、特定のホストやコンテナにアクセスする際に使われている技術を解説してきました。ですが、みなさんが特定のWebサービスを利用する場合、IPアドレスやポート番号を指定した接続は行わないでしょう。次のようなURLを用いてアクセスするはずです。
https://employment.en-japan.com/engineerhub/
URLを用いたアクセスを実現してくれているのが、ドメインとIPアドレスを対応付けて管理するDNS(Domain Name System)という仕組みです。DNS情報を提供しているサーバーのことを、ネームサーバーと呼びます。ネームサーバーはドメインの情報やホスト名、IPアドレスなどの情報を管理しています。
DNSのゾーンとリゾルバの働き
各ネームサーバーには「どのドメインを管理するか」という範囲が定められており、それをゾーンと呼びます。ネームサーバーは階層構造になっており、各サーバーはより上位のサーバーから権限委譲された範囲を管理しているのです。
DNSには、リゾルバというクライアント側の機能があります。これは、ネームサーバーへ問い合わせを行い、IPアドレスの特定を行ってくれる仕組みです。例えば、家庭でインターネットを利用される場合、家の中に置くブロードバンドルーターなどがリゾルバの役割を果たしています。
もし、最初に問い合わせたネームサーバーがドメインの情報を知っていれば、IPアドレスの情報を返却してくれます。知らなかった場合には、以下のような順序で問い合わせを行います。
例)xxx.co.jp
の場合:
- ルートネームサーバー4に問い合わせる
- ルートネームサーバーは
jp
ドメインを管理しているサーバーを教える
- ルートネームサーバーは
-
jp
ドメインを管理するネームサーバーに問い合わせる- 該当サーバーは
co.jp
ドメインを管理しているネームサーバーを教える
- 該当サーバーは
-
xxx.co.jp
ドメインを管理するネームサーバーに到達し、名前解決を行う
ドメイン情報をキャッシュする仕組み
リクエストが発生するたびに全てのリゾルバがDNS情報をサーバーへ問い合わせると多大な負荷がかかってしまいます。そのため、各リゾルバは一度取得したドメイン情報を一定の期間キャッシュし、期間内であればネームサーバーに問い合わせなくとも、DNS情報を返す仕組みになっています。
キャッシュを用いることで、次のような利点があります。
- ネームサーバーにかかる負荷を減らせる
- ネームサーバーへ問い合わせを行わないため、ユーザーへレスポンスを返す速度が向上する
リゾルバがDNS情報をキャッシュする期間の長さは、DNSが持つTTL(Time To Live)という設定によって決められています。数値は秒数を示しており、86400
と設定されていればキャッシュを24時間保持するという意味です。
AWSにおけるDNSの管理の注意点
AWSでは、Amazon Route 53というサービスを用いることで、DNSの管理・登録ができます。ロードバランサーを用いて外部との疎通を行う場合、DNSの設定で注意していただきたいことがあります。
AWSでは、各ロードバランサーに「xxx.amazonaws.com」のようなデフォルトの一意なドメイン名が割り振られています。このドメインと、ユーザーがWebアプリケーションを外部に公開するのに用いる「xxx.en-japan.com」のようなCNAMEを設定してください。
▼ Amazon Route 53を使用した、ELBへのトラフィックのルーティング
ロードバランサーのIPアドレスとDNS情報を紐付けてはいけません。実は、AWSにおいてロードバランサーのデフォルトDNSである「xxx.amazonaws.com」に紐づいているIPアドレスは固定ではありません。裏側では複数台のノードが立ち上がったり、廃止されたりをくり返しています。
例えば、クライアントから大量のリクエストが行われた場合には、そのロードバランサーに割り当てられるノードの台数が徐々に増えていきます(スケールアウト)。また、ロードバランサーとして稼働しているノードに不具合が生じた場合には、そのノードを退場させ代わりに別のノードが割り当てられます。これにより、ロードバランサーの可用性を高めているのです。
ノードが入れ替わり続けるということは、つまりIPアドレスが変動し続けることを意味します。だからこそ、サービスのドメインをロードバランサーのIPアドレスと紐づけてはいけないのです。
おわりに ─ サービスの問題解決にもネットワークの知識を
コンテナを利用してWebアプリケーションを開発している場合には、今回解説したような技術を用いてWebアクセスが成り立っています。これまで、ネットワークに関する知識をそれほど学んでこなかった、もしくはあまり興味のなかった方もいらっしゃるかもしれませんが、こうした技術を身に付けておくことで、エンジニアとしての幅が広がるはずです。
たとえアプリケーションを開発しているエンジニアだとしても、サービスを運用していくなかでアプリケーションレイヤーだけでは解決できない課題に直面することもあるでしょう。例えば、自社サービスに突如アクセスできなくなる障害が発生するケースもあるかもしれません。そうしたとき、アクセス集中が原因で高負荷になっているのか、設定ミスによりアプリケーションが機能していないのか、ネットワーク障害が起きているのかといった原因の切り分けができるようになれば、課題解決能力は大きく向上します。
Webアプリケーションであろうと、スマートフォンのアプリであろうと、ほとんどのシステムはネットワークのインフラがあるからこそ成り立っています。それなのに、ネットワークの知識を学ばずにブラックボックスのまま利用してしまうのは、ちょっと惜しい。絶対に陳腐化することのない技術ですから、覚えておいて損はないと思います。ぜひ若手エンジニアの方々に、ネットワークの基礎知識を身に付けていただければ幸いです。
菊池 之裕(きくち・ゆきひろ) yukihirokikuchi
構成・編集:中薗 昴