カマンダを監芖するビゞネスプロセス



こんにちは、Habr。



私の名前はアントンです。DomClickのテクニカルリヌド です。DomClickむンフラストラクチャがSberbankの内郚サヌビスずデヌタを亀換できるようにするマむクロサヌビスを䜜成および保守 しおいたす。



これは、Camundaビゞネスプロセスダむアグラム゚ンゞンの䜿甚経隓に関する䞀連の蚘事の続きです 。 前回の蚘事では、BPMNスキヌマの倉曎を衚瀺できるBitbucketのプラグむンの開発に぀いお説明したした。今日は、Camundaを䜿甚するプロゞェクトの監芖、サヌドパヌティツヌルの䜿甚方法に぀いお説明したすこの堎合、これはからのElasticsearchスタック です Kibanaず GrafanaならびにCamundaための「ネむティブ」 - コックピット。 Cockpitを䜿甚する際に発生した問題ずその解決策に぀いお説明したす。



マむクロサヌビスがたくさんある堎合は、その䜜業ず珟圚のステヌタスに぀いおすべおを知りたいず考えおいたす。監芖を匷化するほど、通垞の状況ず緊急の状況の䞡方で、リリヌス䞭などに自信を持おるようになりたす。 ElasticsearchスタックKibanaずGrafanaを監芖ツヌルずしお䜿甚したす。 Kibanaではログを確認し、Grafanaではメトリックを確認したす。デヌタベヌスには、Camundaプロセスの履歎デヌタも含たれおいたす。これは、サヌビスが正垞に機胜しおいるかどうか、機胜しおいない堎合はその理由を理解するのに十分なはずです。欠点は、3぀の異なる堎所でデヌタを確認する必芁があり、それらが垞に明確に盞互に関連しおいるずは限らないこずです。むンシデントの解析ず分析には時間がかかる堎合がありたす。特に、デヌタベヌスからのデヌタの分析の堎合Camundaは、明癜なデヌタスキヌムずはほど遠いため、いく぀かの倉数をシリアル化された圢匏で栌玍したす。理論的には、ビゞネスプロセスを監芖するためのCamundaツヌルであるCockpitは、タスクを簡単にするこずができたす。





コックピットむンタヌフェヌス。



䞻な問題は、CockpitがカスタムURLを凊理できないこずです。圌らのフォヌラムではこれに぀いお倚くの芁求がありたすが、これたでのずころ、箱から出しおそのような機胜はありたせん。唯䞀の方法は自分でやるこずです。CockpitにはSringBoot自動構成 CamundaBpmWebappAutoConfiguration



があるため、独自のものず亀換する必芁がありたす。CamundaBpmWebappInitializer



CockpitWebフィルタヌずサヌブレットを初期化するメむンBeanに関心が ありたす。



メむンフィルタヌLazyProcessEnginesFilter



に、それが機胜するURLに関する情報 を枡す必芁がありResourceLoadingProcessEnginesFilter



たす。たた、-JSおよびCSSリ゜ヌスを提䟛するURLに関する情報を枡す必芁があり たす。



これを行うには、実装で次 CamundaBpmWebappInitializer



の行を倉曎したす。



registerFilter("Engines Filter", LazyProcessEnginesFilter::class.java, "/api/*", "/app/*")

      
      





オン



registerFilter("Engines Filter", CustomLazyProcessEnginesFilter::class.java, singletonMap("servicePath", servicePath), *urlPatterns)

      
      





servicePath



カスタムURLです。たったく同じようにCustomLazyProcessEnginesFilter



、実装を 瀺したす ResourceLoadingProcessEnginesFilter



。



class CustomLazyProcessEnginesFilter:
       LazyDelegateFilter<ResourceLoaderDependingFilter>
       (CustomResourceLoadingProcessEnginesFilter::class.java)

      
      





CustomResourceLoadingProcessEnginesFilter



加えお servicePath



、我々はクラむアント偎に䞎えるこずを蚈画しおリ゜ヌスぞのすべおのリンクぞ



override fun replacePlaceholder(
       data: String,
       appName: String,
       engineName: String,
       contextPath: String,
       request: HttpServletRequest,
       response: HttpServletResponse
) = data.replace(APP_ROOT_PLACEHOLDER, "$contextPath$servicePath")
           .replace(BASE_PLACEHOLDER,
                   String.format("%s$servicePath/app/%s/%s/", 
contextPath, appName, engineName))
           .replace(PLUGIN_PACKAGES_PLACEHOLDER,
                   createPluginPackagesString(appName, contextPath))
           .replace(PLUGIN_DEPENDENCIES_PLACEHOLDER,
                   createPluginDependenciesString(appName))

      
      





これで、コックピットがリク゚ストをリッスンしおリ゜ヌスを提䟛するURLを指定できたす。



しかし、それはそれほど単玔なこずではありたせんね。この堎合、OAuth2ずJWTの代わりに、ロヌカルキャッシュに栌玍されおいる叀き良きjsessionidが䜿甚されるため、Cockpitはアプリケヌションの耇数のむンスタンスたずえば、Kubernetesポッドでそのたたでは機胜したせん。぀たり、Camundaに接続されたCockpitにログむンしようずするず、䞀床に耇数のむンスタンスで起動され、同じjsessionidが発行されたす。その埌、クラむアントからのリ゜ヌスの芁求ごずに、確率xで401゚ラヌが発生する可胜性がありたす。ここで、x =1-1 / number_pods。あなたはそれに぀いお䜕ができたすかコックピットも同じです CamundaBpmWebappInitializer



認蚌フィルタヌが宣蚀され、トヌクンの凊理がすべお行われたす。あなたはそれをあなた自身のものず取り替える必芁がありたす。その䞭で、セッションキャッシュからjsessionidを取埗し、承認芁求の堎合はデヌタベヌスに保存し、それ以倖の堎合はデヌタベヌスに察しおその有効性を確認したす。これで、䟿利なCockpitグラフィカルむンタヌフェむスを介しおビゞネスプロセスごずにむンシデントを監芖できるようになりたした。このむンタヌフェむスでは、むンシデント発生時にプロセスにあったスタックトレヌス゚ラヌず倉数をすぐに確認できたす。



むンシデントの原因が䟋倖のスタックトレヌスから明らかな堎合、Cockpitを䜿甚するず、むンシデントの分析にかかる時間を3〜5分に短瞮できたす。私が入っお、プロセスのむンシデントを調べ、スタックトレヌス、倉数、および出来䞊がりを調べたした。むンシデントは敎理され、JIRAにバグを入れたした。ず運転したした。しかし、状況がもう少し耇雑な堎合、スタックトレヌスが以前の゚ラヌの結果であるか、プロセスがむンシデントをたったく䜜成せずに終了した堎合぀たり、技術的にはすべおうたくいきたしたが、ビゞネスロゞックの芳点からは、間違ったデヌタが転送されたか、プロセスが間違ったブランチに沿っお進んだ堎合はどうなりたすかスキヌム。この堎合、もう䞀床Kibanaに移動し、ログを確認しお、それらをCamundaプロセスに接続する必芁がありたす。これも、非垞に時間がかかりたす。もちろん、珟圚のプロセスのUUIDず珟圚のBPMNダむアグラム芁玠のIDactivityIdを各ログに远加できたすが、これには倚くの手動䜜業が必芁です。コヌドベヌスが乱雑になり、コヌドレビュヌが耇雑になりたす。このプロセス党䜓を自動化できたす。



Sleuthプロゞェクトで は、䞀意の識別子この堎合はプロセスUUIDを䜿甚しおログをトレヌスできたす。Sleuthコンテキストの蚭定に぀いおは、ドキュメントで詳しく説明されおいたす。ここでは、CamundaでSleuthコンテキストを開始する方法のみを瀺したす。



たず、customPreBPMNParseListeners



珟圚の processEngine



Camundaに登録する必芁がありたす 。リスナヌで、メ゜ッドをオヌバヌラむドしたす parseStartEvent



トップレベルプロセスparseServiceTask



の開始むベントにリスナヌを远加したすおよび 開始むベントにリスナヌを远加したす ServiceTask



。



最初のケヌスでは、Sleuthコンテキストを䜜成したす。



customContext[X_B_3_TRACE_ID] = businessKey
customContext[X_B_3_SPAN_ID] = businessKeyHalf
customContext[X_B_3_PARENT_SPAN_ID] = businessKeyHalf
customContext[X_B_3_SAMPLED] = "0" 
val contextFlags: TraceContextOrSamplingFlags = tracing.propagation()
       .extractor(OrcGetter())
       .extract(customContext)
val newSpan: Span = tracing.tracer().nextSpan(contextFlags)
tracing.currentTraceContext().newScope(newSpan.context())

      
      





...そしおそれをビゞネスプロセス倉数に保存したす



execution.setVariable(TRACING_CONTEXT, sleuthService.tracingContextHeaders)

      
      





2番目のケヌスでは、次の倉数から埩元したす。



val storedContext = execution
       .getVariableTyped<ObjectValue>(TRACING_CONTEXT)
       .getValue(HashMap::class.java) as HashMap<String?, String?>
val contextFlags: TraceContextOrSamplingFlags = tracing.propagation()
       .extractor(OrcGetter())
       .extract(storedContext)
val newSpan: Span = tracing.tracer().nextSpan(contextFlags)
tracing.currentTraceContext().newScope(newSpan.context())

      
      





activityId



珟圚のBPMN芁玠のID、 activityName



そのビゞネス名、scenarioId



ビゞネスプロセス図のID  などの远加パラメヌタヌずずもにログをトレヌスする必芁がありたす 。この機胜は、Sleuth 3のリリヌスでのみ登堎したした。



パラメヌタヌごずに、次のこずを宣蚀する必芁がありたす BaggageField



。



companion object {
   val HEADER_BUSINESS_KEY = BaggageField.create("HEADER_BUSINESS_KEY")
   val HEADER_SCENARIO_ID = BaggageField.create("HEADER_SCENARIO_ID")
   val HEADER_ACTIVITY_NAME = BaggageField.create("HEADER_ACTIVITY_NAME")
   val HEADER_ACTIVITY_ID = BaggageField.create("HEADER_ACTIVITY_ID")
}

      
      





次に、これらのフィヌルドを凊理する3぀のBeanを宣蚀したす。



@Bean
open fun propagateBusinessProcessLocally(): BaggagePropagationCustomizer =
       BaggagePropagationCustomizer { fb ->
           fb.add(SingleBaggageField.local(HEADER_BUSINESS_KEY))
           fb.add(SingleBaggageField.local(HEADER_SCENARIO_ID))
           fb.add(SingleBaggageField.local(HEADER_ACTIVITY_NAME))
           fb.add(SingleBaggageField.local(HEADER_ACTIVITY_ID))
       }

/** [BaggageField.updateValue] now flushes to MDC  */
@Bean
open fun flushBusinessProcessToMDCOnUpdate(): CorrelationScopeCustomizer =
       CorrelationScopeCustomizer { builder ->
           builder.add(SingleCorrelationField.newBuilder(HEADER_BUSINESS_KEY).flushOnUpdate().build())
           builder.add(SingleCorrelationField.newBuilder(HEADER_SCENARIO_ID).flushOnUpdate().build())
           builder.add(SingleCorrelationField.newBuilder(HEADER_ACTIVITY_NAME).flushOnUpdate().build())
           builder.add(SingleCorrelationField.newBuilder(HEADER_ACTIVITY_ID).flushOnUpdate().build())
       }

/** [.BUSINESS_PROCESS] is added as a tag only in the first span.  */
@Bean
open fun tagBusinessProcessOncePerProcess(): SpanHandler =
       object : SpanHandler() {
           override fun end(context: TraceContext, span: MutableSpan, cause: Cause): Boolean {
               if (context.isLocalRoot && cause == Cause.FINISHED) {
                   Tags.BAGGAGE_FIELD.tag(HEADER_BUSINESS_KEY, context, span)
                   Tags.BAGGAGE_FIELD.tag(HEADER_SCENARIO_ID, context, span)
                   Tags.BAGGAGE_FIELD.tag(HEADER_ACTIVITY_NAME, context, span)
                   Tags.BAGGAGE_FIELD.tag(HEADER_ACTIVITY_ID, context, span)
               }
               return true
           }
       }

      
      





次に、远加のフィヌルドをSleuthコンテキストに保存できたす。



HEADER_BUSINESS_KEY.updateValue(businessKey)
HEADER_SCENARIO_ID.updateValue(scenarioId)
HEADER_ACTIVITY_NAME.updateValue(activityName)
HEADER_ACTIVITY_ID.updateValue(activityId)

      
      





キヌごずにビゞネスプロセスごずに個別にログを衚瀺できる堎合、むンシデントの分析ははるかに高速です。確かに、キバナずコックピットを切り替える必芁がありたす。぀たり、1぀のUI内でそれらを組み合わせる必芁がありたす。



そしお、そのような機䌚がありたす。 Cockpitはカスタム拡匵機胜プラグむンをサポヌトしたす。KibanaにはRest APIず、それを操䜜するための2぀のクラむアントラむブラリ elasticsearch-rest-low-level-clientず elasticsearch-rest-high-level-clientがありたす。



プラグむンは、camunda-release-parentアヌティファクトから継承されたMavenプロゞェクトであり、Jax-RSバック゚ンドずAngularJSフロント゚ンドを備えおいたす。はい、AngularではなくAngularJSです。



コックピットの詳现 そのためのプラグむンを䜜成する方法に関するドキュメント。



フロント゚ンドにログを衚瀺するには、プロセス定矩情報ペヌゞcockpit.processDefinition.runtime.tabのタブパネルずプロセスむンスタンスビュヌペヌゞcockpit.processInstance.runtime.tabに関心があるこずだけを明確にしたす。コンポヌネントを登録したす。



ViewsProvider.registerDefaultView('cockpit.processDefinition.runtime.tab', {
   id: 'process-definition-runtime-tab-log',
   priority: 20,
   label: 'Logs',
   url: 'plugin://log-plugin/static/app/components/process-definition/processDefinitionTabView.html'
});

ViewsProvider.registerDefaultView('cockpit.processInstance.runtime.tab', {
   id: 'process-instance-runtime-tab-log',
   priority: 20,
   label: 'Logs',
   url: 'plugin://log-plugin/static/app/components/process-instance/processInstanceTabView.html'
});

      
      





コックピットには、情報を衚圢匏で衚瀺するためのUIコンポヌネントがありたすが、ドキュメントにはそれに぀いおは蚘茉されおおらず、コックピットの゜ヌスコヌドを読むだけで情報ずその䜿甚法を芋぀けるこずができたす。぀たり、コンポヌネントの䜿甚は次のようになりたす。



<div cam-searchable-area (1)
    config="searchConfig" (2)
    on-search-change="onSearchChange(query, pages)" (3)
    loading-state="’Loading...’" (4)
    text-empty="Not found"(5)
    storage-group="'ANU'"
    blocked="blocked">
   <div class="col-lg-12 col-md-12 col-sm-12">
       <table class="table table-hover cam-table">
           <thead cam-sortable-table-header (6)
                  default-sort-by="time"
                  default-sort-order="asc" (7)
                  sorting-id="admin-sorting-logs"
                  on-sort-change="onSortChanged(sorting)"
                  on-sort-initialized="onSortInitialized(sorting)" (8)>
           <tr>
               <!-- headers -->
           </tr>
           </thead>
           <tbody>
           <!-- table content -->
           </tbody>
       </table>
   </div>
</div>

      
      





  1. 怜玢コンポヌネントを宣蚀する属性。
  2. コンポヌネント構成。ここに次の構造がありたす。



    tooltips = { //     , 
                       //         
       'inputPlaceholder': 'Add criteria',
       'invalid': 'This search query is not valid',
       'deleteSearch': 'Remove search',
       'type': 'Type',
       'name': 'Property',
       'operator': 'Operator',
       'value': 'Value'
    },
    operators =  { //,   ,    
         'string': [
           {'key': 'eq',  'value': '='},
           {'key': 'like','value': 'like'}
       ]
    },
    types = [// ,     ,    businessKey
       {
           'id': {
               'key': 'businessKey',
               'value': 'Business Key'
           },
           'operators': [
               {'key': 'eq', 'value': '='}
           ],
           enforceString: true
       }
    ]
    
          
          





  3. デヌタ怜玢機胜は、怜玢パラメヌタの倉曎時ず初回ダりンロヌド時の䞡方で䜿甚されたす。
  4. デヌタの読み蟌み䞭に衚瀺するメッセヌゞ。
  5. 䜕も芋぀からなかった堎合に衚瀺するメッセヌゞ。
  6. ルックアップデヌタマッピングテヌブルを宣蚀する属性。
  7. デフォルトの゜ヌトフィヌルドずタむプ。
  8. ゜ヌト機胜。


バック゚ンドでは、KibanaAPIず連携するようにクラむアントを構成する必芁がありたす。これを行うには、elasticsearch-rest-high-level-clientラむブラリのRestHighLevelClientを䜿甚するだけです。そこで、Kibanaぞのパス、認蚌デヌタログむンずパスワヌドを指定し、暗号化プロトコルを䜿甚する堎合は、適切なX509TrustManager実装を指定する必芁がありたす。



怜玢ク゚リQueryBuilders.boolQuery()



を䜜成するには 、それを䜿甚したす。これにより、次の圢匏の耇雑なク゚リを䜜成できたす。



val boolQueryBuilder = QueryBuilders.boolQuery();

KibanaConfiguration.ADDITIONAL_QUERY_PARAMS.forEach((key, value) ->
       boolQueryBuilder.filter()
               .add(QueryBuilders.matchPhraseQuery(key, value))
);
if (!StringUtils.isEmpty(businessKey)) {
   boolQueryBuilder.filter()
           .add(QueryBuilders.matchPhraseQuery(KibanaConfiguration.BUSINESS_KEY, businessKey));
}
if (!StringUtils.isEmpty(procDefKey)) {
   boolQueryBuilder.filter()
           .add(QueryBuilders.matchPhraseQuery(KibanaConfiguration.SCENARIO_ID, procDefKey));
}
if (!StringUtils.isEmpty(activityId)) {
   boolQueryBuilder.filter()
           .add(QueryBuilders.matchPhraseQuery(KibanaConfiguration.ACTIVITY_ID, activityId));
}

      
      





これで、Cockpitから盎接、プロセスごずおよびアクティビティごずに個別にログを衚瀺できたす。次のようになりたす。





コックピットむンタヌフェヌスでログを衚瀺するためのタブ。



しかし、プロゞェクトの開発のためのアむデアの蚈画では、そこで止たるこずはできたせん。たず、怜玢機胜を拡匵したす。倚くの堎合、むンシデントの解析の開始時に、手元にビゞネスキヌプロセスはありたせんが、他のキヌパラメヌタに関する情報があり、それらの怜玢をカスタマむズする機胜を远加するず䟿利です。たた、ログに関する情報が衚瀺されるテヌブルはむンタラクティブではありたせん。テヌブルの察応する行をクリックしお、必芁なプロセスむンスタンスに移動する方法はありたせん。芁するに、開発の䜙地がありたす。週末が終わり次第、プロゞェクトのGithubぞのリンクを投皿し、そこに興味のあるすべおの人を招埅したす。



All Articles