docker logs

docker logs命令

$ docker logs [OPTIONS] CONTAINER

Options:

  • 无参数:直接显示容器的所有日志信息
  • -t, --timestamps :显示时间戳
  • --tail n:从尾部开始显示n行日志
  • --since string:从某个时间开始显示容器日志
  • -f, --follow:将当前时间点,容器日志文件 <container-id>-json.log 中的日志信息全部打印;此时间点之后所有的日志信息与日志文件无关,直接接收goroutine 往日志文件中写的文件描述符,并显示
  • --details : 显示更多的信息

示例:docker logs -t cid

docker logs 命令会吧容器内的stdout、stderr 标准输出和标准错误都显示出来。具体原理见下文

原理

如果没有 Docker,您的应用如何打印日志?普遍情况有以下两种:

  1. 向标准输出(stdout)中打印日志;
  2. 设置日志文件 app.log (或其它文件名),向此文件中打印日志。

Docker 从诞生伊始,就从未对用户应用做出标准性规范,日志也不例外,从未有过限制。既然如此,Docker 容器应用的日志也不外乎以上两种。第二种很好理解,依然往容器中某个日志文件打印;然而第一种,应用通过标准输出(stdout)的方式打印日志,该如何呈现给用户?

对于日志文件,Docker 不可能也不应该深入应用内部逻辑,截获并接管日志文件内容,这只会破坏 Docker 的通用性。但是对于 Docker 容器内应用的标准输出,Docker 则是做了工作的。具体实现,可以参考下图:

当输入docker logs的时候会转化为Docker Client向Docker Daemon发起请求,Docker Daemon 在运行容器时会去创建一个协程(goroutine),绑定了整个容器内所有进程的标准输出、标准错误文件描述符。因此容器内应用的所有只要是标准输出、标准错误日志,都会被 goroutine 接收,Docker Daemon会根据容器id和日志类型读取日志内容,最终会输出到用户终端上并且通过json格式存放在/var/lib/docker/containers目录下。

1、docker exec和容器日志:

1)做一个实验:

  • Experiement 1:通过 Docker 运行一个应用,应用中将日志打印到标准输出,然后通过 docker logs 查看日志。
  • Experiement 2:运行一个 Docker 容器,随后 docker exec 命令进入这个容器,接着通过 echo、cat 等命令向容器的标准输出中打印内容,最后通过 docker logs 查看日志。

实验是检验真理的唯一标准。您会发现,Experiement 1 中,查看日志会有日志;而 Experiement 2 中却找不到 echo、cat 等命令标准输出的日志内容。Experiement 2 做完,瞬间毁三观,难道以上内容有差错?可以明确告诉您没有,那矛盾如何会存在?

2)容器进程树和docker exec:

翻看 Docker 的历史,您会发现 Docker 一直在强调「Application」一词,Docker 也希望能为分布式应用提供容器化的解决方案。

从 Docker 化应用软件的生命周期来看,开发工作似乎位于 Docker 的构建之前,而后容器化的测试、部署与运维都与 Docker 容器息息相关。不得不说,Docker 思维下的应用软件,管理流程与传统场景有着很大的区别。

最大的区别当属 Docker 容器运行环境的封闭性。单一应用的运行,使得容器内部缺少功能丰富的服务。虽然用户可以通过 Docker 的层面,获取部分容器的信息,但是依然无法直接获悉应用内部的状态信息(虽然 Docker 倡导容器运行无状态的应用),比如应用容器内部持久化的日志文件、应用容器内部的临时存储等等。而这些信息在传统模式下,都可以较为轻易的获取。

为了缓解容器运行环境封闭性带来的使用障碍,Docker 提供了 docker exec 命令,方便用户在容器之外让容器执行指定的命令,以实现用户的需求。

随着 Docker 的普及,相信越来越多的 Docker 爱好者已经意识到:

  1. Docker 容器其实就是若干进程构成;
  2. 而「一般情况下」这些进程呈现出「树状」关系;
  3. 容器内 pid 为 1 的进程为容器主进程;
  4. 一旦容器主进程退出,容器内所有进程退出。

其实,在 Docker 1.3 之前还不支持 docker exec 的时候,以上观点鲜有不妥,但是现在的情况却已然不同,且看下图:

简述上图中 docker exec 的原理可以理解为以下两个步骤:

  • Docker Daemon 创建一个用户指定的进程 /bin/bash ,因此 /bin/bash 的父进程为 Docker Daemon;
  • Docker Daemon 为进程 P5 设定限制,如:加入容器主进程 P1 所在的隔离环境(namespaces),与其他进程一样受到资源限制(cgroups)等。

观察上图,分析原理,不难发现,容器内部的进程关系已然不是树。然而,为什么总是强调「树状「」关系呢?答案是:树状的继承关系,有利于容器管理。以上文实验「docker exec 的标准输出不会作为容器日志」为例,Docker Daemon 创建容器主进程时,负责接管主进程的标准输出,从而保证容器主进程下所有进程的标准输出被接管,然而 Docker Daemon 在新创建 docker exec 所需执行的进程时,后者的标准输出并未与容器主进程作关联,也并未被 Docker Daemon 特殊处理,故 docker exec 所执行进程的标准输出不会进入容器的日志文件中。

虽然 docker exec 所执行的进程也是容器的一部分,但是更准确的理解 Docker 容器的日志,可以是这样子的:Docker 容器的日志只负责应用本身的标准输出,不包括 docker exec 衍生进程的标准输出。

虽然本文标题与日志相关,但是几乎都在谈论 docker exec ,既然说到,不妨在这个屡试不爽的命令上提个醒:

(1)docker exec 完成容器内外的交互,但希望完成大量标准输出时需谨慎;

(2)每次 docker exec ,Docker Daemon 均会记录一次 execID,切忌过于频繁。

从描述 docker exec 的原理到引入容器日志的一些小 trick,再加上前文的 docker logs 分析,相信很多人都会觉得对 Docker 容器的日志有了一个较为完整的认识。

2、Docker时代的日志

应用 Docker 化之后,日志依然是一件非常棘手的事。笔者认为:容器内部日志文件 app.log 与 docker logs 依然无法满足传统模式下对日志的需求。

目标:通过标准输出,传递 Docker 容器内部的应用持久化日志文件。

问题假设:原先应用 Dockerfile 的 CMD 指令为 CMD ["python", "app.py"] ,应用打印日志的路径为 /var/log/app.log 。

具体实现:

1)原先 Dockerfile 所在目录下,创建一个 run.sh 文件,文件内容为:

#!/bin/bash

tail -F /var/log/app.log &

exec python app.py

2)修改 Dockerfile ,删除原先的 CMD,替换为三个指令。

指令一:ADD run.sh /

指令二:RUN chmod +x run.sh

指令三:CMD ["./run.sh"]

原理剖析:

run.sh 中启动了一个后台进程,使用 tail 命令强制将 /var/log/app.log 的内容传输至标准输出,随后使用 exec 命令将 python app.py 作为容器的主进程运行。示意图:

http://guide.daocloud.io/dcs/docker-logs-9153967.html

https://segmentfault.com/a/1190000010086763

Guess you like

Origin blog.csdn.net/liuxiao723846/article/details/121864929