diff --git a/README.md b/README.md
index 9e8a9c1..acb7a7f 100644
--- a/README.md
+++ b/README.md
@@ -1,63 +1,91 @@
-# Введение
-Данная практика посвещена базовым принципам Docker. Для выполнения практики необходимо использовать 3 виртуальные машины Linux.
-
-# Задание
-
-Необходимо развернуть следующую систему:
-
-
-
-Симуляторы сенсоров, развернутые в докер контейнерах, публикуют сообщения на Mqtt брокер. Сервис Consumer подписывается на все сообщение,
-опубликованные в брокере, и заносит их в базу данных временных рядов. Сервис dashboard отображает графики полученных данных от сервисов.
-
-Для выполнения данного задания необходимо:
-1. Использовать три виртуальные машины из предыдущей практики. Установить на каждой docker и docker-compose.
-
-2. Linux A.
- 1. Разработать скрипты - симулятор технологических данных. Создать класс Sensor, а также наследников этого класса (указать 4 реально существующих типа датчиков, например, температура, давление, потребляемый ток).
- 2. В основной программе реализовать клиента, который подключается к mqtt брокеру и публикует сообщения.
- 3. Конфигурацию типов датчиков вывести через переменные среды
- 4. Запуск реализовать с помощью докер контейнеров. Для запуска нескольких типов использовать docker-compose.
- 5. В итоге сделать 4 типа датчиков, запустить 6 контейнеров разных типов, где задается:
- * тип датчика
- * название датчика
- * дискретность публикации сообщений
- * (ДОП ЗАДАЧА, НЕОБЯЗАТЕЛЬНО) тип формулы (подумайте как можно через конфигурацию повлиять на характер генерации нового числа)
- * в формуле предусмотрите в качестве операнда дату вашего рождения (например умножение на год рождения или месяц)
- * (ДОП ЗАДАЧА, НЕОБЯЗАТЕЛЬНО) тип топика (json формат (topic - /sensor/type, value {"name":"sensor1", "value": 22.4} ) или значение в конкретный топик (topic - /sensor/type/value, value - 22.4))
- 6. Залить на свою страницу в докерхаб созданный image контейнера в публичный репозиторий.
-
-3. Linux B
- 1. Развернуть mosquitto брокер с помощью docker. Настроить проброс портов, необходимый для работы приложения.
- 2. Настроить конфигурационный файл для брокера. Использовать volume для подгрузки файла в докер.
- 3. В файрволе открыть порт, необходимый для работы брокера.
-4. Linux C
- 1. Развернуть influxdb (1.8 версия), telegraf, grafana с помощью docker-compose
- 2. Настроить подписку телеграфа на mqtt топики брокера
- 3. Сохранять все сообщения в influxdb
- 4. В grafana вывести дашборды с текущими значениями всех датчиков и агрегированными показаниями (среднее значение по всем датчикам) - сделать скриншот
- 5. Необходимо создать volume для каждого контейнера с конфигурационными файлами. (ДОП ЗАДАЧА, НЕОБЯЗАТЕЛЬНО) При пересоздании контейнеров конфигурация дашбордов должна сохранится (без самих данных).
- 6. В конфигурационных файлах ссылаться по alias контейнеров (не по ip адресам)
-5. При загрузки задания в гит репозиторий создать две ветки develop и master. В master загрузить только readme - задание.
- В ветку develop - загружаем сделанное задание. Как только отправляете мне на проверку задание, создаете пул реквест в ветку мастер и назначаете меня ревьювером. Таким образом я смогу комментировать ваш репозиторий.
-6. Оформить инструкцию в виде Markdown файла. Приложить конфигурационные файлы в репозиторий.
- Критерием успеха выполнения задания будет являтся также написанная инструкция по запуску ваших контейнеров. То есть, если я загружу с докерхаба ваш image, запущу docker-compose файлы на своих виртуальных машинах, и при этом смогу зайти в интерфейс и посмотреть дашборд с потоком данных, то задание выполнено.
-
-Репозиторий оформить следующим образом:
-- root
- - assets
- - images - картинки для отчета
- - vms
- - client
- - simulator - код симулятора и докерфайл
- - gateway
- - mosquitto - папка с конфигом для mosquitto + sh скрипт по запуску докера
- - server
- - grafana - конфиги для тома графаны
- - influxdb - конфиги для тома инфлукса
- - telegraf - конфиги для тома телеграфа
- - docker-compose.yml
-
- - report.md - Отчет markdown
-
-P.S. опираться можно на данную [статью](https://coderlessons.com/articles/programmirovanie/raspberry-pi-iot-datchiki-influxdb-mqtt-i-grafana)
+## Настройка 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 aleksandr/sensor-sim:latest .
+docker push aleksandr/sensor-sim:latest
+```
+
+4. После этого создаём `docker-compose.yml` для запуска контейнеров из опубликованного образа.
+Каждый контейнер имеет свои env-переменные: SENSOR_TYPE, SENSOR_NAME и т.д.
+Получаем такую картину:
+
+
+## Настройка vmB (gateway) - MQTT Broker
+1. Создаём папку для MQTT Broker (`/mosquitto`).
+2. Далее подготавливаем конфиг и `docker-compose.yml`
+3. Также запускаем контейнер и проверяем открытость порта 1883:
+
+
+
+В MQTT Explorer можно увидеть, что значения с сенсоров поступают корректно:
+
+
+
+## Настройка 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**.
+
+
+
+## Тестирование
+
+1. Данные генерируются на vmA (работы сенсоров), поступают на vmB (**MQTT Broker**), а далее поступают на vmC (считываются **Telegraf** и сохраняются в **InfluxDB**), отображение же уже происходит в **Grafana** на заранее сконфигурированном дашборде.
+2. У всех контейнеров также реализовано сохранение настроек при помощи `restart: unless-stopped` в `docker-compose.yml`.
+
+Финальные результаты работы:
+
+
+
+Представленные скриншоты подтверждают работоспособность системы.
+Все необходимые файлы приложены в репозитории.
diff --git a/grafana.jpg b/grafana.jpg
new file mode 100644
index 0000000..59262af
Binary files /dev/null and b/grafana.jpg differ
diff --git a/mqtt_explorer.jpg b/mqtt_explorer.jpg
new file mode 100644
index 0000000..bb922ca
Binary files /dev/null and b/mqtt_explorer.jpg differ
diff --git a/mqtt_status.jpg b/mqtt_status.jpg
new file mode 100644
index 0000000..511ee16
Binary files /dev/null and b/mqtt_status.jpg differ
diff --git a/photo b/photo
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/photo
@@ -0,0 +1 @@
+
diff --git a/sensors.jpg b/sensors.jpg
new file mode 100644
index 0000000..451507d
Binary files /dev/null and b/sensors.jpg differ
diff --git a/server.jpg b/server.jpg
new file mode 100644
index 0000000..b3a2f01
Binary files /dev/null and b/server.jpg differ
diff --git a/vms/client/docker-compose.yml b/vms/client/docker-compose.yml
new file mode 100644
index 0000000..c8ab021
--- /dev/null
+++ b/vms/client/docker-compose.yml
@@ -0,0 +1,67 @@
+version: '3.3'
+
+services:
+ sensor1:
+ build: .
+ environment:
+ SENSOR_TYPE: temperature
+ SENSOR_NAME: temp_1
+ INTERVAL: 3
+ BROKER_HOST: 192.168.0.150
+ networks:
+ - default
+
+ sensor2:
+ build: .
+ environment:
+ SENSOR_TYPE: pressure
+ SENSOR_NAME: pressure_1
+ INTERVAL: 4
+ BROKER_HOST: 192.168.0.150
+ networks:
+ - default
+
+ sensor3:
+ build: .
+ environment:
+ 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
diff --git a/vms/client/entity/sensor.py b/vms/client/entity/sensor.py
new file mode 100644
index 0000000..12e45f6
--- /dev/null
+++ b/vms/client/entity/sensor.py
@@ -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)
diff --git a/vms/client/requirements.txt b/vms/client/requirements.txt
new file mode 100644
index 0000000..c7b9a31
--- /dev/null
+++ b/vms/client/requirements.txt
@@ -0,0 +1 @@
+paho_mqtt
\ No newline at end of file
diff --git a/vms/gateway/mosquitto/mosquitto.conf.txt b/vms/gateway/mosquitto/mosquitto.conf.txt
new file mode 100644
index 0000000..d7ea974
--- /dev/null
+++ b/vms/gateway/mosquitto/mosquitto.conf.txt
@@ -0,0 +1,4 @@
+listener 1883
+allow_anonymous true
+persistence false
+log_dest stdout
\ No newline at end of file
diff --git a/vms/server/infra/influxdb/scripts/influxdb.conf.txt b/vms/server/infra/influxdb/scripts/influxdb.conf.txt
new file mode 100644
index 0000000..7a6dbf4
--- /dev/null
+++ b/vms/server/infra/influxdb/scripts/influxdb.conf.txt
@@ -0,0 +1,23 @@
+reporting-disabled = true
+bind-address = "127.0.0.1:8088"
+
+[meta]
+ dir = "/var/lib/influxdb/meta"
+
+[data]
+ dir = "/var/lib/influxdb/data"
+ wal-dir = "/var/lib/influxdb/wal"
+ trace-logging-enabled = false
+
+[http]
+ enabled = true
+ bind-address = ":8086"
+ auth-enabled = true
+ log-enabled = true
+ write-tracing = false
+ pprof-enabled = false
+ https-enabled = false
+
+[logging]
+ level = "info"
+ file = "stderr"
\ No newline at end of file
diff --git a/vms/server/infra/telegraf/docker-compose.yml.txt b/vms/server/infra/telegraf/docker-compose.yml.txt
new file mode 100644
index 0000000..d34b98b
--- /dev/null
+++ b/vms/server/infra/telegraf/docker-compose.yml.txt
@@ -0,0 +1,53 @@
+version: '3.3'
+
+services:
+ influxdb:
+ image: influxdb:1.8
+ container_name: influxdb
+ ports:
+ - "8086:8086"
+ volumes:
+ - influxdb-data:/var/lib/influxdb
+ command: influxd -config /etc/influxdb/influxdb.conf
+ environment:
+ - INFLUXDB_DB=sensors
+ - INFLUXDB_ADMIN_ENABLED=true
+ - INFLUXDB_ADMIN_USER=admin
+ - INFLUXDB_ADMIN_PASSWORD=admin
+ - INFLUXDB_USER=telegraf
+ - INFLUXDB_USER_PASSWORD=telegraf
+ networks:
+ - default
+ restart: unless-stopped
+
+ telegraf:
+ image: telegraf:latest
+ container_name: telegraf
+ depends_on:
+ - influxdb
+ volumes:
+ - ./telegraf/telegraf.conf:/etc/telegraf/telegraf.conf:ro
+ networks:
+ - default
+ restart: unless-stopped
+
+ grafana:
+ image: grafana/grafana:latest
+ container_name: grafana
+ ports:
+ - "3000:3000"
+ volumes:
+ - grafana-data:/var/lib/grafana
+ - ./grafana/provisioning:/etc/grafana/provisioning
+ networks:
+ - default
+ restart: unless-stopped
+
+volumes:
+ influxdb-data:
+ grafana-data:
+
+networks:
+ default:
+ external:
+ name: iot_net
\ No newline at end of file
diff --git a/vms/server/infra/telegraf/telegraf.conf.txt b/vms/server/infra/telegraf/telegraf.conf.txt
new file mode 100644
index 0000000..a4f85dc
--- /dev/null
+++ b/vms/server/infra/telegraf/telegraf.conf.txt
@@ -0,0 +1,23 @@
+[agent]
+ interval = "10s"
+ round_interval = true
+ metric_batch_size = 1000
+ metric_buffer_limit = 10000
+ collection_jitter = "0s"
+ flush_interval = "10s"
+ flush_jitter = "0s"
+ precision = ""
+ hostname = "telegraf"
+ omit_hostname = false
+
+[[outputs.influxdb]]
+ urls = ["http://influxdb:8086"]
+ database = "sensors"
+ username = "telegraf"
+ password = "telegraf"
+
+[[inputs.mqtt_consumer]]
+ servers = ["tcp://192.168.0.150:1883"]
+ topics = ["/sensor/+"]
+ data_format = "value"
+ data_type = "float"
\ No newline at end of file