Kubernetes Visibility Agent (旧称 CNO for Visibility) の拡張性とカスタマイズ <!-- /*NS Branding Styles*/ --> .ns-kb-css-body-editor-container { p { font-size: 12pt; font-family: Lato; color: var(--now-color--text-primary, #000000); } span { font-size: 12pt; font-family: Lato; color: var(--now-color--text-primary, #000000); } h2 { font-size: 24pt; font-family: Lato; color: var(--now-color--text-primary, black); } h3 { font-size: 18pt; font-family: Lato; color: var(--now-color--text-primary, black); } h4 { font-size: 14pt; font-family: Lato; color: var(--now-color--text-primary, black); } a { font-size: 12pt; font-family: Lato; color: var(--now-color--link-primary, #00718F); } a:hover { font-size: 12pt; color: var(--now-color--link-primary, #024F69); } a:target { font-size: 12pt; color: var(--now-color--link-primary, #032D42); } a:visited { font-size: 12pt; color: var(--now-color--link-primary, #00718f); } ul { font-size: 12pt; font-family: Lato; } li { font-size: 12pt; font-family: Lato; } img { display: ; max-width: ; width: ; height: ; } } .8 Table of Contents ゴール追加リソースのプル システムによってプルされるデフォルトの情報リソースのフィルタリングカスタム情報入力構成ファイルカスタム関係カスタム関連エントリ インスタンスからのリソース抽出の設定インスタンスに関する情報の処理インスタンスのリソースをフィルターで除外するリソース情報をオンデマンドで取得するためのスクリプト化された API ゴール この機能の目的は、お客様がKubernetes環境から追加のリソースと属性をデータベースに取り込み、ニーズに応じて処理できるようにすることです。さらに、インフォーマーによって報告されたリソースを操作およびフィルタリングする機能も提供します。このドキュメントでは、最初にシステムがプルできる情報について説明し、次に構成ファイルの具体的な例を示します。 追加リソースのプル ユーザーは、任意の Kubernetes リソースで宣言して CMDB にプルできます。リソース定義には、コマンド kubectl api-resources の出力に示されているように、APIVERSION と NAME が含まれます。 システムによってプルされるデフォルトの情報 デフォルトでは、次の情報がプルされます。 名前名前空間 (名前空間リソース用)Kubernetes UIDAPI バージョン種類ラベルと注釈 デフォルトでは、システムは cmdb_ci_kubernetes_component クラスに情報を格納します。 システムは、cmdb_ci_kubernetes_componentからクラスター CI への関係と参照を作成します。 リソースに名前空間が指定されている場合、システムは名前空間 CI への関係を作成します。 リソースを専用の新しいテーブルに配置する場合は、どちらがターゲットテーブルであるかを宣言できます。ターゲットテーブルはcmdb_ci_kubernetes_componentを拡張する必要があり、親クラスから識別ルールを継承する識別ルールが必要です。 リソースのフィルタリング ユーザーは、CMDB に取り込む特定の種類のリソースのサブセットについて宣言できます。たとえば、名前が「version」の ConfigMap リソースの取り込みを要求できます。 フィルタリングメカニズムは、すぐに利用可能なリソースにも、お客様によって定義された追加リソースにも適用できます。 基本フィルタリング フィルタリングは、値を検査する必要があるリソース JSON 内のフィールドの JSON パスと、この値に適用される正規表現を指定することによって行われます。このような条件を複数定義することが可能です。いずれかの条件が一致すると、リソースは CMDB に送信されます。例については、この記事の後半で説明します。 複雑なフィルタリング より複雑なフィルタリングが必要な場合は、 KB2347493を参照してください カスタム情報 ユーザーは、CNO がすぐに利用可能なリソースであるか、追加リソースであるかにかかわらず、特定のリソースから取得する追加情報を定義できます。 リソースごとに、任意の数のカスタムフィールドを定義できます。このようなフィールドごとに、フィールド名とリソース JSON 内の情報への JSON パス、およびその値の一部を抽出するためのオプションの正規表現を定義する必要があります。 デフォルトでは、cmdb_ci_kubernetes_componentの「属性」フィールドに情報が JSON 文字列のキー/値として格納されます。CMDB の関連テーブルでカスタムターゲットフィールド名を定義できます。 [attributes] フィールドの集計サイズが 65K バイト (データベース内のフィールドサイズ) を超える場合、データはインスタンスに送信されません。 入力構成ファイル Helm チャートには、次の 2 つのサブキーを持つ追加の値が含まれるようになりました。 additionalResources: resources: mappings: どちらのサブキー値も、次の構造の YAML ファイルからロードされます。 「resources」部分は YAML 配列で、各要素には apiGroups、apiVersion、およびリソースの配列が含まれています。例: - apiGroups: - '' apiVersions: - v1 resources: - configmaps - apiGroups: - config.openshift.io apiVersions: - v1 resources: - clusterversions この例では、CMDB v1/configmaps に取り込むようにシステムに要求します config.openshift.io/v1/clusterversions Helm チャートで提供される ClusterRole を使用する場合、システムはリソース リストをロール定義にプルし、動詞リスト、get および watch を追加します。 上記の例では、クラスター内の ConfigMap および ClusterVersion リソースごとに cmdb_ci_kubernetes_component CI が作成されます。 「マッピング」部分は YAML マップで、各キーはリソースを表し、サブキーはフィールド、オプションのターゲットクラス、およびフィルターを表します。基本的なフィルタリングを使用した例: v1/configmaps: targetClass: "u_cmdb_kubernetes_configmap" filter: - jsonPath: ".metadata.name" regex: ^version$ fields: - fieldExtractor: jsonPath: ".data.clusterFqdn" name: cluster_fqdn targetColumn: "u_cluster_fqdn" config.openshift.io/v1/clusterversions: fields: - fieldExtractor: jsonPath: ".status.desired.version" name: version この例についての説明は以下のとおりです。 システムは、名前が「version」のクラスター内のすべての ConfigMap リソースをカスタムテーブル u_cmdb_ci_kubernetes_configmap に取り込みます。ConfigMap からキー「clusterFqdn」を抽出し、その値を u_cluster_fqdn というフィールドに入力します また、ClusterVersionの種類のリソースも取得し、cmdb_ci_kubernetes_componentテーブルに配置します。値 status.desired.version を抽出し、「version」というキーの下の「attributes」列の JSON マップに配置します。 どちらの部分もオプションですが 「マッピング」部分にリストされている各リソースは、「リソース」部分にもリストされている必要があります。マッピング部分内では、すべての項目がオプションです。 「フィールド」内では、targetColumn はオプションです 各「fieldExtractor」内では、「regex」はオプションです 「filter」内、「regex」はオプションです informer バージョン 2.4.x から、ネガティブフィルタリングで宣言できるようになりました。つまり、フィルターに一致するリソースは CMDB に取り込まれません。次の例では、値「production」を持つラベル「environment」を持たないすべてのポッドを CMDB に取り込みます。 isNegativeFiltering フィールドはオプションで、デフォルト値は false です。 v1/pods: filter: - jsonPath: ".metadata.labels.environment" regex: "production" isNegativeCondition: true デフォルトでは、Kubernetes API サーバーから取り込まれたすべての情報がインフォーマーのメモリにロードされます。リソースが大きい場合は、保存されたオブジェクトから除外する必要があるリソースの部分の JSON パスを指定することで、メモリフットプリントを削減するオプションを提供します。 マッピング定義で、特定のオブジェクト定義の下にキー excludeFromResource を追加し、その下に除外された JSON パスのアレイを配置します。 例 : excludeFromResource: - ".report.components" helm コマンドの実行中に resources.yaml を設定するには、次のフラグを使用します。 --set-file additionalResources.resources=/path/to/resources.yaml helm コマンドの実行中に mappings.yaml を設定するには、次のフラグを使用します。 --set-file additionalResources.mappings=/path/to/mappings.yaml 注意:k8s_informer.yaml を使用している場合は、リソースとマッピング値を json 文字列として指定する必要があります。参考のために k8s_informer.yaml に例が用意されています。 カスタム関係 システムによって作成されたデフォルトの関係に加えて、追加リソースと Kubernetes クラスター内の他の CI との間の追加の関係を定義できます。システムでは、ユーザーが以下を定義する必要があります。 ターゲットリソースの種類に対する JSON パスとオプションの正規表現。ターゲットリソースの種類が一定の場合は、プレフィックス「const:」を使用します (例:「const:Pod」)ターゲットリソース UID への JSON パスとオプションの正規表現、またはターゲットリソースの名前空間と名前への JSON パスとオプションの正規表現関係タイプ (例:Contains::Contained by)関係の方向 (オプション) – true/false (デフォルトは true です)。True は、関係の親が追加のリソースであり、子が宣言された JSON パスによって識別される CI であることを意味します。 mappings.yaml の定義の例 UID によるルックアップ リソースコントローラーのリビジョンからその所有者への関係を作成したいと考えています。この場合、システムは UID を使用して所有者を検索します apps/v1/controllerrevisions: relations: - targetResourceUid: jsonPath: ".metadata.ownerReferences[:].uid" targetKind: jsonPath: ".metadata.ownerReferences[:].kind" relationType: Controller for::Controlled by relationDirection: true 名前空間と名前によるルックアップ ClusterRoleBinding から対応する ServiceAccount への関係を作成したいと考えています。ServiceAccount には名前空間が設定されているため、名前と名前空間への JSON パスを指定します さらに、ClusterRole との関係を作成したいと考えています。ClusterRole には名前空間が設定されていないため、名前の JSON パスのみを指定します rbac.authorization.k8s.io/v1/clusterrolebindings: relations: - targetKind: jsonPath: ".subjects[:].kind" targetResourceName: jsonPath: ".subjects[:].name" targetResourceNamespace: jsonPath: ".subjects[:].namespace" relationType: Used by::Uses relationDirection: false - targetKind: jsonPath: ".roleRef.kind" targetResourceName: jsonPath: ".roleRef.name" relationType: Used by::Uses relationDirection: false 重要な注意: この時点では、過去に作成されたが、宣言またはオブジェクトデータの変更により検出されなくなった関係は削除されません。 カスタム関連エントリ 関連エントリは、CMDB レコード (この場合はcmdb_ci_kubernetes_componentを拡張する CI) への参照を持つ非 CMDB テーブル内のレコードです。ユーザーは、これらのレコードを保持するカスタムテーブルを定義し、Kubernetes API サーバーからのデータを使用してテーブルに入力できます。 定義は mapping.yaml にあり、次のものが含まれます。 targetTable:関連レコードのテーブルの名前referenceField:親の cmdb_ci_kubernetes_component レコードへの参照を保持するフィールドの名前arrayJsonPath:親 Kubernetes リソースの情報を取得するときに Kubernetes API サーバーによって返されるペイロード内のアレイオブジェクトの JSON パス。フィールド:関連エントリに入力されるフィールドのアレイ。各フィールドには次のものが含まれます。 名前:分かりやすい名前targetColumn:ターゲットテーブルの列fieldExtractor: jsonPath:配列内の各要素からフィールドを抽出する方法regExp (オプション):正規表現を使用して元のフィールドから値を抽出します フィルター (オプション):フィールド名を正規表現にマップします。これが指定されている場合、システムは、値が正規表現と完全に一致する関連エントリのみを取り込みます カスタム関連テーブルは、関連エントリとして親クラス識別ルールに追加する必要があることに注意してください。 例 : rbac.authorization.k8s.io/v1/clusterroles: targetClass: cmdb_ci_kubernetes_cluster_role relatedEntries: - targetTable: u_cluster_role_rule referenceField: u_cluster_role arrayJsonPath: ".rules.*" fields: - name: api_groups targetColumn: u_api_groups fieldExtractor: jsonPath: ".apiGroups" - name: resources targetColumn: u_resources fieldExtractor: jsonPath: ".resources" - name: verbs targetColumn: u_verbs fieldExtractor: jsonPath: ".verbs" 関連エントリの一部のみを取得する必要がある場合は、フィルター条件を追加する必要があります。この例では、動詞の書き込みを含むルールのみを取り込むために、以下のフィルターを追加します。 rbac.authorization.k8s.io/v1/clusterroles: targetClass: cmdb_ci_kubernetes_cluster_role relatedEntries: - targetTable: u_cluster_role_rule referenceField: u_cluster_role arrayJsonPath: ".rules.*" filter: verbs: '.*(create|delete|update|patch).*' fields: - name: api_groups targetColumn: u_api_groups fieldExtractor: jsonPath: ".apiGroups" - name: resources targetColumn: u_resources fieldExtractor: jsonPath: ".resources" - name: verbs targetColumn: u_verbs fieldExtractor: jsonPath: ".verbs" インスタンスからのリソース抽出の設定 additionalResources.mappings パラメーターを介して利用可能なすべての抽出およびマッピングオプションは、インスタンスから構成することもできます。インスタンスでは、構成は informer パラメーターの [Additional Resources ConfigMap] 内で提供されます。 パラメーターには、次の構造を持つ JSON テキストが含まれている必要があります。 { "mappings": <JSON map of resources> } mappings.yaml を作成するときに上記で説明したのと同じ構造がここでも適用されますが、YAML の代わりに JSON テキストを指定する必要があります。 例: { "mappings": { "v1/configmaps": { "targetClass": "u_cmdb_kubernetes_configmap", "filter": [ { "jsonPath": ".metadata.name", "regex": "^version$" } ], "fields": [ { "fieldExtractor": { "jsonPath": ".data.clusterFqdn" }, "name": "cluster_fqdn", "targetColumn": "u_cluster_fqdn" } ] }, "config.openshift.io/v1/clusterversions": { "fields": [ { "fieldExtractor": { "jsonPath": ".status.desired.version" }, "name": "version" } ] } } } インスタンスに関する情報の処理 従来のビジネスルールに加えて、ServiceNow は IRE (識別および調整エンジン) の検査、操作、および後処理のためのフックを提供します。この機能は、パターンの前処理/後処理スクリプトメカニズムに似ています。 インフォーマーがペイロードをインスタンスに送信すると、いくつかの方法でペイロードを処理できます。 従来のビジネスルールを使用して、特定の変更にフックし、それに基づいて対処します。ただし、CI が挿入されても、関係はまだ存在せず、参照はまだ入力されていません。プリセンサースクリプトは、IRE ペイロードの入力を文字列として受け取り、それを調べて操作できます。スクリプトは、元のペイロードまたは操作されたペイロードのいずれかを返します。スクリプトはすべてのペイロードで動作するため、このオプションは非常に効率的であり、不要なデータベース呼び出しを回避する必要があるため、注意して使用する必要があります。このオプションを使用するには、[Pre Post Processing] に移動して [New] を押して、[Patterns] フィールドで [K8s Informer Place Holder Pattern for Pre Post Scripts] というパターンを選択します。次に、[When to execute] で [Pre sensor] を選択し、[Script] フィールドにスクリプトを記述します。ポストセンサースクリプトは、IRE 出力文字列を入力として受け取り、追加のアクションを実行できます。[Pre Post Processing] に移動して [New] を押して、[Patterns] フィールドで [K8s Informer Place Holder Pattern for Pre Post Scripts] というパターンを選択します。次に、[When to execute] で [Post sensor] を選択し、[Script] フィールドにスクリプトを記述します。 以下は、プリセンサースクリプトの例です。ClusterVersion CI からバージョン情報を選択し、クラスター CI のコメントフィールドに入力します。 var rtrn = {}; // Parsing json string to object var payloadObj = JSON.parse(payload); var cluster = payloadObj.items[0]; // Look for the ClusterVersion item var found = false; for (var i = 0; i<payloadObj.items.length; i++) { if (payloadObj.items[i].className != 'cmdb_ci_kubernetes_component') continue; var kind = payloadObj.items[i].values.kind; var attributes = payloadObj.items[i].values.attributes; if ('ClusterVersion' == kind && attributes) { var attrObj = JSON.parse(attributes); if (attrObj && attrObj.version) { cluster.values['comments'] = attrObj.version; found = true; } break; } } // Clearing the payload string to save memory var message = 'Version not found'; if (found) { payload = JSON.stringify(payloadObj); message = 'Version found'; } rtrn = { 'status': { 'message': message, 'isSuccess': true }, 'patternId': patternId, 'payload': payload }; インスタンスのリソースをフィルターで除外する プリセンサースクリプトを使用して、インフォーマーによって送信されたペイロードから不要なリソースを削除できます。以下のスクリプトは、IRE (識別および調整エンジン) を受け取り、指定されたクラスのアイテムを関連する関係および参照とともに削除します。関数 removeClass は、ペイロードオブジェクトとクラス名を受け取り、変更されたペイロードを返します。 var K8sInformerIREHelper = Class.create(); K8sInformerIREHelper.prototype = { initialize: function() { }, /** * Input: IRE payload object * class name of items that should be removed * Output: IRE object */ removeClass: function(payloadObj, sysClassName) { var mapOldToNew = {}; var removedItemsCount = 0; var clusterName = ''; if (payloadObj.items.length != 0) clusterName = payloadObj.items[0].name; // Iterate the items and move to the result only items that do not have the defined class var newPayload = { items: [] , relations: [] , referenceItems: [] }; for (var itemCount in payloadObj.items) { var item = payloadObj.items[itemCount]; if (item.className != sysClassName) { newPayload.items.push(item); mapOldToNew[itemCount + ''] = itemCount - removedItemsCount; } else { removedItemsCount++; } } // Iterate the relations and move to the result only relations whose both parent and child were moved to the for (var relCount in payloadObj.relations) { var relation = payloadObj.relations[relCount]; var newParent = mapOldToNew[relation.parent + '']; var newChild = mapOldToNew[relation.child + '']; if (this.isOK(newParent) && this.isOK(newChild)) { var newRelation = { parent: newParent, child: newChild, type: relation.type}; newPayload.relations.push(newRelation); } } // Iterate the references and move to the result references whose both referenced and referencedBy were moved to the for (var refCount in payloadObj.referenceItems) { var ref = payloadObj.referenceItems[refCount]; var newReferenced = mapOldToNew[ref.referenced + '']; var newReferencedBy = mapOldToNew[ref.referencedBy + '']; if (this.isOK(newReferenced) && this.isOK(newReferencedBy)) { var newRef = { referenced: newReferenced, referencedBy:newReferencedBy, referenceField: ref.referenceField}; newPayload.referenceItems.push(newRef); } } gs.info('K8sInformerIREHelper: ' + removedItemsCount + ' items removed from IRE payload. Cluster: ' + clusterName); return newPayload; }, isOK: function(index) { var indexStr = index + ''; return global.JSUtil.notNil(indexStr) && indexStr != 'undefined'; }, type: 'K8sInformerIREHelper' }; このスクリプトは、プリセンサースクリプトから呼び出すことができます。以下の例では、スクリプトを呼び出して、ペイロードから発生したすべてのcmdb_ci_linux_serverを削除します。 var rtrn = {}; // parsing the json string to a json object var payloadObj = JSON.parse(payload); // Clearing payload string to save memory payload = null; payloadObj = new K8sInformerIREHelper().removeClass(payloadObj, 'cmdb_ci_linux_server'); rtrn = { 'status': { 'message': 'Informer IRE was processed', 'isSuccess' :true }, 'patternId': patternId, 'payload': JSON.stringify(payloadObj) }; リソース情報をオンデマンドで取得するためのスクリプト化された API Kubernetes Visibility Agent が展開されている場合、スクリプト化された API を使用して、特定の Kubernetes リソースに関する情報を取得する要求をインフォーマーに送信できます。KB1648272を参照してください。