Skip to content

Nomad

Nomad — оркестратор рабочих нагрузок Meduza. Он планирует Docker-контейнеры, управляет проверками здоровья, обрабатывает перезапуски и интегрируется с Traefik (для маршрутизации) и Consul (для конфигурации).

Основные концепции

Задачи, группы и задачи

Nomad использует трёхуровневую иерархию:

Job (например, "hub-prod")
├── Group: "backend"
│   └── Task: backend container
└── Group: "frontend"
    └── Task: frontend container
  • Job — единица работы верхнего уровня. Одна задача на сервис на окружение.
  • Group — набор задач, размещённых на одном узле. Каждая группа масштабируется независимо.
  • Task — один контейнер (драйвер Docker), работающий внутри группы.

Переменные

Файлы задач используют HCL-переменные для параметризации деплоев по окружениям:

hcl
variable "env" {
  description = "Environment name: prod, stage, or dev"
  type        = string
}

variable "domain" {
  description = "Public domain for the frontend"
  type        = string
}

variable "api_domain" {
  description = "Public domain for the backend API"
  type        = string
}

variable "image_tag" {
  description = "Docker image tag to deploy"
  type        = string
}

variable "backend_replicas" {
  description = "Number of backend task instances"
  type        = number
  default     = 1
}

Эти переменные передаются при деплое:

bash
nomad job run \
  -var="env=prod" \
  -var="domain=app.meduza.io" \
  -var="api_domain=api.meduza.io" \
  -var="image_tag=v1.2.3" \
  -var="backend_replicas=2" \
  hub.nomad.hcl

Структура задачи: пример Hub

Группа backend

Группа backend использует bridge-сеть с динамическим портом, маппящимся на внутренний порт контейнера 3010. Nomad автоматически выбирает доступный порт хоста, а Traefik обнаруживает его через каталог сервисов.

hcl
group "backend" {
  count = var.backend_replicas

  network {
    mode = "bridge"
    port "http" {
      to = 3010
    }
  }

  service {
    name     = "hub-${var.env}-backend"
    port     = "http"
    provider = "nomad"

    check {
      type     = "http"
      path     = "/health/live"
      interval = "10s"
      timeout  = "3s"
    }
  }

  task "backend" {
    driver = "docker"

    config {
      image = "registry.example.com/hub-backend:${var.image_tag}"
      ports = ["http"]
    }

    # Consul template block — see Consul docs for details
    template {
      data        = <<-TPL
      {{ with consulKey "hub/${var.env}/config" }}
      {{ range $key, $value := .Data | parseJSON }}
      {{ $key }}={{ $value }}
      {{ end }}
      {{ end }}
      TPL
      destination = "secrets/env.env"
      env         = true
    }

    resources {
      cpu    = 500
      memory = 512
    }
  }
}

Динамические порты

При использовании динамических портов Nomad назначает случайный доступный порт на хосте. Директива to = 3010 указывает Nomad маппить этот порт хоста на порт 3010 внутри контейнера. Это позволяет избежать конфликтов портов при запуске нескольких экземпляров или окружений на одном хосте.

Группа frontend

Группа frontend использует статические порты, чтобы каждое окружение имело предсказуемый, фиксированный порт. Traefik маршрутизирует публичный HTTPS-трафик на эти порты на основе заголовка Host.

hcl
group "frontend" {
  count = 1

  network {
    mode = "bridge"
    port "http" {
      static = var.env == "prod" ? 8888 : var.env == "stage" ? 8889 : 8890
      to     = 3000
    }
  }

  service {
    name     = "hub-${var.env}-frontend"
    port     = "http"
    provider = "nomad"

    tags = [
      "traefik.enable=true",
      "traefik.http.routers.hub-${var.env}-frontend.rule=Host(`${var.domain}`)",
      "traefik.http.routers.hub-${var.env}-frontend.entrypoints=websecure",
      "traefik.http.routers.hub-${var.env}-frontend.tls.certresolver=letsencrypt",
    ]

    check {
      type     = "http"
      path     = "/health"
      interval = "10s"
      timeout  = "3s"
    }
  }

  task "frontend" {
    driver = "docker"

    config {
      image = "registry.example.com/hub-frontend:${var.image_tag}"
      ports = ["http"]
    }

    resources {
      cpu    = 200
      memory = 256
    }
  }
}

Режимы сети

РежимОписаниеКогда использовать
bridgeКонтейнер получает собственное сетевое пространство имён. Порты маппятся с хоста в контейнер.По умолчанию для всех сервисов Meduza
hostКонтейнер разделяет сетевое пространство имён хоста напрямую.Не используется в Meduza

Все задачи Meduza используют режим bridge. Ключевое различие — между статическим и динамическим выделением портов:

Тип портаПоведениеКогда использовать
ДинамическийNomad выбирает доступный порт хоста при планированииБэкенд-сервисы (обнаруживаются через каталог сервисов)
СтатическийВы указываете точный порт хостаФронтенд-сервисы (Traefik требуется стабильный upstream)

Проверки здоровья

Каждый блок сервиса должен содержать проверку здоровья. Nomad использует их для определения готовности задачи принимать трафик и необходимости перезапуска.

hcl
check {
  type     = "http"
  path     = "/health/live"
  interval = "10s"
  timeout  = "3s"
}
  • typehttp, tcp или script. HTTP-проверки предпочтительны для веб-сервисов.
  • path — эндпоинт, на который Nomad будет отправлять запросы. Должен возвращать статус 2xx.
  • interval — как часто проверять.
  • timeout — время ожидания, после которого проверка считается неудачной.

WARNING

Если проверка здоровья падает многократно, Nomad перезапустит задачу. Убедитесь, что ваш health-эндпоинт лёгкий и не зависит от внешних сервисов, если вы не хотите каскадных перезапусков.

Выделение ресурсов

Каждая задача объявляет свои требования к CPU и памяти. Nomad использует их для плотной упаковки задач на доступных узлах.

СервисCPU (МГц)Память (МБ)
Backend500512
Frontend200256
hcl
resources {
  cpu    = 500   # MHz
  memory = 512   # MB
}

TIP

Это жёсткие лимиты. Если контейнер превысит выделенную память, Nomad завершит его по OOM. Устанавливайте значения с запасом — отслеживайте реальное потребление через nomad alloc status и корректируйте.

Полезные команды

bash
# Run or update a job
nomad job run -var-file=prod.vars.hcl hub.nomad.hcl

# Check job status
nomad job status hub-prod

# View allocations for a job
nomad job allocs hub-prod

# Stream logs for an allocation
nomad alloc logs -f <alloc-id>

# View allocation resource usage
nomad alloc status <alloc-id>

# Stop a job
nomad job stop hub-prod

# Force a periodic garbage collection
nomad system gc

Дополнительное чтение

  • Traefik — как Traefik читает теги сервисов, показанные выше
  • Consul — как блок template инжектит конфигурацию из Consul KV

Документация по инфраструктуре