Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added assets/images/grafana.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/images/mqtt_explorer.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/images/mqtt_status.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/images/sensors.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/images/server.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
110 changes: 110 additions & 0 deletions report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
# Отчёт по работе №2: Практика Docker
**Цель:** создать систему мониторинга системы датчиков с использованием Docker, Mosquitto, InfluxDB, Telegraf и Grafana на трёх виртуальных машинах (далее ВМ).

## Подготовка ВМ
1. Взяты три ВМ из работы №1 (предварительно настроены в формате client, server, gateway).
2. Необходимо установить на каждую ВМ Docker и Docker Compose посредством:
```shell
sudo apt update
sudo apt upgrade -y
sudo apt install -y docker.io
sudo systemctl enable docker
sudo systemctl start docker
docker --version
sudo usermod -aG docker $USER
sudo apt install -y docker-compose
docker-compose --version
```

## Настройка vmA (client) - Система сенсоров
1. Создаём папку для приложения (у меня `/sensor-sim`).
2. Далее подготавливаем файлы самого приложения:
1. `requirements.txt` в одну строку: `paho-mqtt`.
2. `Dockerfile` для сборки образа и установки зависимости.
3. Сам скрипт `sensor.py`, в котором описан базовый класс **Sensor** и его наследники. Чтение конфигурации происходит из **env-переменных**.

3. Далее необходимо собрать образ и опубликовать его на Docker Hub:
```shell
docker login
docker build -t palllladium/sensor-sim:latest .
docker push palllladium/sensor-sim:latest
```

4. После этого создаём `docker-compose.yml` для запуска контейнеров из опубликованного образа.<br>
Каждый контейнер имеет свои env-переменные: SENSOR_TYPE, SENSOR_NAME и т.д.<br>
Получаем такую картину:

![Запущенные контейнеры с сенсорами](assets/images/sensors.png)

## Настройка vmB (gateway) - MQTT Broker
1. Создаём папку для MQTT Broker (у меня `/mosquitto`).
2. Далее подготавливаем конфиг (по заданию) и `docker-compose.yml`
3. Также запускаем контейнер и проверяем открытость порта 1883:

![MQTT Status](assets/images/mqtt_status.png)

В MQTT Explorer можно увидеть, что значения с сенсоров поступают корректно:

![MQTT Explorer](assets/images/mqtt_explorer.png)

## Настройка vmC (server) - Мониторинг

1. Создаём подобную структуру папок:
```shell
server/
├── docker-compose.yml
├── influxdb/
│ ├── influxdb.conf
│ ├── data/
│ ├── meta/
│ └── wal/
├── telegraf/
│ └── telegraf.conf
├── grafana/
│ ├── provisioning/
│ │ ├── datasources/
│ │ │ └── default.yaml
│ │ └── dashboards/
│ │ ├── default.yaml
│ │ └── dashboard.json
```

- Здесь важно было после запуска контейнера `influxdb` создать базу данных вручную при помощи:
```shell
CREATE database sensors
USE sensors
CREATE USER telegraf WITH PASSWORD 'telegraf' WITH ALL PRIVILEGES
```
- И сконфигурировать `telegraf.conf`, изменив:
```shell
[[outputs.influxdb]]
urls = ["http://influxdb:8086"] # здесь уже указан alias
database = "sensors"
username = "telegraf"
password = "telegraf"

[[inputs.mqtt_consumer]]
servers = ["tcp://192.168.0.150:1883"] # IP ВМ с MQTT
topics = ["/sensor/+"]
data_format = "value"
data_type = "float"
```

2. В `docker-compose.yml` файле указываем одну сеть для всех контейнеров и указываем отдельно именнованые тома для **Grafana** и **InfluxDB**, также монтируем для обоих контейнеров ранее написанные конфиги.
3. После `docker-compose up -d` необходимо зайти на `http://192.168.0.149:3000` (где уже ожидает графический интерфейс **Grafana**) и сконфигурировать вручную дашборд, предварительно добавив источник данных из **InfluxDB**.
4. JSON созданного дашборда экспортируем и сохраняем в `.../grafana/provisioning/dashboards/dashboard.json`.
5. Перезапускаем контейнер с **Grafana**.

![Server](assets/images/server.png)

## Тестирование

1. И в теории, и на практике данные генерируются на vmA (работы сенсоров), поступают на vmB (**MQTT Broker**), а далее поступают на vmC (считываются **Telegraf** и сохраняются в **InfluxDB**), отображение же уже происходит в **Grafana** на заранее сконфигурированном дашборде.
2. У всех контейнеров также реализовано сохранение настроек при помощи `restart: unless-stopped` в `docker-compose.yml`.

Финальные результаты работы:

![Grafana](assets/images/grafana.png)

Представленные скриншоты подтверждают работоспособность системы.
Все необходимые файлы приложены в репозитории.
81 changes: 62 additions & 19 deletions vms/client/simulator/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,24 +1,67 @@
version: "3"
version: '3.3'

services:
temp_sensor:
image: antonaleks/data-simulator
sensor1:
build: .
environment:
- SIM_HOST=192.168.1.1
- SIM_NAME=TIRCAHL24
- SIM_PERIOD=5
- SIM_TYPE=temperature
pressure_sensor:
image: antonaleks/data-simulator
SENSOR_TYPE: temperature
SENSOR_NAME: temp_1
INTERVAL: 3
BROKER_HOST: 192.168.0.150
networks:
- default

sensor2:
build: .
environment:
- SIM_HOST=192.168.1.1
- SIM_NAME=PIRCAHL11
- SIM_PERIOD=10
- SIM_TYPE=pressure
current_sensor:
image: antonaleks/data-simulator
SENSOR_TYPE: pressure
SENSOR_NAME: pressure_1
INTERVAL: 4
BROKER_HOST: 192.168.0.150
networks:
- default

sensor3:
build: .
environment:
- SIM_HOST=192.168.1.1
- SIM_NAME=A0_CURRENT
- SIM_PERIOD=1
- SIM_TYPE=current
SENSOR_TYPE: humidity
SENSOR_NAME: hum_1
INTERVAL: 5
BROKER_HOST: 192.168.0.150
networks:
- default

sensor4:
build: .
environment:
SENSOR_TYPE: current
SENSOR_NAME: curr_1
INTERVAL: 2
BROKER_HOST: 192.168.0.150
networks:
- default

sensor5:
build: .
environment:
SENSOR_TYPE: temperature
SENSOR_NAME: temp_2
INTERVAL: 3
BROKER_HOST: 192.168.0.150
networks:
- default

sensor6:
build: .
environment:
SENSOR_TYPE: pressure
SENSOR_NAME: pressure_2
INTERVAL: 6
BROKER_HOST: 192.168.0.150
networks:
- default

networks:
default:
external:
name: iot_net
54 changes: 0 additions & 54 deletions vms/client/simulator/entity/sensor.py

This file was deleted.

27 changes: 0 additions & 27 deletions vms/client/simulator/main.py

This file was deleted.

2 changes: 1 addition & 1 deletion vms/client/simulator/requirements.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
paho_mqtt==1.6.1
paho_mqtt
66 changes: 66 additions & 0 deletions vms/client/simulator/sensor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import os
import time
import random
import json
import paho.mqtt.client as mqtt
from datetime import datetime

class Sensor:
def __init__(self, name, interval):
self.name = name
self.interval = interval

def generate(self):
return round(random.uniform(10.0, 30.0), 2)

def read(self):
return {
"name": self.name,
"value": self.generate(),
"timestamp": datetime.utcnow().isoformat() + "Z"
}

class TemperatureSensor(Sensor):
def generate(self):
return round(random.uniform(20.0, 40.0), 2)

class PressureSensor(Sensor):
def generate(self):
return round(1000 + random.random() * 50, 2)

class CurrentSensor(Sensor):
def generate(self):
return round(random.uniform(0.1, 10.0), 2)

class HumiditySensor(Sensor):
def generate(self):
return round(random.uniform(30.0, 90.0), 2)

SENSOR_TYPES = {
"temperature": TemperatureSensor,
"pressure": PressureSensor,
"current": CurrentSensor,
"humidity": HumiditySensor,
}

# Параметры из переменных окружения
sensor_type = os.getenv("SENSOR_TYPE", "temperature")
sensor_name = os.getenv("SENSOR_NAME", "sensor1")
interval = int(os.getenv("INTERVAL", 5))
broker_host = os.getenv("BROKER_HOST", "192.168.0.150")
broker_port = int(os.getenv("BROKER_PORT", 1883))

SensorClass = SENSOR_TYPES.get(sensor_type, Sensor)
sensor = SensorClass(sensor_name, interval)

client = mqtt.Client()
client.connect(broker_host, broker_port, 60)

topic = f"/sensor/{sensor_type}"

while True:
payload = json.dumps(sensor.read())
client.publish(topic, payload)
print(json.dumps(sensor.read(), indent=2)) # отладочный вывод
print(f"[{sensor_name}] -> {topic}: {payload}")
time.sleep(interval)
2 changes: 2 additions & 0 deletions vms/gateway/mosquitto/mosquitto.conf
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
listener 1883
allow_anonymous true
persistence false
log_dest stdout
Loading