Comparing D2 and Diagrams as cloud architecture diagram drawing tools

Cloud Architecture using D2

Intro

The solution architect in the cloud often creates architecture diagrams. There are various tools available for drawing diagrams, with services like Draw.io being prominent. These tools allow drawing diagrams through a GUI, but surprisingly, it consumes a significant amount of time. Moreover, different architects may have different patterns for drawing architectures, resulting in variations even for the same architecture.

Recently, the concept of Diagram as Code emerged to address these issues. In fact, this concept has existed for a while but has primarily focused on general diagram creation and was not optimized for cloud architecture. Therefore, there are now code-focused solutions for drawing cloud architectures. One such solution is Diagrams, developed by a Korean developer. This code is open source and has gained some recognition.

However, it seems that the main developer has been busy and hasn’t been updating it for a while. Of course, you could fork the project and continue development, but there is a possibility of maintenance issues unless it is officially archived. (It was actually updated while I was writing this.)

So, while exploring alternatives, you came across the D2 code. D2 has attracted more users and is based on Golang. However, it still has some inconveniences when used for creating cloud architectures. Therefore, you plan to draw the same diagrams with D2 to see if it is suitable as a tool for drawing cloud architecture diagrams.

First Example

The Quick Start for Diagrams includes the following example:

from diagrams import Diagram
from diagrams.aws.compute import EC2
from diagrams.aws.database import RDS
from diagrams.aws.network import ELB
  
with Diagram("", show=False):
    ELB("lb") >> EC2("web") >> RDS("userdb") 
web service diagram(from Diagrams)
web service diagram(from Diagrams)

This is a very simple code, but one thing to note here is the use of >> to represent relationships between instances. This is a feature called operator overloading, where existing operator functionality between instance objects is redefined. In Python, >> is the rshift operator.

To draw this architecture in D2, you can write it as follows.

direction: right
  
lb: "lb" {
  shape: image
  icon: https://icon.icepanel.io/AWS/svg/Networking-Content-Delivery/Elastic-Load-Balancing.svg
  width: 100
  height: 100
}
  
ec2: "web" {
  shape: image
  icon: https://icons.terrastruct.com/aws%2FCompute%2FAmazon-EC2_light-bg.svg
  width: 100
  height: 100
}
  
db: "db" {
  shape: image
  icon: https://icons.terrastruct.com/aws%2FDatabase%2FAmazon-RDS.svg
  width: 100
  height: 100
}
  
lb -> ec2 -> db 
  
web service diagram using D2
web service diagram using D2

Since it’s a simple diagram, it can be almost identical. The >> operator is changed to ->. However, in Diagrams, you can easily refer to each item as instance objects, while in D2, there is an inconvenience where you need to specify the shape using image files directly. D2 has Class and Import features, and we will use them to make the following changes.

To draw this architecture in D2, you can write it as follows. Since it’s a simple diagram, it can be almost identical. The >> operator is changed to ->. However, in Diagrams, you can easily refer to each item as instance objects, while in D2, there is an inconvenience where you need to specify the shape using image files directly. D2 has Class and Import features, and we will use them to make the following changes.

First, create a file to serve as the Class, and name it models.d2.

classes:{
lb: {
  shape: image
  icon: https://icons.terrastruct.com/aws%2FNetworking%20&%20Content%20Delivery%2FElastic-Load-Balancing.svg
  width: 100
  height: 100
}

ec2: {
  shape: image
  icon: https://icons.terrastruct.com/aws%2FCompute%2FAmazon-EC2_light-bg.svg
  width: 100
  height: 100
}

db: {
  shape: image
  icon: https://icons.terrastruct.com/aws%2FDatabase%2FAmazon-RDS.svg
  width: 100
  height: 100
}
}

Then, by importing this models file, you can achieve the same result.

direction: right

...@models.d2

lb.class: lb
ec2.class: ec2
ec2.label: web
db.class: db
db.label: events

lb -> ec2 -> db

Second Example 두번째 예제

The second example is the first example among the Examples in Diagrams.

Link

Grouped Workers on AWS

  from diagrams import Diagram
  from diagrams.aws.compute import EC2
  from diagrams.aws.database import RDS
  from diagrams.aws.network import ELB
  
  with Diagram("Grouped Workers", show=False, direction="TB"):
      ELB("lb") >> [EC2("worker1"),
                    EC2("worker2"),
                    EC2("worker3"),
                    EC2("worker4"),
                    EC2("worker5")] >> RDS("events")
grouped_workers_diagram(from Diagrams)
grouped_workers_diagram(from Diagrams)

Each node represents a Python instance, and you can create these instances as a list and give a single command to connect them all at once. How can you achieve this in D2? Unfortunately, D2 doesn’t have a group feature. There is a concept called Container, which is somewhat similar, but it’s a slightly different concept from grouping, as I will explain in the next example. In D2, you have to write individual connection relationships for each node as shown below.

direction: down

...@models2.d2

lb.class: lb
worker1.class: ec2
worker2.class: ec2
worker3.class: ec2
worker4.class: ec2
worker5.class: ec2
db.class: db
db.label: events

lb -> worker1 -> db
lb -> worker2 -> db
lb -> worker3 -> db
lb -> worker4 -> db
lb -> worker5 -> db

Another feature to explain here is D2’s Layout feature. Layout is an engine that determines how to arrange the diagram. D2 supports three Layout engines: Dagre, ELK, and TALA. If you’re not sure about the concept of Layout, let’s try drawing diagrams and see the results.

The command to extract the diagram in D2 is as follows.

d2 -s -t 302 -l dagre example.d2 example2new_dagre.png

Here, -s indicates that it will be drawn in the sketch version, and -t 302 specifies the theme. Please refer to the official documentation for information about the sketch version and themes. -l is used to specify the layout, and here, Darge is specified. You should list the D2 file and the output file in that order.

First, I tried drawing it using the Dagre version.

grouped_workers_diagram: Dagre Layout
grouped_workers_diagram: Dagre Layout

I will now draw it using the ELK version.

grouped_workers_diagram: ELK Layout
grouped_workers_diagram: ELK Layout

The first two layouts can be used for free, but the TALA layout requires a paid subscription. However, it seems to be free for evaluation purposes, so it should be fine for the purpose of this blog post.

grouped_workers_diagram: Tala Layout
grouped_workers_diagram: Tala Layout

Which of the three layouts is the best? It may vary depending on personal preference. I won’t provide an objective evaluation, but in my opinion, for this example, the free-to-use ELK method seems better.

Third Example

The third example is the second example among the Examples in Diagrams.

Link

from diagrams import Cluster, Diagram
from diagrams.aws.compute import ECS
from diagrams.aws.database import ElastiCache, RDS
from diagrams.aws.network import ELB
from diagrams.aws.network import Route53

with Diagram("Clustered Web Services", show=False):
    dns = Route53("dns")
    lb = ELB("lb")

    with Cluster("Services"):
        svc_group = [ECS("web1"),
                     ECS("web2"),
                     ECS("web3")]

    with Cluster("DB Cluster"):
        db_primary = RDS("userdb")
        db_primary - [RDS("userdb ro")]

    memcached = ElastiCache("memcached")

    dns >> lb >> svc_group
    svc_group >> db_primary
    svc_group >> memcached
Clustered Web Services(from Diagrams)
Clustered Web Services(from Diagrams)

The Cluster function in Diagrams groups multiple nodes and draws them within a single box. Let’s try drawing this in D2. This time, I’ll show you the diagrams first.

Clustered Web Services(D2 Case 1)
Clustered Web Services(D2 Case 1)

This is a similar drawing, but it seems that there are too many arrows cluttering the diagram. In D2, the function to draw within a single box is called Container, and you can connect Containers with nodes. In fact, it’s easier to understand when you see it in the figure.

Clustered Web Services(D2 Case 2)
Clustered Web Services(D2 Case 2)

Depending on what you want to express, it may vary, but personally, I find that drawing it like the latter (Case 2) is neater and better. Diagrams does not yet have a feature to connect nodes to clusters (boxes).

Here’s the code I wrote.

models.d2

classes:{
lb: {
  shape: image
  icon: https://icons.terrastruct.com/aws%2FNetworking%20&%20Content%20Delivery%2FElastic-Load-Balancing.svg
  width: 100
  height: 100
}

ec2: {
  shape: image
  icon: https://icons.terrastruct.com/aws%2FCompute%2FAmazon-EC2_light-bg.svg
  width: 100
  height: 100
}

db: {
  shape: image
  icon: https://icons.terrastruct.com/aws%2FDatabase%2FAmazon-RDS.svg
  width: 100
  height: 100
}

dns: "dns" {
  shape: image
  icon: https://icons.terrastruct.com/aws%2FNetworking%20&%20Content%20Delivery%2FAmazon-Route-53.svg
  width: 100
  height: 100
}

memcached : "memcached" {
  shape: image
  icon: https://icons.terrastruct.com/aws%2FDatabase%2FAmazon-ElastiCache.svg
  width: 100
  height: 100
}

}
direction: right

...@models.d2

dns.class: dns
lb.class: lb
container1.web1.class: ec2
container1.web2.class: ec2
container1.web3.class: ec2
container2.userdb1.class: db
container2.userdb1.label: userdb
container2.userdb2.class: db
container2.userdb2.label: userdb ro
memcached.class: memcached

container1: "Services" {
  web1
  web2
  web3
}

container2: "DB Cluster" {
  userdb1
  userdb2
}

dns -> lb
lb -> container1
container1 -> container2
container1 -> memcached

container2.userdb1 -- container2.userdb2

Conclusion

I have compared Diagrams and D2 from the perspective of drawing cloud architectures. In reality, both of these codes are still not as user-friendly for creating visually appealing diagrams as direct drawing tools like Draw.io.

However, if the need for Diagrams as Code continues to grow, both of these codes could prove to be valuable. It seems that further development is needed for both codes, but the fact that D2 is actively developed by Terrastruct is an advantage for D2.

One advantage of Diagrams is that it is written in Python, making it suitable for integrating with other Python code for additional development. D2 also offers some capabilities for using it as a library in Golang, but it currently does not support as many features.

I will keep an eye on how both of these codes evolve in the future.

김종록

현재 삼성 SDS에서 클라우드 엔지니어로 활동하고 있습니다.

Next
Previous

Related