ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Excalidraw本地化部署实战:从Docker到生产环境的私有白板搭建

Excalidraw本地化部署实战:从Docker到生产环境的私有白板搭建 1. 从云端到本地为什么我们需要一个“离线版”白板最近在和一些做产品设计、技术架构的朋友聊天时发现一个挺有意思的现象大家手头都有一堆好用的在线协作工具但一遇到需要深度思考、梳理复杂逻辑或者涉及一些内部敏感信息草稿时第一反应还是打开一个离线的、完全受自己控制的画布。Excalidraw 这个工具相信很多做技术方案设计、产品原型草绘的朋友都不陌生。它那种手绘风格的清爽界面加上对图形、箭头、文本的优秀支持让它成了快速表达想法的利器。但它的官方版本毕竟是个在线服务数据存在别人的服务器上网络一卡顿思路就断了画点内部架构草图心里总有点不踏实。这就是“Excalidraw本地化部署”这个需求最直接的来源。说白了就是把那个好用的、在浏览器里运行的 Excalidraw完整地搬到我们自己的服务器或者电脑上让它变成一个可以离线运行、数据完全私有的内部工具。这不仅仅是“装个软件”那么简单它涉及到将一个现代的前端应用及其后端服务进行完整的自托管。从搜索热词来看像“deepseek本地化部署”、“本地化安装部署minimax”这类需求也层出不穷说明大家对于将优秀的AI或工具服务进行私有化部署掌握完全的数据主权和控制权已经成为一个普遍且强烈的诉求。我自己在团队内部推动过几次这类工具的本地化从最初的“图个新鲜”到后来的“离不开”感触很深。本地化部署后的 Excalidraw不仅仅是解决了网络和隐私焦虑它更成为了团队知识沉淀的一个“安全屋”。我们可以把一些长期的、迭代中的技术架构图、业务流程草图持久化地放在内网服务器上随时增删改查不用担心服务商突然收费、改政策或者停止服务。接下来我就把自己从零开始在一台干净的Linux服务器上部署 Excalidraw 的完整过程、遇到的坑以及一些优化心得详细地分享出来。整个过程不复杂但有些细节决定了部署的顺畅程度和使用体验。2. 部署前的核心准备理解架构与选择战场在动手敲命令之前我们得先搞清楚我们要部署的到底是个什么东西。Excalidraw 本质上是一个“前后端分离”的现代 Web 应用。这意味着我们需要准备两个部分前端用户直接在浏览器里交互的界面就是那些画布、工具栏。这部分是纯粹的静态文件HTML、CSS、JavaScript。在本地化部署中我们需要自己构建或获取这些文件。后端可选但推荐官方提供了配套的后端服务主要用于房间协作多人实时编辑和持久化存储将绘图保存到服务器。如果你只需要个人单机离线使用理论上只需要前端。但如果你想拥有一个功能完整、可以分享链接、支持多人协作的内网白板后端服务必不可少。理解了架构我们就要选择“战场”——也就是运行环境。最常见的选择有直接Docker部署这是最快捷、最干净的方式。Excalidraw 官方提供了 Docker 镜像我们只需要一条docker run命令就能同时启动前端和后端服务。这种方式隔离性好依赖问题少非常适合快速搭建和体验。从源码构建与部署这种方式更“硬核”也更有掌控感。你需要克隆 GitHub 上的源码安装 Node.js、Yarn 等构建工具自己执行构建命令生成静态文件然后配置一个 Web 服务器如 Nginx来提供这些文件同时可能还需要配置后端服务。这种方式适合需要深度定制比如修改UI、增加组件或学习其内部机制的场景。对于绝大多数以“用起来”为目标的团队和个人我强烈推荐Docker 部署方案。它屏蔽了环境差异和复杂的构建流程让我们能专注于服务本身的配置和使用。本文也将以 Docker 方案为主线进行讲解。我们的目标环境是一台安装了 Docker 和 Docker Compose 的 Linux 服务器Ubuntu 20.04/22.04 或 CentOS 7/8 均可拥有一个可以通过内网或公网IP访问的域名。注意无论选择哪种方式请确保你的服务器资源充足。Excalidraw 本身不重但如果你预期会有很多用户同时进行高强度的协作编辑比如几十人同时在一个房间画图那么需要为后端服务分配足够的内存建议不少于1GB和CPU资源。3. 基于Docker的一键式部署实战假设我们已经有一台安装了 Docker 和 Docker Compose 的服务器并且可以通过 SSH 登录。下面我们开始一步步操作。3.1 使用官方Docker镜像快速启动Excalidraw 团队在 Docker Hub 上维护了官方镜像excalidraw/excalidraw。这是启动服务最快的方式。首先创建一个专门的工作目录并进入mkdir -p /opt/excalidraw cd /opt/excalidraw然后直接运行以下 Docker 命令docker run -d \ --name excalidraw \ -p 80:80 \ -e NODE_ENVproduction \ excalidraw/excalidraw:latest这条命令做了几件事-d让容器在后台运行。--name excalidraw给容器起个名字方便管理。-p 80:80将容器内部的 80 端口映射到宿主机的 80 端口。这意味着你通过服务器的IP地址就能访问服务。-e NODE_ENVproduction设置环境变量为生产模式。excalidraw/excalidraw:latest指定使用的镜像。执行后使用docker ps命令查看容器是否正常运行。如果看到excalidraw容器状态为Up就说明启动成功了。此时在浏览器中访问http://你的服务器IP应该就能看到 Excalidraw 的界面了。但是这种方式有一个很大的局限性它只启动了前端服务没有后端。这意味着你无法使用“房间”协作功能也无法将绘图保存到服务器只能保存为本地文件。要获得完整功能我们需要使用 Docker Compose 来编排前端和后端两个服务。3.2 使用Docker Compose部署完整服务链Docker Compose 允许我们用一个配置文件docker-compose.yml来定义和运行多个相关联的容器。对于 Excalidraw我们需要两个服务前端 (excalidraw) 和后端 (excalidraw-room)。在工作目录下创建docker-compose.yml文件vim docker-compose.yml将以下内容粘贴进去。这里我使用了一个社区维护的、包含完整前后端的 Compose 配置示例它比单纯运行前端镜像更实用version: 3.8 services: excalidraw: image: excalidraw/excalidraw:latest container_name: excalidraw-app restart: unless-stopped ports: - 3000:80 # 前端访问端口 environment: - NODE_ENVproduction - REACT_APP_BACKEND_V1_GET_URLhttp://localhost:3001/api/v1 - REACT_APP_BACKEND_V1_POST_URLhttp://localhost:3001/api/v1 - REACT_APP_WS_SERVER_URLws://localhost:3002 - REACT_APP_FIREBASE_CONFIG{} # 禁用Firebase使用自托管后端 depends_on: - excalidraw-room networks: - excalidraw-net excalidraw-room: image: excalidraw/excalidraw-room:latest container_name: excalidraw-room restart: unless-stopped ports: - 3001:80 # HTTP API 端口 - 3002:80 # WebSocket 端口 (用于实时协作) environment: - NODE_ENVproduction - PORT80 - ALLOWED_ORIGINShttp://localhost:3000 # 允许的前端地址生产环境需替换为你的域名 volumes: - excalidraw-data:/app/data # 持久化存储绘图数据 networks: - excalidraw-net volumes: excalidraw-data: # 声明一个数据卷用于持久化房间和绘图数据 networks: excalidraw-net: # 创建一个独立的网络让两个容器可以互相通信关键配置解析端口映射前端映射到宿主机的3000端口。后端服务映射了两个端口3001用于 HTTP API创建房间、获取绘图数据3002用于 WebSocket实现实时协作。环境变量前端容器中REACT_APP_BACKEND_V1_*和REACT_APP_WS_SERVER_URL告诉前端去哪里找后端服务。这里配置的是通过 Docker 内部网络通信的地址localhost因为它们在同一个自定义网络excalidraw-net下。REACT_APP_FIREBASE_CONFIG{}至关重要它禁用了 Excalidraw 默认集成的 Firebase 后端强制其使用我们自托管的后端。后端容器中ALLOWED_ORIGINS设置了允许跨域请求的前端地址。在开发或内网测试时可以用http://localhost:3000。在生产环境你必须将其改为你实际访问前端的域名或IP地址例如http://draw.your-company.com否则浏览器会因为跨域策略CORS而阻止请求。数据持久化我们创建了一个名为excalidraw-data的 Docker 数据卷并挂载到后端容器的/app/data路径。这样所有创建的房间和绘图数据都会保存在这个卷里即使容器被删除重建数据也不会丢失。你可以通过docker volume inspect excalidraw-data查看卷的实际存储位置。保存文件后在同一个目录下运行docker-compose up -d-d参数同样表示后台运行。Docker Compose 会自动拉取镜像如果本地没有并启动两个容器。使用docker-compose ps查看服务状态两个服务都应为Up。现在访问http://你的服务器IP:3000你应该能看到完整的 Excalidraw 界面。尝试创建一个“房间”点击右上角的“合作”按钮如果一切正常你应该能获得一个可分享的链接并且刷新页面后绘图不会丢失——这说明后端服务正在正常工作。3.3 配置反向代理与HTTPS生产环境必备直接通过IP和端口号访问既不安全也不专业。在生产环境我们通常会用 Nginx 这样的反向代理服务器将服务映射到一个友好的域名下并配置 HTTPS 加密。假设你有一个域名draw.yourdomain.com已经解析到了你的服务器IP。首先安装 Nginx 和 Certbot用于申请免费的 Let‘s Encrypt SSL 证书# Ubuntu/Debian sudo apt update sudo apt install nginx certbot python3-certbot-nginx -y # CentOS/RHEL sudo yum install nginx certbot python3-certbot-nginx -y然后为 Excalidraw 创建一个 Nginx 配置文件sudo vim /etc/nginx/conf.d/excalidraw.conf写入以下配置。这个配置将把所有访问draw.yourdomain.com的流量代理到我们本地的3000端口前端同时将/api/v1和/socket.io的请求代理到后端对应的端口。server { listen 80; server_name draw.yourdomain.com; # 替换为你的域名 # 将HTTP请求重定向到HTTPS配置完证书后启用 # return 301 https://$server_name$request_uri; location / { proxy_pass http://localhost:3000; # 前端服务 proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; # 以下两行对Web应用很重要 proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection upgrade; } # 代理后端API请求 location /api/v1 { proxy_pass http://localhost:3001/api/v1; # 后端API服务 proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } # 代理WebSocket连接用于实时协作 location /socket.io { proxy_pass http://localhost:3002/socket.io; # 后端WebSocket服务 proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection upgrade; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } }保存并退出。检查 Nginx 配置语法是否正确sudo nginx -t如果显示syntax is ok就可以重新加载 Nginx 使配置生效sudo systemctl reload nginx现在你需要修改之前docker-compose.yml文件中的ALLOWED_ORIGINS环境变量以及前端容器中指向后端的地址。因为现在流量都经过 Nginx前端在浏览器中运行时它看到的“后端”地址应该是你的域名而不是localhost。更新docker-compose.yml中的后端服务配置environment: - NODE_ENVproduction - PORT80 - ALLOWED_ORIGINShttps://draw.yourdomain.com # 改为你的HTTPS域名同时更新前端服务的环境变量将localhost替换为你的域名environment: - NODE_ENVproduction - REACT_APP_BACKEND_V1_GET_URLhttps://draw.yourdomain.com/api/v1 - REACT_APP_BACKEND_V1_POST_URLhttps://draw.yourdomain.com/api/v1 - REACT_APP_WS_SERVER_URLwss://draw.yourdomain.com/socket.io # 注意是wss - REACT_APP_FIREBASE_CONFIG{}重要提示在修改了 Compose 文件后需要重启服务以使配置生效docker-compose down docker-compose up -d最后为你的域名申请并配置 SSL 证书。使用 Certbot 可以自动化完成sudo certbot --nginx -d draw.yourdomain.com按照提示操作Certbot 会自动修改你的 Nginx 配置文件添加 HTTPS 支持并设置自动续期。完成后你就可以通过https://draw.yourdomain.com安全地访问你的私有 Excalidraw 服务了。4. 部署后的关键配置、管理与优化服务跑起来只是第一步要让它在生产环境稳定、好用还需要一些额外的配置和日常管理操作。4.1 数据备份与恢复策略我们的绘图数据保存在 Docker 卷excalidraw-data中。定期备份这个卷是重中之重。你可以编写一个简单的备份脚本#!/bin/bash # backup_excalidraw.sh BACKUP_DIR/path/to/your/backup/folder VOLUME_NAMEexcalidraw-data # 根据 docker volume ls 确认卷名 TIMESTAMP$(date %Y%m%d_%H%M%S) BACKUP_FILE$BACKUP_DIR/excalidraw_backup_$TIMESTAMP.tar.gz # 停止相关容器确保数据一致性可选但推荐 docker-compose -f /opt/excalidraw/docker-compose.yml down # 创建备份 docker run --rm -v $VOLUME_NAME:/source -v $BACKUP_DIR:/backup alpine \ tar czf /backup/$(basename $BACKUP_FILE) -C /source . # 重启服务 docker-compose -f /opt/excalidraw/docker-compose.yml up -d echo Backup completed: $BACKUP_FILE # 可以在此添加删除旧备份的逻辑例如保留最近30天的备份将脚本设为可执行并添加到 crontab 中实现每日自动备份。恢复数据时操作类似但顺序相反# 1. 停止服务 docker-compose down # 2. 创建一个临时容器将备份文件解压到数据卷 docker run --rm -v excalidraw-data:/target -v /path/to/backup.tar.gz:/backup.tar.gz:ro alpine \ sh -c rm -rf /target/* tar xzf /backup.tar.gz -C /target # 3. 重启服务 docker-compose up -d4.2 用户认证与访问控制进阶默认部署的 Excalidraw 是完全开放的任何人拿到链接都可以编辑。对于内部敏感信息这显然不够。有几种方式可以增加访问控制网络层控制最简单的方式是只在内网环境部署通过防火墙规则限制外部IP访问服务器的 3000、3001、3002 端口或 Nginx 的 80/443 端口。Nginx 基础认证在 Nginx 配置中增加用户名密码验证。location / { auth_basic Restricted Area; auth_basic_user_file /etc/nginx/.htpasswd; # 使用htpasswd命令创建此文件 ... # 原有的proxy_pass等配置 }这种方式简单但所有用户共享一个密码且体验一般。集成第三方认证如OAuth这是更专业的方式。你可以在 Nginx 前再部署一个反向代理网关例如 oauth2-proxy 或 Authelia 将 Excalidraw 服务保护在后面要求用户使用公司的单点登录如 Google Workspace, GitHub Organization, 或自建的 OIDC 服务进行认证。这需要额外的配置但安全性和用户体验最好。4.3 性能监控与日志查看了解服务的运行状态很重要。查看容器日志# 查看所有服务的日志 docker-compose logs # 实时跟踪日志 docker-compose logs -f # 查看特定服务如后端的日志 docker-compose logs excalidraw-room日志可以帮助你排查启动失败、API错误或协作连接问题。监控资源使用使用docker stats命令可以实时查看容器的 CPU、内存使用情况。如果发现内存持续增长可能的内存泄漏或者协作时CPU占用过高可能需要考虑升级服务器配置或调整 Docker 容器的资源限制在docker-compose.yml中使用mem_limit,cpus等参数。4.4 版本更新与回滚Excalidraw 项目仍在活跃开发中定期更新可以获取新功能和 bug 修复。更新进入项目目录拉取最新的镜像并重启服务即可。cd /opt/excalidraw docker-compose pull # 拉取最新镜像 docker-compose down docker-compose up -d注意更新前务必确认你的数据卷备份是最新的。虽然官方镜像更新通常很平滑但以防万一。回滚如果新版本出现问题需要回滚到旧版本。你需要知道之前稳定运行的镜像标签Tag。假设之前用的是excalidraw/excalidraw:sha-abc123。首先修改docker-compose.yml文件将image: excalidraw/excalidraw:latest改为image: excalidraw/excalidraw:sha-abc123后端镜像同理。然后执行docker-compose down docker-compose up -d。更稳妥的做法是在每次更新前给当前的docker-compose.yml文件打一个标签备份例如docker-compose.yml.bak.20240501这样回滚时直接替换文件即可。5. 常见问题排查与踩坑实录即使按照步骤操作也可能会遇到一些问题。下面是我在多次部署中遇到的一些典型问题及其解决方案。5.1 容器启动失败端口冲突问题描述运行docker-compose up -d后使用docker-compose ps发现某个容器状态是Exit或不断重启。查看日志 (docker-compose logs service-name) 发现类似Error: listen EADDRINUSE: address already in use :::3000的错误。根因分析这意味着宿主机上的 3000、3001 或 3002 端口已经被其他进程占用。Docker 容器启动时无法绑定到这些端口。解决方案检查端口占用sudo netstat -tulpn | grep :3000。如果确认被占用有两种选择停止占用进程如果是不重要的服务可以停止它。修改映射端口在docker-compose.yml中修改ports配置例如将- 3000:80改为- 8080:80然后访问时就用http://服务器IP:8080。记得同时更新 Nginx 配置和前端环境变量中关于地址的配置。5.2 协作功能失效WebSocket连接错误问题描述可以打开白板但创建或加入房间时浏览器控制台F12报错提示 WebSocket 连接失败如WebSocket connection to ws://... failed或者无法实时看到其他人的操作。排查过程检查后端服务首先确认excalidraw-room容器是否正常运行 (docker-compose ps)。检查环境变量这是最常见的原因。确认前端容器中的REACT_APP_WS_SERVER_URL环境变量配置正确。如果使用了 Nginx 反向代理这个地址必须是可被浏览器访问的公开地址并且协议要匹配HTTP用ws://HTTPS用wss://。同时后端的ALLOWED_ORIGINS必须包含前端的访问地址。检查Nginx配置确保 Nginx 配置中正确代理了/socket.io路径并且包含了proxy_set_header Upgrade和Connection upgrade这两行关键指令这是 WebSocket 协议升级所必需的。检查防火墙/安全组如果服务部署在云服务器确保安全组规则放行了后端 WebSocket 服务映射的端口如3002以及 Nginx 的 80/443 端口。我的踩坑点我曾将REACT_APP_WS_SERVER_URL错误地配置为ws://localhost:3002但在生产环境前端代码是在用户的浏览器里运行的用户的浏览器根本无法直接访问我服务器上的localhost:3002。必须配置为服务器对外的域名或IP。5.3 绘图无法保存/房间消失数据卷权限问题问题描述创建房间并绘图后刷新页面或重新打开链接发现房间不存在或绘图内容丢失。排查过程检查后端日志docker-compose logs excalidraw-room看是否有关于文件读写的权限错误例如EACCES: permission denied, open /app/data/...。检查数据卷docker volume inspect excalidraw-data查看Mountpoint然后到宿主机对应目录检查文件是否存在以及文件属主和权限。解决方案这通常是因为后端容器以某个非root用户运行没有权限写入挂载的数据卷目录。在宿主机上手动修改数据卷挂载点的权限# 找到数据卷路径 VOLUME_PATH$(docker volume inspect excalidraw-data --format {{ .Mountpoint }}) # 将目录权限设置为777最简单粗暴但不够安全或改为合适的用户组 sudo chmod -R 777 $VOLUME_PATH # 或者更安全的方式是找出容器内运行进程的用户ID并让宿主机目录归属该用户 # 1. 进入容器查看用户ID docker exec excalidraw-room id # 假设输出 uid1000(node) gid1000(node) # 2. 在宿主机修改目录属主 sudo chown -R 1000:1000 $VOLUME_PATH修改权限后重启后端服务docker-compose restart excalidraw-room。5.4 前端加载缓慢或样式错乱静态资源问题问题描述访问页面很慢或者界面样式不正常浏览器控制台提示某些.js或.css文件加载失败 (404)。根因分析这可能是由于 Docker 镜像构建问题或者前端构建产物不完整。也可能是 Nginx 配置中对静态资源的缓存或 MIME 类型设置不正确。解决方案尝试清除浏览器缓存或使用无痕模式访问。检查 Nginx 配置确保对前端服务的proxy_pass指向正确并且没有错误的location规则拦截了静态资源请求。如果问题持续考虑重建前端镜像。进入项目目录执行docker-compose down docker rmi excalidraw/excalidraw:latest docker-compose up -d这会强制拉取最新的前端镜像并重建容器。6. 从部署到深度使用一些个人经验与建议把 Excalidraw 部署起来只是开始真正让它融入工作流才能发挥价值。这里分享几点我个人的使用心得。关于“房间”与“项目”管理本地化部署的后端提供了一个简单的基于房间ID的存储。但它没有文件夹或标签功能。我们的做法是建立一个内部的 Wiki 页面或文档用来记录重要的绘图链接并附上简单的标题和描述。例如一个技术架构图房间的链接我们会把它记录在对应的项目文档里。对于需要长期维护的图我们甚至会将最终的稳定版本导出为 PNG 或 SVG放入版本控制系统如 Git中而房间链接则用于日常的协作和修改讨论。性能与规模考量Excalidraw-room 后端默认使用内存存储并通过 Socket.IO 进行实时同步。在我们的使用中一个房间内同时有 5-10 人进行频繁绘制时体验依然流畅。但如果人数更多或者绘图元素极其复杂成千上万个图形可能会对服务器内存和网络带宽造成压力。目前还没有遇到瓶颈但这是需要留意的。对于超大规模的使用可能需要考虑对后端服务进行水平扩展但这已经超出了基础部署的范畴。备份策略的细化除了全量备份数据卷我们还定期每周将重要的、已完成的绘图手动导出为.excalidraw文件这是一种 JSON 格式并归档到团队网盘。数据卷备份是为了灾难恢复而手动导出归档则是为了重要的版本留痕和知识管理。探索自定义与集成本地化部署的最大优势之一是你可以修改代码。虽然 Excalidraw 的代码结构比较复杂但社区有一些有趣的修改案例比如增加自定义图形库、修改默认字体、集成内部图标系统等。如果你有前端开发能力可以 Fork 其源码在构建自己的 Docker 镜像前进行定制。另一种更轻量的集成方式是利用 Excalidraw 提供的 API 将其嵌入到其他内部系统中比如在项目管理工具里直接打开一个画板来讨论需求。最后我想说的是本地化部署这类工具技术上的难点往往不大真正的挑战在于让团队成员接受并习惯使用它。我们最初也经历了一个“推广期”主动在技术评审、需求讨论中使用这个白板并展示其便捷性比如实时协作修改架构图。当大家发现它确实比反复传文件、截图更新要高效时自然就用起来了。现在它已经成了我们团队远程协作的一个基础设施。希望这份详细的指南能帮你顺利搭建起属于自己的、安全可靠的数字白板。
返回列表