开头先给结论:在Ubuntu上想把系统里到底有几个Docker彻底搞清楚,答案不是一条命令就能解决的。有人想知道装了几套Docker环境,有人想知道跑着几个容器,还有人其实是在排查为什么docker命令时好时坏。这篇文章就围绕这个话题,从Docker自带命令、系统底层进程、服务与文件、再到多套Docker共存的复杂场景,一层层垫高,把家底摸清楚。无论你是刚在Ubuntu上装完Docker的新手,还是正在折腾服务器环境的运维,跟着这篇文章走一遍,至少能少走很多弯路。
1. 先搞清楚"几个docker"到底是在问什么
1.1 Docker不是单个程序,而是一整套体系
刚开始接触Docker的人很容易被名字绕晕,以为"docker"就是一条命令、一个软件。实际上Docker是一个典型的"C/S架构"系统,简单拆开来看:
- docker命令行客户端(CLI):就是你敲
docker ps、docker run时用的那个工具,它本身只负责"发号施令"。 - dockerd守护进程(Daemon):真正干活的角色,负责管理镜像、容器、网络和存储。客户端发来的命令最终都由它执行。
- containerd运行时:dockerd底层的容器生命周期管理器,负责真正启动和停止容器进程。
- 镜像与容器:镜像是打包好的只读模板,容器是镜像运行后的实例。你可能会拉取几十个镜像、启动多个容器。
这个结构想说明什么?当你问"系统里有几个docker"时,严格来说你是在问"有几个dockerd守护进程在运行"或者"有几个Docker环境配置",而不是数一数命令行工具的个数。就像你家里装了微信,手机上有一个App、电脑上还有一个客户端,你能说有两个微信,也可以说只是一个账号在两台设备上登录——关键取决于你站在哪个角度看。
1.2 不同需求下的三种理解方式
根据我接触到的实际情况,"几个docker"大概对应三种截然不同的需求,下面用表格拆开看:
| 问题类型 | 真实需求 | 最直接的排查方法 |
|---|---|---|
| 想知道系统总共跑着多少容器、镜像 | 资源盘点、磁盘占用排查 | docker ps、docker images、docker system df |
| 想知道是不是装了好几套Docker引擎 | 环境混乱、客户端连不上引擎 | 查dockerd进程数量、查socket文件、docker info |
| 想知道是不是装过多个来源的Docker包 | 升级失败、残留服务冲突 | dpkg -l | grep docker、systemctl list-unit-files |
有一回我帮人看一台Ubuntu服务器,他信誓旦旦说"我明明启动过docker,怎么又没了",跑了一眼才发现,他之前用apt装了docker.io,后来按网上教程又装了docker-ce,两个引擎的systemd服务还都启用了,他以为自己在操作同一个Docker,实际上命令走的是其中一个,数据目录却是另一个的。这种情况在社区里太常见了,所以先把概念理清,再往下看具体命令才有意义。
2. 用Docker自带命令快速摸底
2.1 docker version:先分清客户端和服务端
如果你只想快速确认系统里有没有装Docker、装了能不能用,我一般建议先执行:
docker version注意看输出,它分成了两部分:Client(客户端)和Server(守护进程)。如果Server部分能正常显示版本号,说明当前这个客户端能成功连上一个引擎。如果只看到Client、Server下面报错,通常是引擎没启动、socket文件权限不对,或者DOCKER_HOST环境变量指错了地方。
以前遇到过新手把docker version输出里的两串版本号当成"系统里有两个docker"的证据,其实人家是同一套环境的前后台信息而已。真正判断引擎数量,要看Server部分是否出现多套、或者后面我们要讲的进程和socket排查。
2.2 docker info:一条命令看全部家底
docker version只是开胃菜,真正把Docker"家底"一次性看清楚的命令是docker info:
docker info输出里几个关键字段值得逐行看:
Containers:当前引擎管理的容器总数,包括各种状态的。Running:正在运行的容器数量。Paused:暂停状态的容器数量。Stopped:已停止的容器数量。Images:这个引擎里存放的镜像总数。Server Version:引擎版本号,多套引擎对比时特别有用。Storage Driver:存储驱动类型,Ubuntu上常见的是overlay2。Docker Root Dir:数据存放目录,默认通常是/var/lib/docker。
如果你怀疑自己连着的是哪一套引擎,先看Docker Root Dir。好几台机器我都靠这一项确认用户到底用的是snap包装的Docker,还是传统apt装的Docker,因为前者的数据目录在/var/snap/docker下面,后者才是/var/lib/docker。
2.3 数容器和镜像:docker ps与docker images
数"系统里跑着几个容器",标准答案是:
docker ps这个命令默认只显示正在运行的容器。想看包括已经退出的所有容器,要加参数:
docker ps -a输出里有几列需要注意:CONTAINER ID是容器的唯一编号,IMAGE来自哪个镜像,STATUS表示当前状态。Up 2 hours代表连续运行了两个小时,Exited (0)表示容器正常退出,Exited (137)往往是内存被杀掉的信号,Paused表示暂停。
至于镜像数量,直接:
docker images这里会列出仓库名、标签、镜像ID、创建时间和大小。注意REPOSITORY显示为<none>、TAG也显示<none>的,是悬空镜像,通常是反复构建镜像旧版本留下的,白白占磁盘,后续可以用docker image prune清理。
2.4 磁盘体检:docker system df
如果系统盘空间紧张,想看看是不是Docker占了大头,用这个:
docker system df它把镜像、容器、本地卷、构建缓存几个维度的占用都统计出来了。SIZE代表可回收大小,RECLAIMABLE表示其中多少是可以清理的。这个命令配合docker system df -v还能看到每一个镜像和容器具体占了多大,比单纯用du去翻数据目录高效得多。
我有一次排查一台测试机,df显示磁盘使用率95%,就是靠这个命令定位到某同事反复构建镜像,构建缓存吃掉了接近30G空间。一条docker builder prune -f清理下来,世界清净了。
3. 绕开Docker命令,从系统层面查个底朝天
3.1 进程视角:ps查dockerd和containerd
Docker命令偶尔会失灵,比如socket文件被误删、引擎崩溃、甚至有人手动杀掉了dockerd进程。这时候再依赖docker info就查不出东西了,必须绕到系统进程层面去看:
ps aux | grep -E "dockerd|containerd"正常一套引擎环境下,你会看到类似这样的进程:
/usr/bin/dockerd或者/usr/bin/dockerd --host=fd:///usr/bin/containerd- 一堆
containerd-shim开头的进程,这些其实是每个容器对应的垫片进程。
看到几行dockerd路径,基本就能确定跑着几套引擎。比如同时出现/usr/bin/dockerd和/usr/bin/docker这两个可执行文件路径不一样,要额外留意,后面第四部分展开讲。
还有个细节,grep的时候注意别把grep自己算进去。稳妥一点的做法是:
ps -C dockerd -o pid,cmd ps -C containerd -o pid,cmd3.2 服务视角:systemd里躺着几个docker相关服务
Ubuntu上通过apt或snap安装的Docker,都会注册systemd服务。查服务清单的通用命令是:
systemctl list-unit-files | grep -i docker常见的服务名有:
docker.service:主引擎服务,对应dockerd。docker.socket:socket激活服务,很多发行版用这个来做按需启动。containerd.service:容器运行时服务。docker-desktop.service:装了Docker Desktop才会有。
这里有个坑:如果docker.service显示enabled但docker.socket也显示enabled,两者同时开着并不代表"两个Docker",它们本来就是同一套体系的组成部分。真正需要警惕的是同时出现docker.service和snap.docker.dockerd.service,那基本意味着你同时装过apt版本和snap版本,两套东西都在系统里躺着。
查看某个服务的具体状态:
systemctl status docker.service能看到进程ID、内存占用、以及最近几十条日志,排查启动失败问题时非常有用。
3.3 文件视角:socket、二进制、数据目录
不看命令行输出,直接从文件系统上判断Docker的"存在痕迹",是排查问题的终极手段。
最关键的文件是socket,默认路径在/var/run/docker.sock。这个文件是客户端和守护进程通信的通道,你可以理解成服务器上开的一扇门。查它:
ls -l /var/run/docker.sock正常情况下会看到srw-rw----之类的权限,属主是root,属组是docker。如果这个文件不存在,说明引擎没起来或者被手动删了。
再看二进制本身:
which docker ls -l /usr/bin/docker /usr/local/bin/docker 2>/dev/null多装了几个来源的Docker,很多时候就是/usr/bin/docker和/usr/local/bin/docker同时存在,PATH环境变量决定你默认敲docker时执行的是哪一个。我见过有人的PATH里/usr/local/bin排在/usr/bin前面,结果apt更新了Docker版本,他却还在用老旧的二进制,折腾半天才找到原因。
数据目录也不能忽略:
du -sh /var/lib/docker如果这个目录不存在,而/var/snap/docker存在,那说明你用的是snap版。
3.4 网络视角:看监听端口
Docker引擎默认监听Unix socket,但如果配了远程访问,会额外监听TCP端口。查一下:
ss -lntp | grep -E "2375|2376"2375是明文端口,2376是加密端口。如果系统里同时监听多个IP的2375端口,或者出现两个不同端口的监听,加上前面几个视角的排查结果,基本就能拼凑出系统里有几套Docker引擎了。
这个视角很容易被忽略,但真实环境中靠它抓到过不少"幽灵引擎"案例——比如有人为了远程调试,手动把dockerd的监听端口改成了别的,结果另外的服务也在监听同一端口,Docker启动不了。
4. Ubuntu上"多套Docker"的真实场景与排查
4.1 docker.io与docker-ce的孽缘
Ubuntu官方源里的包叫docker.io,Docker官方源里的包叫docker-ce。很多人跟着不同教程各装了一遍,系统里就会出现两套可执行文件和服务。
先看装了哪些包:
dpkg -l | grep docker如果同时出现docker.io和docker-ce、docker-ce-cli、containerd.io等,那基本可以断定环境被折腾过。处理办法是选定一套保留,另一套彻底移除。我个人推荐保留docker-ce,因为它更新的频率更高,功能更跟得上。
移除时注意docker.io可能和docker-ce存在文件冲突,直接apt purge报错的话,先手动删掉冲突的二进制,再重新安装你想要的那套。
4.2 Docker Desktop介入后的局面
Docker Desktop在Linux上越来越常见,它和传统Docker Engine共存时,局面就更微妙了。Docker Desktop有自己的一套引擎、自己的socket路径,通常不再是/var/run/docker.sock那种直接可用的路径。很多新手装了Docker Desktop后,再敲docker ps发现连不上,其实是你的shell还在请求原来的socket,而原来的引擎没启动。
区分方法很简单:
ls -l /var/run/docker.sock systemctl status docker如果socket正常、服务正常,但docker ps就是报错,那就检查一下环境变量:
env | grep DOCKER_HOST4.3 snap包裹的Docker,坑得最深
Ubuntu对snap推广应用力度很大,很多人图省事直接:
snap install docker这样装的Docker,外壳和命令能用,但数据目录在/var/snap/docker,配置文件、日志路径都和apt版完全不同。最直观的坑是你按网上教程修改/etc/docker/daemon.json,怎么改都不生效,因为snap版根本不用这个路径。其次snap版自带docker compose以及其他插件,但路径和传统版本有微妙差异,导致脚本迁移时莫名其妙找不到命令。
判断是否snap版:
snap list | grep docker在实际情况里,我遇到过有人先装了snap版,后来又装了apt版,两条路径上的dockerd都被启用了,真正是"两个docker同时运行"。排查方法就是前面说的:ss看socket监听、ps看dockerd进程、systemctl看服务清单,三步合起来铁证如山。
4.4 用DOCKER_HOST切换引擎
多套环境下,环境变量DOCKER_HOST起着"指路牌"的作用。它的常见取值:
unix:///var/run/docker.sock:连默认引擎。unix:///var/run/docker-desktop.sock:连Docker Desktop的引擎(具体路径以实际安装为准)。tcp://ip:2375:连远程引擎。
所以说,如果你的shell里设置了DOCKER_HOST指向A引擎,即使系统里默认引擎是B,你敲docker ps看到的也是A的容器列表。判断当前连着哪套,用:
echo $DOCKER_HOST如果输出是空的,说明走默认socket,如果输出了明确路径,那就要小心了。清理僵尸设置的办法是:
unset DOCKER_HOST排查服务器环境时,我只要发现用户怎么都连不上docker,第一件事就是看这个环境变量。很多所谓"多套docker"困惑,最后都发现只是一个指路牌指错了地方。
5. 实战排查:三个常见场景的处理过程
5.1 场景一:docker命令报了command not found
这个报错出现后,不要急着重装系统。按顺序排查:
which docker # 看能不能找到可执行文件 ls -l /usr/bin/docker # 看二进制是否存在 apt list --installed | grep docker # 看apt包情况 snap list | grep docker # 看snap包情况如果是apt包已装但which docker为空,十有八九是PATH配置问题。如果是snap包已装但不在PATH里,重启一次终端或者重新登录会话就能解决。如果两边都没装,那就踏踏实实选一条官方安装路线重装。
重装时我强烈建议先清干净旧痕迹再装,不然下一轮排查又开始循环。删除命令参考:
apt purge docker.io docker-ce docker-ce-cli containerd.io snap remove docker rm -rf /var/lib/docker /var/snap/docker注意删数据目录前想清楚,这些目录里存着所有镜像、容器、卷,删了不可恢复。
5.2 场景二:权限不足,docker.sock被拒
Ubuntu下新手最容易碰到这类报错:
permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock原因是/var/run/docker.sock属主是root,普通用户默认没权限访问。正规做法是把当前用户加进docker组:
sudo usermod -aG docker $USER newgrp docker注意:usermod之后需要重新登录一次会话才彻底生效,否则当前终端还是旧权限。经常有人卡在这一步,加完组发现还是无权限,就直接去chmod 777 /var/run/docker.sock,这是我最不推荐的做法,会让任何用户都能操作Docker,等于把服务器root钥匙发给了所有人。
5.3 场景三:设置过DOCKER_HOST导致连不上
现象是docker ps报:
Cannot connect to the Docker daemon at tcp://xxx.xxx.xxx.xxx:2375. Is the docker daemon running?第一反应查环境变量:
echo $DOCKER_HOST如果输出是tcp://一串地址,确认这个地址到底通不通:
curl telnet://xxx.xxx.xxx.xxx:2375或者直接清掉环境变量再试:
unset DOCKER_HOST多数情况下,这个环境变量是之前某次远程调试残留的,清掉之后立刻恢复正常。如果确实需要远程连另一台机器的Docker,别用明文2375,至少做一层TLS加密,生产环境建议通过SSH隧道或者专门的远程管理方案来连。
5.4 会不会真有"两个docker"同时在跑?
最后回扣主题:怎么确认系统里到底有没有同时运行两份独立的Docker引擎?我的判断清单是:
ps -C dockerd -o pid,cmd:出现几行不同的dockerd启动命令。ss -lx | grep docker:出现几个不同的docker socket。systemctl list-unit-files | grep docker:出现几套互相独立的服务。docker info里Docker Root Dir指向不同路径。
如果两条以上证据指向不同的引擎,那就是真的多套共存。如果只有一个dockerd、一个socket、一个数据目录,那不管你看网上教程多复杂,你的系统其实就一套Docker,只是可能同时装了多个客户端入口而已。
举个我排查过的实例:某台机器上docker ps能看到A项目容器,但换了个用户登录后docker ps看到的是B项目容器,两边数据完全对不上。查下来就是root用户的环境变量指向了一个snap引擎,普通用户的shell没设环境变量,走的是apt引擎,两个跑的其实不是一套东西。理顺之后,各用户统一指向同一套引擎,问题直接消失。
最后说点个人体会
这个"几个docker"的问题,看着简单,背后其实是服务器排查的基本功:永远不要只看一条命令的输出,而是把进程、服务、socket、二进制、环境变量、数据目录全部串起来看。我这些年处理过不少类似案例,九成以上最后都发现不是真的装了两套引擎,而是用户视角错乱了。真正确认多套共存的情况,往往出现在apt版、snap版、Docker Desktop三个来源混装的机器上。
再分享一个我自己的习惯:我会在~/.bashrc里放一个函数,一行命令把Docker全景信息打出来:
dockerview() { echo "===== CLI ====="; which docker; docker version --format '{{.Client.Version}}' 2>/dev/null echo "===== Daemon ====="; ps -C dockerd -o pid,cmd echo "===== Sockets ====="; ss -lx | grep docker echo "===== Services ====="; systemctl list-unit-files | grep -i docker echo "===== Data Root ====="; docker info --format '{{.DockerRootDir}}' 2>/dev/null echo "===== DOCKER_HOST ====="; echo "${DOCKER_HOST:-<unset>}" }以后谁再问我系统里有几个docker,我就让他在终端敲一下这个,几秒钟把底交得明明白白。你也试试,习惯了之后会觉得排查环境问题从来不是难事,难的是思路乱。