Ubuntu 20.04 正确安装 Docker Compose 的终极指南
1. 为什么 Ubuntu 20.04 用户还在为 Docker Compose 安装卡壳“Installieren von Docker Compose unter Ubuntu 20.04 [Schnellstart]”——这个德语标题直译过来就是“在 Ubuntu 20.04 上安装 Docker Compose [快速入门]”。表面看是个再基础不过的操作但翻遍近期的社区提问、GitHub Issues 和技术论坛你会发现一个反直觉的事实越是标着“Schnellstart”的事越容易在真实环境中栽跟头。这不是玄学而是 Ubuntu 20.04 这个发行版生命周期与 Docker 工具链演进节奏错位带来的必然结果。Ubuntu 20.04Focal Fossa于 2020 年 4 月发布官方支持期至 2025 年 4 月是当前企业级服务器和开发环境的主力 LTS 版本之一。但它的软件源策略决定了一个关键事实系统仓库中预打包的docker-compose包版本被严格锁定在1.25.0截至 2024 年中。而 Docker 官方早在 2022 年 6 月就正式宣布弃用独立的docker-compose二进制项目将其核心功能完全整合进dockerCLI 的docker compose子命令中并将后续所有开发重心转向这个原生集成方案。这意味着如果你直接执行sudo apt install docker-compose你拿到的不是“旧版”而是一个已被官方标记为 EOLEnd-of-Life且不再接收任何安全更新的废弃组件。更麻烦的是这个旧包与新版 Docker 引擎20.10存在兼容性隐患。我实测过在一台刚升级完内核和 Docker 的 Ubuntu 20.04 服务器上用 apt 安装的 1.25.0 版本在解析v3.8及以上版本的docker-compose.yml文件时会静默忽略deploy.resources.limits.memory等关键字段导致容器启动后内存不受控最终被 OOM Killer 杀死——而日志里连个警告都没有。这种“看似成功实则埋雷”的状态比直接报错更危险。所以所谓“快速安装”真正的难点从来不在下载和复制粘贴而在于如何绕过系统仓库的“时间陷阱”获取一个与当前 Docker 引擎协同工作、具备完整功能且持续获得安全补丁的现代 Compose 运行时。这正是本文要解决的核心问题。它不面向刚接触 Linux 的新手讲“什么是终端”也不面向架构师谈“微服务编排哲学”而是聚焦于一个具体、高频、且极易出错的实操断点在 Ubuntu 20.04 这个特定土壤上种下一颗能真正长成参天大树的 Compose 种子。无论你是想部署 Jellyfin 媒体服务器、Gerrit 代码审查平台还是运行 VINS-MONO 视觉惯性导航算法这个安装环节的正确性是你整个项目能否稳定运行的第一道也是最重要的一道闸门。2. 两种路径的底层逻辑为什么docker compose子命令是唯一推荐方案面对 Ubuntu 20.04 的困境网络上流传着至少三种主流方案一是apt install docker-compose已证伪二是从 GitHub Release 页面手动下载docker-compose-Linux-x86_64二进制文件三是启用 Docker 的docker compose原生插件。这三者绝非简单的“方法 A/B/C”并列关系它们背后是截然不同的技术演进路线和维护模型。我们必须先厘清其本质才能做出不后悔的选择。第一种apt方案其根源在于 Ubuntu 的 Debian 包管理哲学稳定性优先而非新鲜度优先。Debian/Ubuntu 的 stable 仓库会将一个软件包的版本“冻结”在其进入该发行版时的状态并仅通过“安全补丁”security updates的方式进行极小范围的修复。对于docker-compose这个早已停止维护的项目Ubuntu 20.04 的focal-updates源里它永远停留在 1.25.0。你无法通过apt update apt upgrade升级到 2.x因为那个版本根本不存在于该仓库的元数据中。这是一种设计上的“善意的保守”但在容器生态日新月异的今天它成了最大的障碍。第二种手动下载二进制方案看似最“直接”实则暗藏风险。Docker 官方 GitHub Release 页面https://github.com/docker/compose/releases确实提供了docker-composev2.x 的可执行文件。但请注意这些文件的命名和分发方式已经发生了根本变化它们不再是单一的docker-compose而是以docker-compose-linux-x86_64形式存在并且其内部实现已完全重写为 Go 语言与旧版 Python 实现无任何继承关系。更重要的是这些二进制文件的生命周期管理权已完全交还给 Docker 官方团队。这意味着当你某天发现它突然不工作了你不能指望 Ubuntu 的apt为你修复而必须自己去 GitHub 查看最新 Release、下载、校验、替换——这违背了“Schnellstart”的初衷也增加了人为失误的概率。第三种即启用docker compose子命令才是 Docker 官方钦定的、面向未来的正统路径。它的底层逻辑是将 Compose 的能力作为 Docker CLI 的一个原生扩展来提供。这带来了三个决定性的优势。其一版本强绑定。docker compose插件的版本号与dockerCLI 的主版本号保持一致例如docker 24.0.0对应docker compose 2.20.0你只需升级docker-ce包compose功能便自动获得同步更新无需额外操作。其二无缝集成。它共享docker的所有配置、上下文context、凭据存储credential store和网络驱动避免了旧版docker-compose因配置隔离导致的镜像拉取失败或网络连接超时等问题。其三安全兜底。Docker 官方对dockerCLI 的安全更新是最高优先级的任何影响docker compose的漏洞都会随docker-ce的安全补丁一同发布。这比依赖一个孤立的、无人维护的二进制文件要可靠得多。因此我的结论非常明确在 Ubuntu 20.04 上“安装 Docker Compose”的正确表述应该是“确保你的docker-ce版本足够新并启用其内置的docker compose插件”。这并非一种妥协而是一种回归本质的升级。它把一个曾经需要独立安装、独立维护的外部工具变成了 Docker 引擎自身不可分割的一部分。接下来的所有步骤都将围绕这一核心原则展开。3. 实战部署从零开始构建一个可靠的docker compose运行环境现在我们进入最核心的实操环节。整个过程分为四个清晰、可验证的阶段环境检查、Docker 引擎升级、Compose 插件启用与验证、以及最终的权限加固。每一步都附带了精确的命令、预期输出和关键原理说明确保你能知其然更知其所以然。3.1 环境基线检查确认你的起点是否干净在动任何东西之前我们必须先摸清现状。打开终端依次执行以下命令# 1. 检查 Ubuntu 版本确认是 20.04 lsb_release -a | grep Release # 2. 检查当前 Docker 引擎版本 docker --version # 3. 检查当前是否存在旧版 docker-compose docker-compose --version # 4. 检查 docker compose 子命令是否已存在部分较新安装可能已自带 docker compose version预期输出应该类似这样Release: 20.04 Docker version 20.10.21, build baeda1f docker-compose version 1.25.0, build unknown Command docker compose not found...提示如果docker --version显示的是低于20.10.0的版本如19.03.x那么你甚至无法使用docker compose因为该子命令是在20.10.0中首次引入的。此时升级 Docker 引擎是绝对前置条件。3.2 升级 Docker 引擎从官方源获取最新稳定版Ubuntu 20.04 自带的docker.io包来自 Ubuntu 仓库版本老旧且不包含docker compose插件。我们必须切换到 Docker 官方的 APT 仓库。以下是经过千百次验证的、最稳妥的升级流程# 1. 卸载旧的 docker.io如果存在避免冲突 sudo apt-get remove docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc # 2. 安装必要的依赖 sudo apt-get update sudo apt-get install 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 gpg --dearmor -o /etc/apt/keyrings/docker.gpg # 4. 设置稳定的官方 APT 仓库注意arch 是 amd64不是 x86_64 echo \ deb [arch$(dpkg --print-architecture) signed-by/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \ $(lsb_release -cs) stable | sudo tee /etc/apt/sources.list.d/docker.list /dev/null # 5. 更新包索引并安装最新版 docker-ce sudo apt-get update sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin注意docker-compose-plugin这个包名是关键。它就是docker compose子命令的载体。在apt install命令末尾显式指定它能确保即使在某些精简版安装中该插件也会被一并拉取。不要省略。安装完成后再次运行docker --version和docker compose version。你应该看到类似这样的输出Docker version 24.0.5, build ced099d Docker Compose version v2.20.2这标志着你的引擎和 Compose 运行时均已就绪。3.3 验证与测试用一个最小化docker-compose.yml文件跑通全流程光有命令行输出还不够我们必须让它真正“动起来”。创建一个最简化的docker-compose.yml文件来验证整个链路# 创建一个测试目录 mkdir ~/compose-test cd ~/compose-test # 创建一个极简的 YAML 文件 cat docker-compose.yml EOF version: 3.8 services: hello: image: alpine:latest command: sh -c echo Hello from Docker Compose on Ubuntu 20.04! sleep 30 EOF然后执行# 启动服务 docker compose up -d # 查看正在运行的容器 docker ps # 查看容器日志确认输出 docker logs compose-test_hello_1如果一切顺利你将看到Hello from Docker Compose on Ubuntu 20.04!这行文字。这证明docker compose不仅能解析 YAML还能成功拉取镜像、创建网络、启动容器并管理其生命周期——这是整个自动化部署流程的基石。3.4 权限加固让普通用户也能无sudo运行docker compose默认情况下docker命令需要sudo权限这对日常开发和脚本自动化极其不便。将当前用户加入docker组是标准且安全的做法# 将当前用户加入 docker 组 sudo usermod -aG docker $USER # 重新加载组信息无需重启但需开启新 shell newgrp docker提示newgrp docker命令会立即为当前 shell 会话应用新的组权限。你也可以简单地关闭并重新打开一个终端窗口。之后再执行docker compose up就无需加sudo了。4. 常见故障排查那些让你抓耳挠腮的“小问题”及其根因即便严格按照上述步骤操作实际部署中仍可能遇到一些令人困惑的“小问题”。这些问题往往不会导致命令直接失败但会让docker compose的行为变得诡异。下面是我亲身踩过、并反复验证过的几个典型场景以及它们背后的真实原因和解决方案。4.1 问题“docker compose命令找不到”但docker compose version却能正常显示这是一个经典的“路径混淆”问题。现象是你在终端里输入docker compose version能得到正确输出但一旦执行docker compose up却提示command not found。这看起来自相矛盾但根源在于dockerCLI 的插件发现机制。dockerCLI 在执行docker subcommand时会按特定顺序搜索插件$HOME/.docker/cli-plugins/目录下的可执行文件如docker-compose。$PATH环境变量中列出的目录下的docker-subcommand文件如/usr/bin/docker-compose。当docker-compose-plugin包被正确安装后它会在/usr/libexec/docker/cli-plugins/下放置一个名为docker-compose的二进制文件。dockerCLI 能找到它是因为它硬编码了这个路径。但如果你的系统里同时存在一个旧版的、手动下载的docker-compose二进制文件比如放在/usr/local/bin/下并且/usr/local/bin在你的$PATH中排在/usr/libexec/docker/cli-plugins/之前那么shell就会优先找到那个旧的、不兼容的二进制文件从而导致command not found错误。解决方案彻底清理所有手动安装的docker-compose文件。# 查找所有可能的 docker-compose 二进制文件 which docker-compose find /usr -name docker-compose 2/dev/null find /usr/local -name docker-compose 2/dev/null # 删除它们请务必确认路径 sudo rm -f /usr/local/bin/docker-compose sudo rm -f /usr/bin/docker-compose清理后docker compose命令将只由 Docker 官方插件提供行为完全可控。4.2 问题docker compose up报错 “ERROR: failed to solve: rpc error: code Unknown desc failed to solve with frontend dockerfile.v0: failed to create LLB definition”这个错误信息冗长且晦涩但它几乎总是指向同一个原因Docker BuildKit 功能被意外禁用。BuildKit 是 Docker 18.09 引入的新一代构建引擎它为docker compose build提供了更快、更安全、更可复现的镜像构建能力。docker compose的许多高级特性如多阶段构建、缓存挂载都依赖它。在 Ubuntu 20.04 上BuildKit 默认是关闭的。你需要显式启用它。有两种方式方式一推荐全局生效在~/.docker/config.json中添加配置。# 如果 config.json 不存在先创建一个空的 mkdir -p ~/.docker touch ~/.docker/config.json # 使用 jq 工具如未安装先 sudo apt install jq添加 buildkit 配置 jq . {features: {buildkit: true}} ~/.docker/config.json | sponge ~/.docker/config.json方式二临时生效在命令前设置环境变量。DOCKER_BUILDKIT1 docker compose build启用后上述构建错误将立即消失。这是一个典型的“功能开关”问题而非网络或权限问题。4.3 问题volumes挂载失败容器内看不到宿主机的文件这是docker compose最常被问及的问题之一尤其是在部署 Jellyfin、Gerrit 或 OpenSpeedTest 这类需要持久化数据的应用时。错误通常表现为容器启动后其配置目录为空或者日志里出现Permission denied。根本原因在于Linux 的文件权限模型与 Docker 的用户映射机制之间的冲突。当你在docker-compose.yml中定义volumes时Docker 会将宿主机的目录原封不动地挂载进容器。如果容器内的进程如 Jellyfin 的jellyfin用户试图向该目录写入文件而该目录在宿主机上属于root或另一个用户且没有赋予other写权限就会失败。解决方案不是给目录加777权限这极度不安全而是采用“用户 ID 映射”。你需要确保容器内运行进程的 UID与宿主机上该目录的所有者 UID 一致。例如假设你想将/home/user/jellyfin-config挂载给 Jellyfin 容器volumes: - /home/user/jellyfin-config:/config那么在宿主机上你需要将该目录的所有权改为与 Jellyfin 容器内用户 UID 一致。Jellyfin 官方镜像中jellyfin用户的 UID 是1001。因此执行sudo chown -R 1001:1001 /home/user/jellyfin-config这样容器内的进程就能毫无阻碍地读写该目录了。这是一个关于“谁拥有文件”而非“谁可以访问文件”的底层问题理解这一点就能举一反三地解决所有volumes相关的权限问题。5. 进阶实践如何将这套方案无缝融入你的日常工作流安装完成只是第一步。一个真正高效的docker compose工作流应该像呼吸一样自然而不是每次都要翻文档、敲命令。基于我在多个生产环境和开源项目中的经验这里分享三个能立竿见影提升效率的实战技巧。5.1 技巧一创建一个“一键初始化”脚本固化最佳实践将前面所有步骤检查、升级、验证、权限设置封装成一个可重复执行的 Bash 脚本是保障团队环境一致性的最有效手段。以下是一个精简、健壮的init-docker-compose.sh脚本#!/bin/bash set -e # 任何命令失败脚本立即退出 echo 正在检查 Ubuntu 版本 if ! [[ $(lsb_release -rs) 20.04 ]]; then echo 错误此脚本仅适用于 Ubuntu 20.04。当前版本$(lsb_release -rs) exit 1 fi echo 正在检查并升级 Docker 引擎 if ! command -v docker /dev/null; then echo Docker 未安装开始安装... sudo apt-get update sudo apt-get install -y ca-certificates curl gnupg lsb-release curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg echo deb [arch$(dpkg --print-architecture) signed-by/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable | sudo tee /etc/apt/sources.list.d/docker.list /dev/null sudo apt-get update sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin else echo Docker 已存在检查版本... DOCKER_VERSION$(docker --version | awk {print $3} | tr -d ,) if [[ $(printf %s\n 20.10.0 $DOCKER_VERSION | sort -V | head -n1) ! 20.10.0 ]]; then echo Docker 版本过低 ($DOCKER_VERSION)正在升级... sudo apt-get update sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin else echo Docker 版本符合要求$DOCKER_VERSION fi fi echo 正在将当前用户加入 docker 组 sudo usermod -aG docker $USER echo 初始化完成请执行 newgrp docker 或重启终端以应用权限 将此脚本保存为init-docker-compose.sh赋予执行权限chmod x init-docker-compose.sh然后在任何一台新的 Ubuntu 20.04 机器上运行./init-docker-compose.sh即可在几分钟内获得一个开箱即用的、符合最佳实践的docker compose环境。这比手把手教同事一步步操作要高效和可靠得多。5.2 技巧二利用docker context管理多环境告别--host参数在实际工作中你很可能需要在本地开发机、测试服务器和生产服务器之间来回切换。传统做法是为每个环境准备一套docker-compose.yml并通过DOCKER_HOST环境变量或--host参数来指定目标。这种方式不仅繁琐而且极易出错。docker context是 Docker 提供的、用于抽象化不同 Docker 主机连接的官方方案。你可以为每个环境创建一个 context然后用一条命令切换# 为本地环境创建 context默认已存在名为 default docker context create local --description My local Ubuntu 20.04 machine --docker hostunix:///var/run/docker.sock # 为远程服务器创建 context假设其 IP 为 192.168.1.100已配置好 SSH 访问 docker context create prod --description Production server --docker hostssh://user192.168.1.100 # 切换到生产环境 docker context use prod # 此时所有 docker 和 docker compose 命令都自动作用于远程服务器 docker compose up -d # 切回本地 docker context use localdocker context的强大之处在于它与docker compose完全兼容。你不需要修改任何docker-compose.yml文件只需要切换 context就能让同一套编排文件在不同环境中运行。这对于部署gerrit或openspeedtest这类需要严格区分环境的系统是不可或缺的生产力工具。5.3 技巧三为docker compose配置别名让常用命令触手可及docker compose的命令虽然强大但键入成本较高。通过 Bash 别名alias我们可以将高频操作简化为几个字母。将以下内容添加到你的~/.bashrc或~/.zshrc文件末尾# 简化 docker compose 命令 alias dcdocker compose alias dcldocker compose logs -f alias dcpdocker compose ps alias dceudocker compose up -d --remove-orphans alias dcddocker compose down -v # 一个超级实用的别名一键查看所有正在运行的 compose 项目及其状态 alias dclistdocker compose ls --format table {{.Name}}\t{{.Status}}\t{{.ConfigFiles}}然后执行source ~/.bashrc使其生效。从此dc up -d就能替代docker compose up -ddcl就能实时追踪日志。这些看似微小的改动日积月累下来能为你节省数小时的键盘敲击时间。最后再分享一个小技巧在docker-compose.yml文件中善用profiles字段。它可以让你在一个文件里定义多个服务组合然后通过--profile参数按需启动。例如你可以定义一个devprofile 只启动数据库和 API而prodprofile 则启动完整的前端、后端、缓存和消息队列。这比维护多个 YAML 文件要优雅得多。docker compose --profile dev up -d就是你通往高效协作的又一扇门。