01菜鸟:装好跑起来

2490 字
12 分钟
01菜鸟:装好跑起来

第 1 关 装好跑起来#

本关的产出:一个装好的运行时,一条从拉镜像、起容器到在浏览器里打开的完整链路,以及查看、进入、停止、删除这一套日常操作。

1.1 先搞清这条命令发给了谁#

问题的来源:Docker 分成两半。敲下的 docker 命令是「客户端」,真正拉镜像、起容器的是常驻在后台的「服务端」(也就是守护进程)。客户端每执行一条命令,都得先知道自己该把它发给谁。

两个角色,这里说清楚一次,后面一直用。

服务端是那个常驻后台的守护进程。它掌握全部状态:有哪些镜像、有哪些容器、每个容器在跑什么、网络怎么连、数据存在哪。活都是它干的。

客户端就是敲下的 docker 命令。它自己什么都不做(无状态),只干三件事:把命令翻译成一个请求,发给服务端,再把返回的结果打出来。

最能说明问题的一点:客户端不掌握任何运行状态。敲 docker ps 的那一瞬间,它并不知道这台机器上有几个容器,它只是问服务端「现在有哪些容器」,然后照着答案印出来。

所以客户端不止一个。命令行是一个,图形界面是一个,编辑器里的插件也是一个——它们问的是同一个服务端。这正是上下文必须存在的原因:客户端可以有好几个,但「问谁」只能有一个答案。

想亲眼看到这两个角色,敲一次 docker version:它会打印两段,第一段标题是 Client,第二段是 Server。

在「上下文」出现之前,这个「发给谁」只能靠终端里的一个设置来指定(环境变量 DOCKER_HOST)。它有三个麻烦:

  1. 它是终端窗口的状态,不是系统里的一件东西:只对当前这个终端窗口有效,另开一个窗口就没了;想让它一直有效就得写进终端的配置文件,然后过几个月连自己都忘了配过。
  2. 没有清单:「这台机器上到底有几个 Docker 可以连」这个问题无解,目标只存在于记忆里。
  3. 最要命的是会连错:忘了当前指向哪,对着生产环境的守护进程敲下一句 docker rm。在上下文这个概念出现之前,这是很常见的事故。

在 macOS 上,这个问题一定会撞上:Docker Desktop 的守护进程并不直接跑在 Mac 上,而是跑在 Docker 自己管的一台小 Linux 虚拟机里,Colima 也是同样的路数。两者的守护进程各自守在不同的位置,客户端要连的地址并不相同。两个都装的时候,「这条命令到底发给谁」必须有个明确答案。

上下文就是为这件事设计的:它把「连哪个守护进程」以及连上它所需要的全部设置,打包成一个有名字、会被记住的东西,存在 ~/.docker/ 下。于是:

  1. 可枚举:docker context ls 把「目标」从记忆里的东西变成系统里列得出来的东西。
  2. 可切换:docker context use <名字> 显式切换,切完一直有效。
  3. 可按单条命令覆盖:docker --context <名字> ps 只影响这一条命令,不动全局设置。
  4. 不只终端能用:编辑器插件、图形界面工具都读它;而那个终端设置只有终端有。

它真正给人的东西:从此 docker 命令不关心守护进程在哪儿。跑在本机的虚拟机里,或者跑在另一台服务器上,对它来说都只是「一个上下文」。在同一台 Mac 上敲 docker ps,列出来的可以是远端那台机器上的容器。

一个关键性质:上下文是纯客户端的概念。守护进程完全不知道它存在,它只是客户端本地的一份连接配置。

常见的三个选择:

  1. Docker Desktop:官方方案,带图形界面,自带那台小 Linux 虚拟机,资源占用偏大。
  2. Colima:开源命令行方案,同样是虚拟机,但没有图形界面,内存占用小。
  3. OrbStack:Mac 上性能最好,商业软件,个人免费。

动手:

  1. docker context ls —— 看有哪些上下文,带 * 的是当前在用的那个。
  2. docker context use <名字> 切到另一个,再跑一次 docker ps 对比。
  3. 切回来。

要观察:

  1. docker context ls 里 default 那条的描述写着 Current DOCKER_HOST based configuration —— 它就是把上面说的「老办法」原样保留成了一个上下文,从这能看出这个机制是怎么长出来的。
  2. 切到 Colima 那边的上下文而它又没启动时,docker ps 报的是连不上,而不是「没有容器」。这两句话的区别,就是「客户端连不上服务端」和「服务端上没东西」的区别。

易踩的坑:

  • 带 * 的是「当前在用」,不是「默认」。名字叫 default 的那个,很可能恰恰不是当前在用的。
  • 切换上下文之后,之前 docker ps 能看到的容器会「消失」——它们还在,只是在另一台虚拟机上。

1.2 验证安装,跑出 hello-world#

目标:确认这套装好了,并看清一次 run 到底做了几件事。

  1. 验证:docker version 看客户端与服务端两段版本号,docker info 看服务端信息(存储驱动、内核版本、总内存)。
  2. 跑一次:docker run hello-world。
  3. 拆开这一步:去找镜像 → 没有就从仓库拉 → 用它创建一个容器 → 启动容器里的进程 → 进程输出那几行字 → 进程退出、容器停止。

要观察:

  1. docker ps 看不到刚跑完的容器,docker ps -a 才看得到 —— 因为它已经退出了。
  2. 退出码是 0,表示正常结束,不是失败。

易踩的坑:第一次拉镜像会卡在 Unable to find image ... locally 一会儿,那是真的在下东西,不是卡死。

1.3 拉下镜像、跑起来、访问它#

目标:跑一个不止转瞬即逝的容器,从浏览器里访问到它。

  1. 先把镜像拉下来:docker pull nginx。拉完再敲一次同样的命令,会立刻返回——镜像已经在本地了,不用再下一次。
  2. 起容器:docker run -d -p 8080:80 nginx。
  3. -d:后台运行,命令立刻返回,终端不被占住。
  4. -p 8080:80:把宿主机的 8080 端口转发到容器的 80 端口。顺序是「宿主<容器>」,这里最容易记反。
  5. 访问:浏览器打开 http://localhost:8080,看到 nginx 的欢迎页。

要观察:docker ps 的 STATUS 与 PORTS 两列分别写了什么。

易踩的坑:

  • 第 1 步其实可以省:本地没有这个镜像时,docker run 会自己去拉。单独拉一次的用处是把「拉」和「跑」分成两步——出问题时一眼看得出是压根没拉下来,还是拉下来了但跑不起来。
  • 8080 已被占用时会报 bind: address already in use,换个宿主端口即可(容器的 80 不用动)。
  • macOS 上必须显式做端口映射。Docker Desktop 的「宿主机」其实是那台 Linux 虚拟机,不写 -p,端口就不会出现在 Mac 上,浏览器里自然打不开。

1.4 看日志与进容器#

目标:容器是黑盒,学会从外面看它、钻进它里面。

  1. 看输出:docker logs <容器ID>,加 -f 持续跟随。
  2. 钻进去:docker exec -it <容器ID> bash,进去之后 ls、cat、改文件都跟普通 Linux 一样。
  3. exec 与 attach 的区别:attach 接的是容器主进程的标准输入输出,在里面按 Ctrl+C 会把主进程一起送走、容器跟着停;exec 是另开一个进程,退出它不影响容器。

易踩的坑:

  • docker logs 给的是容器里主进程的输出,不是 Docker 自己的日志。主进程不往标准输出写东西时它就是空的——哪怕程序正往自己的文件里勤勤恳恳地写日志。守护进程自己的日志是另外一套。
  • exec 只对运行中的容器有效,停了的容器要先 start。
  • -it 两个参数缺一不可:-i 保持输入,-t 分配终端,少一个就没有交互。
  • 精简镜像(后面会遇到的 alpine、distroless)里没有 bash,得换成 sh 或者根本没有 shell。

1.5 停掉、删掉、找回#

目标:把「容器」和「镜像」这两样东西的关系理顺。

  1. 停与启:docker stop <ID>、docker start <ID>、docker restart <ID>。停掉的容器还在,能再启。
  2. 删容器:docker rm <ID>,加 -f 可以强删运行中的容器。
  3. 删镜像:docker rmi <镜像>。
  4. 顺序:容器是镜像的实例,所以先删容器,再删镜像。

易踩的坑:

  • docker rm 直接删运行中的容器会报错,要么先 stop,要么加 -f。
  • docker rmi 报 image is being used by ... 时,不是权限问题,是有容器(哪怕是已停止的)还指着它。
  • 容器删掉之后,写进它自己文件系统里的改动一并消失——这是第 4 关要解决的问题。

1.6 看看这些东西占了多大地方#

目标:知道怎么查、怎么收,别让镜像和缓存把磁盘悄悄吃满。

  1. 查:docker system df,分别看镜像、容器、卷、构建缓存各占多少。
  2. 收:docker system prune 清掉已停止的容器和没在用的网络;-a 连没被任何容器引用的镜像一起清。

要观察:docker system df 里 RECLAIMABLE 那一列,就是「现在能收回多少」。

易踩的坑:prune -a 会把构建缓存一起清掉,下次构建会明显变慢。清之前先看清楚。

本关命令速查#

容器生命周期与日常操作,见 命令速查表 · 容器生命周期。

支持与分享

如果这篇文章对你有帮助,欢迎分享给更多人或打赏支持!

打赏
01菜鸟:装好跑起来
https://hecloud.top/posts/tech/docker/01装好跑起来/
作者
贺小云
发布于
2026-09-18
许可协议
CC BY-NC-SA 4.0

评论区

Profile Image of the Author
贺小云
热爱技术与折腾的博客 Dancing with uncertainty. 与不确定性共舞
公告
出于成本考虑,贺云已全面转型Serverless架构
分类
标签
碎碎念
站点统计
文章
91
分类
5
标签
44
总字数
103,192
运行时长
0 天
最后活动
0 天前
站点信息
构建平台
Cloudflare Pages
博客版本
Firefly v6.16.5
文章许可
CC BY-NC-SA 4.0