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
154 changes: 91 additions & 63 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,63 +1,91 @@
# Введение
Данная практика посвещена базовым принципам Docker. Для выполнения практики необходимо использовать 3 виртуальные машины Linux.

# Задание

Необходимо развернуть следующую систему:

![image](assets/images/DockerTask.png)

Симуляторы сенсоров, развернутые в докер контейнерах, публикуют сообщения на 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` для запуска контейнеров из опубликованного образа.<br>
Каждый контейнер имеет свои env-переменные: SENSOR_TYPE, SENSOR_NAME и т.д.<br>
Получаем такую картину:
![lA](https://github.com/AlexKud2002/DockerPractice/blob/master/sensors.jpg)

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

![lA](https://github.com/AlexKud2002/DockerPractice/blob/master/mqtt_status.jpg)

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

![lA](https://github.com/AlexKud2002/DockerPractice/blob/master/mqtt_explorer.jpg)

## Настройка 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**.

![lA](https://github.com/AlexKud2002/DockerPractice/blob/master/server.jpg)

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

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

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

![lA](https://github.com/AlexKud2002/DockerPractice/blob/master/grafana.jpg)

Представленные скриншоты подтверждают работоспособность системы.
Все необходимые файлы приложены в репозитории.
Binary file added grafana.jpg
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 mqtt_explorer.jpg
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 mqtt_status.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions photo
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

Binary file added sensors.jpg
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 server.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
67 changes: 67 additions & 0 deletions vms/client/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -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
66 changes: 66 additions & 0 deletions vms/client/entity/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)
1 change: 1 addition & 0 deletions vms/client/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
paho_mqtt
4 changes: 4 additions & 0 deletions vms/gateway/mosquitto/mosquitto.conf.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
listener 1883
allow_anonymous true
persistence false
log_dest stdout
23 changes: 23 additions & 0 deletions vms/server/infra/influxdb/scripts/influxdb.conf.txt
Original file line number Diff line number Diff line change
@@ -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"
53 changes: 53 additions & 0 deletions vms/server/infra/telegraf/docker-compose.yml.txt
Original file line number Diff line number Diff line change
@@ -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
Loading