Closed8

Pythonでクラウドのシステム構成図が書ける「Diagrams」を試す

kun432kun432

GitHubレポジトリ

https://github.com/mingrammer/diagrams

Diagrams

Diagrams as Code.

DiagramsはPythonコードでクラウドシステムアーキテクチャを描画することができます。新しいシステムアーキテクチャのデザインをプロトタイプするために生まれたツールで、デザインツールを使わずに済みます。また、既存のシステムアーキテクチャを記述したり視覚化することも可能です。Diagramsは現在、主要なプロバイダーをサポートしており、AWS、Azure、GCP、Kubernetes、Alibaba Cloud、Oracle Cloudなどが含まれます。また、オンプレミスノード、SaaS、主要なプログラミングフレームワークや言語もサポートしています。

コードとしてのダイアグラム」により、アーキテクチャダイアグラムの変更を任意のバージョン管理システム追跡することができます。

注意: このツールは実際のクラウドリソースを管理するものではなく、クラウドフォーメーションやTerraformコードを生成するわけでもありません。クラウドシステムアーキテクチャ図の作成のみに使用されます。

プロバイダー

  • AWS
  • Azure
  • GCP
  • IBM
  • Kubernetes
  • Alibaba Cloud
  • Oracle Cloud
  • OpenStack
  • Firebase
  • Digital Ocean
  • Elastic
  • Outscale
  • オンプレミス
  • ジェネリック
  • プログラミング
  • SaaS
  • C4

公式ドキュメント

https://diagrams.mingrammer.com/


全然知らなかった・・・

kun432kun432

Colaboratoryで。

要件

  • Python-3.7以上
  • Graphviz

Getting Started

https://diagrams.mingrammer.com/docs/getting-started/installation

パッケージインストール

!pip install diagrams
!pip freeze | grep diagrams
出力
diagrams==0.23.4

サンプル。

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) as diag:
    ELB("lb") >> EC2("web") >> RDS("userdb")

diag

おー、簡単

kun432kun432

Diagramsクラス

Diagramクラスがグローバルなコンテキストになり、最初の引数が図のタイトルかつファイル名になる。

from diagrams import Diagram
from diagrams.aws.compute import EC2

with Diagram("Simple Diagram"):
    EC2("web")

simple_diagram.pngというファイル名で出力されるので、これを表示。

from IPython.display import Image

display(Image('simple_diagram.png'))

ただし、ColaboratoryやJupyterの場合、上記のようにファイルを使ってセルに表示しなくても、以下で表示できる。

with Diagram("Simple Diagram") as diag:
    EC2("web")

diag

その他のオプション

  • outformat
    • 出力するファイル形式。jpg / png / svg / pdf / dot(Graphvizで使用される形式らしい)から選択できる。デフォルトはpng
    • 配列で渡すとそれぞれの形式で出力することができる
  • filename
    • 出力するファイル名を明示的に指定する。
    • 拡張子は含めずに指定する。
  • show
    • 自動でファイルを開くかどうか?
    • ちょっとここは違いが確認できなかった。
  • graph_attr/node_attr/edge_attr
    • Graphvizの属性のカスタマイズを指定。リファレンスはこちら

全部指定するとこんな感じ。

from diagrams import Diagram
from diagrams.aws.compute import EC2
from diagrams.aws.database import RDS
from diagrams.aws.network import ELB

graph_attr = {
    "fontsize": "45",
    "bgcolor": "grey"
}

node_attr = {
    "fontsize": "20",
    "fontcolor": "blue"
}

edge_attr = {
    "color": "red"
}

with Diagram(
    "Three Tier Architecture",
    outformat=["png", "jpg"],
    filename="tta",
    show=False,
    graph_attr=graph_attr,
    node_attr=node_attr,
    edge_attr=edge_attr,
) as diag:
    ELB("LB") >> EC2("web") >> RDS("DB")

diag

!ls -lt tta*
出力
-rw-r--r-- 1 root root 32705 Nov  4 08:57 tta.jpg
-rw-r--r-- 1 root root 41381 Nov  4 08:57 tta.png
kun432kun432

ノード

ノードは個々のコンポーネントを表す。ノードは以下のように、プロバイダ・リソースタイプ・ノード名で呼び出す。

from diagrams.aws.compute import EC2
              ~~~ ~~~~~~~        ~~~
              |      |            |
              |      |         ノード名
              |  リソースタイプ
          プロバイダ

例えば、Kubernetesだとこうなる

from diagrams import Diagram
from diagrams.k8s.compute import Pod, Deploy, RS
from diagrams.k8s.network import Service

with Diagram("K8S") as diag:
    Service("Service") >> Deploy("Deployment") >> RS("ReplicaSet") >> [Pod("pod"),
                                                                       Pod("pod")]
diag

上で少し使っているけども、ノード間をつなぐエッジは>> / << / - で表現する。

from diagrams import Diagram
from diagrams.aws.compute import EC2
from diagrams.aws.database import RDS

with Diagram("Web Services", show=False) as diag:
    EC2("Web1") >> RDS("DB") << EC2("Web2")

diag

with Diagram("Web Services", show=False) as diag:
    EC2("Web1") - RDS("DB") - EC2("Web2")

diag

ただし、以下はちょっと想定と異なる動きになる。

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 Services", show=False) as diag:
    ELB("LB") >> EC2("Web1") - RDS("DB") << EC2("Web2")

diag

-(マイナス演算子)とシフト演算子を一緒に使用する際には、演算子の優先順位によって予期しない結果を引き起こす可能性があるため注意してください。

なるほど、記号的にイメージしてしまうけど、ここはあくまでも演算子なのね。こう書けば良い。

with Diagram("Web Services", show=False) as diag:
    (ELB("LB") >> EC2("Web1")) - RDS("DB") << EC2("Web2")

diag

あと、

with Diagram("Web Services", show=False) as diag:
    ELB("LB") >> EC2("Web1") >> RDS("DB")
    RDS("DB") << EC2("Web1") << ELB("LB")
    
diag

複数の図を列挙した場合、出力は記載した順序とは逆になる。

あと、データの流れの方向を指定できる。これはノード、というかDiagramクラスのオプションだな。

with Diagram("Web Services", direction="TB") as diag:
    ELB("LB") >> EC2("Web1") >> RDS("DB")
    
diag

指定できる方向は以下。

  • LRLeft → Right) ※デフォルト
  • RLRight → Left)
  • TBTop → Bottom)
  • BTBottom → Top)

フローのグルーピングは配列的に指定する。

with Diagram("Web Services", direction="TB") as diag:
    ELB("LB") >> [EC2("Web1"),
                  EC2("Web2"),
                  EC2("Web3")] >> RDS("DB")
    
diag

ただし、グルーピング同士を接続することはできない。例えばこう。

with Diagram("Web Services", direction="TB") as diag:
    ELB("LB") >> [EC2("Web1"),
                  EC2("Web2"),
                  EC2("Web3")] >> [RDS("Primary"), RDS("Replica")]
    
diag
出力
TypeError: unsupported operand type(s) for >>: 'list' and 'list'

ドキュメントにも書いてある

Pythonでは、リスト同士を直接結合することはできません。リスト間でのシフトや算術演算は許可されていないためです。

例えばこういう回避策はできそう

with Diagram("Web Services", direction="TB") as diag:
    elb = ELB("LB")
    web1 = EC2("Web1")
    web2 = EC2("Web2")
    web3 = EC2("Web3")
    db_primary = RDS("Primary")
    db_replica = RDS("Replica")

    elb >> [web1, web2, web3]
    web1 >> [db_primary, db_replica]
    web2 >> [db_primary, db_replica]
    web3 >> [db_primary, db_replica]
    
diag

ノードのインスタンスを作成して、それをエッジで明示的につなぐ感じ

kun432kun432

クラスタ

ノードをグループとしてクラスタリングするには、with Cluster()でDiagramクラスの中にコンテキストを追加する。

from diagrams import Cluster, Diagram
from diagrams.aws.network import ELB
from diagrams.aws.compute import EC2

with Diagram("Auto Scaling Group") as diag:

    with Cluster("Auto Scaling Group"):
        web_asg = [EC2("Web1"),
                   EC2("Web2"),
                   EC2("Web3")]

    ELB("LB") >> web_asg 

diag

もうちょっと複雑にしてみるとこんな感じ。

from diagrams import Cluster, Diagram
from diagrams.aws.network import ELB
from diagrams.aws.compute import EC2

with Diagram("Auto Scaling Group + DB Cluster") as diag:

    with Cluster("Auto Scaling Group"):
        web_asg = [EC2("Web1"),
                   EC2("Web2"),
                   EC2("Web3")]

    with Cluster("DB Cluster"):
        db_primary = RDS("Primary")
        db_primary - [RDS("replica1"),
                     RDS("replica2")]

    ELB("LB") >> web_asg >> db_primary

diag

クラスタはネストさせることもできる。

from diagrams import Cluster, Diagram
from diagrams.aws.network import ELB
from diagrams.aws.compute import EC2

with Diagram("Inside VPC") as diag:

    with Cluster("VPC"):

        with Cluster("Public Subnet"):
            elb = ELB("LB")

        with Cluster("Private Subnet"):
            with Cluster("Auto Scaling Group"):
                web_asg = [EC2("Web1"),
                        EC2("Web2"),
                        EC2("Web3")]

        elb >> web_asg

diag

kun432kun432

エッジ

ノード間のエッジに以下のプロパティを付与することができる。

  • ラベル
  • スタイル

例えば以下の例

from diagrams import Cluster, Diagram
from diagrams.aws.network import ELB
from diagrams.aws.compute import EC2
from diagrams.aws.database import RDS

with Diagram("Web Services", direction="LR") as diag:
    with Cluster("VPC"):

        with Cluster("Public Subnet"):
            elb = ELB("LB")

        with Cluster("Private Subnet 1"):
            web1 = EC2("Web1")
            web2 = EC2("Web2")
            web3 = EC2("Web3")

        with Cluster("Private Subnet 2"):
            db_primary = RDS("Primary")
            db_replica = RDS("Replica")

        elb >> [web1, web2, web3]
        web1 >> [db_primary, db_replica]
        web2 >> [db_primary, db_replica]
        web3 >> [db_primary, db_replica]
    
diag

このエッジにいろいろプロパティを付与してみる。

from diagrams import Cluster, Diagram, Edge
from diagrams.aws.network import ELB
from diagrams.aws.compute import EC2
from diagrams.aws.database import RDS

with Diagram("Web Services", direction="LR") as diag:
    with Cluster("VPC"):

        with Cluster("Public Subnet"):
            elb = ELB("LB")

        with Cluster("Private Subnet 1"):
            web1 = EC2("Web1")
            web2 = EC2("Web2")
            web3 = EC2("Web3")

        with Cluster("Private Subnet 2"):
            db_primary = RDS("Primary")
            db_replica = RDS("Replica")

        elb >> Edge(color="blue", label="balancing", style="dotted") >> [web1, web2, web3]
        [web1, web2, web3]  >> Edge(color="red", label="WRITE", style="bold") >> db_primary
        [web1, web2, web3]  >> Edge(color="darkgreen", label="READ", style="dashed") >> db_replica
        
diag

kun432kun432

まとめ

コードで管理できるってのが最大のメリットかな、あとアイコンが用意されてるのできれい。

ただ、自在に書くにはひと手間いるかなぁ。個人的にこういう構成図、設計段階ではある程度考えながら試行錯誤して整理したい感があるので、ドローツールを使って書いて、どっちかというと実装段階以降である程度固まってから記録として残していくという使い方のほうが有用な感を持った。

kun432kun432

Diagramsをうまく使うには、ノードを全部インスタンスにしておいて、エッジでチマチマつなげるってのが、一番自由度は高そうな気がした。一番最後の例がそれ。

このスクラップは18日前にクローズされました