Как мы создали топографические карты для пользователей MapMagic. Часть 2: Инфраструктура, автоматизация и экономия

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Всем привет! Я Влад, сооснователь MapMagic — каталога готовых маршрутов и планировщика для пеших походов и велопутешествий.

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

Архитектура обновления карт

Общая схема процесса

Полный цикл обновления карт выглядит так:

Lambda Trigger → Preemptible VM → planet.osm.pbf → Tilemaker → PMTiles → S3

Разберём каждый компонент:

  1. Lambda Trigger — Yandex Cloud Function, которая запускает процесс по расписанию

  2. Preemptible VM — временная виртуальная машина генерации тайлов

  3. planet.osm.pbf — источник свежих данных OpenStreetMap

  4. Tilemaker — генератор векторных тайлов (о нём подробно в первой статье)

  5. PMTiles — итоговый файл с тайлами в оптимизированном формате

  6. S3 — объектное хранилище Yandex Cloud

Почему Preemptible VM

Preemptible VM (прерываемые виртуальные машины) — это ключевой элемент нашей стратегии экономии. В Yandex Cloud они стоят существенно дешевле обычных инстансов.

Трейдофф в том, что такая ВМ может быть принудительно остановлена в любой момент и не может работать дольше 24 часов. Но для нашей задачи это не проблема — если процесс прервался, просто запускаем заново.

Ещё один важный момент — авто-удаление дисков. Когда ВМ удаляется, все подключенные диски с флагом auto_delete тоже удаляются.

Пример конфигурации из python скрипта создания ВМ:

# Создаём политику планирования для прерываемой ВМ
scheduling_policy = SchedulingPolicy(
    preemptible=config['preemptible']  # True по умолчанию
)

# Ресурсы
resources_spec=ResourcesSpec(
    memory=config['vm_memory_gb'] * 1024 * 1024 * 1024,
    cores=config['vm_cores'],
    core_fraction=config['vm_core_fraction']
)

# Загрузочный диск с авто-удалением
boot_disk_spec=AttachedDiskSpec(
    auto_delete=config['auto_delete_disks'],
    disk_spec=AttachedDiskSpec.DiskSpec(
        type_id='network-ssd',
        size=config['boot_disk_size_gb'] * 1024 * 1024 * 1024,
        image_id=config['image_id']
    )
)

Параметры реальной VM, используемой для генерации тайлов для всего мира:

  • 24 vCPU, 240 GB RAM

  • 50 GB загрузочный SSD

  • 1023 GB кэш-диск (network-ssd-nonreplicated) — для хранения входных и выходных данных

HTTP API для управления генерацией

На ВМ крутится простой HTTP-сервер, который принимает команды:

Endpoint

Метод

Описание

/status

GET

Статус генерации (ready/running/done/error)

/run

POST

Запуск генерации с URL на PBF-файл

/kill

POST

Принудительная остановка

/log

GET

Логи процесса

/health

GET

Health check

Пример использования:

# Запуск генерации
curl -X POST http://vm-ip:8000/run \
  -H "Content-Type: application/json" \
  -d '{"url": "https://planet.maps.mail.ru/pbf/planet-latest.osm.pbf"}'

# Проверка статуса
curl http://vm-ip:8000/status

# Получение логов
curl http://vm-ip:8000/log

Оркестрация через Lambda функции

За автоматизацию всего процесса отвечают две Yandex Cloud Functions.

vm_creator.py — создание ВМ

Первая функция создаёт виртуальную машину с нужными параметрами. Она вызывается по расписанию (раз в неделю) или вручную.

Ключевой элемент — cloud-init конфигурация, которая выполняется при первом запуске ВМ:

metadata={
    'user-data': f"""#cloud-config
users:
  - name: {config['vm_user']}
    sudo: ['ALL=(ALL) NOPASSWD:ALL']
    ssh-authorized-keys:
      - {config['ssh_public_key']}

runcmd:
  # Монтируем кэш-диск
  - mkfs.ext4 -F /dev/vdb
  - mount /dev/vdb /mnt/cache
  - echo "/dev/vdb /mnt/cache ext4 defaults,nofail 0 2" >> /etc/fstab

  # Клонируем репозиторий
  - cd /mnt/cache
  - sudo -u {config['vm_user']} git clone {config['git_repo']}

  # Запускаем сервер
  - cd $REPO_DIR
  - sudo -u {config['vm_user']} nohup python3 server.py > /mnt/cache/server.log 2>&1 &
"""
}

Что происходит при старте:

  1. Создаётся пользователь с SSH-ключом

  2. Форматируется и монтируется кэш-диск в /mnt/cache

  3. Клонируется Git-репозиторий с конфигурацией Tilemaker

  4. Запускается HTTP-сервер на порту 8000

Вся настройка занимает около 2-3 минут — после этого ВМ готова принимать команды.

vm_trigger.py — мониторинг и удаление

Вторая функция вызывается каждые нескольку минут и выполняет управление:

  1. Находит ВМ по имени

  2. Проверяет её статус через HTTP API

  3. Запускает генерацию, если ВМ готова

  4. Удаляет ВМ при выполнении условий

Логика принятия решения об удалении:

def should_delete_vm(vm, status_data, config, uptime_minutes, vm_name):
    """Определяет, нужно ли удалять ВМ"""

    uptime_hours = uptime_minutes / 60
    current_status = status_data.get('status')

    # Условие 1: статус "done" — генерация завершена
    if current_status == config['target_status']:
        return True, "target_status_reached"

    # Условие 2: ошибка выполнения скрипта
    if current_status and current_status.startswith('error'):
        return True, "script_error"

    # Условие 3: прошло 20 минут, но сервер не ответил
    if (uptime_minutes >= config['max_startup_minutes'] and
        current_status in ['unknown', 'no_ip', 'connection_failed']):
        return True, "startup_timeout"

    return False, "no_reason"

Итого, три условия для удаления:

  1. Статус “done” — нормальное завершение генерации

  2. Ошибка выполнения скрипта — returncode ≠ 0

  3. Сервер не ответил за 20 минут — что-то пошло не так при старте

Telegram уведомления

Все важные события дублируются в Telegram-чат:

def create_telegram_message(vm_name, vm_id, reason, uptime_minutes, vm_status, ...):
    if action == 'triggered':
        emoji = "						
Источник: https://habr.com/ru/articles/1021284/


Интересные статьи

Интересные статьи

Привет, Хабр! Меня зовут Анастасия Клевцур, я продакт модуля «Структура компании» в Битрикс24. Сегодня расскажу, как мы делали функционал кросс-функциональных команд — решение, которое позволяeт сотру...
Однажды я столкнулся с проблемой, когда почти потерял коммерчески успешный пет-проект из-за устаревших резервных копий БД (ещё до того, как он стал коммерчески неуспешным). При этом, даже после частич...
Сегодня буду писать о новой методике внедрения системы Битрикс и кратко взвесим все за и против.Меня зовут Калашников Роман, предприниматель, собственник интегратора 1С и бизнес-тренер.За все 20 лет я...
Евгений Петриченко пришел в Битрикс24 студентом. За 16 лет в компании он прошел путь от специалиста техподдержки до эксперта по мобильной разработке. И ни разу не думал сменить место работы.  ...
Привет / Merhaba! Я сетевой инженер из Санкт-Петербурга, один год жил и работал в Москве, но всегда мечтал пожить в небольшом поселке, без пробок и суматохи. И вот мы всей семьей уже более полугода жи...