diff --git a/REPORT.md b/REPORT.md new file mode 100644 index 0000000..eaa4faa --- /dev/null +++ b/REPORT.md @@ -0,0 +1,134 @@ +# Отчёт по работе №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 +``` + +# Шаг 1: Разработка Simulator для генерации данных + +В файле `sensor.py` создаем четвертыht типf датчиков. + +Далее в файле `main.py` реализовуем клиента, который подключается к mqtt брокеру и публикует сообщения. + +В `Dockerfile`, необходимом для того, что создать образ, указываем следуюещие инструкции: + +``` Dockerfile +FROM python:alpine3.19 +WORKDIR /app +COPY requirements.txt . +RUN pip install -r requirements.txt +COPY . . +CMD ["python", "main.py"] +``` + +Инструкция `FROM` инициализирует новый этап сборки и устанавливает базовый образ для последующих инструкций. `WORKDIR` создает рабочий каталог для последующих инструкций `Dockerfile`. Инструкция `COPY` копирует файл *requirements.txt* из источника в указанное место внутри образа. +Инструкция `RUN`задает команды, которые следует выполнить и поместить в новый образ контейнера. `RUN` описывает команду с аргументами, которую нужно выполнить когда контейнер будет запущен. + +Содержимое файла *requirements.txt* приведено ниже: +`paho_mqtt==1.6.1` + +*paho_mqtt* предоставляет клиентский класс, который позволяет приложениям подключаться к MQTT-брокеру для публикации сообщений. + +После проделанных выше опреаций можно создать образ командой: + +`docker build -t luyda/sensor-sim . ` + +Таким способом мы создали образ, из которого можно развернуть контейнер. + +# Шаг 2: Запуск Mosquitto брокера + +Для настройки протокола MQTT необходимо создать конфигурационный файл *mosquitto.conf* со следующим содержимымы: + +```c +listener 1883 +allow_anonymous true +``` +Для более удобного запуска брокера создадим файл *docker-compose.yml*. + +Теперь создав контейнер посмотрим его логи: +![example_run_logs](https://github.com/user-attachments/assets/79529d5e-a1a1-4f9a-bb6e-ae171fc12bc2) + +Брокер же отображает присоединившегося клиента. +![mqtt-docker_run](https://github.com/user-attachments/assets/ac0feb01-6071-4bf4-8a9c-e969bd2aea44) + +Теперь можно запустить несколько датчиков. Но перед этим необходимо прописать *docker-compose.yml*: + +С помощью команды `docker compose up` запустим контейнеры. + +При этом брокер отображает: +![mqtt-docker-compose](https://github.com/user-attachments/assets/67cae960-53c8-42cc-a962-8eb575bdbbed) + +# Шаг 3: Получение данных от симулятора +Первоначально необходимо настроить Telegraf, который подписывается на MQTT, где датчики публикуют данные. Данные будут сохраняться в InfluxDB. Отображение информации с датчиков будет происходить при помощи Grafana. + +## Telegraf +Перед использованием Telegram необходимо настроить его. +Для этого создадим конфигурационный файл *telegraf.conf* +Тем самым мы настраиваем Telegraf на чтение данных с машины IP адрес которой 192.168.0.101 через порт 1883. + +Настройка Telegraf на этом завршена. + +## InfluDB + +Для создания базы данных необходимо в конфигурационном файле *influxdb-init.iql* прописать следующее: +```sql +CREATE database sensors +CREATE USER telegraf WITH PASSWORD 'telegraf' WITH ALL PRIVILEGES +``` + +## Grafana + +Данные для отображения датчиков берутся из InfluxDB. +Для настройки в конфигурационном файле необходимо прописать следующее: +```yaml +apiVersion: 1 +datasources: + - name: InfluxDB_v1 + type: influxdb + access: proxy + database: sensors + user: telegraf + url: http://influxdb:8086 + jsonData: + httpMode: GET + secureJsonData: + password: telegraf +``` + +Для запуска всех трех контенйеров воспользуемся docker-compose. создадим файл docker-compose.yml + +После чего можно выполнить команду `docker compose up` + +# Настройка дашборда +После запуска всех необходимых контейнеров, в браузере переходим по `192.168.0.101:3000` + +Для проверки соединения перейдем в Menu -> Connections -> Data sources. Находим в истончиках InfluxDB и проверяем подключение: +![grafana-influxdb-test](https://github.com/user-attachments/assets/db9150a7-259a-4186-94d3-167383dd9e25) + +После переходим через Меню в раздел Dashboards, где создаем собственный дашбоард. +![grafana-new-view](https://github.com/user-attachments/assets/ee1b4398-b5e2-4910-b340-9d1a33185fb5) + +Для отображения информации с датчиков необходимо создать запрос (query). +![query](https://github.com/user-attachments/assets/3dbd7945-7190-470d-bf3c-f6f1b537e77e) + +После создания необходимо количества графиков, отображающих инфомрацию с Simluator, экспортируем дашборд как JSON-файл. + +Для этого находим функцию `Share` -> ``Export` -> `Save to file`. Сохраненный файл помещаем в папку `vms\server\infra\grafana\provisioning\dashboards\mqtt.json` + +# Пример выполненной работы + +![dashboard](https://github.com/user-attachments/assets/f94c4469-4ef2-4c4a-b2bb-9b2c7464bd79) + + diff --git a/vm/client/Dockerfile b/vm/client/Dockerfile new file mode 100644 index 0000000..a393901 --- /dev/null +++ b/vm/client/Dockerfile @@ -0,0 +1,4 @@ +FROM python:alpine3.19 +WORKDIR /app +COPY requirements.txt . +RUN pip install -r requirements.txt diff --git a/vm/client/docker-compose.yml b/vm/client/docker-compose.yml new file mode 100644 index 0000000..d2fd238 --- /dev/null +++ b/vm/client/docker-compose.yml @@ -0,0 +1,31 @@ +version: "3" + +services: + temp_sensor: + image: luyda/sensor-sim + environment: + - SIM_HOST=192.168.0.101 + - SIM_NAME=TEMP1 + - SIM_PERIOD=2 + - SIM_TYPE=temperature + pressure_sensor: + image: luyda/sensor-sim + environment: + - SIM_HOST=192.168.0.101 + - SIM_NAME=PRESS1 + - SIM_PERIOD=2 + - SIM_TYPE=pressure + current_sensor: + image: luyda/sensor-sim + environment: + - SIM_HOST=192.168.0.101 + - SIM_NAME=CURRENT1 + - SIM_PERIOD=2 + - SIM_TYPE=current + co_sensor: + image: polinazhirakova/data-simulator + environment: + - SIM_HOST=192.168.0.101 + - SIM_NAME=CO1 + - SIM_PERIOD=2 + - SIM_TYPE=carbon_oxid diff --git a/vm/client/enity/sensor.py b/vm/client/enity/sensor.py new file mode 100644 index 0000000..db58e87 --- /dev/null +++ b/vm/client/enity/sensor.py @@ -0,0 +1,14 @@ + import math + self.value = math.sin(self.step) + self.step = self.step + 1 + +class CO(Sensor): + step = 0 + + def __init__(self,name): + super().__init__(name) + self.type = "carbon oxid" + + def generate_new_value(self): + self.value = self.step * 1e6 + self.step = self.step + 0.001 diff --git a/vm/client/main.py b/vm/client/main.py new file mode 100644 index 0000000..38198d8 --- /dev/null +++ b/vm/client/main.py @@ -0,0 +1,21 @@ +import paho.mqtt.client as paho +from os import environ +import time +from entity.sensor import * +broker = "localhost" if "SIM_HOST" not in environ.keys() else environ["SIM_HOST"] +port = 1883 if "SIM_PORT" not in environ.keys() else environ["SIM_PORT"] +name = "sensor" if "SIM_NAME" not in environ.keys() else environ["SIM_NAME"] +period = 1 if "SIM_PERIOD" not in environ.keys() else int(environ["SIM_PERIOD"]) +type_sim = "temperature" if "SIM_TYPE" not in environ.keys() else environ["SIM_TYPE"] +sensors = {"temperature": Temperature, "pressure": Pressure, "current": Current, "carbon_oxid": CO} +def on_publish(client, userdata, result): + print(f"data published {userdata}") + pass +sensor = sensors[type_sim](name=name) +client1 = paho.Client(sensor.name) +client1.on_publish = on_publish +client1.connect(broker, port) +while True: + sensor.generate_new_value() + ret = client1.publish("sensors/" + sensor.type + "/" + sensor.name, sensor.get_data()) + time.sleep(period) diff --git a/vm/client/requirements.txt b/vm/client/requirements.txt new file mode 100644 index 0000000..13e9d8e --- /dev/null +++ b/vm/client/requirements.txt @@ -0,0 +1 @@ +paho_mqtt==1.6.1 diff --git a/vm/gateway/docker-compose.yml b/vm/gateway/docker-compose.yml new file mode 100644 index 0000000..3aa4263 --- /dev/null +++ b/vm/gateway/docker-compose.yml @@ -0,0 +1,9 @@ +version: "3" +services: + broker: + image: eclipse-mosquitto + container_name: broker + volumes: + - ./mosquitto/mosquitto.conf:/mosquitto/config/mosquitto.conf + ports: + - "1883:1883" diff --git a/vm/server/docker-compose.yml b/vm/server/docker-compose.yml new file mode 100644 index 0000000..b82fc3d --- /dev/null +++ b/vm/server/docker-compose.yml @@ -0,0 +1,38 @@ +version: "3" +services: + influxdb: + image: influxdb:1.8 + container_name: influxdb + volumes: + - ./influxdb/scripts:/docker-entrypoint-initdb.d + - influx_data:/var/lib/influxdb + networks: + - server-net + telegraf: + image: telegraf + container_name: telegraf + volumes: + - ./telegraf:/etc/telegraf:ro + restart: unless-stopped + networks: + - server-net + grafana: + image: grafana/grafana + container_name: grafana + volumes: + - grafana_data:/var/lib/grafana + - ./grafana/:/etc/grafana/ + environment: + - GF_SECURITY_ADMIN_USER=admin + - GF_SECURITY_ADMIN_PASSWORD=admin + - GF_USERS_ALLOW_SIGN_UP=false + restart: unless-stopped + ports: + - 3000:3000 + networks: + - server-net +volumes: + influx_data: {} + grafana_data: {} +networks: + server-net: {} diff --git a/vm/server/telegraf/telegraf.conf b/vm/server/telegraf/telegraf.conf new file mode 100644 index 0000000..6a2a7c4 --- /dev/null +++ b/vm/server/telegraf/telegraf.conf @@ -0,0 +1,48 @@ +[global_tags] + +[agent] + interval = "10s" + round_interval = true + metric_batch_size = 1000 + collection_jitter = "0s" + flush_interval = "10s" + flush_jitter = "0s" + precision = "0s" + hostname = "" + omit_hostname = false + +[[outputs.influxdb]] + urls = ["http://influxdb:8086"] + database = "sensors" + skip_database_creation = true + username = "telegraf" + password = "telegraf" + +[[inputs.cpu]] + percpu = true + totalcpu = true + collect_cpu_time = false + report_active = false + +[[inputs.disk]] + ignore_fs = ["tmpfs", "devtmpfs", "devfs", "iso9660", "overlay", "aufs", "squashfs"] + +[[inputs.diskio]] + +[[inputs.kernel]] + +[[inputs.mem]] + +[[inputs.processes]] + +[[inputs.swap]] + +[[inputs.system]] + +[[inputs.mqtt_consumer]] + servers = ["tcp://192.168.0.101:1883"] + topics = [ + "sensors/#" + ] + data_format = "value" + data_type = "float"