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
Див. також¶
- Docker Compose — основи Compose
- RouterOS основи — робота з MikroTik
- Бази даних — PostgreSQL
- Caddy — reverse proxy для production
Шлях: infrastructure/mikro-container.md