使用 Telegraf 收集运行进程计数
作者:Noah Crowley / 产品,用例,开发者
2018年2月27日
导航至
将监控添加到您的堆栈中是快速了解您的应用程序的一种方式,让您能够更快地发现问题,并开始做出关于在哪里投入工程力量的数据驱动决策。开始监控的最直接的事情之一就是您的进程本身——如果没有任何进程在运行,那么 Web 服务器很难提供服务请求,另一方面,如果您运行了比预期更多的程序副本,您会迅速耗尽可用的资源。
在大多数“nix”系统上,可以通过多种方式收集进程信息。根据您正在运行的特定操作系统,您可能能够查看包含有关运行进程和系统一般信息的多个文件的 proc
文件系统,或者您可以使用像 ps
这样的工具,它将有关运行进程的信息输出到命令行。
在这个例子中,我们将使用 Python 和 Ubuntu Linux,但许多概念也可以应用于其他语言、应用程序和操作系统。
在 Linux 上获取进程信息
获取更多系统信息的一个很好的地方是 proc
文件系统,根据 man 页面,“它是一个伪文件系统,提供对内核数据结构的接口。它通常挂载在 /proc
。”如果您在 Linux 机器上访问该目录,您可能会看到以下内容(来自 Ubuntu 16.04.3 的全新安装的输出)
$ cd /proc
$ ls
1 1182 14 18 25 3 399 437 53 60 80 839 bus driver kallsyms locks partitions sys version_signature
10 12 141 19 26 30 4 47 54 61 81 840 cgroups execdomains kcore mdstat sched_debug sysrq-trigger vmallocinfo
1017 1200 15 2 266 31 413 48 544 62 817 890 cmdline fb keys meminfo schedstat sysvipc vmstat
1032 1230 152 20 267 320 414 480 55 66 818 9 consoles filesystems key-users misc scsi thread-self zoneinfo
1033 1231 16 21 277 321 420 49 56 671 820 919 cpuinfo fs kmsg modules self timer_list
1095 1243 164 22 278 369 421 5 57 7 828 925 crypto interrupts kpagecgroup mounts slabinfo timer_stats
11 126 165 23 28 373 423 50 58 701 831 acpi devices iomem kpagecount mtrr softirqs tty
1174 128 166 24 29 381 425 51 59 79 837 asound diskstats ioports kpageflags net stat uptime
1176 13 17 241 295 397 426 52 6 8 838 buddyinfo dma irq loadavg pagetypeinfo swaps version
这里有很多内容!您首先会注意到一系列编号的目录;这些对应于正在运行的进程,每个目录都用该进程的“进程 ID”(PID)命名。您还会看到许多其他目录和文件,其中包含有关内核参数、加载的模块、CPU 信息、网络统计信息和系统运行时间的信息。在每个进程目录内部,您几乎可以找到关于每个单个进程的信息——对我们的用例来说太多了。毕竟,我们只想监控进程是否在运行,也许运行了多少个副本。
当系统管理员登录服务器以验证进程是否在运行时,他们不太可能首先考虑 /proc
。相反,他们可能会使用像 ps
这样的工具,它也提供了有关运行进程的信息。您可能会使用 ps
的几个不同版本,但对于 Ubuntu 上的版本,您可以使用以下命令来获取服务器上所有运行进程的信息
$ ps aux
我们将使用Python创建一些用于测试目的的进程。由于我们实际上并不需要它们执行任何工作,我们将编写一个简单的程序,其中包含一个无限循环和调用Python time模块中的sleep函数,以避免使用不必要的CPU周期。请确保您已经安装了Python,在命令行中输入以下命令
$ python --version
Python 2.7.12
由于我们的程序非常简单,它可以与Python 2或Python 3一起使用。接下来,使用文本编辑器创建一个名为loop.py
的文件,并包含以下内容
#!/usr/bin/env python
import time
while True:
time.sleep(5)
第一行告诉操作系统使用哪个解释器来执行脚本。如果这个程序更复杂,或者使用了Python 2和Python 3之间的不同功能,我们想要指定我们使用的是Python的哪个版本,而不是仅仅说python
。
从文件所在目录运行以下命令使脚本可执行
$ chmod 744 loop.py
然后运行程序,在命令末尾添加&
字符(这告诉Linux在后台运行进程),这样我们仍然可以访问shell
$ ./loop.py &
[1] 1886
使用&
字符运行命令后,运行中的进程PID会列在输出中。如果您再次运行ps aux
,您现在应该在结果列表中看到PID为1886
的Python进程。
在我使用的Ubuntu服务器上,此命令返回了100多个结果,手动搜索这个列表效率太低。我们可以使用另一个命令grep
以及一些Linux内置的功能来缩小结果。grep
命令的作用就像是一个搜索过滤器,我们可以使用Linux的“管道”,即|
字符,将ps aux
命令的输出数据发送到grep
命令的输入。让我们试试看
$ ps aux | grep python
noah 1886 0.0 0.2 24512 6000 pts/0 S 20:14 0:00 python ./loop.py
noah 1960 0.0 0.0 14224 1084 pts/0 S+ 20:56 0:00 grep --color=auto python
首先,我们获取有关所有运行进程的信息,然后我们将这些数据“管道”到grep
命令中,该命令正在搜索任何包含字符串python
的行。果然,我们的Python进程1886
就在结果的第一行。那么第二行是什么意思呢?
当我们运行ps
命令时,输出包括我们在每个进程启动时提供的参数;在这种情况下,由于Ubuntu有一个别名,它会运行grep --color=auto
,所以我们添加了--color=auto
,然后是python
参数,这是我们正在搜索的字符串。因此,我们正在搜索“python”,这意味着字符串“python”将包含在ps
命令的输出中,所以grep
会与自身匹配,因为它包含了它正在搜索的字符串。
解决这个问题的常见方法是搜索正则表达式"[p]ython"而不是字符串"python"。这将导致grep
与任何以方括号内字母之一开头(在我们的例子中只有一个“p”)后跟“ython”的字符串匹配。当我们这样做时,grep
仍然与单词“python”匹配,因为它以“p”开头并以“ython”结尾,但它不会与自身匹配,因为"[p]ython"不匹配该模式。试试看吧
$ ps aux | grep [p]ython
noah 1886 0.0 0.2 24512 6000 pts/0 S 20:14 0:00 python ./loop.py
让我们启动另一个Python进程,看看我们会得到什么
$ ./loop.py &
[2] 1978
$ ps aux | grep [p]ython
noah 1886 0.0 0.2 24512 6000 pts/0 S 20:14 0:00 python ./loop.py
noah 1978 0.0 0.2 24512 6004 pts/0 S 21:13 0:00 python ./loop.py
两个Python进程,两个结果。如果我们想验证正在运行特定数量的进程,我们只需统计命令输出的行数即可;幸运的是,为grep
提供-c
参数可以做到这一点
$ ps aux | grep -c [p]ython
2
让我们使用fg
命令将最新的两个Python脚本之一带到前台,然后使用
$ fg
./loop.py
^CTraceback (most recent call last):
File "./loop.py", line 6, in
time.sleep(5)
KeyboardInterrupt
$ ps aux | grep -c [p]ython
1
太棒了!一个是我们要找的数字。
还有一个命令 pgrep
,它也满足了我们的所有使用案例要求,但它的通用性不如前者。它允许你搜索所有匹配字符串的进程,并默认返回它们的进程ID。它还接受 -c
参数,输出匹配的数量。
$ pgrep -c python
1
使用 Telegraf 收集进程计数
既然我们知道了如何统计服务器上运行的进程数,我们就需要定期收集这些数据。 Telegraf 允许我们以 exec
输入插件的形式执行在 shell 中使用的相同命令,以收集数据。
exec
插件将在 Telegraf 的每个收集间隔期间运行一次,执行配置文件中的命令并收集它们的输出。输出可以是多种格式之一,包括所有支持的输入格式,这意味着如果你已经有了输出某些类型度量数据的 JSON 或其他支持的格式的脚本,你可以使用 exec
插件快速开始使用 Telegraf 收集这些度量。
如果你还没有安装 Telegraf,可以参考此处安装文档。按照 Ubuntu 的说明操作后,你应该可以在 /etc/telegraf/telegraf.conf
找到配置文件。
为了本例的目的,我们将输出写入文件,所以我们需要编辑配置文件中的 [[outputs.file]]
部分
# # Send telegraf metrics to file(s)
[[outputs.file]]
## Files to write to, "stdout" is a specially handled file.
files = ["/tmp/metrics.out"]
## Data format to output.
data_format = "influx"
我们将通过重新启动 Telegraf 应用这些更改,然后检查度量是否被写入到 /tmp/metrics.out
。当从包管理器安装 Telegraf 时,默认启用了 system
输入插件,所以我们应该立即看到度量数据。
$ sudo systemctl restart telegraf
$ tail -n 2 /tmp/metrics.out
diskio,name=dm-0,host=demo writes=7513i,read_bytes=422806528i,write_bytes=335978496i,write_time=23128i,io_time=9828i,iops_in_progress=0i,reads=9111i,read_time=23216i,weighted_io_time=46344i 1519701100000000000
diskio,name=dm-1,host=demo write_time=0i,io_time=108i,weighted_io_time=116i,read_time=116i,writes=0i,read_bytes=3342336i,write_bytes=0i,iops_in_progress=0i,reads=137i 1519701100000000000
不幸的是,exec
插件不知道如何处理多个命令,就像我们上面有的那样,所以我们需要将它们放入一个简单的 bash 脚本中。首先,在你的家目录中创建一个名为 pyprocess_count
的文件,并包含以下文本
#!/bin/sh
count=$(ps aux | grep -c [p]ython)
echo $count
这个脚本除了允许我们使用 exec
插件执行管道命令之外,还有一个次要目标——如果 grep -c
返回零结果,它将以状态码 1 退出,表示错误。这导致 Telegraf 忽略命令的输出,并发出自己的错误。通过将命令的结果存储在 count
变量中,然后使用 echo
输出,我们可以确保脚本以状态码 0 退出。请注意,不要在文件名中包含“python”,否则当脚本运行时 grep 会匹配该字符串。一旦创建了文件,设置其权限,以便任何人都可以执行它,并对其进行测试。
$ chmod 755 pyprocess_count
$ ./pyprocess_count
然后将它移动到 /usr/local/bin
$ sudo mv pyprocess_count /usr/local/bin
接下来,我们需要配置 exec
输入插件以执行脚本。编辑 [[inputs.exec]]
文件,使其看起来像这样
# # Read metrics from one or more commands that can output to stdout
[[inputs.exec]]
## Commands array
commands = [
"/usr/bin/local/pyprocess_count"
]
## Timeout for each command to complete.
timeout = "5s"
name_override = "python_processes"
## Data format to consume.
## Each data format has its own unique set of configuration options, read
## more about them here:
## https://github.com/influxdata/telegraf/blob/master/docs/DATA_FORMATS_INPUT.md
data_format = "value"
我们已经将命令直接添加到命令数组中,所以它将在每个收集间隔期间由 Telegraf 执行。我们还已将 data_format
设置为 "value"
,因为命令将输出一个单个数字,我们使用 name_override
为度量指定一个名称。
再次重启 Telegraf,然后查看 metrics.out
文件,看我们的新指标是否显示。我们不必通过肉眼搜索文件,可以使用 grep
再次搜索包含“python”的任何行。
$ grep python < /tmp/metrics.out
python_processes,host=demo value=1i 1519703250000000000
python_processes,host=demo value=1i 1519703260000000000
python_processes,host=demo value=1i 1519703270000000000
我们使用 <
字符将指标文件的全部内容发送到 grep 命令,这是 Linux 的另一个特性,我们得到几行 InfluxDB 行协议的指标,包括指标名称、Telegraf 添加的主机标签、值(带有“i”表示它是一个整数)和时间戳。
如果我们启动另一个 Python 进程,我们应该在我们的输出中看到值的变化。
$ ./loop.py &
[2] 2468
$ grep python < /tmp/metrics.out
python_processes,host=demo value=1i 1519703250000000000
python_processes,host=demo value=1i 1519703260000000000
python_processes,host=demo value=1i 1519703270000000000
python_processes,host=demo value=1i 1519703280000000000
python_processes,host=demo value=1i 1519703290000000000
python_processes,host=demo value=2i 1519703300000000000
就这样!最后的指标显示有两个 Python 进程正在运行。
下一步
将指标写入磁盘并不是一个很有用的做法,但它有助于确保您的设置正在收集您期望的数据。为了使其具有可操作性,您需要将收集到的数据发送到某个中央存储库,以便您可以对其进行可视化和警报。
这些指标的视觉呈现将是最基本的;我们可能不需要一个完整的图表,因为我们获取的数据在历史记录上不应该有太大的变化。相反,显示一个单个数字(例如,在 Chronograf 的 Single Stat 面板)应该足以让您相信一切按预期运行。
您如何对这些指标进行警报将取决于您具体监控的内容。也许您总是想有一个进程在运行。您可以创建一个警报,每当进程计数低于 1 时发送电子邮件。但是,在发送了几次警报之后,您的团队可能希望自动化启动新进程,如果您的进程崩溃,因此您需要调整警报,以便在看到指标为 0 和发送第一次警报之间有时间间隔;如果您的自动化系统可以快速启动进程,那么就不需要联系人类。
或者,您可能有一个定期生成新进程并杀死旧进程的系统,但任何给定时间不应超过 X 个进程。您可能希望设置一个与上面类似的警报,只是当指标从 0 变为 1 时发出警报,而不是当指标大于或小于 X 时。您可能还想为这个警报设置一个时间窗口;也许在杀死和启动新进程时,系统运行 X+1 或 X-1 个进程是短暂的,是可以接受的。
如果您决定将数据发送到 InfluxDB,您可以使用 Chronograf 和 Kapacitor 来可视化和警报您的指标。您可以在它们的相应文档页面上阅读更多关于 创建 Chronograf 仪表板 或 设置 Kapacitor 警报 的信息。