
直接开门见山这篇博文写给想在 Ubuntu 上装好 Docker 和 Docker Compose 的人。不管你是在虚拟机里折腾还是给云服务器做初始化搜到这个标题大概率就是想少走弯路把环境一次性配明白。我先说个结论用官方 apt 源安装把 docker-compose-plugin 一起装进去是最省心的路线稳、可控、后续升级也简单。至于网上那些一键脚本、二进制替换之类的方式我会在文章里做对比把各自适合的场景讲清楚。我本人这两年在 Ubuntu 22.04 和 24.04 上都反复装过 Docker 环境踩过不少坑包括残留版本冲突、socket 权限、Compose 插件找不到之类的问题。这篇文章会把完整的安装步骤、参数背后的原理、以及一整套排错经验写出来适合从没装过的新手也适合想弄明白“为什么这么装”的进阶读者。1. 装之前先做好三件事版本确认、残留清理和原因分析1.1 确定 Ubuntu 版本和内核架构先说为什么不能跳过这一步。Docker 安装源是按 Ubuntu 版本代号区分的不同代号对应不同仓库路径写错了 apt 会直接报错找不到包。而且内核版本太老的话容器运行时会出一些奇怪问题光是排查就能耗掉你半天。打开终端依次执行这几条命令lsb_release -a cat /etc/os-release uname -r uname -m建议直接看/etc/os-release里的VERSION_CODENAME因为配置 apt 仓库时要用到它。Ubuntu 22.04 对应 jammy24.04 对应 noble。这个字段写错后面几步全部白搭。内核方面20.04 以上的系统基本上都能流畅跑 docker-ce。如果你用的是老掉牙的 18.04我建议先升级系统而不是在这个旧版本上硬装否则后续做容器存储驱动配置时会遇到一堆麻烦。很多人看到网上老教程提到 Ubuntu 版本就直接照着抄结果发现自己的 apt update 已经 404这种情况真的很常见。架构信息同样重要。绝大多数 PC 和云服务器是 amd64也就是 x86_64但 ARM 机器比如树莓派、部分云 ARM 实例需要把架构参数换成 arm64。这个在添加软件源时会用到。1.2 清理可能存在的旧版 Docker很多机器上其实已经装过 Docker只是你忘了。比如之前用apt install docker.io装过或者装过 docker-engine、containerd 之类的老组件。docker.io 是 Ubuntu 官方仓库维护的快照版本和 Docker 官方发布的 docker-ce 是两条不同的发布线两者共存时很容易出冲突。先检查一下dpkg -l | grep -i docker如果有输出建议做好备份后清理干净。除非你明确知道自己要保留旧环境否则不要跳过清理步骤。我遇到过最典型的场景机器上残留了 containerd 旧版本新装 docker-ce 后一运行容器就报 shim task 创建失败折腾很久才发现是两个运行时版本在打架。清理命令sudo apt remove docker docker-engine docker.io containerd runc注意这一步不会删除/var/lib/docker里的数据。如果你的旧环境里跑过容器、有过镜像和卷重装后这些数据还在。对新机器来说这些都不用管但如果你是从旧环境迁移记得先把数据备份出来再动手。1.3 为什么要用官方 apt 源而不用脚本一键装Docker 官网给过一个快速安装脚本curl -fsSL https://get.docker.com | sh这个脚本确实方便适合临时测试我在一些一次性实验环境里也这么干过。但我个人不太推荐把它用在正式环境或者长期维护的机器上原因有两点。第一脚本会直接拉取最新版你很难锁定版本做升级管理。今天装的和三个月后同事装的可能就差了版本。第二脚本执行的内容不透明万一团队有安全审计要求你需要知道这台机器上每一步到底做了什么脚本方式不好交代。用 apt 源的好处在于仓库里所有 docker-ce 版本都能查得到可以固定主版本跟随系统的 apt 升级节奏还能配合 Ubuntu 的 unattended-upgrades 做自动安全更新。后面我会把 apt 源的配置过程一步步写出来。提示如果你只是想在本地快速跑个容器体验一下脚本装也行。但如果你打算长期维护、后续要上项目建议直接看第 2 章用官方源来装。2. 用官方 apt 源安装 Docker 引擎一条命令装出标准环境2.1 安装依赖工具先确保curl、ca-certificates、gnupg这些基础工具存在。有很多精简版系统镜像里连 curl 都没有直接跑后续命令会报 command not found。sudo apt update sudo apt install -y ca-certificates curl gnupggnupg 的作用是处理签名密钥。Docker 的 apt 仓库带了 GPG 签名如果没有 gnupg后续 apt update 校验签名时会直接失败。2.2 添加 Docker 官方 GPG 密钥添加密钥这一步不同教程的写法有些差异早期教程喜欢把密钥写到/etc/apt/trusted.gpg.d/但新的官方文档推荐写到/etc/apt/keyrings/下独立目录职责更清晰也方便统一管理。我建议用官方推荐的方式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 ar /etc/apt/keyrings/docker.asc解释一下这三条命令做了什么install先生成目录并设置 0755 权限curl把 Docker 的 GPG 公钥下载到指定位置chmod ar确保 apt 进程能读取这个文件。如果文件权限不对apt update 会报错说 key 无法读取指向的路径问题非常隐晦。2.3 写入 apt 软件源以 Ubuntu 24.04 为例添加如下软件源echo deb [archamd64 signed-by/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu noble stable | sudo tee /etc/apt/sources.list.d/docker.list /dev/null如果你的机器是 arm64 架构把archamd64改成archarm64。如果是 22.04把noble换成jammy。添加完自定义源之后必须让 apt 重新索引一次仓库元数据否则 apt 感知不到新仓库里有哪些包sudo apt update这里如果报签名校验失败基本可以确定是 2.2 节的路径或者权限没写对。可以重新执行一次 2.2 的三条命令再跑一次 update。2.4 一条命令安装 Docker 引擎、CLI 和容器运行时这是核心安装命令sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin很多人只装 docker-ce 和 docker-ce-cli后面需要 Compose 时又发现没有只能回来补装。我在这条命令里直接加了docker-compose-plugin这样docker compose子命令开机即用不用再单独折腾。这几个包分别是什么docker-ceDocker 守护进程本体负责管理容器、镜像、网络。docker-ce-cli命令行客户端你敲的docker命令实际由它提供。containerd.io容器运行时真正把容器跑起来的底层组件。docker-buildx-plugin构建镜像的扩展插件支持多平台构建等高级特性。docker-compose-pluginCompose v2 的官方插件提供docker compose子命令。2.5 验证安装结果sudo systemctl status docker docker versionsystemctl status能看出服务有没有正常运行。docker version会同时显示 client 和 server 两部分的版本信息如果只看到客户端版本而 server 部分连接不上要么是服务没启动要么是权限问题下一章会详细讲。到这里Docker 引擎本身已经装好了。但先别急着把镜像拉起来第 3 章要处理的是启动服务、开机自启、镜像加速这些第一次使用最容易卡住的地方。3. 首次启动前的网络配置镜像加速与开机自启3.1 启动 Docker 并设置开机自启安装完成后 Docker 服务不会自动启动需要手动拉起sudo systemctl start docker sudo systemctl enable dockerenable这一步会把 docker 服务注册到 systemd 的开机启动项里。如果你在虚拟机或者云服务器上长时间使用建议确认 enable 成功否则重启机器之后 Docker 不会自己跑起来服务就“失联”了。也可以用一条命令完成启动和注册sudo systemctl enable --now docker3.2 配置 registry mirror 加速镜像拉取Docker Hub 的访问速度在不同网络环境下差别很大拉大镜像时经常超时重试。最直接的解决办法是配置 registry mirror也就是镜像加速器。这个配置写在/etc/docker/daemon.json里。先检查文件是否存在sudo ls -l /etc/docker/daemon.json如果不存在手动创建并写入配置sudo mkdir -p /etc/docker sudo tee /etc/docker/daemon.json -EOF { registry-mirrors: [https://docker.m.daocloud.io] } EOF我这里写的是一个示例加速地址实际选择请根据你所在网络环境的可用性调整。配好之后重启 Docker 让配置生效sudo systemctl daemon-reload sudo systemctl restart docker需要说清楚的是registry mirror 的本质是给 Docker 拉镜像时提供一条代理路径它不改变你指定镜像地址的行为只是拉取的时候更快更稳。对个人项目和中小团队来说配置一个可用的加速地址基本够用了。3.3 跑第一个容器试试水sudo docker run hello-world这个 hello-world 镜像很小拉取成功后终端会打印一段说明文字代表 Docker 引擎工作正常。注意这里我用了sudo。如果你的普通用户没加入 docker 组不加 sudo 大概率会遇到 permission denied 的报错这个坑在第 5 章会专门讲。如果 hello-world 拉取特别慢优先确认 registry mirror 是否生效可以查一下当前生效的镜像配置sudo docker info --format {{json .RegistryConfig.Mirrors}}输出里能看到你配置的加速地址说明生效了。如果输出是空数组说明配置没加载进去检查一下 daemon.json 的格式sudo systemctl restart docker之后再看一次。4. Docker Compose v2 的三种安装方式我最终选了官方插件方案4.1 三种方式横向对比先做一个对比方便你根据自己的情况一步到位选择方式命令示例版本维护方式适用场景官方插件 apt 安装apt install docker-compose-pluginv2 跟随系统源更新系统包管理器最推荐生产环境首选静态二进制下载 docker-compose-linux-x86_64 文件v2 单文件手动覆盖文件离线环境、定制版本Python pippip install docker-composev1/v2 混杂依赖 Python 环境老项目兼容新项目不推荐v1 时代的docker-compose命令用 Python 写的功能能用但启动慢、日志格式一般而且 Python 版本一变就影响使用。v2 用 Go 重写作为 Docker CLI 的插件形式存在启动速度、输出体验都好很多。所以从 2023 年到现在新项目基本都统一用docker compose也就是空格分隔的子命令格式。如果你搜“docker-compose 安装”网上能翻出大量 v1 年代的教程They will guide you to pip install docker-compose or download docker-compose binary. 这些方式倒不是说不能用但放到今天的 Docker 生态里确实有些过时。4.2 如果你已经通过 apt 源安装如果照着第 2 节的命令安装docker-compose-plugin已经在了直接验证docker compose version输出类似Docker Compose version v2.27.1。只要看到这个后续docker compose up、docker compose ps这些命令都能直接跑。4.3 离线安装 Docker Compose 的完整步骤很多内网机器既没有外网也不能直接使用 apt 源这时静态二进制方案最省心。先确认架构uname -mx86_64 对应 amd64aarch64 对应 arm64。然后在一台可以访问外网的机器上从 GitHub Releases 页面下载对应版本的docker-compose-linux-x86_64或docker-compose-linux-aarch64文件。把文件传到目标机器后执行sudo mkdir -p /usr/local/lib/docker/cli-plugins sudo mv docker-compose-linux-x86_64 /usr/local/lib/docker/cli-plugins/docker-compose sudo chmod x /usr/local/lib/docker/cli-plugins/docker-compose注意文件名必须叫docker-compose目录必须位于 Docker 查找 CLI 插件的路径中。我之前见过有人把文件移到/usr/local/bin下结果docker compose依然提示找不到命令因为 Docker 插件机制不扫描/usr/local/bin。最后验证docker compose version这里有个细节值得说。Docker CLI 查找 compose 插件的路径一共有三处/usr/local/lib/docker/cli-plugins、/usr/share/docker/cli-plugins、以及用户目录下的~/.docker/cli-plugins。我推荐使用/usr/local/lib/docker/cli-plugins因为它在系统级不依赖特定用户的家目录是否存在。4.4 为什么我的选择是官方插件我的倾向很明确能用 apt 源安装的地方不要手动下载二进制。原因不是二进制方式不能用而是升级成本完全不同。apt 安装的插件会跟随系统的apt upgrade一起更新你不必记得每台机器上装了哪个版本的 compose系统统一管理。手动方式虽然也能用但每次升级都要重新下载、覆盖、再次确认机器一多就乱了。不过如果目标机器完全离线静态二进制几乎是唯一稳妥方案。它的优点是文件独立、不依赖 Python 运行时也不依赖系统 libssl 的动态版本拷进去就能用。5. 安装完成后最容易踩的五个坑权限、PATH 与服务状态5.1 没有把用户加入 docker 组导致权限不足这是我最常被问到的问题。Docker 的 socket 文件/var/run/docker.sock默认属于 root 和 docker 组普通用户直接执行docker ps会看到 permission denied 的报错。解决办法是把当前用户加入 docker 组sudo usermod -aG docker $USER newgrp dockernewgrp可以让当前终端立即生效不用退出重登。如果下次登录还是报权限问题检查一下用户组是否真的加上去了id $USER确认输出里有 docker 字样。没有的话说明 usermod 没生效或当前登录的会话没刷新。这里必须提醒一句加入 docker 组等同于拥有 root 权限因为容器可以被映射为 root。生产环境给别人开通权限时一定要谨慎使用最好通过 sudo 做收口管理。5.2 服务起不来时看的几个指标执行docker ps时提示Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?按顺序排查systemctl status docker看服务当前状态journalctl -u docker --no-pager -n 50看日志尾部ls -l /var/run/docker.sock看 socket 文件是否存在多数情况是服务没启动或者启动过程中崩溃。日志里出现 failed to start daemon 之类的关键词时多半是 daemon.json 写错了。这时候先把配置文件恢复默认sudo mv /etc/docker/daemon.json /etc/docker/daemon.json.bak sudo systemctl restart docker等基础功能确认正常之后再小心地把镜像加速配置补回来一次只改一个变量这是排错的基本原则。5.3 旧版本残留导致 containerd 冲突如果你之前手动装过旧版 docker-engine或者系统里有 containerd 残留新装 docker-ce 后运行容器可能报failed to create shim task: OCI runtime create failed这个报错和 OCI runtime 有关本质上是 containerd 或 runc 版本冲突。我会先检查系统里有没有多个 containerd 或 runcwhich containerd which runc dpkg -l | grep containerd dpkg -l | grep runc如果 apt 安装的 containerd.io 和别的路径下旧文件共存建议移除旧文件只保留系统包管理的版本。老版本 runc 对 cgroup v2 的支持不完整而 Ubuntu 22.04 及以上默认使用 cgroup v2所以表现尤其隐蔽。5.4 PATH 和命令调用方式混乱一个很常见的状态就是网上教程一半写docker-compose带横杠一半写docker compose带空格结果你机器上装的可能只有其中一种。我用一张表总结两者的关系命令形式对应版本安装来源docker composev2 插件docker-compose-plugin 或静态插件docker-composev1 / v2 独立可执行文件pip 或手动移动二进制如果你习惯写docker-compose但机器上没这个命令就先确认docker compose是否可用。想兼容两种写法也可以做软链sudo ln -s /usr/local/lib/docker/cli-plugins/docker-compose /usr/local/bin/docker-compose不过这种方案我不太推荐用于生产环境。团队里统一使用docker compose要比每个人各写各的好维护得多。毕竟新版本的 compose 功能迭代都集中在 v2 插件上独立可执行文件更像历史遗留。5.5 启动服务之后容器网络异常这个问题不会在安装阶段出现但第一次实际部署项目时特别容易遇到。容器起来了宿主机却访问不了服务端口或者容器能通外网但容器之间互相访问不通。先检查端口映射是否生效docker ps docker port 容器名再用 iptables 确认 Docker 的 NAT 链路是否正常sudo iptables -t nat -L -n | grep -i docker如果 NAT 链路被清空重启 Docker 服务会自动重建。但如果你同时用了 firewalld 或自写防火墙脚本重启顺序不同可能影响规则加载顺序。一般情况下先起 docker 服务再加载防火墙规则顺序比较稳。6. 用一个真实 Compose 项目验证安装从空目录到服务跑通6.1 搭建一个极简的 Nginx Redis 场景验证完 Docker 引擎基础功能之后我习惯用 Compose 起一个真实服务来确认插件工作正常。这里用 Nginx 和 Redis 做最小组合镜像体积小启动快覆盖了“Web 服务 有状态服务”两个常见类型。先建目录和文件mkdir -p ~/compose-demo cd ~/compose-demo touch docker-compose.yml写入如下内容services: web: image: nginx:1.27-alpine ports: - 8080:80 restart: unless-stopped cache: image: redis:7.2-alpine ports: - 6379:6379 restart: unless-stopped这个文件的语法是 Compose v2 的标准写法。注意services是顶层键v1 时需要写 version 字段v2 已经不需要了新版 compose 会自动判断格式。如果你抄网上老教程看到最顶上写着version: 3在新版本中可以直接不写不影响使用。6.2 后台启动并观察日志docker compose up -d第一次执行会拉取两个镜像期间输出会滚动显示每个镜像层的下载进度。拉完之后docker compose ps看到两个服务都是 running 状态说明环境没问题。再验证一下网页可以直接访问curl -I http://localhost:8080返回 200 OK 就说明 Nginx 已经在容器里跑起来了。查看日志用docker compose logs -f-f是 follow 模式实时滚动输出日志调试非常方便。按 CtrlC 退出日志查看不会影响容器运行。6.3 验证配置变更和重建流程实际项目中经常会改配置比如改端口、改环境变量。改完docker-compose.yml之后不需要手动停掉整个项目再启动直接执行docker compose up -dCompose 会自动对比当前配置和运行中的容器配置发现有变化就只重建变化的那几个服务。这是 Compose 相比手动docker run的核心优势环境配置变成了一个可以版本化的文件。如果需要彻底停止并清理容器和网络docker compose down如果连数据卷一起清理加上-v参数docker compose down -v注意-v会把服务定义里挂载的匿名卷一并删除。我在测试环境里不小心删过数据虽然不是生产库但也够让人心跳加速的。这条命令务必在确认不持有重要数据的情况下才执行。6.4 把 Compose 项目加入开机自启Compose 项目本身不直接支持开机自启但它依赖的 docker 服务会随 systemd 启动。一个常见做法是给 compose 项目写一个 systemd unit更简单的做法是使用restart: unless-stopped让容器在 docker 服务拉起后自动恢复。restart策略有几种策略行为no不自动重启always无论怎么退出都会重启unless-stopped除非手动停止否则开机自动拉起on-failure只在异常退出时重启我一般用unless-stopped因为它能区分“手动停止”和“异常退出”比always更贴近日常运维习惯。比如你临时想停掉某个服务用docker compose stop手动停止后机器重启时它不会自己又拉起来但如果是程序崩溃退出docker 会尝试重新拉起。6.5 验证完之后的体感整个流程走完其实就证明 Docker 引擎、CLI 和 Compose 插件都已经正常工作了。之后你再部署任何项目只需要拿到对应的docker-compose.yml在机器上执行docker compose up -d服务就跑起来了。这个体验和手动一条条敲docker run完全不是一个层级。我在实际使用中还有一个习惯把第 2 节的安装命令固化成setup-docker.sh脚本放到团队的初始化文档里。新机器来了直接跑一遍不用每次回忆步骤也不会漏掉插件。装完后顺手执行两条验证命令——docker compose version和docker run --rm hello-world一个验证插件一个验证引擎两步都过了环境基本不会有坑了。