每次新装一台 Ubuntu 服务器,我要做的第一件事几乎都是装 Docker。不管是部署个人项目、跑中间件做测试,还是搭建一套完整的主机运维环境,Docker 和 docker-compose 基本是绕不开的两件套。这个标题看起来简单,但实际操作里踩坑的点不少,比如 GPG 密钥失效、apt 源拉取超时、docker-compose 版本不匹配、普通用户权限不足等等。这篇文章就把我平时在 Ubuntu 上从零装好这两个工具的全过程、选型思路和排错记录完整梳理一遍,给你一份可以直接照着抄的作业。
文章内容适合刚接触 Linux 和容器的小白,也适合已经装过但想换个更规范方式重新搭建的老手。我会把每一步背后的原理和判断逻辑也讲清楚,不是光给命令,而是让你知道为什么这样做、遇到问题该往哪个方向排查。
1. 装之前先把这些概念和前提理清楚
1.1 Docker Engine 和 docker-compose 分别是什么
Docker Engine 是容器运行时的核心,它负责拉取镜像、创建容器、管理网络和存储卷,是真正干活的那一层。平时我们说“装 Docker”,指的就是在服务器上安装并运行 Docker Engine,装完之后通过docker命令来操作容器。
docker-compose 则是用来定义和运行多容器应用的工具。你可以在一个 YAML 文件里写清楚需要哪几个服务、每个服务用什么镜像、端口怎么映射、依赖关系怎么处理,然后一条docker compose up -d命令就能把整套环境拉起来。对于跑 MySQL、Redis、Nginx 这类常见组合,docker-compose 要比手动敲一长串docker run参数省心得多。
需要特别说明一点,从 Docker Compose v2 开始,官方推荐的方式是以 Docker 插件的形式使用,命令也从原来的docker-compose(带横杠)变成了docker compose(空格)。目前主流的安装方式和网上绝大多数教程,都已经向 v2 迁移,下面的步骤也会以 v2 插件版为主,同时说明独立二进制方式的用法。
1.2 为什么首选付费少、资料全的 Ubuntu LTS 版本
Ubuntu 的 LTS 版本(如 20.04、22.04、24.04)拥有长达五年的安全更新支持,Docker 官方对 LTS 版本的适配和测试也最充分。如果你的服务器是生产环境,优先选择 24.04 LTS 这类长期支持版本,不仅稳定,遇到问题时能搜到的解决方案也最多。
这篇文章以 Ubuntu 24.04 LTS 为例演示,不过对于 20.04、22.04 这些版本,安装命令和步骤基本一致,区别只在部分依赖包的名称可能有细微不同。我实际测试下来,这三代 LTS 系统的安装流程完全通用。
1.3 安装前检查系统内核版本和残留的旧包
Docker 对 Linux 内核有最低版本要求,一般是 3.10 以上。Ubuntu 20.04 及之后版本的内核都远高于这个线,所以基本不用操心内核问题。不过还是建议先确认一下系统信息,避免后续出现莫名其妙的问题:
# 查看系统版本信息 lsb_release -a # 查看当前内核版本 uname -r另外要检查系统里是否已经安装过旧版 Docker 相关软件包。如果你之前用apt install docker或apt install docker.io装过,直接再装 Docker Engine 会冲突。先确认一下现状:
# 列出已安装的docker相关包 dpkg -l | grep -i docker如果有类似docker.io、docker-engine、containerd、runc的旧包,先卸载干净再开始。注意,这里说的是系统自带或手动安装的旧版本,如果以前装过 Docker 官方源里的版本,完全卸载后直接升级即可。不清理干净的话,后面启动服务时可能出现端口占用、启动失败的状况。
2. 安装 Docker Engine 的完整流程
2.1 为什么我推荐走 Docker 官方 apt 仓库安装
安装 Docker Engine 有三种主流方式。
第一种是直接用apt install docker.io,这是 Ubuntu 软件源里自带的版本。优点是一条命令搞定,省事;缺点是版本往往偏老,而且没有 Docker 官方仓库的更新及时。对于追求稳定和懒人而言也不是不能用,但新特性、安全补丁的获取会滞后。
第二种是 Docker 官方提供的便捷安装脚本,执行curl -fsSL https://get.docker.com | sh即可。优点是真的方便,脚本会自动检测系统、配置源并安装最新版;缺点是脚本做了一些自动化操作,你不好控制每一步的细节,如果公司或学校网络有代理要求,处理起来反而麻烦。
第三种就是我推荐的方式:手动添加 Docker 官方 apt 源,再用apt install docker-ce docker-ce-cli containerd.io安装。这样既能确保拿到官方最新版本,也能清楚了解每个步骤在做什么,后续升级只需apt update && apt upgrade即可。
2.2 添加 Docker 官方源并安装 gpg 密钥的细节点
先更新本地的软件包索引,并安装几个后续会用到的工具包:
sudo apt update sudo apt install -y ca-certificates curl gnupg lsb-release这里面的ca-certificates是为了让系统信任 HTTPS 证书,curl用来下载密钥,gnupg用来处理密钥验证,lsb-release则用于识别当前系统的发行版代号。
接下来创建密钥目录并下载 Docker 官方的 GPG 密钥。我在实际运维中发现,这一步很多人会漏掉install -m 0755 -d这个细节,直接随便建目录,导致权限不对。标准的做法是:
sudo install -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg sudo chmod a+r /etc/apt/keyrings/docker.gpg这条命令把 Docker 官方公钥下载后做了一次 dearmor 转换,转换成二进制格式存到/etc/apt/keyrings/docker.gpg。之后的chmod a+r是为了让所有用户都能读取密钥,避免后续 apt 更新时出现权限报错。
然后添加 Docker 的 apt 仓库。注意这里的写法,官方文档里使用了一种根据系统架构和版本号动态生成路径的方式,自动适配 Ubuntu 的 codename:
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/nulldpkg --print-architecture会自动输出当前系统的 CPU 架构(通常是 amd64 或 arm64),lsb_release -cs会输出 Ubuntu 的代号(如 noble、jammy、focal)。这样写的好处是,一段命令在任何 Ubuntu 版本上都能正确生成对应的源地址。
2.3 安装 docker-ce 并验证服务状态
添好仓库后,先执行sudo apt update让系统识别新仓库。如果之前没有配置过 Docker 源,这一步可能会提示Get:... docker.list之类的信息,说明仓库已经成功加载。
然后安装三个核心组件:
sudo apt update sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin我这边把docker-buildx-plugin和docker-compose-plugin也一并安装了。buildx 是 Docker 官方推出的多平台镜像构建插件,docker compose子命令则依赖docker-compose-plugin这个包。一次装齐,后面不用再折腾。
安装完成后,启动 Docker 服务并设为开机自启:
sudo systemctl enable docker sudo systemctl start docker用systemctl status docker可以看服务状态,一个绿色的 active (running) 就代表正常。最后跑一遍经典的验证命令:
sudo docker run hello-world如果能看到 "Hello from Docker!" 的输出,说明整个 Docker Engine 已经正常工作,能拉取镜像、创建并运行容器。
3. 安装 docker-compose 的两种方式和版本选择
3.1 直接使用官方插件,一条命令省心省事
之前装 Docker 时如果一并安装了docker-compose-plugin,那现在直接就能用docker compose了。验证方式:
docker compose version输出如Docker Compose version v2.24.0即正常。这种方式的优点是:
- 随 Docker Engine 一起升级,版本由 apt 统一管理
- 不需要关心独立二进制的安装路径、权限问题
- 命令调用速度也更快,毕竟少了一层解释执行
日常使用中,docker compose up -d、docker compose logs -f、docker compose down这些命令都直接在项目目录下敲就行。
网上很多旧教程会让你去 GitHub 下载docker-compose-linux-x86_64二进制文件并放到/usr/local/bin。这个方案也没错,但独立二进制方式有两个明显的缺点:一是需要手动跟进版本,GitHub 上新版本发布后,系统的 apt 永远不会帮你升级;二是和 Docker Engine 的 version 匹配偶尔会出问题,Compose 版本太旧时,某些新特性(如 profiles 多环境配置)会提示不支持。
我的建议很明确:如果是 2023 年以后装的系统,默认就走插件方式,不要再折腾独立二进制。除非你有特殊需求,比如需要在 CI/CD 流水线里指定特定版本号的docker-compose可执行文件。
3.2 坚持要独立二进制的话,按住这几个关键点来
假设你就是想在系统里保留一个传统风格的docker-compose命令,操作路径如下。
先去 GitHub 的 docker/compose 仓库 Releases 页面,查看最新的版本号。以 v2.29.1 为例:
sudo curl -L "https://github.com/docker/compose/releases/download/v2.29.1/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-composeuname -s会输出 Linux,uname -m会输出 x86_64 或 aarch64,自动匹配正确的包名。下载完成后需要给执行权限:
sudo chmod +x /usr/local/bin/docker-compose验证是否成功:
docker-compose --version需要升级时,直接重复一遍覆盖下载的过程,替换版本号即可。删除时,删掉/usr/local/bin/docker-compose这个文件就行。
还要留意一个坑:独立二进制版默认不在docker compose的 PATH 搜索范围内,它和插件版的docker compose是两条平行的命令,二者并不互相感知。我不建议同一台机器上同时装插件版和独立二进制版,容易混淆,也会让后续维护脚本的人困惑到底用的是哪一份配置。
3.3 版本选择策略:到底该追新还是求稳
不管走插件还是独立二进制,都会遇到版本选择的问题。
插件版的版本由 Docker Engine 的 apt 源决定,默认给你装的就是官方仓库中的最新稳定版,基本不需要自己指定。如果你真的需要锁定版本,可以先用apt-cache policy docker-compose-plugin查看可用版本,再通过sudo apt install docker-compose-plugin=版本号来指定。
独立二进制版则可以随意选择。根据我的经验:
- 生产环境,优先选择最近半年内发布的稳定版,不要一上来就追刚出的版本,也没必要执着于最新版。
- 开发测试环境,直接用最新版问题不大。
- 如果现有项目里的 docker-compose.yml 是好几年前写的,遇到语法报错时,优先检查 Compose 版本和格式声明是否匹配,不要急着骂写文件的人。Compose 规范版本有 v2、v3 的区别,部分字段在不同版本间有兼容性差异。
4. 装完之后的三个基础配置,建议顺手做掉
4.1 把当前用户加入 docker 组,省掉每次敲 sudo
默认情况下,执行docker命令需要 root 权限,否则会报permission denied trying to connect to the docker daemon socket。这个设计是出于安全考虑,因为能操作 Docker 的用户基本等同于能操作整个主机。
不过对于个人开发机,每次都打 sudo 实在太繁琐。标准做法是把用户加入docker用户组:
sudo usermod -aG docker $USER然后注销重新登录,或者执行newgrp docker使组权限立即生效。之后直接运行docker ps,不再需要 sudo。
这里有个安全提示真的值得多说一句:把用户加入 docker 组,等价于授予该用户 root 权限,因为可以通过挂载主机目录等方式完全控制宿主机。如果你配置的是多人共用的服务器,不要轻易给不信任的账号加 docker 组权限。
4.2 镜像拉取慢的通用解决思路,配置 registry mirror
很多人在 Ubuntu 上装完 Docker 后,第一个明显的痛点就是“镜像下载慢”。这个问题在国内网络环境下尤为突出,拉一个几百 MB 的镜像可能要等上几分钟甚至超时。常规的解决办法是给 Docker 配置镜像加速器,也就是 registry mirror。
Docker 官方文档中,registry mirror 的作用是让 Docker 守护进程先从配置的镜像源拉取镜像,如果目标仓库有缓存,就能明显加速。需要注意的是,镜像加速器通常只覆盖 Docker Hub 上的公共镜像,拉取 ghcr.io、quay.io 这类第三方仓库里的镜像时不会生效。
配置步骤如下:
sudo mkdir -p /etc/docker sudo tee /etc/docker/daemon.json <<EOF { "registry-mirrors": ["https://your-mirror-address"] } EOF然后重启 Docker 服务:
sudo systemctl daemon-reload sudo systemctl restart docker关于镜像加速地址的选择,我的建议是不要随便找一个来路不明的地址填上去。比较可靠的来源是各云服务商容器镜像服务页面,注册后可以获取专属格式的加速地址,个人用户免费额度足够开发使用。如果你的服务器本身就在云厂商内网,可以直接尝试云厂商提供的默认内网加速域名。
改完配置后可以用docker info查看 Registry Mirrors 一栏是否生效,再拉一个实际镜像测试速度。
4.3 日志上限配置,防止磁盘被容器日志塞满
容器默认会无限收集标准输出日志,如果应用打印信息比较多(比如 Nginx 访问日志、Java 应用错误栈),时间一长容易把磁盘撑爆。这是我在服务器上踩过最实在的坑之一。
推荐在daemon.json中统一设置日志轮转策略:
{ "log-driver": "json-file", "log-opts": { "max-size": "10m", "max-file": "3" } }这个配置限制单个容器日志文件最大 10MB,最多保留 3 份,超出后自动滚动清理。设置完成后重启 Docker 服务再创建容器时生效,旧容器如果在创建时显式指定了 log 参数则不受全局配置影响。
5. 常见问题与排查实录
5.1 权限类问题:docker 命令报权限不足
报错示例:
docker: permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock排查思路分两步。
第一步确认 Docker 服务是否正常运行:
systemctl status docker如果服务没跑起来,先sudo systemctl start docker再试。服务正常运行却报权限不足,那就是当前用户在 docker 组的问题。
groups $USER sudo usermod -aG docker $USER修改组之后,务必要重新登录终端或执行newgrp docker,否则当前会话里组信息不会刷新。
5.2 网络与密钥问题:apt update 卡住或密钥报错
添加 Docker 官方仓库后执行sudo apt update,有时会出现两种情况。
一是仓库地址连接超时,卡在一个百分数不动。这种通常是服务器到 download.docker.com 的网络链路不稳定。可以尝试切换到 mirror 地址。网络上常见的做法是更换成适合自己网络环境的 Docker 镜像站来做为 apt 源,但这样做的代价是更新不及时,而且来源可靠性需要你自己判断。我的建议是先多试几次,再看是不是系统代理配置的问题。如果服务器有 HTTP 代理,apt 默认不读http_proxy环境变量,需要在/etc/apt/apt.conf.d/下配置Acquire::http::Proxy。
二是密钥相关报错:
The following signatures couldn't be verified because the public key is not available: NO_PUBKEY xxxxx这种多半是因为/etc/apt/keyrings/docker.gpg没有正确读取。先确认文件是否存在、权限是否能被 apt 读取。可以用sudo chmod a+r /etc/apt/keyrings/docker.gpg强制修正。如果还是不行,重新执行一遍下载密钥的命令即可。
5.3 验证阶段报错:hello-world 镜像拉不下来
执行sudo docker run hello-world时卡在 Pulling 阶段,或者报timeout、TLS handshake timeout之类的错误,原因基本都是 Docker Hub 的默认仓库连接不畅。
解决办法就是参照 4.2 的 registry mirror 配置,配好镜像加速地址后重启 Docker 再试。如果改完依然超时,可以用docker pull hello-world单独拉一次看具体报错,然后再逐项排查 DNS、防火墙、代理等。
另外我还遇到过一种情况,daemon.json格式写错了(比如多了一个逗号),Docker 服务直接启动失败。排查时可以查看 Docker 服务日志:
sudo journalctl -u docker -n 50如果输出里提示 daemon.json 解析错误,修正 JSON 格式后重启即可。
5.4 版本不兼容:docker-compose.yml 格式报错
有时从网上复制了一份 docker-compose.yml,执行docker compose up -d时直接报错:
services.web Additional property xxx is not allowed这种大概率是版本声明和实际 Componse 版本不匹配。Compose 规范在 v2 和 v3 之间废弃了一些字段,比如version在较新版本里已经不再推荐显式声明。遇到这种问题,我一般处理方式是:先把文件顶部的version:字段去掉,因为 Compose 插件会根据命令推断格式;如果还报错,就逐行检查出错的 service 字段是否拼写正确,特别是缩进,YAML 对空格非常敏感。
调试时可以用docker compose config来校验文件语法,这条命令会解析整个 Compose 文件并输出最终配置,不启动任何容器,是排查格式问题最好的工具。如果不输出内容,说明docker-compose.yml没有问题。
5.5 一个容易被忽略的点:重启系统后 Docker 服务消失
如果你装了 Docker 之后没有执行过systemctl enable docker,重启机器后 Docker 服务不会自动启动,届时所有容器都会变成“停止”状态。这个问题在新手环境里非常常见。装好之后我总会先确认 enable 状态:
sudo systemctl is-enabled docker输出enabled表示已设置开机自启。如果是disabled,执行:
sudo systemctl enable docker如果容器是通过 docker-compose 部署的,等 Docker 启动后还需要docker compose start拉起对应项目中的服务。注意,start和up的区别在于,start不会读取文件里的构建或映射变更,只会启动已存在的容器,适合系统重启后的恢复场景;up则适合首次部署或配置变更后的更新场景。
写在最后的一点个人体会
Docker 和 docker-compose 的安装本身其实不难,难的是装完之后整个环境是否真正顺手、后续升级维护是否省心。我推荐的方式总结下来就一句话:走 Docker 官方 apt 仓库安装最新稳定版 Docker Engine,同时带上 compose 插件包,装完立刻配上用户组、镜像加速和日志上限三件事。这套组合我在多台 Ubuntu 20.04、22.04、24.04 服务器上验证过,既能满足日常开发需求,也适合生产环境长期运行。
最后再分享一个个人习惯:每次安装完 Docker 后,我会顺手写一个docker-compose.yml测试文件,里面放一个 Nginx 服务和一个 PostgreSQL 服务,跑一遍docker compose up -d并确认两个容器都能正常启动。这个动作耗时不到两分钟,却能一次性验证网络、镜像源、端口映射、卷挂载、服务健康检查等多个环节是否正常。比起等到真正部署业务时再发现问题,这个提前检查的方式能省下很多排查时间。如果你准备装完就投入实际项目,强烈建议也做一次这样的快速验证。