zookeeperでkibanaクラスタを同期する
13030 ワード
最近、社内の応用ログ分析システムの構築に追われており、会社のアーキテクチャの要求に鑑みて、すべてのサービスが二重に機能しなければならない.アプリケーションログ分析システムでは、現在一般的にELK stackのフレームワークが採用されており、フロントエンド部分のkibanaが必須です.一方、kibanaをダブルアクティブにする必要がある場合は、2つのkibana間で一定の同期が必要です(1つのelasticsearchクラスタをデータストレージとして共有するため、2つのkibana間でalarm、reportなどの動作を実行する際には、同じalarmまたはreportが2台のサーバで2回送信されないように、一定の同期メカニズムが必要です).もちろん、同期の方法はたくさんあります.elasticsearchを同期のサーバとして直接使用することができますが、これはより多くの開発作業とデータの設計作業に関連します.私は怠け者です.結局、ログ分析システムでkafkaとzookeeperを使っています.それはzookeeperで分布式同期をしましょう.結局、この商品はこれを持ってきたのです.しかしkibanaはnodejsが開発したアプリケーションで、javaに及ばないので、ここで分かち合って、その中で踏んだ穴があります.
同期が必要なのはkibanaのプラグインsentinlです.これはx-packのwatcherに似たプラグインで、アラームとレポートサービスに使用されます.2つのkibanaサーバがインストールされているため、1つのアラームルールが作成されるとelasticsearchにルールが格納され、2つのサーバがタイミングよく出発し、同じアラームが再発する問題が発生します.この問題を解決するには、2台のサーバ間で同期する必要があります.kibanaはnodeで開発されているので、node上のnode-zookeeeperを使用する必要があります.ここで注意すべきことは、このモジュールはnode native moduleであり、対応するプラットフォーム上でコンパイルする必要がある.具体的な方法は、native C++モジュールでNodeを拡張する2つの博文を参照する.jsはdockerを使用して異なるプラットフォーム上のnode native moduleをコンパイルします.
くり
まず簡単な例を見てみましょう.この例はnode-zookeeperが持っているtestです.js:
次のようになります. connect:ホスト名とZooKeeperサーバを含むポート. timeout:ミリ秒単位で、ZooKeeperがクライアント通信を待つ最長時間を表し、その後、セッションが死亡したと宣言します.ZooKeeperのセッションでは、通常、タイムアウト時間を5~10秒設定します. debug_レベル:ログの出力レベルを設定します.ZOO_の4つのレベルがあります.LOG_LEVEL_ERROR, ZOO_LOG_LEVEL_WARN, ZOO_LOG_LEVEL_INFO, ZOO_LOG_LEVEL_DEBUG host_order_deterministic:zkクライアントインスタンスを初期化した後、そのインスタンスがZooKeeperサーバクラスタ内のホストに接続が成功するまで、またはセッションが切断されるまで、決定された順序で接続されているかどうか.
共通API connect():ZooKeeper Server への接続 a_create(path,data,flags,path_cb):znodeを作成し、このznodeのノードタイプ(永続、一時、永続、一時的、一時的) を決定する値を付与します. a_get(path,watch,data_cb):path:データのzondeノードパスを取得したいです.watch:ノードの後続のデータ変更を傍受したいかどうかを示します.data_cb(rc,error,stat,data):rc:return code,0は成功です.Error:エラーメッセージ.stat:znodeのメタデータ情報.data:znodeのデータ. a_set(path,data,version,stat_cb):ZooKeeperは、znodeのデータを局所的に書き込んだり読み込んだりすることは許されず、znodeノードのデータを設定したり読み込んだりすると、znodeノードの内容が全体に置き換えられたり、すべて読み込まれたりすることに注意してください.path:データのzondeノードパスを設定したいです.Data:znodeノードには任意のデータが含まれ、バイト配列(byte array)として格納されるデータを設定します.バイト配列の具体的なフォーマットは各アプリケーションの実装に固有であり、ZooKeeperは直接解析のサポートを提供しない.ユーザーはProtobuf、Thrift、AvroまたはMessagePackなどのシーケンス化プロトコルを使用してznodeに保存されたデータフォーマットを処理することができ、一般的にUTF-8符号化された文字列で十分である.version:znodeのversionは、statから抽出されます.data_cb(rc,error,stat):データのコールバックを設定します. close():クライアント接続を閉じる a_exists(path,watch,stat_cb):znodeが存在するか否かを判断する adelete(path,version,voidcb):znodeを削除し、末尾に「予約ワード」deleteと衝突しないようにします.の a_get_children(path,true,function(rc,error,children_cb):pathで指定されたノードの下のサブノードを取得し、ソートされていない配列 を返します.
zookeeperでロックする
zookeeperHelperを作成します.js
モジュールをsentinlのserver/lib/ディレクトリにコピーします.sentinlのpackageを同時に更新します.jsonファイル.次のdependencyを追加
次のコマンドでzookeeperをダウンロードします.
scheduleを変更します.jsファイル:
これにより、単純なzookeeperロックにより、同じ時刻にkibanaサーバがアラームとレポートサービスに応答できることを保証できます.
同期が必要なのはkibanaのプラグインsentinlです.これはx-packのwatcherに似たプラグインで、アラームとレポートサービスに使用されます.2つのkibanaサーバがインストールされているため、1つのアラームルールが作成されるとelasticsearchにルールが格納され、2つのサーバがタイミングよく出発し、同じアラームが再発する問題が発生します.この問題を解決するには、2台のサーバ間で同期する必要があります.kibanaはnodeで開発されているので、node上のnode-zookeeeperを使用する必要があります.ここで注意すべきことは、このモジュールはnode native moduleであり、対応するプラットフォーム上でコンパイルする必要がある.具体的な方法は、native C++モジュールでNodeを拡張する2つの博文を参照する.jsはdockerを使用して異なるプラットフォーム上のnode native moduleをコンパイルします.
くり
まず簡単な例を見てみましょう.この例はnode-zookeeperが持っているtestです.js:
var ZooKeeper = require ("zookeeper");
var zk = new ZooKeeper({
connect: "localhost:8888" // zk server
,timeout: 200000 //
,debug_level: ZooKeeper.ZOO_LOG_LEVEL_WARN
,host_order_deterministic: false
});
zk.connect(function (err) {
if(err) throw err;
console.log ("zk session established, id=%s", zk.client_id);
zk.a_create ("/node.js1", "some value", ZooKeeper.ZOO_SEQUENCE | ZooKeeper.ZOO_EPHEMERAL, function (rc, error, path) {
if (rc != 0) {
console.log ("zk node create result: %d, error: '%s', path=%s", rc, error, path);
} else {
console.log ("created zk node %s", path);
process.nextTick(function () {
zk.close ();
});
}
});
});
次のようになります.
共通API
zookeeperでロックする
zookeeperHelperを作成します.js
'use strict'
const ZooKeeper = require('zookeeper');
const Promise = require('bluebird');
const _ = require('lodash');
'development';
let connect = 'localhost:2181';
let timeout = 20000; // client , 。server
let path = '/sentinl';
let debug_level = ZooKeeper.ZOO_LOG_LEVEL_WARN;
let host_order_deterministic = false;
let defaultInitOpt = {
connect,
timeout,
debug_level,
host_order_deterministic
};
class ZK {
constructor(opt) {
this.opt = opt;
this._initZk();
}
_initZook() {
this.zookeeper = new ZooKeeper(this.opt || defaultInitOpt);
}
_initZk() {
this.zookeeper = new ZooKeeper(this.opt || defaultInitOpt);
}
registZk() {
let self = this;
self.zookeeper.connect(function (err, client) {
if (err) throw err;
console.log('zk session established, id=%s', self.zookeeper.client_id);
self.client = client;
// create parent node
client.a_create(path, null, ZooKeeper.ZOO_PERSISTENT, function (rc, error, path) {
if (rc != 0) {
console.log("zk node create result: %d, error: '%s', path=%s", rc, error, path);
} else {
console.log("created zk node %s", path);
}
})
// create children node
client.a_create(path + '/' + 'alarm', null, ZooKeeper.ZOO_SEQUENCE | ZooKeeper.ZOO_EPHEMERAL, function (rc, error, path) {
if (rc != 0) {
console.log("zk node create result: %d, error: '%s', path=%s", rc, error, path);
} else {
let pathArr = path.split('/');
self.node = pathArr[pathArr.length - 1];
console.log("mynode is %s", self.node);
}
})
})
}
getLock() {
let self = this;
return new Promise((resolve, reject) => {
console.log('zk session established, id=%s', self.zookeeper.client_id);
self.client.a_get_children(path, true, function (rc, error, children) {
if (rc !== 0) {
console.log('zk node get result: %d, error: "%s", stat=%s, children=%s', rc, error, stat, children);
} else {
children = children.sort();
console.log('get zk children: ' + children);
if (children[0] === self.node) {
resolve(true);
}
else {
resolve(false);
}
}
})
});
}
}
module.exports = ZK;
モジュールをsentinlのserver/lib/ディレクトリにコピーします.sentinlのpackageを同時に更新します.jsonファイル.次のdependencyを追加
"zookeeper": "^3.4.9"
次のコマンドでzookeeperをダウンロードします.
npm update
scheduleを変更します.jsファイル:
import ZookeeperHelper from './zookeeperHelper';
/**
* Schedules and executes watchers in background
*/
export default function Scheduler(server) {
const config = getConfiguration(server);
let watcher;
let client;
const zkHelper = new ZookeeperHelper();
...
/* Run Watcher in interval */
server.sentinlStore.schedule[task._id].later = later.setInterval(() => {
zkHelper.getLock().then(function (result) {
if(result)
{
server.log(['status', 'info', 'Sentinl', 'zookeeper'],
'get the lock, trigger watcher ' + task._id);
watching(task);
}
else {
server.log(['status', 'info', 'Sentinl', 'zookeeper'],
'not get the lock');
}
}, function (error) {
server.log(['status', 'error', 'Sentinl', 'scheduler'],error);
})
}, interval);
これにより、単純なzookeeperロックにより、同じ時刻にkibanaサーバがアラームとレポートサービスに応答できることを保証できます.