Ubuntu安装Docker与Docker Compose全攻略:从原理到实战避坑
最近这阵子好几个朋友都在折腾Ubuntu要么是给老笔记本装了双系统要么是在虚拟机上开了一台纯净的服务器版做实验。结果装完系统后第一个动作几乎都一样跑来问我Docker怎么装。确实现在很多服务的标准交付方式就是容器不管是搭个个人博客、跑个MySQL、还是部署一套GitLabDocker基本绕不开。而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 composeV2插件依赖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-plugin2.23.0~ubuntu-~jammy不过具体版本号需要先通过apt-cache policy docker-compose-plugin查看可用版本列表。例如apt-cache madison docker-compose-plugin根据你使用的Ubuntu版本前缀会不一样比如jammy对应22.04noble对应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://你的加速地址] }保存后重启Dockersudo 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 ar /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.04Docker官方源提供的版本会随时间变化。如果你看到的是2.20或更早建议通过 apt update 升级源后再安装或者用前面提到的指定版本方式。如果你更习惯传统用法也可以创建一个别名让自己既能用docker compose也能用docker-composeecho alias docker-composedocker 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 couldnt 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-plugin2.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-serverutf8mb4 --collation-serverutf8mb4_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不要急着把一堆容器全部用--restartalways跑起来先在一个临时目录里测试Compose文件语法是否正确。可以执行docker compose config这个命令会校验并打印最终的Compose配置不会启动任何容器相当于语法检查。每次改完YAML先执行这个命令再up能帮你省下大量调试时间。另外如果你在虚拟机或云服务器上装Docker记得确保硬件虚拟化没有把CPU的虚拟化功能关掉否则某些需要嵌套虚拟化的容器比如Android模拟器可能跑不起来但这和Docker本体安装无关就不展开说了。