2. Docker基础与日志管理:Docker核心概念、容器日志的stdout/stderr机制、Docker日志驱动、实战:部署一个打印日志的Nginx容器

好,咱们进入第二章。这一章我打算聊聊Docker的日志管理。说实话,很多刚接触容器的朋友,第一反应是「日志?不就是docker logs嘛」。嗯,这话没错,但只对了一半。容器日志的玩法,比你想象的要深得多。

2.1 Docker核心概念速览

在聊日志之前,咱们先快速过一下Docker的几个核心概念。毕竟,日志是跑在容器里的,不理解容器,谈何日志管理?

  • 镜像(Image):一个只读的模板,包含了运行应用所需的代码、运行时、库、环境变量和配置文件。说白了,就是应用的「快递打包盒」。
  • 容器(Container):镜像的运行实例。你可以把它理解成一个轻量级的、隔离的「小虚拟机」。但它不虚拟硬件,只虚拟操作系统。
  • 仓库(Repository):存放镜像的地方。Docker Hub 是官方的公共仓库,企业内部一般会用 Harbor 或 Nexus 搭建私有仓库。
  • Dockerfile:构建镜像的「配方」。你写清楚要装什么、配什么环境,Docker 就按这个配方一层层构建出镜像。

我个人习惯把容器比作「集装箱」。每个集装箱里装什么货(应用),怎么装(Dockerfile),运到哪里(仓库),都由你说了算。而日志,就是集装箱里那个「运行状态记录仪」。

2.2 容器日志的 stdout/stderr 机制

这是理解容器日志最关键的一环。你想想看,传统服务器上,应用日志通常写进文件,比如 /var/log/app.log。但容器不一样——它默认把日志输出到标准输出(stdout)和标准错误(stderr)。

为什么会这样?因为容器是「一次构建,到处运行」的。如果日志写死在文件里,你换个环境,路径变了怎么办?文件权限不对怎么办?所以 Docker 设计者干脆规定:所有日志都走 stdout/stderr,由 Docker 引擎统一接管。

核心要点:容器内应用打印到 stdout/stderr 的内容,会被 Docker 引擎捕获,然后根据你配置的日志驱动(Log Driver)进行处理。你执行 docker logs 看到的,就是这些被捕获的内容。

我在项目中遇到过一个问题:有个同事把日志写到了容器内的文件里,然后问我为什么 docker logs 看不到。嗯,这就是典型的「没理解 stdout/stderr 机制」。记住:想让 docker logs 看到,就必须输出到 stdout/stderr

2.3 Docker 日志驱动(Log Driver)

Docker 支持多种日志驱动,用来决定「捕获到的日志往哪送」。默认是 json-file,但生产环境很少用这个。我列个表,你一看就明白:

日志驱动 说明 适用场景
json-file 默认驱动,日志以 JSON 格式写入宿主机文件 开发测试环境,单机调试
syslog 将日志发送到 syslog 服务(本地或远程) 已有 syslog 基础设施的团队
fluentd 将日志发送到 Fluentd 收集器 需要统一日志收集、过滤、转发的场景
gelf 发送到 Graylog 或 Logstash 使用 ELK/EFK 栈的团队
journald 发送到 systemd journal 使用 systemd 的 Linux 发行版
none 禁用日志记录 性能测试或不需要日志的场景

我的建议:生产环境强烈推荐使用 fluentdgelf 驱动。因为 json-file 驱动默认不轮转日志,时间长了会撑爆磁盘。我曾经见过一个线上事故,就是 json-file 日志把 /var 分区写满了,导致整个宿主机挂掉。从那以后,我再也不敢在生产环境用默认驱动了。

2.4 实战:部署一个打印日志的 Nginx 容器

光说不练假把式。咱们动手部署一个 Nginx 容器,看看它的日志是怎么输出的。

2.4.1 启动 Nginx 容器

# 拉取 Nginx 镜像
docker pull nginx:latest

# 启动容器,映射端口 8080
docker run -d --name my-nginx -p 8080:80 nginx:latest

# 查看容器是否运行
docker ps | grep my-nginx

启动后,你可以访问 http://localhost:8080,看到 Nginx 的欢迎页面。

2.4.2 查看容器日志

# 查看实时日志(类似 tail -f)
docker logs -f my-nginx

# 查看最近 10 行
docker logs --tail 10 my-nginx

# 查看带时间戳的日志
docker logs -t my-nginx

当你访问 Nginx 页面时,会看到类似这样的输出:

2025/01/15 10:30:45 [notice] 1#1: start worker processes
2025/01/15 10:30:45 [notice] 1#1: start worker process 29
172.17.0.1 - - [15/Jan/2025:10:31:02 +0000] "GET / HTTP/1.1" 200 615 "-" "curl/7.68.0"

注意看,Nginx 的访问日志和错误日志都混在一起了。这就是 stdout/stderr 机制的特点——所有输出都合并到同一个流。如果你想把访问日志和错误日志分开,需要在 Nginx 配置里分别指定输出到 stdout 和 stderr。

2.4.3 使用 json-file 驱动查看日志文件

默认情况下,日志存在宿主机的 /var/lib/docker/containers/<容器ID>/<容器ID>-json.log 里。你可以直接查看这个文件:

# 获取容器 ID
CONTAINER_ID=$(docker ps -q --filter name=my-nginx)

# 查看日志文件
sudo tail -f /var/lib/docker/containers/$CONTAINER_ID/$CONTAINER_ID-json.log

你会看到 JSON 格式的日志,每条记录包含 logstream(stdout 或 stderr)、time 三个字段。这就是 json-file 驱动的「原貌」。

2.4.4 切换日志驱动为 syslog

咱们试试切换到 syslog 驱动:

# 启动一个使用 syslog 驱动的 Nginx 容器
docker run -d --name my-nginx-syslog \
  --log-driver syslog \
  --log-opt syslog-address=udp://localhost:514 \
  --log-opt tag="nginx" \
  -p 8081:80 nginx:latest

这样,Nginx 的日志就会发送到本机的 syslog 服务。你可以用 journalctl 或查看 /var/log/syslog 来确认。

注意:切换日志驱动后,docker logs 命令可能无法正常工作。因为 docker logs 只支持 json-filejournald 驱动。如果你用了 syslogfluentd,就得通过对应的日志系统来查看日志了。

2.4.5 清理容器

# 停止并删除容器
docker stop my-nginx my-nginx-syslog
docker rm my-nginx my-nginx-syslog

2.5 本章小结

这一章咱们把 Docker 日志的「根」给刨清楚了。核心就三点:

  • 容器日志走 stdout/stderr,这是设计哲学,不是 bug
  • Docker 通过日志驱动决定日志的「归宿」,生产环境别用默认的 json-file
  • 实战中,Nginx 的访问日志和错误日志默认混在一起,需要的话可以配置分开

下一章,咱们会深入聊聊 Docker Compose 和更复杂的日志收集方案。到时候,你会看到这些基础概念是如何在真实项目中落地的。

嗯,今天就到这儿。记得动手敲一遍代码,光看是学不会的。