FAQ-18:在 Ubuntu 上安装 Docker Compose

预计阅读时间:14 分钟

📖 目录

问题速查表

问题解决章节
随 Docker Engine 一起安装 Compose 插件(推荐)前提条件:安装 Docker Engine
旧版独立二进制安装方式方式二:独立安装 Docker Compose(旧版方式)
docker: command not found错误一:docker: command not found
permission denied while trying to connect to the Docker daemon socket错误二:Got permission denied while trying to connect to the Docker daemon socket
Cannot connect to the Docker daemon错误三:Cannot connect to the Docker daemon at unix:///var/run/docker.sock
docker compose up 报端口占用错误四:docker compose up 报端口占用

Docker Compose 用于定义和运行多容器 Docker 应用。通过一个 YAML 文件,你可以声明式地配置所有服务、网络和存储卷,然后用一条命令启动整个应用栈。无论是搭建本地开发环境、运行数据库+缓存+Web 服务的组合,还是部署微服务架构,Docker Compose 都是不可或缺的工具。本文涵盖完整的安装流程、版本管理、常见错误排查和最佳实践。

安装方法

前提条件:安装 Docker Engine

Docker Compose 是 Docker Engine 的插件,需要先安装 Docker Engine。

# 移除旧版本(如果存在)
sudo apt remove docker docker-engine docker.io containerd runc

# 安装依赖
sudo apt update
sudo apt install -y ca-certificates curl gnupg

# 添加 Docker 官方 GPG 密钥
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc

# 添加 Docker 官方仓库
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

# 更新包索引
sudo apt update

# 安装 Docker Engine + Compose 插件(二合一)
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

# 将当前用户加入 docker 组(避免每次 sudo)
sudo usermod -aG docker $USER

# 重新登录后生效,或在当前 shell 立即生效
newgrp docker

方式二:独立安装 Docker Compose(旧版方式)

如果你使用的是旧版 Docker Engine 或需要特定版本的 Compose,可以通过 GitHub Releases 下载独立二进制。

# 下载最新版本(以 v2.27.1 为例)
sudo curl -L "https://github.com/docker/compose/releases/download/v2.27.1/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose

# 添加执行权限
sudo chmod +x /usr/local/bin/docker-compose

# 验证
docker-compose --version

注意:独立安装的 docker-compose(带连字符)和插件版的 docker compose(空格)是两种调用方式。新项目推荐使用插件版。

方式三:通过 pip 安装(Python 版,已不推荐)

Docker Compose 最初是用 Python 编写的,可以通过 pip 安装。但此方式现已不推荐,仅作为备选方案列出。

# 安装 pip(如果尚未安装)
sudo apt install -y python3-pip

# 安装 Docker Compose
pip3 install docker-compose

# 验证
docker-compose --version

版本管理

Docker Compose 随 Docker Engine 一起更新,通过 APT 安装时会自动获取最新版本。如需锁定版本或手动更新:

# 查看当前版本
docker compose version

# 查看可用版本
apt list -a docker-compose-plugin

# 安装特定版本
sudo apt install -y docker-compose-plugin=2.27.1-1~ubuntu.24.04~noble

# 锁定版本,防止自动更新
sudo apt-mark hold docker-compose-plugin

# 解除锁定
sudo apt-mark unhold docker-compose-plugin

# 更新到最新版本
sudo apt update
sudo apt install -y --only-upgrade docker-compose-plugin

compose.yaml vs docker-compose.yml:Compose V2(插件版)同时识别 compose.yamlcompose.ymldocker-compose.yml 三个文件名,推荐使用 compose.yaml(官方新标准)。旧版独立二进制(docker-compose)只认 docker-compose.yml,迁移时注意。

Compose 常用命令速查

docker compose up -d              # 后台启动全部服务(-d 守护)
docker compose ps                 # 查看服务状态
docker compose logs -f web        # 跟踪某服务日志
docker compose logs --tail=100    # 最近 100 行
docker compose exec web bash      # 进入运行中容器执行命令
docker compose build              # 重新构建镜像
docker compose pull               # 拉取最新镜像
docker compose down               # 停止并删除容器/网络
docker compose down -v            # 连同数据卷一起删除(危险!)
docker compose restart web        # 重启单个服务
docker compose config             # 校验并展开 compose 文件(排错神器)
docker compose top                # 查看各服务进程

依赖与启动顺序:compose 的 depends_on 只保证启动顺序,不保证服务"就绪"(如数据库还没接受连接 Web 就启动了)。生产环境应配合 healthcheck + condition: service_healthy(Compose V2)解决。

常见错误

错误一:docker: command not found

Docker 没有正确安装,或者当前用户不在 docker 组中:

# 检查 Docker 是否安装
which docker
docker --version

# 检查用户是否在 docker 组
groups $USER | grep docker

# 如果不在,加入 docker 组
sudo usermod -aG docker $USER

# 立即生效(无需重新登录)
newgrp docker

错误二:Got permission denied while trying to connect to the Docker daemon socket

Docker 守护进程未运行,或当前用户权限不足:

# 检查 Docker 守护进程状态
systemctl status docker

# 如果未运行,启动并设置开机自启
sudo systemctl start docker
sudo systemctl enable docker

# 确认 docker 组权限
sudo usermod -aG docker $USER
newgrp docker

错误三:Cannot connect to the Docker daemon at unix:///var/run/docker.sock

Docker 服务没有启动,或者配置文件有问题:

# 启动 Docker 服务
sudo systemctl start docker

# 查看 Docker 日志排查原因
sudo journalctl -u docker --no-pager -n 50

# 如果是 socket 文件权限问题:把用户加入 docker 组并重新登录
# (不要 chmod 666 /var/run/docker.sock——等于把 root 权限开放给所有本地用户)
sudo usermod -aG docker $USER

错误四:docker compose up 报端口占用

目标端口已被其他进程占用:

# 查看占用端口的进程
sudo ss -tlnp | grep :8080

# 停止占用进程,或修改 compose.yml 中的端口映射
# 例如将 "8080:80" 改为 "9090:80"

真实案例

案例 A:docker compose 报 unknown command

用户运行 docker compose version 报错 docker: 'compose' is not a docker command,但手册里明明有这条命令。

# 报错
docker: 'compose' is not a docker command.
See 'docker --help'

# 排查:compose 是插件,需确认插件包已安装
docker compose version          # 失败
ls /usr/libexec/docker/cli-plugins/   # 目录为空或不存在

# 修复:安装 compose 插件
sudo apt install -y docker-compose-plugin

# 验证
docker compose version          # Docker Compose version v2.27.1

根因:旧版 Docker 安装(如用 get.docker.com 脚本但版本过旧)未带 compose 插件,或插件目录缺失;修复:装 docker-compose-plugin验证docker compose version 输出版本号。

案例 B:compose 文件 YAML 缩进错误

# 报错
services.web.ports must be a list
services.web.environment must be a mapping
yaml: line 12: mapping values are not allowed in this context

# 错误写法(ports 少了两个空格,缩进到 web 同级)
services:
  web:
    image: nginx
  ports:            # ← 错:与 web 同级了,实际应缩进到 image 同级
    - "8080:80"

# 正确写法
services:
  web:
    image: nginx
    ports:
      - "8080:80"

# 用 config 命令校验(推荐先跑再 up)
docker compose config   # 无输出=语法正确;有错误会标出行号

根因:YAML 缩进敏感,端口/环境变量等键必须与服务名同级缩进;预防:编辑器开启 YAML 对齐提示,提交前 docker compose config 校验,CI 中可加 docker compose config --quiet 步骤。

案例 C:容器间通信失败——用 localhost 而非服务名

现象:Web 容器连接数据库容器报 Connection refused,compose.yml 中配置了 links 但仍然失败。

# Web 容器内测试
$ docker exec web curl -s db:5432
curl: (7) Failed to connect to db port 5432: Connection refused

# 错误写法:用 localhost 连接数据库
DATABASE_URL=postgres://localhost:5432/mydb

# 正确写法:用 compose 服务名连接
DATABASE_URL=postgres://db:5432/mydb

根因:Docker Compose 创建的网络中,每个容器可以通过服务名作为 DNS 名称互相访问。localhost 指向容器自身,不是数据库容器。

修复:连接其他容器时使用 compose.yml 中定义的服务名(如 dbredis)而非 localhost。用 docker compose exec web ping db 验证 DNS 解析。

验证安装

# 检查版本
docker --version
docker compose version

# 创建测试目录
mkdir ~/compose-test && cd ~/compose-test

# 创建 compose.yml
cat > compose.yml << 'YAMLEOF'
services:
  web:
    image: nginx:alpine
    ports:
      - "8080:80"
YAMLEOF

# 启动服务
docker compose up -d

# 查看运行状态
docker compose ps

# 访问测试
curl http://localhost:8080

# 查看日志
docker compose logs web

# 停止并清理
docker compose down

# 清理测试目录
cd ~ && rm -rf ~/compose-test

案例 D:docker compose down -v 误删生产数据

用户执行 docker compose down -v 清理环境,结果把生产数据库的数据卷也删了,数据全部丢失。

# 危险命令
docker compose down -v    # -v 会删除所有 named volumes!

# 排查:查看哪些卷会被删除
docker compose down --dry-run 2>&1 | grep volume
# 或
docker volume ls | grep myproject

# 已删除的卷无法恢复!只能从备份恢复

# 预防措施:使用 docker compose down(不带 -v)
docker compose down    # 只删除容器和网络,保留数据卷

# 生产环境备份策略
# 方案一:定期导出数据库
docker exec db pg_dump -U postgres mydb > backup_$(date +%Y%m%d).sql

# 方案二:使用 Docker 卷备份工具
docker run --rm -v myproject_db-data:/data -v $(pwd)/backups:/backup \
  alpine tar czf /backup/db-data-$(date +%Y%m%d).tar.gz -C /data .

# 方案三:使用 docker volume inspect 确认卷存在
docker volume inspect myproject_db-data

根因docker compose down -v 会删除所有 named volumes,包括数据库数据卷;教训:生产环境永远不要使用 -v 参数,定期备份数据卷。

案例 E:docker compose build 失败——COPY 指令找不到文件

Dockerfile 中的 COPY . . 在构建时找不到预期文件,导致构建失败。

# 报错
#8 [2/3] COPY . .
#9 ERROR: failed to solve: failed to compute cache key: 
#   "/package.json" not found: not found

# 排查:检查 .dockerignore 是否排除了必要文件
cat .dockerignore
# node_modules    ← 可能排除了 package.json?检查是否有通配规则

# 检查构建上下文(docker compose build 时的目录)
ls -la package.json
# 文件存在

# 问题:.dockerignore 规则过于宽泛
cat .dockerignore
# *              ← 排除了所有文件!
# !src/          ← 只允许 src 目录
# 漏了 !package.json 和 !package-lock.json

# 修复 .dockerignore
cat > .dockerignore << 'EOF'
.git
node_modules
*.md
.env
.env.*
# 不要排除 package.json 和 package-lock.json
EOF

# 验证
docker compose build --no-cache web

根因.dockerignore 规则排除了构建所需的文件(如 package.json);修复:检查并修正 .dockerignore 规则,确保构建所需文件不被排除;预防:编写 .dockerignore 时明确列出不需要的文件,而非用 * 通配排除所有。

案例 F:docker compose exec 报 "no container found"

执行 docker compose exec web bash 报错 no container found for web_1,但 docker compose ps 显示服务在运行。

# 报错
Error: No container found for web_1

# 排查:docker compose ps 看到的是旧容器
docker compose ps
# NAME      STATUS      PORTS
# web_1     running     0.0.0.0:8080->80/tcp
# 但容器 ID 可能与 compose 预期不匹配

# 检查容器是否真的在运行
docker ps | grep web
# 可能没有匹配的容器

# 原因:容器在 compose down 后又手动 docker run 启动了
# 或者 compose.yml 修改了服务名导致不匹配

# 修复:重建服务
docker compose up -d --force-recreate web
docker compose exec web bash    # 成功

# 如果是服务名变更
docker compose down
docker compose up -d            # 使用新的 compose.yml

根因:compose 管理的容器与实际运行的容器不匹配(可能是手动操作或配置变更导致);修复docker compose up -d --force-recreate 强制重建容器。

版本管理详解

Docker Compose V1 vs V2 对比

# V1(旧版,已废弃)
# 命令:docker-compose(带连字符)
# 语言:Python
# 安装:独立二进制或 pip
# 已于 2023 年 6 月停止维护

# V2(当前版本)
# 命令:docker compose(空格,作为 Docker CLI 插件)
# 语言:Go
# 安装:随 Docker Engine 一起安装

# 检查当前版本
docker compose version    # V2
docker-compose --version  # V1(如果存在)

# V1 → V2 迁移
# 1. 安装 V2 插件
sudo apt install -y docker-compose-plugin

# 2. 替换脚本中的命令
# 旧:docker-compose up -d
# 新:docker compose up -d

# 3. 更新 CI/CD 脚本
# 旧:docker-compose -f prod.yml up -d
# 新:docker compose -f prod.yml up -d

# 4. 如果使用 V1 的 .env 文件,V2 兼容
# 但 V2 推荐使用 compose.yml 而非 docker-compose.yml

compose.yaml 版本声明

# V2 不再需要 version 字段(已废弃)
# 旧写法(V1 兼容)
# version: "3.8"
services:
  web:
    image: nginx

# 新写法(V2 推荐,省略 version)
services:
  web:
    image: nginx

# compose 文件 schema 校验
docker compose config    # 无输出=语法正确
docker compose config --quiet    # 静默模式,只检查语法

# 多文件合并(环境区分)
# 基础配置
cat > compose.yml << 'EOF'
services:
  web:
    image: nginx
  db:
    image: postgres:16
EOF

# 开发环境覆盖
cat > compose.override.yml << 'EOF'
services:
  web:
    volumes:
      - ./src:/usr/share/nginx/html
    ports:
      - "8080:80"
EOF

# 生产环境覆盖
cat > compose.prod.yml << 'EOF'
services:
  web:
    restart: always
    logging:
      driver: json-file
      options:
        max-size: "10m"
        max-file: "3"
EOF

# 启动
docker compose up -d                      # 开发(自动合并 override)
docker compose -f compose.yml -f compose.prod.yml up -d  # 生产

环境变量管理最佳实践

# 方案一:.env 文件(推荐)
cat > .env << 'EOF'
POSTGRES_PASSWORD=secretpassword
POSTGRES_DB=myapp
REDIS_PASSWORD=redissecret
EOF

# compose.yml 中引用
cat > compose.yml << 'EOF'
services:
  db:
    image: postgres:16
    environment:
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      POSTGRES_DB: ${POSTGRES_DB}
    volumes:
      - db-data:/var/lib/postgresql/data
  redis:
    image: redis:7-alpine
    command: redis-server --requirepass ${REDIS_PASSWORD}
EOF

# 方案二:env_file 指定文件
cat > compose.yml << 'EOF'
services:
  db:
    image: postgres:16
    env_file:
      - ./config/db.env
EOF

# 方案三:Docker secrets(生产环境推荐)
cat > compose.yml << 'EOF'
services:
  db:
    image: postgres:16
    environment:
      POSTGRES_PASSWORD_FILE: /run/secrets/db_password
    secrets:
      - db_password
secrets:
  db_password:
    file: ./secrets/db_password.txt
EOF

# 重要:.env 和 secrets 文件加入 .gitignore
echo ".env" >> .gitignore
echo "secrets/" >> .gitignore
echo "*.env" >> .gitignore

故障排查决策树

当 Docker Compose 出现问题时,按以下流程排查:

问题:docker: command not found
├── which docker 有输出?
│   ├── 是 → 检查用户权限:groups $USER | grep docker
│   │   ├── 有 docker 组 → 检查 Docker 守护进程:systemctl status docker
│   │   └── 无 docker 组 → sudo usermod -aG docker $USER && newgrp docker
│   └── 否 → Docker 未安装
│       └── 安装:curl -fsSL https://get.docker.com | sh

问题:docker compose 报 unknown command
├── docker compose version 有输出?
│   ├── 有 → 命令正确,检查拼写
│   └── 无 → compose 插件未安装
│       └── sudo apt install -y docker-compose-plugin

问题:permission denied 连接 Docker daemon
├── Docker 守护进程运行中?systemctl status docker
│   ├── 未运行 → sudo systemctl start docker
│   └── 运行中 → 检查 docker 组
│       ├── 用户在 docker 组 → 检查 /var/run/docker.sock 权限
│       └── 不在 → sudo usermod -aG docker $USER && newgrp docker

问题:Cannot connect to Docker daemon
├── 检查 Docker 服务状态:systemctl status docker
│   ├── 未运行 → sudo systemctl start docker
│   └── 运行中 → 查看日志:journalctl -u docker --no-pager -n 50
├── 检查 daemon.json 配置
│   └── cat /etc/docker/daemon.json
├── 检查磁盘空间:df -h
│   └── 空间不足 → docker system prune -a
└── 检查内核版本:uname -r
    └── 内核过旧 → 升级内核

问题:docker compose up 报端口占用
├── ss -tlnp | grep :
│   ├── 有进程占用 → 停止占用进程或修改 compose.yml 端口映射
│   └── 无进程 → 可能是 TIME_WAIT 状态
│       └── 等待或修改为其他端口
└── docker compose ps 检查服务状态

问题:容器启动后立即退出
├── docker compose logs  查看日志
├── 检查 Dockerfile 中的 CMD/ENTRYPOINT
├── 检查环境变量是否正确
├── 检查挂载的文件/目录是否存在
└── docker compose up --abort-on-container-exit 查看退出码

问题:镜像拉取失败
├── 检查网络:docker pull hello-world
│   ├── 成功 → 镜像名/标签错误
│   └── 失败 → 配置镜像加速器
│       └── /etc/docker/daemon.json 添加 registry-mirrors
├── 检查 Docker Hub 限制
│   └── 登录:docker login
└── 使用国内镜像仓库
    └── 阿里云/腾讯云容器镜像服务

生产环境配置建议

  • 使用 compose.yml + compose.prod.yml 分离配置:基础配置放 compose.yml,生产覆盖放 compose.prod.yml,通过 -f 参数合并加载。
  • 设置 restart: always:生产服务应设置自动重启策略,容器崩溃后自动恢复。
  • 配置日志轮转:默认 JSON 文件日志驱动会无限增长,需配置 max-sizemax-file 限制日志大小。
  • 使用 healthcheck 确保服务就绪depends_on 只保证启动顺序,不保证服务就绪。配合 healthcheck + condition: service_healthy 解决。
  • 数据持久化使用 named volumes:数据库等有状态服务必须使用 named volumes,不要使用 bind mount。
  • 限制容器资源:使用 deploy.resources.limits 限制 CPU 和内存,防止某个容器耗尽宿主机资源。
  • 定期更新基础镜像:使用 docker compose pull 拉取最新镜像,配合 docker compose up -d --force-recreate 更新服务。
  • 使用 docker compose profiles 区分环境:开发工具(如调试器、数据库 GUI)放在 dev profile,生产环境只启动必要服务。

最佳实践

  • 使用 compose.yml 而非 docker-compose.yml:Docker Compose V2 默认识别 compose.yml,旧版的 docker-compose.yml 仍然支持,但新项目建议使用新命名。
  • 使用 .env 文件管理环境变量:将敏感信息(如数据库密码)放在 .env 文件中,并加入 .gitignore,避免提交到版本库。
  • 善用 docker compose profiles:通过 profiles 区分开发环境和生产环境的服务,只启动需要的服务。
  • 定期清理无用容器和镜像docker system prune -a 可以清理所有停止的容器、无用的网络和悬空镜像,释放磁盘空间。
  • 使用 depends_on 管理启动顺序:在多服务场景中,用 depends_on 确保数据库等基础设施先于应用启动。

延伸阅读

案例 G:docker compose profiles 未启用导致服务未启动

现象:compose.yml 中定义了 profiles,执行 docker compose up -d 后某些服务未启动:

# compose.yml
services:
  web:
    image: nginx
    ports:
      - "8080:80"
  debug:
    image: busybox
    profiles:
      - debug

# 启动后检查
$ docker compose ps
NAME      STATUS      PORTS
web_1     running     0.0.0.0:8080->80/tcp
# debug 服务未启动

根因:Docker Compose V2 的 profiles 功能允许按环境区分服务。未指定 profile 的服务会自动启动,但指定了 profile 的服务需要显式启用。

修复

# 启用 debug profile
docker compose --profile debug up -d

# 或在 .env 文件中配置默认 profile
echo "COMPOSE_PROFILES=debug" >> .env
docker compose up -d

# 查看所有服务(包括未启动的)
docker compose ps -a

预防:在 compose.yml 中为可选服务添加 profiles 注释说明。团队文档中记录各环境需要启用的 profiles。

↑ 回到顶部