本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。
设置与 Docker 的开源集成 (Linux)
为了简化部署流程,你可以使用 Docker 在 Linux 环境中设置 Node-RED®、InfluxDB® 和 Grafana®。此方法使用预先配置的容器,可以快速部署并更轻松地管理组件。
Docker 安装前提条件
在开始之前,请验证是否具备以下条件:
-
支持 MQTT 的 V3 网关。有关更多信息,请参阅 支持 MQTT 的边缘版 V3 网关 AWS IoT SiteWise。
-
Docker Compose 插件。有关安装步骤,请参阅 Docker 手册文档中的安装Docker Compose插件
。
部署服务
此部署在同一台主机上运行 SiteWise Edge、InfluxDB、Node-RED 和 Grafana。
设置环境
-
获得根访问权限:
sudo -i
-
创建.env 文件或导出以下环境变量:
export INFLUXDB_PASSWORD=
your-secure-influxdb-password
export INFLUXDB_TOKEN=your-secure-influxdb-token
export GRAFANA_PASSWORD=your-secure-grafana-password
配置 Docker 网络
-
使用名称创建桥接网络
SiteWiseEdgeNodeRedDemoNetwork
。docker network create --driver=bridge SiteWiseEdgeNodeRedDemoNetwork
准备Docker Compose文件
将以下 YAML 文件的内容复制到您的 SiteWise Edge 网关设备。
services: influxdb: image: influxdb:latest container_name: influxdb ports: - "127.0.0.1:8086:8086" volumes: - influxdb-storage:/.influxdbv2 environment: - DOCKER_INFLUXDB_INIT_MODE=setup - DOCKER_INFLUXDB_INIT_USERNAME=admin - DOCKER_INFLUXDB_INIT_PASSWORD=${INFLUXDB_PASSWORD} - DOCKER_INFLUXDB_INIT_ORG=iot-sitewise-edge - DOCKER_INFLUXDB_INIT_BUCKET=WindFarmData - DOCKER_INFLUXDB_INIT_RETENTION=0 - DOCKER_INFLUXDB_INIT_ADMIN_TOKEN=${INFLUXDB_TOKEN} networks: - SiteWiseEdgeNodeRedDemoNetwork restart: unless-stopped grafana: image: grafana/grafana:latest container_name: grafana ports: - "127.0.0.1:3000:3000" volumes: - grafana-storage:/var/lib/grafana - ./grafana/provisioning:/etc/grafana/provisioning environment: - GF_SECURITY_ADMIN_USER=admin - GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD} - GF_INSTALL_PLUGINS=grafana-clock-panel,grafana-simple-json-datasource - GF_PATHS_PROVISIONING=/etc/grafana/provisioning - GF_PATHS_CONFIG=/etc/grafana/grafana.ini - GF_LOG_LEVEL=info configs: - source: grafana_datasource target: /etc/grafana/provisioning/datasources/influxdb.yaml - source: grafana_preload_dashboard_config target: /etc/grafana/provisioning/dashboards/dashboard.yml - source: grafana_preload_dashboard target: /etc/grafana/provisioning/dashboards/demo_dashboard.json depends_on: - influxdb networks: - SiteWiseEdgeNodeRedDemoNetwork restart: unless-stopped nodered: build: context: . dockerfile_inline: | FROM nodered/node-red:latest RUN npm install node-red-contrib-influxdb container_name: nodered ports: - "127.0.0.1:1880:1880" volumes: - node_red_data:/data environment: - NODE_RED_ENABLE_SAFE_MODE=false - NODE_RED_ENABLE_PALETTE_EDIT=true - NODE_RED_AUTO_INSTALL_MODULES=true configs: - source: nodered_flows target: /data/flows.json - source: nodered_settings target: /data/settings.js - source: nodered_flows_cred target: /data/flows_cred.json depends_on: - influxdb networks: - SiteWiseEdgeNodeRedDemoNetwork restart: unless-stopped volumes: influxdb-storage: grafana-storage: node_red_data: networks: SiteWiseEdgeNodeRedDemoNetwork: external: true configs: grafana_datasource: content: | apiVersion: 1 datasources: - name: windfarm-demo type: influxdb access: proxy url: http://influxdb:8086 jsonData: version: Flux organization: iot-sitewise-edge defaultBucket: WindFarmData tlsSkipVerify: true secureJsonData: token: ${INFLUXDB_TOKEN} editable: false grafana_preload_dashboard_config: content: | apiVersion: 1 providers: - name: "Dashboard provider" orgId: 1 type: file options: path: /etc/grafana/provisioning/dashboards grafana_preload_dashboard: content: | { "annotations": { "list": [ { "builtIn": 1, "datasource": { "type": "grafana", "uid": "-- Grafana --" }, "enable": true, "hide": true, "iconColor": "rgba(0, 211, 255, 1)", "name": "Annotations & Alerts", "type": "dashboard" } ] }, "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, "id": 1, "links": [], "panels": [ { "datasource": { "type": "influxdb", "uid": "PEB0DCBF338B3CEB2" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green" }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 0 }, "id": 1, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.6.0", "targets": [ { "datasource": { "type": "influxdb", "uid": "PEB0DCBF338B3CEB2" }, "query": "from(bucket: \"WindFarmData\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"TurbineData\")\n |> filter(fn: (r) => r[\"_field\"] == \"value\")\n |> filter(fn: (r) => r[\"name\"] == \"/Renton/WindFarm/Turbine/WindSpeed\")\n |> filter(fn: (r) => r[\"quality\"] == \"GOOD\")\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\n |> yield(name: \"mean\")", "refId": "A" } ], "title": "Wind Speed", "type": "timeseries" } ], "preload": false, "schemaVersion": 41, "tags": [], "templating": { "list": [] }, "time": { "from": "now-6h", "to": "now" }, "timepicker": {}, "timezone": "browser", "title": "Demo Dashboard", "uid": "eejtureqjo9a8c", "version": 2 } nodered_flows: content: | [ { "id": "95fce448fdd43b47", "type": "tab", "label": "Demo Flow", "disabled": false, "info": "" }, { "id": "5f63740b66af3386", "type": "mqtt out", "z": "95fce448fdd43b47", "name": "Publish to MQTT broker", "topic": "/Renton/WindFarm/Turbine/WindSpeed", "qos": "1", "retain": "", "respTopic": "", "contentType": "", "userProps": "", "correl": "", "expiry": "", "broker": "5744207557fa19be", "x": 830, "y": 200, "wires": [] }, { "id": "8f2eb590d596679b", "type": "function", "z": "95fce448fdd43b47", "name": "Translate to SiteWise payload", "func": "let input = msg.payload;\nlet output = {};\n\noutput[\"propertyAlias\"] = input.name;\n\nlet propertyVal = {}\n\nlet timeInSeconds = Math.floor(input.timestamp / 1000);\nlet offsetInNanos = (input.timestamp % 1000) * 1000000;\n\npropertyVal[\"timestamp\"] = {\n \"timeInSeconds\": timeInSeconds,\n \"offsetInNanos\": offsetInNanos,\n};\n\npropertyVal[\"quality\"] = input.quality\n\nlet typeNameConverter = {\n \"number\": (x) => Number.isInteger(x) ? \"integerValue\" : \"doubleValue\",\n \"boolean\": (x) => \"booleanValue\",\n \"string\": (x) => \"stringValue\", \n}\nlet typeName = typeNameConverter[typeof input.value](input.value)\npropertyVal[\"value\"] = {}\npropertyVal[\"value\"][typeName] = input.value;\n\noutput[\"propertyValues\"] = [propertyVal]\n\nreturn {\n payload: JSON.stringify(output)\n};", "outputs": 1, "timeout": "", "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 530, "y": 200, "wires": [ [ "5f63740b66af3386" ] ] }, { "id": "4b78cbdea5e3258c", "type": "inject", "z": "95fce448fdd43b47", "name": "Turbine Simulator", "props": [ { "p": "payload.timestamp", "v": "", "vt": "date" }, { "p": "payload.quality", "v": "GOOD", "vt": "str" }, { "p": "payload.value", "v": "$$random()", "vt": "jsonata" }, { "p": "payload.name", "v": "/Renton/WindFarm/Turbine/WindSpeed", "vt": "str" } ], "repeat": "1", "crontab": "", "once": false, "onceDelay": "", "topic": "", "x": 270, "y": 200, "wires": [ [ "8f2eb590d596679b" ] ] }, { "id": "b658bf337ea2e316", "type": "influxdb out", "z": "95fce448fdd43b47", "influxdb": "2f1c38495035d2e4", "name": "Store data in InfluxDB", "measurement": "TurbineData", "precision": "", "retentionPolicy": "", "database": "", "retentionPolicyV18Flux": "", "org": "iot-sitewise-edge", "bucket": "WindFarmData", "x": 840, "y": 340, "wires": [] }, { "id": "9432d39af35b202f", "type": "function", "z": "95fce448fdd43b47", "name": "Translate to InfluxDB payload", "func": "let data = msg.payload;\n\nlet timeInSeconds = data.propertyValues[0].timestamp.timeInSeconds;\nlet offsetInNanos = data.propertyValues[0].timestamp.offsetInNanos;\nlet timestampInMilliseconds = (timeInSeconds * 1000) + (offsetInNanos / 1000000);\n\nmsg.payload = [\n {\n \"timestamp(milliseconds_since_epoch)\": timestampInMilliseconds,\n \"value\": data.propertyValues[0].value.doubleValue\n },\n {\n \"name\": data.propertyAlias,\n \"quality\": data.propertyValues[0].quality\n }\n]\n\nreturn msg", "outputs": 1, "timeout": "", "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 560, "y": 340, "wires": [ [ "b658bf337ea2e316" ] ] }, { "id": "b689403d2c80816b", "type": "mqtt in", "z": "95fce448fdd43b47", "name": "Subscribe to MQTT broker", "topic": "/Renton/WindFarm/Turbine/WindSpeed", "qos": "1", "datatype": "auto-detect", "broker": "5744207557fa19be", "nl": false, "rap": true, "rh": 0, "inputs": 0, "x": 290, "y": 340, "wires": [ [ "9432d39af35b202f" ] ] }, { "id": "4f59bed8e829fc35", "type": "comment", "z": "95fce448fdd43b47", "name": "Data Publish Flow", "info": "dfgh", "x": 270, "y": 160, "wires": [] }, { "id": "b218c7fc58c8b6e7", "type": "comment", "z": "95fce448fdd43b47", "name": "Data Retention flow", "info": "", "x": 270, "y": 300, "wires": [] }, { "id": "5744207557fa19be", "type": "mqtt-broker", "name": "emqx", "broker": "emqx", "port": "1883", "clientid": "", "autoConnect": true, "usetls": false, "protocolVersion": "5", "keepalive": 15, "cleansession": true, "autoUnsubscribe": true, "birthTopic": "", "birthQos": "0", "birthPayload": "", "birthMsg": {}, "closeTopic": "", "closePayload": "", "closeMsg": {}, "willTopic": "", "willQos": "0", "willPayload": "", "willMsg": {}, "userProps": "", "sessionExpiry": "" }, { "id": "2f1c38495035d2e4", "type": "influxdb", "hostname": "influxdb", "port": 8086, "protocol": "http", "database": "", "name": "InfluxDB", "usetls": false, "tls": "", "influxdbVersion": "2.0", "url": "http://influxdb:8086", "timeout": "", "rejectUnauthorized": false } ] nodered_flows_cred: content: | { "2f1c38495035d2e4": { "token": "${INFLUXDB_TOKEN}" } } nodered_settings: content: | module.exports = { flowFile: 'flows.json', credentialSecret: false, adminAuth: null, editorTheme: { projects: { enabled: false } } }
更新边 SiteWise 缘部署
-
导航到 AWS IoT 控制台
-
在 “管理” 部分下的左侧导航菜单中选择 Greengrass 设备,然后选择 “核心设备”。
-
选择连接到 SiteWise Edge 网关的核心设备。
-
选择 “部署” 选项卡,然后选择 “部署 ID” 值。
-
选择 “操作”,然后选择 “修订”。
-
阅读弹出消息,然后选择 “修订部署”。
-
在步骤 2-选择组件中,选择以下组件,然后选择下一步。
-
aws.greengrass.clientdevices.mqtt.EMQX
-
aws.iot.SiteWiseEdgePublisher
-
-
在步骤 3-配置组件中,选择
aws.greengrass.clientdevices.mqtt.EMQX
组件值并添加以下网络配置:{ "emqxConfig": { "authorization": { "no_match": "allow" }, "listeners": { "tcp": { "default": { "enabled": true, "enable_authn": false } } } }, "authMode": "bypass", "dockerOptions": "-p 127.0.0.1:1883:1883 --network=SiteWiseEdgeNodeRedDemoNetwork", "requiresPrivilege": "true" }
-
选择下一步。
-
在步骤 4-配置高级设置中,选择下一步。
-
选择部署
启动服务
-
使用 Docker Compose 文件启动服务。在包含该
compose.yaml
文件的目录下运行以下命令。docker compose up -d
-
创建 SSH 隧道以访问服务:
ssh -i
path_to_your_ssh_key
-L 1880:127.0.0.1:1880 -L 3000:127.0.0.1:3000 -L 8086:127.0.0.1:8086username
@gateway_ip_address
此部署在中创建了以下服务SiteWiseEdgeNodeRedDemoNetwork network
:
- InfluxDB v2(端口 8086)
-
包括预先配置的组织 (iot-sitewise-edge)、 WindFarmData InfluxDB 存储桶和管理员凭证
- Node-red(端口 1880)
-
包括 InfluxDB 节点和用于集成的预配置流程 AWS IoT SiteWise
- Grafana(端口 3000)
-
包括管理员用户、InfluxDB 数据源和监控控制面板
访问服务
部署后,使用以下内容 URLs 和凭据访问服务:
注意
您可以从主机或网关计算机访问每项服务。
服务 | URL | 凭证 |
---|---|---|
Node-RED | http://127.0.0.1:1880 |
无需凭证 |
InfluxDB | http://127.0.0.1:8086 |
用户名:管理员 密码:$INFLUXDB_PASSWORD |
Grafana | http://127.0.0.1:3000 |
用户名:管理员 密码:$GRAFANA_PASSWORD |
验证部署
为确保部署成功,请执行以下检查:
-
对于 Node-RED,请验证是否存在两个预加载的流:
-
数据发布流程
-
数据保留流程
-
-
对于 AWS IoT SiteWise,在 AWS IoT SiteWise 控制台中,使用别名确认是否存在数据流
/Renton/WindFarm/Turbine/WindSpeed
。 -
对于 InfluxDB,使用数据资源管理器验证存储桶内
TurbineData
测量中的数据存储。WindFarmData
-
对于 Grafana,请查看控制面板以确认显示从 Node-RED 生成的时间序列数据。