ネットワーク構成図は、1枚に全部を描かず、「物理」「L2(VLAN)」「L3(IP とルーティング)」に分けて描くと、読み手が迷いません。 物理構成図は「何がどこにあり、どのポートとどのケーブルでつながっているか」、論理構成図は「どのネットワークに属し、どこを通って届くか」を表します。

図 答える問い 主に書くもの 使う人
物理構成図 どの機器が、どこに、何でつながっているか 設置場所・機器名・ポート番号・ケーブルの種類と速度 設置・配線・障害時の現地作業
論理構成図(L2) どの端末が、どの VLAN に属しているか VLAN ID と名前・アクセス/トランク・許可する VLAN スイッチの設定・追加
論理構成図(L3) どのネットワークを、どこを通って届くか サブネット・ゲートウェイ・インターフェースの IP・経路 ルーティング・FW のルール・障害の切り分け
クラウド構成図 どのサブネットに何を置き、外へどう出るか VPC・AZ・サブネット・ルートテーブル・NAT・IGW 設計レビュー・運用

この記事はネットワークの層の描き分けと、IP 設計の表し方に絞ります。システム全体(サーバー・アプリ・データの流れ)の構成図は、関連記事「システム構成図の書き方と例 Mermaidテンプレートと記号一覧」で扱っています。

確認環境:この記事の Mermaid の図は、すべて Mermaid 12.1.0(@mermaid-js/mermaid-cli 12.0.0、Linux)で描画できることを確かめました。IP アドレス設計表は Python 3.11 の ipaddress モジュールで重なりと使える数を検証しています。AWS の構成は、AWS のエミュレーター moto 5.2.4 に AWS CLI 1.46.1 を向けて実際に作り、describe-subnets・describe-route-tables の出力と、その出力から作った図を確かめました。AWS の仕様(予約 IP・サブネットの大きさ・NAT の置き方)は Amazon VPC のユーザーガイドで確かめています(確認日 2026年10月13日)。

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「ネットワーク構成図の書き方|記号・レイヤー別の書き分けとAWS構成の例」

書く前に決めること

決めること 例 理由
目的と読み手 「本社 LAN の配線替え用」「AWS 本番のレビュー用」 目的で描く層と細かさが決まる
範囲 本社だけ/本社+拠点+AWS 範囲の外は「雲」や点線の箱でまとめる
層 物理・L2・L3 のどれを描くか 1枚に混ぜると線が増えて読めなくなる
名前の付け方 RT01・CORE01・SW-2F・prod-vpc 図・設定・監視・台帳で同じ名前を使う
版と日付 図の右下に「v1.3 2026-10-13 作成者」 どの時点の図かが分かる
凡例 線の種類・色・記号の意味 読み手によって解釈が変わらない

名前は、機器の設定(ホスト名)と同じにします。 図だけ違う名前だと、障害のときに図と画面を突き合わせられません。

記号と線の一覧

機器の記号

ネットワーク図の記号は、ツールやメーカーごとに少しずつ違いますが、形の意味はほぼ共通です。図の中で同じ機器は同じ記号にそろえ、凡例を付けます。

機器 よく使われる記号の形 Mermaid で描くときの形 書き添える情報
ルーター 円柱、または矢印が入った丸 ["RT01<br/>ルーター"] 機器名・WAN 側の IP
L3 スイッチ 矢印の入った箱 ["CORE01<br/>L3 スイッチ"] SVI(VLAN の IP)
L2 スイッチ 横に長い箱に矢印 ["SW-2F<br/>L2 スイッチ"] 設置場所・ポート数
ファイアウォール れんがの壁 ["FW01<br/>ファイアウォール"] outside/inside の IP
サーバー 縦長の箱 [("SV01")](円柱) ホスト名・IP・役割
無線 AP アンテナ付きの箱・電波の形 ["AP-2F<br/>無線 AP"] SSID・つながる VLAN
ロードバランサー 矢印が分かれる箱 ["ALB"] リスナーのポート
インターネット・外部 雲 (("Internet"))(円) 回線の種類・事業者
NAT 両向きの矢印の箱 {{"NAT GW"}}(六角形) 置いたサブネット

AWS の構成図は、AWS が配っている公式のアーキテクチャアイコンを使うと、読み手が迷いません。公式ページでは、アイコンのパッケージは四半期ごと(Q1・Q2・Q3)に更新され、ライブラリに以前のアイコンが混ざっていないか確かめるよう書かれています。

線と囲みの意味

線・囲み 意味の例 Mermaid の書き方
実線 物理の接続(UTP・光) A --- B
太線 10G 以上・幹線・トランク A === B
点線 論理の接続(VPN・管理の通信・同期) A -.- B
矢印 通信の向き・経路 A --> B・A -.-> B
線のラベル ポート番号・速度・プロトコル A ---|"Gi1/0/1 - Te1/1/1<br/>10G"| B
囲み(箱) 部屋・ラック・VLAN・VPC・AZ・サブネット subgraph 名前 ... end
色 ゾーン(外部・DMZ・内部)、本番/検証 style や classDef

色だけで意味を表さず、凡例やラベルでも書きます。 白黒で印刷したときや、色の見分けが難しい人にも伝わります。Mermaid の記法全般は、関連記事「Mermaid記法一覧|全図種のコピペ用コードと描画結果まとめ」で扱っています。

物理構成図の書き方

書く内容

項目 書き方の例
設置場所 「1F サーバールーム ラック R1」「2F 執務室」を囲みで
機器名と機種 RT01(機種名)。機種・シリアルは台帳に回してもよい
ポート番号 両端を書く:Te1/1/1 - Te1/1/1
ケーブル 種類と速度:1G UTP・10G 光 SMF
台数が多い端末 「PC 群 x40」のようにまとめる
回線 事業者・回線種別・回線 ID(台帳の番号)

例:本社の物理構成図

flowchart TB
  isp(("ISP<br/>光回線"))
  subgraph rack["1F サーバールーム ラック R1"]
    rt01["RT01<br/>ルーター<br/>(機種名)"]
    fw01["FW01<br/>ファイアウォール"]
    core01["CORE01<br/>L3 スイッチ"]
    sv01[("SV01<br/>ファイルサーバー")]
  end
  subgraph f2["2F 執務室"]
    sw2f["SW-2F<br/>L2 スイッチ"]
    pc2["PC 群 x40"]
    ap2["AP-2F<br/>無線 AP"]
  end
  subgraph f3["3F 開発室"]
    sw3f["SW-3F<br/>L2 スイッチ"]
    pc3["PC 群 x30"]
  end
  isp ---|"ONU - WAN<br/>1G"| rt01
  rt01 ---|"LAN1 - outside<br/>1G UTP"| fw01
  fw01 ---|"inside - Gi1/0/1<br/>1G UTP"| core01
  core01 ===|"Te1/1/1 - Te1/1/1<br/>10G 光 SMF"| sw2f
  core01 ===|"Te1/1/2 - Te1/1/1<br/>10G 光 SMF"| sw3f
  core01 ---|"Gi1/0/10 - eth0<br/>1G UTP"| sv01
  sw2f ---|"Gi1/0/1-40<br/>1G UTP"| pc2
  sw2f ---|"Gi1/0/48 PoE<br/>1G UTP"| ap2
  sw3f ---|"Gi1/0/1-30<br/>1G UTP"| pc3

描画すると、ラック・フロアの囲みの中に機器が並び、幹線(10G 光)だけが太線になります。物理構成図には IP アドレスを書きすぎないようにします。 IP は L3 の図と設計表に任せ、物理の図は「ケーブルを抜くとき・挿すとき」に迷わないことを優先します。

ポートが多い場合は、図には代表だけを書き、ポートの対応表(配線表)を別に作ります。

自機器のポート 対向機器のポート ケーブル VLAN
CORE01 Te1/1/1 SW-2F Te1/1/1 10G 光 SMF trunk 10,99
CORE01 Te1/1/2 SW-3F Te1/1/1 10G 光 SMF trunk 20,99
CORE01 Gi1/0/10 SV01 eth0 1G UTP access 30
SW-2F Gi1/0/48 AP-2F LAN 1G UTP(PoE) access 10

論理構成図(L2):VLAN の書き方

L2 の図は「どの端末とどの端末が、同じブロードキャストの範囲(同じ VLAN)にいるか」を表します。 VLAN ごとに囲みを作り、スイッチ間の線にはトランクで通す VLAN を書きます。

flowchart LR
  core01["CORE01<br/>L3 スイッチ"]
  sw2f["SW-2F"]
  sw3f["SW-3F"]
  subgraph v10["VLAN10 業務 192.168.10.0/24"]
    pc2["2F の PC"]
  end
  subgraph v20["VLAN20 開発 192.168.20.0/24"]
    pc3["3F の PC"]
  end
  subgraph v30["VLAN30 サーバー 192.168.30.0/24"]
    sv01["SV01"]
  end
  subgraph v99["VLAN99 管理 192.168.99.0/24"]
    mgmt["各スイッチの管理 IP"]
  end
  core01 ===|"trunk<br/>許可 10,99"| sw2f
  core01 ===|"trunk<br/>許可 20,99"| sw3f
  sw2f ---|"access 10"| pc2
  sw3f ---|"access 20"| pc3
  core01 ---|"access 30"| sv01
  sw2f -.- mgmt
  sw3f -.- mgmt

VLAN 一覧表

図と一緒に、VLAN の一覧表を作ります。図は全体の形、表は正確な値、と役割を分けると、図が文字で埋まりません。

VLAN ID 名前 サブネット ゲートウェイ 用途・つながる場所
10 GYOMU 192.168.10.0/24 192.168.10.1(CORE01) 2F 執務室の PC・無線
20 DEV 192.168.20.0/24 192.168.20.1(CORE01) 3F 開発室の PC
30 SERVER 192.168.30.0/24 192.168.30.1(CORE01) 社内サーバー
99 MGMT 192.168.99.0/24 192.168.99.1(CORE01) スイッチ・AP の管理 IP
L2 の図で書くこと 書き方
アクセスポート access 10(1つの VLAN に属する)
トランクポート trunk 許可 10,99(通す VLAN を全部書く)
ネイティブ VLAN 使う場合は native 1 のように明記
管理 VLAN 業務の VLAN と分け、点線などで区別

VLAN ID と第3オクテット(192.168.10.0 の 10)をそろえると、IP を見ただけで VLAN が分かります。

論理構成図(L3):IP とルーティングの書き方

L3 の図は「パケットがどの機器を経由して届くか」を表します。 ネットワーク(サブネット)を箱、ルーティングする機器を箱にして、機器のインターフェースごとに IP を書きます。

flowchart TB
  inet(("Internet"))
  rt01["RT01<br/>WAN: 203.0.113.10/29<br/>LAN: 172.16.0.1/30"]
  fw01["FW01<br/>outside: 172.16.0.2/30<br/>inside: 172.16.0.5/30"]
  core01["CORE01 (L3)<br/>uplink: 172.16.0.6/30<br/>既定ルート → 172.16.0.5"]
  n10["VLAN10 業務<br/>192.168.10.0/24<br/>GW .1"]
  n20["VLAN20 開発<br/>192.168.20.0/24<br/>GW .1"]
  n30["VLAN30 サーバー<br/>192.168.30.0/24<br/>GW .1"]
  n99["VLAN99 管理<br/>192.168.99.0/24<br/>GW .1"]
  inet --- rt01
  rt01 ---|"172.16.0.0/30"| fw01
  fw01 ---|"172.16.0.4/30"| core01
  core01 ---|"SVI Vlan10 .1"| n10
  core01 ---|"SVI Vlan20 .1"| n20
  core01 ---|"SVI Vlan30 .1"| n30
  core01 ---|"SVI Vlan99 .1"| n99

203.0.113.0/24 は、ドキュメント用に予約されたアドレス(RFC 5737)です。図の例やマニュアルでは、本物のグローバル IP を書かずにこの範囲を使います。

IP・サブネットの書き方のルール

書くもの 書き方の例 ポイント
ネットワーク 192.168.10.0/24 CIDR(/ の後ろがマスクのビット数)で書く
インターフェースの IP 172.16.0.2/30 機器の IP にもマスクを付ける
同じネットワーク内の省略 GW .1 箱にネットワークを書いたら、ホスト部だけでよい
機器間のリンク 172.16.0.0/30 を線のラベルに 2台だけをつなぐリンクは /30(使える IP が2つ)
経路 既定ルート → 172.16.0.5 次に渡す先(ネクストホップ)を書く
冗長化の仮想 IP VIP .1(実 IP .2/.3) VRRP などは仮想 IP と実 IP を分けて書く
NAT 192.168.0.0/16 → 203.0.113.10 変換の前と後を矢印で書く

マスクの長さと使える IP の数は、関連記事「CIDR計算・サブネット早見表|/0〜/32一覧とPython計算コード」で扱っています。

ルーティング表を図の横に置く

図に全部の経路は書けないので、主な機器のルーティング表を添えます。

機器 宛先 ネクストホップ 備考
CORE01 0.0.0.0/0 172.16.0.5(FW01) 既定ルート
FW01 192.168.0.0/16 172.16.0.6(CORE01) 社内への戻り
FW01 0.0.0.0/0 172.16.0.1(RT01) インターネットへ
RT01 172.16.0.0/16・192.168.0.0/16 172.16.0.2(FW01) 社内への戻り

「行き」の経路だけでなく「戻り」の経路も書きます。 戻りの経路が無いと、片方向しか通信できない障害になります。障害の切り分けのコマンドは、関連記事「Linuxネットワークコマンド一覧|ip/ss/ncで障害切り分け」で扱っています。

IP アドレス設計表の作り方

設計表の項目

項目 例
区分 本社/AWS 本番/AWS 検証
名前 VLAN10 業務・app-a
CIDR 192.168.10.0/24
親の範囲 192.168.0.0/16(拠点に割り当てた範囲)
ゲートウェイ .1
固定 IP の範囲・DHCP の範囲 .2〜.49 固定/.50〜.250 DHCP
使える数・使用中の数 254/120

重なりを Python で確かめる

拠点や VPC を VPN・ピアリングでつなぐとき、アドレスが重なっていると経路が作れません。 設計表を作ったら、重なりと「親の範囲に入っているか」を機械的に確かめます。

"""IP アドレス設計表の重なり・範囲・使える数を確かめる"""
import ipaddress as ip
from itertools import combinations

plan = [  # 名前, CIDR, 親(含まれるべき範囲), AWS か
    ("本社 業務 VLAN10",  "192.168.10.0/24", "192.168.0.0/16", False),
    ("本社 開発 VLAN20",  "192.168.20.0/24", "192.168.0.0/16", False),
    ("本社 サーバ VLAN30", "192.168.30.0/24", "192.168.0.0/16", False),
    ("本社 管理 VLAN99",  "192.168.99.0/24", "192.168.0.0/16", False),
    ("public-a",  "10.10.0.0/24",  "10.10.0.0/16", True),
    ("public-c",  "10.10.1.0/24",  "10.10.0.0/16", True),
    ("app-a",     "10.10.10.0/24", "10.10.0.0/16", True),
    ("app-c",     "10.10.11.0/24", "10.10.0.0/16", True),
    ("db-a",      "10.10.20.0/24", "10.10.0.0/16", True),
    ("db-c",      "10.10.21.0/24", "10.10.0.0/16", True),
]
for n, c, parent, aws in plan:
    net = ip.ip_network(c)
    ok = net.subnet_of(ip.ip_network(parent))
    usable = net.num_addresses - (5 if aws else 2)   # AWS は 5 個予約
    hosts = list(net.hosts())
    print(f"{n:12} {c:16} 親に含まれる={ok}  使える数={usable:3}  先頭={hosts[0]}  末尾={hosts[-1]}")
bad = [(a[0], b[0]) for a, b in combinations(plan, 2)
       if ip.ip_network(a[1]).overlaps(ip.ip_network(b[1]))]
print("重なり:", bad or "なし")
print("本社と VPC の重なり:", ip.ip_network("192.168.0.0/16").overlaps(ip.ip_network("10.10.0.0/16")))
本社 業務 VLAN10 192.168.10.0/24  親に含まれる=True  使える数=254  先頭=192.168.10.1  末尾=192.168.10.254
本社 開発 VLAN20 192.168.20.0/24  親に含まれる=True  使える数=254  先頭=192.168.20.1  末尾=192.168.20.254
本社 サーバ VLAN30 192.168.30.0/24  親に含まれる=True  使える数=254  先頭=192.168.30.1  末尾=192.168.30.254
本社 管理 VLAN99 192.168.99.0/24  親に含まれる=True  使える数=254  先頭=192.168.99.1  末尾=192.168.99.254
public-a     10.10.0.0/24     親に含まれる=True  使える数=251  先頭=10.10.0.1  末尾=10.10.0.254
...
db-c         10.10.21.0/24    親に含まれる=True  使える数=251  先頭=10.10.21.1  末尾=10.10.21.254
重なり: なし
本社と VPC の重なり: False

アドレスの割り当ての考え方

考え方 例
拠点・環境ごとに大きな範囲を先に決める 本社 192.168.0.0/16、AWS 本番 10.10.0.0/16、AWS 検証 10.20.0.0/16
役割で第3オクテットを分ける public .0〜.9、app .10〜.19、db .20〜.29
AZ・フロアの違いを末尾の数でそろえる a は偶数(.0・.10・.20)、c は奇数(.1・.11・.21)
将来の分を空けておく 第3オクテット .30 以降は予備
他社・他システムとよく重なる範囲を避ける 家庭用ルーターの既定に多い 192.168.0.0/24・192.168.1.0/24 をリモート接続の環境で使わない

AWS の構成図の例(VPC・サブネット・ルートテーブル・NAT)

構成

種類 名前 CIDR AZ ルートテーブル
VPC prod-vpc 10.10.0.0/16 — —
パブリックサブネット public-a・public-c 10.10.0.0/24・10.10.1.0/24 1a・1c rt-public
プライベート(アプリ) app-a・app-c 10.10.10.0/24・10.10.11.0/24 1a・1c rt-app-a・rt-app-c
プライベート(DB) db-a・db-c 10.10.20.0/24・10.10.21.0/24 1a・1c rt-db

構成図(Mermaid)

flowchart TB
  inet(("Internet"))
  igw["IGW"]
  inet --- igw
  subgraph vpc["VPC prod-vpc 10.10.0.0/16(東京 ap-northeast-1)"]
    subgraph aza["AZ ap-northeast-1a"]
      subgraph pa["public-a 10.10.0.0/24<br/>RT: rt-public"]
        alb_a["ALB"]
        nat_a{{"NAT GW a"}}
      end
      subgraph aa["app-a 10.10.10.0/24<br/>RT: rt-app-a"]
        ec2_a["EC2 app"]
      end
      subgraph da["db-a 10.10.20.0/24<br/>RT: rt-db"]
        rds_a[("RDS primary")]
      end
    end
    subgraph azc["AZ ap-northeast-1c"]
      subgraph pc["public-c 10.10.1.0/24<br/>RT: rt-public"]
        alb_c["ALB"]
        nat_c{{"NAT GW c"}}
      end
      subgraph ac["app-c 10.10.11.0/24<br/>RT: rt-app-c"]
        ec2_c["EC2 app<br/>(DB は primary へ)"]
      end
      subgraph dc["db-c 10.10.21.0/24<br/>RT: rt-db"]
        rds_c[("RDS standby<br/>(a と同期)")]
      end
    end
  end
  igw --> alb_a & alb_c
  alb_a --> ec2_a
  alb_c --> ec2_c
  ec2_a -->|"5432"| rds_a
  ec2_a -.->|"0.0.0.0/0"| nat_a
  ec2_c -.->|"0.0.0.0/0"| nat_c
  nat_a -.-> igw
  nat_c -.-> igw
描き方のポイント 理由
囲みの順は VPC → AZ → サブネット 「どの AZ が落ちたら何が止まるか」が見える
サブネットの箱に CIDR とルートテーブル名 パブリックかプライベートかは、ルートテーブルで決まる
実線は利用者の通信、点線は外向きの通信(NAT 経由) 入ってくる通信と出ていく通信を分けて見られる
NAT は パブリックサブネットの中に描く NAT の置き場所の間違い(プライベートに置く)を見つけやすい
DB のサブネットは外への矢印を描かない 外に出られないことが図で分かる

ルートテーブルの表

サブネットが「パブリック」になるのは、ルートテーブルに 0.0.0.0/0 → IGW があるときです。 図と一緒に、ルートテーブルの表を添えます。AWS のエミュレーター(moto)で同じ構成を作り、AWS CLI で読んだ値です。

ルートテーブル 関連付けたサブネット 宛先 ターゲット
rt-public public-a・public-c 10.10.0.0/16 local
rt-public 〃 0.0.0.0/0 igw-…(IGW)
rt-app-a app-a 10.10.0.0/16・0.0.0.0/0 local・nat-…(NAT GW a)
rt-app-c app-c 10.10.0.0/16・0.0.0.0/0 local・nat-…(NAT GW c)
rt-db db-a・db-c 10.10.0.0/16 local(外への経路なし)

Amazon VPC のユーザーガイドの NAT ゲートウェイの例も、同じ形です。

  • NAT ゲートウェイはパブリックサブネットに置き、そのサブネットの 0.0.0.0/0 は IGW に向ける
  • プライベートサブネットの 0.0.0.0/0 は NAT ゲートウェイに向ける
  • 耐障害性のために、外に出る必要のあるリソースがある AZ ごとに NAT ゲートウェイを作ることが勧められている

サブネットの予約 IP と使える数

AWS のユーザーガイドでは、各サブネットの先頭4つと最後の1つは使えません(10.0.0.0/24 の例)。

アドレス 用途
10.0.0.0 ネットワークアドレス
10.0.0.1 VPC ルーター用に AWS が予約
10.0.0.2 AWS が予約(DNS サーバーは VPC の範囲の先頭+2)
10.0.0.3 将来のために AWS が予約
10.0.0.255 ブロードキャストアドレス(VPC はブロードキャストを扱わないため予約)

AWS CLI で作ったサブネットの空き IP 数です。/24 の 256 個から 5 個を引いた 251 が使える数になり、NAT ゲートウェイを置いた public の2つは、NAT の分が減って 250 でした。

aws ec2 describe-subnets \
  --filters Name=vpc-id,Values=vpc-xxxxxxxx \
  --query "sort_by(Subnets,&CidrBlock)[].{Name:Tags[?Key=='Name']|[0].Value,CIDR:CidrBlock,AZ:AvailabilityZone,Free:AvailableIpAddressCount}" \
  --output table
-----------------------------------------------------------
|                     DescribeSubnets                     |
+------------------+------------------+-------+-----------+
|        AZ        |      CIDR        | Free  |   Name    |
+------------------+------------------+-------+-----------+
|  ap-northeast-1a |  10.10.0.0/24    |  250  |  public-a |
|  ap-northeast-1c |  10.10.1.0/24    |  250  |  public-c |
|  ap-northeast-1a |  10.10.10.0/24   |  251  |  app-a    |
|  ap-northeast-1c |  10.10.11.0/24   |  251  |  app-c    |
|  ap-northeast-1a |  10.10.20.0/24   |  251  |  db-a     |
|  ap-northeast-1c |  10.10.21.0/24   |  251  |  db-c     |
+------------------+------------------+-------+-----------+

ユーザーガイドでは、サブネットの大きさは /16 から /28 までです。重なる CIDR でサブネットを作ろうとすると、InvalidSubnet.Conflict(moto で The CIDR '10.10.0.128/25' conflicts with another subnet)になりました。VPC を作る手順は、関連記事「AWS CLIでVPCを作成する手順|サブネットからIGWまで一式」で扱っています。

本社と AWS をつなぐとき

flowchart LR
  subgraph office["本社 192.168.0.0/16"]
    core01["CORE01"]
    cgw["RT01<br/>カスタマーゲートウェイ<br/>203.0.113.10"]
  end
  subgraph aws["AWS 東京"]
    vgw["VGW / TGW"]
    subgraph vpc["prod-vpc 10.10.0.0/16"]
      app["app-a / app-c"]
    end
  end
  core01 --- cgw
  cgw ===|"Site-to-Site VPN<br/>IPsec トンネル x2"| vgw
  vgw --- app
書き添えること 例
両側のアドレス範囲 本社 192.168.0.0/16、VPC 10.10.0.0/16(重ならないこと)
接続の種類と本数 Site-to-Site VPN、トンネル2本
経路の広告のしかた 静的ルート/BGP
両側のルートテーブル VPC 側の 192.168.0.0/16 → VGW、本社側の 10.10.0.0/16 → RT01

AWS CLI の出力から図を作る

手で描いた図は、設定を変えたときに実物とずれがちです。今の設定を読んで Mermaid を出力するスクリプトを用意しておくと、図と実物の差を見つけられます。

"""VPC の構成を読み取り、Mermaid の flowchart を出力する
使い方: python vpc2mermaid.py vpc-xxxxxxxx > vpc.mmd
"""
import sys
import boto3

vpc_id = sys.argv[1]
ec2 = boto3.client("ec2")

def name(tags, default):
    return next((t["Value"] for t in tags or [] if t["Key"] == "Name"), default)

def nid(s):
    return s.replace("-", "_")

flt = [{"Name": "vpc-id", "Values": [vpc_id]}]
vpc = ec2.describe_vpcs(VpcIds=[vpc_id])["Vpcs"][0]
subnets = sorted(ec2.describe_subnets(Filters=flt)["Subnets"], key=lambda s: s["CidrBlock"])
rts = ec2.describe_route_tables(Filters=flt)["RouteTables"]
nats = ec2.describe_nat_gateways(Filters=flt)["NatGateways"]
igws = ec2.describe_internet_gateways(
    Filters=[{"Name": "attachment.vpc-id", "Values": [vpc_id]}])["InternetGateways"]

rt_of = {a["SubnetId"]: rt for rt in rts for a in rt.get("Associations", []) if a.get("SubnetId")}

out = ["flowchart TB", '  inet(("Internet"))']
for g in igws:
    out.append(f'  {nid(g["InternetGatewayId"])}["IGW"]')
    out.append(f'  inet --- {nid(g["InternetGatewayId"])}')
out.append(f'  subgraph {nid(vpc_id)}["{name(vpc.get("Tags"), vpc_id)} {vpc["CidrBlock"]}"]')
for az in sorted({s["AvailabilityZone"] for s in subnets}):
    out.append(f'    subgraph {nid(az)}["{az}"]')
    for s in (x for x in subnets if x["AvailabilityZone"] == az):
        rt = rt_of.get(s["SubnetId"])
        rtn = name(rt["Tags"], rt["RouteTableId"]) if rt else "main"
        out.append(f'      {nid(s["SubnetId"])}["{name(s.get("Tags"), s["SubnetId"])}<br/>{s["CidrBlock"]}<br/>RT: {rtn}"]')
        for n in (n for n in nats if n["SubnetId"] == s["SubnetId"]):
            out.append(f'      {nid(n["NatGatewayId"])}{{{{"NAT"}}}}')
    out.append("    end")
out.append("  end")
# 0.0.0.0/0 の行き先を矢印にする
for s in subnets:
    rt = rt_of.get(s["SubnetId"])
    for r in (rt or {}).get("Routes", []):
        if r.get("DestinationCidrBlock") == "0.0.0.0/0":
            tgt = r.get("GatewayId") or r.get("NatGatewayId")
            out.append(f'  {nid(s["SubnetId"])} -->|0.0.0.0/0| {nid(tgt)}')
for n in nats:
    s = next(x for x in subnets if x["SubnetId"] == n["SubnetId"])
    rt = rt_of.get(s["SubnetId"])
    tgt = next(r.get("GatewayId") for r in rt["Routes"] if r.get("DestinationCidrBlock") == "0.0.0.0/0")
    out.append(f'  {nid(n["NatGatewayId"])} --> {nid(tgt)}')
print("\n".join(out))

上の構成で実行した出力の一部です。この出力も mermaid-cli で描画でき、AZ ごとにサブネットと NAT が並び、0.0.0.0/0 の矢印が app → NAT → IGW、public → IGW の向きで出ました。

flowchart TB
  inet(("Internet"))
  igw_f6c00f41014f5100a["IGW"]
  inet --- igw_f6c00f41014f5100a
  subgraph vpc_72a697e2542efaeeb["prod-vpc 10.10.0.0/16"]
    subgraph ap_northeast_1a["ap-northeast-1a"]
      subnet_6f341bf8f6519c8bf["public-a<br/>10.10.0.0/24<br/>RT: rt-public"]
      nat_8e4599ebd2ed3d1e0{{"NAT"}}
      subnet_b093e9efe2eb7a83e["app-a<br/>10.10.10.0/24<br/>RT: rt-app-a"]
      subnet_3ba42c036fadc45a5["db-a<br/>10.10.20.0/24<br/>RT: rt-db"]
    end
    ...
  subnet_b093e9efe2eb7a83e -->|0.0.0.0/0| nat_8e4599ebd2ed3d1e0
  nat_8e4599ebd2ed3d1e0 --> igw_f6c00f41014f5100a

自動で作った図は「事実の確認」、手で描いた図は「読み手への説明」と役割を分けます。Terraform で管理している場合は、コードと図の両方をレビューの対象にします。Terraform の基本は、関連記事「Terraform入門|基本の流れからstate管理・既存リソースの取り込みまで」で扱っています。

Mermaid と draw.io の使い分け

比べる点 Mermaid draw.io(diagrams.net)
描き方 テキストで書き、自動で配置される マウスで配置する
差分のレビュー Git でテキストの差分が見える XML の差分は読みにくい
配置の自由度 低い(線が遠回りすることがある) 高い(ラックの絵・フロア図も描ける)
アイコン 図形の形とラベルで表す ネットワーク機器・AWS のアイコンを使える
向く図 L3 の図・VPC の図・自動生成の図 物理構成図・顧客に出す図

Mermaid で描くときのコツ

コツ 書き方
向きを選ぶ 上下に流れる図は flowchart TB、横に並べる図は flowchart LR
入れ子の囲み subgraph の中に subgraph(VPC → AZ → サブネット)
改行 ラベルの中で <br/>
線を減らす 同じ向きの線は A --> B & C でまとめる
確かめる npx -p @mermaid-js/mermaid-cli mmdc -i net.mmd -o net.svg で描画できるかを CI で確かめる

この記事の最初の AWS の図では、AZ をまたぐ線(1c のアプリ → 1a の DB、DB の同期)を入れると、線が図の外側を大きく回り込みました。AZ をまたぐ線はラベルや注記に置き換えると、図がすっきりしました。

draw.io で描くときのコツ

  • 図形のライブラリから、ネットワーク機器や AWS のアイコンの図形を有効にして使う
  • レイヤー機能で「物理」「L2」「L3」を分けると、1つのファイルで層を切り替えて見られる
  • ファイル(.drawio)をリポジトリに置き、PNG・SVG の書き出しも一緒に置くと、レビューで中身が見える

レビューの観点チェックリスト

区分 観点
全体 目的・範囲・版・日付・作成者が書いてある
全体 凡例がある(記号・線・色の意味)
全体 物理と論理(L2・L3)が分かれている、または層が分かる
名前 機器名が設定のホスト名・台帳と一致している
物理 両端のポート番号・ケーブルの種類と速度が書いてある
物理 冗長化した経路(2本目の線・2台目の機器)が描かれている
L2 VLAN ID・名前・トランクで通す VLAN が書いてある
L2 管理用の VLAN が業務の VLAN と分かれている
L3 すべてのサブネットに CIDR とゲートウェイがある
L3 既定ルートと「戻り」の経路が分かる
IP 拠点・VPC の間でアドレスが重なっていない(設計表で機械的に確認)
IP 将来の追加分の範囲が空けてある
AWS サブネットごとにルートテーブル名が書いてある
AWS NAT ゲートウェイがパブリックサブネットにあり、AZ ごとに置くかが決まっている
AWS DB など外に出ないサブネットに 0.0.0.0/0 の経路が無い
AWS AZ が1つ止まったときに残るものが図から分かる
セキュリティ インターネットから入れる入口(IGW・ALB・FW の outside)がすべて描かれている
セキュリティ 本物のグローバル IP・パスワード・回線 ID などを、公開範囲に合わせて伏せている
運用 変更したときに図を直す人と手順が決まっている

図の粒度(どこまで細かく描くか)をそろえる考え方は、関連記事「C4モデルの書き方|コンテキスト図・コンテナ図のMermaid例」で扱っています。

よくある質問

Q. 物理と論理を1枚にまとめてはいけませんか。 A. 機器が10台程度までなら、1枚にまとめて読めることもあります。線が交差し始めたら分けるのが目安です。draw.io のレイヤーを使えば、1ファイルのまま切り替えられます。

Q. IP アドレスは全部図に書くべきですか。 A. 図にはネットワーク(CIDR)とゲートウェイ、機器のインターフェースの IP を書き、端末ごとの IP は設計表や IPAM(IP の管理台帳)に任せると、図が読みやすくなります。

Q. クラウドの図に物理構成図はありますか。 A. クラウドの物理(データセンターの機器)は利用者からは見えないので、VPC・AZ・サブネットの論理の図が中心です。オンプレミスとつなぐ部分(回線・ルーター)は物理の図に描きます。

まとめ

  • ネットワーク構成図は 物理・L2(VLAN)・L3(IP・経路) に分けて描く
  • 物理はポート番号とケーブル、L2 は VLAN とトランク、L3 は CIDR・ゲートウェイ・経路(行きと戻り)
  • 図は全体の形、正確な値は VLAN 表・IP 設計表・ルーティング表に持たせる
  • IP 設計表は、重なりと範囲を Python などで機械的に確かめる
  • AWS は VPC → AZ → サブネットの入れ子にし、サブネットにルートテーブル名、NAT は パブリックサブネットに AZ ごと
  • 予約 IP の5個により、/24 のサブネットで使えるのは 251(AWS CLI の出力でも確認)

参考・出典

確認日はいずれも 2026年10月13日です。

  • Amazon VPC ユーザーガイド「Subnet CIDR blocks」:https://docs.aws.amazon.com/vpc/latest/userguide/subnet-sizing.html
  • Amazon VPC ユーザーガイド「NAT gateway use cases」:https://docs.aws.amazon.com/vpc/latest/userguide/nat-gateway-scenarios.html
  • Amazon VPC ユーザーガイド「Configure route tables」:https://docs.aws.amazon.com/vpc/latest/userguide/VPC_Route_Tables.html
  • AWS Architecture Icons:https://aws.amazon.com/architecture/icons/
  • RFC 5737「IPv4 Address Blocks Reserved for Documentation」:https://www.rfc-editor.org/rfc/rfc5737
  • RFC 1918「Address Allocation for Private Internets」:https://www.rfc-editor.org/rfc/rfc1918
  • Python ドキュメント「ipaddress」:https://docs.python.org/3/library/ipaddress.html
  • Mermaid ドキュメント「Flowcharts Syntax」:https://mermaid.js.org/syntax/flowchart.html
  • mermaid-cli(GitHub):https://github.com/mermaid-js/mermaid-cli
  • moto(GitHub):https://github.com/getmoto/moto

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「ネットワーク構成図の書き方|記号・レイヤー別の書き分けとAWS構成の例」

株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。

WE ARE HIRING

RJCで一緒に開発しながら、
成長を楽しみませんか?

RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!

  • 127日年間休日
  • 12時間平均残業時間
  • 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!

ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?