D2开源工具:用纯文本绘制自动布局的架构图
这两年我给团队、给客户画的架构图加起来没有五十张也有三十张。说句实话,用Visio这类传统工具画图,最大的问题不是画不出来,而是画完之后的“维护成本”高得离谱:改一个模块名要把相关连接线挨个改,导出的PNG边距还要专门调,文件发给别人版本又乱了。后来我切到一个叫D2的开源命令行工具,GitHub上已经有两万五千多颗星,彻底把“画架构图”这件事变成了“写架构图”。D2的核心玩法是:你用纯文本描述节点、容器和连接关系,工具自动完成排版布局,输出SVG、PNG或者PDF。这篇文章我把从安装、语法、实操到踩坑的整套经验写出来,希望对准备换工具的人有帮助。
1. D2到底是个什么项目,为什么我建议你试试
1.1 站在绘图工具的岔路口:拖拽式与文本声明式
市面上的绘图工具其实就两个流派。一类是Visio、draw.io这类拖拽式工具,所有图形、箭头都靠鼠标摆,优点是灵活到极致,一个像素不满意都能改,缺点是“画”的占比太高,而“想清楚结构”的占比太低。你花大量时间在对齐、调线、改字体上,真正有价值的设计决策反而被挤到后排。另一类是以Mermaid、PlantUML代表的文本声明式工具,你只管用代码描述“谁连谁”,布局交给引擎,这类工具在程序员群体里接受度很高,但之前的方案总有一些让人不舒服的地方:Mermaid语法简单但复杂系统图容易乱,PlantUML的年代感太强,长图布局经常失控。
D2明显选择了第二类路线,而且把体验做到了一个新高度。它的设计哲学非常明确:用户的输入只描述关系,不描述坐标。任何节点、容器、连接线都是由声明式语法生成,底层的dagre或ELK布局引擎自动计算位置。我第一次看到D2渲染结果的时候,被它的默认排版质量惊到了,线条的拐弯、节点的间距、标签的放置都相当克制,不需要像Mermaid那样频繁依赖人工样式修正。
1.2 两万五千颗星背后的需求信号
一个工具能在GitHub拿到两万五千星,说明它踩中了某个群体的集体痛点。架构图这件事,听上去很小,但所有做系统设计、微服务拆分、网络拓扑、数据流梳理的人都绕不开。过去大家默认“架构图就该用Visio画”,可真到落地才发现,二进制文件没法做代码评审,没法diff,没法自动合并,多人协作时来回传压缩包,最后图里的状态跟线上代码完全是两码事。
D2火的底层逻辑,其实是把架构图从“一次性交付物”变成了“可版本管理的资产”。你可以把.d2源文件丢进Git仓库,每次需求变更只改几行文本,Pull Request里能清楚看到连接关系的变化,CI里还能自动渲染最新版本。这种工作流太对技术团队的胃口了,所以它几乎靠口碑就滚到了2.5万星。
2. 安装与第一个demo:3种方式跑起来
2.1 安装d2的三种常见途径
D2的安装方式相当友好,官方提供了脚本、Homebrew、Go install三条主要路径。我在macOS上用得最多的是Homebrew,一条命令搞定,升级也方便。Windows环境我建议走WSL,然后把d2放到PATH里,体验与Linux完全一致。
| 系统 | 安装方式 | 命令 |
|---|---|---|
| macOS | Homebrew | brew install d2 |
| Linux | 官方安装脚本 | curl -fsSL https://d2lang.com/install.sh | sh -s -- |
| Windows | WSL后走Linux脚本,或下载Windows二进制 | 推荐WSL方案 |
| 任意系统 | Go工具链 | go install oss.terrastruct.com/d2@latest |
安装完先跑一下d2 --version,能输出版本号就说明环境没问题。我个人更推荐Homebrew和官方脚本,因为自动处理了PATH和依赖,Go install方案适合本来就常年在命令行里折腾的玩家,而且需要确保$GOPATH/bin在你的PATH里,否则容易遇到“命令找不到”的尴尬。
2.2 写一个10行的d2文件,渲染成svg
安装完成后,找一个空目录,新建一个first.d2文件,把下面的内容复制进去:
然后在这个目录里执行:
一条命令,svg就出来了。用浏览器打开first.svg,你会看到一张简洁的横向流程图,节点和箭头都已经自动排好版。箭头上的文字也不用你手动摆位置,D2会把它放在合适的位置,这一点就比Visio里手工拖标签省了无数时间。
如果嫌一张静态图片不过瘾,还可以用监听模式:
之后你每次保存first.d2,D2都会自动重新渲染,改完代码立刻刷新浏览器就能看到效果,画图体验接近热更新,非常顺手。
2.3 第一次接触最容易忽略的几个细节
刚开始用D2,有几个小细节容易让人感到疑惑。
第一个是缩进。D2用缩进表达容器嵌套,但不像Python那样强制4个空格,关键是同级节点要保持一致的缩进层级,否则很容易渲染成意外的容器结构。我习惯统一用两个空格,时间长了不会乱。
第二个是--pad参数。D2默认会给画布留一定的边距,但如果追求“导出PNG边距小”或贴合文档排版,可以自己指定:d2 --pad=16 first.d2 first.png。这个参数在导出PNG、嵌入网页时非常实用,也是对比Visio时一个让我很舒服的点。
第三个是--sketch参数,加在命令里就能输出手绘风格的图,适合画概念草图、给非技术人员做演示。很多人第一次看到这功能都以为是另一个工具,其实D2原生就支持:d2 --sketch first.d2 sketch.svg。
3. 核心语法与设计思路拆解
3.1 变量、容器和连接线是怎么表达的
D2的语法走的是“让人一眼能看懂”的路线,几乎不需要学习曲线。最基本的三件事是节点、连接和容器。
节点就是一行文本,格式是“节点名: 显示标签”。你可以只写一个节点名,也可以给一个更友好的显示标签:
连接用箭头表示,->表示单向连接,<->表示双向关联,箭头后面可以带冒号标签,用来描述关系:
容器是D2一个很核心的概念,用大括号包裹子节点,视觉上会渲染成一个带边框的盒子,非常适合表达微服务边界、云平台分区、逻辑分组:
容器里可以继续嵌套容器,层级深度没有硬性限制,但实际项目里我建议最多套三层,太深了画布会非常拥挤,反而不利于阅读。真正复杂的系统,拆成多张图往往比一张巨图更清晰。
3.2 让架构图更好看的通用做法:shape、主题、方向
纯文本默认渲染出来的图是干净,但架构图如果只有方框加箭头,看久了会疲劳,也缺乏层次。D2的样式定制是文档驱动的,比拖拽工具更可控。
节点形状可选的相当多,比如数据库用cylinder、用户端用person、消息队列用queue、决策点用diamond,一眼就能从形状上识别节点类型:
全局观感靠--theme参数调整,D2内置了几十套配色,编号从0到300多,我常用的是官网默认的0和偏深色系的300。调主题只需要改命令行,不用改源文件,这在给不同客户出图时特别方便。
布局方向用direction关键字控制,默认是垂直向下,常见的还有right横向分布。如果觉得单张图太挤,可以给不同区域分别设定布局方向,这是D2在复杂图中比较灵活的地方。
3.3 布局引擎的选型:dagre和ELK的区别
布局是所有自动排版工具最容易拉胯的环节。D2默认使用dagre引擎,这个引擎擅长层次化布局,对大多数系统架构图、流程图来说表现很稳定。但如果你画的是网状结构特别多的图,比如服务间互相调用很密集、跨层连线很多,dagre排出来的线条可能交叉得厉害。
这时候可以切换到ELK布局引擎:
ELK对长连接、分层布局的处理更克制,在微服务调用关系复杂、数据流向多的场景下,效果经常比默认引擎高一个档次。代价是渲染耗时略高,但架构图的节点规模一般不会太大,几十个节点的图基本感受不到延迟。
我自己的习惯是:先默认引擎出图,如果感觉交叉过多、可读性差,再切ELK对比一下。花了不到五分钟就能得到两个版本,这种试错成本在Visio时代想都不敢想。
4. 实操:从零到一画一张微服务架构图
4.1 先把架构关系在脑子里过一遍
画图之前,别急着写代码。无论用什么工具,最花时间的永远是“想清楚系统里有谁、谁依赖谁”。我会先拿一张草稿纸或备忘录列出三样东西:节点清单、容器层级和连接关系。以一套简单的电商微服务为例:
- 外部入口:移动端App、管理后台
- API网关:承接所有外部流量,做鉴权和路由
- 业务服务:用户服务、订单服务、商品服务
- 中间件:Redis缓存、Kafka消息队列
- 存储:用户库、订单库
- 依赖关系:网关转发到各业务服务,用户服务依赖用户库,订单服务依赖订单库和消息队列,商品服务依赖Redis缓存
这些关系用自然语言描述也就几句话,但落在Visio里要画十几个形状、十几条线、还要调整布局。在D2里,整个过程就是把这段话翻译成声明式语法。
4.2 编写d2文件:完整代码与逐行解释
我给出一个可以直接复制的完整示例,文件名叫microservice.d2:
解释几个关键点:direction: right让整张图横向排布,适合微服务这类需要体现“前端到后端”流向的场景。业务中台和数据层是两层容器,把所有服务按边界收纳起来,一眼就能看到系统被分成了哪几个区域。存储节点统一用了cylinder形状,图形的“数据库感”立刻出来了。
4.3 渲染、微调与把SVG嵌入文档
编写完源文件后,执行:
--theme=200会给出一套偏清新的蓝色系配色,--pad=20控制画布边距。如果最终要放进技术方案文档或PPT,需要PNG格式,指令改成:
SVG和PNG的区别要想清楚:SVG是矢量格式,放再大都不会糊,适合网页和PPT;PNG适合直接贴进Word、飞书文档,但放大到超过原始渲染像素会发虚。D2默认渲染的SVG分辨率非常高,一般场景够用。
我实际做方案时,最常用的路径是:D2渲染SVG,然后直接嵌入Markdown文档或拖进公司Wiki。这样源文件和出图一一对应,之后业务变了,改几行文本重新跑一次,图就同步更新,彻底告别曾经“文档里的图过期半年”的尴尬。
5. 常见问题与避坑实录
5.1 中文显示成方框怎么办
D2默认字体对中文的覆盖不一定理想,在部分环境里,直接写中文标签,渲染出来的SVG里中文可能显示成方块。这个问题和语法无关,纯粹是字体匹配的问题。
解决办法有两种:一种是在D2文件的全局样式里指定中文字体,比如把根节点的font-family设置为系统已有的中文字体名称;另一种是渲染出SVG后,用文本替换的方式把字体设置全局替换成中文字体。我自己更推荐前者,因为一次配置,后续所有图都生效。
另外建议节点名本身用英文小写加连字符,中文内容放到显示标签里:
这样既避免文件名和CI脚本里的编码问题,也不影响图上展示中文。
5.2 布局怎么调都很别扭,问题可能不在参数
很多人一上来就狂试--layout参数和direction,但改了之后还是乱。遇到这种情况,我会先检查是不是容器层级出了问题。D2的布局是受内容结构约束的,同层的节点会被尽可能排列在一起,如果某个节点忘了放进容器,它就可能被甩到图的最边缘,横跨几条长线,怎么看怎么别扭。
还有一点是人为把图塞得过大。一个文件里塞了五六十个节点、上百条连接线,再强的布局引擎也救不回来。我的经验是单图层控制在三十个节点以内,内容多就按业务边界拆成多张图,用跨图引用或文字说明关联关系。
5.3 导出的PNG背景、边距和精度问题
有段时间我做PPT急需透明背景的PNG,D2提供了--transparent参数,一行命令就解决:
没有这个参数时,默认白色背景,嵌入深色PPT页面会有一个明显的白色方块,很丑。边距问题前文提过,用--pad控制。另外,如果你发现PNG在4K屏上放大后锯齿严重,建议优先使用SVG,或者直接把渲染尺寸调大,D2对分辨率没有硬性限制,这与当年在Visio里费劲调整页面大小和导出选项的体验完全不同。
5.4 在CI里自动出图,让架构图永远和代码同步
文本源文件带来的另一个福利是自动渲染。我目前在团队里做的方案是:.d2源文件放在仓库的diagrams目录,推送代码时触发GitHub Actions,自动安装D2、渲染成SVG和PNG,再把产物发布到文档站点或作为构建产物保存。
一个很简化的Actions流程思路是这样的:
这样架构图和代码进入同一套评审、合并流程。设计变更在Pull Request里就能通过diff看到,而不是同事私聊里发一个“更新后的图”压缩包。
6. 和Visio、draw.io、Mermaid、PlantUML的一线对比
6.1 一张表看清六类工具
| 维度 | D2 | Visio | draw.io | Mermaid | PlantUML |
|---|---|---|---|---|---|
| 操作方式 | 文本声明式 | 鼠标拖拽 | 鼠标拖拽 | 文本声明式 | 文本声明式 |
| 学习成本 | 很低 | 中高 | 中 | 低 | 中 |
| 自动布局 | 优秀 | 弱 | 中 | 中 | 中 |
| 版本管理 | 天然支持 | 差 | 差 | 天然支持 | 天然支持 |
| 中文显示 | 需配置字体 | 好 | 好 | 依赖环境 | 依赖环境 |
| 样式定制 | 文档驱动 | 自由但繁琐 | 自由 | 一般 | 一般 |
| 适合场景 | 系统架构、微服务、ER、网络拓扑 | 运维图、平面图、精细排版 | 临时脑暴、非技术协作 | 简单流程图、时序图 | 类图、时序图 |
| 价格 | 开源免费 | 商业授权 | 免费 | 开源免费 | 开源免费 |
这个表比较粗暴,但能看出一个趋势:Visio在精细排版和传统文档场景里确实不可替代,但凡是跟软件工程、代码仓库、自动化工作流相关的场景,文本声明式工具的优势是压倒性的。
6.2 什么时候你还需要继续用Visio
我不打算把D2吹成Visio终结者,这是不诚实的态度。Visio还有一些D2目前很难覆盖的场景:它的精准对齐和网格非常适合画机房机柜拓扑图、综合布线图、平面布局图这类需要真实尺寸感的图纸;流程泳道图也是它的强项,D2虽然能画容器分组,但在泳道、时间轴这类复杂布局上还达不到Visio的精细度。
所以我的建议是:如果你主要画的是“系统逻辑架构”“软件调用关系”“数据流向”这类偏软件设计的图,可以直接切到D2;如果你经常画的是物理拓扑、工程部署、复杂流程泳道,Visio继续留着,不冲突。
6.3 我自己的迁移路线建议
从Visio迁移到D2不需要一步到位。我可以给一个稳当的路线:第一步,挑一张不复杂的架构图,用D2重画一遍,感受语法和默认渲染效果;第二步,把团队最近新产生的图直接用D2出,旧图不着急迁移;第三步,将D2源文件纳入Git仓库,配合CI自动渲染,让团队习惯通过文本审图;第四步,等大家体会到版本管理和自动布局的好处后再逐步覆盖存量图。
我在团队里推这套流程大概用了一个月,最初也有人觉得“还不如Visio拖一下快”,但一旦涉及方案修改,就会发现D2只改几行文本的优势实在明显。
7. 进阶玩法:主题、图标和生态扩展
7.1 用主题快速改变整套图的观感
给客户出方案时,不同客户对配色的偏好差异非常大,有些喜欢浅色清新,有些喜欢深色科技感。如果每次手动调颜色会很浪费时间,D2的主题系统就是为这个场景准备的。
渲染命令里加--theme=301、--theme=200这类编号,就能整体替换配色。主题编号覆盖了常见的设计风格,从默认的白底蓝灰到深色暗黑,基本能满足对外汇报需求。还可以把主题参数写进脚本,针对不同客户分别出一套图,整个过程非常“工程化”。
7.2 自定义形状与图标资源
除了常见的基本形状,D2还支持加载一些图标资源,让节点看起来更像真实服务。比如用户相关节点用人物图标、数据库节点用桶状图标、云计算节点用云朵图标,配合CSS级别的颜色设置,整张图的信息密度和美观度可以提升很多。
资源文件的加载方式网上有现成案例,但我的建议是:图标是锦上添花,不要一上来就追求花哨。大部分架构图最重要的是关系表达清晰,色彩和图标只用来强调层级和边界。如果图标库版本和D2版本不匹配,还有可能出错,所以在正式环境里我倾向于先使用内置形状,等核心结构稳定后再加图标。
7.3 社区生态与后续展望
D2的生态还在快速成长,已经出现了不少编辑器插件、CI模板和周边工具。VSCode、Neovim里都有语法高亮插件,写源文件时体验接近写代码。社区提供的Makefile模板和GitHub Actions例子也很多,直接把渲染流程接进现有工程并不难。
更有想象力的方向是把它和生成式AI结合。D2的语法足够简单、结构化,非常适合让AI根据一段需求描述直接生成.d2文件,再由D2渲染成图。我最近在尝试一个工作流:让业务方用自然语言描述系统模块和依赖,基于这个描述生成D2源文件,再渲染出第一版架构图,人工微调几处关键边界后,整张图的产出时间从半天压缩到十分钟。
D2目前还在快速迭代,偶尔会遇到一些语法变动,但核心的声明式表达逻辑已经很稳。如果你经常被架构图折磨,不妨花一个下午把基本语法过一遍,再挑一张最近的图重画试试。我在实际使用中最深的体会是:一旦源文件进入Git,架构图就从一个静态交付物变成了活文档,这个转变带来的效率提升,比任何拖拽上的技巧都大。