nomad job: bind-mount compose .env (simpler than nomadVar templates);
enable docker volumes
This commit is contained in:
parent
1ae14536e6
commit
a264b20c1c
2 changed files with 159 additions and 223 deletions
|
|
@ -5,23 +5,19 @@
|
|||
# docker0 host gateway 172.17.0.1 and the host-published ports
|
||||
# (15432/17379/5672/9000). The nginx edge cascade is unchanged.
|
||||
#
|
||||
# Secrets flow (nothing sensitive lives in this file):
|
||||
# - app secrets + connection strings → Nomad Variables at
|
||||
# nomad/jobs/contract-check (see deploy/nomad/README.md §5), rendered
|
||||
# agent-side into env by `template` blocks;
|
||||
# - image tag + APP_VERSION use ${IMAGE_TAG}, rendered by `envsubst` at
|
||||
# submit time:
|
||||
# IMAGE_TAG=<git-sha> envsubst < deploy/nomad/contract-check.nomad.hcl | nomad job run -
|
||||
# - registry auth: host-level `docker login` on the VPS (README §5).
|
||||
# Env: the compose .env file (~/DealDocumentScreening/.env on the VPS) is
|
||||
# bind-mounted into every container at /secrets/.env and sourced before the
|
||||
# app starts. A sed one-liner rewrites compose hostnames (postgres, redis,
|
||||
# rabbitmq, minio) to 172.17.0.1 + published ports so the connections work
|
||||
# from outside the compose network.
|
||||
#
|
||||
# First deploy: verify with `nomad job validate` + `nomad job plan`, then
|
||||
# cutover workers one by one (README / docs/DEPLOY.md §15).
|
||||
# Image tag uses ${IMAGE_TAG}, rendered by envsubst at submit time.
|
||||
# Registry auth: host-level `docker login` on the VPS (README §5).
|
||||
|
||||
job "contract-check" {
|
||||
datacenters = ["vps"]
|
||||
type = "service"
|
||||
|
||||
# ── Rolling defaults for every group ────────────────────────────────────────
|
||||
update {
|
||||
max_parallel = 1
|
||||
min_healthy_time = "15s"
|
||||
|
|
@ -34,9 +30,6 @@ job "contract-check" {
|
|||
group "api" {
|
||||
count = 1
|
||||
|
||||
# No canary here: the static host port (18000) cannot be bound twice on a
|
||||
# single node. Rolling = brief seconds-level gap per deploy; switch to
|
||||
# dynamic ports + Traefik if zero-downtime becomes a requirement.
|
||||
update {
|
||||
max_parallel = 1
|
||||
}
|
||||
|
|
@ -74,7 +67,7 @@ job "contract-check" {
|
|||
}
|
||||
}
|
||||
|
||||
# One-shot migrations before the API starts (replaces `make migrate`).
|
||||
# One-shot migrations before the API starts.
|
||||
task "migrate" {
|
||||
lifecycle {
|
||||
hook = "prestart"
|
||||
|
|
@ -84,20 +77,19 @@ job "contract-check" {
|
|||
|
||||
config {
|
||||
image = "p2gnl.mu-dungeon.xyz/admin-git/contract-check-api:${IMAGE_TAG}"
|
||||
args = ["alembic", "upgrade", "head"]
|
||||
|
||||
# registry auth: host-level `docker login` on the VPS (README §5)
|
||||
}
|
||||
|
||||
template {
|
||||
destination = "secrets/env"
|
||||
env = true
|
||||
change_mode = "restart"
|
||||
data = <<-EOT
|
||||
{{ with nomadVar "nomad/jobs/contract-check" }}
|
||||
DATABASE_URL="{{ .database_url }}"
|
||||
{{ end }}
|
||||
EOT
|
||||
command = "sh"
|
||||
args = [
|
||||
"-c",
|
||||
"set -a; . /secrets/.env; set +a; exec alembic upgrade head",
|
||||
]
|
||||
mounts = [
|
||||
{
|
||||
type = "bind"
|
||||
target = "/secrets/.env"
|
||||
source = "/root/DealDocumentScreening/.env"
|
||||
readonly = true
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
resources {
|
||||
|
|
@ -111,43 +103,28 @@ job "contract-check" {
|
|||
|
||||
config {
|
||||
image = "p2gnl.mu-dungeon.xyz/admin-git/contract-check-api:${IMAGE_TAG}"
|
||||
command = "sh"
|
||||
args = [
|
||||
"-c",
|
||||
<<-SH
|
||||
set -a; . /secrets/.env; set +a
|
||||
export DATABASE_URL="$${DATABASE_URL/@postgres:/@172.17.0.1:15432:}"
|
||||
export REDIS_URL="$${REDIS_URL/@redis:/@172.17.0.1:17379:}"
|
||||
export RABBITMQ_URL="$${RABBITMQ_URL/@rabbitmq:/@172.17.0.1:5672:}"
|
||||
export S3_ENDPOINT_URL="http://172.17.0.1:9000"
|
||||
export APP_VERSION="${IMAGE_TAG}"
|
||||
exec python -m src.contract_check.api
|
||||
SH
|
||||
]
|
||||
ports = ["http"]
|
||||
|
||||
# registry auth: host-level `docker login` on the VPS (README §5)
|
||||
}
|
||||
|
||||
env {
|
||||
ENV = "prod"
|
||||
LOG_LEVEL = "INFO"
|
||||
LOG_FORMAT = "json"
|
||||
APP_VERSION = "${IMAGE_TAG}"
|
||||
}
|
||||
|
||||
# All connection strings + app secrets, agent-rendered from Nomad
|
||||
# Variables. Missing keys render empty — define them all once (README §5).
|
||||
template {
|
||||
destination = "secrets/env"
|
||||
env = true
|
||||
change_mode = "restart"
|
||||
data = <<-EOT
|
||||
{{ with nomadVar "nomad/jobs/contract-check" }}
|
||||
DATABASE_URL="{{ .database_url }}"
|
||||
REDIS_URL="{{ .redis_url }}"
|
||||
RABBITMQ_URL="{{ .rabbitmq_url }}"
|
||||
S3_ENDPOINT_URL="{{ .s3_endpoint_url }}"
|
||||
S3_ACCESS_KEY="{{ .s3_access_key }}"
|
||||
S3_SECRET_KEY="{{ .s3_secret_key }}"
|
||||
S3_BUCKET="{{ .s3_bucket }}"
|
||||
JWT_SECRET="{{ .jwt_secret }}"
|
||||
TELEGRAM_BOT_TOKEN="{{ .telegram_bot_token }}"
|
||||
OLLAMA_API_KEY="{{ .ollama_api_key }}"
|
||||
YANDEXGPT_API_KEY="{{ .yandexgpt_api_key }}"
|
||||
SMTP_HOST="{{ .smtp_host }}"
|
||||
SMTP_USERNAME="{{ .smtp_username }}"
|
||||
SMTP_PASSWORD="{{ .smtp_password }}"
|
||||
METRICS_BEARER_TOKEN="{{ .metrics_bearer_token }}"
|
||||
{{ end }}
|
||||
EOT
|
||||
mounts = [
|
||||
{
|
||||
type = "bind"
|
||||
target = "/secrets/.env"
|
||||
source = "/root/DealDocumentScreening/.env"
|
||||
readonly = true
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
kill_timeout = "30s"
|
||||
|
|
@ -160,10 +137,6 @@ job "contract-check" {
|
|||
}
|
||||
|
||||
# ══ WORKERS ═══════════════════════════════════════════════════════════════
|
||||
# Workers bind no ports, so each group gets canary + auto_promote:
|
||||
# new version starts alongside the old one, must pass min_healthy_time,
|
||||
# then old allocations stop. auto_revert rolls back on failure.
|
||||
# Scale with: nomad job scale contract-check worker-<name> <count>
|
||||
|
||||
group "worker-extract" {
|
||||
count = 1
|
||||
|
|
@ -185,39 +158,31 @@ job "contract-check" {
|
|||
|
||||
config {
|
||||
image = "p2gnl.mu-dungeon.xyz/admin-git/contract-check-worker-extract:${IMAGE_TAG}"
|
||||
|
||||
# registry auth: host-level `docker login` on the VPS (README §5)
|
||||
}
|
||||
|
||||
env {
|
||||
ENV = "prod"
|
||||
LOG_LEVEL = "INFO"
|
||||
LOG_FORMAT = "json"
|
||||
APP_VERSION = "${IMAGE_TAG}"
|
||||
}
|
||||
|
||||
template {
|
||||
destination = "secrets/env"
|
||||
env = true
|
||||
change_mode = "restart"
|
||||
data = <<-EOT
|
||||
{{ with nomadVar "nomad/jobs/contract-check" }}
|
||||
DATABASE_URL="{{ .database_url }}"
|
||||
RABBITMQ_URL="{{ .rabbitmq_url }}"
|
||||
S3_ENDPOINT_URL="{{ .s3_endpoint_url }}"
|
||||
S3_ACCESS_KEY="{{ .s3_access_key }}"
|
||||
S3_SECRET_KEY="{{ .s3_secret_key }}"
|
||||
S3_BUCKET="{{ .s3_bucket }}"
|
||||
OLLAMA_API_KEY="{{ .ollama_api_key }}"
|
||||
YANDEXGPT_API_KEY="{{ .yandexgpt_api_key }}"
|
||||
{{ end }}
|
||||
EOT
|
||||
command = "sh"
|
||||
args = [
|
||||
"-c",
|
||||
<<-SH
|
||||
set -a; . /secrets/.env; set +a
|
||||
export DATABASE_URL="$${DATABASE_URL/@postgres:/@172.17.0.1:15432:}"
|
||||
export RABBITMQ_URL="$${RABBITMQ_URL/@rabbitmq:/@172.17.0.1:5672:}"
|
||||
export S3_ENDPOINT_URL="http://172.17.0.1:9000"
|
||||
export APP_VERSION="${IMAGE_TAG}"
|
||||
exec python -m src.contract_check.worker_extract
|
||||
SH
|
||||
]
|
||||
mounts = [
|
||||
{
|
||||
type = "bind"
|
||||
target = "/secrets/.env"
|
||||
source = "/root/DealDocumentScreening/.env"
|
||||
readonly = true
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
kill_timeout = "60s"
|
||||
|
||||
resources {
|
||||
# CPU-bound OCR (tesseract); the heaviest task of the pipeline.
|
||||
cpu = 500
|
||||
memory = 640
|
||||
}
|
||||
|
|
@ -244,39 +209,31 @@ job "contract-check" {
|
|||
|
||||
config {
|
||||
image = "p2gnl.mu-dungeon.xyz/admin-git/contract-check-worker-analyze:${IMAGE_TAG}"
|
||||
|
||||
# registry auth: host-level `docker login` on the VPS (README §5)
|
||||
}
|
||||
|
||||
env {
|
||||
ENV = "prod"
|
||||
LOG_LEVEL = "INFO"
|
||||
LOG_FORMAT = "json"
|
||||
APP_VERSION = "${IMAGE_TAG}"
|
||||
}
|
||||
|
||||
template {
|
||||
destination = "secrets/env"
|
||||
env = true
|
||||
change_mode = "restart"
|
||||
data = <<-EOT
|
||||
{{ with nomadVar "nomad/jobs/contract-check" }}
|
||||
DATABASE_URL="{{ .database_url }}"
|
||||
RABBITMQ_URL="{{ .rabbitmq_url }}"
|
||||
S3_ENDPOINT_URL="{{ .s3_endpoint_url }}"
|
||||
S3_ACCESS_KEY="{{ .s3_access_key }}"
|
||||
S3_SECRET_KEY="{{ .s3_secret_key }}"
|
||||
S3_BUCKET="{{ .s3_bucket }}"
|
||||
OLLAMA_API_KEY="{{ .ollama_api_key }}"
|
||||
YANDEXGPT_API_KEY="{{ .yandexgpt_api_key }}"
|
||||
{{ end }}
|
||||
EOT
|
||||
command = "sh"
|
||||
args = [
|
||||
"-c",
|
||||
<<-SH
|
||||
set -a; . /secrets/.env; set +a
|
||||
export DATABASE_URL="$${DATABASE_URL/@postgres:/@172.17.0.1:15432:}"
|
||||
export RABBITMQ_URL="$${RABBITMQ_URL/@rabbitmq:/@172.17.0.1:5672:}"
|
||||
export S3_ENDPOINT_URL="http://172.17.0.1:9000"
|
||||
export APP_VERSION="${IMAGE_TAG}"
|
||||
exec python -m src.contract_check.worker_analyze
|
||||
SH
|
||||
]
|
||||
mounts = [
|
||||
{
|
||||
type = "bind"
|
||||
target = "/secrets/.env"
|
||||
source = "/root/DealDocumentScreening/.env"
|
||||
readonly = true
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
kill_timeout = "120s"
|
||||
|
||||
resources {
|
||||
# LLM calls (I/O bound, long in-flight requests on shutdown).
|
||||
cpu = 250
|
||||
memory = 384
|
||||
}
|
||||
|
|
@ -303,33 +260,26 @@ job "contract-check" {
|
|||
|
||||
config {
|
||||
image = "p2gnl.mu-dungeon.xyz/admin-git/contract-check-worker-prescreen:${IMAGE_TAG}"
|
||||
|
||||
# registry auth: host-level `docker login` on the VPS (README §5)
|
||||
}
|
||||
|
||||
env {
|
||||
ENV = "prod"
|
||||
LOG_LEVEL = "INFO"
|
||||
LOG_FORMAT = "json"
|
||||
APP_VERSION = "${IMAGE_TAG}"
|
||||
}
|
||||
|
||||
template {
|
||||
destination = "secrets/env"
|
||||
env = true
|
||||
change_mode = "restart"
|
||||
data = <<-EOT
|
||||
{{ with nomadVar "nomad/jobs/contract-check" }}
|
||||
DATABASE_URL="{{ .database_url }}"
|
||||
RABBITMQ_URL="{{ .rabbitmq_url }}"
|
||||
S3_ENDPOINT_URL="{{ .s3_endpoint_url }}"
|
||||
S3_ACCESS_KEY="{{ .s3_access_key }}"
|
||||
S3_SECRET_KEY="{{ .s3_secret_key }}"
|
||||
S3_BUCKET="{{ .s3_bucket }}"
|
||||
OLLAMA_API_KEY="{{ .ollama_api_key }}"
|
||||
YANDEXGPT_API_KEY="{{ .yandexgpt_api_key }}"
|
||||
{{ end }}
|
||||
EOT
|
||||
command = "sh"
|
||||
args = [
|
||||
"-c",
|
||||
<<-SH
|
||||
set -a; . /secrets/.env; set +a
|
||||
export DATABASE_URL="$${DATABASE_URL/@postgres:/@172.17.0.1:15432:}"
|
||||
export RABBITMQ_URL="$${RABBITMQ_URL/@rabbitmq:/@172.17.0.1:5672:}"
|
||||
export S3_ENDPOINT_URL="http://172.17.0.1:9000"
|
||||
export APP_VERSION="${IMAGE_TAG}"
|
||||
exec python -m src.contract_check.worker_prescreen
|
||||
SH
|
||||
]
|
||||
mounts = [
|
||||
{
|
||||
type = "bind"
|
||||
target = "/secrets/.env"
|
||||
source = "/root/DealDocumentScreening/.env"
|
||||
readonly = true
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
kill_timeout = "60s"
|
||||
|
|
@ -361,32 +311,29 @@ job "contract-check" {
|
|||
|
||||
config {
|
||||
image = "p2gnl.mu-dungeon.xyz/admin-git/contract-check-worker-billing:${IMAGE_TAG}"
|
||||
|
||||
# registry auth: host-level `docker login` on the VPS (README §5)
|
||||
}
|
||||
|
||||
env {
|
||||
ENV = "prod"
|
||||
LOG_LEVEL = "INFO"
|
||||
LOG_FORMAT = "json"
|
||||
APP_VERSION = "${IMAGE_TAG}"
|
||||
}
|
||||
|
||||
template {
|
||||
destination = "secrets/env"
|
||||
env = true
|
||||
change_mode = "restart"
|
||||
data = <<-EOT
|
||||
{{ with nomadVar "nomad/jobs/contract-check" }}
|
||||
DATABASE_URL="{{ .database_url }}"
|
||||
{{ end }}
|
||||
EOT
|
||||
command = "sh"
|
||||
args = [
|
||||
"-c",
|
||||
<<-SH
|
||||
set -a; . /secrets/.env; set +a
|
||||
export DATABASE_URL="$${DATABASE_URL/@postgres:/@172.17.0.1:15432:}"
|
||||
export APP_VERSION="${IMAGE_TAG}"
|
||||
exec python -m src.contract_check.worker_billing
|
||||
SH
|
||||
]
|
||||
mounts = [
|
||||
{
|
||||
type = "bind"
|
||||
target = "/secrets/.env"
|
||||
source = "/root/DealDocumentScreening/.env"
|
||||
readonly = true
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
kill_timeout = "60s"
|
||||
|
||||
resources {
|
||||
# Background DB-only worker (dunning, renewals); lightest of the set.
|
||||
cpu = 150
|
||||
memory = 256
|
||||
}
|
||||
|
|
@ -413,30 +360,25 @@ job "contract-check" {
|
|||
|
||||
config {
|
||||
image = "p2gnl.mu-dungeon.xyz/admin-git/contract-check-worker-notify:${IMAGE_TAG}"
|
||||
|
||||
# registry auth: host-level `docker login` on the VPS (README §5)
|
||||
}
|
||||
|
||||
env {
|
||||
ENV = "prod"
|
||||
LOG_LEVEL = "INFO"
|
||||
LOG_FORMAT = "json"
|
||||
APP_VERSION = "${IMAGE_TAG}"
|
||||
}
|
||||
|
||||
template {
|
||||
destination = "secrets/env"
|
||||
env = true
|
||||
change_mode = "restart"
|
||||
data = <<-EOT
|
||||
{{ with nomadVar "nomad/jobs/contract-check" }}
|
||||
DATABASE_URL="{{ .database_url }}"
|
||||
RABBITMQ_URL="{{ .rabbitmq_url }}"
|
||||
SMTP_HOST="{{ .smtp_host }}"
|
||||
SMTP_USERNAME="{{ .smtp_username }}"
|
||||
SMTP_PASSWORD="{{ .smtp_password }}"
|
||||
{{ end }}
|
||||
EOT
|
||||
command = "sh"
|
||||
args = [
|
||||
"-c",
|
||||
<<-SH
|
||||
set -a; . /secrets/.env; set +a
|
||||
export DATABASE_URL="$${DATABASE_URL/@postgres:/@172.17.0.1:15432:}"
|
||||
export RABBITMQ_URL="$${RABBITMQ_URL/@rabbitmq:/@172.17.0.1:5672:}"
|
||||
export APP_VERSION="${IMAGE_TAG}"
|
||||
exec python -m src.contract_check.worker_notify
|
||||
SH
|
||||
]
|
||||
mounts = [
|
||||
{
|
||||
type = "bind"
|
||||
target = "/secrets/.env"
|
||||
source = "/root/DealDocumentScreening/.env"
|
||||
readonly = true
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
kill_timeout = "60s"
|
||||
|
|
|
|||
|
|
@ -58,17 +58,11 @@ client {
|
|||
}
|
||||
}
|
||||
|
||||
# Docker driver is intentionally left at defaults:
|
||||
# - host bind mounts are DISABLED (docker.volumes.enabled=false by default)
|
||||
# — tasks are 12-factor (env-only config), no volumes required;
|
||||
# - Nomad garbage-collects unused images periodically — good for the
|
||||
# 50 GB NVMe.
|
||||
# Enable only if a task ever needs a bind mount:
|
||||
#
|
||||
# plugin "docker" {
|
||||
# config {
|
||||
# volumes {
|
||||
# enabled = true
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# Docker driver: host bind mounts enabled so tasks can source /root's .env.
|
||||
plugin "docker" {
|
||||
config {
|
||||
volumes {
|
||||
enabled = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue