構成図を『描く』から『書く』へ：DiagramsでAWS構成図かいてみる


## やること

"Diagram as Code"ことDiagramsで、AWS構成図をコードでかいてみる

## Diagramsとは

- Diagramsは、Pythonモジュール
- Diagrams使うにはGraphvizも必要
- Pythonのコードでクラウドシステムアーキテクチャを描画できる
- サイトはここ
    - https://diagrams.mingrammer.com/
- サイトにあるDiagramsの説明は、英語だけど分量が少ないので一読すべし！（自動翻訳で十分理解できる）

このように、構成図をPythonのコードで書ける。このコードを、Pythonスクリプトとして実行すると、構成図が画像として出力される。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274270/c456fcba-500d-792d-fb35-a8c63834f57d.png)


- **良いところ**
    - 構成図をコード化できる
    - コード編集で作図編集できるので、画像修正より楽
    - コードなので、CloudFormationのコードとまとめてGitで管理できる
    - AWS以外のアイコンも用意されている
    - 出力される構成図はラフスケッチかそれ以上で使えそう
- **課題**
    - AWSのアイコン全てが用意されてない
    - アイコンの配置場所を指定できない（だからこそシンプルなコードになるんだけど）

## Diagramsインストール環境

**『Raspberry Pi 4 Model B』**と**『Cloud9』**の2環境に`Diagrams`をインストールした。

#### 環境（１）Raspberry Pi 4 Model B

```console
$ cat /etc/os-release
PRETTY_NAME="Raspbian GNU/Linux 10 (buster)"
NAME="Raspbian GNU/Linux"
VERSION_ID="10"
VERSION="10 (buster)"
VERSION_CODENAME=buster
ID=raspbian
ID_LIKE=debian
HOME_URL="http://www.raspbian.org/"
SUPPORT_URL="http://www.raspbian.org/RaspbianForums"
BUG_REPORT_URL="http://www.raspbian.org/RaspbianBugs"
```

Pythonは、2.7 と 3.7 の2バージョンが入っている。
Python 3.7 に`Diagrams`をインストールする

```
$ python --version
Python 2.7.16
$ python3 --version
Python 3.7.3
```

#### 環境（２）Cloud9

```console
$ cat /etc/os-release
NAME="Amazon Linux AMI"
VERSION="2018.03"
ID="amzn"
ID_LIKE="rhel fedora"
VERSION_ID="2018.03"
PRETTY_NAME="Amazon Linux AMI 2018.03"
ANSI_COLOR="0;33"
CPE_NAME="cpe:/o:amazon:linux:2018.03:ga"
HOME_URL="http://aws.amazon.com/amazon-linux-ami/"
```

Cloud9は、Python 3.x のみで Python 2.x はない

```console
$ python --version
Python 3.6.8
```

## Diagramsインストール

#### インストール（１）Raspberry Pi

Python3に`diagrams`と`Graphviz`をインストールするだけ。

python3 に diagram をインストール

```console
$ python3 -m pip install diagrams
```

Graphvizインストール

```console
$ sudo apt-get install graphviz
```

#### インストール（２）Cloud9

```console
$ sudo pip install --upgrade pip
$ sudo python3 -m pip install diagrams
$ sudo yum install graphviz
```


## （検証）AWS構成図をかいてみる

※構成図を『描く』→『書く』

#### （検証１）Webサーバーの構成図をかいてみる

できたのはこちら、まずまずの完成度

![図1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274270/ee4e9f73-b210-04fc-3551-cc752db2da43.png)

Cloud9だとこんな感じで見れた（便利！）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274270/8e265dd6-8978-fa2a-ba61-fe250b28a9f6.png)


- 構成の概要
  - EC2でWebAppサーバーを構築
  - WebAppサーバーを2台用意して冗長化
  - ALBでWebAppサーバーへの負荷を分散
  - Rote53でドメイン登録
  - WebAppサーバーのデータベースをRDSで用意

作ったコードはこれ。

```python:Webサーバーの構成図
from diagrams import Cluster, Diagram, Edge
from diagrams.aws.network import VPC
from diagrams.aws.compute import EC2
from diagrams.aws.database import RDS
from diagrams.aws.network import Route53
from diagrams.aws.network import NATGateway
from diagrams.aws.network import InternetGateway
from diagrams.aws.network import ElasticLoadBalancing
from diagrams.onprem.network import Internet
from diagrams.onprem.client import Client
from diagrams.onprem.client import Users

with Diagram("Fig-001-WebSite", show=False):
    with Cluster("Fig-001-WebSite"):
        internet_01 = Internet("Internet")
        users_01 = Users("ユーザー")
        with Cluster("AWS/Develop/ap-northeast-1"):
            r53_01 = Route53("hoge.fuga.local")
            with Cluster("VPC/192.168.0.0/HOGE-SYSTEM"):
                alb_01 = ElasticLoadBalancing("ALB")
                igw_01 = InternetGateway("InternetGateway")
                with Cluster("subnet/192.168.1.0/public"):
                    natgw_01 = NATGateway("NATGW")
                    ec2_01 = EC2("WebApp")
                with Cluster("subnet/192.168.101.0/private"):
                    rds_01 = RDS("postgres-Master")
                with Cluster("subnet/192.168.2.0/public"):
                    natgw_02 = NATGateway("NATGW")
                    ec2_02 = EC2("WebApp")
                with Cluster("subnet/192.168.102.0/private"):
                    rds_02 = RDS("postgres-Slave")

    # 構成図 Webサーバー
    users_01 - internet_01 - igw_01 - alb_01 # ユーザーアクセス経路
    alb_01 - ec2_01 - rds_01 # WebApp冗長化
    alb_01 - ec2_02 - rds_01 # WebApp冗長化
    rds_01 - Edge(style="dotted") - rds_02 # DB冗長化
```




#### （検証２）WorkSpacesの構成図をかいてみる

- Amazon WorkSpacesとは、AWSが提供する仮想デスクトップサービス
- Diagramsは、WorkSpacesのアイコンが用意されてない
- 作成したい構成図はこれ ↓

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274270/2c23430d-dbd5-ce1c-1e5b-a1397f18c6cb.png)

Diagramsでかいたのがこれ（まずまずかな）

![図2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274270/92576fb8-36bc-875b-0533-442847c5b79d.png)



- Storage Gateway, WorkSpacesなどDiagramsにないアイコンは、別のアイコンで代用



WorkSpacesやENIなど、Diagramsに用意されてないアイコンは、Clientアイコンで代用


```python:WorkSpaceの構成図
from diagrams import Cluster, Diagram, Edge
from diagrams.aws.network import VPC
from diagrams.aws.compute import EC2
from diagrams.aws.database import RDS
from diagrams.aws.network import Route53
from diagrams.aws.network import NATGateway
from diagrams.aws.network import InternetGateway
from diagrams.onprem.network import Internet
from diagrams.onprem.client import Client
from diagrams.aws.security import DirectoryService
from diagrams.aws.network import DirectConnect
from diagrams.onprem.compute import Server
from diagrams.onprem.client import User
from diagrams.onprem.compute import Nomad

with Diagram("Fig-002-WorkSpaces", show=False):
    with Cluster("Fig-002-WorkSpaces"):
        Internet_01 = Internet("Internet")
        User_01 = User("ユーザー１")
        User_02 = User("ユーザー２")
        UserDevice_01 = Client("デバイス")
        UserDevice_02 = Client("デバイス")

        with Cluster("VPC/AWS管理"):
            StorageGw_01 = Client("StorageGateway")
            WorkSpace_01 = Client("WorkSpacesForUser1")
            WorkSpace_02 = Client("WorkSpacesForUser2")

        with Cluster("VPC/192.168.0.0/HOGE"):
            with Cluster("subnet/192.168.1.0/public"):
                eni_01 = Client("ENI User1")
                eni_02 = Client("ENI User2")

        DirectConnect_01 = DirectConnect("DirectConnect")
        ADConnector_01 = DirectoryService("AD Connector")

        with Cluster("拠点"):
            with Cluster("社内ネットワーク"):
                ActiveDirectory_01 = Server("AD")

    # 構成図 WorkSpace
    User_01 - UserDevice_01 - Internet_01 - StorageGw_01 - WorkSpace_01 - eni_01 # デバイス-WorkSpace間の通信経路
    User_02 - UserDevice_02 - Internet_01
    StorageGw_01 - WorkSpace_02 - eni_02
    ADConnector_01 - DirectConnect_01 - ActiveDirectory_01 # 拠点内AD-AD Connector間の通信経路
    ADConnector_01 - Edge(style="dotted") - WorkSpace_01
    ADConnector_01 - Edge(style="dotted") - WorkSpace_02
```

※余談※
拠点のアイコンが左上になるとか、思ったところにアイコンを配置できずに苦労した。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274270/a9dedcc8-021d-db9b-56e5-054ecd9c1443.png" width="300" />


## 所感:Diagramsを使ってみて思ったこと

- 全体
  - Storage Gateway, WorkSpacesなどDiagramsにないアイコンは、別のアイコンで代用
  - 代用アイコンを決めておけば、チーム内で共有する構成図として十分に使えそう
    - (Diagramsが代用アイコンを用意してくれると嬉しい、お願いします中の人！)
  - ラフスケッチとして使えるレベル、清書を頼むときに使えそう
  - 学習コスト低くて助かる
- コードのかきかた
  - 変数の命名規則が重要（これに限らず重要）
  - 各アイコンの接続情報はコード最後尾に集約がオススメ
  - 接続情報を見て構成図がわかるよう規則をチームで共有するとよさげ(*1)

(*1) 規則きめれば ↓ 見て構成図を脳内作成できそぉ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274270/259b79fb-0d0a-6dd3-0e8c-76036535713f.png)

## おまけ

[onPrem](https://diagrams.mingrammer.com/docs/nodes/onprem)のアイコン出力とPythonコードをのせておく(2020年5月時点の情報)

![onprem-aicons.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274270/c0cfab40-3d2d-becc-1555-e35925e2017a.png)

```python:onPremアイコン出力
from diagrams import Cluster, Diagram, Edge
# onprem.database
from diagrams.onprem.database import Cassandra
from diagrams.onprem.database import Clickhouse, ClickHouse
from diagrams.onprem.database import Cockroachdb, CockroachDB
from diagrams.onprem.database import Couchdb, CouchDB
from diagrams.onprem.database import Dgraph
from diagrams.onprem.database import Hbase, HBase
from diagrams.onprem.database import Influxdb, InfluxDB
from diagrams.onprem.database import Janusgraph, JanusGraph
from diagrams.onprem.database import Mariadb, MariaDB
from diagrams.onprem.database import Mongodb, MongoDB
from diagrams.onprem.database import Mssql, MSSQL
from diagrams.onprem.database import Mysql, MySQL
from diagrams.onprem.database import Neo4J
from diagrams.onprem.database import Oracle
from diagrams.onprem.database import Postgresql, PostgreSQL
from diagrams.onprem.database import Scylla
# onprem.security
from diagrams.onprem.security import Trivy
from diagrams.onprem.security import Vault
# onprem.ci
from diagrams.onprem.ci import Circleci, CircleCI
from diagrams.onprem.ci import Jenkins
from diagrams.onprem.ci import Teamcity, TC
from diagrams.onprem.ci import Travisci, TravisCI
# onprem.etl
from diagrams.onprem.etl import Embulk
# onprem.mlops
from diagrams.onprem.mlops import Polyaxon
# onprem.network
from diagrams.onprem.network import Apache
from diagrams.onprem.network import Caddy
from diagrams.onprem.network import Consul
from diagrams.onprem.network import Envoy
from diagrams.onprem.network import Etcd, ETCD
from diagrams.onprem.network import Haproxy, HAProxy
from diagrams.onprem.network import Internet
from diagrams.onprem.network import Istio
from diagrams.onprem.network import Kong
from diagrams.onprem.network import Linkerd
from diagrams.onprem.network import Nginx
from diagrams.onprem.network import Pfsense, PFSense
from diagrams.onprem.network import Pomerium
from diagrams.onprem.network import Tomcat
from diagrams.onprem.network import Traefik
from diagrams.onprem.network import Vyos, VyOS
from diagrams.onprem.network import Zookeeper
# onprem.inmemory
from diagrams.onprem.inmemory import Aerospike
from diagrams.onprem.inmemory import Hazelcast
from diagrams.onprem.inmemory import Memcached
from diagrams.onprem.inmemory import Redis
# onprem.search
from diagrams.onprem.search import Elasticsearch
from diagrams.onprem.search import Solr
# onprem.container
from diagrams.onprem.container import Docker
from diagrams.onprem.container import Rkt, RKT
# onprem.iac
from diagrams.onprem.iac import Ansible
from diagrams.onprem.iac import Awx
from diagrams.onprem.iac import Terraform
# onprem.compute
from diagrams.onprem.compute import Nomad
from diagrams.onprem.compute import Server
# onprem.vcs
from diagrams.onprem.vcs import Git
from diagrams.onprem.vcs import Github
from diagrams.onprem.vcs import Gitlab
# onprem.workflow
from diagrams.onprem.workflow import Airflow
from diagrams.onprem.workflow import Digdag
from diagrams.onprem.workflow import Kubeflow, KubeFlow
from diagrams.onprem.workflow import Nifi, NiFi
# onprem.queue
from diagrams.onprem.queue import Activemq, ActiveMQ
from diagrams.onprem.queue import Celery
from diagrams.onprem.queue import Kafka
from diagrams.onprem.queue import Rabbitmq, RabbitMQ
from diagrams.onprem.queue import Zeromq, ZeroMQ
# onprem.cd
from diagrams.onprem.cd import Spinnaker
# onprem.gitops
from diagrams.onprem.gitops import Argocd, ArgoCD
# onprem.monitoring
from diagrams.onprem.monitoring import Datadog
from diagrams.onprem.monitoring import Grafana
from diagrams.onprem.monitoring import Kibana
from diagrams.onprem.monitoring import Prometheus
from diagrams.onprem.monitoring import Splunk
from diagrams.onprem.monitoring import Thanos
# onprem.client
from diagrams.onprem.client import Client
from diagrams.onprem.client import User
from diagrams.onprem.client import Users
# onprem.logging
from diagrams.onprem.logging import Fluentd
from diagrams.onprem.logging import Logstash, LogStash
from diagrams.onprem.logging import Loki
# onprem.analytics
from diagrams.onprem.analytics import Beam
from diagrams.onprem.analytics import Flink
from diagrams.onprem.analytics import Hadoop
from diagrams.onprem.analytics import Hive
from diagrams.onprem.analytics import Metabase
from diagrams.onprem.analytics import Norikra
from diagrams.onprem.analytics import Spark
from diagrams.onprem.analytics import Storm
from diagrams.onprem.analytics import Tableau



with Diagram("OnPrem-Aicons", show=False):
    with Cluster("OnPrem"):
        # onprem.database("# onprem.database")
        Cassandra("Cassandra")
        Clickhouse, ClickHouse("Clickhouse, ClickHouse")
        Cockroachdb, CockroachDB("Cockroachdb, CockroachDB")
        Couchdb, CouchDB("Couchdb, CouchDB")
        Dgraph("Dgraph")
        Hbase, HBase("Hbase, HBase")
        Influxdb, InfluxDB("Influxdb, InfluxDB")
        Janusgraph, JanusGraph("Janusgraph, JanusGraph")
        Mariadb, MariaDB("Mariadb, MariaDB")
        Mongodb, MongoDB("Mongodb, MongoDB")
        Mssql, MSSQL("Mssql, MSSQL")
        Mysql, MySQL("Mysql, MySQL")
        Neo4J("Neo4J")
        Oracle("Oracle")
        Postgresql, PostgreSQL("Postgresql, PostgreSQL")
        Scylla("Scylla")
        # onprem.security("# onprem.security")
        Trivy("Trivy")
        Vault("Vault")
        # onprem.ci("# onprem.ci")
        Circleci, CircleCI("Circleci, CircleCI")
        Jenkins("Jenkins")
        Teamcity, TC("Teamcity, TC")
        Travisci, TravisCI("Travisci, TravisCI")
        # onprem.etl("# onprem.etl")
        Embulk("Embulk")
        # onprem.mlops("# onprem.mlops")
        Polyaxon("Polyaxon")
        # onprem.network("# onprem.network")
        Apache("Apache")
        Caddy("Caddy")
        Consul("Consul")
        Envoy("Envoy")
        Etcd, ETCD("Etcd, ETCD")
        Haproxy, HAProxy("Haproxy, HAProxy")
        Internet("Internet")
        Istio("Istio")
        Kong("Kong")
        Linkerd("Linkerd")
        Nginx("Nginx")
        Pfsense, PFSense("Pfsense, PFSense")
        Pomerium("Pomerium")
        Tomcat("Tomcat")
        Traefik("Traefik")
        Vyos, VyOS("Vyos, VyOS")
        Zookeeper("Zookeeper")
        # onprem.inmemory("# onprem.inmemory")
        Aerospike("Aerospike")
        Hazelcast("Hazelcast")
        Memcached("Memcached")
        Redis("Redis")
        # onprem.search("# onprem.search")
        Elasticsearch("Elasticsearch")
        Solr("Solr")
        # onprem.container("# onprem.container")
        Docker("Docker")
        Rkt, RKT("Rkt, RKT")
        # onprem.iac("# onprem.iac")
        Ansible("Ansible")
        Awx("Awx")
        Terraform("Terraform")
        # onprem.compute("# onprem.compute")
        Nomad("Nomad")
        Server("Server")
        # onprem.vcs("# onprem.vcs")
        Git("Git")
        Github("Github")
        Gitlab("Gitlab")
        # onprem.workflow("# onprem.workflow")
        Airflow("Airflow")
        Digdag("Digdag")
        Kubeflow, KubeFlow("Kubeflow, KubeFlow")
        Nifi, NiFi("Nifi, NiFi")
        # onprem.queue("# onprem.queue")
        Activemq, ActiveMQ("Activemq, ActiveMQ")
        Celery("Celery")
        Kafka("Kafka")
        Rabbitmq, RabbitMQ("Rabbitmq, RabbitMQ")
        Zeromq, ZeroMQ("Zeromq, ZeroMQ")
        # onprem.cd("# onprem.cd")
        Spinnaker("Spinnaker")
        # onprem.gitops("# onprem.gitops")
        Argocd, ArgoCD("Argocd, ArgoCD")
        # onprem.monitoring("# onprem.monitoring")
        Datadog("Datadog")
        Grafana("Grafana")
        Kibana("Kibana")
        Prometheus("Prometheus")
        Splunk("Splunk")
        Thanos("Thanos")
        # onprem.client("# onprem.client")
        Client("Client")
        User("User")
        Users("Users")
        # onprem.logging("# onprem.logging")
        Fluentd("Fluentd")
        Logstash, LogStash("Logstash, LogStash")
        Loki("Loki")
        # onprem.analytics("# onprem.analytics")
        Beam("Beam")
        Flink("Flink")
        Hadoop("Hadoop")
        Hive("Hive")
        Metabase("Metabase")
        Norikra("Norikra")
        Spark("Spark")
        Storm("Storm")
        Tableau("Tableau")
```