Docker日志管理
Docker的日志大致有两种,一是Docker 引擎日志,也就是 dockerd服务自身运行时的日志;二是容器内的服务产生的日志。后一种有一定使用经验的童鞋应该发现有时候我们能通过docker logs查看容器日志,有时候又不能,这是为什么呢?希望本文能对你有一定的帮助。
一 、Docker 引擎日志
Docker 引擎日志一般是交给了 Upstart(Ubuntu 14.04) 或者 systemd (CentOS 7, Ubuntu 16.04)。前者一般位于 /var/log/upstart/docker.log 下,后者我们一般 通过 journalctl -u docker 来进行查看。
二、容器日志
1、常用查看日志命令-docker logs
docker logs CONTAINER显示当前运行的容器的日志信息, UNIX 和 Linux 的命令有三种 输入输出,分别是 STDIN(标准输入)、STDOUT(标准输出)、STDERR(标准错误输出),docker logs 显示的内容包含 STOUT 和 STDERR。在生产环境,如果我们的应用输出到我们的日志文件里,所以我们在使用 docker logs 一般收集不到太多重要的日志信息。
如nginx 官方镜像,使用了一种方式,让日志输出到 STDOUT,也就是创建一个软链接/var/log/nginx/access.log 到 /dev/stdout,默认就能通过docker logs看到nginx的access日志。(删除 rm -rf /var/log/nginx/access.log ,需要重启才能看到效果,就不能看到日志了,可以手动再创建软链接后重启容器恢复查看ln -sf /dev/stdout /var/log/nginx/access.log)。
而httpd 使用的是让其输出到指定文件 ,正常日志输出到 /proc/self/fd/1 (STDOUT) ,错误日志输出到 /proc/self/fd/2 (STDERR)。
[root@k8s-m1 ~]# docker run -id -p 80:80 httpd
root@fdfe62f0dafb:/usr/local/apache2# cat conf/httpd.conf |grep -i log|grep proc
ErrorLog /proc/self/fd/2
CustomLog /proc/self/fd/1 common
当日志量比较大的时候,我们使用 docker logs 来查看日志,会对 docker daemon 造成比较大的压力,容器导致容器创建慢等一系列问题。**只有使用了 `local 、json-file、journald` 的日志驱动的容器才可以使用 docker logs 捕获日志,使用其他日志驱动无法使用 `docker logs`**Docker支持的LogDriver具体来说有以下几种,详情请参考docker官网。(https://docs.docker.com/config/containers/logging/configure/)
2、查看日志驱动
#查看某个容器目前使用的日志驱动
[root@k8s-m1 ~]# docker inspect fd -f {{.HostConfig.LogConfig.Type}}
json-file
#查看系统当前docker设置的日志驱动
[root@k8s-m1 ~]# docker info |grep -i driver
Storage Driver: overlay2
Logging Driver: json-file
Cgroup Driver: systemd
3、Docker日志驱动
Docker 提供了两种模式用于将消息从容器到日志驱动。
(默认)拒绝,阻塞从容器到容器驱动
非阻塞传递,日志将储存在容器的缓冲区。当缓冲区满,旧的日志将被丢弃。
在 mode 日志选项控制使用 blocking(默认) 或者 non-blocking, 当设置为 non-blocking需要设置 max-buffer-size 参数(默认为 1MB)。
支持的驱动
使用 Docker-CE 版本,docker logs命令 仅仅适用于以下驱动程序(前面 docker logs 详解也提及到了)
local、json-file、journald
修改日志驱动,在配置文件 /etc/docker/daemon.json(注意该文件内容是 JSON 格式的)进行配置即可。
示例:
{“log-driver”: “syslog”}以上更改是针对所有的容器的日志驱动的。我们也可以单独为单一容器设置日志驱动。
三、Docker 单一容器日志驱动配置
在 运行容器的时候指定 日志驱动 --log-driver。
1、日志类型为none
[root@k8s-m1 ~]#docker run -itd --log-driver none nginx # 这里指定的日志驱动为 none
[root@k8s-m1 ~]#docker inspect 30 -f {{.HostConfig.LogConfig.Type}}
[root@k8s-m1 ~]#docker inspect 30 -f {{.NetworkSettings.IPAddress}}
[root@k8s-m1 ~]#curl `docker inspect 30 -f {{.NetworkSettings.IPAddress}}`
[root@k8s-m1 ~]#docker logs 30
Error response from daemon: configured logging driver does not support reading
2、日志驱动 local
local 驱动支持的选项
全局日志驱动设置为—local
在配置文件 /etc/docker/daemon.json(注意该文件内容是 JSON 格式的)进行配置即可。
{“log-driver”: “local”,“log-opts”: {“max-size”: “10m” }}
需要重启 docker 即可生效,并只对后面创建的容器生效。
单个容器日志驱动设置为—local
运行容器并设定为 local 驱动。
local 日志驱动 记录从容器的 STOUT/STDERR 的输出,并写到宿主机的磁盘。
[root@k8s-m1 ~]#docker run -itd --log-driver local nginx
[root@k8s-m1 ~]# docker exec -it 23 /bin/bash
root@230b90b9ab90:/# ls -la /var/log/nginx/access.log
lrwxrwxrwx 1 root root 11 Sep 10 2020 /var/log/nginx/access.log -> /dev/stdout
默认情况下,local 日志驱动为每个容器保留 100MB 的日志信息,并启用自动压缩来保存。(经过测试,保留100MB 的日志是指没有经过压缩的日志)
local 日志驱动的储存位置 /var/lib/docker/containers/容器id/local-logs/ 以container.log 命名。
注意事项:经过测试,当我们产生了100 MB 大小的日志时 会有 四个压缩文件和一个container.log:[root@localhost local-logs]# ls -ltotal 32544-rw-r-----. 1 root root 18339944 May 16 09:41 container.log-rw-r-----. 1 root root 3698660 May 16 09:41 container.log.1.gz-rw-r-----. 1 root root 3726315 May 16 09:41 container.log.2.gz-rw-r-----. 1 root root 3805668 May 16 09:41 container.log.3.gz-rw-r-----. 1 root root 3744104 May 16 09:41 container.log.4.gz那么当超过了 100MB 的日志文件,日志文件会继续写入到 container.log,但是会将 container.log 日志中老的日志删除,追加新的,也就是 当写满 100MB 日志后 ,再产生一条新日志,会删除 container.log 中的一条老日志,保存 100MB 的大小。**这个 对我们是会有一些影响的,**当我运行系统时 第一天由于bug产生了 100MB 日志,那么之前的日志就已经有 80MB 日志变成的压缩包,所以我在后续的运行中,只能获取最近的 20MB日志。
3、 默认的日志驱动-JSON
所有容器默认的日志驱动 json-file。
json-file 日志驱动 记录从容器的 STOUT/STDERR 的输出 ,用 JSON 的格式写到文件中,日志中不仅包含着 输出日志,还有时间戳和 输出格式。
json-file 日志的路径位于 /var/lib/docker/containers/container_id/container_id-json.log。相比直接用docker logs查看多了时间戳。
json-file 的 日志驱动支持以下选项:
json-file 的日志驱动示例
# 设置 日志驱动为 json-file ,我们也可以不设置,因为默认就是 json-file
[root@k8s-m1 ~]# docker run \-itd \-\-name test\-log\-json \-\-log\-driver json\-file alpine ping www.baidu.com
b8ac15abf30aeacfea8e6ecd8b9b5df967e5996ed3b7af49946ef1da08489720
查看日志,通过docker logs或者存放在宿主机的文件
[root@k8s-m1 ~]# docker logs b8
[root@k8s-m1 ~]# tail -f /var/lib/docker/containers/b8ac15abf30aeacfea8e6ecd8b9b5df967e5996ed3b7af49946ef1da08489720/b8ac15abf30aeacfea8e6ecd8b9b5df967e5996ed3b7af49946ef1da08489720-json.log
{"log":"64 bytes from 14.119.104.254: seq=202 ttl=53 time=33.848 ms\r\n","stream":"stdout","time":"2023-06-07T01:02:33.112767747Z"}
{"log":"64 bytes from 14.119.104.254: seq=203 ttl=53 time=33.855 ms\r\n","stream":"stdout","time":"2023-06-07T01:02:34.112985708Z"}
{"log":"64 bytes from 14.119.104.254: seq=204 ttl=53 time=33.819 ms\r\n","stream":"stdout","time":"2023-06-07T01:02:35.113281475Z"}
{"log":"64 bytes from 14.119.104.254: seq=205 ttl=53 time=33.080 ms\r\n","stream":"stdout","time":"2023-06-07T01:02:36.112737837Z"}
{"log":"64 bytes from 14.119.104.254: seq=206 ttl=53 time=33.279 ms\r\n","stream":"stdout","time":"2023-06-07T01:02:37.113129431Z"}
{"log":"64 bytes from 14.119.104.254: seq=207 ttl=53 time=33.650 ms\r\n","stream":"stdout","time":"2023-06-07T01:02:38.113798225Z"}
{"log":"64 bytes from 14.119.104.254: seq=208 ttl=53 time=33.337 ms\r\n","stream":"stdout","time":"2023-06-07T01:02:39.113779104Z"}
{"log":"64 bytes from 14.119.104.254: seq=209 ttl=53 time=33.455
4、syslog类型
syslog 日志驱动将日志路由到 syslog 服务器,syslog 以原始的字符串作为 日志消息元数据,接收方可以提取以下的消息:
level 日志等级 ,如debug,warning,error,info。timestamp 时间戳hostname 事件发生的主机facillty 系统模块进程名称和进程 ID
syslog 日志驱动全局配置
编辑 /etc/docker/daemon.json 文件
{“log-driver”: “syslog”,“log-opts”: {“syslog-address”: “[udp://1.2.3.4:514])” }}重启 docker 即可生效,注意调整格式。
单个容器日志驱动设置为—syslog
Linux 系统中 我们用的系统日志模块是rsyslog ,它是基于syslog 的标准实现。我们要使用 syslog 驱动需要使用 系统自带的 rsyslog 服务。
# 查看当前 rsyslog 版本和基本信息
[root@k8s-m2 ~]# rsyslogd \-v
rsyslogd 8.24.0-34.el7, compiled with:
PLATFORM: x86_64-redhat-linux-gnu
PLATFORM (lsb_release -d):
FEATURE_REGEXP: Yes
GSSAPI Kerberos 5 support: Yes
FEATURE_DEBUG (debug build, slow code): No
32bit Atomic operations supported: Yes
64bit Atomic operations supported: Yes
memory allocator: system default
Runtime Instrumentation (slow code): No
uuid support: Yes
Number of Bits in RainerScript integers: 64
See http://www.rsyslog.com for more information.
我测试了将一台服务器上容器的日志传到另一台服务器。如下:
在k8s-m2配置 syslog , 在配置文件 /etc/rsyslog.conf 大约14-20行,我们可以看到两个配置,一个udp,一个tcp ,都是监听 514 端口,提供 syslog 的接收。选择 tcp 就将 tcp 的两个配置的前面 # 号注释即可。
# Provides UDP syslog reception
$ModLoad imudp
$UDPServerRun 514
# Provides TCP syslog reception
$ModLoad imtcp
$InputTCPServerRun 514
然后重启 rsyslog,我们可以看到514端口在监听。
systemctl restart rsyslog
[root@k8s-m2 ~]# netstat -anp|grep 514
tcp 0 0 0.0.0.0:514 0.0.0.0:* LISTEN 21072/rsyslogd
tcp 0 0 192.168.2.141:51492 192.168.2.140:6443 ESTABLISHED 1928/kube-scheduler
tcp 0 0 192.168.2.141:2379 192.168.2.142:37514 ESTABLISHED 17909/etcd
tcp6 0 0 :::514 :::* LISTEN 21072/rsyslogd
udp 0 0 0.0.0.0:514 0.0.0.0:* 21072/rsyslogd
udp6 0 0 :::514 :::* 21072/rsyslogd
在k8s-m1上创建将日志传到k8s-m2的容器:
[root@k8s-m1 ~]# docker run \-d \-it \-p 8080:80 \-\-log\-driver syslog \-\-log\-opt syslog\-address=udp://192.168.2.141:514 nginxcb28bb13dee1fe646a46a254005c886740b88e4f007f6ec94e25afd70b369979
# 在k8s-m1服务器访问nginx
curl 127.0.0.1:8080
# 在k8s-m2服务器查看访问日志tail -f /var/log/messages,当然该文件还包括其他日志。
Jun 7 09:33:44 k8s-m1 cb28bb13dee1[1581]: 172.16.0.1 - - [07/Jun/2023:01:33:44 +0000] "GET / HTTP/1.1" 200 615 "-" "curl/7.29.0" "-"#015
Jun 7 09:33:45 k8s-m1 cb28bb13dee1[1581]: 172.16.0.1 - - [07/Jun/2023:01:33:45 +0000] "GET / HTTP/1.1" 200 615 "-" "curl/7.29.0" "-"#015
Jun 7 09:33:45 k8s-m1 cb28bb13dee1[1581]: 172.16.0.1 - - [07/Jun/2023:01:33:45 +0000] "GET / HTTP/1.1" 200 615 "-" "curl/7.29.0" "-"#015
5、Journald类型
journald 日志驱动程序将容器的日志发送到 systemd journal, 可以使用 journal API 或者使用 docker logs 来查日志。
除了日志本身以外, journald 日志驱动还会在日志加上下面的数据与消息一起储存。
选项
journald 日志驱动全局配置
编辑 /etc/docker/daemon.json 文件·
{“log-driver”: “journald”}**单个容器日志驱动设置为—**journald
[root@k8s-m1 ~]# docker run \-d \-it \-\-log\-driver=journald \-\-log\-opt labels=location \-\-log\-opt env=test \-\-env "TEST=true" \-\-label location=china \-\-name nginx\-journald \-p 80:80 nginx
[root@k8s-m1 ~]# curl 127.0.0.1:80
查看日志 journalctl
# 只查询指定容器的相关消息 journalctl CONTAINER_NAME=nginx-journald
# -b 指定从上次启动以来的所有消息 journalctl -b CONTAINER_NAME=webserver
# -o 指定日志消息格式,-o json 表示以json 格式返回日志消息 journalctl -o json CONTAINER_NAME=webserver
# -f 一直捕获日志输出 journalctl -f CONTAINER_NAME=webserver
# --all 显示完整的日志
如果我们的容器在启动的时候加了 -t 参数,启用了 TTY 的话,那么我查看日志是会像下面一样May
Jun 07 09:42:09 k8s-m1 10016be35a75[1581]: [74B blob data]
Jun 07 09:42:09 k8s-m1 10016be35a75[1581]: [86B blob data]
Jun 07 09:42:09 k8s-m1 10016be35a75[1581]: [94B blob data]
Jun 07 09:42:09 k8s-m1 10016be35a75[1581]: [96B blob data]
显示[96B blob data] 而不是完整日志原因是因为有 \r 的存在,如果我们要完整显示,需要加上参数 --all 。
四、 生产环境中该如何储存容器中的日志
上面我们了解到了 Docker 官方提供了 很多种日志驱动,但是上面的这些驱动都是针对的标准输出的日志驱动。
容器日志分类
容器的日志实际是有两大类的:
1、标准输出的,也就是 STDOUT 、STDERR
这类日志我们可以通过 Docker 官方的日志驱动进行收集。示例:Nginx 日志,Nginx 日志有 access.log 和 error.log ,通过在 Docker Hub 上可以看到 Nginx 的 dockerfile 对于这两个日志的处理是:RUN ln -sf /dev/stdout /var/log/nginx/access.log \ && ln -sf /dev/stderr /var/log/nginx/error.log都软连接到 /dev/stdout 和 /dev/stderr ,也就是标准输出,所以这类 容器是可以使用 Docker 官方的日志驱动。
2、文本日志
存在在于容器内部,并没有重定向到 容器的标准输出的日志。如我们很多的java后端业务服务,日志并没有做重定向,这类容器我们下面有专门的方案来应对。
方案一:当是完全是标准输出的类型的容器
我们可以选择 json-file 、syslog、local 等 Docker 支持的日志驱动。
方案二:当有文件文本日志的类型容器
挂载目录 bind
创建一个目录,将目录挂载到 容器中产生日志的目录。
--mount type=bind,src=/opt/logs/,dst=/var/log/*.log
示例:
# 创建挂载目录/opt/logs
[root@k8s-m1 ~]# mkdir /opt/logs
# 创建容器tomcat并将 /opt/logs 挂载至 /usr/local/tomcat/logs/
[root@k8s-m1 ~]# docker run -d --name tomcat-bind -p 8080:8080 --mount type=bind,src=/opt/logs/,dst=/usr/local/tomcat/logs/ tomcat
6558292625ecc908028714fb798d079db671e71df58c60d872af62a6b433bae3
[root@k8s-m1 ~]# ll /opt/logs/
total 8
-rw-r----- 1 root root 4962 Jun 7 10:34 catalina.2023-06-07.log
-rw-r----- 1 root root 0 Jun 7 10:34 localhost_access_log.2023-06-07.txt
方案三 使用数据卷 volume
创建数据卷,创建容器时绑定数据卷,
--mount type=volume src=volume_name dst=/usr/local/tomcat/logs/ 示例:
# 创建tomcat应用数据卷名称为 tomcat
[root@k8s-m1 ~]# docker volume create tomcat
[root@k8s-m1 ~]# docker volume inspect tomcat
[
{
"CreatedAt": "2023-06-07T11:29:40+08:00",
"Driver": "local",
"Labels": {},
"Mountpoint": "/var/lib/docker/volumes/tomcat/_data",
"Name": "tomcat",
"Options": {},
"Scope": "local"
}
]
# 创建容器tomcat-volume 并指定数据卷为 tomcat,绑定至 /usr/local/tomcat/logs/
[root@k8s-m1 ~]# docker run -d --name tomcat-volume -p 8080:8080 --mount type=volume,src=tomcat,dst=/usr/local/tomcat/logs/ tomcat
#查看数据卷里面的内容
[root@k8s-m1 ~]# ll /var/lib/docker/volumes/tomcat/_data
total 8
-rw-r----- 1 root root 4962 Jun 7 11:29 catalina.2023-06-07.log
-rw-r----- 1 root root 0 Jun 7 11:29 localhost_access_log.2023-06-07.txt
[root@k8s-m1 ~]#
方案四 计算容器 rootfs 挂载点
上面使用挂载宿主机目录的方式采集日志对应用会有一定的侵入性,因为它要求容器启动的时候包含挂载命令。如果采集过程能对用户透明那就完美了。事实上,可以通过计算容器 rootfs 挂载点来达到这种目的,类似上面的volume,只是这种情况下,挂载目录是自动生成的。
和容器 rootfs 挂载点密不可分的一个概念是 storage driver。实际使用过程中,用户往往会根据 linux 版本、文件系统类型、容器读写情况等因素选择合适的 storage driver。不同 storage driver 下,容器的 rootfs 挂载点遵循一定规律,因此我们可以根据 storage driver 的类型推断出容器的 rootfs 挂载点,进而采集容器内部日志。下表展示了部分 storage dirver 的 rootfs 挂载点及其计算方法。
Storage driver | rootfs 挂载点 | 计算方法 |
---|---|---|
aufs | /var/lib/docker/aufs/mnt/ | id 可以从如下文件读到,/var/lib/docker/image/aufs/layerdb/mounts//mount_id |
overlay | /var/lib/docker/overlay//merged | 完整路径可以通过如下命令得到。docker inspect -f ‘{{.GraphDriver.Data.MergedDir}}’ |
overlay2 | /var/lib/docker/overlay2//merged | 完整路径可以通过如下命令得到。docker inspect -f ‘{{.GraphDriver.Data.MergedDir}}’ |
devicemapper | /var/lib/docker/devicemapper/mnt//rootfs | id 可以通过如下命令得到。docker inspect -f ‘{{.GraphDriver.Data.DeviceName}}’ |
示例:
# 创建容器 tomcat-test
[root@k8s-m1 ~]# docker run -d --name tomcat-test -p 8080:8080 tomcat
f71a364e77ae6e6e3ad62ea49f8aa24ca066724987b7d74f1eb28f4822cffc1c
# 查看tomcat-test 容器的 挂载点位置
[root@k8s-m1 ~]# docker inspect -f '{{.GraphDriver.Data.MergedDir}}' f7
/var/lib/docker/overlay2/ffd66194a1758ab960a2e743fc7fd1999148174b88de310398f81aa7235d6f55/merged
# 查看挂载点的目录结构
[root@k8s-m1 ~]# ll /var/lib/docker/overlay2/ffd66194a1758ab960a2e743fc7fd1999148174b88de310398f81aa7235d6f55/merged
total 0
drwxr-xr-x 1 root root 179 Dec 22 2021 bin
drwxr-xr-x 2 root root 6 Dec 12 2021 boot
drwxr-xr-x 1 root root 43 Jun 7 13:47 dev
drwxr-xr-x 1 root root 66 Jun 7 13:47 etc
drwxr-xr-x 2 root root 6 Dec 12 2021 home
drwxr-xr-x 1 root root 6 Dec 23 2021 lib
drwxr-xr-x 2 root root 34 Dec 20 2021 lib64
drwxr-xr-x 2 root root 6 Dec 20 2021 media
drwxr-xr-x 2 root root 6 Dec 20 2021 mnt
drwxr-xr-x 2 root root 6 Dec 20 2021 opt
drwxr-xr-x 2 root root 6 Dec 12 2021 proc
drwx------ 1 root root 24 Dec 22 2021 root
drwxr-xr-x 3 root root 30 Dec 20 2021 run
drwxr-xr-x 1 root root 20 Dec 21 2021 sbin
drwxr-xr-x 2 root root 6 Dec 20 2021 srv
drwxr-xr-x 2 root root 6 Dec 12 2021 sys
drwxrwxrwt 1 root root 29 Dec 23 2021 tmp
drwxr-xr-x 1 root root 19 Dec 20 2021 usr
drwxr-xr-x 1 root root 41 Dec 20 2021 var
# 查看日志
[root@k8s-m1 ~]# ll /var/lib/docker/overlay2/ffd66194a1758ab960a2e743fc7fd1999148174b88de310398f81aa7235d6f55/merged/usr/local/tomcat/logs/
total 8
-rw-r----- 1 root root 4962 Jun 7 13:47 catalina.2023-06-07.log
-rw-r----- 1 root root 0 Jun 7 13:47 localhost_access_log.2023-06-07.txt
方案五 在代码层中实现直接将日志写入redis或者通过日志收集工具进行收集
docker ->redis ->Logstash ->Elasticsearch,通过代码层面,直接将日志写入redis,最后写入 Elasticsearch。
或者
docker(k8s) ->filebeat ->elasticsearch->kibana
docker(k8s) ->filebeat ->redis->logstash->elasticsearch->kibana,通过日志收集工具收集处理后存写入elasticsearch中,最后通过kibana进行展示查看。
以上就是对 Docker 日志的所有的概念解释和方提供,具体采用什么方案,根据公司的具体的业务来选择。合适的才是最好的。