第一章:Docker基础回顾
各位同学,咱们开始上课。在正式进入故障排查之前,我觉得有必要先花一章的时间,把Docker的基础知识捋一遍。你可能会想:「这些我都知道啊,还用讲?」
嗯,我理解。但说实话,我在生产环境里见过太多人,明明基础概念没吃透,一出问题就抓瞎。所以这一章,咱们就当是「战前热身」。
1.1 Docker架构:到底谁在干活?
Docker不是魔法,它是个典型的C/S架构。说白了,就是客户端发指令,服务端干活。
我个人习惯把Docker架构拆成三块来看:
- Docker Client:你敲命令的那个终端。比如
docker run,就是客户端在发号施令。 - Docker Daemon:真正干活的家伙。它负责管理镜像、容器、网络、存储卷。我遇到过有人以为Docker命令是直接操作容器的,其实所有命令都要经过Daemon。
- Docker Registry:镜像仓库。你从哪拉镜像?从Registry。你往哪推镜像?也是Registry。默认是Docker Hub,但企业里一般用私有仓库。
重点记住:Docker Daemon是核心。它挂了,你啥也干不了。我曾经在凌晨三点被叫起来,就是因为Daemon进程OOM了。从那以后,我养成了监控Daemon内存的习惯。
1.2 核心概念:镜像、容器、仓库
这三个概念,你肯定听过。但咱们得说清楚它们之间的关系。
镜像(Image)
镜像是什么?是一个只读的模板。它包含了运行应用所需的一切:代码、运行时、系统工具、库、配置。
你可以把镜像想象成一个「快照」。我刚开始学Docker时,总觉得镜像和虚拟机镜像差不多。其实区别很大——Docker镜像是分层构建的,每一层都可以复用。
我的经验:镜像层数不是越多越好。层数太多,拉取和构建都会变慢。我一般控制在10层以内。超过这个数,就该考虑合并了。
容器(Container)
容器是镜像的运行实例。镜像启动后,就变成了容器。你可以对容器进行启动、停止、删除等操作。
这里有个关键点:容器是可写的。你在容器里做的任何修改,比如创建文件、安装软件,都只存在于当前容器。一旦容器被删除,这些修改就没了。
为什么会这样?因为Docker用了写时复制(Copy-on-Write)技术。说白了,就是容器在镜像层之上加了一个可写层。你改的东西都写在这个层里。
注意:千万别把重要数据直接写在容器里。我见过有人把数据库文件放在容器里,结果容器一删,数据全没了。那场面,真是欲哭无泪。正确的做法是用数据卷(Volume)或绑定挂载。
仓库(Registry)
仓库就是存镜像的地方。Docker Hub是公共的,但企业里一般用Harbor、Nexus或者AWS ECR。
我记得有一次,生产环境拉镜像特别慢,查了半天发现是仓库配置了限流。从那以后,我建议团队在本地搭建镜像代理缓存,能省不少时间。
1.3 常用命令速查
命令这东西,不用全记住。但常用的几个,你得烂熟于心。我整理了一张表,你直接拿去用。
| 操作 | 命令 | 说明 |
|---|---|---|
| 拉取镜像 | docker pull nginx:latest |
从仓库拉取指定标签的镜像 |
| 查看镜像 | docker images |
列出本地所有镜像 |
| 删除镜像 | docker rmi nginx |
删除指定镜像,注意先删依赖的容器 |
| 运行容器 | docker run -d --name mynginx nginx |
-d后台运行,--name指定名称 |
| 查看容器 | docker ps -a |
-a显示所有容器(包括已停止的) |
| 进入容器 | docker exec -it mynginx bash |
-it交互模式,bash是shell |
| 查看日志 | docker logs -f mynginx |
-f实时跟踪日志输出 |
| 停止容器 | docker stop mynginx |
发送SIGTERM信号,优雅停止 |
| 删除容器 | docker rm mynginx |
删除已停止的容器,加-f强制删除运行中的 |
| 构建镜像 | docker build -t myapp:1.0 . |
-t指定标签,.是Dockerfile路径 |
小技巧:我习惯给常用命令起别名。比如 alias dps='docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"'。这样一眼就能看到容器状态,排查问题快很多。
1.4 镜像与容器的生命周期
你想想看,镜像和容器到底是什么关系?
镜像就像是一个「类」,容器就是「实例」。你可以从一个镜像启动多个容器,每个容器都是独立的。
容器的生命周期大概是这样的:
- 创建:
docker create,创建一个容器但不启动 - 启动:
docker start,启动已创建的容器 - 运行:容器正在执行主进程
- 停止:
docker stop,主进程退出 - 删除:
docker rm,彻底移除容器
这里有个坑:容器的主进程一旦退出,容器就停了。我遇到过有人用 docker run 启动一个脚本,脚本执行完就退出了,容器也跟着停了。他还纳闷为什么容器没在运行。其实,容器就是为了跑那个进程而存在的。
一句话总结:容器不是虚拟机,它不负责「保持运行」。它只负责运行你指定的那个进程。进程挂了,容器就挂了。
1.5 数据持久化:Volume与Bind Mount
前面说了,容器里的数据是临时的。那怎么持久化?两种方式:
- Volume(数据卷):由Docker管理,存在宿主机的一个特定目录下。推荐使用。
- Bind Mount(绑定挂载):直接把宿主机目录挂载到容器里。方便调试,但安全性差一些。
举个例子:
# 使用Volume
docker run -v mydata:/data nginx
# 使用Bind Mount
docker run -v /home/user/data:/data nginx
我个人更推荐Volume。为什么?因为Volume由Docker管理,你不需要关心它在宿主机上的具体路径。而且Volume可以跨容器共享,备份也方便。
避坑指南:我曾经在生产环境用Bind Mount挂载了一个配置文件,结果不小心在宿主机上改了文件权限,容器里的应用直接报错。从那以后,我只有在开发调试时才用Bind Mount,生产环境一律用Volume。
1.6 网络模式:容器之间怎么通信?
Docker的网络模式有几种,咱们快速过一下:
| 网络模式 | 说明 | 适用场景 |
|---|---|---|
| bridge | 默认模式,容器通过虚拟网桥通信 | 单机多容器通信 |
| host | 容器直接使用宿主机网络 | 性能要求高,但端口冲突风险大 |
| none | 容器没有网络 | 安全隔离,不需要网络 |
| overlay | 跨主机容器通信 | Swarm或Kubernetes集群 |
你想想看,默认的bridge模式,容器之间怎么通信?靠IP。但容器重启后IP会变,所以更推荐用容器名通信。前提是你在同一个自定义网络中。
# 创建自定义网络
docker network create mynet
# 在自定义网络中启动容器
docker run -d --name web --network mynet nginx
docker run -d --name app --network mynet myapp
# 现在app可以通过"web"这个名称访问nginx了
我记得有一次排查问题,发现两个容器明明在同一个宿主机上,却死活连不上。最后发现它们在不同的默认bridge网络中。嗯,从那以后,我只要涉及容器间通信,一律用自定义网络。
1.7 本章小结
这一章咱们回顾了Docker的架构、核心概念和常用命令。说白了,就是给后面的故障排查打基础。
你可能会觉得这些内容太基础。但相信我,真正遇到问题的时候,能快速定位到根因的,往往是那些基础扎实的人。
下一章,咱们开始讲真正的故障排查。到时候你会感谢今天认真复习了这些基础知识的自己。
公众号:蓝海资料掘金营,微信deep3321