
次のことを想像してみましょう。1つまたは複数のITシステムによってサポートされるビジネスプロセスが存在する組織(自分の組織である場合もあります)で働いています。 監視システムがあることは確かです。 さらに、ビジョンは少しぼやけており、明確ではありません。それは産業システムか、無料のオープンソースかです。 ただし、すべてのセンサーが緑色であるが、ビジネスプロセス自体が不可解な障害を引き起こし、主要なインジケーターの減少を示している状況があります。 無許可の集会での法執行機関のスタンガンの閃光のように、状況が制御不能になり、対処する必要があるという考えがすぐに頭に浮かびます。 しかし、それはどのように明確ではありません。 これはかなり一般的なケースであり、症状からできるだけ早く取り除くことをお勧めします。 この問題を解決する体系的なアプローチは、サービスモデルのコンパイルを提供します。
(ITだけでなく)企業で発生するプロセスを視覚化するサービスを提供するTom Woodzhekは 、1つの興味深い研究を実施しました。 彼は、さまざまな人々にトーストを作るプロセスを描くように頼みました。 以下は、この作業の結果です。
![]() | ![]() | ![]() |
多くの写真には何が見えますか? そう! あらゆるシステムに存在するオブジェクトと通信。 それらが多ければ多いほど、アプローチはより体系的になります。 正確な粒度は、ビジネスシステムの「健康」をより正確に追跡します。 Visioを使用して図を作成することもできますが、リンクを描画するためのマーカー、オブジェクトの付箋を取り、マーカーボードにシステムを描画する方がはるかに興味深いです。 黄色の葉の数が多いほど、リンクが多いほど、問題の原因を正確に特定するために監視ポイントの最大数を特定する機会が増えます。
そして、標準のZabbix機能を拡張し、上記の体系的なアプローチを適用する分野での開発についてお話します。 それらは正確に2つあります。
1つ目は、システムのマッピングとサービスのヒートマップの作成です。 銀行業務プロセスの監視に関する真剣な経験を踏まえて、この分野の例を示します。 最も一般的な3つの銀行システムを考えてみましょう。 あなたの銀行がより典型的なシステムを持っている場合、すみません-ここではそれらを考慮しません。
リモートバンキングシステム(RBS):

エンタープライズデータバス(ESB):

意思決定システム(DSS):

このアプリケーションでは、次のようになります(はい、構造に多少違反していますが、同時に可視性は残っています)。

必要に応じて、次のレベルに移動できます。 同様に重要なのは、オブジェクトの上にマウスを移動すると、ポップアップウィンドウがポップアップし、イベントの説明とZabbixのチャートへのリンクが表示されることです。

このアプローチと指定された詳細度のおかげで、便利なダッシュボードが得られると同時に、問題を特定するためのシンプルなツールが得られます。 私たちのシステムの機能に関するいくつかの言葉:
-企業システム間の依存関係の視覚化。
-コンポーネントの相互の影響度の設定(通信の重み);
-Zabbixとの統合(ヒートマップ上のオブジェクトはトリガーに関連付けられています);
-オブジェクトにカーソルを合わせると、イベントテキストがポップアップ表示されます。
-オブジェクトの関係を設定するための視覚的なインターフェース。
-オブジェクトとZabbixトリガーの接続を設定するための視覚的なインターフェース。
例として、すでに開発済みのインターフェースをいくつか紹介します。
ヒートマップへのオブジェクトの追加:

Zabbixとの統合の追加:

Zabbixトリガーをヒートマップ上のオブジェクトに接続する:

このシステムは、 Google ChartとBootstrapを使用しています。 これはアルファ版ですが、長年にわたって成功を収めてきた産業システムの便利な機能を追加することで、さらに開発する予定です。 新しい機会が山積するにつれて、私はあなたに情報を提供し、投稿を公開しようとします。
2番目の操作時間は、Zabbixとの統合と、合成トランザクションの機能のヒートマップです。 実際、これはヒートマップの続きですが、反対側からの外観です。 間違いなく、アプリケーションとインフラストラクチャ自体の側からのみシステムを制御すると、必要な情報の完全性が得られません。 統合トランザクションを使用すると、ユーザーの側からこの問題を確認し、ユーザーがヘルプデスクを最初に呼び出す前でも問題を特定できます。
合成トランザクションはphantom.jsフレームワークに基づいています(ただし、casper.js、純粋なセレン、または好みに合わせて他のものに切り替えることを妨げるものは何もありません)。 テストラボでは、テストスクリプトの実行がcronを介して設定され、受信したデータがzabbix_trapperを介してZabbixに送信されます。 テストシナリオの例として、MTS個人アカウントにログインし、アカウントの残りのお金とインターネットパッケージのトラフィックを受け取ります。 以下はスクリプトのリストです。 銀行環境では、このツールの最も可能性の高い用途は、たとえばRBSです。 誰もシステムにログインしてアカウントからアカウントに1ルーブルを転送することを気にしません。
バランスとトラフィックバランスをチェックするためのテストシナリオ(Javascript)
/** * * 1.0 * * : * phantomjs --web-security=no getMtsBalance.js "< >" " (XXX) XXX-XX-XX" "<>" * : * phantomjs --web-security=no getMtsBalance.js "/tmp/getMtsBalance" "(916) 123-45-67" "P@ssw0rd" * * (c) Jet/ 2016 */ // PhantomJs stuff var fs = require('fs'); var system = require('system'); var webpage = require('webpage'); var args = system.args; var TRAFFIC_REGEX = /.{1,100}?([0-9.]+).{0,50}?(|).{0,50}? (\d+) /i; var DATE_REGEX = / - ([0-9]{1,2})\.([0-9]{1,2})\.([0-9]{2}) ([0-2][0-9]):([0-5][0-9])/; var SCRIPT_TIMEOUT = 40000; var config = { lkUrl : 'https://lk.ssl.mts.ru/', lkLogin: null, lkPass : null, outDir : null, debugDir: null, formattedStartTime: null }; var timer = { lastActionStartTime: null, lastActionTimeMs : null, currentTimeMillis: function() { return Date.now(); }, startAction: function() { this.lastActionStartTime = this.currentTimeMillis(); this.lastActionTimeMs = null; }, stopAction: function() { lastActionTimeMs = this.currentTimeMillis() - this.lastActionStartTime; }, getLastActionTimeMs: function() { return lastActionTimeMs; }, getLastActionTimeSec: function() { return lastActionTimeMs === null ? null : lastActionTimeMs/1000; } } var metrics = { trafLeftMb: null, daysLeft : null, balance : null, pages: { login: { availability : null, responseTimeSec: null }, lk: { availability : null, responseTimeSec: null } } }; ////////// HELPER FUNCTIONS ////////// var func = { log: function(s) { console.log(this.formatDateTimeForLog(new Date()) + " " + s); }, roundToTwo: function(num) { return +(Math.round(num + "e+2") + "e-2"); }, zero: function(i) { return i < 10 ? '0' + i : i; }, formatDateTimeForLog: function(date) { var dd = date.getDate(); var mm = date.getMonth() + 1; var yy = date.getFullYear(); var hh = date.getHours(); var min = date.getMinutes(); var ss = date.getSeconds(); var ms = date.getMilliseconds(); ms = ('00' + ms).slice(-3); return yy + '-' + this.zero(mm) + '-' + this.zero(dd) + ' ' + this.zero(hh) + ':' + this.zero(min) + ':' + this.zero(ss) + '.' + ms; }, formatDateTimeForFileName: function(date) { return date.getFullYear() + this.zero(date.getMonth() + 1) + this.zero(date.getDate()) + '-' + this.zero(date.getHours()) + this.zero(date.getMinutes()) ; }, writeMetricToFileAndLog: function(filePrefix, metricName, metricValue) { if ( metricValue == null ) { metricValue = 0; } fs.write(config.outDir + filePrefix + config.formattedStartTime + '.log', this.roundToTwo(metricValue), 'w'); this.log(' ' + metricName + ' = ' + metricValue); } } // config.outDir = args[1] + '/'; config.lkLogin = args[2]; config.lkPass = args[3]; config.debugDir = config.outDir + 'debug/'; fs.makeDirectory(config.debugDir); func.log(" : " + config.outDir); // - setTimeout(function() { func.log(" " + SCRIPT_TIMEOUT + " "); if ( metrics.pages.login.availability == null ) { metrics.pages.login.availability = 0; metrics.pages.login.responseTimeSec = 0; } if ( metrics.pages.lk.availability == null ) { metrics.pages.lk.availability = 0; metrics.pages.lk.responseTimeSec = 0; } outMetricsAndExit(); }, SCRIPT_TIMEOUT); // "" var page = webpage.create(); page.settings.userAgent = 'Mozilla/4.0'; // config.formattedStartTime = func.formatDateTimeForFileName(new Date()); // func.log(" " + config.lkUrl); timer.startAction(); page.open(config.lkUrl, function (status) { timer.stopAction(); metrics.pages.login.responseTimeSec = timer.getLastActionTimeSec(); if (status !== "success" ) { func.log(" " + config.lkUrl + " "); metrics.pages.login.availability = 0; outMetricsAndExit(); } else { func.log(" " + config.lkUrl + " "); metrics.pages.login.availability = 1; page.render(config.debugDir + 'login.png'); // iframe', var contentN = 0; page.onLoadFinished = function(status) { // , // (iframe'), // , .. , // -. - // "", // . . // timer.stopAction(); contentN++; func.log(' N' + contentN + ':' + status); page.render(config.debugDir + contentN + '.png'); fs.write(config.debugDir + contentN + '.html', page.content, 'w'); if ( status === 'success') { getMtsMetrics(page, contentN); } }; func.log(" , : " + config.lkLogin); timer.startAction(); page.evaluate(function(config) { var form = document.forms[0]; form.phone.value = config.lkLogin; form.password.value = config.lkPass; form.elements[2].click(); }, config); } }); function getMtsMetrics(page, contentN) { if ( page.content.match(' ') ) { func.log(" : . "); metrics.pages.lk.availability = 0; metrics.pages.lk.responseTimeSec = 0; outMetricsAndExit(); } // iFrame' findBalanceInPage(page, contentN); // - findTrafficInfoInPage(page); // , if ( checkGotMetricsAlready() ) { outMetricsAndExit(); } } /** * */ function findBalanceInPage(page, contentN) { // iframe' if ( page.framesCount == 0 ) { return; } func.log(" iframe'"); var balanceResult = page.evaluate(function() { var result = { iframes: [], balance: null }; $("iframe").each(function(i, iframe) { var iframeBody = $(iframe).contents().find('body'); if ( iframeBody.size() > 0 ) { result.iframes.push( iframeBody.html() ); // 1 - DOM if ( result.balance === null ) { iframeBody.find(".b-header_balance").each(function() { var m = $(this).text().match(/([0-9.]+) /i); if ( m ) { result.balance = m[1]; } }); } // 2 - regex if ( result.balance === null ) { var m = iframeBody.text().match(/\s*:\s*-?([0-9.]+)\s*/i); if ( m ) { result.balance = m[1]; } } } }); return result; }); var iframesAnalyzed = balanceResult.iframes.length; func.log(" iframe':" + iframesAnalyzed); if ( iframesAnalyzed > 0 ) { // iFrame' for (var i = 0; i < iframesAnalyzed; i++) { var iframeContent = balanceResult.iframes[i]; func.log(" iframe " + config.debugDir + contentN + '_iframe' + i + '.html'); fs.write(config.debugDir + contentN + '_iframe' + i + '.html', iframeContent, 'w'); } // , if ( balanceResult.balance !== null ) { if ( metrics.pages.lk.availability === null ) { // , metrics.pages.lk.availability = 1; metrics.pages.lk.responseTimeSec = timer.getLastActionTimeSec(); } func.log(" : " + balanceResult.balance); metrics.balance = balanceResult.balance; } } } /** * - */ function findTrafficInfoInPage(page) { var traf = page.content.match(TRAFFIC_REGEX); if ( traf ) { func.log(" - : " + traf); metrics.trafLeftMb = traf[1]; var trafUnits = traf[2]; if ( trafUnits.toLowerCase() == '' ) { metrics.trafLeftMb *= 1024; } metrics.daysLeft = traf[3]; } else if (page.content.match(" ") ) { func.log(" : "); metrics.trafLeftMb = 0; if ( page.injectJs("jquery.min.js") ) { metrics.daysLeft = page.evaluate(function() { var p = $("p:contains('- ')"); var pText = p.find("b").text(); console.log( " : " + pText); return pText.replace(/\D/g, ''); }); } } } /** * , */ function checkGotMetricsAlready() { if ( metrics.pages.login.availability == 0 || metrics.pages.lk.availability == 0 ) { // ( ) // , return true; } if ( metrics.balance != null && metrics.daysLeft != null && metrics.trafLeftMb != null ) { // , return true; } return false; } /** * , * */ function outMetricsAndExit() { func.log(":"); func.writeMetricToFileAndLog('traffic', 'metrics.trafLeftMb', metrics.trafLeftMb); func.writeMetricToFileAndLog('money', 'metrics.balance', metrics.balance); func.writeMetricToFileAndLog('daysLeft', 'metrics.daysLeft', metrics.daysLeft); func.writeMetricToFileAndLog('status-initialpageload', 'metrics.pages.login.availability', metrics.pages.login.availability); func.writeMetricToFileAndLog('time-initialpageload', 'metrics.pages.login.responseTimeSec', metrics.pages.login.responseTimeSec); func.writeMetricToFileAndLog('status-lkpageload', 'metrics.pages.lk.availability', metrics.pages.lk.availability); func.writeMetricToFileAndLog('time-lkpageload', 'metrics.pages.lk.responseTimeSec', metrics.pages.lk.responseTimeSec); phantom.exit(); }
収集されるアイテムは次のとおりです。

それぞれに独自のスケジュールがあります。
監視にオープンソース監視ソリューションを使用することは、すべてのトラブルのピルであるとは言いたくありません。 オープナーの秘密を教えましょう。物理学と同様に、ここではお金と労働の保存の法則が適用されます。 完成品に注ぐお金が多くなればなるほど、洗練のための人件費は少なくなり、逆もまた同様です。 常識、利用可能な予算、人的要因に常に従う必要があります。チームは、最初の電話でビジネス監視を急いで行う準備ができていますか?
特に監視技術に興味がある場合は、このトピックに関する以前の記事「ビジネスアプリケーションの監視の原則」に精通することをお勧めします。
記事の著者: アントンカシモフ