构建和加载 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 资源,因此您必须为摄取插件配置新规则和操作指南。