最近这阵子好几个朋友都在折腾Ubuntu,要么是给老笔记本装了双系统,要么是在虚拟机上开了一台纯净的服务器版做实验。结果装完系统后第一个动作几乎都一样:跑来问我Docker怎么装。确实,现在很多服务的标准交付方式就是容器,不管是搭个个人博客、跑个MySQL、还是部署一套GitLab,Docker基本绕不开。而Ubuntu又是很多人的首选Linux发行版,所以“Ubuntu上把Docker和Docker Compose装好”这个需求,几乎可以算是现代Linux玩家的入门必修课。
这篇内容我会把自己实际操作中验证过的完整流程、安装方式选型的底层逻辑、以及各种奇奇怪怪的坑都写出来。适合刚接触Linux的人跟着一步步操作,也适合已经装过Docker但被版本问题、镜像拉取慢、Compose用不了等问题卡住的老手直接跳到对应章节抄答案。我的目标很简单:不管你用Ubuntu 20.04、22.04、24.04还是最新的26.04(如果你已经勇敢地用上了),看完之后都能得到一个干净、能用、配置合理的Docker环境。
1. 整体设计与思路拆解
1.1 为什么我建议在Ubuntu上使用原生Docker Engine,而不是Docker Desktop
经常有人问,为什么不在Ubuntu上装Docker Desktop?毕竟Windows和macOS上大家习惯用Docker Desktop,图形界面操作起来很直观。但这里有个关键区别:Docker Desktop在Linux上其实是“套娃”方案,它本质上还依赖底层的Docker Engine,但多加了一层虚拟机管理逻辑(现在新版本虽然可以不用虚拟机了,但仍然和你直接装原生Docker没有本质区别)。更重要的是,生产环境、服务器场景下几乎没人用Docker Desktop,所有人都是直接部署Docker Engine。既然你想认真学Linux下的容器,那就一步到位,直接使用命令行操作的原生Docker Engine。
另外,Ubuntu官方软件源里的docker包很老,甚至在一些版本里压根没有独立的Docker Engine包(之前有个叫docker.io的包是Debian维护的,但版本滞后)、所以最合理的方式是使用Docker官方维护的apt源。这样做的好处是:Docker官方源会即时更新、支持你指定安装特定版本、能够同时安装Compose插件。虽然国内访问Docker官方源偶尔有网络波动,但可以通过配置镜像站点的方式解决,这个我后面会详细说。
1.2 安装方式选型:官方apt源、get.docker.com脚本还是二进制包
Docker Engine在Linux上常见的安装方式就那么几种:
- 官方apt源安装:最推荐,便于用apt管理、升级,因为到时候你执行apt upgrade就能顺带更新Docker。
- get.docker.com脚本:官方提供的一键脚本,实际就是给你配置好apt源然后安装,适合不想手动敲太多命令的人,但脚本内部对系统会做很多自动判断,出问题时不太好控制。
- 二进制包/离线包:适合完全离线的内网环境,需要自己去下载对应架构的二进制压缩包,然后手动配置systemd服务,比较繁琐,不推荐新手。
个人装过几十台机器后的结论是:如果你能联网,老老实实用官方apt源配合软件源里提供的 docker-ce、docker-ce-cli、containerd.io 和 docker-compose-plugin 这四个套件。这套组合几乎覆盖了所有日常需求。这里的docker-compose-plugin至关重要,因为新版Docker Compose已经变成Docker CLI的一个插件,命令是 docker compose(中间有空格),而不是以前独立的 docker-compose 命令。很多人装完发现 docker-compose 不存在,多半就是不清楚这个插件机制。
1.3 版本规划:Docker Engine 与 Docker Compose 版本对应关系
Docker Engine的版本号目前是24.x、25.x、26.x、27.x这样滚动的,而Docker Compose插件是单独发布版本号,两者并不强制绑定。但有个实用经验:同一时间安装的Docker Engine和Compose插件版本最好保持在一定的新旧范围内,避免出现docker compose命令不可用或API版本不匹配的问题。
更准确地说,docker compose(V2插件)依赖Docker Engine的API版本,虽然V2插件会自动协商API版本,但如果你用的是非常老旧的Docker Engine(比如18.09、19.03)、新版的Compose V2可能跑不起来,我在旧机器上就遇到过类似情况。所以我的原则是:尽量使用当前主流的Docker Engine版本,并且通过docker compose version命令查看Compose版本,确保它是2.20.0以上。后面我会给出具体更新到2.23以及指定特定版本的方法。
另外需要提一下,很多网络教程还停留在让你去GitHub下载 docker-compose-linux-x86_64 这个二进制的时代。现在完全没必要这样干了。从Docker 20.10开始,Compose V2已经以插件形式内置发布,你用apt装Docker的时候顺便就能装好。直接从GitHub下载二进制的方式容易踩权限、架构、路径的坑,而且后续升级麻烦。我建议把它当成备用方案来理解,但不要作为首选操作。
2. 核心细节解析与实操要点
2.1 Ubuntu系统准备:更新软件源、安装依赖和基础工具
准备工作看似简单,但很多安装失败都是因为这一步偷懒导致的。在开始之前,你需要确保系统能够正常访问Ubuntu软件源,并更新到最新的软件包信息。
首先要做的是:
sudo apt update sudo apt upgrade -y这里的apt update是刷新软件包索引,apt upgrade则是把系统已有的软件包升级到软件源中的最新版。升级耗时可能比较久,取决于你网络速度和系统原有包的数量。千万别跳过upgrade直接装Docker,否则可能因为依赖库版本旧导致Docker无法启动。
接下来安装一些常用的基础工具:
sudo apt install -y ca-certificates curl gnupg lsb-release这些工具是干嘛的呢?ca-certificates用来验证HTTPS证书,curl用来下载GPG密钥,gnupg用来导入和验证密钥,lsb-release用于识别当前系统的发行版版本代号。而这些正是添加Docker官方apt源所必需的前置条件。后面在添加源时我们会用到它们的。
如果你的Ubuntu是24.04或更新版本,系统默认可能已经装了curl和ca-certificates,但多执行一遍也无妨。
2.2 Docker Engine安装的三种方案对比
在动手之前,先把方案对比清楚,这样你就明白自己正在执行的操作到底在做什么。
| 方案 | 命令/方式 | 优点 | 缺点 | 建议 |
|---|---|---|---|---|
| 官方apt源 | 添加Docker源后安装docker-ce | 版本新、升级方便、官方支持 | 需要先配置GPG密钥和源文件 | 首选 |
| 一键脚本 | curl -fsSL https://get.docker.com | sh | 命令少、适合快速安装 | 不便于控制版本、脚本逻辑不透明 | 仅限临时体验 |
| 手动二进制 | 下载压缩包解压、配systemd | 可离线安装、可定制 | 步骤多、升级麻烦 | 不推荐新手 |
我强烈推荐方案一。虽然需要多敲几条命令,但对系统侵入更可控,后面升级维护也简单。而且脚本方式安装的Docker源和方案一其实是同一个,只是它自动帮你配置好了,如果你能接受它自动添加源的行为,其实也无妨。但我见多了脚本执行到一半网络断开导致残留垃圾的情况,所以还是手动来吧。
2.3 Docker Compose 插件安装与升级的核心逻辑
前面说到新版Docker Compose是Docker CLI插件,所以安装Docker Compose最优雅的方式就是安装Docker官方源里那个docker-compose-plugin包。
安装命令非常简单:
sudo apt install -y docker-compose-plugin安装后执行:
docker compose version如果输出了类似Docker Compose version v2.24.2的内容,说明已经成功。注意这里输出的版本号前缀是v2.x.x,这一点很重要。
但很多人会遇到一种情况:系统里本身已经有一个名为docker-compose的旧版二进制文件(通常是V1版本,Python编写,版本号是1.29.2之类),或者是之前通过pip安装的docker-compose。此时你执行docker compose和docker-compose可能会得到完全不同的版本,甚至相互冲突。我的建议是,确定部署环境中的主要版本,最好是统一使用V2插件,并把旧V1二进制卸掉:
sudo apt remove docker-compose如果你确实需要把Compose插件更新到指定新版本(比如热搜里面出现的2.23版本),可以通过如下方式:
sudo apt install -y docker-compose-plugin=2.23.0~ubuntu-~jammy不过具体版本号需要先通过apt-cache policy docker-compose-plugin查看可用版本列表。例如:
apt-cache madison docker-compose-plugin根据你使用的Ubuntu版本,前缀会不一样,比如jammy对应22.04,noble对应24.04。照着输出的版本号选择即可。
顺带提一下,当你只需要更新Compose插件时,直接执行:
sudo apt install -y --only-upgrade docker-compose-plugin这个命令会只升级Compose插件,不动Docker Engine本体,适合在不确定的情况下安全更新。
2.4 配置镜像加速器:解决Docker镜像下载慢的核心手段
刚装好Docker,很多人兴致勃勃想拉一个镜像,结果发现速度惨不忍睹。这里的原因不用多说,但解决方法非常明确:给Docker daemon配置一个国内的镜像加速地址。这个地址可以是云服务商提供的加速器地址,比如“你自己的加速器地址”;由于加速器地址每个人不一样,需要在相关云服务商的控制台获取。如果你没有云服务商账户,也可以用一些公共的镜像源,但公共源可能不稳定,我更推荐你在自己的云服务商控制台找到专属地址。
关键是配置文件格式。Docker守护进程会读取 /etc/docker/daemon.json 文件,如果这个文件不存在就新建。内容如下:
{ "registry-mirrors": ["https://你的加速地址"] }保存后重启Docker:
sudo systemctl daemon-reload sudo systemctl restart docker验证是否生效:
docker info在输出中找“Registry Mirrors”这一行,如果列出了你配置的地址就代表成功了。注意,这里的daemon.json还可以同时配置日志大小、存储驱动等参数,我后面会分享一个比较实用的组合配置。
3. 实操过程与核心环节实现
3.1 从头开始:逐步安装Docker Engine(基于官方apt源)
假定你的Ubuntu已经完成了系统更新和基础工具安装,现在开始正式的安装流程。
第一步,添加Docker官方GPG密钥:
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这里解释一下每个动作:首先创建 /etc/apt/keyrings 目录,如果目录已经存在不会报错;然后用curl下载Docker官方GPG公钥,并通过 gpg --dearmor 命令把公钥转换为二进制存储格式,放到指定路径;最后给这个文件添加读权限,让apt可以读取。
第二步,添加Docker的apt软件源。这一步很关键,建议使用下面的方式,它根据你的Ubuntu版本自动选择对应代号:
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$(dpkg --print-architecture) 会自动识别系统架构(amd64、arm64等),$(lsb_release -cs) 会自动识别Ubuntu版本代号(如jammy、noble)。这样写的好处是,将来系统升级或在不同机器上复制粘贴时不需要手动改。
第三步,更新源并安装Docker套件:
sudo apt update sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin这里我加了docker-buildx-plugin,它用于构建多架构镜像,属于现代Docker的标准配置。如果你只打算跑别人的镜像,buildx可以不装,但我建议还是装上,免得到时候用Dockerfile构建镜像时发现缺少指令。
安装完成后,启动Docker并设置开机自启:
sudo systemctl enable docker --now上面的命令等效于先执行systemctl enable docker再执行systemctl start docker,一条命令搞定。通过systemctl status docker查看状态,看到active (running)就对了。
3.2 安装和验证Docker Compose插件
如果你按照上面的命令执行了,docker-compose-plugin已经一并装好了,无需再单独操作。此时执行:
docker compose version正常情况下输出类似:
Docker Compose version v2.30.3注意,我当前环境是Ubuntu 24.04,Docker官方源提供的版本会随时间变化。如果你看到的是2.20或更早,建议通过 apt update 升级源后再安装,或者用前面提到的指定版本方式。
如果你更习惯传统用法,也可以创建一个别名,让自己既能用docker compose也能用docker-compose:
echo "alias docker-compose='docker compose'" >> ~/.bashrc source ~/.bashrc不过这不是必须的,只是为了照顾肌肉记忆。
3.3 配置用户组:让当前用户免sudo使用Docker
每次敲docker命令都要带sudo,非常烦人。而且更麻烦的是,在Compose文件中如果宿主机文件权限处理不当,sudo执行和普通用户执行会产生不同的文件归属,容易让人困惑。
解决办法是将当前用户加入docker用户组:
sudo usermod -aG docker $USER然后关键的一步是:你必须重新登录系统,或者执行newgrp docker命令切换用户组,否则当前shell不会生效。最简单的方式是注销重新登录,或者重启虚拟机。执行完后用id命令验证:
id输出中应该包含docker字样。然后直接执行:
docker ps如果不再提示permission denied,就说明成功了。
这里有个安全提醒:加入了docker组的用户相当于拥有了root权限,因为docker的socket控制能力极强。所以请只把自己信任的用户加入docker组,不要在多人共享的服务器上随意给用户加这个权限。
3.4 首次运行验证:用hello-world和Nginx确认环境OK
安装完成之后,第一件事就是跑一个hello-world镜像验证Docker引擎是否能正常工作。
docker run hello-world如果一切正常,会看到一段英文提示,说明你的安装是成功的。但hello-world只是一个很小的测试镜像,我建议再拉一个Nginx镜像并启动容器来做一次更实际的测试,同时验证网络和端口映射是否正常:
docker run -d --name test-nginx -p 8080:80 nginx:alpine然后执行docker ps,如果看到STATUS为Up,就可以用浏览器访问 http://你的UbuntuIP:8080,看到Nginx欢迎页就算大功告成。测试完删除容器:
docker rm -f test-nginx这样你就完成了一个完整的“拉镜像→创建容器→访问服务→清理容器”的流程。接下来可以放心去玩其他镜像了。
4. 常见问题与排查技巧实录
4.1 添加Docker源时GPG报错、依赖无法安装
这个问题在我帮别人排查时出现频率最高。现象通常是执行apt update后,apt提示:
The following signatures couldn't be verified because the public key is not available: NO_PUBKEY 4D1A8B5D...原因很简单:你系统中缺少Docker官方源的GPG公钥。解决方法是手动下载并导入这个公钥。注意不要再用旧的sudo apt-key add方式了,那是被废弃的。正确的做法是重新执行前面添加GPG密钥的那三步,或者从报错里拿到KEY_ID手动添加:
sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg <(curl -fsSL https://download.docker.com/linux/ubuntu/gpg)还有一个常见错误是执行apt install时报依赖不满足,比如“需要containerd.io >= 1.4.3”但系统里的版本更低。这种通常是因为没有先执行apt update,导致apt看到了老的索引。解决办法:
sudo apt update sudo apt install -y docker-ce docker-ce-cli containerd.io如果还是不行,可以尝试清理本地apt缓存再更新:
sudo apt clean sudo apt update4.2 镜像拉取超时、加速器不生效
拉取镜像时卡住不动,或者报“dial tcp: i/o timeout”,基本可以确定是网络访问镜像仓库的问题。如果你已经按照2.4节配置了daemon.json,但docker info里还没看到Registry Mirrors,就要检查文件路径和JSON格式是否正确。比如我见过有人把 daemon.json 写成了 daemon.json.txt,或者用记事本编辑保存后带上了BOM头,都会导致Docker无法解析。
配置完加速器之后,记得重启:
sudo systemctl daemon-reload sudo systemctl restart docker如果你用的是云服务器,由于安全组策略,部分对外访问端口可能被限制,但这不影响主动拉取镜像。此外,如果你配置了多个镜像加速地址,Docker会按顺序尝试,当前面的地址连接不上时自动切换。我建议至少配置两个或三个,防止某个地址抽风。
不过也得说明白一点:即使配置了加速器,某些特殊镜像(比如超大的AI模型镜像)拉取速度依然可能不乐观,这是正常的。可以适当调整镜像拉取的并发数,不过这属于进阶调优,日常使用没必要折腾。
4.3 Docker Compose版本更新到指定版本的方法
我已经在前面提到,可以通过apt安装或升级docker-compose-plugin。但如果你确实想用传统方式手动更新到2.23版本,也可以这么做。先在GitHub官方Release页面找到对应版本,然后根据你的CPU架构下载二进制文件,比如x86_64的机器:
sudo curl -L "https://github.com/docker/compose/releases/download/v2.23.0/docker-compose-linux-x86_64" -o /usr/local/bin/docker-compose sudo chmod +x /usr/local/bin/docker-compose然后验证:
docker-compose --version这种方法其实是V2的独立二进制形态,安装后可以使用docker-compose命令。但我建议优先用插件方案,因为在apt体系下,Compose、Engine、CLI等组件版本可以互相协调。手动下二进制虽然也能用,但容易和插件方式并存,导致你执行docker compose和docker-compose输出不同版本,后面维护时容易混乱。
如果你只是想更新插件到2.23版本,可以这样:
sudo apt update sudo apt-cache madison docker-compose-plugin找到对应版本后安装:
sudo apt install -y docker-compose-plugin=2.23.0~ubuntu-~jammy具体的版本号字符串以apt-cache从输出为准,不要抄我这里的示例,因为不同Ubuntu版本对应不同的后缀。
试过几次后你会发现,用apt管理Docker的好处就是,无论怎么更新都不会把系统搞乱,这一点非常省心。
4.4 Docker启动失败、daemon.json配置导致无法启动
如果你修改了/etcdocker/daemon.json后,执行systemctl start docker发现启动失败,可以先查看具体报错:
journalctl -u docker --no-pager | tail -50最常见的错误是JSON格式错误。一个很简单的校验方式是在本地执行:
python3 -m json.tool /etc/docker/daemon.json如果输出的是格式化后的JSON内容,说明格式没问题;如果报错,会提示具体的行和列。再比如,有时候你配置了 "iptables": false,会导致容器网络无法正常通信,我遇到过有人为了“优化”照抄网上的配置,最后所有容器都不能联网。请先搞清楚每个参数的含义再改,不要盲目配置。
下面给出一份比较实用的daemon.json配置,包含了镜像加速、日志大小限制和存储驱动设置:
{ "registry-mirrors": ["https://你的加速地址"], "log-driver": "json-file", "log-opts": { "max-size": "100m", "max-file": "3" }, "storage-driver": "overlay2" }这段配置的意义是:限制单个容器日志文件最大100MB、最多保留3个文件,防止Docker日志把磁盘占满;存储驱动使用overlay2,这是目前性能和兼容性比较好的方案。如果你不确定某个参数的含义,宁可不写,也不要乱写。
4.5 用docker-compose快速部署一个MySQL实例(扩展练习)
Docker本身安装好了,Compose也有了,如果不来点实际场景,总觉得少了点什么。这里我分享一个非常常见的场景:用docker-compose跑一个MySQL 8.0,并设置好密码和数据库。
创建一个项目目录并进入:
mkdir -p ~/mysql-demo && cd ~/mysql-demo创建一个docker-compose.yml文件:
services: mysql: image: mysql:8.0 container_name: mysql-demo restart: always environment: MYSQL_ROOT_PASSWORD: yourpassword MYSQL_DATABASE: demo ports: - "3306:3306" volumes: - ./data:/var/lib/mysql command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci然后启动:
docker compose up -d等待拉取镜像和启动容器完毕后,用如下命令验证:
docker ps docker exec -it mysql-demo mysql -uroot -p输入你设置的密码后,就能进入MySQL控制台了。
这里有几个细节值得说明。volumes把宿主机当前目录下的data挂载到容器内的/var/lib/mysql,这样即使删掉容器,数据库文件也不会丢。command后的参数是设置MySQL默认字符集为utf8mb4,避免出现中文乱码。restart: always保证Docker重启后MySQL容器自动拉起,省去手工启动。
如果你发现“3306端口被占用”,说明宿主机上已经有一个MySQL或MariaDB服务占用了这个端口。可以修改ports为“3307:3306”,然后用3307端口访问容器内的MySQL。
这个例子很小,但足够让你体会docker-compose带来的便利:一个yaml文件,一条命令,整个应用环境就起来了。后面你部署Redis、GitLab、Jumpserver等都能用同样的思路。
最后再分享一个我在实际使用中踩过的坑:刚装好Docker,不要急着把一堆容器全部用--restart=always跑起来,先在一个临时目录里测试Compose文件语法是否正确。可以执行:
docker compose config这个命令会校验并打印最终的Compose配置,不会启动任何容器,相当于语法检查。每次改完YAML,先执行这个命令再up,能帮你省下大量调试时间。另外,如果你在虚拟机或云服务器上装Docker,记得确保硬件虚拟化没有把CPU的虚拟化功能关掉,否则某些需要嵌套虚拟化的容器(比如Android模拟器)可能跑不起来,但这和Docker本体安装无关,就不展开说了。