Nomad — планировщик от HashiCorp для контейнеров и обычных процессов на пуле машин. В отличие от Kubernetes, здесь нет etcd, CNI и десятка контроллеров: один бинарник работает и сервером, и клиентом, а состояние хранится через Raft. Для небольшой лаборатории этого достаточно для обновлений без простоя и самовосстановления.
Как устроен кластер
Серверы хранят состояние, клиенты запускают задачи и сообщают о ресурсах. На маленьком стенде три сервера совмещают обе роли, а остальные машины остаются клиентами. Порты: 4646 — HTTP API и веб-интерфейс, 4647 — RPC между узлами, 4648 — Serf для служебных сообщений.
Отличия от Kubernetes:
- нет отдельного хранилища состояния и CNI-плагина;
- jobspec описывает задачу, группу и сеть в одном файле HCL;
- поддерживаются контейнеры, обычные процессы и Java;
- namespaces, canary и multi-region доступны только в Enterprise.
Лицензия — BUSL 1.1: исходники открыты, но коммерческое использование ограничено.
Установка
Официальный репозиторий HashiCorp содержит пакеты для Debian и Ubuntu. Ключ и список источников добавляются один раз, после чего обновление идёт через apt.
wget -O- https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
sudo apt update
sudo apt install nomad
nomad version
Режим nomad agent -dev поднимает сервер и клиент в одном процессе и не сохраняет состояние; он подходит только для ноутбука.
Конфигурация сервера и клиента
Файлы лежат в /etc/nomad.d/. Ниже минимальный стенд из трёх узлов, каждый из которых совмещает сервер и клиент. В боевой схеме клиенты выносят отдельно.
datacenter = "dc1"
data_dir = "/opt/nomad/data"
bind_addr = "0.0.0.0"
server {
enabled = true
bootstrap_expect = 3
}
client {
enabled = true
servers = ["10.0.0.11:4647", "10.0.0.12:4647", "10.0.0.13:4647"]
}
telemetry {
prometheus_metrics = true
disable_hostname = true
}
acl {
enabled = true
}
bootstrap_expect должен совпадать с числом серверов, иначе выборы лидера не завершатся. После запуска получите management-токен командой nomad acl bootstrap и сохраните его в менеджере секретов. Не открывайте 4646 в интернет: панель без ACL доступна всем.
Первый job: веб-сервис
Jobspec — файл HCL. В нём задаются группа, количество экземпляров, сеть, задача и проверка здоровья. Ниже nginx с динамическим портом и регистрацией в службе обнаружения Nomad.
job "web" {
datacenters = ["dc1"]
type = "service"
group "app" {
count = 2
network {
mode = "bridge"
port "http" {
to = 80
}
}
task "server" {
driver = "docker"
config {
image = "nginx:stable-alpine"
ports = ["http"]
}
resources {
cpu = 300
memory = 256
}
service {
name = "web"
port = "http"
provider = "nomad"
check {
type = "http"
path = "/"
interval = "10s"
timeout = "2s"
}
}
}
}
}
nomad job plan web.nomad.hcl
nomad job run web.nomad.hcl
nomad job status web
nomad alloc logs -f 1a2b3c4d server
plan показывает, что изменится, и не трогает кластер. Просматривайте diff: он ловит опечатки в образе и неверный порт. alloc logs принимает идентификатор аллокации, а не имя задачи.
Обновления и откаты
Стратегия обновления задаётся в группе. Nomad поднимает новые аллокации постепенно, ждёт проверок и при неудаче возвращает предыдущую версию.
update {
max_parallel = 1
min_healthy_time = "10s"
healthy_deadline = "3m"
progress_deadline = "10m"
auto_revert = true
}
История хранится на сервере: nomad job history web показывает версии, а nomad job revert web 2 откатывает на вторую. Перед обслуживанием узла включите drain, чтобы задачи переехали:
nomad node status
nomad node drain -enable -deadline 10m 2f4a1b7c
nomad node drain -disable 2f4a1b7c
Если задача не переезжает, проверьте constraint и свободные ресурсы: планировщик не разместит аллокацию там, где не хватает CPU или памяти.
Постоянные данные
Для домашних сервисов проще всего host volume: каталог на клиенте монтируется в контейнер. Том объявляется в конфигурации клиента, а точка монтирования — в jobspec.
# в блоке client на узле
host_volume "appdata" {
path = "/srv/appdata"
read_only = false
}
# фрагмент jobspec
group "app" {
volume "appdata" {
type = "host"
source = "appdata"
}
task "server" {
volume_mount {
volume = "appdata"
destination = "/data"
}
}
}
Host volume привязывает задачу к конкретному узлу, поэтому для отказоустойчивости нужны репликация средствами приложения или CSI-плагин сетевого хранилища. Состояние кластера сохраняется снимком Raft:
nomad operator snapshot save /var/backups/nomad.snap
nomad operator snapshot inspect /var/backups/nomad.snap
nomad operator snapshot restore /var/backups/nomad.snap
Снимок содержит задания и метаданные, но не данные приложений. Храните его вне того же диска и проверяйте восстановление на тестовом стенде.
Безопасность
После nomad acl bootstrap все запросы требуют токен. Передавайте его через NOMAD_TOKEN, а не аргументом командной строки: аргументы видны в ps. Для автоматизации создайте политику с минимальными правами и отдельный токен.
export NOMAD_TOKEN="management-token"
nomad acl policy apply deploy deploy.hcl
nomad acl token create -name=deploy -policy=deploy -ttl=720h
nomad acl token self
Включайте TLS для HTTP и RPC, если узлы общаются через недоверенную сеть. Веб-интерфейс держите за обратным прокси с TLS и дополнительной аутентификацией.
Диагностика: чек-лист
Порядок проверки — от кластера к задаче. Сначала убедитесь, что серверы видят друг друга и есть лидер, затем проверьте узел и только потом аллокацию.
nomad server members
nomad operator raft list-peers
nomad node status -verbose
nomad alloc status 1a2b3c4d
journalctl -u nomad -n 100 --no-pager
ss -lntp | grep -E '4646|4647|4648'
- нет лидера — проверьте
bootstrap_expect, время и связность по 4647; - узел в статусе
down— смотритеjournalctlи версию Nomad; - аллокация в
pending— не хватает ресурсов или не выполняются constraints; - контейнер падает — читайте логи задачи и проверяйте переменные окружения;
- обновление зависло — смотрите
nomad deployment status.
Итог
Nomad закрывает средний класс задач: несколько машин, десятки контейнеров, предсказуемые обновления и минимум инфраструктуры. Он не заменяет Kubernetes в крупной компании с платформенной командой, но в домашней лаборатории или небольшом отделе экономит время и ресурсы.
Начните с трёх серверов, ACL и резервных снимков, затем перенесите один реальный сервис. Если через неделю вы не заглядываете в панель без необходимости, а обновления проходят сами, выбор сделан правильно.