2、Docker基础回顾:Docker架构、核心概念(镜像、容器、仓库)、常用命令
好,咱们正式开始。这一章我打算带大家快速过一遍 Docker 的核心基础。你可能会想:「我都来学迁移和改造了,还要回顾基础?」
嗯,我理解这个想法。但根据我这些年带团队的经验,很多容器化翻车事故,恰恰就出在基础概念没吃透。比如镜像层搞混了,容器数据卷没挂对,仓库标签打错了——这些看似小问题,上了生产就是大麻烦。
所以,咱们花点时间,把地基夯实。我保证,不会啰嗦,全是干货。
2.1 Docker 架构:Client-Server 与守护进程
Docker 用的是典型的 C/S 架构。说白了,就是两个角色在干活:
- Docker Client(客户端):你敲命令的那个终端。比如
docker run、docker build。 - Docker Daemon(守护进程):真正干活的家伙。它负责管理镜像、容器、网络、存储卷。
它们之间通过 REST API 通信。你想想看,你在笔记本上敲 docker ps,命令先发给本地的 daemon,daemon 再返回结果。但如果你把 DOCKER_HOST 环境变量设成远程地址,客户端就能操作远程的 daemon 了。我在项目中就经常用这个技巧,远程调试测试环境的容器,省得来回跳板机。
核心要点:
- Client 只管发指令,不干活。
- Daemon 是大脑,负责所有脏活累活。
- 通信走 REST API,端口默认 2375(未加密)或 2376(TLS)。
注意:千万不要把 2375 端口暴露到公网!我曾经见过一个团队,为了方便调试,直接把 Docker 的 TCP 端口开到了公网 IP 上。结果呢?被人植入了挖矿容器,整个集群的资源被吃光。教训深刻啊。
2.2 三大核心概念:镜像、容器、仓库
这三个概念,是 Docker 的基石。我习惯用一个类比来理解:
- 镜像(Image) = 类(Class)
- 容器(Container) = 实例(Instance)
- 仓库(Registry) = 代码仓库(Git Repo)
是不是一下子就清晰了?咱们一个一个拆开讲。
2.2.1 镜像(Image)
镜像是只读的模板。它包含了运行应用所需的一切:代码、运行时、系统工具、库、配置。你可以把它理解成一个「压缩包」,但这个压缩包是分层构建的。
为什么会这样?因为分层可以复用。比如你基于 Ubuntu 20.04 做镜像,又基于它做 Nginx 镜像,那 Ubuntu 那一层就可以被多个镜像共享,不用重复下载。我在项目中遇到过,一个团队把同一个基础镜像打了 10 个不同的应用镜像,结果磁盘占用飙升。后来我让他们用 docker image ls 一看,全是重复的基础层。优化后,磁盘占用直接降了 60%。
小技巧:用 docker image history <镜像名> 可以查看镜像的构建历史,每一层做了什么一目了然。排查镜像体积问题时特别好用。
2.2.2 容器(Container)
容器是镜像的运行实例。你可以启动、停止、删除、暂停它。每个容器都是隔离的,有自己的文件系统、网络、进程空间。
嗯,这里要注意:容器是「有状态」的。你可以在容器里写文件,但容器一旦被删除,这些数据就没了。除非你用了数据卷(Volume)或绑定挂载(Bind Mount)。
我记得有一次,一个同事在容器里跑数据库,数据全写在容器内部。结果容器因为 OOM 被自动重启了,重启后数据全丢了。他当时脸都绿了。从那以后,我要求所有有状态数据必须挂载到宿主机或持久卷上。
一句话总结:镜像负责「是什么」,容器负责「怎么跑」。
2.2.3 仓库(Registry)
仓库就是存放镜像的地方。有公开的 Docker Hub,也有私有的 Harbor、Nexus、AWS ECR 等。
镜像的命名规则是:[仓库地址/]用户名/镜像名:标签。比如 nginx:latest 其实是 docker.io/library/nginx:latest 的简写。
我个人习惯,在私有仓库里一定要打上明确的版本标签,比如 myapp:v1.2.3,而不是 latest。因为 latest 是个「移动靶」,你永远不知道它指向哪个版本。生产环境用 latest,迟早要出问题。
避坑指南:我曾经接手过一个项目,所有镜像都只打了 latest 标签。结果某次部署时,有人不小心 push 了一个有 bug 的版本上去,所有节点自动拉取了新镜像,线上直接崩了。从那以后,我强制要求:生产环境必须使用固定版本标签,禁止使用 latest。
2.3 常用命令:从入门到日常
命令这东西,不用全记住,记住最常用的就行。我平时用的最多的,也就下面这些。我按场景分类,方便你记忆。
2.3.1 镜像操作
| 命令 | 说明 | 示例 |
|---|---|---|
docker pull |
拉取镜像 | docker pull nginx:1.25 |
docker images |
列出本地镜像 | docker images |
docker rmi |
删除镜像 | docker rmi nginx:1.25 |
docker build |
构建镜像 | docker build -t myapp:v1 . |
docker tag |
打标签 | docker tag myapp:v1 myreg.com/myapp:v1 |
docker push |
推送镜像到仓库 | docker push myreg.com/myapp:v1 |
个人习惯:我每次构建完镜像,都会先用 docker image prune 清理一下 <none> 标签的悬空镜像。不然时间长了,磁盘会被这些「僵尸镜像」占满。
2.3.2 容器操作
| 命令 | 说明 | 示例 |
|---|---|---|
docker run |
创建并启动容器 | docker run -d --name web -p 80:80 nginx |
docker ps |
列出运行中的容器 | docker ps -a(列出所有) |
docker stop |
停止容器 | docker stop web |
docker start |
启动已停止的容器 | docker start web |
docker rm |
删除容器 | docker rm -f web(强制删除) |
docker logs |
查看容器日志 | docker logs -f web(实时跟踪) |
docker exec |
进入容器内部 | docker exec -it web bash |
这里我特别想强调一下 docker run 的常用参数:
-d:后台运行(detached)--name:给容器起个名字,方便管理-p:端口映射,宿主机端口:容器端口-v:挂载数据卷-e:设置环境变量--restart=always:容器退出后自动重启
实战示例:启动一个 MySQL 容器,挂载数据卷,设置密码:
docker run -d \
--name mysql8 \
-p 3306:3306 \
-v /data/mysql:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=my-secret-pw \
--restart=always \
mysql:8.0
2.3.3 仓库与网络操作
| 命令 | 说明 | 示例 |
|---|---|---|
docker login |
登录仓库 | docker login myreg.com |
docker search |
搜索镜像 | docker search nginx |
docker network ls |
列出网络 | docker network ls |
docker network create |
创建网络 | docker network create mynet |
docker network connect |
将容器连接到网络 | docker network connect mynet web |
避坑指南:我曾经在项目中,两个容器之间通信一直不通。排查了半天,发现它们不在同一个 Docker 网络里。默认的 bridge 网络只能通过 IP 通信,而且容器重启后 IP 会变。后来我创建了一个自定义网络,用容器名通信,问题就解决了。记住:自定义网络支持 DNS 解析,用容器名代替 IP 才是正道。
2.4 本章小结
好了,这一章的内容就这些。咱们回顾一下:
- Docker 是 C/S 架构,Client 发指令,Daemon 干活。
- 镜像是只读模板,容器是运行实例,仓库是存放镜像的地方。
- 常用命令分三类:镜像操作、容器操作、仓库与网络操作。
这些基础,是后续所有容器化迁移和改造的根基。下一章,我会带你深入 Dockerfile 的编写技巧,以及如何把遗留应用一步步「塞」进容器里。到时候,咱们会用到很多今天讲的内容。
嗯,先消化这些吧。有问题随时翻回来看看。