💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
# 第15章 日志和监控 在虚拟化时代来临时,很多企业迫切需要一种新手段来监控它们的新的虚拟环境。业内的监控工具也因此需要作出更新,以支持监控宿主机和虚拟机两种不同的环境。如今,相同的问题在部署容器化的基础设施时再次发生。Docker环境下的日志收集和监控乍听起来可能会让人头疼不已,但是如果可以打破现有环境的束缚,并且使用新兴的工具的话,便可以让读者从这一新的虚拟环境中真正受益。 运行在容器里的系统和应用与如今部署在虚拟服务器上的容器也不尽相同。对许多人来说,监控和记录虚拟化系统上的另外的抽象层可能会是一件非常难以想象的事情。无论环境有多复杂、多抽象,我们始终需要建立一套监控和日志系统来了解应用的健康状态及性能,并满足各种审计的要求。事实上,对于企业来说似乎其最大的问题在于,针对这一抽象层现有的工具缺乏足够的可视化能力。接下来,我们重点关注日志和监控这两部分,只要做到这两点用户就可以让Docker容器在自己的环境里成功地运行起来。 需要查看Docker容器里的日志时,有几种不同的方案可供选择。这些方案取决于你的容器所处的状态,是正在运行还是已销毁/已删除。当考虑的是正在运行中的容器的日志方案时,有如下方案可供选择: - Docker原生提供的日志模块支持; - 通过连接正在运行的容器来提取日志; - 将日志导出到Docker宿主机; - 将日志发送到集中式的日志平台; - 将日志装载到其他Docker容器里。 对于一个已经销毁的/已删除的容器来说,用户的选择非常有限。由于容器默认是临时性的,因此用户可能会在它们终止服务时丢失相应的数据。日志数据的丢失可能会不利于对已发生故障的应用的故障排查及定位。为了能够在容器终止时将其日志保存下来,用户可能需要将这些日志从容器导出到宿主机或者线上系统里。 Docker会自动捕获运行在Docker容器里的进程的标准输出和标准出错信息,并且将它们重定向到容器的根文件系统上的一个指定的日志路径里,宿主机上拥有`root`权限的用户可以直接访问这一容器日志。可以通过运行以下命令来获取宿主机上容器默认的日志路径: `docker inspect -f '{{.LogPath}}' <container_id>`因此,运行在Docker容器里的进程若以前台模式运行会让用户能够很方便地利用Docker原生提供的日志功能来记录其输出信息。 Docker的1.6版本还引入了一些开箱即用的日志引擎。默认情况下,Docker采用的是`json-file`,它会将应用的日志以`json`格式存储到前面提到的容器根文件系统的日志路径下。这一点非常人性化,因为许多的集中式日志方案都需要一个`json`格式的数据流来方便建立搜索索引。 可以通过运行Docker客户端内置的`logs`命令来查看容器日志的内容。请注意,运行`logs`命令将会输出从容器启动开始所有的日志,所以最好将它重定向到其他的类似于`more`或`less`这样的查看工具: `docker logs redis`如上所见,Docker命令行客户端会自动将json编码的文件解码并且以文本的格式输出到界面。 如果用户正在检查一个正在运行的容器发生的问题,那么一般便是运行上面这条命令。该命令会告诉Docker从宿主机记录日志的地方找出一个名为`redis`的容器的日志。在默认情况下,如果没有变动,容器的日志会存放到保存容器本身的系统文件的容器根文件系统里。切记在生产环境下运行容器时,如果应用日志的输出量很大,一定要对日志配置轮换或将日志文件存放在一个可扩展的磁盘存储里。如果运行下面这条命令,容器的日志会被源源不断地写到文件系统里,因此用户将可以看到一个持续不断的日志输出: `docker logs redis --follow (or -f for short)`Docker的`logs`命令还支持一个额外的参数以显示文件的时间戳并限制logs命令可以查看的最多行数。当需要调试时间敏感的问题时可以使用--`timestamps`(或者-`t`)参数来显示日志里每行的具体时间戳。也可以使用`--tail`加上一个数值,如`--tail 10,`来显示容器标准输出和标准错误输出的最后10行内容。 Docker原生的`logs`命令目前所能提供的就是这些。例如,用户正在运行`redis`数据库服务器,它会将日志输出到该容器的`/logs/redis.log`里。然而,由于Docker只会捕获`stdout`和`stderr`的输出,因此执行`docker logs redis`将无法显示该文件的输出内容。我们将在本节的后面部分解释该如何处理这些日志。 Docker的第二个日志驱动的选择便是`syslog`。一旦使用它,Docker会将所有的应用日志发送到运行在宿主机上的`syslog`。当然,用户必须在启动容器时明确指定该参数。在`redis`例子里,想要运行的命令应该是这样的: `docker run -d --log-driver=syslog redis`如果现在去查看宿主机上`syslog`的内容,用户会看到我们刚启动的`redis`容器生成的日志内容。由于许多开源的日志传输工具都是用来解析和传输`syslog`里的日志数据的,所以`syslog`是一个非常方便的日志方案。用户可以轻松地以传统的方式复用这些工具,通过将它们直接运行在宿主机上,然后指定宿主机的`syslog`作为它们的日志源,如此一来用户会感觉自己好像回到了没有Docker的时代。 最后,Docker还支持参数为`none`的日志选项,也就是说它会忽略应用程序生成的所有日志。同样地,和`syslog`驱动类似,必须在启动容器时明确指定该参数才能生效。虽然我们不建议在生产环境中运行的应用采取这样的不捕获任何日志的方式,但是它在某些场景下的确是一个非常方便的选择,例如一些容器会生成大量的无用日志然后可能导致宿主机上的文件系统出现真实的I/O灾难。 通常,我们不建议在容器或者宿主机操作系统里查看日志文件。然而,在排障时,用户可能需要根据自己的排查顺序查看一些正在运行的容器里的额外日志信息。接下来,为了继续调查,用户可能需要连接到一个正在运行的容器上。为了连接一个正在运行的容器,用户需要做一些额外的、繁琐的且重复的操作来实现安全访问(`root`和`ssh`)。但是,如果的确需要这样做,用户可以使用Docker原生提供的`attach`命令。下面是一个使用该命令的实际用例: `docker attach redis`连接容器即是连接到该容器里正在运行的进程的shell上。当连接到容器内部时,由于大多数的镜像都是非常轻量地在运行单个进程,因此用户可以在容器操作系统里执行的命令往往也会非常有限。 Docker在1.3版本推出了`docker exec`子命令,它允许用户在正在运行的Docker容器里执行任何命令,包括用户最喜欢的shell。这种方式比将`TTY`连接到正在运行的容器要优雅得多。用户可以通过执行如下命令轻松来运行`bash`(如果容器的镜像里装了`bash`的话),随后便可以在容器文件系统里查看日志或任何其他文件: `docker exec -it redis /bin/bash`上述命令将会在运行`redis`服务的容器里再运行一个新进程。一旦进入到容器里,用户便可以看到一些可能不会被记录到`stderr`或者`stdout`的额外的日志文件。这可以用来帮助调试故障的应用或根据日志文件的内容来评估性能。然而,这并不是一个适用于生产系统的可扩展方案。生产系统一般需要的是一个集中式的日志平台来查看汇总的日志流。 如今,企业通常运行或使用的都是集中式的日志系统。在传统的服务器环境里,人们通常会使用各种客户端软件(如`syslog`)来读取文件系统上存放日志的目录或路径,然后将它们发送到一个集中平台上。为了利用集中式日志系统,用户可能需要将容器里的日志文件转移到宿主系统上。实际上,将Docker日志存放在宿主系统上并不是一件难事。将日志记录到宿主系统里常用的有两种方法。用户可以在`Dockerfile`镜像里使用`VOLUME`指令或使用`docker run -v`参数将容器里的一个文件路径挂载到宿主文件系统上的一个位置上。 当需要将日志存放在宿主系统上时,Docker官方推荐的做法是采用`docker run -v volume`选项来挂载卷。使用`-v`参数便可以灵活地指定文件系统重定向输出的位置。默认情况下,`Dockerfile`里的`VOLUME`指令使用的是容器里的路径,其对应的一般是一个根文件系统的位置。如果用户的根文件系统已经满了,这就会导致用户的宿主机出现一些大问题,而且它很难被清理干净。例如,运行一个`redis`容器并且将它配置为日志存放到`/redislogs/redis.log`,然后将日志文件存放在宿主系统中。 如果`redis`配置文件被设置为将日志放到`/redislogs/redis.log`文件里,用户可以很轻松地在`Dockerfile`里通过`VOLUME`命令(即`VOLUME /redislogs)`将日志归档。在镜像里使用`VOLUME`指令会导致Docker运行时在容器里的`/redislogs`位置上创建一个新的挂载点,然后将内容复制到宿主系统上新创建的卷目录。然而,宿主系统上的卷目录并不是一个众所周知的位置(一般地,它可能会是`/var/lib/docker/volumes/7d3b93bf75d687530a159ae77e61b035424dd0d52605224ff3e3c6479e603824/redislogs/redis.log`这样的路径)。在这种情况下便很难再在宿主机上去配置一个集中式的日志收集客户端来收集数据卷里的文件内容。这也正是我们推荐使用`docker run -v`的方式来将日志持久化的原因。 通过在启动容器时使用`docker run –v`选项,用户可以从本质上将容器里的目录或者文件路径重定向到宿主文件系统上的某个位置上。一般将容器里的日志目录“重定向”到宿主系统的日志位置的做法是使用如下指令:`docker run -v /logs:/apps/logs`。 当用户把容器的日志重定向到宿主机上的某个集中的地方时,如`/logs/apps`,用户便可以轻松地配置一个日志采集客户端,从`/logs/apps`里收集所有的日志,然后将它们发送到集中的日志系统里。这也适用于在单台宿主机上运行多个容器实例的场景。假设用户在同一台宿主机上运行了`nginx`、`redis`以及一个`worker`容器的实例。通过执行如下`docker`命令用户将可以在`/apps`目录里得到3个日志文件: ``` docker run -v /logs/apps/nginx/nginx.log nginx docker run -v /logs/apps/redis/redis.log redis docker run -v /logs/apps/worker/worker.log worker ``` 运行在宿主机上的日志采集客户端,如果它被配置为从`/logs/apps/**/*.log`读取日志,便能够收集所有的3个日志文件然后将它们发送出去。以上便是一个简单的整合用户的容器及日志采集客户端的方法,这样一来,开发/运维团队便都能轻松记住该到哪去查找日志文件。 如果要将日志发送到一个集中式的日志系统里,除了将它导出到宿主机似乎别无他法。实际上,用户无需把日志文件重定向到宿主系统。取而代之的是可以采取其他的办法将日志导出,例如,用户可以在容器里的应用进程一侧运行一个日志采集客户端,然后将收集到的日志通过网络直接发送出去。如果运维团队支持在一个容器里运行多个进程,用户可以把容器简单地看成是一台服务器,然后在容器启动的时候安装和配置该客户端。 这里以标准的`syslog`为例。运行在容器里的应用会把日志传到`/logs/apps`里,然后安装和配置一个`syslog`守护进程,从该目录位置下读取任何存在的日志信息。随后,它被配置成可以将日志发送到一个集中式的日志服务器里。一旦该目录下有新文件加进来,`syslog`守护进程将会如预期那样读取和传输对应的日志数据。 这一模式的确也很有价值,然而它存在一些明显的短板。这些短板包括,若在一个容器里运行多个进程,这可能会导致容器在进程利用方面变得更加笨重,而且它在服务启动脚本里增加了一些额外的复杂度,并使得应用隔离的便利性荡然无存。在这个模式下,如果一个系统要运行10个容器,用户就得在宿主机上运行10个`syslog`服务。如果这些`syslog`客户端能够被整合到一个单一的进程的话性能也许会更好。 将日志导出到在线系统的另一种办法是直接从应用中获取。假设在容器里运行一个Java应用,该Java代码被配置成向代码函数中写入日志文件。该函数还可以设置成将日志发送到远端服务器上的一个集中式队列里(如[Kafka](http://kafka.apache.org)),之后这些日志数据会交由其他系统处理。这一方案使得开发人员能够很轻松地配置一个可扩展到任意集中式系统的日志模型。然而,这一模式只适用于应用在远程日志采集函数方面均采用同样一段Java代码的场景。如果另外有一个容器运行的是一段Python脚本,那么便意味着就得再写一个Python实现的远程日志函数。 这最后一个从容器里获取日志的方案在业界正逐渐变得流行起来。通过在系统上的其他容器之间使用共享的数据卷,用户便可以在其他正在运行的容器里提取自己所需的数据(在这个例子里即日志)。我们喜欢将这个从其他容器一侧挂载日志的做法比作在一辆摩托车上安装了一个侧座。在一个系统上有多个容器的场景下采用这一方案可以节省一些不必要的处理时间。例如,如果系统上的每个容器都通过运行一个服务的方式来发送它的日志,那么由于每个容器里运行的是冗余的服务,因此会浪费一些不必要的资源。用户可以把各个容器里所有的日志放到一个单独的容器里面,然后将其集中起来并发送到一个集中式的日志系统。由于将所有的事务都整合到了一个统一的日志采集服务,因此用户便可以节省一大笔的资源。 我们不妨用两个容器来举一个简单的例子。一个容器运行一个`redis`服务,并且将日志事件发送到它的容器中的/logs/redis.log位置,对应的即是一个数据卷/logs。如果我们在`docker run`执行时运行标志`--volumes-from`来启动另外一个容器,用户就能在这个新容器里提取/logs数据卷的数据。当然,这样做可能在概念上不容易理解,所以接下来我们会列举一些实际的命令来帮助用户更好地理解这一过程。 首先,我们会启动一个新的名为`redis`的容器。随后,我们会使用`-v`参数创建一个新的数据卷,由于我们之前通过`--name`参数指定了该容器的名称,因此其他容器可以很方便地从它这里提取数据。 `docker run -d -v /logs --name redis registry/redis`然后,我们可以启动一个日志收集容器,使用`--volumes-from`将第一个容器创建的/logs数据卷挂载到容器里面。注意别忘了带上`redis`的名字。 `docker run -d --volumes-from redis --name log_collector registry/logcollector`这使得一个运行了大量Docker容器的系统具备了访问共享资源的能力。这类案例在像Mesosphere这样的网格式部署场景中尤为常见。 Docker提供了多种不同的方案来采集和查看日志。我们建议的做法是选择一个最合适自己的基础设施的、易于维护且可扩展的方案。一般公司最终选择的也会是那套在他们的环境里工作最稳定的模式。那么,现在让我们一起来看看该如何监控Docker容器吧。 对于Docker容器的监控归根到底是用户要怎样监控运行在容器里的服务以及采集哪些指标数据。用户所采取的Docker容器监控的手段实际上取决于现有的工具以及监控的形式。我们建议选择那款团队喜欢并且用的舒心的工具。大公司里一般可能采用的会是一些业界成熟的监控工具,如Nagios,而当需要监控一项像Docker这样的新兴技术时可能就需要发挥自己的创造力了。创业型公司则往往一开始使用的就是最新潮、最棒的技术,而它们一般都已经加入了对Docker的支持,如New Relic、Datadog或Sysdig。无论哪款工具,在绝大部分的场景下一般都能够工作得很棒,因此这只关乎团队究竟偏向于哪个以及它本身的成本收益比。 从某种意义上来讲,监控和从容器里获取日志的形式非常相似。用户完全可以通过像使用一个init文件来管理运行在系统上的服务那样去监控一个正在运行中的容器。另外一种选择是去监控Docker的子系统(使用`docker ps`或`docker stats`),只要容器仍然在运行,它就可以被认为是健康的。有些公司甚至把监控客户端像传统服务器那样放到了容器里面。用户可以通过访问应用层的如`http://<service>/health`这样的端点服务或一些其他措施来监控容器的健康状况。最后,如果用户采用的是Etsy工程方案,用户可能会对系统里的所有事情做些相应的权衡,最终可能会选择通过在宿主机上、Docker服务以及容器本身的健康状况方面采集相应的指标数据来完成监控。 许多已经开始使用Docker容器的企业就等于已经切换到了一个面向服务的架构(SOA),这可能就需要建立一套全新的监控标准。我们不打算深入探讨如何监控SOA环境,但是读者应该意识到这里面的区别。SOA里的服务通常都是以临时模式运行的。这就意味着,如果服务运行在容器里的话可能会挂掉,但是,这并没有什么太大问题。一个服务可以使用运行相同服务的多个容器以负载均衡的方式承载,然后如果它们停止服务的话会自动重启,并自动去重新注册自己的服务发现,随后它们便可以恢复并且这中间没有任何的宕机损失。一个负载均衡的临时系统通常监控的重点会是应用服务本身的健康状况而不是单台服务器上的单个服务。绝大多数情况下这便意味着服务需要一个外部系统来监控其应用层的状态或监控它服务端口的可用性,这可能就需要利用一些新的监控工具才能办到。 监控服务器、服务及应用的方式多种多样。下面我们通过一款名为Datadog的工具来监控一个简单的环境,然后运行一个简单的Python脚本来监控HTTP端点服务(如图15-1所示)。Datadog提供了一个基于主机的客户端来监控服务器,然后上面运行一个statsd的后端服务,用来完成应用级别的监控。在这个例子里,我们将会运行一台单独的服务器,然后在上面运行一个单个的Docker容器。我们会监控宿主机的CPU、内存以及磁盘IO,并且会从Docker守护进程里收集一些统计数据。紧接着,我们会使用一个运行在其他系统上的Python脚本来监控应用的HTTP端点服务,它会把一些指标数据发送到StatsD后端。最后,我们将端对端地监控一个在Docker容器里运行的系统。 ![图像说明文字](https://box.kancloud.cn/416c0b8d66d05e81a36fe23ade49632d_700x488.jpeg) 图15-1 大多数服务器所处的环境里都会部署一个监控系统,它会负责CPU、内存以及磁盘IO这些传统的监控指标的处理和告警。当它们监控的是宿主机上的这些指标时,大部分监控系统也许还能正常运转。但是如今应用服务是运行在Docker里的一个单独进程,使用一些如`ps aux|grep nagios`或`service nagios status`之类的传统命令来监控它们不太容易。这时候,用户就需要查看自己的监控系统是否能监控Docker进程的状态或者能否调用它提供的API来提取健康状况的数据。如果它还没有集成Docker的话,要是用户觉得有必要,那么可以考虑自行采取一些措施来监控它。 在这个案例场景里,使用Datadog来监控宿主机会是一件非常简单的事情。Datadog的客户端和绝大多数传统的监控系统客户端一样,它会安装到本地然后将采集到的监控指标数据发送到一个集中式的系统里。这就跟Nagios、Zabbix或Hyperic监控客户端做的事情没什么两样。安装Datadog客户端很简单,用户只需要照着Datadog的安装命令就能把它们的宿主机客户端(在这里即Ubuntu)快速地配置起来。这里我们使用的命令是`DD_API_KEY=cdfadffdada9a... bash -c "$(curl -L https://raw. githubusercontent.com/DataDog/dd-agent/master/packaging/datadog-agent/source/install_agent.sh)"`。在客户端安装成功后,它便会立即开始将所收集到的数据发送到Datadog的基础设施中。几分钟后,宿主机便能够将用户所有的CPU、内存和磁盘IO的具体指标收集到它们的监控数据浏览器和汇总看板里。一旦数据汇总到了系统里,我们便可以据此设置告警邮件,或如果某些员工需要还可以定制一些告警页面。图15-2所示是采集后的数据展示的一个简单看板。 ![http://write.epubit.com.cn/api/storage/getbykey/original?key=1603e314dd63522cc0e0](https://box.kancloud.cn/6fd3211b005e6c104162beb9b57d4a6b_1002x571.jpeg) 图15-2 在这种情况下,用户最多只能看到大多数基础设施团队针对应用环境想要监控内容的三分之一。在这个场景里,我们只能看到主机层面的监控指标,如CPU、内存和磁盘IO。我们无法知道系统里单个进程或服务的使用情况。最可能出现的场景便是当CPU占用率过高时,用户想知道大部分资源具体是被哪个容器,进程或者服务占用的。问题的核心在于我们需要采集系统上运行的每个Docker容器的性能指标,然后据此制定相应的策略,以减少平均维护时间(MTTR)。就目前来说,我们对于系统上运行的Docker服务以及正在运行的容器的性能或者健康状况几乎一无所知。因此,接下来让我们一起来看看如何监控Docker服务以及如何才能采集到相应的指标数据。 Docker提供了多种方式来监控容器以及从系统中采集数据。首先。我们将介绍Docker原生提供的几个不同的命令。随后,我们将介绍如何使用Datadog来监控Docker守护进程以及怎样采集容器的监控指标。默认情况下,Datadog不会通过它们的API去采集Docker的监控指标。用户需要到它们的网站上开启集成支持。一旦开启该集成,Datadog便能从Docker中采集相应的监控数据,这样一来用户便可以实现大规模的容器的监控。在我们讲解例子之前不妨先使用`docker ps`看一下容器当前的状态。 当在Docker容器里运行服务时,用户将无法再依赖以前的一些众所周知的工具来监控容器的进程。由于服务或进程是以容器的形式在宿主系统中运行,因此除了Docker服务本身,用户将无法查看它们具体的健康状况。以前像`ps`这样用来检查一个进程是否运行的工具,如今针对运行在Docker下的服务则被抽象需要运行`docker ps`或者`docker info`才能获取到当前作为容器运行的服务的具体信息。 `docker ps`是一个简单的、返回当前所运行容器的状态的命令。要运行它也非常简单。用户只需要在其安装了Docker的系统上输入`docker ps`即可。用户可能无法看到容器相关的所有信息,但是可以得到绝大部分的重要指标以及容器当前所处的状态信息。有些企业甚至只需要运行`docker ps`便能满足他们所有的需求。它允许用户查看当前一个特定容器是否在运行,如果没有的话可以将它重启。下面这个简单案例即是运行一段bash脚本来监控一个特定的Redis容器的运行状态。如果该容器没有处于运行状态就会去重启它: ``` #!/bin/bash # Check for running Redis container, if its not running then start it STATE=$(docker inspect redis | jq ".[0].State.Running") if [[ "$STATE" != "true" ]]; then docker run -p 6379:6379 --name redis -v /logs/apps/redis/:/ logs -d hub.docker.com/redis fi ``` 这是一个非常简单和初级的展示如何监控容器运行状态的用例,而这也是许多企业目前的做法。绝大多数生产环境的编排组件采取类似方法来维护和自动化监控系统的健康状况。当用户所处的环境里Docker的部署已经达到了一定的规模,并且在任意给定的时间内均有大量的容器运行,那么用户很可能需要一款更棒的工具和编排手段。如果想构建自己的容器监控组件(很多公司已经这么做了),Docker除了使用Bash脚本外还提供了其他方式来获取容器的状态,如使用[Docker Python API docker-py](https://github.com/docker/docker-py)或使用Docker的远程[API](https://docs.docker.com/reference/api/docker_remote_api/)。用户甚至可以使用Docker的集群管理系统,如[Shipyard](https://github.com/shipyard/shipyard),来查看多个系统里的不同容器的状态。 现在,读者应该了解了该如何获取系统上容器的运行状态。那么,接下来我们需要做的便是获取容器的性能指标。就以之前系统出现过的高CPU占用率的情况为例。我们需要找出系统里是哪个容器占用了大部分的CPU资源。在这个场景下,用户需要了解Docker提供的另外一个命令。Docker自1.5以上的版本提供了一个很棒的统计API及命令行工具,即`docker stats`,它可以获取容器的实时性能指标,如CPU、内存、网络IO、磁盘IO和块IO。在该API开放之前,它实际上是cgroup使用的一些性能指标的原始导出数据。大部分监控公司采纳了这个新的API,并尝试将其用于从Docker系统中获取更多的监控指标。用户也可以借此命令推出自己的监控系统。`docker stats`命令为用户提供了大量的容器运行时的信息,因此用户可以据此构建整合了所需信息的更复杂的看板。 使用`docker ps`和`docker stats`命令已经让用户在监控容器的基础状态的道路上前进了一大步。然而,当用户将容器从单个系统扩展到更大规模时,用户还需要一个工具来把采集的监控指标聚合起来并扩展到一个易于使用的集中看板上。让我们再来回顾下使用Datadog的这个案例,看看如何在大规模场景下用它来完成监控。 Datadog是通过使用Docker守护进程的套接字来访问Docker API从而实现与Docker的集成。通过访问该API,Datadog能够监控当前宿主系统上运行了多少容器,Docker的CPU和内存占用情况,Docker容器状态更改的事件流,cgroup一些不常见的指标数据,以及Docker镜像的统计信息等。它会为用户提供非常丰富的指标信息,展示当前在容器上运行的容器化服务的健康状态和性能的具体情况。再次重申一遍,默认情况下,Datadog不会使用Docker API来监控,因此用户需要到他们的官方网站上去手动开启这一功能。在开启该功能之后,用户还需要将它加到系统上的Docker组才能让Datadog客户端有权限访问Docker。一旦准备工作就绪,Datadog就会开始从系统里采集监控数据,并自动将这些数据发送到基础设施里。如此一来,用户便能设置自身团队所需的特定Docker监控数据的看板和告警。依照[Datadog提供的具体例子](https://www.datadoghq.com/blog/monitor-docker-datadog/),用户可以使用他们的Metrics Explorer查看对应的监控指标(如图15-3所示)。 ![http://write.epubit.com.cn/api/storage/getbykey/original?key=160348a33954ca7464b6](https://box.kancloud.cn/4a66f2c1487d222bda10ec9acdfbaffb_561x456.jpeg) 图15-3 通过Datadog与Docker的集成,我们得以了解单个宿主机或多主机上有多少容器在运行,容器状态变更的事件流,以及特定镜像的一些指标信息。运维和开发团队如今均可以使用这些信息来实现他们容器级的服务状态及性能的监控。如果有大的变动,我们可以给运维团队发出告警并通知他们这些问题。通过Datadog提供的Docker监控集成支持,用户可以获取一些普通主机级监控客户端不能提供的抽象指标参数和信息。如今,我们能够采集基于宿主机和Docker两方面的监控数据,这样一来整个系统具备了更高的可视化程度。这里面唯一漏掉的便是运行在容器里的应用程序本身的健康状况。现在,让我们一起来看看整个案例的最后一个部分吧。 用户可以通过多种途径来监控运行在容器里的应用的健康状态。由于容器本身是一个完整打包的操作系统,因此可以在容器里面运行任意类型的应用程序。用户可以把正在运行的容器简单地看做是一台普通的服务器,然后在镜像创建过程中在容器里安装一个基于主机层面的监控客户端。但是,我们强烈建议将容器尽可能地保持在比较轻量的水准,最好只运行单个进程。虽然我们不建议一个容器里运行多个进程,但是有些公司的确成功地这样做了,并且它的好处在于有时候可以很方便地与用户现有的监控设施集成。在这个例子里,系统上的容器会运行一个简单的HTTP服务并且对外开放一个/health的端点。为了确认运行在容器里的应用的健康状态是好的,我们可以通过多种手段对它进行监控。用户可以使用StatsD或其他自定义监控框架,如bash或者Python脚本,来监控它。我们先来看看StatsD的方案。 StatsD是一款轻量级的监控工具,它是由Etsy创建的用来简化监控数据的统计和聚合,可以通过网上的一篇[博客](https://codeascraft.com/2011/02/15/measure-anything-measure-everything/)了解到更多细节。Statsd因它轻量和大规模场景下良好的扩展性的特点,已然成为一款非常受欢迎的应用层监控服务工具。针对一个运行在容器里的应用的健康状况的监控,其中一种方案是调用Statsd的库将应用的指标数据发送出去。例如,有个运行在容器里的Web应用,它会获取POST请求,并据此处理一些信用卡数据。为了追踪究竟收到了多少个POST请求,每当接收到一个POST请求时它会把对应的指标数据发送到StatsD服务器。该服务器随后会通过连接到信用卡中心提供的API来处理相应的信用卡数据。用户可以在连接该API的代码里设置记录开始访问的时间。然后在调用返回且信用卡数据被处理之后,代码部分再次记录对应的结束时间。该代码随后计算两个记录时间之间的差值从而获取该任务总共的耗时,然后将另外一个StatsD指标发送出去。而每当它成功响应一次客户端提交的POST请求时,它会发送最后一个标志成功的StatsD指标以记录总共处理的信用卡数量。在本例中,如果将所有采集到的StatsD监控指标绘制到一个看板上,用户便能几乎实时地观察应用的健康状况。用户能看到该容器接收到了多少个POST请求,调用API所花费的时间,以及完成处理的信用卡数量。这是测量运行在容器里的应用健康状态的一种方式。例如,若看板上已完成的信用卡处理数量突然下降,用户可以给它发送一个包含其详细信息的告警。 在本例的最开始我们曾经讲过一个/health的端点服务,那么接下来我们将具体介绍一下这方面的内容。StatsD(和Datadog的具体实现)会将任意字符串的输入看做是一个指标,然后将它们聚合汇总后展示给用户。例如,如果我们想监控http://myservice.corp/health这个http端点服务,可以使用`echo"myservice.status- code:curl -sL -w "%{http_code}" "http://myservice.corp/ health"|c" | nc -u -w0 statsd.server.com 8125`。该命令会检查服务器上这一http端点服务返回的状态码,然后借助netcat将数据发送给StatsD。Datadog还可以和StatsD集成在一起,专门针对用户发送给statsd的监控数据做监控和告警。在本例中,如果该服务是健康的,它会返回一个200的状态码。Datadog将会收到`myservice.statuscode`的监控数据,而它的值即是200。如果该状态码返回的是一个500或者404错误,我们随后便可以使用Datadog通过邮件或专门的呼叫系统去发送一个故障通知。下面的这个Python脚本即是一个采用Datadog结合StatsD实现的具体例子。 ``` import requests # For URL monitoring import statsd # We installed the Datadog statsd module import sys import time sites = ['http://myservice.corp/health'] def check_web_response_code(url): r = requests.get(url,allow_redirects=True,veri- fy=False,stream=True) return str(r.status_code) def send_dogstatsd(options,site): c = statsd.DogStatsd(options.statsd, options.statsport) c.connect(host=options.statsd, port=options.statsport) statname = 'httpmonitor' tags = [] tags += ['site:'+site] r = check_web_response_code(site) c.gauge(statname, r, tags=tags) def monitor_sites(options): for site in sites: send_dogstatsd(options,site) def main(): while True: monitor_sites(options) time.sleep(30); if __name__ == '__main__': sys.exit(main()) ``` 用户可以让上面这个简单的Python脚本在一个容器里运行,然后通过它来监控其他容器的健康状况。这是一个非常简单、轻松地获取应用健康状况的方法。读者可以到StatsD的GitHub项目中了解更多详细内容以及它所提供的额外的核心类库。 总体来说,上述案例已经较为完整地介绍了如何端对端地监控一个系统上的Docker以及正在运行的容器。借助一个单独的工具,我们便能监控服务器本身、Docker守护进程以及容器内的应用服务。在上述案例中,通过使用Datadog,应用服务的监控数据可以被整合到Datadog的复杂看板上,Datadog提供了宿主机、Docker服务及应用健康状态的告警。这是一个非常基础的例子,但是我们希望它可以展示该如何着手监控运行在Docker基础设施里的应用服务。 而这一点也贯穿于整本书。我们希望读者已经了解了在生产环境使用Docker所需要的工具和信息。