ARTICLE DETAIL

资讯详情

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

Docker部署CodiMD:从容器化原理到生产环境实战

Docker部署CodiMD:从容器化原理到生产环境实战 1. 项目概述为什么选择 Docker 部署 CodiMD如果你正在寻找一个能快速搭建、易于维护且功能强大的团队协作笔记平台CodiMD 绝对是一个绕不开的名字。它脱胎于著名的 HackMD提供了实时协作、Markdown 支持、导出分享等核心功能非常适合技术团队用来写会议记录、共享代码片段或者作为个人知识库。但直接在一台服务器上部署 CodiMD意味着你要手动处理 Node.js 环境、数据库配置、反向代理等一系列繁琐的依赖任何一个环节出错都可能让服务跑不起来后续升级更是头疼。这时候Docker 的优势就体现得淋漓尽致。它把 CodiMD 及其所有依赖比如数据库、缓存打包成一个或多个独立的“集装箱”容器。你不再需要关心服务器上具体装了什么版本的 Node.js也不用担心不同软件之间的依赖冲突。部署过程简化为几条命令升级就是拉取新镜像、重启容器的事极大地降低了运维复杂度。对于个人开发者、小团队或是想快速验证产品原型的场景用 Docker 部署 CodiMD 几乎是最高效、最可靠的选择。接下来我就以一个实际在 Ubuntu 服务器上部署的完整流程为例带你走一遍从零到一的搭建过程并分享一些我踩过坑才总结出来的配置技巧和运维心得。2. 部署前的核心准备与环境检查在动手敲下第一条 Docker 命令之前充分的准备工作能避免你掉进很多“坑”。这不仅仅是安装 Docker 那么简单你需要从服务器环境、网络规划到持久化策略都有一个清晰的蓝图。2.1 服务器环境与 Docker 安装首先确保你有一台拥有公网 IP或至少在内网可访问的 Linux 服务器。我推荐使用 Ubuntu 20.04 LTS 或 22.04 LTS它们对 Docker 的支持非常成熟。通过 SSH 连接到你的服务器我们开始第一步。更新系统包列表并安装一些基础工具是良好的习惯sudo apt update sudo apt upgrade -y sudo apt install -y curl wget vim git接下来安装 Docker。官方提供了便捷的安装脚本但为了可控性我更喜欢使用仓库安装。依次执行以下命令# 1. 卸载可能存在的旧版本 sudo apt remove docker docker-engine docker.io containerd runc # 2. 安装依赖包允许 apt 通过 HTTPS 使用仓库 sudo apt install -y ca-certificates curl gnupg lsb-release # 3. 添加 Docker 官方 GPG 密钥 sudo mkdir -p /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gosu tee /etc/apt/keyrings/docker.asc /dev/null # 4. 设置稳定版仓库 echo \ deb [arch$(dpkg --print-architecture) signed-by/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \ $(lsb_release -cs) stable | sudo tee /etc/apt/sources.list.d/docker.list /dev/null # 5. 安装 Docker Engine sudo apt update sudo apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin安装完成后验证 Docker 是否正常运行sudo systemctl status docker sudo docker run hello-world如果看到 “Hello from Docker!” 的信息说明 Docker 引擎已经成功安装并运行。注意很多新手会忽略用户组权限。默认情况下运行docker命令需要sudo权限。为了避免每次都要输入sudo可以将当前用户加入docker组sudo usermod -aG docker $USER。操作完成后你必须完全退出当前 SSH 会话并重新登录这个改动才会生效。这是一个非常常见但容易被遗忘的步骤。2.2 关键决策使用 Docker Compose 还是纯 Docker 命令这是部署架构上的一个关键选择。Docker 本身可以运行单个容器但 CodiMD 通常需要数据库如 PostgreSQL 或 MySQL配合工作。你有两种主流方案纯 Docker 命令分别启动 CodiMD 容器和数据库容器并通过--link或自定义网络手动连接它们。这种方式更底层适合学习 Docker 网络原理但在管理多容器应用时命令会变得冗长复杂。Docker Compose通过一个docker-compose.yml文件以声明式的方式定义 CodiMD 服务、数据库服务、网络和卷。一条docker compose up -d命令就能启动整个应用栈。这是目前管理多容器应用的事实标准强烈推荐。为了可维护性和清晰度本项目我们将采用Docker Compose。确保你已经安装了docker-compose-plugin在上述安装步骤中已包含。你可以通过docker compose version来验证。2.3 规划数据持久化与网络容器本身是无状态的当容器被删除其内部产生的所有数据如用户上传的图片、数据库里的笔记都会丢失。因此我们必须将重要数据“映射”到宿主机你的服务器的磁盘上这个过程称为“挂载卷”Volume。你需要规划两个核心数据的持久化数据库数据这是最重要的包含了所有用户、笔记、权限信息。我们将把数据库容器内的数据目录挂载到服务器的一个路径下例如./postgres-data。CodiMD 上传的文件用户通过 CodiMD 上传的图片、附件等。我们将挂载./uploads目录。同时我们需要创建一个独立的 Docker 网络让 CodiMD 容器和数据库容器能够相互通信同时与外部隔离这比使用默认的bridge网络更安全、更清晰。3. 编写 Docker Compose 配置文件详解一切准备就绪现在我们来创建核心的docker-compose.yml文件。这个文件定义了整个应用栈的蓝图。我建议在服务器上创建一个专属目录例如~/codimd然后在此目录下工作。mkdir ~/codimd cd ~/codimd vim docker-compose.yml下面是一个功能完整、经过生产环境简化的配置示例。我会逐段解释每个关键配置项的作用和背后的考量。version: 3.8 services: database: image: postgres:15-alpine container_name: codimd-postgres restart: unless-stopped environment: POSTGRES_USER: codimd POSTGRES_PASSWORD: your_strong_password_here # 务必修改 POSTGRES_DB: codimd volumes: - ./postgres-data:/var/lib/postgresql/data networks: - codimd-network healthcheck: test: [CMD-SHELL, pg_isready -U codimd] interval: 10s timeout: 5s retries: 5 codimd: image: ghcr.io/hackmdio/codimd:latest container_name: codimd-app restart: unless-stopped depends_on: database: condition: service_healthy environment: - CMD_DB_URLpostgres://codimd:your_strong_password_heredatabase:5432/codimd - CMD_DB_DIALECTpostgres - CMD_URL_ADDPORTfalse - CMD_PROTOCOL_USESSLtrue - CMD_HOSTNAMEnotes.yourdomain.com # 修改为你的域名或IP - CMD_PORT3000 - CMD_ALLOW_ANONYMOUSfalse - CMD_ALLOW_FREEURLtrue - CMD_IMAGE_UPLOAD_TYPEfilesystem volumes: - ./uploads:/home/hackmd/app/public/uploads ports: - 3000:3000 networks: - codimd-network networks: codimd-network: driver: bridge volumes: postgres-data: driver: local uploads: driver: local3.1 服务定义解析数据库与 CodiMD数据库服务 (database):image: postgres:15-alpine: 我们选择了 PostgreSQL 15 的 Alpine Linux 版本。Alpine 镜像体积极小安全性相对更高是容器化数据库的常见选择。environment: 这里设置了数据库的初始用户、密码和数据库名。POSTGRES_PASSWORD是重中之重必须修改为一个强密码切勿使用示例中的密码。volumes:./postgres-data:/var/lib/postgresql/data将容器内的数据目录映射到宿主机的./postgres-data目录。即使容器销毁数据也安然无恙。healthcheck: 这是一个非常实用的配置。它定期检查数据库是否已准备好接受连接。CodiMD 服务通过depends_on的condition: service_healthy来等待数据库健康后才启动避免了因数据库未就绪而导致的启动失败。CodiMD 服务 (codimd):image: ghcr.io/hackmdio/codimd:latest: 使用官方维护的最新镜像。注意HackMD 团队已将镜像仓库迁移至 GitHub Container Registry (ghcr.io)。depends_on: 确保 CodiMD 在数据库健康之后才启动。environment: 这是配置 CodiMD 的核心部分通过环境变量传递。CMD_DB_URL: 数据库连接字符串格式为postgres://用户名:密码数据库服务名:端口/数据库名。注意这里的database就是我们在 Compose 文件中定义的数据库服务名Docker 的网络 DNS 会自动解析。CMD_PROTOCOL_USESSLtrue和CMD_URL_ADDPORTfalse: 当你在前端使用 Nginx 等反向代理处理 HTTPS 时需要这样设置告诉 CodiMD 生成正确的 URL。CMD_HOSTNAME: 必须设置为你的最终访问地址域名或IP这是 CodiMD 生成分享链接的基础。CMD_ALLOW_ANONYMOUSfalse: 我建议关闭匿名编辑强制用户登录便于管理和追溯。CMD_IMAGE_UPLOAD_TYPEfilesystem: 将图片上传到本地文件系统即我们挂载的./uploads卷。ports:3000:3000将容器内的 3000 端口映射到宿主机的 3000 端口。这只是临时测试用在生产环境我们不会直接暴露这个端口。3.2 网络与卷的配置逻辑networks: 我们定义了一个名为codimd-network的桥接网络。两个服务都加入此网络它们可以通过服务名database,codimd直接通信与宿主机和其他容器隔离。volumes: 在文件顶部声明了postgres-data和uploads这两个“命名卷”。在服务定义中我们使用了相对路径./postgres-data这属于“绑定挂载”bind mount。使用命名卷还是绑定挂载取决于习惯命名卷由 Docker 管理备份和迁移可能稍复杂绑定挂载直接对应主机目录直观且易于直接操作文件。这里使用绑定挂载是为了演示的清晰性。4. 启动服务与基础配置实战配置文件写好之后部署就变得异常简单。4.1 一键启动与状态监控在docker-compose.yml所在目录执行以下命令# 在后台启动所有服务 docker compose up -d # 查看所有服务的实时日志 docker compose logs -f # 查看各容器的运行状态 docker compose ps如果一切顺利docker compose ps会显示两个服务的状态都是Up。此时你已经可以通过服务器的 IP 地址和 3000 端口如http://your-server-ip:3000访问 CodiMD 的初始页面了。首次访问你需要注册第一个管理员账户。第一个注册的账户会自动成为管理员。登录后点击右上角头像进入“管理”面板你可以进行更细致的设置如用户管理、系统配置等。4.2 生产环境关键配置使用 Nginx 反向代理与 SSL直接暴露 3000 端口是非常不安全的也缺乏 HTTPS 加密。在生产环境中我们必须使用 Nginx或 Caddy、Traefik作为反向代理并配置 SSL 证书。1. 安装 Nginxsudo apt install -y nginx2. 配置 Nginx 站点创建一个新的配置文件例如/etc/nginx/sites-available/codimdserver { listen 80; server_name notes.yourdomain.com; # 你的域名 # 将 HTTP 请求重定向到 HTTPS return 301 https://$server_name$request_uri; } server { listen 443 ssl http2; server_name notes.yourdomain.com; # SSL 证书路径通过 Certbot 获取 ssl_certificate /etc/letsencrypt/live/notes.yourdomain.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/notes.yourdomain.com/privkey.pem; # SSL 强化配置可使用 Mozilla SSL 配置生成器生成 ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:...; ssl_prefer_server_ciphers off; # 反向代理到 CodiMD 容器 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; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection upgrade; # 支持 WebSocket用于实时协作 client_max_body_size 20M; # 调整上传文件大小限制 } # 静态文件缓存优化 location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ { proxy_pass http://localhost:3000; expires 30d; add_header Cache-Control public, immutable; } }启用该配置并测试sudo ln -s /etc/nginx/sites-available/codimd /etc/nginx/sites-enabled/ sudo nginx -t # 测试配置语法 sudo systemctl reload nginx3. 获取 SSL 证书以 Certbot 为例sudo apt install -y certbot python3-certbot-nginx sudo certbot --nginx -d notes.yourdomain.com按照 Certbot 的交互提示操作它会自动修改你的 Nginx 配置并启用 HTTPS。4. 修改 CodiMD 配置完成以上步骤后必须回头修改docker-compose.yml中 CodiMD 的环境变量并重启服务environment: - CMD_HOSTNAMEnotes.yourdomain.com # 确保与你的域名一致 - CMD_PROTOCOL_USESSLtrue - CMD_URL_ADDPORTfalse然后运行docker compose up -d重启服务。实操心得proxy_set_header的配置至关重要尤其是X-Forwarded-Proto它告诉 CodiMD 用户是通过 HTTPS 访问的否则 CodiMD 生成的链接可能仍是http://导致前端资源加载或重定向出错。另外WebSocket 的配置 (Upgrade,Connection) 保证了实时协作功能的正常使用。5. 日常运维、问题排查与数据备份部署完成只是开始稳定的运行离不开日常维护。5.1 常用运维命令速查将这些命令保存下来你会经常用到# 查看服务状态 docker compose ps # 查看实时日志CtrlC 退出 docker compose logs -f [service_name] # 如 codimd # 重启服务 docker compose restart [service_name] # 停止并移除所有容器、网络但保留卷和数据 docker compose down # 停止并移除所有容器、网络、以及 volumes 中定义的卷危险数据会丢失 docker compose down -v # 拉取最新镜像并重新部署 docker compose pull docker compose up -d # 进入容器内部执行命令用于调试 docker exec -it codimd-app /bin/bash5.2 常见问题与排查实录即使按照步骤操作也可能会遇到问题。这里记录几个我遇到过的典型情况问题1访问 CodiMD 页面显示 “Internal Server Error” 或数据库连接错误。排查首先查看 CodiMD 容器的日志docker compose logs codimd。最常见的错误是数据库连接失败。解决检查docker-compose.yml中的CMD_DB_URL环境变量密码和数据库名是否与database服务中设置的一致。检查数据库容器是否健康运行docker compose logs database。查看是否有初始化错误。确保网络连通在 CodiMD 容器内尝试连接数据库docker exec -it codimd-app ping database。问题2上传图片失败提示文件系统错误。排查检查./uploads目录的权限。CodiMD 容器内的应用通常以非 root 用户如hackmd运行需要对该挂载目录有写权限。解决在宿主机上确保uploads目录存在且权限正确mkdir -p uploads sudo chown -R 1000:1000 uploads # 1000 是常见非root用户的UID具体需查看镜像定义更稳妥的方法是在docker-compose.yml的codimd服务中指定用户user: 1000:1000 # 与宿主机上一个有权限的用户/组匹配问题3通过域名访问页面样式错乱或 API 调用失败。排查这几乎都是反向代理配置问题。检查 Nginx 配置中的proxy_set_header Host $host;和proxy_set_header X-Forwarded-Proto $scheme;是否已设置。并确认 CodiMD 的CMD_HOSTNAME和CMD_PROTOCOL_USESSL环境变量配置正确。解决使用浏览器开发者工具的“网络”Network选项卡查看加载失败的资源 URL 是什么。如果 URL 还是http或包含了错误的端口就说明反向代理或 CodiMD 的环境变量配置有误。5.3 数据备份与恢复策略你的笔记数据都在./postgres-data目录里。备份就是备份这个目录。简单备份方案# 进入项目目录 cd ~/codimd # 1. 停止服务确保数据一致性对于小型服务短暂停机是可接受的 docker compose down # 2. 打包备份数据目录 tar -czpf codimd-backup-$(date %Y%m%d).tar.gz postgres-data/ uploads/ # 3. 重新启动服务 docker compose up -d # 4. 将备份文件传输到安全的异地存储如另一台服务器、云存储 scp codimd-backup-*.tar.gz userbackup-server:/path/to/backup/恢复数据# 1. 停止服务 docker compose down # 2. 删除当前数据目录请务必先确认备份文件是好的 rm -rf postgres-data uploads # 3. 解压备份文件 tar -xzpf codimd-backup-20231027.tar.gz # 4. 启动服务 docker compose up -d重要提醒这个备份方法在备份期间服务会中断。对于要求 7x24 小时可用的服务需要考虑使用数据库的在线备份工具如pg_dump结合文件同步的方式进行热备份。但对于个人或小团队使用上述定时脚本可放入 crontab配合短暂停机的方案已经足够可靠。我个人的习惯是每周日凌晨执行一次自动备份并保留最近一个月的备份文件。
返回列表