

기계 번역으로 제공되는 번역입니다. 제공된 번역과 원본 영어의 내용이 상충하는 경우에는 영어 버전이 우선합니다.

# Docker(Linux)와 오픈 소스 통합 설정
<a name="linux-docker-setup"></a>

간소화된 배포 프로세스를 위해 Docker를 사용하여 Linux 환경에서 Node-RED®, InfluxDB® 및 Grafana®를 설정할 수 있습니다. 이 방법은 사전 구성된 컨테이너를 사용하므로 구성 요소를 신속하게 배포하고 더 쉽게 관리할 수 있습니다.

## Docker 설정 사전 조건
<a name="linux-docker-prerequisites"></a>

시작하기 전에에 다음이 있는지 확인합니다.
+ MQTT 지원 V3 게이트웨이입니다. 자세한 내용은 [AWS IoT SiteWise Edge용 MQTT 지원 V3 게이트웨이](mqtt-enabled-v3-gateway.md) 단원을 참조하십시오.
+ Docker Compose 플러그인입니다. 설치 단계는 *Docker* 설명서[의 Docker Compose 플러그인 설치를](https://docs.docker.com/compose/install/linux/) 참조하세요.

## 서비스 배포
<a name="linux-docker-deployment"></a>

이 배포는 동일한 호스트에서 SiteWise Edge, InfluxDB, Node-RED 및 Grafana를 실행합니다.

### 환경 설정
<a name="linux-docker-env-setup"></a>

1. 루트 액세스 권한 획득:

   ```
   sudo -i
   ```

1. .env 파일을 생성하거나 다음 환경 변수를 내보냅니다.

   ```
   export INFLUXDB_PASSWORD={{your-secure-influxdb-password}}
   export INFLUXDB_TOKEN={{your-secure-influxdb-token}}
   export GRAFANA_PASSWORD={{your-secure-grafana-password}}
   ```

### Docker 네트워크 구성
<a name="linux-docker-network-config"></a>
+ 이름을 사용하여 브리지 네트워크를 생성합니다`SiteWiseEdgeNodeRedDemoNetwork`.

  ```
  docker network create --driver=bridge SiteWiseEdgeNodeRedDemoNetwork
  ```

### Docker Compose 파일 준비
<a name="linux-docker-compose-file"></a>

다음 YAML 파일의 내용을 SiteWise Edge 게이트웨이 디바이스에 복사합니다.

#### Docker Compose YAML 파일 예제를 보려면 확장
<a name="collapsible-section-docker-compose-file"></a>

```
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 Edge 배포 업데이트
<a name="w2aac17c19c19c26c27b7c11"></a>

1. [AWS IoT 콘솔](https://console.aws.amazon.com/iot/)로 이동합니다.

1. 왼쪽 탐색 메뉴의 **관리** 섹션에서 **Greengrass 디바이스**를 선택한 다음 **코어 디바이스**를 선택합니다.

1. SiteWise Edge Gateway에 연결된 코어 디바이스를 선택합니다.

1. **배포** 탭을 선택한 다음 **배포 ID** 값을 선택합니다.

1. **작업을** 선택한 다음 **수정을** 선택합니다.

1. 팝업 메시지를 읽은 다음 **배포 수정을** 선택합니다.

1. **2단계 - 구성 요소 선택**에서 다음 구성 요소를 선택한 **후 다음을** 선택합니다.
   + `aws.greengrass.clientdevices.mqtt.EMQX`
   + `aws.iot.SiteWiseEdgePublisher`

1. **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"
   }
   ```

1. **다음**을 선택합니다.

1. **4단계 - 고급 설정 구성**에서 **다음을** 선택합니다.

1. **배포** 선택

### 서비스 시작
<a name="linux-docker-launch"></a>

1. Docker Compose 파일을 사용하여 서비스를 시작합니다. `compose.yaml` 파일이 포함된 디렉터리에서 다음 명령을 실행합니다.

   ```
   docker compose up -d
   ```

1. 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:8086 {{username}}@{{gateway_ip_address}}
   ```

이 배포는에서 `SiteWiseEdgeNodeRedDemoNetwork network`다음 서비스를 생성합니다.

**InfluxDB v2(포트 8086)**  
사전 구성된 조직(iot-sitewise-edge), WindFarmData InfluxDB 버킷 및 관리자 자격 증명 포함

**Node-RED(포트 1880)**  
 AWS IoT SiteWise 통합을 위해 InfluxDB 노드 및 사전 구성된 흐름 포함

**Grafana(포트 3000)**  
관리자 사용자, InfluxDB 데이터 소스 및 모니터링 대시보드 포함

### 서비스에 액세스
<a name="linux-docker-access-services"></a>

배포 후 다음 URLs.

**참고**  
호스트 또는 게이트웨이 시스템에서 각 서비스에 액세스할 수 있습니다.


**서비스 액세스 세부 정보**  

| 서비스 | URL | 자격 증명 | 
| --- | --- | --- | 
| Node-RED | [http://127.0.0.1:1880](http://127.0.0.1:1880) | 자격 증명 필요 없음 | 
| InfluxDB | [http://127.0.0.1:8086](http://127.0.0.1:8086) | 사용자 이름: admin<br />암호: $INFLUXDB\_PASSWORD | 
| Grafana | [http://127.0.0.1:3000](http://127.0.0.1:3000) | 사용자 이름: admin<br />암호: $GRAFANA\_PASSWORD | 

## 배포 확인
<a name="linux-docker-verify-deployment"></a>

배포에 성공하려면 다음 검사를 수행합니다.

1. Node-RED의 경우 미리 로드된 흐름이 두 개 있는지 확인합니다.
   + 데이터 게시 흐름
   + 데이터 보존 흐름

1. 의 경우 AWS IoT SiteWise 콘솔 AWS IoT SiteWise에서 별칭이 인 데이터 스트림이 있는지 확인합니다`/Renton/WindFarm/Turbine/WindSpeed`.

1. InfluxDB의 경우 Data Explorer를 사용하여 `WindFarmData` 버킷 내 `TurbineData` 측정에서 데이터 스토리지를 확인합니다.

1. Grafana의 경우 대시보드를 보고 Node-RED에서 생성된 시계열 데이터의 표시를 확인합니다.