Docker Engine自带有很多种日志类型,其中journald类型和systemd结合紧密。但是journald查看日志非常不方便,除了可以通过

journalctl -u docker

过滤一下日志来源为Docker这种systemd的服务外,其他的过滤选项大多数是通过变量的完全匹配来实现的。通常情况下包含下面这些容器相关的变量:

  • CONTAINER_ID:默认是容器ID的前12位。
  • CONTAINER_TAG:默认情况下和CONTAINER_ID相同。
  • CONTAINER_ID_FULL:默认是完整的容器ID。
  • CONTAINER_NAME:默认是容器名。

在单机环境使用下,问题不大。因为我们往往可以通过容器名来过滤。但是在Swarm集群环境中使用就非常不便了。因为在集群中使用时,我们通常过滤某个特定的服务的日志。而服务通常分布在一个或者多个,名字为服务名.序号.随机ID的容器中。由于容器名具有很大的随机性,所以就无法简单的通过容器名作为过滤器过滤了。

这种情况下,Docker的journald日志驱动器提供了两类高级的自定义方式,可以便于我们处理。

  1. 采用journald专有的labelsenvenv-regexp三个日志选项。他们分别将容器的标签、环境变量转换成journald日志的变量。例如设置选项labels为com.docker.swarm.service.namecom.docker.swarm.task.id,那么,就会在jounald日志记录里,生成COM_DOCKER_SWARM_SERVICE_NAMECOM_DOCKER_SWARM_TASK_ID这两个变量,值分别就是容器对应标签的值。
  2. 采用大多数日志驱动器都支持的tag日志选项,通过一些模板替换和操作,获取具体的值。

这里我们倾向于选择第二类方式。因为我们的需求是获取指定服务的日志。如果采用第一种方式,要么通过com.docker.swarm.service.name这种label获取,但是生成的journald变量名太长了。要么通过env,需要手动添加一个变量,也比较有侵入性。而第二种方式的话,一方面不需要对容器的配置作任何修改,另一方面生成的journald变量名为CONTAINER_TAG,也比较短小。

那么怎样通过tag选项获取服务名呢?

通过Docker的文档我们知道,Docker的tags参数,默认可以通过{{.xxx}}这种方式,获取到容器的ID、完整名称、镜像等信息。但是不能简单的通过这种方式获取到容器的服务名。但是同时文档中也提到了,可以从日志的运行时loginfo中环境中获取。

翻看Docker日志的运行时环境,Info结构成员很丰富。其中就有一个map[string]string类型的ContainerLabels的成员。顾名思义,肯定就是容器的标签列表了。而所有的Swarm服务都会打上com.docker.swarm.service.name这个标签,值就是容器的服务名。所以,只需要把tag设置为下面的模板即可:

{{index .ContainerLabels “com.docker.swarm.service.name”}}

如果你是通过/etc/docker/daemon.json来配置的Docker Engine,可以在文件中这样配置:

{
	"log-driver": "journald",
	"log-opts": {"tag": "{{index .ContainerLabels \"com.docker.swarm.service.name\"}}"}
}

注意为了同时符合JSON语法以及模板的Go语言语法,模板的双引号应该转义。 如果你是通过命令行参数的方式配置的Docker Engine,可以添加如下命令行参数:

--log-driver=journald --log-opt tag='{{index .ContainerLabels "com.docker.swarm.service.name"}}'

参考资料: