はじめに
アプリケーションの開発は普段やっているが、その際にはあまりインフラに関しては意識しないで開発している事が多かった。今回は少しネットワークがどうなっているか?が気になって図解してみたので、その備忘録を残す(理解に誤り等があればご指摘いただけると幸いです)。
ネットワークの基本的な概念
いきなりどのようなネットワークになっているのか?を見ていく前に、ネットワークに関する重要そうな概念について理解を深めてみた。
IPアドレス(IPv4)
ネットワーク内の住所。ネットワーク機器やPCなどネットワークにつながっている端末の場所を特定するための値。グローバルIPとプライベートIPがある。
表記は192.168.56.2
のように10進数表記で4つの区切りで示される。コンピュータ内では2進数として扱われている。
- 参考:IPアドレスとは
サブネットマスク
ネットワークの範囲を定義するために使用する値。ネットワークを示す際にはこのサブネットマスクをIPアドレスとくっつけた表記がされる。具体的には、
192.168.56.2/24
のようにIPアドレス+サブネットマスク
のような表記で、これをCIDR表記という。
サブネットマスクはネットワークの範囲(サブネット)を指定するためのものだが、仕組みとしては以下の図の通りで、ネットワーク部(同一ネットワーク内で固定で同じになるIPアドレスの部分)とホスト部(ネットワーク内で端末などでそれぞれ異なる値になるIPアドレスの部分)がどこになるか?を数値で表現するものになる。
なので上記の図のように、192.168.56.0/24
というCIDRの場合、IPアドレスを2進数で表記した時の左から24桁(bit)分は固定部=ネットワーク部になり、可変部=ホスト部は2進数の残りの8桁(bit)分になる。つまり2の8乗=256パターンのIPアドレスを指定できるネットワーク、という事になる。ただし、192.168.56.0と192.168.56.255はそれぞれネットワークアドレス・ブロードキャストアドレスになるので使用できず、実際に使えるIPの範囲は1~254の254通りになる(2^8-2=254)。
サブネットマスクでよく見るのは、以下の3パターン。
CIDR形式 | IPアドレス形式 | IPアドレス形式を2進数にすると |
---|---|---|
/8 | 255.0.0.0 | 11111111 00000000 00000000 00000000 |
/16 | 255.255.0.0 | 11111111 11111111 00000000 00000000 |
/24 | 255.255.255.0 | 11111111 11111111 11111111 00000000 |
※サブネットをIPアドレス形式で書く場合、1になっている部分がサブネットマスクを意味するので上記の表のようなアドレスになる。もし/1
なら10000000 ...(0が24個)
なので10進数表記をするなら128.0.0.0
というサブネットマスクになる。
サブネットを作成する具体的な場面としては、インターネットからアクセスできるパブリックなサブネットと、インターネットからアクセスできないプライベートなサブネットを作成することで、プライベートなネットワーク(サブネット)はセキュリティが高いというような状態が作れる事(役割をネットワークごとに区切る)。サブネットが違うネットワークは直接通信できないので、ルーターやネットワーク機器を使用してネットワークを切り替えて通信する必要がある。
AWSだと以下のような形で、VPC内にパブリックサブネットとプライベートサブネットがある構成がよく見られるだろう(パブリックサブネットaはIP10.0.1.1~10.0.1.254までの254通りのIPを持つサブネットで、プライベートサブネットaはIP10.0.2.1~10.0.2.254までの254通りのIPを持つサブネット)。
※サブネットというとサブのネットという意味で、VPCを作成する時にVPC自体はサブじゃなくね?となったが、VPCそのものもサブネットという言い方をし、VPC内にサブネットマスクで新しく区切って定義したネットワークもサブネットという言い方をするみたい。
- 参考:サブネットマスクとは?
- 参考:サブネットマスク計算(IPv4)
ローカル環境の全体構成のイメージ
実際のアクセスの経路
起点はブラウザ(Chrome)で、終点はNode.js Expressのアプリケーションサーバー。その経路としては、
- Chrome
- Virtualbox内のDockerで立っているNignx
- Virtualbox内のNode.js Express
という経路になる。
実際に上記の経路でアクセスした際のは以下の図の通り。
以下では上記の経路の詳細を見ていきたいと思う。
アクセスの経路を詳細に追っていく
Chrome → Nignx
まず、https://example.com/
というパスでアクセスしていたが、example.com
というドメインは実際には存在しないドメイン。今回はWindowsのhostsファイルを利用して、なんちゃってDNSを利用してIPアドレスを解決している。
ファイルの中身は以下のようになっている(一部抜粋)。
user@DESKTOP-PS112J6 MINGW64 ~
$ cat /c/Windows/System32/drivers/etc/hosts
# Copyright (c) 1993-2009 Microsoft Corp.
#
# This is a sample HOSTS file used by Microsoft TCP/IP for Windows.
#
...
# localhost name resolution is handled within DNS itself.
# 127.0.0.1 localhost
# ::1 localhost
192.168.56.2 example.com
続いて192.168.56.2
というIPアドレスが何者か?だが、VirtualBox上でip addr
コマンドを実行すると分かる。
study@localhost:~/workspace/openid-connect (aws-iam-federation *)
$ ip addr
...
3: enp0s8: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 08:00:27:b1:62:72 brd ff:ff:ff:ff:ff:ff
inet 192.168.56.2/24 brd 192.168.56.255 scope global noprefixroute enp0s8
valid_lft forever preferred_lft forever
inet6 fe80::a00:27ff:feb1:6272/64 scope link
valid_lft forever preferred_lft forever
...
192.168.56.2/24
というのがあるが、上記のサブネットマスクで見たように、これはCIDR表記のIPアドレスで、192.168.56.0~192.168.56.255
というIPアドレスの範囲のサブネット(192.168.56.0/24というサブネット)に属している1つのアドレス。では192.168.56.0/24
というサブネットはどこから出てきたのか?(誰が作った者なのか?)という疑問が出てくるが、これはVirtualBoxのホストオンリーアダプターによって作成されたものになる(以下の図を参照)。
まず、IPアドレスのルール同様、192.168.56.0と192.168.56.255はそれぞれネットワークアドレス・ブロードキャストアドレスであり、利用できない。なので、実際に192.168.56.0/24というサブネットで利用可能なIPアドレスは192.168.56.1~192.168.56.254までになる。
そして、ホストオンリーアダプターとはホストOS(Windows)→ゲストOS(VirtualBox上のCentOS)との通信を可能にするもので、やっている事はどうやらゲストOSとホストOSを共通のネットワーク(今回だと192.168.56.0/24)に属させるというものと考えていいだろう(ホストOSと接続するにはホストオンリーアダプターを使うを参照)。そのゲストOS・ホストOS両方が属するネットワーク=ホストOSの方でもIPアドレスが割り振られる、という事になるがそのIPアドレスが192.168.56.1
というものになる。
それは実際にWindows上のcmdでipconfig
コマンドを実行すると確かめられる。
C:\Users\user>ipconfig
Windows IP 構成
...
イーサネット アダプター VirtualBox Host-Only Network:
接続固有の DNS サフィックス . . . . .:
リンクローカル IPv6 アドレス. . . . .: fe80::e186:8563:7ab9:2c23%20
IPv4 アドレス . . . . . . . . . . . .: 192.168.56.1
サブネット マスク . . . . . . . . . .: 255.255.255.0
デフォルト ゲートウェイ . . . . . . .:
...
192.168.56.2
の方の正体についてはまだ触れていなかったが、これはゲストOS(CentOS)に割り振られた192.168.56.0/24内のIPアドレス。
つまり、192.168.56.0/24内には以下の2つのIPアドレスを持つ機器が接続されているのと同じ状態になっている。
したがって、Chrome → Nignxとアクセスする時には、IPアドレスでアクセス経路を見ると、
- Chrome = Windows = ホストOS = 192.168.56.1 → Nignx = VirtualboxのCentOS = 192.168.56.2
というアクセスになる。そのため、実際のアクセスの経路で見たNignxのアクセスログの方に192.168.56.1というのが出力されていた。
Nignx
今回はDockerのNginxをdocker composeで立てていたが、DockerはDockerで独自のネットワークを持つ。が、今回はdocker-compose.yamlの方でportsを指定しているので、DockerのネットワークとCentOSのネットワークで通信できるようになっており、利用時には特に気にしないでいいようになっている。
version: '3.9'
services:
nginx:
image: nginx:latest
container_name: nginx
ports:
- 443:443
volumes:
- ./config/nginx:/etc/nginx/conf.d
- ./data/nginx:/var/log/nginx
上記のように設定すると、公式のShort syntaxに以下のように記載されている通り、ホスト(CentOS)のあらゆるIP(127.0.0.1など)からDockerのネットワーク内のポート443(Nginx)にアクセスする事ができるので、ChromeからNignxにアクセスする際、example.com(httpsのデフォルトのポートは443なので省略されている) → 192.168.56.2:443 → 0.0.0.0:443 → Docker内のNginx
という経路でアクセスできている。
Specify the host IP address to bind to AND both ports (the default is 0.0.0.0, meaning all interfaces): (IPADDR:HOSTPORT:CONTAINERPORT). If HOSTPORT is empty (for example 127.0.0.1::80), an ephemeral port is chosen to bind to on the host. (両方のポートにバインドするホストIPアドレスを指定します(デフォルトは0.0.0.0、つまりすべてのインターフェイスを意味します)。(ipaddr:hostport:containerport) を指定します。HOSTPORT が空の場合 (たとえば 127.0.0.1::80) は、そのホストのエフェメラルポートがバインド先として選択されます。)
ちなみに、DockerのNginxのIPアドレスは以下のように確かめられる。
study@localhost:~/workspace/openid-connect (aws-iam-federation *)
$ docker container exec -it nginx /bin/sh
# hostname -I
172.19.0.2
Nginx → Node.js Express(アプリケーションサーバー)
この経路はいわゆるリバースプロキシと呼ばれる部分で、Nginxに来たアクセスをNginxの設定(nginx.conf
)の設定でプロキシ(他の所に中継)する事でExpressというアプリケーションサーバーへのアクセスが実現されている部分。
Nginxの設定の中身は以下のようになっている(locationディレクティブの設定は一部省略している)。
server {
listen 443 ssl;
server_name example.com;
ssl_certificate /etc/nginx/conf.d/ssl/server.crt;
ssl_certificate_key /etc/nginx/conf.d/ssl/server.key;
location / {
# 省略
proxy_pass http://192.168.56.2:3000;
}
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
}
proxy_pass
というディレクティブがポイントで、これによりNginxに来たアクセス(リクエスト)はNginxを中継してhttp://192.168.56.2:3000
というIP・ポートに届くようになる。
経路のまとめ
上記の詳細を踏まえて全体の流れを整理すると、結局Chromeからのリクエストは以下のようにしてExpress(アプリケーションサーバー)に届いていたという事が分かる。
- Chrome:
example.com
にアクセス(ポートはhttpsデフォルトの443) - Windows:hostsファイルのDNSで
192.168.56.2
にアクセス(ポートは443) - VirtualBox:ホストオンリーアダプターのネットワーク内の192.168.56.1→192.168.56.2へのアクセス
- CentOS:
192.168.56.2:443
へのアクセスは、Dockerのネットワークに中継されて、Dockerのネットワーク上に存在するNginxへアクセス - Nignx:プロキシの設定により自身に来たアクセスを中継し、
192.168.56.2:3000
にアクセス - Express:最終的にアクセス(リクエスト)が届く
まとめとして
今回、普段あまり意識していないネットワーク(インフラの側面)についてアプリケーションエンジニアとして理解を深めてみて、色々な仕組みでアプリケーションサーバーにリクエストが届くようになっているんだなとしみじみ思った。本番環境だともっと複雑になると思うが、これをきっかけにインフラ面も理解を深めていきたいと思った。
上記の構成で実際に設定したコードは以下。