美しいアーキテクチャ図が描けるようになりたい
はじめに
私は以前、AWS CDKを使ってみたという内容の記事をQiitaに投稿させて頂きました。
その際に、構築したアーキテクチャ図を自分で描いて載せたのですが、まぁこれがなかなかの代物でして…
どうでしょうか?
Diagrams.net(旧draw.io)を使って描いてみたのですが、あまり使いこなせておらず、そして自分にセンスがないことも相まって、リソースのサイズ感や矢印の見せ方も、なんだかごちゃごちゃして美しさに欠ける図となってしまい、モヤモヤしていました。
そこで、今回は「Diagrams」という、Pythonコードで記述してアーキテクチャ図が描けるツールがあるとのことで、今回はこれを使ってリベンジしてみたいと思います!!
「Diagrams.net(旧draw.io)」と、今回使う「Diagrams」は、名前がほぼ同じですが、たまたま名前が似てるというだけで、おそらく全く別のサービスかと思いますのでご注意ください。
前提
前提1. Pythonの実行環境の構築ができていること
冒頭にも書いた通り、今回使う「Diagrams」はPythonを使って記述するものなので、Pythonを実行できる環境構築ができていることが前提となります。
ちなみに、今回筆者が実行する環境は以下の記事の通りになりますので、異なるPython実行環境の方はそれに読み替えてください↓
前提2. Graphvizがインストールできていること
Diagramsの公式サイトのインストールガイドのページの冒頭では、「Python 3.7以上の実行環境」と「Graphvizがインストールできていること」という前提で始まっていました。
私はGraphvizを入れていないので、後述するインストール手順の中で入れようと思います。
ちなみにGraphvizとは、オープンソースのグラフ可視化ソフトウェアで、DOT言語で記述をしてアーキテクチャ図やネットワーク図などが描けるツールらしいです。
今回触るDiagramsは、内部で実際に動作しているGraphvizに命令を与えて図を生成してくれるインターフェースみたいなものということですね。
1. Graphvizをインストールする
私はWindows PC上のWSL2でUbuntu-24.04のディストリビューション上の環境で、Python環境構築をしているので、そこにGraphvizをインストールします。
sudo apt update
sudo apt install graphviz
2. Diagramsインストールに向けて前準備をする
任意の場所に作業用ディレクトリを作成し、そのディレクトリに移動する
mkdir hello-diagrams
cd hello-diagrams/
プロジェクトごとの仮想環境を作成する
python3 -m venv .venv
仮想環境を有効にする
source .venv/bin/activate
Pythonパッケージマネージャ(pip)を最新化する
python -m pip install --upgrade pip
3. Diagramsをインストールする
こちらのDiagramsの公式サイトに載っている方法に従ってインストールをします。
Diagramsをインストールする
pip install diagrams
上記のコマンドが無事に成功したらDiagramsのインストールは完了です。
4. チュートリアルをやってみる
今回は初めて使うので、そのまま引き続き同ページに載っているクイックスタート(チュートリアル的なもの)もやってみようと思います。
「diagram.py」という名前のファイルを作成する
touch diagram.py
diagram.pyに以下のコードを記述する。
from diagrams import Diagram
from diagrams.aws.compute import EC2
from diagrams.aws.database import RDS
from diagrams.aws.network import ELB
with Diagram("Web Service", show=False):
ELB("lb") >> EC2("web") >> RDS("userdb")
ここで少し余談ですが、正直私はPythonのことあまり詳しくないので、「with」ってなんだ?と思ったのですが、使い終わったら必ず閉じなければならないリソースを自動で閉じてくれる構文らしいです。
Javaでいう「try-with-resources文」みたいなやつですね。
で、コードの内容ですが、私は初めてDiagramsのコードを見たのですが、おそらく見たまんまの内容かと思います。
ELB→EC2→RDSの3つのリソースが繋がったようなアーキテクチャ図ができるのでしょう。
では早速これで実行してみます。
python diagram.py
特に何も起こらずプログラムは即終了します。
カレントディレクトリの中を確認してみると「web_service.png」というファイルが新たに作成されていました。
ls -la
total 32
drwxrwxrwx 1 puchanko puchanko 512 Jul 22 10:02 .
drwxrwxrwx 1 puchanko puchanko 512 Jul 22 09:20 ..
drwxrwxrwx 1 puchanko puchanko 512 Jul 22 09:21 .venv
-rwxrwxrwx 1 puchanko puchanko 229 Jul 22 09:35 diagram.py
-rwxrwxrwx 1 puchanko puchanko 32585 Jul 22 10:02 web_service.png # ←これ
そのファイルを開いてみると以下のような画像ファイルが作成されていました。
無事にチュートリアルも成功ですね。
予想通り、ELB→EC2→RDSの3つのリソースが繋がったアーキテクチャ図の作成ができました。
5. アーキテクチャ図を作成する
さて、ここからが本番です。
以前の記事で作成したAWSアーキテクチャ図をDiagramsで作成していきましょう。
で、早速ですが、まずは完成したコードと図をどうぞ。
from diagrams import Cluster, Diagram, Edge
from diagrams.aws.compute import EC2
from diagrams.aws.database import RDS
from diagrams.aws.network import ALB, IGW, NATGateway
from diagrams.onprem.client import User, Client
from diagrams.onprem.network import Internet
graph_attr = {
"splines": "ortho",
"nodesep": "0.8",
"ranksep": "1.0",
"pad": "0.3",
}
with Diagram(
"CDK Crawl App",
show=False,
direction="LR",
graph_attr=graph_attr,
):
user = User("User")
client = Client("Browser")
internet = Internet("Internet")
with Cluster("AWS Cloud"):
with Cluster("Region (ap-northeast-1)"):
with Cluster("VPC\n(10.0.0.0/16)"):
igw = IGW("Internet Gateway")
with Cluster("Availability Zone A"):
with Cluster("Public Subnet\n(10.0.1.0/24)"):
alb = ALB("ALB\nPublic Subnets in 2 AZs")
nat = NATGateway("NAT Gateway\nOutbound access")
with Cluster("Private Subnet (EGRESS)\n(10.0.2.0/24)"):
ec2 = EC2("EC2")
with Cluster("Private Subnet (ISOLATED)\n(10.0.3.0/24)"):
rds = RDS("RDS")
# 主経路:レイアウトを決める
user >> client >> internet >> igw >> alb >> ec2 >> rds
# 外向き通信:配置には影響させない
ec2 >> Edge(
style="dashed",
constraint="false",
) >> nat
nat >> Edge(
style="dashed",
constraint="false",
) >> igw
igw >> Edge(
style="dashed",
constraint="false",
) >> internet
どうでしょうか?
なかなかいい感じに仕上がったのではないでしょうか?
では作成していく中で気付いた点などを挙げていきます。
5-1. 空のClusterを描画することはできない
まず、大前提としてですが、以前の記事で作成した旧アーキテクチャ図は以下になります。
(再掲)
見てもらったらお分かりの通りで、ALBを配置してるのでAZは2つ以上必ず作成する必要がありますが、今回は個人開発での検証用途でお金は節約する方針なので、実際のリソースは片方のAZに1つずつしか配置してませんでした。
つまりこれをDiagramsで表現しようとすると以下のようになります。
# リソースを配置していないほうのAZ
with Cluster("Availability Zone C"):
with Cluster("Public Subnet"):
pass
with Cluster("Private Subnet"):
pass
with Cluster("Private Subnet"):
pass
with Cluster()で定義した中には必ず何かを記述しないとエラーになるので、Pythonで「何もしない」ことを意味するpass文を便宜上書いてますが、Diagramsはこれを「空っぽのCluster = 描画しない」という判断をするので、記述をしても出力される画像には一切反映されません。
そのため、今回は利用していない方の空のAZに関しては記述しないという方針にしました。
まぁ実際の実務ではこんな構成にすることはまず無いので、こんな状況はレアケースだと思うのですが、1つ勉強にはなりました。
5-2. レイアウトの向きを決める
今回作成した図は左から右へ向かっていく構成にしました。
(再掲)
ソースコードでいうと「direction」で明示的に「LR」という値を指定して、左(Left)から右(Right)向きに構成されるようにしました。
with Diagram(
"CDK Crawl App",
show=False,
direction="LR", # ←ここ
graph_attr=graph_attr,
):
このレイアウトの向きは、Diagramsではなく、内部で動作しているGraphvizが決めています。
今回は明示的に指定しましたが、この引数を省略するとデフォルトで「Top -> Bottom」、つまり「direction="TB"」となって、上から下の向きのレイアウトの値が指定されます。
ただし、省略したからといって必ず「Top -> Bottom」の見た目になるわけではなく、中で配置されているノードの数、接続関係、Cluster、Edgeなどを見て、できるだけ「Left -> Right」になるようにGraphvizが内部で最適化をするような仕組みになっているそうです。
一応、このレイアウトの向きを決める「direction」には4種類あります。
- TB (Top to Bottom):上から下
- BT (Bottom to Top):下から上
- LR (Left to Right):左から右
- RL (Right to Left):右から左
5-3. 主経路と副経路を分けて考える
再び、今回作成した図がこちらです。
(再掲)
前節でも触れた通り、今回は「Left -> Right」の向きで作成しました。
つまり矢印の向きとしても「Left -> Right」が主経路になります。
一番メインとなるのはユーザーからインターネット経由でAWSリソースに向けてアクセスされるこの経路こそが主経路です。
一方、破線の矢印で描画されている「EC2 -> NAT -> IGW -> Internet」のこの経路は、EC2に配置したアプリケーションを稼働させるために必要なパッケージの更新およびインストールのためだけにインターネット通信を要するもので、いわば開発者向けの経路なので副経路となります。
これをそのまま素直にDiagramsで記述すると、「Internet ⇔ IGW」で循環が起きてしまい、矢印の描画が不自然な形になってしまいます。
そのため、副経路とするほうには、以下のように「constraint="false"」を指定することで、主経路の描画には影響を及ぼさないようにするというテクニックが必要となります。
# 外向き通信:配置には影響させない
ec2 >> Edge(
style="dashed",
constraint="false", # ←ここ
) >> nat
nat >> Edge(
style="dashed",
constraint="false", # ←ここ
) >> igw
igw >> Edge(
style="dashed",
constraint="false", # ←ここ
) >> internet
5-4. Graphvizに渡す詳細設定用の引数がある
コードを見てもらえば、最初にこのような記述があると思いますが、これは何かというと、Diagramsの内部で動作しているGraphvizに直接渡す引数になります。
graph_attr = {
"splines": "ortho",
"nodesep": "0.8",
"ranksep": "1.0",
"pad": "0.3",
}
with Diagram(
"CDK Crawl App",
show=False,
direction="LR",
graph_attr=graph_attr, # ←ここ
):
1つずつ見ていきましょう。
5-4-1. splines
Edge(線・矢印)をどう描画するかを決めるパラメータになります。
今回は「ortho」を指定しましたが、これは矢印が直角になるように描画されます。
以下のような種類があります。
-
ortho:直角 -
polyline:折れ線 -
curved:曲線
5-4-2. nodesep
同じランク内のノード同士の距離を設定するものです。
例えば、以下のように「0.1」とするとグッとノード同士が近くなります。
digraph {
nodesep=0.1;
node1; node2; node3;
}
以下のように「0.9」とするとかなり余裕ができます。
digraph {
nodesep=0.9;
node1; node2; node3;
}
5-4-3. ranksep
ランク同士の距離を設定するものです。
と言われてもあんまりピンと来ないので実際にここの値をいじってみましょう。
今回作成したコードでは「"1.0"」としましたが、これを「"0.1"」にしてみます。
graph_attr = {
"splines": "ortho",
"nodesep": "0.8",
"ranksep": "0.1", # ←ここ
"pad": "0.3",
}
かなりギュッとなりましたね。
ある意味これはこれでコンパクトで見やすいのかなとも思いますが、流石に狭すぎる感じがしますね。
次は「"2.0"」にしてみます。
graph_attr = {
"splines": "ortho",
"nodesep": "0.8",
"ranksep": "2.0", # ←ここ
"pad": "0.3",
}
「"0.1"」に比べると圧迫感はなくなり、とても広々としましたが、流石に離れすぎてるように思います。
ということで、今回は間をとって「"1.0"」が良さそうかなということでこれにしてます。
5-4-4. pad
図全体の余白を設定するものです。
例えば「"pad": "0.0"」とすれば「余白なし」と見なして、画像の端のギリギリまで描画しようとします。
今回は「"pad": "0.3"」と設定して、程よい余白が出るように調整をしました。
これらのほかにも色々と設定可能な属性があります。
-
concentrate:同じ経路のEdgeを枝分かれして見やすくするもの -
bgcolor:背景色の設定 -
dpi:画像の解像度 -
fontsize:フォントのサイズ -
fontname:フォントの指定 -
margin:Cluster内部の余白
おわりに
今回初めてDiagramsを使ってみましたが、個人的にはかなり気に入ってます。
コードの内容がかなり素直でわかりやすいので、慣れてきたらパパっと作れそうだなと思いました。
Pythonの実行環境さえあれば何度でも修正してすぐに作り直すということも可能です。
何よりコードなので、差分管理ができることは強いなと思いますし、AIとの親和性も高そうだなと思いました。
今回触ったDiagramsのほかにもMermaidなどの作図ツールもあるので、次回はそれに挑戦してみるのもありだと思いました。






