导读:本期聚焦于梧桐创作的《如何使用Docker容器化部署Excalidraw实现团队协作绘图?》,敬请观看详情。团队协作绘图工具的需求日益增长,Excalidraw作为一款开源的手绘风格白板应用,凭借其简洁的界面和实时协作功能受到广泛关注。然而直接通过源码运行存在环境依赖复杂、端口冲突、升级维护困难等问题。通过Docker容器化部署可以有效解决这些痛点,将应用及其依赖打包到独立容器中运行,实现一次构建到处运行的效果。本文将详细介绍如何编写Dockerfile构建Excalidraw镜像,配置docker-compose.yml实现快速部署,以及如何处理数据持久化、反向代理配置和HTTPS证书等实际生产环境中需要面对的问题。同时还会探讨资源限制、日志收集和自动重启策略等运维细节,帮助开发者快速搭建一个稳定可靠的团队绘图协作平台。

Excalidraw是一款备受欢迎的开源手绘风格白板工具,支持实时协作和多种图形导出格式。对于团队来说,将其部署到自有服务器上既能保护数据隐私,又能灵活控制访问权限。容器化部署是目前最推荐的方案,它将Excalidraw及其运行环境封装在Docker容器中,避免了宿主机环境污染和版本冲突问题。

如何使用Docker容器化部署Excalidraw实现团队协作绘图?

部署前的环境准备与依赖分析

在开始容器化之前,我们需要充分了解Excalidraw的技术栈和运行方式。Excalidraw本质上是一个基于React构建的前端单页应用,源码托管在GitHub上。它使用Node.js作为构建环境,通过yarn管理依赖包,最终编译生成纯静态资源文件。在生产环境中运行时,只需要一个Web服务器(如Nginx)来托管这些静态文件即可,并不需要Node.js运行时。

对于实时协作功能,Excalidraw依赖WebSocket进行通信。如果只需要单用户使用,部署前端静态文件就够了。但如果需要多人实时协作,则需要额外部署一个协作后端服务。官方提供了基于Socket.IO的协作服务器,可以与Redis配合实现会话管理。在容器化时,我们可以将这些组件分别打包为独立容器,通过Docker网络互联。

服务器环境方面,建议使用Docker Engine 20.10及以上版本,同时安装Docker Compose V2用于多容器编排。操作系统可以选择Ubuntu 22.04 LTS或CentOS Stream等主流Linux发行版。服务器配置方面,2核CPU、2GB内存、20GB磁盘空间足以支撑中小型团队(50人以下)的日常使用。如果需要公网访问,还需确保防火墙开放80和443端口,并准备好域名和SSL证书。

编写Dockerfile构建高效镜像

构建Excalidraw镜像的核心思路是采用多阶段构建(Multi-stage Build)。第一阶段使用Node.js官方镜像编译源码生成静态资源,第二阶段使用Nginx Alpine镜像托管这些静态文件。这种方式可以显著减小最终镜像体积,避免将Node.js运行时、npm缓存和源码文件带入生产环境,最终镜像通常只有几十MB。

下面是完整的Dockerfile示例:

# 第一阶段:构建阶段
FROM node:18-alpine AS builder

WORKDIR /app

# 安装git用于克隆源码
RUN apk add --no-cache git

# 克隆Excalidraw官方仓库
RUN git clone https://github.com/excalidraw/excalidraw.git .

# 安装项目依赖
RUN yarn install --frozen-lockfile

# 执行生产环境构建
RUN yarn build

# 第二阶段:运行阶段
FROM nginx:alpine

# 设置时区
RUN apk add --no-cache tzdata
ENV TZ=Asia/Shanghai

# 复制构建产物到Nginx默认目录
COPY --from=builder /app/build /usr/share/nginx/html

# 复制自定义Nginx配置
COPY nginx.conf /etc/nginx/conf.d/default.conf

EXPOSE 80

# 健康检查
HEALTHCHECK --interval=30s --timeout=3s \
  CMD wget --no-verbose --tries=1 --spider http://localhost/ || exit 1

CMD ["nginx", "-g", "daemon off;"]

接下来需要准备Nginx配置文件,用于正确处理前端路由和WebSocket代理。Excalidraw使用客户端路由,所有路径都需要回退到index.html,否则刷新页面会出现404错误:

server {
    listen 80;
    server_name localhost;

    root /usr/share/nginx/html;
    index index.html;

    # 开启gzip压缩
    gzip on;
    gzip_types text/css application/javascript application/json image/svg+xml;
    gzip_min_length 1024;

    # 前端路由处理
    location / {
        try_files $uri $uri/ /index.html;
    }

    # 静态资源缓存
    location ~* \.(js|css|png|jpg|svg|woff2)$ {
        expires 30d;
        add_header Cache-Control "public, immutable";
    }

    # WebSocket代理(协作功能)
    location /socket {
        proxy_pass http://collab:3000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_read_timeout 86400;
    }
}

多阶段构建的优势非常明显。第一阶段镜像可能超过1GB,包含完整的Node.js运行时和所有devDependencies,但这个镜像只是中间产物,不会出现在最终结果中。第二阶段只包含Nginx和静态文件,体积通常在50MB左右。构建过程中需要注意yarn.lock文件的存在性,确保依赖版本一致性。如果在国内网络环境下构建,建议在yarn install之前添加yarn config set registry https://registry.npmmirror.com来加速依赖安装。

使用Docker Compose实现一键编排部署

单容器运行虽然简单,但难以管理多组件协作和数据持久化。使用Docker Compose可以将Excalidraw前端、协作后端、Redis等组件统一编排,实现一键启动、停止和更新。下面是一个适用于生产环境的完整编排方案:

version: "3.8"

services:
  excalidraw:
    build:
      context: .
      dockerfile: Dockerfile
    container_name: excalidraw-web
    ports:
      - "8080:80"
    restart: unless-stopped
    depends_on:
      - collab
      - redis
    environment:
      - REACT_APP_BACKEND_V2_API_URL=http://draw.ipipp.com
      - REACT_APP_SOCKET_SERVER_URL=ws://draw.ipipp.com
    deploy:
      resources:
        limits:
          cpus: "1.0"
          memory: 256M
    networks:
      - excalidraw-net

  collab:
    image: excalidraw/excalidraw-room:latest
    container_name: excalidraw-collab
    restart: unless-stopped
    environment:
      - REDIS_URL=redis://redis:6379
      - PORT=3000
    depends_on:
      - redis
    deploy:
      resources:
        limits:
          cpus: "0.5"
          memory: 128M
    networks:
      - excalidraw-net

  redis:
    image: redis:7-alpine
    container_name: excalidraw-redis
    command: redis-server --appendonly yes --maxmemory 256mb --maxmemory-policy allkeys-lru
    volumes:
      - redis-data:/data
    restart: unless-stopped
    deploy:
      resources:
        limits:
          cpus: "0.5"
          memory: 256M
    networks:
      - excalidraw-net

volumes:
  redis-data:

networks:
  excalidraw-net:
    driver: bridge

上述Compose文件定义了三个服务。excalidraw是前端Web服务,监听宿主机8080端口;collab是协作后端,基于Socket.IO实现实时通信;redis用于存储协作会话数据和房间状态。三个容器通过excalidraw-net桥接网络互联,前端容器通过Nginx配置中的proxy_pass http://collab:3000将WebSocket请求转发到协作后端。

在资源限制方面,通过deploy.resources.limits为每个容器设置了CPU和内存上限。前端容器限制1核CPU和256MB内存,协作后端和Redis各限制0.5核CPU和128-256MB内存。这些限制防止单个容器在异常情况下占用过多宿主机资源。Redis配置了appendonly持久化和LRU淘汰策略,在内存不足时自动清理旧数据,保证服务稳定性。

启动服务只需执行docker compose up -d命令。首次启动会自动构建Excalidraw镜像并拉取协作后端和Redis镜像。查看日志可以使用docker compose logs -f,停止服务使用docker compose down。如果只更新Excalidraw版本,执行docker compose build excalidraw && docker compose up -d excalidraw即可滚动更新前端,不影响协作后端和Redis的运行。

反向代理配置与HTTPS支持

公网部署Excalidraw必须配置HTTPS。浏览器的Service Worker、IndexedDB和WebSocket等API要求安全上下文(Secure Context),HTTP环境下部分功能会受限甚至不可用。使用Nginx作为反向代理是最常见的方案,它同时负责SSL终止和请求转发。

# HTTP跳转HTTPS
server {
    listen 80;
    server_name draw.ipipp.com;
    return 301 https://$host$request_uri;
}

# HTTPS主配置
server {
    listen 443 ssl http2;
    server_name draw.ipipp.com;

    ssl_certificate /etc/letsencrypt/live/draw.ipipp.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/draw.ipipp.com/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;

    # 安全相关响应头
    add_header X-Frame-Options SAMEORIGIN;
    add_header X-Content-Type-Options nosniff;
    add_header Strict-Transport-Security "max-age=31536000" always;

    client_max_body_size 50m;

    # 前端代理
    location / {
        proxy_pass http://excalidraw-web:80;
        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 {
        proxy_pass http://excalidraw-collab:3000;
        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_read_timeout 86400;
        proxy_send_timeout 86400;
    }
}

配置反向代理时最容易踩坑的就是WebSocket代理。必须添加UpgradeConnection两个请求头,将HTTP连接升级为WebSocket连接,否则实时协作功能完全无法工作。proxy_read_timeout设置为86400秒(24小时)是为了避免长时间空闲的WebSocket连接被Nginx主动断开,默认60秒超时会导致用户短暂离开后协作断线。

SSL证书管理推荐使用Certbot自动申请和续期Let's Encrypt免费证书。可以将Certbot也容器化,通过Docker Compose与Nginx配合实现自动续期。另一种更简洁的方案是使用Caddy作为反向代理,它内置了自动HTTPS功能,无需手动申请证书,配置文件只需几行即可完成所有工作。无论选择哪种方案,都要定期检查证书有效期,避免证书过期导致服务不可用。

数据持久化与日常运维管理

容器化部署最大的风险之一是数据丢失。Excalidraw本身是前端应用,用户绘制的图表数据默认存储在浏览器IndexedDB中,这部分数据不依赖服务器。但如果启用了服务端存储或协作功能,则需要确保Redis中的会话数据持久化。在Compose文件中已经使用命名卷redis-data挂载到Redis容器的/data目录,即使容器删除重建,数据依然保留。

定期备份是保障数据安全的重要手段。可以编写一个简单的备份脚本,通过cron定时执行:

#!/bin/bash
# Excalidraw Redis数据备份脚本

BACKUP_DIR="/data/backups/excalidraw"
DATE=$(date +%Y%m%d_%H%M%S)
mkdir -p $BACKUP_DIR

# 触发Redis后台持久化
docker exec excalidraw-redis redis-cli BGSAVE

# 等待持久化完成
sleep 5

# 复制RDB文件到备份目录
docker cp excalidraw-redis:/data/dump.rdb $BACKUP_DIR/redis_$DATE.rdb

# 压缩备份文件
gzip $BACKUP_DIR/redis_$DATE.rdb

# 清理30天前的旧备份
find $BACKUP_DIR -name "redis_*.rdb.gz" -mtime +30 -delete

echo "备份完成: redis_$DATE.rdb.gz"

日常运维还需要关注容器健康状态。Dockerfile中已经添加了HEALTHCHECK指令,Docker会每30秒检查一次Nginx是否正常响应。配合restart: unless-stopped策略,当容器异常退出时会自动重启。建议同时配置监控告警系统,使用Prometheus采集容器CPU、内存、网络等指标,Grafana展示监控面板,当服务异常或资源使用率过高时及时通知运维人员。

镜像升级是日常运维的另一个重要环节。更新Excalidraw版本时,只需拉取最新源码重新构建镜像,然后执行docker compose up -d即可滚动更新。由于用户数据存储在浏览器端和Redis卷中,升级过程不会丢失任何数据。建议在非高峰时段执行升级操作,并提前在测试环境验证新版本的兼容性。升级后检查Nginx日志和容器日志,确认服务正常运行,WebSocket连接正常建立,协作功能测试通过后再开放给团队使用。

日志管理方面,Docker默认使用json-file日志驱动,长期运行会导致日志文件不断增长。可以在/etc/docker/daemon.json中配置日志轮转策略,限制单个日志文件大小和保留数量。也可以将日志驱动改为fluentd或loki,将容器日志统一收集到集中式日志平台,方便检索和分析。对于Excalidraw这种以静态文件为主的应用,日志量通常不大,但协作后端的Socket.IO日志可能会在高峰期产生较多输出,需要重点关注。

DockerExcalidraw容器化部署修改时间:2026-08-26 02:25:16

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。