我重新看了一遍 Docker 官方文档,发现入门真正该先懂这几张图

前阵子那篇 Docker 文章,刚发的时候我没觉得它会有多强。
结果过了一段时间再看,阅读慢慢涨到了三千多,点赞也不少。
这件事让我有点意外。它不像那种热点文章,当天冲一下就结束。Docker 这种老工具,反而会在后面慢慢被人翻出来看。
所以我又回头看了一遍 Docker 官方文档。
这次看完以后,我更确定一件事:很多人学 Docker 卡住,不是因为命令没背熟,而是脑子里一开始没有图。
docker run、docker ps、docker exec、docker logs 这些命令当然要会,但它们更像是最后露出来的按钮。真正决定你能不能用明白的,是这几层关系:
-
命令到底发给谁 -
镜像和容器到底是什么关系 -
端口为什么要映射 -
数据为什么不能只放在容器里 -
Compose 到底是在帮你省什么
我以前学 Docker 的时候,也喜欢先从命令开始记。
但现在回头看,顺序可能反了。
第一张图:docker 命令背后,其实还有一个 dockerd

我以前刚接触 Docker 的时候,会下意识觉得 docker 命令本身就在干活。
比如执行:
docker run nginx
感觉就是这个命令把 nginx 跑起来了。
但官方文档里讲得很清楚,Docker 是 client-server 架构。我们在终端里敲的 docker 是 Docker Client,它会把请求发给 Docker Daemon,也就是 dockerd。真正管理镜像、容器、网络、数据卷的是 daemon。
这个图一旦有了,很多事情就顺了。
为什么有时候 Docker Desktop 没启动,命令就报错?
为什么远程 Docker 可以通过 API 管?
为什么 docker ps 不是简单扫一下本机进程列表?
因为你不是直接操作一个普通进程,而是在跟 Docker 后面的 daemon 说话。
这个点不复杂,但我觉得它应该放在入门最前面。否则新手很容易把 Docker 理解成一堆命令,而不是一个有后台服务的容器管理系统。
第二张图:Image 是模板,Container 是跑出来的实例

第二个容易混的,是 image 和 container。
这两个词刚开始看很像,尤其中文里一个叫镜像,一个叫容器,新手很容易一起说成“那个 Docker”。
但它们不是一回事。
image 更像模板。
container 是从这个模板跑出来的实例。
同一个 nginx:latest 镜像,可以启动一个容器,也可以启动多个容器。删掉容器,不等于删掉镜像。删掉镜像,也不等于你已经停止了所有相关容器。
所以这些命令其实是在操作不同层:
docker images docker ps docker rm docker rmi
docker images 看的是本地有哪些镜像。
docker ps 看的是正在运行的容器。
docker rm 删的是容器。
docker rmi 删的是镜像。
这个地方我不建议一开始就讲太多镜像分层、联合文件系统、写时复制。那些以后当然要懂,但入门时先把“模板”和“实例”分开,已经能少掉很多困惑。
第三张图:端口映射,其实就是两边端口对上

我见过不少人第一次写 -p 8080:80 的时候,最先记的是格式。
左边还是右边?
宿主机还是容器?
这个确实容易忘。
但如果把它画成图,就没那么玄乎了:
docker run -p 8080:80 nginx
左边的 8080 是你电脑上的端口。
右边的 80 是容器里的端口。
你在浏览器里访问:
http://localhost:8080
请求先到宿主机的 8080,Docker 再把它转到容器里的 80。
这里最容易误解的一点是:容器里的服务不是天然就暴露给你电脑了。它可以在容器内部监听 80,但如果你没有做端口发布,外面不一定能访问到。
所以端口映射不是一个需要死记的参数,它就是一条连接线:
宿主机端口 -> 容器端口
以后看到 -p 3000:3000、-p 8080:8080、-p 5433:5432,都可以用同一张图理解。
第四张图:Volume 和 Bind Mount,不是在解决同一个问题

数据这块,我觉得是 Docker 入门里最容易被讲乱的地方。
很多教程会直接告诉你:
-v xxx:yyy
然后你就开始背路径。
但官方文档里其实把持久化数据和共享本地文件分得很清楚。
我现在更愿意先这样理解:
volume 更像是 Docker 帮你管理的一块数据空间。
bind mount 更像是把你电脑上的某个目录,直接挂进容器。
它们解决的问题不一样。
如果是数据库数据、上传文件、应用运行时产生的数据,我会优先想到 volume。因为这些数据属于容器运行出来的结果,需要在容器删除以后继续保留。
如果是开发阶段,我想把当前项目目录挂进容器,让容器里能看到我本机改的代码,那 bind mount 更直观。
比如:
docker run -v my-data:/var/lib/mysql mysql
这个更像是在说:MySQL 的数据放到 Docker 管理的 my-data 里。
而:
docker run -v .:/app node
这个更像是在说:把我当前目录挂到容器里的 /app。
这两个都用了 -v,但脑子里应该是两张不同的图。
一个偏数据持久化。
一个偏本地目录共享。
把这件事分清以后,再去看 Compose 里的 volumes,也不会那么容易乱。
第五张图:Compose 不是更复杂,而是把一堆参数收起来

很多人学到 Docker Compose 时,会觉得又来了一个新东西。
其实我现在更愿意把 Compose 理解成:
把你本来要手写的一堆
docker run参数,整理成一个可以复用的文件。
比如一个项目里有:
-
web 服务 -
MySQL 或 PostgreSQL -
Redis -
网络配置 -
数据卷 -
端口映射 -
环境变量
如果全靠命令行手敲,会很长,也很难让别人复现。
Compose 的价值就在这里。
它把这些东西写进 compose.yml:
services: web: image: nginx:latestports: - "8080:80"db: image: postgres:16volumes: - db-data:/var/lib/postgresql/datavolumes: db-data:
然后你用:
docker compose up
就能把这一组服务跑起来。
所以 Compose 不是另一套 Docker。
它更像是把容器、网络、端口、数据卷这些关系收进一份项目配置里。别人拿到项目以后,不用猜你当时敲过哪些命令。
这也是为什么很多项目文档里不再写一长串 docker run,而是直接给你一份 Compose 配置。
真正要背的命令,其实没那么多
这几张图顺了以后,再回来看命令,会轻很多。
我觉得入门阶段真正要熟的命令,其实就这些:
docker pull docker images docker run docker ps docker logs docker exec docker stop docker rm docker rmi docker compose up docker compose down
但这些命令不应该孤零零地背。
docker pull 是把 image 拉到本地。
docker run 是从 image 跑出 container。
docker logs 是看某个 container 的输出。
docker exec 是进到某个 container 里执行命令。
compose up 是按配置文件启动一组服务。
命令没变,但脑子里有图以后,它们就不再像一堆零散单词。
我现在会怎么学 Docker
如果让我重新带一个人入门 Docker,我可能不会先让他背命令表。
我会先让他画这几张图。
先画 Docker Client 和 Docker Daemon。
再画 Image 到 Container。
再画 8080:80 的端口映射。
再画 volume 和 bind mount 的区别。
最后画 Compose 怎么把多个服务收在一起。
画完以后,再拿一个最小项目跑一遍。
比如一个 nginx:
docker run --name demo-nginx -p 8080:80 nginx
然后看容器:
docker ps
看日志:
docker logs demo-nginx
停掉它:
docker stop demo-nginx docker rm demo-nginx
这个过程不复杂,但每一步都能对应回图里某个位置。
我觉得这比一开始复制一大段命令有效。
Docker 本身不神秘,它真正容易让人晕的地方,是概念之间的关系太多:镜像、容器、网络、端口、数据卷、Compose 全都挤在一起。
先把图画出来,再去看命令。
至少我现在重新看官方文档,感觉最该补的不是更多命令,而是这几张关系图。
参考资料
-
Docker 官方文档:Docker overview -
Docker 官方文档:What is an image? -
Docker 官方文档:What is a container? -
Docker 官方文档:Publishing and exposing ports -
Docker 官方文档:Persisting container data -
Docker 官方文档:Sharing local files with containers -
Docker 官方文档:What is Docker Compose?
夜雨聆风