-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathlog-system.tex
623 lines (525 loc) · 34.6 KB
/
log-system.tex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
\chapter{OpenStackのログに\\関する個人的な見解}
\setcounter{page}{1}
\begin{wrapfigure}[0]{r}{0.25\textwidth}
\vspace*{-13\intextsep}
\begin{center}
\includegraphics[width=0.25\textwidth]{img/yukidaruma.png}
\end{center}
\end{wrapfigure}
兎にも角にもログである。これは運用するにあたって一番(ではないかもしれんが)考慮する必要がある。これをまぁいいかって感じで運用をスタートさせてしまうと、あとで地獄を見るであろう(そのほうが後学のためでもあるという意見もある)。今回は、ボク個人が至極個人的な趣味趣向とミーハー精神に基づき構築し運用もしているログのシステムのごく一部について記す。たぶん、これからOpenStackを導入する人にとっては、参考になる部分は、はっきり言って無い。街談巷説、道聴塗説、話半分、そんな本章である。
\section{ログについて考えてみる}
前回出版した本 OpenStackの本 上級編ではこのように言及していた。
\begin{quotation}
Q. 入れたはいいけど監視したいです
A. 何を監視するか決めましょう
まずは仮想マシンの監視ですが、これは普通のOS監視と同じ考え方で大丈夫です。ZabbixでもInfluxDBでも好きな物を使えますので、運用スタイルにあったものを選択しましょう。仮想環境ではマシンが頻繁に作成されては消えていく性質があるため、それに対応できるような監視ソリューションがいいと思います。
コンピュートホストやネットワークノードなど、OpenStackで使われる物理サーバーの監視は普通のログやシステムメトリクスの監視に加えて、OpenStack専用の監視項目を追加する必要があるかもしれません。メモリは実使用メモリと仮想マシン割り当て量が大きく違ってきますので注意が必要です。また、ディスクもThinプロビジョニングですと、あるとき急にディスクがあふれる、なんていうこともあり得ます。libvirtのAPIなどを使用して(\verb|virsh|コマンドなど)仮想マシンホスト専用のメトリクスを取得することを考えるとよいと思います。
\end{quotation}
そう、何を監視するか、というのはとても重要である。死活監視は今回置いておくとして、監視対象のログは大きく2つに分けられる。1つ目は、イベントログ、2つ目は、パフォーマンスログである。イベントログというと、OpenStackの各コンポーネントから出力されたログである。主に、\verb|/var/log/XXX/|配下にあるものを指す。パフォーマンスログとは、OpenStackの各コンポーネントを稼働させている各サーバ、及びKVMホストの性能データを指す。
これらを取得し、ストアし、分析し、障害対応のために役に立てるための定番の構成というものがある。
\begin{itemize}
\item イベントログ (EFK, ELK stack, Splunk, etc)
\item パフォーマンスログ (Collectd, Graphite, Grafana, etc)
\end{itemize}
\subsection{イベントログ}
まずは、イベントログに関して見ていこう。これについては、お金があるなら何も考えずにSplunkを導入しよう。あれは神ツールである。大規模にまで容易にスケールし、ログの構造を考えずにストアが可能で、他のツールと連携しアラートまで行えるのである。Splunkと同じことが出来る製品は他にない。Splunkを導入する予算がないのであれば、OpenStackの正確なイベントログ監視は諦めるべきである。ちなみに、経験上、Splunkそのものの運用には、ほぼ工数を割くことはない。Splunk導入は、まさにOpenStack監視における分水嶺的プロダクトといえる。
簡単な図にすると、こうなる。すべてのノードでSplunk Forwerderというデータコレクターを各OpenStackのホストにインストールする。そして、適切な設定をすれば、送りたいログを思うようにSplunkに転送してくれる。
\begin{lstlisting}
+-------------------------------------------+
| |
| OpenStack (Splunkfowerder) |
| |
+----+----------------+----------------+----+
| | |
| | |
| v |
| +------------------+ |
| | | |
+------>| Splunk |<-----+
| |
+------------------+
\end{lstlisting}
なぜ、Splunk導入を強くすすめるかというと、それは、OpenStackのエラーログ(及び\verb|TRACE|ログ)の出力にある。OpenStackは、知っての通りPythonで記述されている。つまりどういうことかというと、OpenStackのコンポーネントが何かエラーを発生させてPythonの例外処理に入った際、ログはトレース(Pythonのコールスタック)付きのCRITICALなログメッセージの形式で出力される、ということである。
この例を見ていただければ、察しがつくと思うが、このようなログをそのままログ分析用のストレージに格納し分析まで可能にするプロダクトはSplunkだけである。
\begin{lstlisting}
2013-02-25 21:05:51 17409 CRITICAL cinder [-] Bad or unexpected response from the storage volume backend API: volume group
cinder-volumes doesn't exist
2013-02-25 21:05:51 17409 TRACE cinder Traceback (most recent call last):
2013-02-25 21:05:51 17409 TRACE cinder File "/usr/bin/cinder-volume", line 48, in <module>
2013-02-25 21:05:51 17409 TRACE cinder service.wait()
2013-02-25 21:05:51 17409 TRACE cinder File "/usr/lib/python2.7/dist-packages/cinder/service.py", line 422, in wait
2013-02-25 21:05:51 17409 TRACE cinder _launcher.wait()
2013-02-25 21:05:51 17409 TRACE cinder File "/usr/lib/python2.7/dist-packages/cinder/service.py", line 127, in wait
2013-02-25 21:05:51 17409 TRACE cinder service.wait()
2013-02-25 21:05:51 17409 TRACE cinder File "/usr/lib/python2.7/dist-packages/eventlet/greenthread.py", line 166, in wait
2013-02-25 21:05:51 17409 TRACE cinder return self._exit_event.wait()
2013-02-25 21:05:51 17409 TRACE cinder File "/usr/lib/python2.7/dist-packages/eventlet/event.py", line 116, in wait
2013-02-25 21:05:51 17409 TRACE cinder return hubs.get_hub().switch()
2013-02-25 21:05:51 17409 TRACE cinder File "/usr/lib/python2.7/dist-packages/eventlet/hubs/hub.py", line 177, in switch
2013-02-25 21:05:51 17409 TRACE cinder return self.greenlet.switch()
2013-02-25 21:05:51 17409 TRACE cinder File "/usr/lib/python2.7/dist-packages/eventlet/greenthread.py", line 192, in main
2013-02-25 21:05:51 17409 TRACE cinder result = function(*args, **kwargs)
2013-02-25 21:05:51 17409 TRACE cinder File "/usr/lib/python2.7/dist-packages/cinder/service.py", line 88, in run_server
2013-02-25 21:05:51 17409 TRACE cinder server.start()
2013-02-25 21:05:51 17409 TRACE cinder File "/usr/lib/python2.7/dist-packages/cinder/service.py", line 159, in start
2013-02-25 21:05:51 17409 TRACE cinder self.manager.init_host()
2013-02-25 21:05:51 17409 TRACE cinder File "/usr/lib/python2.7/dist-packages/cinder/volume/manager.py", line 95,
in init_host
2013-02-25 21:05:51 17409 TRACE cinder self.driver.check_for_setup_error()
2013-02-25 21:05:51 17409 TRACE cinder File "/usr/lib/python2.7/dist-packages/cinder/volume/driver.py", line 116,
in check_for_setup_error
2013-02-25 21:05:51 17409 TRACE cinder raise exception.VolumeBackendAPIException(data=exception_message)
2013-02-25 21:05:51 17409 TRACE cinder VolumeBackendAPIException: Bad or unexpected response from the storage volume
backend API: volume group cinder-volumes doesn't exist
2013-02-25 21:05:51 17409 TRACE cinder
\end{lstlisting}
Splunkを導入できない場合、この類のログを正確に分析することは諦めよう。考えるだけ時間の無駄である。せいぜい、FluentdのFilterを駆使しまくって頑張ってくれ。
\subsection{OpenStackホストのパフォーマンスログ}
OpenStackホストのパフォーマンスログの扱いは悩ましい。いわゆる、メトリクス監視と言うものなのだが、これは一度製品選定を行うとなかなか他のものに切り替えることができないからだ。大抵の企業は、GraphiteやCollectedやSensuを駆使して監視をしているであろう。が、OpenStackを運用するような企業は比較的インフラ規模が大きくなることを見越しているはずである。前述の監視ツール群でも事足りるであろうが、個人的には、より簡単にスケールアウトさせるためにKafkaをメッセージキューとして使うことを勧めたい。なぜなら、データを転送するクライアント(= KVMホストetc)が増加すると、確実にデータを格納するストレージとの間の通信が詰まるからだ。あと、最近流行ってるじゃん。
ということで、メトリクスに関するデータは、一旦Kafkaにためて、そこからFluentdでConsumerして、Time-Series DBに格納するというのがいいんじゃなかろうか。
データコレクターに何を使うかは考えものである。自分の中では以下の3つが考えられる。
\begin{itemize}
\item Fluentd + dstat and df Plugin
\item Telegraf
\item Metricbeat
\end{itemize}
Fluentd + dstat and df Plugin はそれはそれできちんと動く。ただ、Fluentdはメトリクス監視を目的として作られているプロダクトではなく、公式でdstatやdfのPluginをサポートしているのではない。したがって、Fluentdの互換性に対してそれらのプラグインが追従するかどうか、という問題がある。現在のバージョンでは稼働するのだが、今後使い続けられるかどうかはわからない。そういった意味で、Fluentdによる監視はあまりおすすめしない。ちなみにこのPlugin問題はすべてのPluginに対して言うことが出来る。よって、Fluentdを導入する場合には、GitHub上のFluentプロジェクトで管理されている公式サポートがあるPluginのみを使うことを個人的にはおすすめする。
Telegrafは、InfluxDBに転送することを前提に設計されている。よって、通常メトリクスデータはInfluxDB Line Protocolの形式で転送される。以下のようなスタイルである。
\begin{lstlisting}
<measurement>[,<tag_key>=<tag_value>[,<tag_key>=<tag_value>]] <field_key>=<field_value>[,<field_key>=<field_value>] [<timestamp>]
\end{lstlisting}
これを、Kafkaに転送するのは良いが、Consumeが難しい。TelegrafでConsumeするのであれば、何も考えずにConsumeしてInfluxDBに投入するという流れにすることが出来るのだが、Telegraf自体は汎用性がなく、次章で少し説明するVMのデータのConsumeには向かない。よって、ConsumerはFluentd一択になるのだが、そうすると今度はInfluxDB Line Protocolのパースができなくなる。ただ、TelegrafからJsonでKafkaにデータを送ることが可能であるため、そこからかなり頑張ってFluentdでパースすれば、Telegrafを使うことも可能である。が、あまりおすすめできる方法ではない。
ということで、個人的なオススメはMetricbeatである。このプロダクトはもともとElasticsearchに転送することを想定して作られているためデータはすべてJson形式になっている。よって、Kafka ConsumerとしてFluentdを使用した場合でも、パースすることが容易となる。
\subsection{稼働しているVMのパフォーマンスログ}
OpenStackで稼働中のVMのメトリクススを取得する方法は、公式では無い。しかし、libvirtのAPIを使うことで簡単にVMのメトリクス監視が可能となる。その場合、データコレクターとしてはFluentdを使用すると良い。詳細については後述する。
\subsection{ログ転送の全体像}
大まかな全体像としてはこのような形になる。次の章からは1つずつ細かな説明をする。ここまで言及してなかったが、ログストレージとしては現時点でInfluxDB一択である。Elasticsearchは本来検索のためのものであり、時系列データを取り扱うのに最適とは言い難い。ただ問題は、InfluxDBが単一障害点になるということである(ただし、Enterprise版除く)。これを解決する手段としても、KafkaとConsumerで実現するダブルポスト方法がベターだと考えられる。
\newpage
\begin{lstlisting}
+-------------------------------------------+
| |
| OpenStack (Metricbeat, Fluentd) |
| |
+----+----------------+----------------+----+
| | |
| | |
| v |
| +------------------+ |
| | | |
+------>| Kafka |<-----+
| |
+--------+---------+
|
|
v
+------------------------------+
| |
| Kafka Consumer (Fluentd) |
| |
+-+--------------------------+-+
| |
| |
v v
+------------------+ +------------------+
| | | |
| InfluxDB | | InfluxDB |
| | | |
+------------------+ +------------------+
\end{lstlisting}
\section{Fluentdについて}
前のセクションでSplunkを賞賛したが、Fluentdに関しては使い所を限定すれば、とても良いプロダクトである。Splunkは、データコレクターとしては汎用性は全く無く、Splunkにのみデータ転送が可能である。そういう意味で、Fluentdは、Splunk以外のログストレージ、その他の転送先(例えば、別のKafkaに転送するetc)にデータを転送する場合に、Pluginという形で対応が可能であるため、導入すればログシステム全体の拡張性が高くなる。ただし、前述のPluginの互換性問題には注意されたし。
前の章で、メトリクス監視にはMetricbeatがよいと言ったが、一応dstat + df Pluginを使った例を記す。まぁこれでも監視できるよって感じの例である。一旦Kafkaにデータを送って、それをまたFluentdでConsumeするという方法である。
\subsection{Fluentdを使ったメトリクス監視をする時、Kafka Producerとして動かすFluentdの設定}
dstatが入っていない場合は、事前にdstatをインストールしておく必要がある。またFluentdはv0.12以降をインストールする必要がある。インストールが必要なアプリとFluentdのPluginは以下の通りである。
\begin{itemize}
\item Fluentd v0.12以降
\item dstat
\end{itemize}
また、Fluentdに別途インストールが必要なPluginはこれである。夏辺りまでは、fluent-plugin-kafkaも別途インストールが必要だったが、最新版のtd-agentにはインストール時に同梱されているため別途入れる必要はない。がしかし、このKafka Pluginのバージョンは最新までアップデートしておくことをおすすめする。
\begin{itemize}
\item fluent-plugin-record-reformer
\item fluent-plugin-dstat
\end{itemize}
メトリクス監視用の監視対象上で動くKafka ProducerとしてのFluentdのconfは以下のような設定にする。
\begin{lstlisting}
<source>
@type dstat
tag raw_dstat
option -cmldrn
delay 10
tmp_file /tmp/dstat_all.csv
</source>
<source>
@type exec
tag raw_df
command df -TP | sed 1d | sed 's/%//' | sed 's/\s\+/\t/g'
run_interval 10s
format tsv
keys device,type,size,used,available,capacity,mounted_on
</source>
<filter raw_df>
@type record_transformer
enable_ruby true
<record>
hostname ${hostname}
</record>
</filter>
<match raw_dstat raw_df>
@type kafka_buffered
brokers kafkabroker001:9092,kafkabroker002:9092
default_topic metrics-topic
flush_interval 60
buffer_type file
buffer_path /tmp/td-agent.*.buffer
output_data_type json
output_include_tag true
output_include_time true
</match>
\end{lstlisting}
Brokerがkafkabroker001:9092,kafkabroker002:9092であり、Topic名が\verb|metrics-topic|という名前であるという仮定している。dfに関してはデバイスごとのキャパシティが取得できるのだが、Fluentdのtag名が\verb|df.<デバイス名>|になってしまい、かつ取得できるjsonにデバイス名が含まれていないため、\verb|reord_reformer|で書き換えている。
\subsection{Fluentdを使ったメトリクス監視をする時、Kafka Consumerとして動かすFluentdの設定}
FluentdをKafka Consuemerとしても使用する。ConsumerとしてKafkaからデータを取得してInfluxDBにダブルポストを行う。こっちにインストールが必要なFluentd Pluginはこちらである。
\begin{itemize}
\item fluent-plugin-record-reformer
\item fluent-plugin-influxdb
\end{itemize}
Kafka Consumerサーバ上で動くFluentdのconfは以下のような設定にする。
\begin{lstlisting}
<source>
@type kafka_group
brokers kafkabroker001:9092,kafkabroker002:9092
consumer_group consumer_group_001
topics metrics-topic
format json
</source>
<match metrics-topic>
@type record_reformer
tag ${record['tag']}
enable_ruby true
auto_typecast true
</match>
<match raw_dstat>
@type copy
<store>
@type record_reformer
tag cpu
enable_ruby true
auto_typecast true
renew_record true
<record>
host ${record['hostname']}
usr ${record['dstat']['total_cpu_usage']['usr'].to_f}
sys ${record['dstat']['total_cpu_usage']['sys'].to_f}
idl ${record['dstat']['total_cpu_usage']['idl'].to_f}
wai ${record['dstat']['total_cpu_usage']['wai'].to_f}
hiq ${record['dstat']['total_cpu_usage']['hiq'].to_f}
siq ${record['dstat']['total_cpu_usage']['siq'].to_f}
time ${record['time']}
</record>
</store>
<store>
@type record_reformer
tag mem
enable_ruby true
auto_typecast true
renew_record true
<record>
host ${record['hostname']}
used ${record['dstat']['memory_usage']['used'].to_f}
buff ${record['dstat']['memory_usage']['buff'].to_f}
cach ${record['dstat']['memory_usage']['cach'].to_f}
free ${record['dstat']['memory_usage']['free'].to_f}
time ${record['time']}
</record>
</store>
<store>
@type record_reformer
tag load
enable_ruby true
auto_typecast true
renew_record true
<record>
host ${record['hostname']}
1m ${record['dstat']['load_avg']['1m'].to_f}
5m ${record['dstat']['load_avg']['5m'].to_f}
15m ${record['dstat']['load_avg']['15m'].to_f}
time ${record['time']}
</record>
</store>
<store>
@type record_reformer
tag disk
enable_ruby true
auto_typecast true
renew_record true
<record>
host ${record['hostname']}
read ${record['dstat']['dsk/total']['read'].to_f}
writ ${record['dstat']['dsk/total']['writ'].to_f}
time ${record['time']}
</record>
</store>
<store>
@type record_reformer
tag diskio
enable_ruby true
auto_typecast true
renew_record true
<record>
host ${record['hostname']}
read ${record['dstat']['io/total']['read'].to_f}
writ ${record['dstat']['io/total']['writ'].to_f}
time ${record['time']}
</record>
</store>
<store>
@type record_reformer
tag net
enable_ruby true
auto_typecast true
renew_record true
<record>
host ${record['hostname']}
recv ${record['dstat']['net/total']['recv'].to_f}
send ${record['dstat']['net/total']['send'].to_f}
time ${record['time']}
</record>
</store>
</match>
<match raw_df>
@type record_reformer
tag df
enable_ruby true
auto_typecast true
renew_record true
<record>
host ${record['hostname']}
size ${record['size'].to_f}
used ${record['used'].to_f}
available ${record['available'].to_f}
capacity ${record['capacity'].to_f}
device ${record['device']}
type ${record['type']}
mounted_on ${record['mounted_on']}
time ${record['time']}
</record>
</match>
<filter df cpu mem load disk diskio net>
@type record_transformer
renew_time_key time
</filter>
<filter df cpu mem load disk diskio net>
@type record_transformer
remove_keys time
</filter>
<match df cpu mem load disk diskio net>
@type copy
<store>
@type influxdb
host influxdb001
port 8086
dbname metrics_db
user kafka
password kafka
use_ssl false
verify_ssl false
tag_keys ["host", "device"]
time_precision s
flush_interval 10s
</store>
<store>
@type influxdb
host influxdb002
port 8086
dbname metrics_db
user kafka
password kafka
use_ssl false
verify_ssl false
tag_keys ["host", "device", "type", "mounted_on",]
time_precision s
flush_interval 10s
</store>
</match>
\end{lstlisting}
InfluxDBのホスト名をinfluxdb001とinfluxdb002とし、DB名を両方とも\verb|metrics_db|と仮定している。また、InfluxDBのTagには、Strings型で取得しているhostとdeviceを設定している。
\section{InfluxDBについて}
\subsection{クラスタリング機能が無くなったOSS版InfluxDBで簡単な冗長性を持たせる}
OSS版InfluxDBについては元々クラスタリング機能が存在したが、v0.12以降はOSS版InfluxDBでのクラスタリングは廃止され、以後Influx Enterprise及び、Influx Cloudを通じての提供する方針になっている。
日本語の詳細記事はこちらを是非参照してみてほしい。
\begin{itemize}
\item \url{https://yakst.com/en/posts/3870}
\end{itemize}
今回は、OSS版InfluxDBを使う前提なので、カジュアルにDBへのダブルポストを行い、かつ耐障害性もそこそこある構成にするためにKafkaを使っている。Kafkaを使うことで、メトリクスデータは、InfluxDBに投入される前の段階において、Retention期間中Kafkaクラスター内で冗長化された状態で保持される。また、Kafka Consumerとして使うFluentdもConsumer Groupを用いて複数台にスケールしている。そうすることで、監視対象のクライアントサイドからInfluxDBまでのどこで障害が起こっても、どこかの地点では確実にデータが保持され、SPOFになることはないはずである。
\subsection{InfluxDBを構築する}
2016年も終わりそろそろ2017年なるという時代なので、マニュアルで構築しようとかいう人はいないと信じてる。InfluxDBには、かなり出来の良いChefのレシピが転がっているので、それを使うのが妥当である。
\begin{itemize}
\item \url{https://github.com/bdangit/chef-influxdb}
\end{itemize}
最近これを使ってv1.1.0までアップデートしたが、特に問題ない。最近のバージョンでも安定してデプロイが可能である。ちなみに、僕も少しだけこのレポジトリにコントリビュートしたというのは、ここだけの話である。
構築できたら、その後、最低限のDBの作成まではやっておく必要がある。Measurementsはデータ投入時に勝手に作成されるため何もしなくて問題ない。
\begin{lstlisting}
# Inlfuxdbのコンソールに入る
$ influx
# adminユーザを作成する
> CREATE USER admin WITH PASSWORD 'xxxxxxxx' WITH ALL PRIVILEGES
# adminユーザでログインし直すことが可能
$ influx -username admin -password xxxxxxxx
# metrics_dbというDBを作成(同時にRetention期間14日のポリシーshortを作成)
> CREATE DATABASE metrics_db WITH DURATION 14d REPLIDATION 1 SHARD DURATION 1d NAME short
# 必要ならnon-adminユーザも作っておく(以下は1例)
> CREATE USER kafkaconsumer WITH PASSWORD 'kafkaconsumer'
> CREATE USER grafana WITH PASSWORD 'grafana'
> CREATE USER openstackadmin WITH PASSWORD 'openstackadmin'
> GRANT ALL ON metrics_db TO kafkaconsumer
> GRANT READ ON metrics_db TO grafana
> GRANT ALL ON metrics_db TO openstackadmin
\end{lstlisting}
InfluxDBに関しては、これくらいである。特に運用も難しくなく、よしなに動いてくれる。負荷も低く、ディスク容量も食わない、唯一無二のTime-series DBである。
\section{OpenStack運用のためにチェックしたほうが良いイベントログについて}
イベントログについて言及すると、当たり前だがチェックしたほうがいいログはたくさんある。とりあえず、出力されてるログは全部集めよう。それは何かというと、/var/log/配下のすべてのコンポーネントに紐づくログのことである。
前述のとおり、イベントログはSplunkで分析するのがベターである。どういう検索分を使ってSplunk上でアラートやレポートやダッシュボードを作成すればいいかというところは各人によって異なってしまうので、ここで具体的な例を出すことはできない。作成したほうがいいアラートの一例をここに記す。
\begin{itemize}
\item DHCPエラーチェック
\item 全コンポーネントのログレベル\verb|ERROR|以上のログチェック
\item APIのヘルスチェック
\item 物理ディスクの障害検知
\item 各プロジェクトごとのCinderボリューム使用率閾値チェック
\item 各プロジェクトごとのNovaのvCPU使用率閾値チェック
\item Glanceのスナップショット失敗ログチェック
\item \verb|nf_conntrack_count|の値チェック
\end{itemize}
ダッシュボードの一例はこういったものがある。
\begin{itemize}
\item 各ユーザごとの操作ログのモニタリング
\item VM使用率のモニタリング
\item CinderボリュームのQuota値モニタリング
\item NovaのvCPU, MemoryなどのQuota値モニタリング
\end{itemize}
あくまで、一例である。ぶっちゃけると、こういったアラートやダッシュボードは、自分がOpenStackの運用を開始した時点では1つもなかった。ありとあらゆるトラブルに遭遇する中でちょっとづつ増えていったものである。
\section{KVMからVMのメトリクスを取得する方法}
nova-computeが稼働するKVMホストのサーバから、その上で動くインスタンスのメトリクスを取得する方法について記す。他の人がどうやってるのか、そもそもそんなことやっていないのか分からない。本サークスでは、各メトリクスの項目の情報をjsonで出力するPythonスクリプトをcronで実行してファイルにログを出力して、それをFluentdで読み込んで転送する、という流れで行っている。
\subsection{Pythonスクリプトの実装について}
メトリクス取得用のPythonスクリプトのリポジトリはこちらである。ちなみに、これを利用するには、Python 2.7とlibvirt Python bindingsが必要であるので、事前にインストールされたし。
\begin{description}
\item[GitHub:] \url{https://github.com/Project-VI/openstack-vm-stats-collector}
\end{description}
ディレクトリ構造をツリーにするとこうなる。\verb|osvmstat|というディレクトリ配下に各メトリクスごとのスクリプトを配置しており、かつ、その下の\verb|common|配下に共通で使用する関数と例外処理を配置している。
\begin{lstlisting}
openstack-vm-stats-collector/
|
+-LICENSE
|
+-osvmstat/
| |
| +-block_info.py
| |
| +-block_stats.py
| |
| +-common/
| | |
| | +-__init__.py
| | |
| | +-exceptions.py
| | |
| | +-utils.py
| |
| +-interface_stats.py
| |
| +-memory_stats.py
| |
| +-vcpus.py
|
+-README.md
\end{lstlisting}
\verb|exceptions.py|は、特に珍しいことはしていないので解説しない。\verb|utils.py|から見ていこう。このライブラリでは、\verb|domain_xml|と\verb|nova_metadata|という2つの関数を実装している。加えて、\verb|xml.etree.ElementTree|を\verb|import|している。
\verb|domain_xml|は、\verb|xml.etree.ElementTree|を使いインスタンスIDの要素に紐づく情報を取得している。\verb|xml.etree.ElementTree|自体は、任意のxml要素を渡すことでその要素の値を抽出することが出来るライブラリである。
\begin{lstlisting}
def domain_xml(domain):
return xml.etree.ElementTree.fromstring(domain.XMLDesc())
\end{lstlisting}
\verb|nova_metadata|は、まず、\verb|xml.etree.ElementTree|を使い、メタデータを取得する。そのメタデータの中から、VMの名前と紐づくProjectのUUIDと名前を抽出して、辞書の形式で値を\verb|return|している。
\begin{lstlisting}
def nova_metadata(domain):
metadata = xml.etree.ElementTree.fromstring(
domain.metadata(libvirt.VIR_DOMAIN_METADATA_ELEMENT,
"http://openstack.org/xmlns/libvirt/nova/1.0"))
return {
"name": metadata.find("name").text,
"project": {
"uuid": metadata.find("owner/project").get("uuid"),
"name": metadata.find("owner/project").text
}}
\end{lstlisting}
\verb|utils.py|で行っている処理はこれだけである。次に各メトリクス項目を取得するためのスクリプトを見ていきたいと思う。が、どれもやってることは同じようなロジックなので、例として、一番簡単な\verb|memory_stats.py|を見ていこう。見ていこうと言っても、main関数しかないのですぐ終わる。
\begin{lstlisting}
def main():
conn = libvirt.openReadOnly()
if conn is None:
raise exceptions.HypervisorConnectionFailError()
for id in conn.listDomainsID():
dom = conn.lookupByID(id)
print(json.dumps({
"nova": utils.nova_metadata(dom),
"uuid": dom.UUIDString(),
"name": dom.name(),
"id": dom.ID(),
"memory_stats": dom.memoryStats(),
}))
\end{lstlisting}
流れとしてはこの通りとなる。
\begin{enumerate}
\item \verb|libvirt.openReadOnly()|を使い、ReadOnlyでlibvirtdに接続する
\item \verb|listDomainsID()|を使って、アクティブなドメインのリスト一覧を取得する
\item 取得したIDでループを回して、次に\verb|lookupByID()|で各ドメインの接続情報を取得する
\item \verb|memoryStats()|というメモリー関連の情報を含めて\verb|json.dumps|を使い辞書形式で出力する
\end{enumerate}
\subsection{VMメトリクス監視のためにKVMホストで稼働するデータ転送用のFluentdについて}
メモリ監視の項目を例に、データ転送用のFluentdの設定すると、以下の通りにとなる。基本的に前章で解説したスクリプトを各KVMホストでcronで稼働させておき、そのログを適当なディレクトリに吐き出すようにする。そして、Fluentd側ではtailプラグインを使って、ログをtailしてKafkaに転送する、という流れである。
\begin{lstlisting}
<source>
type tail
format json
time_key timestamp
time_format %Y-%m-%dT%H:%M:%S
path /var/log/virtstat/memory_stats.log
pos_file /var/log/td-agent/virtstat/virtstat.pos
tag virtstat.memory_stats
</source>
\end{lstlisting}
\subsection{VMメトリクス監視のためにKafka Consumerで稼働するConsume用のFluentdについて}
こちらは、Kafkaにまで転送されてきたデータを取得して、整形した後にInfluxDBに送るという部分になる。Kafkaプラグインで送られてきた場合、TagはKafkaのTopic名になってしまう。しかし、データ内にtagというKeyが入っており、KVMホスト内で設定したTagがValueに記載されているので、それに差し替える。その後、以下の例(これもメモリ監視)の通り、\verb|record_reformer|プラグインを使い、各項目をネスト無しのjson形式に整形してInfluxDBに送る。
\begin{lstlisting}
<match raw_memory_stats>
@type record_reformer
tag memory_stats
enable_ruby true
auto_typecast true
renew_record true
<record>
host ${record['host']}
id ${record['id']}
name ${record['name']}
nova_name ${record['nova_name']}
nova_project_name ${record['nova_project_name']}
nova_project_uuid ${record['nova_project_uuid']}
region ${record['region']}
uuid ${record['uuid']}
actual ${record['actual'].to_f}
available ${record['available'].to_f}
major_fault ${record['major_fault'].to_f}
minor_fault ${record['minor_fault'].to_f}
rss ${record['rss'].to_f}
swap_in ${record['swap_in'].to_f}
swap_out ${record['swap_out'].to_f}
unused ${record['unused'].to_f}
time ${record['time']}
</record>
</match>
\end{lstlisting}
\section{ログについて考えてみた}
ここまで、思いつくままにログについていくつか記載してみた。本誌では、全体像、Fluentdの設定、そしてVM監視用Pythonの解説を行ったことで、Kafkaについてはほとんど詳細を書けなかったが、実はいちばん重要なコンポーネントである。監視対象のスケールアウトも、InfluxDBへのダブルポストも、SPOFをなくすという点も、Kafkaがなければ実現しない。今回は紙面を割けなかったが、Kafkaについては、また別の機会に記すことになるかもしれない。以上である。
\vspace*{\stretch{1}}
\begin{flushright}
\includegraphics[width=0.35\textwidth]{img/kotatsu.png}
\end{flushright}