Docker常用命令与3种日志查看方式详解 看到标题你大概也明白我不打算再堆一遍docker help里的全部输出。日常用得上的 docker常用命令就那些真正难的是把它们放进正确的场景而 docker日志查看这块几乎每个刚接触容器的人都会栽一两次跟头。这篇先把常用命令按“容器生命周期——镜像管理——调试操作”这条线理一遍再把 3种 docker日志查看方式 拆开docker logs、宿主机 json 日志文件、进入容器看应用日志文件。适合刚入门、想从“照抄命令”走向“理解命令”的读者也适合已经被“容器在运行但不知道问题出在哪”折磨过的同学。1. 生命周期命令先把容器和虚拟机的差别揉碎1.1 容器本质是“被隔离开的进程”很多初学者刚接触 Docker 时脑子里会把容器想象成一台“特别轻量的虚拟机”于是习惯性地往里面塞 systemd、塞多个常驻服务最后发现容器要么起不来要么起来以后行为非常诡异。容器的真相其实很朴素它就是一个被命名空间、Cgroups 等机制隔离出来的进程。容器没有自己的完整内核也谈不上“开机”和“关机”它只是在宿主机内核上启动一个用户态进程然后让这个进程以为自己是独立的机器。理解了这一点很多命令的用法就豁然开朗了。比如docker run的作用是创建并启动一个容器但它真正做的事情其实是“启动一个进程”。这个进程如果退出了容器就跟着变成 exited 状态。所以你会发现官方镜像里的 nginx、redis、mysql 都是以“前台进程”方式运行的绝不会走传统虚拟机上那种“后台守护进程 systemd 托管”的路子。1.2 run、stop、rm 的组合链条看一组日常最常用的生命周期命令# 前台运行一个一次性容器跑完自动删除 docker run --rm hello-world # 后台运行一个 Web 服务命名为 webapp docker run -d --name webapp -p 8080:80 nginx:alpine # 停止、启动、重启 docker stop -t 30 webapp docker start webapp docker restart webapp # 强制删除 docker rm -f webapp-d代表 detached让容器在后台运行不然它会一直占着当前终端。--name是必须养成的习惯后面绝大多数命令都可以直接用名字而不用记一长串容器 ID。--rm适用于临时容器进程退出后顺手清理文件系统层特别适合做实验和跑一次性脚本。docker stop默认会给容器主进程发 SIGTERM然后等 10 秒如果进程还不退出再发 SIGKILL。-t 30就是把等待时间改为 30 秒。生产环境里如果你的应用有优雅退出的逻辑一定要把 stop 超时时间调够否则每次停服务都像直接拔电源。docker restart是 stop start 的组合适合改了配置想快速重置进程的场景。docker rm -f比较暴力直接删容器运行中的也会强行终止。我的建议是除非容器状态已经没法正常 stop否则不要把它当常规操作。删掉之前先想一想这个容器的数据是不是写在可写层里如果没挂载外部卷删了就真的没了。2. 镜像和容器的日常高频命令认识它们才算真正上手2.1 镜像拉取、删除和空间清理镜像命令看起来不多但踩坑点很集中。最典型的错误就是镜像删不掉报错说有容器在用。这其实不是镜像命令的问题而是没意识到镜像和容器的依赖关系。# 搜索和拉取镜像 docker search nginx docker pull nginx:alpine # 查看本地镜像列表和详细信息 docker image ls docker image inspect nginx:alpine # 删除镜像 docker rmi nginx:alpinedocker rmi只能删掉没有被容器引用的镜像。一个镜像可能有多个版本也可能被多个容器共用。当你看到一个镜像删不掉时先用docker ps -a找出关联容器把容器删掉再回来删镜像。也可以用docker image prune清理所有悬空镜像用docker image prune -a把没被容器使用的镜像一并清掉但这条命令要谨慎它会连你刚拉下来还没用的镜像一起删。docker image inspect查的是镜像元信息比如环境变量、入口命令、暴露端口。排查“为什么这个镜像启动后行为不对劲”时先用它看看 CMD 和 ENTRYPOINT 到底是什么往往能省下一小时排查时间。2.2 进入容器、拷文件、查端口容器运行起来之后你还需要一组“进入现场”的命令。这一组是我使用频率最高的# 查看所有容器包括已经退出的 docker ps -a # 查看容器详细信息 docker inspect webapp # 获取容器 IP-f 是格式化输出 docker inspect -f {{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}} webapp # 进入容器终端 docker exec -it webapp sh # 从宿主机拷贝文件到容器或反向拷贝 docker cp ./nginx.conf webapp:/etc/nginx/conf.d/default.conf docker cp webapp:/etc/nginx/nginx.conf ./nginx.conf.bak # 查看容器内进程和端口映射 docker top webapp docker port webapp # 查看容器可写层做了哪些改动 docker diff webappdocker exec -it优先选 sh因为很多精简镜像没有 bash。如果连 sh 都没有说明镜像极度精简需要回到 Dockerfile 层面加调试工具或者改用别的方式排查。docker cp是我调试配置时最常用的命令。遇到容器里配置写错的情况我不会直接进容器里改而是先在宿主机改好再docker cp进去然后docker restart。这样至少配置文件有副本改坏了也能恢复。docker top和docker port属于快速查看型命令。前者看容器里到底跑了哪些进程后者看端口映射关系。尤其是docker port配合 docker-compose 里一堆端口定义时特别好用能一眼确认宿主机 8080 到底映射到容器的哪个端口。docker diff是个容易被忽略的命令。它能列出容器启动后哪些文件被新增、修改、删除。排查“镜像启动后怎么多出一个奇怪文件”或者“容器里的配置是不是被运行时改过”时它比翻日志快很多。输出里 A 代表新增C 代表修改D 代表删除。3. 日志查看前必须搞懂的一件事stdout 和文件日志不是一回事3.1 一条日志从应用写入到被 docker logs 读到的完整路径如果你直接去搜“docker日志查看”大概率搜到一堆docker logs -f之类的命令。但很多人完全照着敲完之后发现日志要么是空的要么只有一行启动信息。问题多半不是命令写错而是没弄明白容器的日志流动逻辑。容器里的应用日志有两条完全不同的去向。一条是应用把日志写到 stdout 和 stderr这是 Docker 引擎层面的捕获通道另一条是应用自己写文件比如常见的logs/app.log、/var/log/xxx.log。大多数 Web 框架默认走的是第二条路而不是 stdout。Docker 引擎捕获 stdout/stderr 后会交给日志驱动默认的 json-file 驱动会把它落盘成宿主机上的 JSON 文件。你执行docker logs webapp时引擎读取的正是这套捕获文件。整个过程可以理解为容器内进程写入 stdout/stderr ↓ Docker 引擎捕获 ↓ 日志驱动编码并落盘 ↓ docker logs 命令读取并展示这条链路上任何一个环节断掉都会导致docker logs不可用。最常见的断点就是应用根本没写 stdout而是写进了文件。所以你在学日志查看命令之前一定要先想清楚我要看的日志到底是容器的 stdout还是应用自己的文件日志。3.2 日志驱动为什么有的环境里 docker logs 突然不顶用日志驱动决定了 Docker 引擎把捕获到的输出送到哪里。默认是json-file也就是文件形式保存在宿主机上。除此之外还有local、journald、syslog、gelf等。在绝大多数 Linux 环境下默认 json-file 驱动配合docker logs都很稳定。但如果你所在的环境里 Docker 守护进程被改成了 journald 驱动日志会被交给 systemd-journald 管理docker logs一样能读取但原来--since、--tail这类参数在不同驱动下的表现可能会有差异。更极端的情况是使用none驱动它会彻底禁用日志记录这时候docker logs返回空结果一切排查只能靠其他手段。判断当前环境用什么驱动可以执行docker info --format {{.LoggingDriver}}我还遇到过一种情况生产环境为了对接统一日志平台配了 gelf 或 fluentd 驱动但这套驱动本身对docker logs的支持并不完整。结果就是日志确实都存在日志平台里可得用docker logs查的时候感觉时有时无。这不是命令的问题是驱动选型的问题。所以凡是涉及日志排查第一步永远是确认两件事应用有没有写 stdout当前容器用的什么日志驱动。确认完这两点再去套用什么命令都不迟。4. 3种docker日志查看方式一条条从易到难讲透4.1 方式一docker logs 直接读标准输出这是最顺手的查看方式适合大多数官方镜像因为官方镜像里的应用通常都配置成把日志打到 stdout。常用命令如下# 查看最近 200 行日志 docker logs --tail 200 webapp # 跟踪输出类似 tail -f docker logs -f webapp # 只看最近 30 分钟的日志 docker logs --since 30m webapp # 带时间戳查看 docker logs --timestamps webapp--tail一定要养成习惯。一个跑了很久的服务日志文件可能有几百兆直接docker logs webapp会把整个文件从头到尾吐到终端既慢又伤终端。先--tail 200看尾部再决定要不要扩大范围。-f是跟踪模式适合部署完新版本后观察启动日志。此时别忘了打开另一个终端一边执行启动命令一边看着这里的实时输出。--since支持30m、2h、2024-01-01T00:00:00这类写法也可以在事后排查时快速定位某个时间点。配合--timestamps一起用能方便地确定报错发生时刻。实际项目里如果服务是多容器编排比如 docker compose 起的服务可以在服务目录下执行docker compose logs -f --tail 50一次看所有相关容器的日志。不过它的本质也是调用引擎的日志接口原理和docker logs一样。4.2 方式二在宿主机上翻 json.log 文件当docker logs因为某些原因不太好用时第二种方式的好处就体现出来了。json-file 驱动会把所有容器日志落盘成宿主机上的 JSON 文件所以你完全可以绕过 Docker 引擎直接在文件层面做查看、过滤、统计。前提是你要能找到这个文件路径。最稳妥的方式是让 Docker 引擎告诉你docker inspect --format {{.LogPath}} webapp输出类似于/var/lib/docker/containers/abcd1234.../abcd1234...-json.log。拿到路径后你可以直接# 实时跟踪 sudo tail -f /var/lib/docker/containers/abcd1234.../abcd1234...-json.log # 用 grep 过滤关键字 sudo grep ERROR /var/lib/docker/containers/abcd1234.../abcd1234...-json.log | tail -50 # 按时间范围抓取 sudo awk -F time: {print $2} /var/lib/docker/containers/abcd1234.../abcd1234...-json.log | headjson.log 文件里每一行都是一条 JSON 记录字段包括 log、time、stream 等。生产日志文件很大时用grep、awk在宿主机上直接过滤通常比docker logs更灵活也不占用容器进程资源。用这个方式要注意两点第一文件路径里的容器 ID 是完整 ID用docker ps只能看到前 12 位最好直接用docker inspect --format {{.LogPath}}取完整路径第二这个文件会随容器销毁而删除一旦容器被docker rm文件就没了别指望事后取证。4.3 方式三docker exec 进容器看应用日志文件第三种方式解决的是“应用只写文件、不写 stdout”的场景。很多 Java 后端、Python 服务、Node.js 服务默认都把日志写到容器内的文件里。这时候你想看日志就得进入容器去看# 进入容器终端 docker exec -it webapp sh # 然后进入日志目录查看 ls -lh /var/log/ tail -f /var/log/app.log如果容器里没有 shell也可以不进入交互终端直接执行单条命令docker exec webapp tail -n 200 /var/log/app.log docker exec webapp sh -c cat /var/log/app.log | grep ERROR | tail -50使用这种方式时要注意权限问题。容器里的应用可能以普通用户身份运行日志文件权限有限需要加上--user root参数重新执行docker exec -u root webapp tail -n 200 /var/log/app.log还要注意很多精简镜像里没有vim、less甚至连find都没有所以习惯上会用tail、grep、cat这三个命令搞定大部分操作。把三种方式放在一起看它们的适用场景其实是互补的查看方式日志来源常用命令适合场景注意点docker logsstdout/stderrdocker logs -f webapp官方镜像、标准输出型服务看不到应用写文件日志宿主机 json.log引擎捕获的日志文件docker inspect tail日志量大、要 grep/awk 过滤需要宿主机权限容器删则文件删容器内文件日志应用自行写入的文件docker exec tail应用只写文件不写 stdout需要镜像里有 shell注意权限这三种方式不是单选题。线上排查时我通常的组合拳是先用docker logs --tail 200看引擎层日志再用docker inspect --format {{.LogPath}}快速判断日志落盘位置最后进容器看应用框架自己写的日志文件。三步下来一般就能把问题定位到具体方向。5. 日志实战的坑从日志文件膨胀到容器重建5.1 应用只写文件不写 stdoutdocker logs 就是空的这是新手最容易怀疑人生的问题容器明明在运行应用也可能正在报错但docker logs只有一行启动日志甚至什么都没有。原因就是应用没有写 stdout。比如不少 Java 应用默认的日志配置是输出到文件控制台只打少量启动信息。你指望docker logs看到完整调试日志完全是打错了算盘。碰到这种情况我见过几种处理思路。第一种是改应用日志配置让它同时输出到文件和控制台。这是最干净的方案也不是所有框架都支持双输出配置时需要翻一下日志库的文档。第二种是改 Dockerfile 的入口脚本在容器启动时用tail -f把应用日志文件实时转发到 stdout。这样做的好处是你仍然能用docker logs看日志但要额外注意不要让 tail 变成孤儿进程尽量用exec tail -f让它成为主进程。第三种是直接改变运维习惯回到第 4 节的第三种方式进容器看文件。我的建议是生产环境尽量走第一种和第三种因为它们不改变容器的进程树也不引入额外常驻进程。真要在入口脚本里转发 stdout务必保证转发进程能拿到 SIGTERM 并正常退出否则 stop 容器时会白白等满超时时间。5.2 日志文件越来越大轮转参数怎么设置才有效json-file 驱动默认不会对日志文件做大小限制。一个高频服务跑上几天容器日志文件可能膨胀到几个 GB直接拖慢宿主机磁盘和 IO。解决方式是在 Docker 守护进程配置文件里加上轮转参数。通常路径是/etc/docker/daemon.json{ log-driver: json-file, log-opts: { max-size: 50m, max-file: 5 } }这段配置的含义是单个日志文件最大 50MB超过后自动轮转最多保留 5 个文件。也就是说单个容器最多占用 250MB 日志空间。改完 daemon.json 后需要重启 Docker 服务而且这个配置只对之后新建的容器生效已存在的容器必须重建才能应用。如果你只想针对某个容器单独限制不需要动全局配置docker run -d --name webapp \ --log-driver json-file \ --log-opt max-size20m \ --log-opt max-file3 \ nginx:alpine已经膨胀到很大的历史日志想清理又不想删除容器可以这样处理而不是直接rm文件sudo truncate -s 0 /var/lib/docker/containers/abcd1234.../abcd1234...-json.log为什么用 truncate 而不是删除文件因为容器进程可能还持有这个文件的文件描述符如果你直接删除文件磁盘空间并不会立刻释放反而会出现“文件删了但空间没回来”的假象。用 truncate 把文件截断为 0 字节既清了内容又不影响容器继续写入。5.3 容器删了日志就没了时区还总差 8 小时最后说两个不太起眼但很磨人的细节。第一个是日志的存留问题。只要容器被docker rmjson.log 文件就会跟着没。就算容器是被误删如果你没有搭建日志采集系统那这些日志就是彻底找不回来。所以涉及多环境长期运行的容器最好尽早把日志接入集中平台或者至少挂到宿主机目录里。比如启动时加参数-v /var/log/webapp:/app/logs让应用日志直接写到宿主机目录即使容器消失日志还在。第二个是时区问题。docker logs --timestamps显示的时间是容器运行时所在的时区如果容器镜像是默认的 UTC 时区那你看到的时间和本地时间差 8 小时排查时很容易对不上号。处理方式是启动时给容器设置时区环境变量比如-e TZAsia/Shanghai或者挂载/etc/localtime。如果已经跑起来的容器里应用日志时间不对先别改代码先用docker exec webapp date确认容器内系统时间。最后分享一个我自己的习惯写 Dockerfile 时我会在应用日志部分尽量保留 stdout 输出同时在启动脚本里把文件日志目录固定下来并挂载到宿主机。这样不管用本文哪种 docker日志查看方式至少有一条路是通的。排查问题最怕的不是命令不熟而是现场连一条日志都找不到那就真的只能靠猜了。