装Docker这件事,网上教程一搜一大把,但每次看到有人一上来就sudo apt install docker.io一把梭,我都替他们捏把汗。不是说这个命令不能用,而是Ubuntu软件源里的Docker版本通常落后官方好几个大版本,等你踩坑的时候拿着新版本的文档去查,根本对不上号。这篇文章我基于Ubuntu 22.04 LTS,把从环境检查、选型、安装、验证到排错的完整过程记录下来,内容也覆盖Docker Desktop在Ubuntu上常见的KVM报错、虚拟机里装Docker的注意事项。写完这套流程之后你就能自己判断:你的机器到底该装Docker Engine还是Docker Desktop,装完怎么验证,出问题了怎么查,后面再装MySQL 8.0、Redis主从、Nginx这些常规容器也不会再被基础环境卡住。
1. 动手之前先看清这三件事:版本、架构、内核
很多人Docker装失败,根子不在安装步骤,而是第一步就没做环境检查。我见过有人在20.04上用22.04的教程,也见过arm64的机器照着x86_64的帖子敲命令,还有内核太老导致overlay2存储驱动起不来的。这些问题都能通过提前检查规避,几分钟的事,却能省下后面几个小时的排查时间。
1.1 系统版本要求:不是所有Ubuntu都适合装
Docker官方对Ubuntu的支持范围是明确的:目前主流的22.04 LTS、20.04 LTS、24.04 LTS都在支持列表里,更早的18.04虽然还能装,但官方已经不再积极维护,建议重装系统而不是在老版本上折腾。这里有个判断标准:用lsb_release -a看版本号,凡是带LTS的长期支持版本优先选择;如果是22.10、23.04这种中间版本,能升级就升级,不能升级就换LTS,否则后续Docker升级很容易碰到依赖冲突。
lsb_release -a输出里Codename如果是jammy(22.04)或者noble(24.04),就没问题。这里有个细节很多人不知道:Docker官方apt源是按Codename分发软件包的,老系统的Codename不在官方仓库里,直接add-apt-repository之后apt update会报404,这也是安装时最容易卡住的地方之一。后面第6章我会专门讲这个排查链路。
1.2 系统架构:x86_64还是arm64,后续差别很大
Ubuntu可以跑在x86_64、arm64、ppc64le等架构上,最常见的是前两种。查看架构:
uname -mx86_64对应Docker仓库里的amd64,arm64对应aarch64。这个信息决定了你下载的软件包、拉取的镜像标签。比如在树莓派或某些ARM开发板上装Docker,拉镜像时要写arm64v8/ubuntu而不是ubuntu,后者默认拉取的是amd64版本,虽然也能跑(有模拟层),但性能和兼容性都打折扣。同理,在x86_64机器上就没必要刻意写amd64前缀,默认行为就是对的。
1.3 内核与网络转发检查
Docker依赖Linux内核的cgroups、namespaces、overlayfs这些特性。内核太老,Docker虽然能装,但跑容器时会报各种诡异错误。检查方法:
uname -r建议内核版本在5.x以上,Ubuntu 22.04默认内核是5.15,完全够用。再看overlayfs支持:
cat /proc/filesystems | grep overlay有overlay输出就说明没问题。如果输出为空,大概率是系统太老或者用了特殊内核,安装后只能把存储驱动改成vfs,但性能会差很多,建议还是先升级系统。
另外,确认系统包管理器状态正常:
sudo apt update如果这一步就报错,先解决apt源的问题再往下走,不要在依赖破损的基础上安装。这一步看似废话,实际上我在云主机和旧服务器上遇到过不止一次,用户源里残留了失效的PPA导致update直接失败,这时候装什么都白搭。
2. Docker Engine、Docker Desktop和rootless模式,到底装哪个
这是安装前最容易迷糊的问题。Ubuntu上安装Docker,实际有三条路线,很多人被网络上的教程搞混,装了一个不适合自己的东西,然后开始怀疑是Docker的问题。
2.1 三条路线的核心差异
Docker Engine是纯命令行工具,包含dockerd守护进程和docker CLI,占用资源小,适合服务器和开发机,是Linux上的标准选择。在服务器上装Docker,指的就是它。
Docker Desktop是带图形界面的桌面应用,Linux版也有。它内部其实跑了一个虚拟机,用来承载容器和内置的Kubernetes环境,资源占用比Engine高一截,主要面向需要图形管理界面、需要在Linux和macOS/Windows之间保持一致的开发体验的人群。见过有人在2核4G的服务器上装Docker Desktop,开机内存就吃掉一大半,纯属给自己找麻烦。
rootless模式是让dockerd以普通用户身份跑在用户态,不需要root权限,安全性和隔离性更好。代价是端口映射、overlay网络有一些限制,配置复杂度也高一些,适合对安全要求高、不想给普通用户root权限的机器。
三条路线的对比如下:
| 方案 | 资源占用 | 是否需要root | 适用场景 |
|---|---|---|---|
| Docker Engine | 低 | 是(守护进程) | 服务器、普通开发机 |
| Docker Desktop | 高 | 是(内部虚拟机) | 桌面开发、需要内置K8s |
| rootless模式 | 中 | 否 | 安全敏感、多用户机器 |
2.2 不同场景的选型建议
普通服务器、云主机:装Docker Engine,一步到位。带桌面环境的Ubuntu个人电脑,日常做开发学习:Docker Engine完全够用。除非你特别需要图形界面,或者要开发Kubernetes相关的东西(Docker Desktop自带单机K8s),否则没必要多花一倍内存。
这里提醒一句,网上很多教程标题写着"Ubuntu安装Docker",正文却是在教下载Docker Desktop的deb包,很多新手装完发现多了一堆东西,卸载也麻烦。看清自己的需求再动手。另外,Docker Desktop对大型企业收费,个人和小企业免费,但Engine本身一直保持开源,对绝大多数人来说,Engine才是"Docker"本体。如果你在虚拟机里的Ubuntu上装Docker,并且只打算跑普通容器,就不要考虑Docker Desktop那条线了,它依赖KVM嵌套虚拟化,配置起来麻烦,也没必要。这个话题我在第6章还会展开。
3. 官方apt仓库安装全流程:从卸载旧版到写对源
确定装Docker Engine之后,推荐的方式是用官方apt仓库安装。这一步做对了,后面所有事情都顺畅。
3.1 为什么不用apt install docker.io
Ubuntu的软件源里其实有一个docker.io包,装上之后也能跑Docker,很多人图省事直接装它。问题在于:这个包是Ubuntu发行版维护的,版本通常比Docker官方源滞后很多,而且不一定跟最新的Docker CLI、containerd配套。你以后想升级到新版本,或者用到buildx这类新特性,就会卡在版本上。说白了,apt install docker.io适合那些对Docker版本无所谓的场景,但凡你想正经用Docker做开发或部署,都建议走官方源。
3.2 彻底清理旧版本
如果系统里曾经装过Docker或相关组件,先清理干净再装,避免冲突:
sudo apt remove docker docker-engine docker.io containerd runc注意,这条命令不会删除镜像、容器、卷和自定义配置文件,它们默认放在/var/lib/docker下面。想彻底清掉再执行:
sudo rm -rf /var/lib/docker sudo rm -rf /var/lib/containerd不过如果你是想保留旧数据做升级,千万别执行这两条rm。我见过有同事在升级前手滑执行了清理,结果几十个容器的数据全没了。正式环境操作前先备份/var/lib/docker,这比什么技巧都重要。
3.3 添加GPG密钥和仓库的正确姿势
首先安装必要的依赖工具:
sudo apt update sudo apt install ca-certificates curl然后创建密钥目录并导入Docker官方GPG公钥:
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 a+r /etc/apt/keyrings/docker.asc这里用install -d创建目录的好处是自动处理权限,省得后面单独chmod。然后把Docker源写入apt源列表。
echo \ "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \ $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \ sudo tee /etc/apt/sources.list.d/docker.list > /dev/null这段命令体现了两个自动化的点:$(dpkg --print-architecture)自动识别架构,$(. /etc/os-release && echo "$VERSION_CODENAME")自动识别版本代号,避免手写写错。如果你是从Ubuntu旧版本升级上来的系统,VERSION_CODENAME可能还是旧代号,这时要手动改成目标版本代号,否则apt update会报404。这个细节我在第6章会用一个完整的排查案例来讲。
3.4 安装包组成与版本固定
更新索引并安装:
sudo apt update sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin这里解释一下每个包的作用:docker-ce是社区版核心守护进程,docker-ce-cli是命令行工具,containerd.io是容器运行时,docker-buildx-plugin是buildx构建插件,docker-compose-plugin是compose插件。早期版本compose要单独装,现在官方推荐直接装插件版,命令是docker compose,中间有个空格,别跟老的docker-compose搞混。
安装完成后先不急着启动,看一下仓库里都有哪些版本:
apt-cache madison docker-ce | head -5这个命令列出所有可用版本,以后想固定版本安装或者回滚版本都用得上。如果只是想装默认最新版,上面那步就够了。装完确认守护进程能随系统启动:
sudo systemctl enable docker --now4. 装完别急着用:用户组、systemd服务与网络验证
很多教程到这里就喊"安装完成",实际上还差几步。直接跑docker命令大概率会踩权限坑,云主机上还会遇到容器没外网的问题。
4.1 docker组权限配置与安全边界
装完Docker后,直接用普通用户执行docker ps,最常见的报错是:
permission denied while trying to connect to the Docker daemon socket原因是docker CLI默认通过/var/run/docker.sock与守护进程通信,这个socket归root所有。解决办法是把当前用户加入docker组:
sudo usermod -aG docker $USER然后重新登录(或者执行newgrp docker)让组权限生效。注意,把用户加入docker组,等价于授予该用户root权限,因为Docker可以挂载宿主机目录、执行特权容器、修改iptables。所以不要在多人共享的机器上随便加人,要么用rootless模式,要么严格限制docker组的成员。这一点在团队服务器上尤其重要,我见过因为docker组权限太随意,导致有人误删了别人的容器和卷。
4.2 服务状态与开机自启
Ubuntu上Docker通过systemd管理,装完包之后服务默认会启动。确认一下:
systemctl status docker如果显示active (running),没问题。如果没起来,看日志:
journalctl -u docker -n 50大多数情况下是网络或内核问题,先看报错再处理。设置开机自启:
sudo systemctl enable docker有些云镜像会默认禁用systemd服务,或者服务器托管平台有特殊的进程管理方式,这时要按平台文档处理。但绝大多数情况下,enable一下就够。
4.3 IP转发和防火墙这两个隐藏坑
Docker正常工作的前提是网络转发和iptables功能正常。检查IP转发:
sysctl net.ipv4.ip_forward输出应该是1。如果是0,编辑/etc/sysctl.conf加一行net.ipv4.ip_forward=1,然后sysctl -p生效。这个坑在云主机上很常见:有些云镜像默认关闭IP转发,装完Docker容器能启动但没外网,很多人排查半天发现是这里。
还有一个常见的冲突点:Ubuntu的ufw防火墙和Docker的iptables规则。Docker默认会操作iptables的DOCKER链,可能绕过ufw对容器端口的限制。如果你启用了ufw,装完Docker后要重新审视防火墙规则,尤其是容器端口暴露到公网的时候。这不是安装时的必选项,但每次装完Docker我都建议顺手确认一下,免得后面暴露端口出安全问题。
5. 跑通第一个容器,顺便做一次安装质量体检
装完不是终点,跑一个真正的容器并验证各项功能,才算安装完成。这一步我做得很固定,因为能一次性覆盖Docker的大部分核心链路。
5.1 hello-world到底验证了什么
docker run hello-world这个命令会从Docker Hub拉取hello-world镜像并运行一个打印信息的容器。它能验证:守护进程正常、CLI能连上守护进程、镜像拉取链路通了、容器创建和启动链路正常。但仅此而已,它不验证网络互通、端口映射、存储驱动。所以我的习惯是,hello-world通过后,再跑一个更深入的测试。
5.2 用nginx实测端口映射、外网与DNS
docker run -d --name web-test -p 8080:80 nginx:alpine然后访问宿主机IP的8080端口,看到nginx欢迎页,说明端口映射正常。接着验证容器内网络:
docker exec web-test ping 8.8.8.8 -c 2能通说明容器能访问外网。再测试DNS解析:
docker run --rm busybox nslookup baidu.comnginx镜像里可能没有nslookup,用busybox一个命令搞定,--rm参数让容器用完整理完自动删除,不留垃圾。
这一套下来,容器生命周期、端口映射、外网访问、DNS解析、镜像下载、容器删除都验证过了,基本可以断定Docker安装质量没问题。测试完清理:
docker rm -f web-test5.3 我的安装质量体检清单
我习惯用一组固定检查项做体检,不写死脚本,但每次都会过一遍:
docker version能同时显示Client和Server信息,只有Client没有Server,多半是daemon没起来docker info里Storage Driver显示overlay2,而不是vfsdocker info里Server Version和Client Version一致docker run --rm alpine echo ok能正常输出okjournalctl -u docker里没有持续报错
其中第一条特别值得强调。很多人装完docker version显示正常,但仔细一看只有Client段,Server段全是报错,那是因为CLI装好了,daemon废了。docker version命令会分别询问CLI版本和连接daemon,如果连接失败,Server段直接显示错误。判断标准很简单:两段都正常输出才算装好。
6. 安装过程中的高频报错与完整排查链路
这一章集中记录我在不同机器上装Docker遇到过的典型报错,全部是真实场景,按"症状→原因→解决"的思路展开。这里每个案例都给了完整的排查链路,而不是直接说答案,目的是让你学会思路,以后遇到类似问题能自己定位。
6.1 权限拒绝:permission denied while trying to connect
症状:普通用户执行docker ps,报permission denied while trying to connect to the Docker daemon socket。
原因:当前用户不在docker组内,或者docker socket权限不对。
排查链路:先ls -l /var/run/docker.sock看socket权限,正常是srw-rw---- root docker。如果不是,多半是docker组不存在或权限被改动。然后id $USER确认用户是否在docker组里。如果用户确实在组里但还报错,大概率是shell会话没刷新组权限,重新登录一次。如果sudo docker ps能通但普通用户不行,基本就是这个原因,解决方式就是上面说过的usermod加组。
6.2 守护进程失联:Cannot connect to the Docker daemon
症状:docker ps报Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?。
原因:docker.service没起来,或者daemon启动后崩溃了。
排查链路:先systemctl status docker看服务状态,再journalctl -u docker -n 50看最近日志。常见原因有三个:磁盘满了,daemon无法写日志或创建容器;/var/lib/docker数据损坏;内核模块冲突,比如iptables相关模块加载失败。磁盘问题用df -h查,/var/lib/docker所在分区如果满到100%,先清理空间再启动。数据损坏的话,日志里通常会有corrupted、failed to load之类的字样,可以尝试把/var/lib/docker临时改名备份,再启动一个新的空目录测试,确认是数据问题再逐层恢复。内核模块问题,日志里能看到类似iptables failed的提示,重启系统一般能解决,解决不了就检查内核模块状态。
6.3 Docker Desktop的KVM虚拟化报错
症状:在Ubuntu上装Docker Desktop,启动时报virtualization support not detected,或者提示KVM不可用。
原因:Docker Desktop在Linux上依赖KVM虚拟化。如果是物理机,可能是BIOS里没开虚拟化;如果是虚拟机,可能是嵌套虚拟化没开启,或者CPU不支持。
排查链路:先确认CPU虚拟化标志:
grep -Eo '(vmx|svm)' /proc/cpuinfo | sort -u有vmx(Intel)或svm(AMD)输出说明CPU支持。然后确认KVM设备:
ls -l /dev/kvm不存在说明KVM模块没加载或虚拟化没开。物理机进BIOS开启VT-x/AMD-V,虚拟机则要检查宿主机是否支持嵌套虚拟化。这里要强调一个观点:如果你只是在虚拟机的Ubuntu里跑普通Docker容器,根本不需要KVM,也不需要Docker Desktop,直接装Docker Engine就行。Docker Desktop这套是给需要图形界面和内置K8s的人准备的,普通容器场景并不需要它。换句话说,这个报错本身就是该换用Docker Engine的信号。
6.4 apt update 404:Codename写错的经典案例
症状:添加Docker源后,apt update提示类似Release file for ... not found或404。
原因:写进docker.list里的Codename和当前系统版本不匹配,或者系统版本太老,不在Docker官方支持列表里。
排查链路:先cat /etc/apt/sources.list.d/docker.list看写入的Codename,再用lsb_release -cs对比当前版本代号。如果系统是从旧版升级来的,VERSION_CODENAME变量可能还是旧代号,比如从22.04升级到24.04后,/etc/os-release里的VERSION_CODENAME仍旧是jammy,这时写入源就变成了jammy,但系统已经是noble,仓库里自然找不到匹配的包。手动把docker.list里的代号改成noble就行。如果你用的系统连Docker官方支持列表都不在,那就别挣扎了,换LTS系统,或者去查官方文档找存档源方案。
6.5 镜像拉取超时的处理思路
症状:docker pull ubuntu卡住,或者报net/http: TLS handshake timeout之类错误,国内网络环境尤其常见。
原因:Docker Hub官方源的网络连通性在某些地区不稳定,或者公司网络限制了外网访问。
解决思路是配置镜像加速器(registry mirror)。Docker daemon支持在/etc/docker/daemon.json里配置registry-mirrors字段,比如:
{ "registry-mirrors": ["https://docker.mirrors.ustc.edu.cn"] }配置后重启Docker:
sudo systemctl restart docker然后docker info | grep -A2 Registry Mirrors确认生效。注意三点:第一,镜像加速只对Docker Hub的官方镜像库生效,拉取第三方镜像仓库的镜像不经过mirror;第二,不同加速器的可用性和速度会变化,选择时需要自行评估,云厂商提供的加速器、高校公共镜像站都是常见选择,没有哪个是永恒的,失效了换个可用的;第三,如果你在公司内网,可能需要在daemon.json里同时配置HTTP代理,这是安装Docker后经常忽略的一环。
提示:改daemon.json之前先备份原文件,改错字段会导致Docker无法启动。改完用docker info验证,有问题马上用备份恢复。
7. 镜像加速、日志轮转与日常维护
安装完成只是开始,日常使用中有几个高频操作需要提前知道,否则用一阵子就会出现磁盘报警、日志爆炸这类问题。
7.1 daemon.json的常见配置组合
daemon.json不是只有镜像加速一个功能,它还可以配日志驱动、数据目录、insecure-registries等。修改前先看有没有已存在的daemon.json,有就在原文件基础上加字段,别直接覆盖。一个比较典型的配置:
{ "registry-mirrors": ["https://docker.mirrors.ustc.edu.cn"], "log-driver": "json-file", "log-opts": { "max-size": "10m", "max-file": "3" }, "data-root": "/data/docker" }>docker system prune
会清理停止的容器、未使用的网络、悬空镜像和构建缓存。加-a参数会连未使用的镜像一起清理,小心别在还需要的环境上执行。我的习惯是先在测试环境跑一遍docker system df看当前占用分布,再决定要不要prune,避免误删。
7.3 升级、卸载与脚本化安装
Docker的升级很简单,因为用的是官方apt源:
sudo apt update sudo apt upgrade docker-ce docker-ce-cli containerd.io卸载则分两步,先移除软件包再清理数据:
sudo apt remove docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin sudo rm -rf /var/lib/docker sudo rm -rf /var/lib/containerd卸载前确认容器数据是否需要备份。这里再强调一次,/var/lib/docker是容器、镜像、卷、网络的默认存储位置,rm -rf不可恢复,执行前一定要三思。
有时候需要在多台机器上快速安装,手敲命令容易出错。我自己会把前面所有步骤整理成一个shell脚本,核心逻辑是幂等的:已经装过就跳过,没装过就装。脚本里固定好系统版本判断、密钥导入、仓库写入、daemon.json备份与写入、用户组添加这几个环节。这里不贴完整脚本,因为不同环境的daemon.json配置不一样,但提醒一句:脚本里千万别写死密码,也别把docker组权限乱加,否则在多用户服务器上就是安全漏洞。
我最后说一点个人体会。在所有Linux发行版里,Ubuntu装Docker算是相当成熟的路线,但"成熟"不等于"闭眼装"。我踩过最深的坑是反复在旧机器上套用新版教程,以及在云主机上忘了检查IP转发。其实只要把安装前的环境检查当成步骤零,后面80%的问题都不会发生。如果你在安装过程中遇到我没写到的报错,别急着搜"Docker 安装失败"这类大词,先看systemctl status docker和journalctl的日志,日志里写的比你想象的清楚得多。工具链会一直有新版本,但排查思路永远是先环境、后配置、再网络,这条路几乎不会绕远。