image

Привет, Хабр! Меня зовут Дмитрий, я старший системный инженер в Selectel, работаю с серверами и клиентским оборудованием.

Ранее я написал статью о том, как появилась идея создать бота на базе API Telegram, который анализирует показатель S.M.A.R.T дисков. Теперь более детально расскажу о его разработке и о том, как было развернуто приложение.

Перед тем как мы погрузимся в детали, добавлю, что у меня нет опыта коммерческой разработки и администрирования инфраструктуры. В сети довольно много информации о том, как это делать в теории, но мало практических материалов для выхода на уровень продакшена.

Как системный инженер я изучаю новые вещи «методом тыка». Поэтому все представленные сборки приложения и способы его развертывания основаны на личном опыте и умении искать информацию в сети, а также применении «метода тыка» для получения конечного результата.

Воспользуйтесь навигацией, чтобы выбрать интересующий вас блок:

Инфраструктура
Разработка
Заключение

Инфраструктура


Важный аспект при планировании инфраструктуры приложения — ее готовность к будущему масштабированию. Начнем с визуализации и описания схемы взаимодействия компонентов.

Основные компоненты модели взаимодействия:
  1. Сервер: виртуальная машина с двумя vCPU, 4 ГБ ОЗУ и 10 ГБ SSD. Операционная система — Ubuntu 22.04.
  2. Внутрикорпоративные сервисы Selectel: сервисы с содержанием актуальных данных S.M.A.R.T и пользователей, которые могут с ними взаимодействовать.
  3. Telegram: интеграция с Telegram API для уведомлений и связи с пользователями.
  4. Сеть: задает связи между компонентами.
  5. Инженер: имеет доступ в ОС через SSH-ключ и на веб-панель Portainer.

Сервер


Сервер — ключевая часть инфраструктуры, я развернул его в облаке Selectel. В качестве ОС выбрал Ubuntu 22.04.


Этапы настройки удаленного хоста srv-smart
Разметка дискового пространства
> root@server:~# lsblk
NAME    MAJ:MIN RM  SIZE RO TYPE MOUNTPOINTS
sda       8:0    0   10G  0 disk
├─sda1    8:1    0   10G  0 part /
└─sda14   8:14   0    8M  0 part
sr0      11:0    1  492K  0 rom

Сетевые интерфейсы

IP-адрес сервера — 172.168.1.1.

> ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether fa:16:3e:a9:a5:8a brd ff:ff:ff:ff:ff:ff
    altname enp0s3
    altname ens3
    inet 172.168.1.1/24 brd 172.168.1.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::f816:3eff:fea9:a58a/64 scope link
       valid_lft forever preferred_lft forever

Настройка SSH-доступа

В данном примере SSH-ключ копируется на сервер для безопасного доступа. Также изменяется конфигурация SSH для отключения входа по паролю.

ssh-copy-id</i> <i>root@172.168.1.1

> sed -i 's/#PermitRootLogin.*/PermitRootLogin yes/g' /etc/ssh/sshd_config
> sed -i 's/PasswordAuthentication.*/PasswordAuthentication no/g' /etc/ssh/sshd_config

> systemctl restart sshd


Все шаги по развертыванию приложения будем выполнять с использованием Ansible.

Установим Ansible на локальную операционную систему

> apt-add-repository ppa:ansible/ansible
> apt update
> apt install ansible -y
> ansible --version
ansible [core 2.15.2]
  config file = /etc/ansible/ansible.cfg
  configured module search path = ['/root/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python3/dist-packages/ansible
  ansible collection location = /root/.ansible/collections:/usr/share/ansible/collections
  executable location = /usr/bin/ansible
  python version = 3.10.12 (main, Jun 11 2023, 05:26:28) [GCC 11.4.0] (/usr/bin/python3)
  jinja version = 3.0.3
  libyaml = True

В файле /etc/ansible/hosts определяем удаленный хост srv-smart.

[servers]
srv-smart ansible_host=172.168.1.1

[all:vars]
ansible_python_interpreter=/usr/bin/python3

Проверяем доступность удаленного хоста.

> ansible srv-smart -m ping

srv-smart | SUCCESS => {
    "changed": false,
    "ping": "pong"
}

На схеме взаимодействия указаны:

  • Порт 22 — для удаленного доступа к серверу по SSH,
  • eth0 — интерфейс, на который подан VLAN для связи с внешней и внутренней сетями сервисов Selectel.

Для развертывания приложения использую Docker, в котором работают три контейнера:

  • portainer — для запуска Portainer, который позволяет управлять контейнерами в Docker,
  • mongodb — для развертывания MongoDB,
  • service — для запуска сервисов приложения на Node.js.

Сначала клонируем репозиторий на локальную ОС.

> git clone git@bitbucket.org:khamchenko/smart-slc.git

Этапы локальной установки Docker
Начинаем настройку Docker с установки его компонентов.

> curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
> echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
> apt update
> apt-cache policy docker-ce
> apt install docker-ce docker-ce-cli containerd.io docker-compose-plugin

Проверяем статус.

> systemctl status docker
● docker.service - Docker Application Container Engine
     Loaded: loaded (/lib/systemd/system/docker.service; enabled; vendor preset: enabled)
     Active: active (running) since Tue 2023-06-06 15:55:44 UTC; 30s ago
TriggeredBy: ● docker.socket
       Docs: https://docs.docker.com
   Main PID: 3371 (dockerd)
      Tasks: 9
     Memory: 27.1M
        CPU: 706ms
     CGroup: /system.slice/docker.service
             └─3371 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
> docker -v
Docker version 24.0.5, build ced0996


Редактируем переменные окружения в файле smart-slc/docker.infra/.env.

MONGO_USER=<mongo-user>
MONGO_PASS_PROD=<mongo-pass-prod>
MONGO_PASS_DEV=<mongo-pass-dev>
MONGO_DB=<mongo-db>

MONGO_INITDB_ROOT_USERNAME=<mongo-root>
MONGO_INITDB_ROOT_PASSWORD=<mongo-root-pass>
MONGO_INITDB_DATABASE=<mongo-init-db>

TG_TOKEN=<tg-token-jwt>


JWT_SECRET=<jwt-secret>
TELEGRAM_TOKEN_DEV=<tg-token-dev>
TELEGRAM_TOKEN_PROD=<tg-token-prod>

Для сборки приложения используется webpack. Для этого выделили отдельный контейнер nodejs-build, который содержит окружение node.js.

> cd ./smart-slc/docker.infra/nodejs-build
> docker compose up -d
> bash build-prod.sh

Запускаем Ansible-плейбук для развертывания инфраструктуры на удаленном хосте srv-smart.

> ansible-playbook docker.infra/ansible/srv-smart/main/pb_main.yml  

После развертывания на удаленном хосте проверяем статус контейнеров.

> docker ps
CONTAINER ID   IMAGE                           COMMAND                  CREATED          STATUS          PORTS                                                           NAMES
adb13e7089d8   mongo:latest                    "docker-entrypoint.s…"   33 seconds ago   Up 32 seconds   0.0.0.0:35689->27017/tcp, :::35689->27017/tcp                   mongodb
b6c3e9e2fdfe   nodejs-nodejs                   "docker-entrypoint.s…"   2 minutes ago    Up 2 minutes    0.0.0.0:5001-5002->5001-5002/tcp, :::5001-5002->5001-5002/tcp   services
694bb2e80cb5   portainer/portainer-ce:latest   "/portainer"             18 minutes ago   Up 18 minutes   8000/tcp, 9000/tcp, 0.0.0.0:9443->9443/tcp, :::9443->9443/tcp   portainer

Контейнер Portainer


Portainer.io предоставляет средство управления Docker-контейнерами. Рассмотрим его структуру и взаимодействие с другими контейнерами.

  • Подсеть (172.18.0.0/16) с названием network-smart создана для связи между контейнерами mongodb и services, обеспечивая их взаимодействие.
  • Подсеть (172.17.0.0/16) bridge.
  • Порт 9443 предоставляет внешний доступ к веб-интерфейсу Portainer.

Конфигурационный файл docker-compose.yml.

version: "3.8"
services:
  portainer:
    image: portainer/portainer-ce:latest
    container_name: portainer
    hostname: portainer
    restart: always
    ports:
      - 9443:9443
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - {{ path_infra }}/smart-slc/portainer/data:/data

Файлы конфигурации контейнера и проверка его работы
Проверка работы

Переходим в браузерной строке на https://172.168.1.1:9443 и задаем username и password.


Проверим список запущенных контейнеров.


Проверим подключение к базе данных извне с помощью MongoDB Compass.



Файл конфигурации контейнера docker-compose.yml.

version: "3.8"
services:
  portainer:
    image: portainer/portainer-ce:latest
    container_name: portainer
    hostname: portainer
    restart: always
    ports:
      - 9443:9443
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - {{ path_infra }}/smart-slc/portainer/data:/data


Контейнер MongoDB


Представляет собой NoSQL базу данных MongoDB. В этом контейнере созданы коллекции users (пользователи) и devices (устройства). Более подробно со схемами коллекций можно ознакомиться здесь. Давайте рассмотрим структуру контейнера и проверим работу.

На схеме указаны:

  • Порт 35689 — используется для подключения к базе данных,
  • связь с контейнером services осуществляется через сеть network-smart.

Файлы конфигурации контейнера и проверка его работы
Конфигурационный файл docker-compose.yml.

version: "3.8"
services:
  mongodb:
    image: mongo:latest
    container_name: mongodb
    hostname: mongodb
    restart: always
    env_file: [.env]
    ports:
      - 35689:27017
    volumes:
      - {{ path_infra }}/smart-slc/mongodb/data:/data/db/
      - {{ path_infra }}/smart-slc/mongodb/init-mongo.js:/docker-entrypoint-initdb.d/init-mongo.js
    networks:
      - network-smart
    logging:
      options:
        max-size: "200k"
        max-file: "3"
networks:
  network-smart:
    external: true

Проверяем версию установленной Mongodb.

> docker exec -it mongodb /bin/bash
> mongosh
Current Mongosh Log ID: 64d7802347ddb6a2ae56c7b0
Connecting to:          mongodb://127.0.0.1:27017/?directConnection=true&serverSelectionTimeoutMS=2000&appName=mongosh+1.10.1
Using MongoDB:          6.0.8
Using Mongosh:          1.10.1

Проверим подключение к базе данных извне с помощью MongoDB Compass.



Контейнер Services


Представляет собой среду node.js с пакетом pm2. Внутри контейнера запущены следующие компоненты приложения:

  • workers-smart — обработчики для синхронизации MongoDB с БД Selectel и синхронизации пользователей во внутренних сервисах,
  • srv-smart — обработчик для анализа информации S.M.A.R.T.,
  • tg-smart — обработчик событий Telegram.

Файлы конфигурации контейнера
Файл конфигурации контейнера docker-compose.yml.

version: "3.8"
services:
  mongodb:
    image: mongo:latest
    container_name: mongodb
    hostname: mongodb
    restart: always
    env_file: [.env]
    ports:
      - 35689:27017
    volumes:
      - {{ path_infra }}/smart-slc/mongodb/data:/data/db/
      - {{ path_infra }}/smart-slc/mongodb/init-mongo.js:/docker-entrypoint-initdb.d/init-mongo.js
    networks:
      - network-smart
    logging:
      options:
        max-size: "200k"
        max-file: "3"
networks:
  network-smart:
    external: true

Файл Dockerfile с инструкциями для создания образа контейнера.

FROM node:18.14.0

RUN npm install pm2 -g

Файл init-node.sh для запуска сервисов.

#!/bin/bash

docker exec -it services /bin/bash -c "cd /srv/tg-smart && npm install"
docker exec -it services /bin/bash -c "cd /srv/srv-smart && npm install"
docker exec -it services /bin/bash -c "cd /srv/workers-smart && npm install"

docker exec -it services /bin/bash -c "pm2 start"
docker exec -it services /bin/bash -c "pm2 save"

Файл upd-build.sh для перезапуска сервисов после повторной сборки.

docker exec -it services /bin/bash -c "cd /srv/tg-smart && npm install && pm2 restart srv-smart"
docker exec -it services /bin/bash -c "cd /srv/srv-smart && npm install && pm2 restart smart-slc"
docker exec -it services /bin/bash -c "cd /srv/workers-smart && npm install && pm2 restart workers-smart"

Файл конфигурации pm2 ecosystem.config.js.

module.exports = {
    apps : [{
        name: "tg-smart",
        script: "/srv/tg-smart/api.prod.js"
    },{
        name: "srv-smart",
        script: "/srv/srv-smart/api.prod.js"
    },{
        name: "workers-smart",
        script: "/srv/workers-smart/api.prod.js"
    }]
}

Проверяем работу сервисов.

docker exec -it services bash
> root@services:/# pm2 status 


> root@services:/# pm2 monit 



Схема связей:

  • Порты 5001-5003 используются для внутреннего взаимодействия.
  • Связь tg-smart и srv-smart осуществляется через socket.io для доставки сообщений.
  • Связь mongodb-smart и srv-smart.
  • Cвязь mongodb-smart и tg-smart.
  • Cвязь mongodb-smart и workers-smart — для синхронизации пользователей и пороговых значений S.M.A.R.T.
  • Cвязь worker-smart и внутрикорпоративных сервисов Selectel.
  • Связь tg-smart и Telegram.

Внутрикорпоративные сервисы Selectel


Доступ к сервисам Selectel строго ограничен правами группы доступа. Для управления логикой взаимодействия с сервисами создал контейнеры mongodb и services. Сервис workers-smart выполняет эту логику взаимодействия, а база данных MongoDB хранит пороговые значения S.M.A.R.T и информацию о пользователях.

Telegram


Для взаимодействия с Telegram API использую npm-пакет node-telegram-bot-api. В составе сборки сервиса tg-smart он обрабатывает логику подключения к Telegram.

Сеть


Как я описал выше, отдельные компоненты инфраструктуры имеют связи. Они обусловлены организацией сети на уровне Docker и самой ОС. Доступ извне ограничен правилами iptables и ufw.
Особенности настройки правил iptables
На Github есть полезная публикация с подробным описанием использования UFW и Docker.

Текущий вывод iptables после запуска контейнеров Docker.

>iptables -L
Chain INPUT (policy ACCEPT)
target     prot opt source               destination

Chain FORWARD (policy DROP)
target     prot opt source               destination
DOCKER-USER  all  --  anywhere             anywhere
DOCKER-ISOLATION-STAGE-1  all  --  anywhere             anywhere
ACCEPT     all  --  anywhere             anywhere             ctstate RELATED,ESTABLISHED
DOCKER     all  --  anywhere             anywhere
ACCEPT     all  --  anywhere             anywhere
ACCEPT     all  --  anywhere             anywhere
ACCEPT     all  --  anywhere             anywhere             ctstate RELATED,ESTABLISHED
DOCKER     all  --  anywhere             anywhere
ACCEPT     all  --  anywhere             anywhere
ACCEPT     all  --  anywhere             anywhere

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination

Chain DOCKER (2 references)
target     prot opt source               destination
ACCEPT     tcp  --  anywhere             172.17.0.2           tcp dpt:9443
ACCEPT     tcp  --  anywhere             172.18.0.2           tcp dpt:5002
ACCEPT     tcp  --  anywhere             172.18.0.2           tcp dpt:5001
ACCEPT     tcp  --  anywhere             172.18.0.3           tcp dpt:27017

Chain DOCKER-ISOLATION-STAGE-1 (1 references)
target     prot opt source               destination
DOCKER-ISOLATION-STAGE-2  all  --  anywhere             anywhere
DOCKER-ISOLATION-STAGE-2  all  --  anywhere             anywhere
RETURN     all  --  anywhere             anywhere

Chain DOCKER-ISOLATION-STAGE-2 (2 references)
target     prot opt source               destination
DROP       all  --  anywhere             anywhere
DROP       all  --  anywhere             anywhere
RETURN     all  --  anywhere             anywhere

Chain DOCKER-USER (1 references)
target     prot opt source               destination
RETURN     all  --  anywhere             anywhere

Проверим сетевые интерфейсы.

>ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether fa:16:3e:a9:a5:8a brd ff:ff:ff:ff:ff:ff
    altname enp0s3
    altname ens3
    inet 172.168.1.1/24 brd 172.168.1.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::f816:3eff:fea9:a58a/64 scope link 
       valid_lft forever preferred_lft forever
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default 
    link/ether 02:42:06:d0:81:b1 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
       valid_lft forever preferred_lft forever
    inet6 fe80::42:6ff:fed0:81b1/64 scope link 
       valid_lft forever preferred_lft forever
4: br-6bf419f6296f: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:e0:a6:17:ae brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.1/16 brd 172.18.255.255 scope global br-6bf419f6296f
       valid_lft forever preferred_lft forever
    inet6 fe80::42:e0ff:fea6:17ae/64 scope link 
       valid_lft forever preferred_lft forever
5: br-7a47cf77f7b2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:b8:d1:fc:36 brd ff:ff:ff:ff:ff:ff
    inet 172.18.0.1/16 brd 172.19.255.255 scope global br-7a47cf77f7b2
       valid_lft forever preferred_lft forever
    inet6 fe80::42:b8ff:fed1:fc36/64 scope link 
       valid_lft forever preferred_lft forever
25: veth011a094@if24: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-6bf419f6296f state UP group default 
    link/ether ba:5e:30:fe:7e:b9 brd ff:ff:ff:ff:ff:ff link-netnsid 2
    inet6 fe80::b85e:30ff:fefe:7eb9/64 scope link 
       valid_lft forever preferred_lft forever
27: veth664666f@if26: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-7a47cf77f7b2 state UP group default 
    link/ether 92:81:05:84:5b:8e brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet6 fe80::9081:5ff:fe84:5b8e/64 scope link 
       valid_lft forever preferred_lft forever
29: veth373aaf8@if28: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-6bf419f6296f state UP group default 
    link/ether ea:71:35:2a:9f:fb brd ff:ff:ff:ff:ff:ff link-netnsid 1
    inet6 fe80::e871:35ff:fe2a:9ffb/64 scope link 
       valid_lft forever preferred_lft forever

В файл /etc/ufw/after.rules добавим правила.

*filter
:DOCKER-USER - [0:0]
:ufw-user-input - [0:0]

-A DOCKER-USER -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A DOCKER-USER -m conntrack --ctstate INVALID -j DROP
-A DOCKER-USER -i eth0 -j ufw-user-input
-A DOCKER-USER -i eth0 -j DROP
COMMIT

Далее выполняем команды для перезапуска службы ufw, открытия портов 22 и 9443 и включения ufw.

> systemctl restart ufw
> ufw allow 22
> ufw allow 9443
> ufw enable

Результат сканирования портов.

> nmap -p- 172.168.1.1
Nmap scan report for 172.168.1.1                                                                                        Host is up (0.030s latency).                                                                                            Not shown: 65533 filtered tcp ports (no-response)                                                                       PORT     STATE SERVICE                                                                                                  22/tcp   open  ssh                                                                                                      9443/tcp open  tungsten-https


Инженер


Роль инженера в данной схеме описывает его взаимодействие с компонентами инфраструктуры:

  • доступ в ОС через SSH-ключ;
  • доступ к веб-интерфейсу Portainer.



Разработка


Предлагаю пройтись по основным моментам разработки.

Что использовал?


koa.js, axios, lodash, moment, mongoose, nodemon, uuid, babel, webpack, jwt, socket.io, node-telegram-bot-api


Ниже вы можете кратко ознакомиться с некоторыми деталями.

webpack


Сборщик модулей, который используется для упаковки и оптимизации кода приложения. Подробнее с конфигурацией можно ознакомиться в репозитории.

mongoose


Библиотека для работы с MongoDB. Используется для определения объектов со строго типизированной схемой, соответствующей документам MongoDB.

Подробнее со схемами коллекции устройств и пользователей можно ознакомиться в репозитории. Для подключения к MongoDB используется метод mongoConnection.

koa.js


Фреймворк для Node.js, используемый как middleware. Обеспечивает гибкость разработки и масштабируемость, минимизируя объем ресурсов для запуска.

socket.io

Библиотека на основе протокола WebSocket. Используется для обмена сообщениями между сервисами tg-smart и srv-smart.

Структура

.
├── .env // константы окружения
├── .babelrc // конфигурационный файл babel
├── api
│   ├── api.prod.js // build
│   ├── app.js // основной файл приложения на koa.js.
│   ├── server.js // точка входа в приложение
│   ├── collection // доступ к коллекциям mongodb
│   ├── config // формирование констант из окружения и конфигурационных файлов
│   ├── connectors // подключения к базам данных и другим сервисам
│   ├── constants // константы окружения
│   ├── handlers // обработчики ошибок и другие обработчики
│   ├── helpers // вспомогательные функции и классы
│   ├── services // вспомогательные методы для работы с данными
│   ├── socket // логика подключения socket.io
│   └── utils //  утилиты для работы с окружением
├── config // содержит конфигурационные объект
├── package.json // зависимости
├── schemes // содержит схемы для работы с данными MongoDB
└── webpack // содержит конфигурационный файл webpack для сборки модулей

Сборка workers-smart


Я уже писал, что опущу подробности о сервисе workers-smart, так как его главная функция заключается в синхронизации с сервисами Selectel. Конкретно он занимается обновлением текущих пороговых значений атрибутов S.M.A.R.T в базе данных MongoDB. Детали его реализации находятся внутри нашего закрытого окружения и остаются в рамках строгой политики конфиденциальности и безопасности данных.

Сборка tg-smart


Подробнее ознакомиться с данной сборкой можно в репозитории. Этот сервис выделен в отдельный модуль, что обеспечивает удобство масштабирования приложения в будущем. Главные задачи tg-smart — это управление пользовательским интерфейсом, трансляция сообщений от других сервисов и проверка подлинности пользователей.

Благодаря использованию транспорта socket.io впоследствии появится возможность декомпозировать приложение на функциональные части и распределять нагрузку между различными сервисами.
Подробнее о коде файла
В коде файла присутствуют два метода для работы с JWT: один – для проверки, а другой – для генерации токена для аутентификации сервисов, подключенных к tg-smart через socket.io. Также в коде файла осуществляется подписка на события состояния соединения с другими сервисами через socket.io. Подключение к Telegram API реализуется через библиотеку node-telegram-bot-api.

При подключении сервиса srv-smart сначала проверяется валидность токена с помощью метода jwt.verify. После успешной проверки сервис присоединяется к комнате “service” в socket.io и ожидает сообщения для передачи в Telegram от других сервисов.

В этой части кода реализована подписка на событие “message” от Telegram. Когда сообщение поступает, происходит проверка наличия пользователя в базе данных. Если пользователь уже существует, отправляется сообщение сервису srv-smart через socket.io. В случае отсутствия пользователя он создается в базе данных, и ему отправляется приветственное сообщение, аналогичное тому, что генерируется при отправке события “/start”. Обработка события “/start” также предусматривает свой обработчик, который будет использоваться для отрисовки будущего меню.

Сборка srv-smart


Подробнее ознакомиться с этим сервисом можно в репозитории. Данный компонент инфраструктуры занимается обработкой входящих сообщений и предоставляет информацию о состоянии атрибутов S.M.A.R.T.
Подробнее о коде файла
В коде файла осуществляется подписка на событие “message” от сервиса tg-smart. Когда происходит получение сообщения, важно учесть, что информация о состоянии атрибутов S.M.A.R.T может быть представлена несколькими сообщениями.

Для обработки этой ситуации используется механизм таймаута. Как только приходит сообщение, запускается таймаут длительностью одну секунду, в течение которого от пользователя ожидается следующее сообщение. При этом сообщение сохраняется в базе данных вместе с идентификатором таймера. Если в течение этой секунды не поступает новое сообщение, таймаут обнуляется, а все данные, связанные с ним, также удаляются из базы данных. Когда собирается достаточное количество информации, производится анализ сообщений на наличие данных о состоянии атрибутов S.M.A.R.T с помощью метода checkSmart.
Наконец, метод checkSmart выполняет анализ показателей S.M.A.R.T и определяет общий статус диска. После формирования ответа происходит вызов метода sendMessage для отправки сообщения в сервис tg-smart, которое затем передается в чат Telegram.

Заключение


Вот так устроен и работает созданный бот для проверки дисков. Могу предположить, что появится вопрос про пороговые значения — отвечу сразу.

Я понимаю, что вопрос пороговых значений атрибутов S.M.A.R.T вызывает интерес. Но Selectel придерживается строгой политики конфиденциальности и безопасности данных. В связи с этим мы не можем раскрыть подробные пороговые значения по каждой модели накопителя. Вы можете настроить свои системы мониторинга, исходя из базовых параметров, которые я указал в прошлой статье.

Описанный бот — небольшая функциональность, которая облегчает жизнь инженерам дата-центров. Возможности мониторинга состояния инфраструктуры для клиентов мы реализуем на другом уровне. Сейчас мы рассматриваем возможность добавления новых функциональностей и улучшений в ближайшем будущем, в том числе за счет внедрения модуля IPMI. Это даст пользователям еще больше инструментов для мониторинга и управления своими ресурсами.

Другие полезные материалы


   

Комментарии (12)


  1. TwenKey
    17.08.2023 13:02
    +1

    Свои решения это всегда круто. У себя для этого используем smarctl exporter - prometheus - grafana. К ней можно прикрутить уже плагины на slack и прочие сервисы, или пользоваться alertManager.


    1. dkhamchenko Автор
      17.08.2023 13:02
      +1

      В компании для внутренних сервисов и облачной инфраструктуры активно используется Zabbix. Он интегрирован с пользователем alertManager в Jira, что позволяет автоматически создавать задачи. Далее, эти задачи дублируются в проекте инженеров для согласования и оперативных действий с дежурной сменой. Это позволяет довольно быстро и эффективно реагировать на проблемы и производить замены комплектующих даже в выходные и праздничные дни. Этот процесс также распространен на другие компоненты инфраструктуры.


  1. esche_odin
    17.08.2023 13:02
    +1

    троллейбус_буханка.джипег


    1. dkhamchenko Автор
      17.08.2023 13:02
      +2

      Ради искусства. Вы еще не видели, что способен сотворить инженер из пачкордов.

      Спойлер

      Например, плетку.


      1. Wesha
        17.08.2023 13:02

        Джунов на галере подгонять?


  1. Wesha
    17.08.2023 13:02

    У меня только один вопрос: а что Вы делать будете, если когда этот Ваш Телеграм — ффсё?


    1. dkhamchenko Автор
      17.08.2023 13:02
      +2

      В компании существует отдел, занимающийся внутренними сервисами и вспомогательными инструментами. Думаю, в какой-то момент вся эта история перейдет в общий web-сервис со множеством вспомогательных инструментоа. Ну а пока боты, созданные инженерами в Телеграме, являются быстрым решением их задач.


  1. AlexandreFrolov
    17.08.2023 13:02

    Я у себя на сервисе настроил мониторинг всех серверов через Zabbix, в том числе состояние дисков. При этом в зависимости от уровней серьезности сообщения отправляются либо на E-Mail, либо на E-Mail+SMS, либо на E-Mail+SMS+Звонок_синтезированным_голосом+Отправка_в_Телеграм

    И все, это, конечно, можно настраивать.
    Для отправки SMS и голосовых звонков используются разные провайдеры.


    1. dkhamchenko Автор
      17.08.2023 13:02

      Как я писал в комментарии выше, в компании также используется Zabbix. Из идей: можно было бы клиентам услуг Selectel добавлять интеграции с тикет системой. Например, по API создавать тикеты о планировании замены дисков или других комплектующих.


      1. AlexandreFrolov
        17.08.2023 13:02

        Тикеты да, создавать можно, только вот стоит ли это автоматизировать?
        Обычно когда я получаю критичные сообщения от Zabbix, то вначале пытаюсь понять сам, что случилось и что делать.

        Вот если бы у вас была служба администрирования серверов, которой можно было бы поручить решение подобных задач, то тут да, им можно было бы отправлять такие тикеты, созданные zabbix.


        1. dkhamchenko Автор
          17.08.2023 13:02

          С одной стороны, да, в каждом тикете еще необходимо потвердеть действия, к которым мог бы приступить инженер. С другой, все зависит от количества арендуемых серверов и количества затраченных ресурсов. Для тех, у кого парк из десятков серверов, довольно длительно разбирать каждый. При правильно настроенном процессе на своевременную замену несправной комплектующей (блок питания, CPU, ОЗУ, диски, оптические трансиверы) риск возникновения проблем снижается.

          К сожалению, услуги по администрированию сейчас не предоставляются. Техническая поддержка старается предлагать помощь партнеров.


          1. AlexandreFrolov
            17.08.2023 13:02

            К сожалению, когда после перезагрузки одного из моих арендованных серверов произошла остановка из-за проблем с RAID-массивом, мне в тикете ничего такого не предложили - никаких партнеров. Хорошо что мне помог один мой хороший знакомый, а то была бы беда.

            Полагаю, было бы неплохо вашей компании пересмотреть возможность предоставления подобных услуг. На мой взгляд, было бы очень даже ориентировано на клиентов!