第一章: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 镜像与容器的生命周期

你想想看,镜像和容器到底是什么关系?

镜像就像是一个「类」,容器就是「实例」。你可以从一个镜像启动多个容器,每个容器都是独立的。

容器的生命周期大概是这样的:

  1. 创建docker create,创建一个容器但不启动
  2. 启动docker start,启动已创建的容器
  3. 运行:容器正在执行主进程
  4. 停止docker stop,主进程退出
  5. 删除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