构建和加载 BYOI 自定义插件映像
总结
要使用自带摄取 (BYOI) 自定义插件将数据发送到 Paragon Automation,您必须对输入插件进行编码,并使用 shell 脚本和流程文件创建插件摄取映像文件。Paragon Automation 会在 BYOI 摄取和设备组(映射到摄取)的配置发生变化时执行 shell 脚本。引入映像还包含引入容器的 Kubernetes YAML 文件。Kubernetes YAML 文件包含使 Kubernetes 引擎能够启动和停止 BYOI 插件摄取服务的配置。
构建和加载 BYOI 自定义插件映像的工作流程如下:
-
创建流程文件以将数据写入 Paragon Automation 数据库,并创建用于配置更新的 shell 脚本。
- 有关解析 JSON 配置文件中的属性以将数据发送到 Paragon Insights 数据库的示例 Python 脚本(流程文件),请参阅为 插件映像创建流程文件 。
- 有关示例 shell 脚本,请参阅 为配置更新创建 shell 脚本 。
-
标记图像文件并将图像导出为压缩的 tar 文件。有关标记和导出映像文件的命令,请参阅 标记和导出 BYOI 自定义插件映像 。
-
修改 Kubernetes Jinja 模板,为 Kubernetes 容器 Pod 创建 YAML 文件。容器 Pod 是 Paragon Automation 中部署 BYOI 摄取的位置。有关示例 Kubernetes Jinja 模板文件,请参阅 配置 Kubernetes YAML 文件 。
-
(可选)如果您希望外部应用程序可以访问 BYOI 插件,请分配不同的 IP 地址。请参阅 将 虚拟 IP 地址分配给插件 ,了解 Kubernetes Jinja 模板,您可以在其中为 BYOI 插件分配自定义虚拟 IP 地址。
-
将压缩的 tar 文件和 Kubernetes YAML 文件加载到 Paragon Automation 主节点。请参阅 加载 BYOI 自定义插件以加载 BYOI 插件映像文件和 Kubernetes YAML 文件。
为插件映像创建过程文件
您可以创建一个进程文件,例如以下示例 Python 文件,并将其包含在 BYOI 插件映像中。在 Kubernetes 容器中运行映像时,Paragon Automation 会执行流程文件。以下示例 Python 文件使用 表 1 中描述的属性将度量 (topic/rule/sensor_name/byoi) 的键(示例文件中介于 0 和 9 之间的随机整数)发送到数据库。
import requests import time import random import os import json # read tand_host, tand_port from env vars tand_host = os.environ.get('TAND_HOST', 'localhost') tand_port = os.environ.get('TAND_PORT', '3000') # read device, plugin, rule related attributes from config json with open('/etc/byoi/config.json', 'r') as f: config_json = json.load(f) # input, device, sensor as lists. modify index as needed. Using 0 for all idxes database = config_json['hbin']['inputs'][0]['plugin']['config']['device'][0]['healthbot-storage']['database'] measurement = config_json['hbin']['inputs'][0]['plugin']['config']['device'][0]['sensor'][0]['measurement'] # Construct post request and data url = 'http://{}:{}/write?db={}'. \ format(tand_host, tand_port, database) data = '{} {} {}' metric = 'key' while True: fields = '{}={}'.format(metric, random.randint(0,9)) timestamp = int(time.time()) * 1000000000 x = requests.post(url, data=data.format(measurement, fields, timestamp)) time.sleep(10)
在 Python 示例文件中,使用以下 URL 格式将数据发送到 Paragon Automation。
url = 'http://{}:{}/write?db={}'. \
format(tand_host, tand_port, database)
数据库属性的值必须遵循语法 database-name:device-group-name:。device-name
线路协议(发布正文)包含以下格式的字符串。
data = '{} {} {}'.format(measurement, fields, timestamp)
创建自定义 BYOI 插件的实例时,JavaScript 对象表示法 (JSON) 配置文件将作为卷附加到 BYOI 摄取实例的 Kubernetes 容器中。JSON 配置文件包含接收数据的后端服务的设备、设备组、传感器路径、主机名和端口等信息。您可以使用 /etc/byoi/config.json 文件完成所有可用属性的 JSON 配置。
表 1 列出了 JSON 配置文件中的几个关键属性。
属性 | 说明 | 如何访问属性 |
---|---|---|
tand_host | 插件向其发送摄取数据的后端服务的主机名。 |
环境变量 $TAND_HOST |
tand_port | 插件向其发送数据的后端服务的端口号。 |
环境变量 $TAND_PORT |
数据库 | 存储引入数据的数据库的名称。 此属性的值因每个 Paragon Automation 节点而异。 |
config_json['hbin'] ['inputs']['plugin']['config'] ['device'][idx]['healthbot-storage'] ['database'] |
测量 | 在线协议数据库中的测量。例如,topic/rule/sensor_name/byoi
注意:
sensor_name的值因传感器而异。 请参阅 https://docs.influxdata.com/influxdb 以了解有关测量的更多信息。 |
config_json['hbin']['inputs']['plugin'] ['config']['device'][idx]['sensor'] [sensor_idx]['measurement'] |
领域 | 度量值对,用逗号分隔,不带空格。 例如, cpu_usage=50,memory_utilization=12. |
没有 |
时间 戳 | Unix 纪元时间戳(以纳秒为单位)。 | 没有 |
密码 | 接收流数据的设备的编码密码。 请参阅 解码设备密码以解码设备密码。 |
config_json['hbin'] ['inputs']['plugin']['config'] ['device'][idx]['authentication'] ['password']['password'] |
解码设备密码
JSON 配置文件可以包含编码的敏感信息,例如流式传输数据的设备的密码。
要对数据进行解码,您可以使用插件容器内的 API api-server:9000/api/v2/junos-decode
发起 POST 调用,并将编码的数据放在 post 正文中。
以下示例 POST 调用解码 JSON 配置文件中存在的编码密码。
curl -X POST -L api-server:9000/api/v2/junos-decode -H "Content-Type: application/json" -d '{"data": "$ABC123"}' -v
创建用于配置更新的 shell 脚本
当 BYOI 摄取映像配置或 Paragon Automation 设备组配置发生更改时,JSON 配置文件将会更新。当配置发生更改时,Paragon Automation 会执行位于 /jfit_scripts/jfit_reconfigure.sh 的 shell 脚本,向 BYOI 发出有关配置更新的信号。构建摄取插件映像时,必须将 shell 脚本命名为 jfit_reconfigure.sh 并将脚本复制到 /jfit_scripts/ 文件夹。
在 shell 脚本中,您可以向主进程发送 SIGHUP 信号,或者干脆终止旧进程并启动新进程。以下示例 shell 脚本将 SIGHUP 信号发送到主插件进程:
pid=`ps -ef | grep ".*main.py" | grep -v 'grep' | awk '{ print $1}'` && \ kill -s HUP $pid
标记并导出 BYOI 自定义插件映像
构建自定义插件后,必须标记插件映像并将其导出为 tar 文件。您可以按格式 healthbot_plugin_name:your_version
标记插件图像。必须使用以下命令将插件映像导出为压缩的 tar 文件:
docker save tag -o healthbot_plugin_name.tar.gz
配置 Kubernetes YAML 文件
Kubernetes Jinja 模板文件具有部署 Kubernetes 资源(如用于 BYOI 摄取 Pod 的容器)所需的基本配置。
您可以使用以下示例 Kubernetes Jinja 模板创建 YAML 文件。您必须替换:
-
命令和参数、<ADD_COMMAND> 和 <ADD_ARGUMENTS> 的占位符。
例如,将 <ADD_COMMAND> 替换为 python3,将 <>ADD_ARGUMENTS替换为 Python 文件的名称。
-
<PLUGIN_NAME_CAPITALIZED>插件名称以大写字母表示。
您可以在模板中的“容器”部分添加其他属性,例如卷或 Kubernetes 机密。修改示例 Kubernetes Jinja 模板后,将文件名 healthbot_<plugin_name>.yml.j2 更改为并保存。
以下代码是一个示例 Kubernetes Jinja 模板。
set sg_name = '-' + env['SUBGROUP'] -%} {%- set sg_dir = '_' + env['SUBGROUP'] -%} {% if env['SUBGROUP'] == '' -%} {%- set sg_name = '' -%} {%- set sg_dir = '' -%} {%- endif %} kind: ConfigMap apiVersion: v1 metadata: namespace: {{ env['NAMESPACE'] }} name: {{ env['GROUP_TYPE'] }}-{{ env['GROUP_NAME_VALID'] }} {{ sg_name }}-{{ env['CUSTOM_PLUGIN_NAME'] }} labels: app: {{ env['CUSTOM_PLUGIN_NAME'] }} group-name: {{ env['GROUP_NAME'] }} group-type: {{ env['GROUP_TYPE'] }} subgroup: {{ env['SUBGROUP'] }} data: TAND_HOST: '{{ env['GROUP_TYPE_SHORT'] }}-{{ env['GROUP_NAME_VALID'] }} {{ sg_name }}-{{ env['CUSTOM_PLUGIN_NAME'] }}-terminus' TAND_PORT: '{{env['tand:TAND_PORT']}}' PUBLISHD_HOST: '{{env['publishd:PUBLISHD_HOST']}}' PUBLISHD_PORT: '{{env['publishd:PUBLISHD_PORT']}}' CONFIG_MANAGER_PORT: {{env['configmanager:CONFIG_MANAGER_PORT']}} CHANNEL: '{{ env['GROUP_TYPE'] }}-{{ env['GROUP_NAME'] }}' GODEBUG: 'madvdontneed=1' IAM_SERVER: '{{ env['iam:IAM_SERVER'] }}' IAM_SERVER_PORT: '{{ env['iam:IAM_SERVER_PORT'] }}' IAM_SERVER_PROTOCOL: '{{ env['iam:IAM_SERVER_PROTOCOL'] }}' IAM_NAMESPACE: '{{ env['iam:IAM_NAMESPACE'] }}' --- apiVersion: apps/v1 kind: Deployment metadata: namespace: {{ env['NAMESPACE'] }} name: {{ env['GROUP_TYPE'] }}-{{ env['GROUP_NAME_VALID'] }} {{ sg_name }}-{{ env['CUSTOM_PLUGIN_NAME'] }} labels: app: {{ env['CUSTOM_PLUGIN_NAME'] }} group-name: {{ env['GROUP_NAME'] }} group-type: {{ env['GROUP_TYPE'] }} subgroup: {{ env['SUBGROUP'] }} spec: replicas: 1 selector: matchLabels: app: {{ env['CUSTOM_PLUGIN_NAME'] }} group-name: {{ env['GROUP_NAME'] }} group-type: {{ env['GROUP_TYPE'] }} subgroup: {{ env['SUBGROUP'] }} template: metadata: namespace: {{ env['NAMESPACE'] }} labels: app: {{ env['CUSTOM_PLUGIN_NAME'] }} group-name: {{ env['GROUP_NAME'] }} group-type: {{ env['GROUP_TYPE'] }} subgroup: {{ env['SUBGROUP'] }} spec: tolerations: - key: "node.kubernetes.io/not-ready" operator: "Exists" effect: "NoExecute" tolerationSeconds: 180 - key: "node.kubernetes.io/unreachable" operator: "Exists" effect: "NoExecute" tolerationSeconds: 180 initContainers: - name: sync image: {{ env['REGISTRY'] }}/{{ env['HEALTHBOT_INIT_CONTAINER_IMAGE'] }}: {{ env['HEALTHBOT_INIT_CONTAINER_TAG'] }} imagePullPolicy: Always command: ["python3"] args: ["/root/sync_files.py", "-c", "{{ env['GROUP_TYPE'] }}- {{ env['GROUP_NAME'] }}"] env: - name: NODE_IP valueFrom: fieldRef: fieldPath: status.hostIP envFrom: - configMapRef: name: {{ env['GROUP_TYPE'] }}-{{ env['GROUP_NAME_VALID'] }} {{ sg_name }}-{{ env['CUSTOM_PLUGIN_NAME'] }} containers: - name: {{ env['CUSTOM_PLUGIN_NAME'] }} image: {{ env['REGISTRY'] }}/{{ env['HEALTHBOT_<<variable >PLUGIN_NAME_CAPITALIZED</variable>>_IMAGE’] }}: {{ env[’HEALTHBOT_<<variable>PLUGIN_NAME_CAPITALIZED</variable >>_TAG’] }} imagePullPolicy: Always #example #command: [“python3”] #args: [“/main.py”] command: [<<variable>ADD_COMMAND</variable>>] args: [<<variable>ADD_ARGUMENTS</variable>>] env: - name: NODE_IP valueFrom: fieldRef: fieldPath: status.hostIP envFrom: - configMapRef: name: {{ env['GROUP_TYPE'] }}-{{ env['GROUP_NAME_VALID'] }} {{ sg_name }}-{{ env['CUSTOM_PLUGIN_NAME'] }} volumeMounts: - name: default mountPath: /etc/byoi - name: data-model mountPath: /etc/ml volumes: - name: default hostPath: type: DirectoryOrCreate path: {{ env['JFIT_OUTPUT_PATH'] }}/{{ env['GROUP_NAME'] }} {{ sg_dir }}/custom_{{ env['CUSTOM_PLUGIN_NAME'] }}_collector - name: data-model hostPath: type: DirectoryOrCreate path: {{ env['JFIT_ETC_PATH'] }}/data/models/{{ env['GROUP_NAME'] }} imagePullSecrets: - name: registry-secret
为插件分配虚拟 IP 地址
要从外部网络访问自定义 BYOI 插件,需要将该插件公开为 Kubernetes 负载均衡器服务。这是一个可选配置。默认情况下,该插件使用 Paragon Automation 网关的虚拟 IP 地址。您还可以分配自定义虚拟 IP 地址,并将以下模板添加到 配置 Kubernetes YAML 文件中的 Kubernetes Jinja 模板文件的末尾。
确保将给定模板中的 <>PLUGIN_PORT 和 <PROTOCOL> 替换为所需的值,例如协议 HTTP 的端口 80。有关支持的协议,请参阅 https://kubernetes.io/docs/concepts/services-networking/service/#protocol-support 。
要为 BYOI 插件配置自定义虚拟 IP 地址,请将 <custom_load_balancer_ip> 替换为虚拟 IP 地址。
--- {% set service_values = env.get('SERVICE_VALUES', {}) -%} {%- set global_annotations = service_values.get('annotations') -%} {%- set global_load_balancer_ip = service_values.get('loadBalancerIP') -%} {%- set custom_annotations = service_values.get(svc_name, {}).get('annotations') -%} {%- set custom_load_balancer_ip = service_values.get(svc_name, {}).get('loadBalancerIP') -%} {%- set service_type = service_values.get(svc_name, {}).get('type', 'LoadBalancer') -%} {%- for ip in env['LOAD_BALANCER_IPS'] %} apiVersion: v1 kind: Service metadata: namespace: {{ env['NAMESPACE'] }} {%- if loop.index0 == 0 %} name: {{ env['GROUP_TYPE_SHORT'] }}-{{ env['GROUP_NAME_VALID'] }} {{ sg_name }}-{{ env['CUSTOM_PLUGIN_NAME'] }} {%- else %} name: {{ env['GROUP_TYPE_SHORT'] }}-{{ env['GROUP_NAME_VALID'] }} {{ sg_name }}-{{ env['CUSTOM_PLUGIN_NAME'] }}-{{loop.index0}} {%- endif %} labels: app: {{ env['CUSTOM_PLUGIN_NAME'] }} group-name: {{ env['GROUP_NAME'] }} group-type: {{ env['GROUP_TYPE'] }} subgroup: '{{ env['SUBGROUP'] }}' annotations: {% if env.get('LOADBALANCER_PROVIDER', 'User') == 'HealthBot' %} metallb.universe.tf/allow-shared-ip: healthbot-{{loop.index0}} {% elif custom_annotations %} {{ custom_annotations }} {% elif global_annotations %} {{ global_annotations }} {% else %} {} {% endif %} spec: type: LoadBalancer {%- if env.get('LOADBALANCER_PROVIDER', 'User') == 'HealthBot' %} loadBalancerIP: {{ ip }} {%- elif custom_load_balancer_ip %} loadBalancerIP: {{ <custom_load_balancer_ip> }} {%- elif global_load_balancer_ip %} loadBalancerIP: {{ global_load_balancer_ip }} {%- endif %} selector: app: {{ env['CUSTOM_PLUGIN_NAME'] }} group-name: {{ env['GROUP_NAME'] }} group-type: {{ env['GROUP_TYPE'] }} subgroup: '{{ env['SUBGROUP'] }}' ports: - name: port port: <PLUGIN_PORT> protocol:<PROTOCOL> {% endfor %}
配置端口和协议后,外部应用程序可以通过 与 <gateway_IP>:<PLUGIN_PORT>
自定义 BYOI 插件通信。如果为插件配置了定制虚拟 IP 以充当负载平衡器服务,那么外部应用程序可以通过 与 <custom_load_balancer_ip>:<PLUGIN_PORT>
插件通信。
用于 <0.0.0.0>:<PLUGIN_PORT>
从 Kubernetes 主机连接到插件内部运行的服务器。
您可以在 ports 部分下的给定 Kubernetes Jinja 模板中为不同的应用程序配置不同的端口。
如果在 Kubernetes Jinja 模板中配置不同的端口号,则必须在各自的应用程序中对端口号和相应的端口名进行硬编码。
加载 BYOI 自定义插件
将 BYOI 自定义插件映像 tar 文件和修改后的 Kubernetes YAML 文件挂载到 Paragon Automation 主节点,并在 Paragon Automation 管理 CLI 中加载插件。
使用以下命令挂载 BYOI 插件映像(压缩的 tar 文件):
export HB_EXTRA_MOUNT1=/path/to/healthbot_plugin_name.tar.gz
使用以下命令挂载 Kubernetes YAML 文件:
export HB_EXTRA_MOUNT2=/path/to/healthbot_plugin_name.yml.j2
使用以下命令加载插件映像和 Kubernetes YAML 文件
sudo -E healthbot load-plugin -i $HB_EXTRA_MOUNT1 -c $HB_EXTRA_MOUNT2
当插件成功加载时,您将看到一条确认消息。
-
(可选) > BYO 引入插件页面选择配置>数据引入>设置 “,然后在自定义 插件 选项卡中查看自定义插件。
加载插件后,在自带摄取页面中创建自定义 BYOI 插件的实例。由于自定义插件不使用默认的 Paragon Automation 资源,因此您必须为摄取插件配置新规则和操作指南。