NetBox как единый источник правды: как мы задокументировали 2000 устройств для ДЦ-оператора «ДатаЦентрПро»

Ситуация: 2000 устройств в Excel и головах инженеров

ДЦ-оператор «ДатаЦентрПро» обратился к нам в itfresh.ru с классической проблемой: инфраструктура выросла до 2000 устройств (серверы, коммутаторы, роутеры, PDU, патч-панели), а документация осталась в Excel-файлах и Confluence-страницах, которые устарели на 6-12 месяцев.

Масштаб проблемы:

  • 3 площадки (2 собственных ДЦ + 1 арендованный колокейшн)
  • 148 стоек суммарно
  • ~2000 устройств (серверы, СХД, сетевое оборудование, PDU)
  • 24 VLAN в production, 8 в management
  • 12 подсетей /16-/24, часть пересекается между площадками
  • 5 инженеров, каждый ведёт свою часть в своём формате

Конкретные инциденты из-за плохой документации:

  1. IP-конфликт — два сервера получили один IP, оба упали. В Excel этот IP был «свободен»
  2. Потерянный сервер — физически стоит в стойке, но никто не знает чей он и что на нём работает. Нашли через 3 дня по MAC-адресу
  3. Неверная коммутация — при замене коммутатора подключили кабели не в те порты, потому что схема коммутации была нарисована от руки год назад

Установка NetBox в Docker и базовая настройка

NetBox — open-source DCIM/IPAM платформа, которая стала стандартом де-факто для документирования инфраструктуры. Мы развернули NetBox 3.7 через официальный docker-compose:

# Клонируем официальный docker-compose
git clone -b release https://github.com/netbox-community/netbox-docker.git
cd netbox-docker

# Конфигурация через .env
cat > .env << 'EOF'
SUPERUSER_NAME=admin
SUPERUSER_EMAIL=netbox@datacenter-pro.ru
SUPERUSER_PASSWORD=ChangeMeStr0ng!
ALLOWED_HOSTS=netbox.datacenter-pro.ru localhost
DB_NAME=netbox
DB_USER=netbox
DB_PASSWORD=SecureDBPass2026!
REDIS_PASSWORD=RedisSecure2026!
SECRET_KEY=$(python3 -c 'import secrets; print(secrets.token_urlsafe(50))')
TIME_ZONE=Europe/Moscow
EOF

# Переопределяем для production
cat > docker-compose.override.yml << 'EOF'
services:
  netbox:
    ports:
      - "127.0.0.1:8080:8080"
    environment:
      - REMOTE_AUTH_ENABLED=true
      - REMOTE_AUTH_BACKEND=netbox.authentication.LDAPBackend
      - LOGIN_REQUIRED=true
    volumes:
      - ./custom_config.py:/etc/netbox/config/custom.py:ro

  postgres:
    volumes:
      - netbox-postgres-data:/var/lib/postgresql/data
    environment:
      - POSTGRES_INITDB_ARGS=--encoding=UTF-8 --lc-collate=C --lc-ctype=C

  redis:
    sysctls:
      - net.core.somaxconn=1024

volumes:
  netbox-postgres-data:
    driver: local
EOF

# Запуск
docker compose up -d

# Проверка
docker compose ps
curl -s http://localhost:8080/api/ | jq '.status'

Перед Netbox поставили Nginx с SSL (Let's Encrypt) для доступа по https://netbox.datacenter-pro.ru. LDAP-аутентификация позволила всем инженерам входить с корпоративными учётными данными.

Модель данных: sites, racks, devices, cables

NetBox имеет иерархическую модель данных DCIM. Мы заполнили её снизу вверх:

1. Sites (площадки):

# Создание площадок через API (pynetbox)
import pynetbox

nb = pynetbox.api('https://netbox.datacenter-pro.ru', token='your-api-token-here')

sites = [
    {'name': 'DC-MSK-1', 'slug': 'dc-msk-1', 'status': 'active',
     'region': {'name': 'Moscow'}, 'facility': 'DataLine OST',
     'physical_address': 'г. Москва, ул. Боровая, д. 7',
     'description': 'Основная площадка, 80 стоек'},
    {'name': 'DC-MSK-2', 'slug': 'dc-msk-2', 'status': 'active',
     'region': {'name': 'Moscow'}, 'facility': 'Собственный ДЦ',
     'physical_address': 'г. Москва, Южнопортовая, д. 15',
     'description': 'Резервная площадка, 48 стоек'},
    {'name': 'DC-SPB-1', 'slug': 'dc-spb-1', 'status': 'active',
     'region': {'name': 'Saint Petersburg'}, 'facility': 'Selectel',
     'description': 'Колокейшн, 20 стоек'},
]
for site in sites:
    nb.dcim.sites.create(**site)

2. Racks (стойки) — для каждой площадки создали стойки с указанием высоты (42U/47U), максимальной мощности и расположения в зале:

# Массовое создание стоек
for row in range(1, 11):  # 10 рядов
    for rack_num in range(1, 9):  # 8 стоек в ряду
        nb.dcim.racks.create(
            name=f'MSK1-R{row:02d}-{rack_num:02d}',
            site={'slug': 'dc-msk-1'},
            status='active',
            u_height=42,
            width=600,
            outer_depth=1200,
            max_weight=800,
            comments=f'Ряд {row}, место {rack_num}'
        )

3. Devices (устройства) — каждое устройство привязано к стойке с указанием позиции в юнитах:

# Сначала создаём device type (шаблон оборудования)
device_type = nb.dcim.device_types.create(
    manufacturer={'name': 'Dell'},
    model='PowerEdge R750',
    slug='dell-poweredge-r750',
    u_height=1,
    is_full_depth=True,
    comments='2x Xeon 8380, до 2TB RAM'
)

# Затем создаём конкретное устройство
device = nb.dcim.devices.create(
    name='srv-web-01',
    device_type=device_type.id,
    role={'name': 'Web Server'},
    site={'slug': 'dc-msk-1'},
    rack={'name': 'MSK1-R03-05'},
    position=25,           # юнит в стойке
    face='front',
    status='active',
    serial='JKXYZ123456',
    asset_tag='DCP-SRV-0847',
    tenant={'name': 'ClientA'},
    primary_ip4={'address': '10.10.3.25/24'}
)

4. Cables (кабели) — соединения между портами устройств. NetBox отслеживает полный путь сигнала от сервера до коммутатора через патч-панель:

# Соединение сервера с коммутатором
nb.dcim.cables.create(
    a_terminations=[{'object_type': 'dcim.interface', 'object_id': server_iface.id}],
    b_terminations=[{'object_type': 'dcim.interface', 'object_id': switch_port.id}],
    type='cat6a',
    color='0000ff',  # синий
    length=3,
    length_unit='m',
    label='SRV-WEB-01:eth0 -> SW-TOR-03:Gi0/25'
)

За две недели мы внесли все 2000 устройств. Для ускорения использовали CSV-импорт NetBox и скрипты миграции из Excel.

IPAM: управление IP-адресами и VLAN

IPAM (IP Address Management) — одна из главных ценностей NetBox. Вместо Excel-таблицы с IP-адресами — полноценная база с валидацией, поиском и историей изменений.

Структура IP-пространства:

# Создание RIR и агрегатов
nb.ipam.rirs.create(name='RIPE NCC', slug='ripe-ncc')

# Агрегаты — верхнеуровневые блоки
nb.ipam.aggregates.create(
    prefix='10.0.0.0/8',
    rir={'slug': 'ripe-ncc'},
    description='Private RFC1918'
)

# Префиксы (подсети) с привязкой к VLAN и площадке
prefixes = [
    {'prefix': '10.10.0.0/16', 'site': 'dc-msk-1', 'description': 'MSK-1 Internal'},
    {'prefix': '10.10.1.0/24', 'vlan': {'vid': 10}, 'role': 'Management',
     'site': 'dc-msk-1', 'description': 'Management network'},
    {'prefix': '10.10.2.0/24', 'vlan': {'vid': 20}, 'role': 'Production',
     'site': 'dc-msk-1', 'description': 'Web servers'},
    {'prefix': '10.10.3.0/24', 'vlan': {'vid': 30}, 'role': 'Production',
     'site': 'dc-msk-1', 'description': 'Database servers'},
    {'prefix': '10.10.100.0/24', 'vlan': {'vid': 100}, 'role': 'Storage',
     'site': 'dc-msk-1', 'description': 'iSCSI/NFS storage network'},
]
for p in prefixes:
    nb.ipam.prefixes.create(**p)

VLAN management:

# VLAN-группы по площадкам
for site_slug in ['dc-msk-1', 'dc-msk-2', 'dc-spb-1']:
    nb.ipam.vlan_groups.create(
        name=f'VLANs-{site_slug}',
        slug=f'vlans-{site_slug}',
        scope_type='dcim.site',
        scope_id=nb.dcim.sites.get(slug=site_slug).id,
        min_vid=1,
        max_vid=4094
    )

# Создание VLAN с привязкой к группе
vlans = [
    {'vid': 10, 'name': 'MGMT', 'status': 'active', 'description': 'Management'},
    {'vid': 20, 'name': 'WEB', 'status': 'active', 'description': 'Web production'},
    {'vid': 30, 'name': 'DB', 'status': 'active', 'description': 'Database'},
    {'vid': 40, 'name': 'BACKUP', 'status': 'active', 'description': 'Backup network'},
    {'vid': 100, 'name': 'STORAGE', 'status': 'active', 'description': 'iSCSI/NFS'},
    {'vid': 999, 'name': 'QUARANTINE', 'status': 'active', 'description': 'Isolated'},
]

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

Circuit tracking — отслеживание каналов связи от провайдеров:

# Учёт каналов связи
nb.circuits.providers.create(name='Ростелеком', slug='rostelecom')
nb.circuits.providers.create(name='МТС', slug='mts')

nb.circuits.circuits.create(
    cid='RT-MSK-2026-04521',
    provider={'slug': 'rostelecom'},
    type={'slug': 'internet'},
    status='active',
    commit_rate=10000,  # 10 Gbps
    description='Основной аплинк DC-MSK-1'
)

API-автоматизация через pynetbox и webhooks

NetBox — это API-first платформа. Всё, что можно сделать в GUI, доступно через REST API. Мы написали набор скриптов автоматизации:

1. Автообнаружение новых серверов — скрипт, который сканирует сеть и создаёт устройства в NetBox:

#!/usr/bin/env python3
"""auto_discover.py — обнаружение новых серверов и регистрация в NetBox"""
import pynetbox
import subprocess
import json

nb = pynetbox.api('https://netbox.datacenter-pro.ru', token='api-token')

def scan_subnet(subnet):
    """Сканирует подсеть и возвращает активные хосты"""
    result = subprocess.run(
        ['nmap', '-sn', '-oJ', '-', subnet],
        capture_output=True, text=True
    )
    # Парсим результат
    active_ips = []
    for line in result.stdout.splitlines():
        if 'Up' in line:
            ip = line.split()[0]
            active_ips.append(ip)
    return active_ips

def check_and_register(ip):
    """Проверяет, есть ли IP в NetBox; если нет — создаёт как discovered"""
    existing = nb.ipam.ip_addresses.filter(address=ip)
    if not list(existing):
        nb.ipam.ip_addresses.create(
            address=f'{ip}/24',
            status='active',
            description='Auto-discovered, needs assignment',
            tags=[{'name': 'auto-discovered'}]
        )
        print(f'[NEW] {ip} зарегистрирован в NetBox')
    else:
        print(f'[OK] {ip} уже в NetBox')

# Сканируем management-подсети
for subnet in ['10.10.1.0/24', '10.20.1.0/24', '10.30.1.0/24']:
    for ip in scan_subnet(subnet):
        check_and_register(ip)

2. Webhook-триггеры — NetBox отправляет уведомления при изменениях:

# Webhook: при создании/изменении устройства — обновить DNS
# Настройка через NetBox UI: Operations → Webhooks
{
    "name": "update-dns-on-device-change",
    "content_types": ["dcim.device"],
    "type_create": true,
    "type_update": true,
    "enabled": true,
    "url": "https://automation.datacenter-pro.ru/hooks/netbox-device",
    "http_method": "POST",
    "http_content_type": "application/json",
    "body_template": "{{ data | tojson }}",
    "secret": "webhook-secret-key",
    "conditions": {
        "and": [
            {"attr": "status.value", "value": "active"}
        ]
    }
}

3. Custom fields — расширяем модель данных под нужды «ДатаЦентрПро»:

# Custom fields через API
custom_fields = [
    {'name': 'warranty_expires', 'type': 'date', 'label': 'Гарантия до',
     'content_types': ['dcim.device']},
    {'name': 'monthly_cost', 'type': 'decimal', 'label': 'Стоимость/мес (₽)',
     'content_types': ['dcim.device']},
    {'name': 'responsible_engineer', 'type': 'text', 'label': 'Ответственный',
     'content_types': ['dcim.device', 'dcim.rack']},
    {'name': 'backup_policy', 'type': 'select', 'label': 'Политика бэкапа',
     'content_types': ['dcim.device'],
     'choices': ['daily', 'weekly', 'monthly', 'none']},
]
for cf in custom_fields:
    nb.extras.custom_fields.create(**cf)

Интеграция с Ansible: dynamic inventory

Ключевая интеграция — использование NetBox как dynamic inventory для Ansible. Вместо ручного поддержания файла hosts.ini, Ansible запрашивает список серверов напрямую из NetBox.

Устанавливаем коллекцию и настраиваем:

# Установка коллекции
ansible-galaxy collection install netbox.netbox

# netbox_inventory.yml — файл inventory
plugin: netbox.netbox.nb_inventory
api_endpoint: https://netbox.datacenter-pro.ru
token: "{{ lookup('env', 'NETBOX_TOKEN') }}"
validate_certs: true

# Группировка хостов
group_by:
  - site
  - device_role
  - platform
  - tenant
  - tag

# Маппинг переменных из NetBox в Ansible host vars
compose:
  ansible_host: primary_ip4.address | ansible.utils.ipaddr('address')
  ansible_user: "'ansible'"
  datacenter: site.name
  rack_name: rack.name
  rack_position: position
  device_serial: serial
  warranty_date: custom_fields.warranty_expires

# Фильтрация — только active устройства
query_filters:
  - status: active
  - has_primary_ip: true
  - region: Moscow

Теперь Ansible-плейбуки автоматически применяются к правильным серверам:

# Обновление всех web-серверов на площадке MSK-1
ansible-playbook -i netbox_inventory.yml update_nginx.yml \
  --limit 'site_dc_msk_1:&device_role_web_server'

# Результат: Ansible получает список из NetBox:
# srv-web-01 (10.10.2.25)
# srv-web-02 (10.10.2.26)
# srv-web-03 (10.10.2.27)
# ...и применяет плейбук только к ним

# Бэкап конфигурации всех коммутаторов
ansible-playbook -i netbox_inventory.yml backup_switches.yml \
  --limit 'device_role_switch'

# Применить security-патч только к серверам клиента "ClientA"
ansible-playbook -i netbox_inventory.yml security_patch.yml \
  --limit 'tenant_clienta'

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

Миграция с Excel: пошаговый процесс

Миграция 2000 устройств из Excel в NetBox — нетривиальная задача. Мы разработали поэтапный процесс:

Этап 1: Нормализация данных (3 дня). В Excel-файлах данные были в произвольном формате. Мы написали скрипт нормализации:

#!/usr/bin/env python3
"""normalize_excel.py — приводим Excel к формату NetBox"""
import pandas as pd
import re

df = pd.read_excel('inventory_msk1.xlsx')

# Нормализация имён серверов: 'Web Server #3' -> 'srv-web-03'
def normalize_name(raw):
    raw = raw.lower().strip()
    raw = re.sub(r'[^a-z0-9-]', '-', raw)
    raw = re.sub(r'-+', '-', raw).strip('-')
    return raw

df['normalized_name'] = df['hostname'].apply(normalize_name)

# Нормализация IP: убираем пробелы, проверяем формат
def normalize_ip(raw):
    if pd.isna(raw):
        return None
    ip = str(raw).strip()
    parts = ip.split('.')
    if len(parts) != 4:
        return None  # пометить для ручной проверки
    return ip

df['normalized_ip'] = df['ip_address'].apply(normalize_ip)

# Поиск дубликатов IP
duplicates = df[df.duplicated(subset='normalized_ip', keep=False)]
if not duplicates.empty:
    print(f'ВНИМАНИЕ: {len(duplicates)} дубликатов IP!')
    print(duplicates[['hostname', 'normalized_ip']].to_string())

# Экспорт в CSV для импорта в NetBox
df.to_csv('netbox_import_ready.csv', index=False)

Этап 2: Импорт через CSV (2 дня). NetBox поддерживает CSV-импорт для всех объектов. Формат CSV для устройств:

name,device_role,device_type,site,rack,position,face,status,serial,asset_tag
srv-web-01,Web Server,Dell PowerEdge R750,DC-MSK-1,MSK1-R03-05,25,front,active,JKXYZ123456,DCP-0847
srv-web-02,Web Server,Dell PowerEdge R750,DC-MSK-1,MSK1-R03-05,26,front,active,JKXYZ123457,DCP-0848
sw-tor-01,ToR Switch,Cisco Nexus 93180YC-FX,DC-MSK-1,MSK1-R03-05,42,front,active,FOC2345678,DCP-NET-0123

Этап 3: Верификация (5 дней). Самый важный этап — физический обход стоек с планшетом и сверка данных в NetBox с реальностью. Нашли 87 несоответствий (4.3% от общего числа), исправили.

Этап 4: Процедуры поддержания актуальности. Правило: ни одно изменение в инфраструктуре не выполняется без предварительного обновления NetBox. Это закреплено в change management процессе и проверяется на weekly review.

Результаты и выводы

Через 2 месяца после внедрения NetBox для «ДатаЦентрПро»:

МетрикаДо (Excel)После (NetBox)
Время поиска информации об устройстве5-15 мин10 сек
IP-конфликты в месяц3-40
Точность документации~65%97%+
Время онбординга нового инженера2 недели2 дня
Время планирования размещения сервера30-60 мин5 мин
Ручных обновлений Ansible inventoryежедневно0 (автоматически)

NetBox стал единым источником правды (source of truth) для всей инфраструктуры. Все инструменты — Ansible, Zabbix, скрипты автоматизации — получают данные из NetBox через API. Человек обновляет информацию в одном месте, и она автоматически распространяется по всей цепочке.

Главный вывод: NetBox — это не просто «красивый Excel». Это платформа, которая меняет процессы: от «документация как артефакт» к «документация как код». Если у вас более 50 устройств и вы ещё не используете NetBox — вы теряете время и деньги на поиск информации. Обращайтесь к нам в itfresh.ru, поможем внедрить.

Часто задаваемые вопросы

NetBox — полностью бесплатный open-source (Apache 2.0 лицензия). Из альтернатив: RackTables (устарел), phpIPAM (только IPAM, без DCIM), Device42 (платный, от $1000/год). Для большинства задач NetBox — лучший выбор по соотношению функциональности и стоимости.
Для инфраструктуры до 5000 объектов достаточно: 2 CPU, 4 GB RAM, 20 GB SSD. NetBox работает на Django + PostgreSQL + Redis. В Docker-установке всё запускается одной командой. Для 50 000+ объектов рекомендуем 4 CPU, 8 GB RAM и отдельный PostgreSQL-сервер.
Три подхода: 1) процессный — правило, что любое изменение сначала вносится в NetBox; 2) технический — скрипты автообнаружения, которые сравнивают реальность с NetBox и создают отчёт о расхождениях; 3) ревизионный — ежемесячный физический обход стоек с планшетом и сверка. Самый эффективный — комбинация всех трёх.
Да, NetBox поддерживает виртуальные машины, кластеры и облачные сервисы. Для AWS/GCP/Yandex Cloud можно создавать объекты через API, синхронизируя с Terraform state. Однако для чисто облачной инфраструктуры проще использовать нативные инструменты облака. NetBox максимально полезен для гибридных и on-premise сред.
Пошагово: 1) экспорт Excel в CSV; 2) нормализация данных скриптом (единый формат имён, IP, серийных номеров); 3) валидация — поиск дубликатов и конфликтов; 4) импорт в NetBox через CSV-импорт или pynetbox API; 5) верификация — физическая сверка с реальностью. Для 2000 устройств весь процесс занимает 2-3 недели.

Нужна помощь с проектом?

Специалисты АйТи Фреш помогут с архитектурой, DevOps, безопасностью и разработкой — 15+ лет опыта

📞 Связаться с нами
#NetBox#IPAM#DCIM#infrastructure documentation#pynetbox#Ansible dynamic inventory#VLAN management#source of truth
Комментарии 0

Оставить комментарий

загрузка...