MikroTik Management Container

Практичний приклад того, як контейнеризувати сервіс для управління мережевим обладнанням MikroTik. Цей приклад демонструє типову архітектуру Python/FastAPI застосунку в Docker.

Що це робить?

Сервіс для централізованого управління MikroTik роутерами:

┌─────────────────────────────────────────────────────────────────┐
│                     MikroTik Management                         │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│   ┌───────────┐     ┌─────────────────┐     ┌──────────────┐   │
│   │  Web UI   │────▶│  FastAPI        │────▶│  PostgreSQL  │   │
│   │ (Browser) │     │  Backend        │     │  (metadata)  │   │
│   └───────────┘     └────────┬────────┘     └──────────────┘   │
│                              │                                  │
│                              │ RouterOS API                     │
│                              ▼                                  │
│         ┌────────────────────────────────────────┐             │
│         │                                        │             │
│         ▼              ▼              ▼          │             │
│   ┌──────────┐  ┌──────────┐  ┌──────────┐      │             │
│   │ Router 1 │  │ Router 2 │  │ Router N │      │             │
│   │ hAP ax3  │  │ CCR2004  │  │  RB5009  │      │             │
│   └──────────┘  └──────────┘  └──────────┘      │             │
│                                                  │             │
└─────────────────────────────────────────────────────────────────┘

Можливості:
- Збір статистики з роутерів (трафік, клієнти, ресурси)
- Масове виконання команд
- Бекап конфігурацій
- Моніторинг стану мережі
- API для інтеграції з іншими системами

Структура проєкту

mikrotik-manager/
├── docker-compose.yml
├── Dockerfile
├── .env
├── .env.example
├── requirements.txt
├── app/
│   ├── __init__.py
│   ├── main.py           # FastAPI application
│   ├── config.py         # Settings
│   ├── database.py       # Database connection
│   ├── models/           # SQLAlchemy models
│   │   ├── __init__.py
│   │   └── router.py
│   ├── routers/          # API endpoints
│   │   ├── __init__.py
│   │   ├── devices.py
│   │   └── commands.py
│   └── services/         # Business logic
│       ├── __init__.py
│       └── mikrotik.py   # RouterOS API client
└── tests/
    └── ...

Dockerfile

# Dockerfile

# Базовий образ з Python
FROM python:3.12-slim

# Метадані
LABEL maintainer="umtc@example.com"
LABEL description="MikroTik Management Service"

# Змінні середовища
ENV PYTHONDONTWRITEBYTECODE=1 \
    PYTHONUNBUFFERED=1 \
    PIP_NO_CACHE_DIR=1 \
    PIP_DISABLE_PIP_VERSION_CHECK=1

# Робоча директорія
WORKDIR /app

# Системні залежності
RUN apt-get update && apt-get install -y --no-install-recommends \
    gcc \
    libpq-dev \
    && rm -rf /var/lib/apt/lists/*

# Python залежності (окремо для кешування)
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# Код застосунку
COPY app/ ./app/

# Непривілейований користувач
RUN useradd --create-home --shell /bin/bash appuser
USER appuser

# Порт
EXPOSE 8000

# Healthcheck
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
    CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8000/health')"

# Запуск
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]

Пояснення Dockerfile

┌─────────────────────────────────────────────────────────────────┐
│                    ШАРИ DOCKER IMAGE                            │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│   ┌─────────────────────────────────────────────────────────┐  │
│   │  CMD uvicorn...                              (runtime)  │  │
│   ├─────────────────────────────────────────────────────────┤  │
│   │  COPY app/ ./app/                          (ваш код)    │  │
│   ├─────────────────────────────────────────────────────────┤  │
│   │  pip install -r requirements.txt      (залежності)      │  │
│   ├─────────────────────────────────────────────────────────┤  │
│   │  apt-get install gcc libpq-dev      (системні пакети)   │  │
│   ├─────────────────────────────────────────────────────────┤  │
│   │  python:3.12-slim                    (базовий образ)    │  │
│   └─────────────────────────────────────────────────────────┘  │
│                                                                 │
│   Шари кешуються! Змінюється код → перебудовується тільки      │
│   верхній шар. Тому requirements.txt копіюємо окремо.          │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

docker-compose.yml

services:
  # Основний сервіс
  app:
    build:
      context: .
      dockerfile: Dockerfile
    container_name: mikrotik-manager
    restart: unless-stopped
    ports:
      - "8000:8000"
    environment:
      - DATABASE_URL=postgresql://mikrotik:${DB_PASSWORD}@db:5432/mikrotik
      - SECRET_KEY=${SECRET_KEY}
      - LOG_LEVEL=${LOG_LEVEL:-INFO}
    depends_on:
      db:
        condition: service_healthy
    networks:
      - internal
      - management  # Мережа до роутерів
    healthcheck:
      test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8000/health')"]
      interval: 30s
      timeout: 10s
      retries: 3

  # База даних
  db:
    image: postgres:15-alpine
    container_name: mikrotik-db
    restart: unless-stopped
    environment:
      POSTGRES_USER: mikrotik
      POSTGRES_PASSWORD: ${DB_PASSWORD}
      POSTGRES_DB: mikrotik
    volumes:
      - postgres_data:/var/lib/postgresql/data
    networks:
      - internal
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U mikrotik"]
      interval: 10s
      timeout: 5s
      retries: 5

  # Redis для кешу та черг (опціонально)
  redis:
    image: redis:7-alpine
    container_name: mikrotik-redis
    restart: unless-stopped
    volumes:
      - redis_data:/data
    networks:
      - internal
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 5s
      retries: 5

volumes:
  postgres_data:
  redis_data:

networks:
  internal:
    driver: bridge
  management:
    external: true  # Зовнішня мережа для доступу до роутерів

Файл оточення

.env.example

# Database
DB_PASSWORD=change_me_in_production

# Application
SECRET_KEY=generate_with_openssl_rand_hex_32
LOG_LEVEL=INFO

# MikroTik defaults (можна перевизначити per-device)
MIKROTIK_DEFAULT_USER=admin
MIKROTIK_DEFAULT_PORT=8728

Генерація секретів

# SECRET_KEY
openssl rand -hex 32

# DB_PASSWORD
openssl rand -base64 24

requirements.txt

fastapi==0.109.0
uvicorn[standard]==0.27.0
sqlalchemy==2.0.25
psycopg2-binary==2.9.9
alembic==1.13.1
pydantic-settings==2.1.0
librouteros==3.2.1
redis==5.0.1
httpx==0.26.0

Приклад коду

app/main.py

from fastapi import FastAPI
from contextlib import asynccontextmanager

from app.database import engine
from app.models import Base
from app.routers import devices, commands


@asynccontextmanager
async def lifespan(app: FastAPI):
    # Startup
    async with engine.begin() as conn:
        await conn.run_sync(Base.metadata.create_all)
    yield
    # Shutdown
    await engine.dispose()


app = FastAPI(
    title="MikroTik Manager",
    description="Centralized MikroTik management API",
    version="1.0.0",
    lifespan=lifespan,
)

app.include_router(devices.router, prefix="/api/devices", tags=["devices"])
app.include_router(commands.router, prefix="/api/commands", tags=["commands"])


@app.get("/health")
async def health_check():
    return {"status": "healthy"}

app/services/mikrotik.py

import librouteros
from librouteros import connect
from typing import Optional


class MikroTikClient:
    """Клієнт для роботи з RouterOS API."""

    def __init__(self, host: str, username: str, password: str, port: int = 8728):
        self.host = host
        self.username = username
        self.password = password
        self.port = port
        self._api: Optional[librouteros.Api] = None

    def connect(self) -> "MikroTikClient":
        self._api = connect(
            host=self.host,
            username=self.username,
            password=self.password,
            port=self.port,
        )
        return self

    def disconnect(self):
        if self._api:
            self._api.close()

    def __enter__(self):
        return self.connect()

    def __exit__(self, *args):
        self.disconnect()

    def get_identity(self) -> str:
        """Отримати ім'я роутера."""
        result = list(self._api.path("/system/identity"))
        return result[0]["name"] if result else "unknown"

    def get_resources(self) -> dict:
        """Отримати інформацію про ресурси."""
        result = list(self._api.path("/system/resource"))
        return result[0] if result else {}

    def get_interfaces(self) -> list:
        """Отримати список інтерфейсів."""
        return list(self._api.path("/interface"))

    def run_command(self, path: str, **kwargs) -> list:
        """Виконати довільну команду."""
        return list(self._api.path(path, **kwargs))

Як запустити

Локальна розробка

# Клонувати та перейти в директорію
cd mikrotik-manager

# Створити .env файл
cp .env.example .env
# Відредагувати .env, встановити паролі

# Створити мережу для доступу до роутерів (якщо потрібно)
docker network create management

# Запустити
docker compose up -d

# Перевірити логи
docker compose logs -f app

# Відкрити API docs
# http://localhost:8000/docs

Production

# Використовувати production compose файл
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d

# Або з Caddy reverse proxy
# Caddy автоматично отримає SSL сертифікат

docker-compose.prod.yml

services:
  app:
    restart: always
    environment:
      - LOG_LEVEL=WARNING
    deploy:
      resources:
        limits:
          cpus: '1'
          memory: 512M

Локальна розробка

Для зручної розробки з hot-reload:

docker-compose.override.yml

services:
  app:
    build:
      context: .
      target: development  # Якщо є multi-stage build
    volumes:
      - ./app:/app/app:ro  # Монтуємо код
    environment:
      - LOG_LEVEL=DEBUG
    command: uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload
# Запустити з override (застосовується автоматично)
docker compose up

# Тепер зміни в коді автоматично перезавантажують сервер

Корисні команди

# Зайти в контейнер
docker compose exec app bash

# Виконати міграції
docker compose exec app alembic upgrade head

# Запустити тести
docker compose exec app pytest

# Переглянути логи конкретного сервісу
docker compose logs -f app

# Перебудувати образ
docker compose build --no-cache app

# Оновити та перезапустити
docker compose pull && docker compose up -d

Моніторинг

Додайте endpoint для Prometheus:

# app/routers/metrics.py
from prometheus_client import Counter, Histogram, generate_latest
from fastapi import Response

REQUEST_COUNT = Counter('requests_total', 'Total requests')
REQUEST_LATENCY = Histogram('request_latency_seconds', 'Request latency')

@router.get("/metrics")
async def metrics():
    return Response(generate_latest(), media_type="text/plain")

Типові помилки

Не вдається підключитись до роутера

# Перевірити:
# 1. API увімкнено на роутері
/ip service enable api

# 2. Firewall дозволяє з'єднання
# 3. Правильні credentials
# 4. Контейнер в правильній мережі

База даних не готова

# Використовуйте healthcheck + condition
depends_on:
  db:
    condition: service_healthy

Permission denied при монтуванні

# На Linux: перевірити права
chmod -R 755 ./app

# Або в Dockerfile:
RUN chown -R appuser:appuser /app

Див. також

Шлях: infrastructure/mikro-container.md

UMTC Wiki © 2026 | Ukrainian Military Tactical Communications