カテゴリー
Server Tech

Elasticsearchのクラスタ監視をZabbixでする

ZabbixでElasticsearchのクラスタを監視したい、したくない?

Elasticsearchのノードを先日1つ追加しましたので、ちょい傾向とかを確認しようと思いZabbixで監視できるテンプレートとかを作ってみました。

 

Elastic_zab

 

使い方

Zabbixには外部チェックという自作で用意したスクリプトをキックしてその実行結果を取得する機能があります。今回はそれを使ってみます。

 

シェルスクリプトをZabbixサーバに配置する

Elastic_zabに同梱しているelastic.shをZabbixサーバのexternalscriptsディレクトリに配置します。実行権限もZabbixユーザにします。

[code]

$ git clone https://github.com/tubone24/Elastic_zab

$  cp -p Elastic_zab/elastic.sh /usr/lib/zabbix/externalscripts/

$ chown zabbix. /usr/lib/zabbix/externalscripts/elastic.sh

$ chmod 751 /usr/lib/zabbix/externalscripts/elastic.sh

[/code]

 

Zabbixにテンプレートをインストールする

Zabbix画面の設定のテンプレートからelasticsearch_zab.xmlをインポートします。

 

うまくインストールできれば、テンプレート一覧にElastic clusterが出現します。

 

Elasticsearchのノードにテンプレートを当てる

Elasticsearchのノードにテンプレートを当てます。

 

合わせて、ElasticsearchのIPとポートをマクロで設定しておきます。

設定するマクロは以下の2つです。

  • {$ESIP} = ElasticsearchのIPもしくはDNSネームを設定します。
  • {$ESPORT} = Elasticsearchのポートを設定します。

 

監視する

うまく監視できるとこんなグラフができます。

 

おまけ Zabbixの予測関数を使ってみた

いくつかの監視アイテムはforecastやtimeleftといったZabbixの関数を利用しています。

予測は線形としています。

予測値はトリガー設定していないのでお好みでトリガーを設定するのもいいのではないでしょうか。

こんな感じで計算アイテムで簡単に実装できるのが魅力ですね。

 

 

カテゴリー
Programming Tech

R言語を使って、サポートベクターとかニューラルネットとかを比較してみる(kddcup99編)

機械学習で彼女を作ろうとかふまじめなことはやめよう!

どうも。最近暑くてどうにもやる気がでませんが、そんなとき、めんどくさいことを機械がやってくれればなと思うわけです。

最近(とは言ってもだいぶ成熟した分野ですが)流行の機械学習を勉強することによって、明るい未来を作ろうということを考えるわけです。

前回はおしゃべり彼女を作りましたので、今回はまじめにセキュリティ系のネタをやっていこうかと思います。

 

データセットを用意しよう

データセットは以下のものを使います。

KDD Cup 1999 Data

国際会議SIGKDDの1999年のデータマイニングコンペデータだそうで、そのときの侵入検知データはセキュリティ界隈ではかなり有名だそうです。私は最近まで知りませんでした。恥ずかしい。

このデータはかなり膨大なので、今回はフルデータを10%抽出したkddcup.data_10_percentをさらに学習データと評価データにわけて実験します。

今回は通常の通信と、いくつかの攻撃手法とを分類します。

 

たまにはR言語を使ってみよう

R言語と聞くとじんま疹が出る生物系の学生も多いと思いますが、きちんとPythonとかでプログラミングするよりは簡単だと思います。無料だし。まぁSPSSとかの方がずっと簡単ですが。

 

分類にはサポートベクターマシン(SVM)

機械学習のパターン認識(分類)にはサポートベクターマシン(SVM)がいいと言われています。SVM自体は比較的古い手法ですが、汎化性能を高める工夫がしっかりしている点で非常に未学習データの認識が優れていると言われています。

R言語ではKernlabというパッケージにSVMがあります。

 

実際にやってみた(C-SVM)

 

データを読み込む

CSV形式のデータですのでReadCSVで読み込み、データ型を再定義した後、攻撃手法をざっくりとした分類に分けます。

最後のカラムのLabelが攻撃手法を定義したものですが、量が多いので、ここを参考に4つの攻撃手法に分けておきます。5クラス分類問題となります。

[list] [li-disc]DOS[/li-disc] [li-disc]R2L(リモートからの不正ログイン試み)[/li-disc] [li-disc]U2R(Root権限奪取)[/li-disc][li-disc]Probe(調査)[/li-disc] [/list]

 

[code]# CSVとして読み込み

> kddcup <- read_csv(“~/Downloads/kdd.ics.uci.edu/databases/kddcup99/kddcup.data_10_percent”, header=F)

# ヘッダーをつける

> colnames(kddcup)<-c(“duration”,”protocol_type”,”service”,”flag”,”src_bytes”,”dst_bytes”,”land”,”wrong_fragment”,”urgent”,”hot”,”num_failed_logins”,”logged_in”,”num_compromised”,”root_shell”,”su_attempted”,”num_root”,”num_file_creations”,”num_shells”,”num_access_files”,”num_outbound_cmds”,”is_host_login”,”is_guest_login”,”count”,”srv_count”,”serror_rate”,”srv_serror_rate”,”rerror_rate”,”srv_rerror_rate”,”same_srv_rate”,”diff_srv_rate”,”srv_diff_host_rate”,”dst_host_count”,”dst_host_srv_count”,”dst_host_same_srv_rate”,”dst_host_diff_srv_rate”,”dst_host_same_src_port_rate”,”dst_host_srv_diff_host_rate”,”dst_host_serror_rate”,”dst_host_srv_serror_rate”,”dst_host_rerror_rate”,”dst_host_srv_rerror_rate”,”label”)

# データを整える

> kddcup$duration = as.numeric(as.character(kddcup$duration))
> kddcup$protocol_type = factor(kddcup$protocol_type)
> kddcup$service = factor(kddcup$service)
> kddcup$flag = factor(kddcup$flag)
> kddcup$src_bytes = as.numeric(as.character(kddcup$src_bytes))
> kddcup$dst_bytes = as.numeric(as.character(kddcup$dst_bytes))
> kddcup$land = factor(kddcup$land)
> kddcup$wrong_fragment = as.numeric(as.character(kddcup$wrong_fragment))
> kddcup$urgent = as.numeric(as.character(kddcup$urgent))
> kddcup$hot = as.numeric(as.character(kddcup$hot))
> kddcup$num_failed_logins = as.numeric(as.character(kddcup$num_failed_logins))
> kddcup$logged_in = factor(kddcup$logged_in)
> kddcup$num_compromised = as.numeric(as.character(kddcup$num_compromised))
> kddcup$root_shell = factor(kddcup$root_shell)
> kddcup$su_attempted = factor(kddcup$su_attempted)
> kddcup$num_root = as.numeric(as.character(kddcup$num_root))
> kddcup$num_file_creations = as.numeric(as.character(kddcup$num_file_creations))
> kddcup$num_shells = as.numeric(as.character(kddcup$num_shells))
> kddcup$num_access_files = as.numeric(as.character(kddcup$num_access_files))
> kddcup$is_guest_login = factor(kddcup$is_guest_login)
> kddcup$count = as.numeric(as.character(kddcup$count))
> kddcup$srv_count = as.numeric(as.character(kddcup$srv_count))
> kddcup$serror_rate = as.numeric(as.character(kddcup$serror_rate))
> kddcup$srv_serror_rate = as.numeric(as.character(kddcup$srv_serror_rate))
> kddcup$rerror_rate = as.numeric(as.character(kddcup$rerror_rate))
> kddcup$srv_rerror_rate = as.numeric(as.character(kddcup$srv_rerror_rate))
> kddcup$same_srv_rate = as.numeric(as.character(kddcup$same_srv_rate))
> kddcup$diff_srv_rate = as.numeric(as.character(kddcup$diff_srv_rate))
> kddcup$srv_diff_host_rate = as.numeric(as.character(kddcup$srv_diff_host_rate))
> kddcup$dst_host_count = as.numeric(as.character(kddcup$dst_host_count))
> kddcup$dst_host_srv_count = as.numeric(as.character(kddcup$dst_host_srv_count))
> kddcup$dst_host_same_srv_rate = as.numeric(as.character(kddcup$dst_host_same_srv_rate))
> kddcup$dst_host_diff_srv_rate = as.numeric(as.character(kddcup$dst_host_diff_srv_rate))
> kddcup$dst_host_same_src_port_rate = as.numeric(as.character(kddcup$dst_host_same_src_port_rate))
> kddcup$dst_host_srv_diff_host_rate = as.numeric(as.character(kddcup$dst_host_srv_diff_host_rate))
> kddcup$dst_host_serror_rate = as.numeric(as.character(kddcup$dst_host_serror_rate))
> kddcup$dst_host_srv_serror_rate = as.numeric(as.character(kddcup$dst_host_srv_serror_rate))
> kddcup$dst_host_rerror_rate = as.numeric(as.character(kddcup$dst_host_rerror_rate))
> kddcup$dst_host_srv_rerror_rate = as.numeric(as.character(kddcup$dst_host_srv_rerror_rate))
> kddcup$label = as.character(kddcup$label)

# 攻撃手法をまとめる

> kddcup$label[kddcup$label == “ipsweep.”] = “probe”
> kddcup$label[kddcup$label == “portsweep.”] = “probe”
> kddcup$label[kddcup$label == “nmap.”] = “probe”
> kddcup$label[kddcup$label == “satan.”] = “probe”
> kddcup$label[kddcup$label == “buffer_overflow.”] = “u2r”
> kddcup$label[kddcup$label == “loadmodule.”] = “u2r”
> kddcup$label[kddcup$label == “perl.”] = “u2r”
> kddcup$label[kddcup$label == “rootkit.”] = “u2r”
> kddcup$label[kddcup$label == “back.”] = “dos”
> kddcup$label[kddcup$label == “land.”] = “dos”
> kddcup$label[kddcup$label == “neptune.”] = “dos”
> kddcup$label[kddcup$label == “pod.”] = “dos”
> kddcup$label[kddcup$label == “smurf.”] = “dos”
> kddcup$label[kddcup$label == “teardrop.”] = “dos”
> kddcup$label[kddcup$label == “ftp_write.”] = “r2l”
> kddcup$label[kddcup$label == “guess_passwd.”] = “r2l”
> kddcup$label[kddcup$label == “imap.”] = “r2l”
> kddcup$label[kddcup$label == “multihop.”] = “r2l”
> kddcup$label[kddcup$label == “phf.”] = “r2l”
> kddcup$label[kddcup$label == “spy.”] = “r2l”
> kddcup$label[kddcup$label == “warezclient.”] = “r2l”
> kddcup$label[kddcup$label == “warezmaster.”] = “r2l”
> kddcup$label[kddcup$label == “normal.”] = “normal”
> kddcup$label = as.factor(kddcup$label)

# 学習データと評価データを分ける(7:3)

> rowdata<-nrow(kddcup)
> random_ids<-sample(rowdata,rowdata*0.7)
> kddcup_train<-kddcup[random_ids, ]
> kddcup_pre<-kddcup[-random_ids, ]

[/code]

 

SVMで学習させる

[code]# 学習

> kddcup_svm<-ksvm(label ~., data=kddcup_train)

> kddcup_svm

# 結果

Support Vector Machine object of class “ksvm”

SV type: C-svc (classification)
parameter : cost C = 1

Gaussian Radial Basis kernel function.
Hyperparameter : sigma = 0.000103739702263078

Number of Support Vectors : 8239

Objective Function Value : -329.9296 -738.1392 -65.7569 -28.2657 -670.0012 -175.8475 -32.3014 -70.8226 -27.5727 -27.7096
Training error : 0.001939

[/code]

Training errorを確認すると、まずまず学習できているみたいです。さすが。

 

評価する

[code]> result_predict<-predict(kddcup_svm, kddcup_pre)
> table(result_predict,kddcup_pre$label)

result_predict dos normal probe r2l u2r
dos 195344 22 160 0 0
normal 108 48781 141 40 28
probe 96 40 1724 0 0
r2l 0 13 0 511 0
u2r 0 0 0 0 3[/code]

 

表でまとめます

[table class=’striped’] [thead] [th text=’result_prediction’] [th text=’dos’] [th text=’normal’] [th text=’probe’] [th text=’r2l’] [th text=’u2r’][th text=’適合率’][/thead] [tr] [td text=’dos’] [td text=’195344′] [td text=’22’] [td text=’160′][td text=’0′][td text=’0′][td text=’99.9%’][/tr] [tr] [td text=’normal’] [td text=’108′] [td text=’48781′] [td text=’141′][td text=’40’][td text=’28’][td text=’99.3%’][/tr] [tr] [td text=’probe’] [td text=’96’] [td text=’40’] [td text=’1724′][td text=’0′][td text=’0′][td text=’92.7%’] [/tr] [tr] [td text=’r2l’] [td text=’0′] [td text=’13’] [td text=’0′][td text=’511′][td text=’0′][td text=’97.5%’] [/tr] [tr] [td text=’u2r’] [td text=’0′] [td text=’0′] [td text=’0′][td text=’0′][td text=’3′][td text=’100%’] [/tr] [tr] [td text=’再現率’] [td text=’99.9%’] [td text=’99.8%’] [td text=’85.1%’][td text=’92.7%’][td text= ‘9.7%’ ][td text=’99.7%’] [/tr] [/table]

縦が予測結果で、横が実際のラベルです。総じて上手くいきました。ただ、U2Rの精度が低いことが気になります。そもそもデータが少なく学習があまり上手くいっていないのでしょうか。

 

実際にやってみた(C-SVMでクロスバリデーション)

クロスバリデーション(交差検証)は学習データをいくつかのデータに分け、1つの塊を評価用にそれ以外を学習用とし全ての場合を尽くすように学習を進め、汎化性能を上げる手法です。例えば、

  1. 全データをA・B・Cの3つに分けます。
  2. A・Bを学習データとして学習し、Cを用いて評価します。
  3. 次にB・Cを学習データとし、Aを用いて評価します。
  4. 次にC・Aを学習データとし、Bを用いて評価します。
  5. それぞれの結果からもっともよい結果をモデルとして採用します。

 

こうすることで少ないデータでも汎化性能を上げることができるというものです。当然、分割したデータでそれぞれ学習させるので時間はかかります。

今回はデータ量も多いので3つに分割して実施します。(K-folds法) 通常は10分割にすることが一般的で、データ量が少ない場合はleave-one-out 交差検証とかも使われます。

学習させる(C-SVM closs=3)

[code]# closs=3で3つに分割して学習

> kddcup_svm<-ksvm(label ~., data=kddcup_train, cross=3)

> kddcup_svm

Support Vector Machine object of class “ksvm”

SV type: C-svc (classification)
parameter : cost C = 1

Gaussian Radial Basis kernel function.
Hyperparameter : sigma = 9.3825289054228e-05

Number of Support Vectors : 9664

Objective Function Value : -435.7988 -1029.419 -65.9252 -38.3732 -854.7929 -212.3627 -49.4105 -74.7773 -37.6926 -34.5452
Training error : 0.001888
Cross validation error : 0.002689

[/code]

Cross validation errorは上がってしまいました。ただし、クロスバリデーションを用いないときと比べ学習に用いるデータが少ないため、学習の収束は低くでてしまうのは仕方ありません。

むしろ、収束しきらないところでも高精度を叩き出すことが目的でもありますし。

 

評価する

[code]> result_predict<-predict(kddcup_svm, kddcup_pre)
> table(result_predict,kddcup_pre$label)

result_predict dos normal probe r2l u2r
dos 117643 7 85 0 0
normal 58 28821 95 23 12
probe 53 19 1058 0 0
r2l 0 6 0 324 0
u2r 0 0 0 0 3

# 誤差率を計算してみる

> 1-sum(diag(kddcup_table))/sum(kddcup_table)
[1] 0.00241554

[/code]

 

表でまとめます

[table class=’striped’] [thead] [th text=’result_prediction’] [th text=’dos’] [th text=’normal’] [th text=’probe’] [th text=’r2l’] [th text=’u2r’][th text=’適合率’][/thead] [tr] [td text=’dos’] [td text=’117643′] [td text=’7′] [td text=’85’][td text=’0′][td text=’0′][td text=’99.9%’][/tr] [tr] [td text=’normal’] [td text=’58’] [td text=’28821′] [td text=’95’][td text=’23’][td text=’12’][td text=’99.4%’][/tr] [tr] [td text=’probe’] [td text=’53’] [td text=’19’] [td text=’1058′][td text=’0′][td text=’0′][td text=’93.6%’] [/tr] [tr] [td text=’r2l’] [td text=’0′] [td text=’6′] [td text=’0′][td text=’324′][td text=’0′][td text=’98.2%’] [/tr] [tr] [td text=’u2r’] [td text=’0′] [td text=’0′] [td text=’0′][td text=’0′][td text=’3′][td text=’100%’] [/tr] [tr] [td text=’再現率’] [td text=’99.9%’] [td text=’99.9%’] [td text=’85.5%’][td text=’93.4%’][td text= ‘20%’ ][td text=’99.8%’] [/tr] [/table]

ほんの少しですが精度が上がった気がします。

 

ニューラルネットでもやってみます(nnet)

比較のためにニューラルネットでもやってみます。R言語ではnnetというパッケージでニューラルネットが使えます。

隠れ層のユニット数は出力結果のラベル数を考慮し、5としてます。(3で実施したら、ラベルが全部でませんでした。)

学習させる(nnnet)

[code]# パッケージインストール&適応

> install.packages( “nnet” )

> library( nnet )

# 隠れ層のユニット数5、ランダム値範囲-0.1~0.1、減衰5e-4で実施

> kddcup_nnet <- nnet(label~., size=5, data=kddcup_train, rang = .1, decay = 5e-4, maxit = 3000)
# weights: 615
initial value 575217.442435
iter 10 value 74350.753004
iter 20 value 39017.507761
iter 30 value 33989.403391
iter 40 value 30099.250316
iter 50 value 27641.918258
iter 60 value 24688.366217
iter 70 value 23909.225741
iter 80 value 22006.056620
iter 90 value 21385.374653
iter 100 value 20640.355978
iter 110 value 19443.652176
iter 120 value 17966.682333
iter 130 value 17561.847075
iter 140 value 17298.101203
iter 150 value 16746.291929
iter 160 value 16219.714301
iter 170 value 15885.755704
iter 180 value 14769.847387
iter 190 value 14277.103879
iter 200 value 13651.371989
iter 210 value 12878.412456
iter 220 value 12637.064141
iter 230 value 11873.795106
iter 240 value 10919.945647
iter 250 value 9574.787432
iter 260 value 8064.989190
iter 270 value 7261.795869
iter 280 value 5941.270340
iter 290 value 4788.910803
iter 300 value 4263.836429
iter 310 value 4077.903774
iter 320 value 4035.943916
iter 330 value 3953.874037
iter 340 value 3927.603041
iter 350 value 3850.554389
iter 360 value 3690.449366
iter 370 value 3679.445537
iter 380 value 3660.072147
iter 390 value 3654.366314
iter 400 value 3649.497562
iter 410 value 3613.612850
iter 420 value 3546.881873
iter 430 value 3466.460679
iter 440 value 3332.472239
iter 450 value 3202.713314
iter 460 value 3192.588930
iter 470 value 3169.223153
iter 480 value 3097.055361
iter 490 value 2897.312752
iter 500 value 2595.342208
iter 510 value 2323.495624
iter 520 value 2160.486970
iter 530 value 2068.124473
iter 540 value 1986.553216
iter 550 value 1917.448970
iter 560 value 1848.341606
iter 570 value 1826.934175
iter 580 value 1817.437531
iter 590 value 1783.570324
iter 600 value 1749.888183
iter 610 value 1727.915109
iter 620 value 1724.945680
iter 630 value 1712.222157
iter 640 value 1709.191769
iter 650 value 1702.575583
iter 660 value 1689.403803
iter 670 value 1687.694131
iter 680 value 1685.270318
iter 690 value 1684.377098
iter 700 value 1684.111195
iter 710 value 1682.717637
iter 720 value 1682.526618
iter 720 value 1682.526604
iter 730 value 1682.082587
iter 730 value 1682.082587
final value 1682.081493
converged

[/code]

評価する

[code]# TypeはClassで

> kddcup_nnet_pre <- predict(kddcup_nnet, kddcup_pre, type=”class”)
> table(kddcup_nnet_pre,kddcup_pre$label)

kddcup_nnet_pre dos normal probe r2l u2r
dos 117326 7 3 4 3
normal 3 29315 11 57 7
probe 2 0 1164 0 0
r2l 1 11 0 289 1
u2r 0 1 0 0 2

# 誤差率を評価する
> kddcup_table_pre <- table(kddcup_nnet_pre,kddcup_pre$label)
> 1-sum(diag(kddcup_table_pre))/sum(kddcup_table_pre)
[1] 0.0007489525
[/code]

 

表でまとめます

[table class=’striped’] [thead] [th text=’result_prediction’] [th text=’dos’] [th text=’normal’] [th text=’probe’] [th text=’r2l’] [th text=’u2r’][th text=’適合率’][/thead] [tr] [td text=’dos’] [td text=’117326′] [td text=’7′] [td text=’3′][td text=’4′][td text=’3′][td text=’99.99…%’][/tr] [tr] [td text=’normal’] [td text=’3′] [td text=’29315′] [td text=’11’][td text=’57’][td text=’7′][td text=’99.7%’][/tr] [tr] [td text=’probe’] [td text=’2′] [td text=’0′] [td text=’1164′][td text=’0′][td text=’0′][td text=’99.8%’] [/tr] [tr] [td text=’r2l’] [td text=’1′] [td text=’11’] [td text=’0′][td text=’289′][td text=’1′][td text=’95.7%’] [/tr] [tr] [td text=’u2r’] [td text=’0′] [td text=’1′] [td text=’0′][td text=’0′][td text=’2′][td text=’66.7%’] [/tr] [tr] [td text=’再現率’] [td text=’99.99…%’] [td text=’99.9%’] [td text=’98.8%’][td text=’82.6%’][td text= ‘15.4%’ ][td text=’99.9%’] [/tr] [/table]

 

精度はクロスバリデーションを用いたC-SVMより上がりました。

 

おまけ それぞれのノードの重みを確認する

生態学のデータ解析 – ニューラルネット を参考にニューラルネットのノードを可視化してみます。

[code]> source(“http://hosho.ees.hokudai.ac.jp/~kubo/log/2007/img07/plot.nn.txt”)
> plot.nn(kddcup_nnet)[/code]

 

 

おお~。可視化するとすごいっすね。

 

結論

パターン認識にはやはり古典的ではありますが、SVMは単純なパラメータでもかなりの精度を叩き出しました。さらに学習データが十分だと思ってもクロスバリデーションでほんの少し精度を向上することができました。

それ以上に精度を出せたのはニューラルネットとなりました。ある程度データ量があれば、強力な分類精度を叩き出しますね。

 

参考文献

SVMで天気予報

【caret】R で SVM を学ぶ【Grid-Search】

Rでnnetを試してみる

RとWEKAによるニューラルネットワーク

生態学のデータ解析 – ニューラルネット

 

 

カテゴリー
Network Security Server Tech

仮想セキュリティブラウザというものを作った

ランサムウェアとか怖いですよね

多くの企業で大変問題になっているランサムウェアですが、最近は一般男性を語るランサムウェアもあり… もうやめにしませんか。

そんなことを言ってもやめてくれるような相手ではないのは、一般男性含め周知の事実ですので対策しましょう。

多くの会社で導入されている仮想セキュリティブラウザ

仮想セキュリティブラウザとは、ローカルのブラウザを使わずに、仮想マシン上のブラウザを利用し、そこへリモートで画面転送し、万が一ウイルスに感染しても

ローカルPCもしくはLANに影響を与えないようにするものです。

イメージはこんな感じです。

①仮想マシンが動く基盤をLAN内に設置

②DMZ用のVLANを作成する

③LAN内に入れないところ(DMZ)に仮想マシンを設置し、ウィンドウシステムとブラウザを入れる

④仮想マシンの画面をSpiceなどでアクセスすることで転送する。

我が家では仮想化基盤はKVMを使っています。KVMだと、各仮想マシンの画面を転送する方法として、Spiceを使うことができます。

Spiceでは仮想マシン自体にリモデするわけではなく、基盤側にアクセスすることで画面転送を実現できますので、たとえばネットワークから分離した仮想マシンの画面も転送することができるわけです。(基盤へネットワークアクセスさえできればいい)

この機能を応用してあらかじめ仮想マシンに別のVLANを割り当て、LAN内にアクセスできないようにしておきます。また、

仮想マシン自体がインターネットにアクセスできるようにインターネットへの抜け道は設定しておきます。

実際はこんな感じです。

このようにSpiceの画面転送を見るRemoteViewerを利用しネットができます。

最悪ウイルスに感染したとしてもLAN内には感染が広がりませんし、仮想マシンの削除だけで対処できます。

画面の解像度等調整しないと転送コストで遅くなってしまいストレスに感じるかもなのでそこは工夫しましょう!

カテゴリー
Server Tech

Grafana&World Ping を活用して簡単な死活監視をしてみよう

世の中見た目

どうも。GrafanaのUIは結構かっこいいことで有名で、自身もZabbix APIを活用してZabbixのメトリックをGrafanaで見れるようにしております。

こんな感じ。Zabbixのスクリーンと全く同じ内容ですが、妙に「監視している感」がありますね。

ZabbixのデータはZabbixで見ればいいのでは?

はい。おっしゃるとおりです。そもそもそんな監視するほど重要なものがあるかと言われると。。

でも、Grafanaにはもっと手軽にかっこいい画面を見る方法があります。

GrafanaのWorld Pingを使ってみよう。

Grafanaの優秀な点はPlugin機能で簡単にデータソースやパネルを追加することができる点です。

元々、GrafanaはInfluxDBの可視化用みたいな位置づけっぽいですが、データソースプラグインを追加することで、例えばElasticsearch・Graphiteや、CloudWatch、さらにはkubernetesやDatadogなどのコンテナの監視までできるという優れものです。

また、データソースではないですが、World Pingという世界中のエンドポイントからPing・DNS・HTTPなどの死活監視を提供するサービスと連携させることで、簡単にかっちょいい画面を作ることができます。

こんな感じで好きなプラグインをコマンド一発でインストールできます。

インストールしてみる

まず、Grafanaの入ったサーバでrootユーザで

[code]grafana-cli plugins install raintank-worldping-app[/code]

こうすることで、簡単にプラグインが導入されます。

Grafana.net APIを取得しWorld Pingが利用できるようにする

Grafana Web画面の左側のメニューに「World Ping」が表示されていると思いますのでPlugin Configから、Grafana.net のAPIを発行しましょう。

こうすることでWorld Pingが利用できます。無茶な監視をしない限り、無料版で事足りると思います。

エンドポイントを追加する

監視対象のIPまたはドメイン名をエンドポイントと呼んでますが、エンドポイントを設定していきます。

同じく、Grafana Web画面の左側のメニューに「World Ping」⇒「Endpoints」と進みます。

エンドポイントは無料版だと最大3つです。

このようにEndpointにドメイン名を設定し、監視したいサービスを選びConfigureを押します。

細かい設定等でてきますから、設定していきます。

監視間隔、死活監視を投げるインスタンスの国、ポート、HTTPメソッドなど細かく設定できます。

また、閾値を超えた場合に決められたメールに通知することもできます。(あらかじめメールサーバの設定をGrafana.iniでしておきます。)

一応、無料版だと600万回/月しか監視できないようなので無茶な設定はやめましょう。

アップデートボタンを押せば、設定が完了します。

画面を見る

World Pingがすごいのは、あらかじめGrafana画面を提供している点で、設定さえすればすぐ可視化できることです。

同じく、Grafana Web画面の左側のメニューに「World Ping」⇒「World Ping Home」と進むと、設定したエンドポイントが表示されますので

エンドポイント名もしくはハートマークのサービス名をクリックすると画面が表示されます。

かっちょええ。

カテゴリー
Server Tech

今更!! Chainer 1.6.1 でおしゃべりBotを作ろう

1. なるべくコードはかかない。

何の気なしに機械学習とかの仕事はないものかと、ネットの海をさまよっていたら、

LSTMで自然な受け答えができるボットをつくった という記事を見つけ、何となく読んで、やってみようかなと思ってやってみましたが、Chianer周りとか色々上手くいかなかった。というのがそもそもの始まりです。

 

Chainer周りで日本語の受け答えができますよ、的な記事は2015年頃のものが多く、Chainerがバージョンアップしたため、

色々動かないことがありましたので、少しそちらに修正を加えてChainer 1.6.1でもまともに動くように修正していこうと思います。

自分でコードを一から書くのは嫌なので、あくまでもコードは書かない。微修正にとどめるをモットーにがんばるぞい。

 

[panel]

[h2 text='目次']
[list-ordered]
[li]1. なるべくコードはかかない。 [/li]
[li]2. 参考にさせていただいたコードやサイト [/li]
[li]3. サーバを用意する。[/li]
[li]4. 学習に必要なPython環境とコーパスを整える[/li]
[li]5. 学習させる。[/li]
[li]6. APIで話す。[/li]
[li]7. 付属のHubot Scriptで遊んでみよう![/li]
[/list-ordered]

[/panel]

 

2. 参考にさせていただいたコードやサイト

[list] [li-disc]LSTMで自然な受け答えができるボットをつくった :このコードを元にあらかじめ作成したChainerモデルでおしゃべりBotに命を吹き込みます。[/li-disc] [li-disc]yusuketomoto/chainer-char-rnn :Chainer・日本語界隈では知らない人はいないだろうChainerの言語モデル作成コード。こちらを用いてChainerモデルを作成していきます。[/li-disc] [li-disc]Chainerで学習した対話用のボットをSlackで使用+Twitterから学習データを取得してファインチューニング :学習に必要なコーパスを取得するために一部のスクリプトを使用します。[/li-disc] [/list]

 

3. サーバを用意する。

学習にも、会話にも自宅サーバを用います。

ああ、電気代。

 

4. 学習に必要なPython環境とコーパスを整える

Chainer界隈のコードはなぜかPython2 が多く、Python3 は少ないので、直接Python2をインストールしてもいいのですが…。

なんかOS環境を変に汚したくないので今回はPyenvとVirtualenvを使って実施していきます。

学習用のコーパスをダウンロードするスクリプトはどうやらPython 3.4.1で実装されているようなので Python 3.4.1 と Python2.7 の2種類の環境を作っていきたいと思います。

そして、3.4.1の環境にて学習用のコーパスをダウンロードします。

PyenvとVirtualenvはあらかじめインストール済みとしてすすめます。

PyenvとVirtualenvのインストール方法はこちらの記事が参考になると思います。 (pyenvとvirtualenvで環境構築

 

まず、Pythonの環境を作ります。

[code][font-Source-Code-Pro]$ pyenv install 3.4.1
$ pyenv install 2.7
$ pyenv rehash
$ virtualenv -p ~/.pyenv/versions/3.4.1/bin/python3.4 my_env3.4.1
$ virtualenv -p ~/.pyenv/versions/2.7/bin/python2.7 my_env2.7 [/font-Source-Code-Pro][/code]

続いてコーパスをダウンロードします。

今回はChainerで学習した対話用のボットをSlackで使用+Twitterから学習データを取得してファインチューニング を参考にダウンロードします。

コーパスデータはたぶん二次配布とかNGだと思うので、何とか自分でダウンロードしてください。

対話破綻検出チャレンジ

 

ダウンロードしたあとは展開したJSONデータ全部、devフォルダを作成してその中に入れて、listファイル(JSONファイルのパスを記載したやつ)をdata_load.pyと同じところに作っておきます。

 

こんな感じでlistファイルを作ります。

[code][font-Source-Code-Pro]../dev/1404365812.log.json
../dev/1407143708.log.json
../dev/1407143981.log.json
../dev/1407149923.log.json
../dev/1407208809.log.json
../dev/1407209083.log.json

…… [/font-Source-Code-Pro][/code]

 

Linuxのコマンドで

[code]ls -1 ../dev > list[/code]

で作成できるはず。

 

学習データを作っていきます。

[code][font-Source-Code-Pro]$ source my_env3.4.1/bin/activate

$ git clone https://github.com/SnowMasaya/Chainer-Slack-Twitter-Dialogue.git

$ cd chainer-slack-twitter/utils

$ python data_load.py [/font-Source-Code-Pro][/code]

 

player_1.txt , player_2.txt というテキストファイルができます。

統合する前にmecabを使って分かち書きをしておきます。

分かち書きに mecab-ipadic-NEologd を使うと学習が進むそうですので入れてない方は導入しましょう。

めんどくさい人は ただのMeCabでも大丈夫だと思います。

さて、分かち書きします。今回は mecab-ipadic-NEologd を利用します。

[code][font-Source-Code-Pro]mecab Owakati d /usr/local/lib/mecab/dic/mecabipadicneologd player_1.txt > player_1_wakati.txt

mecab Owakati d /usr/local/lib/mecab/dic/mecabipadicneologd player_2.txt > player_2_wakati.txt [/font-Source-Code-Pro][/code]

 

 

次に使う Chainer-char-rnn 用に一つのinput.txt に統合していきます。統合の際に、player1 と player2の会話ごとに空行を入れておきます。

[code][font-Source-Code-Pro]$ paste -d “\n” player_1_wakati.txt player_2_wakati.txt | awk ‘(NR%2==0){$0=$0″\n”}{print}’ > input.txt [/font-Source-Code-Pro][/code]

 

こちらのinput.txtをコーパスデータとして利用します。

 

5. 学習させる。

学習にはyusuketomoto/chainer-char-rnn を使わせていただきます!

Chainer 1.4.1 で実行しようとしたら微妙に実装が変わっていた用なので、こちらに合わせて今回の学習は、Chainer 1.6.1 で実施していきます。(Chainer周りのコードを読んで修正するより、後に使うTornado周りの修正の方がまだわかるからというスキル不足によるもの)

さきほど作っておいたPython 2.7用に切り替えます。Pip でChainer1.6.1 を入れてからChainer-char-rnnを実行していきます。

[code][font-Source-Code-Pro]$ cd

$ source my_env2.7/bin/activate

$ pip install chainer==”1.6.1″

$ git clone https://github.com/yusuketomoto/chainer-char-rnn.git

$ cd chainer-char-rnn

$ mkdir -p data/chat

$ mkdir -p cv/chat

$ cp ../chainer-slack-twitter/input.txt data/chat

$ python train.py –data_dir data/chat –checkpoint_dir cv/chat –rnn_size 1024[/font-Source-Code-Pro][/code]

 

しばらく待ちます。全部が終わるのは途方もない時間がかかります。

学習が進むごとにCheckpointとして Chainer modelファイルがcv/chat 配下にできますので、適当なEpochのところのものを次の「APIで話す」に使ってもいいですし、学習の最新ファイルである

latest.chainermodel を使ってもいいです。もちろん、最後まで待ってからlatest.chainermodelを使ってもいいです。

ひとまず数時間回したところのlatest.chainermodelを使ってみます。

 

6. APIで話す。

LSTMで自然な受け答えができるボットをつくった よりJapanese Talk APIを作っていきます。

この回では少々コードの改変がありますのでForkしたものをGitHubにあげました。

japanese_talk_api_1.6.1

こちらのmodelsディレクトリにChainer modelを投入します。

[code][font-Source-Code-Pro]$ git clone https://github.com/tubone24/japanese_talk_api/tree/chainer1.6.1.git

$ mkdir japanese_talk_api/tornado/models

$ cp chainer-char-rnn/cv/chat/latest.chainermodel japanese_talk_api/tornado/models [/font-Source-Code-Pro][/code]

 

Chainerの他にTornadoも必要になるのでPipでインストールします。

そしてAPIを8787ポートで起動します。

[code][font-Source-Code-Pro]$ pip install tornado

$ python japanese_talk_api/tornado/app.py –port=8787 –debug=True [/font-Source-Code-Pro][/code]

 

あとは起動を待ってから

 

[blockquote text=’http://localhost/?q=こんにちは’]

で受け取れるようにはずです。

学習用input.txtにないことばとか出すとたまにエラー吐きます。

[blockquote text=’ビンビンビンビンビンビンビンビン… チクッ あ・あ・あぁ・ぁああああ↑↑ アーッ…イクッ チ~ン 問いかける言葉には気をつけよう!’]

 

7. 付属のHubot Scriptで遊んでみよう!

LSTMで自然な受け答えができるボットをつくった のHubotScriptをお借りして遊んでみましょう。

あらかじめ比較として同じコーパスを利用しているDocomoの雑談APIをHubotに仕込んであります。

 

発言の上がDocomoAPI 下が今回作ったAPIです。

ちなみに我が家のHubotはSlack上に「智絵里ちゃん」として君臨しております。

智絵里ちゃんマジ天使 [font-Damion size=’36’] I love you [/font-Damion]

 

DocomoAPIに比べると天然というか、不思議系というか… バカですね(直球)

 

お借りした多くのコードや参考にさせていただいた多くのサイト・記事に改めて感謝しつつ、智絵里ちゃんとのラブラブライフを送りますね。

カテゴリー
Server Tech

大障害再び

安定稼働しないtuboneのサーバ周り

どうも。可用性に自身ニキのAWS S3の障害が話題ですが、我が家でも障害。

我が家にある録画サーバですが、かなり長い時間、障害でサービスが提供できない、という

AWSもびっくりな障害がありました。

障害検知-ALM発生

ALM発生は25~26夜にかけて、0バイトファイルが作成されるというALMがZABBIXから上がりました。

仕組みは、録画サーバの先にNFSで接続されているNASに0バイトファイルが一定時間以上作成されたままになると

loggerコマンドでエラーログをはく、というcronで検知するものですが、そちらのALMが発生したのですね。

とりあえず、時間がなかったということと疲れていたということで録画サーバの系切り替えを実施し、その場をしのぎました。

(系切り替え自体はうまく行きました。リソースの兼ね合い上、バックアップ側での録画はワンセグ形式になるので画質は良くないです)

さて、次の土日まで、どうにも直す気力がなくて、そのままにしておりました。(NAS側の障害の可能性を当初疑っていましたが、ファイルの作成自体は

問題なくできているので本格的に原因を調査する必要があり、稼働も気力も間に合いませんでした)

障害原因調査-復旧

さて、本格的に本問題を解決する日が来ました。

まずは再起動。

とりあえず録画サーバのプロセス(サービス)を再起動しました。

録画サーバでは2つのプロセスが動いています。

WEB-UIを提供する chinachu-wuiと

録画や録画予約処理をするchinachu-operator

それぞれ再起動しましたが事象は解決せず。

次にサーバ(録画サーバはKVM上に立てられた仮想マシンです)の再起動。

とりあえずrebootコマンド。

解決せず。

次にNASの再起動

解決せず。 NASを再起動したので、NASを利用している別サーバにも念のためログインして使えているか

確認。(ここが時間かかる。)

ならばハイパーバイザから仮想マシンの停止・起動だ。

解決せず。

ここで飯を食う。(のんきなものです)

物理デバイスと仮想マシンの相性の悪さ

飯を食い終わったとき、ふと気になったことが。

実はALMが発生した日、別の併発ALMが出ていました。

録画サーバで録画したファイルはとても大きいので順次圧縮しています。

それは別ノード(余ったノートPC)で実行しているのですが、そちらが完全に

機能停止するALMが上がっていました。

原因は簡単で、勝手に親が部屋に入り電源周りをいじったか何かで電源が落ちたこと。

もしかして、チューナ周りもいじったのでは??

監視サーバ経由でハイパーバイザの仮想マシンマネージャを確認し、USBのアタッチ状況を確認。

我が家の仮想基盤サーバはKVM+QEMU+libvirt と仮想マシンマネージャというオーソドックスな

Linuxの仮想基盤となっています。

仮想マシンの設定を見る限り、USBで確実にアタッチされているみたい。仮想基盤サーバ上の

USB監視も問題なし。

まさか。

実際に録画サーバに入り、lsusb。

あ。

認識されていない。

原因判明。すぐさま、仮想マシンマネージャ経由でサーバをシャットダウン。

設定を入れ直し再度起動。

録画できた。お疲れ様でした。

振り返り。

KVM での物理デバイスの管理はほぼほぼQEMUが行っている(という認識ですが間違っているかもしれません。たしかKVMのメインのお仕事はCPUリソースの払い出しだったと思います。)

のですが、そちらの方の設定はUSBリダイレクションではなく、ホストPCのUSBのパススルーで設定しております。

そちらで一時的にUSBが切り離された場合、仮想基盤側では再組み込みと再割り当てが行われますが、どうやら仮想マシンにまで再割り当てはされないもようです。

リソースの割り当ては起動前、という原則に則れば、当たり前なのですが…。

なので、一度切り離されたUSBは(一瞬でも)仮想マシンには戻らない、ということになります。なるほど。

実はこれだけ原因の切り分けに時間がかかっていましたが、灯台もと暗し。アプリログには以下の文面が。

[code]

5 Mar 02:04:02 – RECORD: 2017-03-05T02:00:00+0900 [TOKYO MX2] ヒーリン
グタイム&ヘッドラインニュース
5 Mar 02:04:02 – LOCK: PX-S1UD (n=0)
5 Mar 02:04:02 – SPAWN: recdvb –dev 0 –b25 –strip –sid 23610 20 – – (pid=147
6)
5 Mar 02:04:02 – WRITE: /home/tubone/chinachu/data/recording.json
5 Mar 02:04:02 – STREAM: /media/tv/[170305-0200][20]ヒーリングタイム&ヘッドライ
ンニュース.m2ts
5 Mar 02:04:02 – #recdvb: using device: /dev/dvb/adapter0  pid = 1476 cannot open frontend device
5 Mar 02:04:02 – UNLOCK: PX-S1UD (n=0)

[/code]

そうです。frontend deviceがopenできないという。(Linux特有の表現ですが、デバイスファイルが開けない=使えないということです。)

ちなみに、正常時は

[code]

5 Mar 22:26:55 – STREAM: /media/tv/[170305-2227][20]にゃんこデイズ.m2ts
5 Mar 22:26:55 – #recdvb: using device: /dev/dvb/adapter0 using pid = 10294 device = /dev/dvb/adapter0/frontend0 Using DVB card “Siano M
obile Digital MDTV Receiver” tuning to 515143 kHz polling..

[/code]

と、デバイスファイルが開ける。

こんな簡単なログを見落としてたのか。失格ですね。

対策

今後、このような障害を起こさないように以下の対策をします。

まず、lsusb監視を仮想サーバ側にもつけるようにします。(今週中)

アプリログについて、監視文言にcannot open frontend deviceを加えます。

また、それ以外にも明示的にErrorと表記されないものについて見直しをし、

Zabbixに挙げられるようにします。(3月中)

さらに、監視サーバ上のVNCの解像度が低く、仮想マシンマネージャの操作に難があり、

一部virshコマンド等でオペレーションをしておりました。画面解像度の見直しをします。(対策済み)

サービスを守る。という使命感が乏しかったので、今後はサーバ優先の生活をするように担当者(自分)に言い聞かせました。

以上。

カテゴリー
Server Tech

サイトが大幅にリニューアル!

せっかくだから大幅リニューアルしました

どうも。昨日はサーバを落としてしまい、ご迷惑とご心配をおかけしました。

13:00から21:00ごろまでつながらない、もしくはメンテ画面が出ていたと思います。

 

サーバが落ちた原因はなんだったのか。

端的に言うと [fontcolor class=’danger’] 無茶なオペレーション [/fontcolor] です。

このサーバはzabbixで監視しているのですが、最近、ディスク空き容量が

50%を切っているというALMが頻発しており、そのたびにゴミを消してたのですが、

いい加減嫌になってきてディスク容量の増設をしました。

その作業自体は粛々とできたのですが、ALMがやむことはありませんでした。

詳しく見てみると、どうやら増設したはずのディスクが認識されていなかったようで、

それを認識させるためにあろう事か昼休みの限られた時間でマウント・アンマウント・fdiskなど

様々実施しました。そのとき悲劇が起きました。

 

ルート領域が認識しない…

時間が無い中の作業だったので、どこかでミスがあったのかもしれませんが、

ルート領域がマウントできなくなるというかなりやばいことが起きました。

昼休みが終わってしまいそうだったので、やむなくサーバを停止させ

FWの穴をふさいで放置。

 

対応は定時後を予定しそのまま仕事へ。

 

DBに泣かされた夜

大学の研究室とかでサーバを少しでも管理したことがある人なら

経験あると思いますが、/home領域は別パーティション、できれば別ディスクに

書き出しておくとOSの障害時、データ部を守ることができ、頭のわるい使い方をする

人の無茶な使い方に一定の保険をかけることができると思います。

このことは頭に入っていましたので、このサーバもwebのDocumentRootはhome領域に

書き出していました。ですが…。

DBのデータ部分はルート領域だった!!

なんとお粗末な!

DBのデータを何とか吸い出そうとしましたが叶わず。

画像の在処とかもDB管理だったので記事の復元はほぼ不可能と判断し、

サーバの再構築・新しいサイトの作成となりました。

 

今までまずかったところを修正したサイトを作成

まず、今回もWordpressでサイトを作成しました。

 

セキュリティ的な部分

DB関係の修正

前のサイトはDBのテーブル名・テーブルプレフィクスをデフォルトのまま作成してあったので

セキュリティ上よろしくないので修正。

SSL証明書のFQDNを修正

今までwwwを抜いてアクセスすると証明書エラーになっていたので、wwwなしのhttpsアクセス時でも

問題なくアクセスできるように改善。

WordPressログイン設定の修正

login画面のパスがわからないようにデフォルトから変更。

 

使い勝手の部分

VPN経由のアクセスを修正

http以外の通信についてはVPN経由でのアクセスとして

FWの穴開けは最低限しか行わないもののVPN経由では自由度の高い

オペレーションができるように改善。

自動バックアップスケジュール作成

自動でバックアップします。

全てのコンテンツデータを/home領域へ

二度と同じ間違いはしない。DBも/home領域にデータ吐きます。

 

サイト自体の改善

DBのメモリ等チューニング・クエリの最適化・bcache/fcacheの利用などで

サイトパフォーマンスが向上。レスポンス平均0.05秒まで改善。

さらにプラグインの整理で平均クエリを50まで減らす。

 

そしてリニューアル!

 

黒を基調としたシックなサイトになりました。

ちなみに一眼レフで撮った写真は上のメニューの

Photo Gallay に作成したボカロ曲は Music にはっつけてあります。

まだ完全に完成とは行きませんがとりあえずこれでやっていこうと思います。

 

終わりに

僕たちの戦いはこれからだ!(打ち切りエンド)