本站包含联盟链接——如果你通过链接注册,我们可能获得佣金。 Affiliate links.

DigitalOcean Droplet 上的 Docker Compose 指南 2026

DigitalOcean Droplet 上的 Docker Compose 指南 2026

Docker Compose 是在单个 DigitalOcean Droplet 上运行多容器应用的标准工具,无需依赖复杂的 Kubernetes 等编排系统。本指南将引导您完成整个过程:在 Droplet 上安装 Docker Engine、编写真实的 docker-compose.yml 文件、连接卷以实现持久数据存储、使用 Nginx 设置反向代理,以及解决生产环境中会遇到的常见问题。

安装 Docker + Docker Compose

运行 Ubuntu 24.04 LTS 的 DigitalOcean Droplet 是 Docker 的最佳选择,因为内核和软件包与最新的 Docker Engine 版本完全兼容。对于小型到中等规模的堆栈(Web 应用 + 数据库 + 缓存),Basic 2 GiB RAM / 2 vCPU Droplet(每月 $18)通常足够使用;单个测试容器可以使用 1 GiB RAM(每月 $6)(价格截至 2026 年 7 月 - 请在提供商网站上验证当前费率)。推荐的安装方法是 Docker 的便利脚本,它在单个命令中提取 Docker Engine、CLI 和 Compose 插件:curl -fsSL https://get.docker.com -o get-docker.sh && sudo sh get-docker.sh 安装完成后,将当前用户添加到 docker 组,以避免每次都输入 sudo:sudo usermod -aG docker $USER 然后退出并重新登录一次以激活权限。 一个重要细节是:当前版本的 Docker Compose 不再是一个名为 docker-compose(带连字符)的独立程序。相反,它是内置在 Docker CLI 中的插件,通过 docker compose(带空格)调用。如果您通过便利脚本或官方 apt 存储库安装,此插件会自动提供。通过 docker compose versiondocker --version 验证它是否正常工作。如果您使用不同的发行版(如 CentOS 或 Alpine),请根据其官方指南添加 Docker 自己的存储库,因为许多发行版的默认软件包集合提供的是更旧版本或 fork 版本,语法略有不同。 最后,启用 Docker 服务以便在 Droplet 重新启动时自动启动:sudo systemctl enable --now docker,并配置 DigitalOcean 的云防火墙(免费 - 无额外费用)仅允许必要的端口,如 22(SSH)、80/443(HTTP/HTTPS),然后再运行公开服务的容器。

  1. Ubuntu 24.04 LTS + Docker 便利脚本推荐用于快速安装,Compose 插件包含在单个命令中
  2. Basic 2 GiB RAM / 2 vCPU Droplet(每月 $18)适合典型的 Web+DB+缓存堆栈;1 GiB(每月 $6)足以用于单个测试容器
  3. 使用 usermod -aG docker $USER 将用户添加到 docker 组,然后重新登录以删除 sudo 要求

使用多个容器编写 docker-compose.yml

基于真实使用经验,当前 Compose 规范不再要求在文件顶部使用 version: 键,因为它现在将 2.x/3.x 语法合并为单一标准。主要文件结构包括列出单个容器的 services: 键、用于持久存储的 volumes: 以及超出 Compose 为每个项目创建的默认网络所需的附加内部网络的 networks:。以下是一个包含 Web 应用和 PostgreSQL 数据库的示例堆栈:services: web: build: . ports: - 3000:3000 depends_on: - db env_file: .env restart: unless-stopped db: image: postgres:16 volumes: - db_data:/var/lib/postgresql/data environment: POSTGRES_PASSWORD: ${DB_PASSWORD} restart: unless-stopped volumes: db_data: 请注意,web 服务使用 build: . 从当前目录中的 Dockerfile 构建镜像,而 db 服务使用 image: postgres:16 直接从注册表中拉取预构建的镜像,版本明确固定而不是依赖 latest。ports: 值映射主机:容器端口,environment: 设置容器在启动时读取的变量。如果您不想将密钥直接提交到 compose 文件中,请将它们分离到 .env 文件中,并使用 env_file: .env 引用它,或使用 ${DB_PASSWORD} 语法内联替换值。 一个常见的误解是 depends_on 只控制容器的启动顺序 - 它不会等待容器内部的应用程序实际准备好接受连接。例如,db 容器可能已完成启动,但 PostgreSQL 仍在初始化。要等待就绪状态,请向 db 服务添加 healthcheck:,并在 web 服务中使用 depends_on.condition: service_healthy。在文件末尾,volumes: db_data: 声明一个命名卷,以便 Docker 单独管理存储而不依赖容器生命周期。

  1. 不再需要在文件顶部包含 version: - 当前 Compose 规范将所有语法统一为一个标准
  2. build: . 从当前目录中的 Dockerfile 构建镜像;image: 从注册表中拉取现成镜像
  3. depends_on 仅控制启动顺序,不控制内部应用是否实际准备好接受请求 - 如果需要等待真正的就绪状态,请添加 healthcheck
  4. env_file: .env 将数据库密码等密钥保留在您提交到 git 的 compose 文件之外
  5. 在文件末尾的 volumes: 块中声明命名卷(如 db_data:),以便即使删除容器,数据也会持久化

运行、停止和更新容器

编写 docker-compose.yml 后,使用 docker compose up -d 启动堆栈,它构建(如适用)、从注册表拉取缺失的镜像,并在分离模式(后台)下运行所有服务。使用 docker compose ps 检查项目中所有容器的状态,并使用 docker compose logs -f web 从任何服务流式传输实时日志 - 当应用未响应或在启动后崩溃时,这对调试极其有用。 更新到较新的镜像版本时,安全的步骤是 docker compose pull 获取所有指定 image: 的服务的新镜像,然后 docker compose up -d --build 仅重新创建配置或镜像实际更改的容器 - 未更改的服务保持不变,从而最大限度地减少总体停机时间。如果只修改了服务的 Dockerfile 并想重新构建它,请使用 docker compose build --no-cache web && docker compose up -d web 强制完整重建而不使用旧缓存层。 停止有两种截然不同的结果:docker compose down 停止并移除容器和项目的网络,但保留所有命名卷 - 您的数据不会消失。docker compose down -v 也删除卷,仅当您真正想要擦除所有内容时(如测试环境)- 要极其小心,不要在生产数据库上使用此命令。对于诸如重新启动单个服务而不重新创建它的小型维护,请使用 docker compose restart web,要检查运行中的容器的内部,请使用 docker compose exec web sh。随着时间的推移,未使用的镜像、层和网络会积累并消耗 Droplet 的 SSD 空间,因此定期运行 docker system prune -f 清理它们。

  1. docker compose up -d 在后台(分离模式)运行所有服务
  2. docker compose ps 和 docker compose logs -f web 检查状态并流式传输实时日志
  3. docker compose pull && docker compose up -d --build 获取新镜像并仅重新创建更改的容器
  4. docker compose down 保留卷,与 docker compose down -v 不同,后者会删除卷 - 要小心数据丢失
  5. docker system prune -f 定期删除未使用的镜像和层,防止 Droplet 的 SSD 被填满

连接卷以实现持久数据存储

容器本质上是无状态的 - 当您删除容器时,其文件系统数据随之消失。必须持久化的数据库或上传的文件需要始终绑定到卷。Docker 提供两种主要类型:命名卷,如 db_data:/var/lib/postgresql/data,Docker 在 /var/lib/docker/volumes 中管理存储,以及绑定挂载,如 ./data:/var/lib/mysql,绑定到主机上的真实路径,当您想要直接从主机查看或编辑文件而无需进入容器时非常有用。 如果 Droplet 的主 SSD 开始不足(例如,2 GiB Droplet 有 60 GB SSD),DigitalOcean 提供可以单独附加的块存储卷,而无需调整整个 Droplet 的大小 - 每 GiB/月 $0.10,因此 100 GiB 每月花费 $10。步骤是:从控制面板或 doctl 创建卷,将其附加到 Droplet,格式化它,并通过 fstab 将其挂载到 /mnt/volume_sgp1_01 之类的路径,以便它在重启时自动挂载。然后在 compose 文件中将该路径用作绑定挂载的主机端:/mnt/volume_sgp1_01/pgdata:/var/lib/postgresql/data。这将数据与 Droplet 的主磁盘分离,使未来的备份、调整大小或迁移更容易,因为卷独立移动。 无论是使用命名卷还是块存储卷,都要始终维护单独的备份过程。永远不要依赖单个卷作为关键数据的唯一副本。最简单的方法是启动一个临时容器,将数据从卷 tar 出来并将其保留在离线状态:docker run --rm -v db_data:/data -v $(pwd):/backup busybox tar czf /backup/db_data.tar.gz /data 然后将备份文件移到其他地方,如 Spaces 对象存储。

要点总结: 命名卷(例如 db_data:)让 Docker 在 /var/lib/docker/volumes 中管理存储,仅在 docker 操作之间可移植

使用 Nginx 进行反向代理

这一点很重要——与其直接将每个应用的端口发布到互联网,更安全和可管理的方法是运行 Nginx 作为单个入口点(反向代理)并在 Compose 的内部网络中保留其他容器。最简单的方法是将 nginx:alpine 服务添加到 compose 文件并将您自己的配置挂载到默认配置上:nginx: image: nginx:alpine ports: - 80:80 volumes: - ./nginx.conf:/etc/nginx/conf.d/default.conf:ro depends_on: - web 在 nginx.conf 中,直接使用 proxy_pass http://web:3000;,因为同一 Compose 网络上的容器会自动将服务名称解析为 IP,无需知道实际地址。 这种方法的优点是服务(如 web 或 db)根本不需要向主机发布端口(从这些服务中删除 ports:),因为只有 nginx 将端口 80/443 暴露给互联网 - 这大大减少了攻击面。在 DigitalOcean 上,启用云防火墙(免费,无额外费用)仅允许 80/443 入站,并完全阻止所有其他容器端口的外部访问。 对于 HTTPS,常见的设置是将 Nginx 与 Certbot 配对,从 Let's Encrypt 发布免费证书并每 90 天自动续订。或者,使用预构建的镜像(如带有 acme-companion 的 nginx-proxy)自动处理基于每个容器的环境变量的证书发放和续订,全部在单个 compose 文件中,无需手动编写 nginx.conf。无论哪种方式,在重新加载之前始终使用 nginx -t 测试配置以捕获可能意外导致您的网站离线的语法错误。

何时使用此指南(真实用例)

单个 Droplet 上的 Docker Compose 最适合用于副项目、测试环境、内部工具或服务较少且开发团队较小的 MVP - 设置快速、控制直接明了,无需学习复杂的编排概念,如 pod、服务网格或入口控制器(Kubernetes)。成本保持低位,仅限于单个 Droplet 加上您添加的任何其他卷或负载均衡器;与某些平台不同,没有控制平面费用。 关键限制是 Docker Compose 在一台机器上运行。如果该 Droplet 崩溃或意外重启,compose 文件中的所有服务会一起停止 - 跨节点没有自动故障转移。这使其不适合需要真正高可用性或流量足够高以至于需要跨多个实例进行水平扩展的系统。一旦您的业务增长到这一点,考虑迁移到托管 Kubernetes(DOKS),其中控制平面是免费的,节点起价为每月 $12/个,或者如果您宁愿不管理基础设施,可以选择 App Platform。当您有需要在内部通信的多个 Droplet 时,使用 DigitalOcean 的 VPC(私有网络)免费,无需按连接付费。 对于新的 DigitalOcean 账户(从未在之前使用过试用版),您在注册后 60 天内获得有效的 $200 试用额度(根据当前条款,截至 2026 年 7 月,需要信用卡或 PayPal)。通过此链接注册以获得 $200:DigitalOcean $200 免费额度 - 足以创建测试 Droplet、部署本指南中的堆栈并进行几周的实验。

常见错误和解决方案

最频繁的错误是 Error: port is already allocated,原因是 ports: 中的主机端口已被 Droplet 上的另一个服务使用(例如,在容器外安装的 Nginx 使用端口 80)。使用 ss -tulpn 检查哪些端口在使用中,然后要么更改 compose 文件中的端口映射,要么先停止冲突的服务。 另一个常见的启动问题是 permission denied while trying to connect to the Docker daemon socket,通常是因为您的用户要么未被添加到 docker 组,要么 SSH 会话尚未收取新的组成员资格。退出并重新登录(或临时运行 newgrp docker)。稍后积累的问题是 no space left on device,由旧镜像、构建缓存和未使用的层堆积导致,直到 Droplet 的 SSD 被填满。用 docker system prune -a 移除所有未使用的内容来修复它,或者如果您的实际数据太大,并且您不断空间不足,应附加块存储卷添加永久空间,而不是反复删除。 如果容器进入重启循环,不要只是一直重启它 - 打开 docker compose logs -f service_name 首先找到实际的错误。.env 中的环境变量未被读取通常意味着 .env 文件不在与 docker-compose.yml 相同的目录中,或变量名称与 compose 期望的不匹配。罕见但困难的问题是容器无法到达互联网,即使网络看起来很好 - 有时 Docker 的 iptables 规则与主机上的 UFW 规则冲突。在这种情况下,添加规则到 Docker 的 DOCKER-USER 链,而不是仅依赖 UFW。

  1. 错误"port is already allocated":主机上的另一个服务已使用该端口;使用 ss -tulpn 检查并更改主机映射
  2. "permission denied ... docker.sock":用户不在 docker 组中或会话尚未刷新权限;退出并重新登录
  3. "no space left on device":旧镜像和层堆积;定期运行 docker system prune -a 或附加块存储卷以获得永久空间

最佳实践

始终明确固定镜像版本,如 postgres:16nginx:1.27-alpine 而不是 latest,因为 latest 在您下次拉取时可能会更新到具有破坏性更改的更新版本,且没有警告。每个持续运行的服务都应该有 restart: unless-stopped,这样 Docker 会在 Droplet 重启或容器意外崩溃时自动重启容器,并添加 healthcheck:,以便 Compose 不仅知道"容器正在运行"还要知道"容器内的应用实际上准备好处理请求"。 在安全方面,始终通过在 Dockerfile 中指定 USER 在容器内运行进程作为非 root 用户 - 不要让它们作为 root 运行(许多基础镜像的默认值),因为容器泄露变得更加严重。将 API 密钥和数据库密码等密钥保留在您永远不会提交到 git 的 .env 文件中。对于数据安全,使用定期快照(每 GiB/月 $0.06)备份 Droplet,并另外使用前面提到的 tar 方法单独备份实际数据,因为快照捕获整个 Droplet,并不是恢复单个数据库的理想方式。 为了获得可见性,启用 DigitalOcean 的免费监控(包含在每个 Droplet 中)来绘制 CPU、RAM 和磁盘使用情况的图表,以便您可以在容器消耗接近 OOM 的内存或磁盘即将满时在系统崩溃前发现。最后,定期清理:从 compose 文件中删除服务时使用 docker compose down --remove-orphans 以防止孤立容器在配置后挂起,并定期运行 docker system prune,以便您的 Droplet 不会积累垃圾镜像、网络和旧构建缓存,这些会困扰真实使用。

领取 $200 免费额度 →

常见问题(FAQ)

docker compose 和 docker-compose(带连字符)有什么区别?
docker-compose(带连字符)是已弃用的较旧独立 Python 程序,而 docker compose(带空格)是从 Compose V2 开始内置于 Docker CLI 中的插件,用 Go 编写,速度更快,持续更新。docker-compose.yml 文件的语法在两者之间几乎相同 - 只是命令发生了变化。如果您通过便利脚本或官方存储库安装,docker compose 会自动提供。
运行 Docker Compose 需要多大的 Droplet?
这取决于您的服务数量和权重。对于单个测试容器或小型应用,Basic 1 GiB RAM / 1 vCPU(每月 $6)效果不错。包含 Web 应用、数据库和缓存的典型堆栈受益于 2 GiB RAM / 2 vCPU(每月 $18)或更高。对于许多一起运行的重型服务,从 4 GiB(每月 $24)开始(价格截至 2026 年 7 月 - 请在提供商网站上验证当前费率)。
我如何防止数据库数据在删除容器时消失?
始终将数据库存储文件的路径(例如 /var/lib/postgresql/data)绑定到命名卷或绑定挂载,永远不要将其保留在容器的裸文件系统中。此外,维护单独的备份过程将副本存储在卷外,如 tar 到 Spaces 对象存储,因为单个卷仍然是单点故障,如果 Droplet 出现灾难性问题。
我何时应该从 Docker Compose 切换到 Kubernetes(DOKS)?
单个 Droplet 上的 Docker Compose 适合尚不需要跨多台机器的高可用性或自动扩展的系统。一旦流量增长足以需要同时跨多个节点扩展,或者如果您需要即使在一个节点故障时系统仍保持运行,请考虑托管 Kubernetes(DOKS),其中控制平面是免费的,节点起价为每月 $12/个。如果您的系统还不需要,就不必从 Kubernetes 开始。
我如何在无额外成本的情况下为容器设置 HTTPS?
运行 Nginx 作为反向代理,与 Certbot 配对发布来自 Let's Encrypt 的免费 TLS 证书并每 90 天自动续订,或使用现成的镜像(如带有 acme-companion 的 nginx-proxy)自动化 compose 文件中的证书发放和续订。两种方法都没有 DigitalOcean 的额外成本,因为云防火墙允许端口 80/443 已经是免费的。
如果我的 Droplet 因为旧 Docker 镜像而空间不足怎么办?
定期运行 docker system prune -a 以删除所有未使用的镜像、容器、网络和构建缓存。如果您的实际数据(数据库、上传)占用空间而不是旧镜像,考虑附加块存储卷(每 GiB/月 $0.10),而不是反复删除腾出空间,因为它更灵活且在 Droplet 调整大小时保留。