zookeeperでkibanaクラスタを同期する


最近、社内の応用ログ分析システムの構築に追われており、会社のアーキテクチャの要求に鑑みて、すべてのサービスが二重に機能しなければならない.アプリケーションログ分析システムでは、現在一般的に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:
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 ();
            });
        }
    });
});

次のようになります.
  • 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
    '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サーバがアラームとレポートサービスに応答できることを保証できます.