first commit
This commit is contained in:
309
deploy/stack.yaml
Normal file
309
deploy/stack.yaml
Normal file
@@ -0,0 +1,309 @@
|
||||
version: "3.8"
|
||||
|
||||
x-app-environment: &app-environment
|
||||
APP_ENV: production
|
||||
DATABASE_URL_FILE: /run/secrets/database_url
|
||||
S3_ENDPOINT: ${R2_ENDPOINT:?set R2_ENDPOINT}
|
||||
S3_PUBLIC_ENDPOINT: ${R2_PUBLIC_ENDPOINT:?set R2_PUBLIC_ENDPOINT}
|
||||
S3_BUCKET: ${R2_BUCKET:?set R2_BUCKET}
|
||||
AWS_ACCESS_KEY_ID_FILE: /run/secrets/r2_access_key_id
|
||||
AWS_SECRET_ACCESS_KEY_FILE: /run/secrets/r2_secret_access_key
|
||||
AWS_DEFAULT_REGION: auto
|
||||
OPERATOR_USER: ${OPERATOR_USER:?set OPERATOR_USER}
|
||||
OPERATOR_PASSWORD_FILE: /run/secrets/operator_password
|
||||
PAYMENT_ADAPTER: ${PAYMENT_ADAPTER:?set PAYMENT_ADAPTER}
|
||||
FREIGHT_ADAPTER: ${FREIGHT_ADAPTER:?set FREIGHT_ADAPTER}
|
||||
TINY_ADAPTER: ${TINY_ADAPTER:?set TINY_ADAPTER}
|
||||
WHATSAPP_ADAPTER: ${WHATSAPP_ADAPTER:?set WHATSAPP_ADAPTER}
|
||||
STORAGE_ADAPTER: s3-r2
|
||||
PAYMENT_TOKEN_FILE: /run/secrets/payment_token
|
||||
PAYMENT_WEBHOOK_SECRET_FILE: /run/secrets/payment_webhook_secret
|
||||
TINY_TOKEN_FILE: /run/secrets/tiny_token
|
||||
WHATSAPP_TOKEN_FILE: /run/secrets/whatsapp_token
|
||||
PUBLIC_ORIGIN: ${PUBLIC_ORIGIN:?set PUBLIC_ORIGIN}
|
||||
PUBLIC_HOST: ${PUBLIC_HOST:?set PUBLIC_HOST}
|
||||
ALLOWED_HOSTS: ${PUBLIC_HOST:?set PUBLIC_HOST},${KANBAN_HOST:?set KANBAN_HOST}
|
||||
ALLOWED_ORIGINS: ${PUBLIC_ORIGIN:?set PUBLIC_ORIGIN},https://${KANBAN_HOST:?set KANBAN_HOST}
|
||||
COOKIE_SECURE: "true"
|
||||
MAX_UPLOAD_BYTES: ${MAX_UPLOAD_BYTES:-5368709120}
|
||||
UPLOAD_PART_BYTES: ${UPLOAD_PART_BYTES:-8388608}
|
||||
STORAGE_QUOTA_BYTES: ${STORAGE_QUOTA_BYTES:?set STORAGE_QUOTA_BYTES}
|
||||
OWNER_UPLOAD_QUOTA_BYTES: ${OWNER_UPLOAD_QUOTA_BYTES:?set OWNER_UPLOAD_QUOTA_BYTES}
|
||||
MAX_PENDING_UPLOADS: ${MAX_PENDING_UPLOADS:-10}
|
||||
SCAN_MAX_BYTES: ${SCAN_MAX_BYTES:-134217728}
|
||||
|
||||
x-app-secrets: &app-secrets
|
||||
- database_url
|
||||
- r2_access_key_id
|
||||
- r2_secret_access_key
|
||||
- operator_password
|
||||
- payment_token
|
||||
- payment_webhook_secret
|
||||
- tiny_token
|
||||
- whatsapp_token
|
||||
|
||||
x-rolling: &rolling
|
||||
update_config:
|
||||
parallelism: 1
|
||||
delay: 10s
|
||||
order: start-first
|
||||
failure_action: rollback
|
||||
monitor: 45s
|
||||
rollback_config:
|
||||
parallelism: 1
|
||||
delay: 5s
|
||||
order: start-first
|
||||
failure_action: pause
|
||||
monitor: 45s
|
||||
restart_policy:
|
||||
condition: on-failure
|
||||
delay: 5s
|
||||
max_attempts: 5
|
||||
window: 60s
|
||||
|
||||
services:
|
||||
db:
|
||||
image: ${POSTGRES_IMAGE:?set POSTGRES_IMAGE}
|
||||
environment:
|
||||
POSTGRES_DB: ${POSTGRES_DB:?set POSTGRES_DB}
|
||||
POSTGRES_USER: ${POSTGRES_USER:?set POSTGRES_USER}
|
||||
POSTGRES_PASSWORD_FILE: /run/secrets/db_admin_password
|
||||
secrets: [db_admin_password]
|
||||
volumes:
|
||||
- postgres-data:/var/lib/postgresql/data
|
||||
networks: [backend]
|
||||
healthcheck:
|
||||
test: [CMD-SHELL, 'pg_isready -U "$$POSTGRES_USER" -d "$$POSTGRES_DB"']
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 12
|
||||
start_period: 20s
|
||||
stop_grace_period: 60s
|
||||
deploy:
|
||||
replicas: 1
|
||||
placement:
|
||||
constraints: [node.labels.dtf_database == true]
|
||||
update_config:
|
||||
parallelism: 1
|
||||
order: stop-first
|
||||
failure_action: rollback
|
||||
monitor: 60s
|
||||
rollback_config:
|
||||
parallelism: 1
|
||||
order: stop-first
|
||||
failure_action: pause
|
||||
monitor: 60s
|
||||
restart_policy:
|
||||
condition: on-failure
|
||||
delay: 10s
|
||||
max_attempts: 5
|
||||
window: 120s
|
||||
resources:
|
||||
limits: {cpus: "2.0", memory: 4G}
|
||||
reservations: {cpus: "0.5", memory: 1G}
|
||||
|
||||
db-init:
|
||||
image: ${API_IMAGE:?set API_IMAGE}:${IMAGE_TAG:-latest}
|
||||
command: python -m local.bootstrap
|
||||
environment:
|
||||
APP_ENV: production
|
||||
DATABASE_ADMIN_URL_FILE: /run/secrets/database_admin_url
|
||||
APP_DB_USER: ${APP_DB_USER:?set APP_DB_USER}
|
||||
APP_DB_PASSWORD_FILE: /run/secrets/app_db_password
|
||||
secrets: [database_admin_url, app_db_password]
|
||||
networks: [backend]
|
||||
deploy:
|
||||
replicas: 1
|
||||
restart_policy: {condition: none}
|
||||
placement:
|
||||
constraints: [node.platform.os == linux]
|
||||
resources:
|
||||
limits: {cpus: "0.5", memory: 512M}
|
||||
|
||||
scanner:
|
||||
image: ${CLAMAV_IMAGE:?set CLAMAV_IMAGE}
|
||||
user: "100:101"
|
||||
entrypoint: [clamd, --foreground=true, --config-file=/etc/clamav/clamd.conf]
|
||||
configs:
|
||||
- source: clamd_config
|
||||
target: /etc/clamav/clamd.conf
|
||||
mode: 0444
|
||||
networks: [backend]
|
||||
read_only: true
|
||||
cap_drop: [ALL]
|
||||
security_opt: [no-new-privileges:true]
|
||||
tmpfs:
|
||||
- /tmp:uid=100,gid=101,mode=0750
|
||||
- /run/clamav:uid=100,gid=101,mode=0750
|
||||
- /var/log/clamav:uid=100,gid=101,mode=0750
|
||||
healthcheck:
|
||||
test: [CMD, clamdscan, --config-file=/etc/clamav/clamd.conf, --ping, "3"]
|
||||
interval: 15s
|
||||
timeout: 5s
|
||||
retries: 20
|
||||
start_period: 90s
|
||||
deploy:
|
||||
replicas: 1
|
||||
restart_policy: {condition: on-failure, delay: 10s}
|
||||
resources:
|
||||
limits: {cpus: "2.0", memory: 3G}
|
||||
reservations: {cpus: "0.5", memory: 1G}
|
||||
|
||||
api:
|
||||
image: ${API_IMAGE:?set API_IMAGE}:${IMAGE_TAG:-latest}
|
||||
environment: *app-environment
|
||||
secrets: *app-secrets
|
||||
networks: [backend, egress]
|
||||
read_only: true
|
||||
tmpfs: [/tmp]
|
||||
init: true
|
||||
cap_drop: [ALL]
|
||||
security_opt: [no-new-privileges:true]
|
||||
healthcheck:
|
||||
test:
|
||||
- CMD-SHELL
|
||||
- >-
|
||||
python -c "import os,urllib.request; r=urllib.request.Request('http://localhost:8000/health',headers={'Host':os.environ['PUBLIC_HOST']}); urllib.request.urlopen(r,timeout=3)"
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 12
|
||||
start_period: 30s
|
||||
stop_grace_period: 30s
|
||||
deploy:
|
||||
<<: *rolling
|
||||
replicas: 2
|
||||
resources:
|
||||
limits: {cpus: "1.0", memory: 1G}
|
||||
reservations: {cpus: "0.25", memory: 256M}
|
||||
|
||||
worker:
|
||||
image: ${API_IMAGE:?set API_IMAGE}:${IMAGE_TAG:-latest}
|
||||
command: python -m local.worker
|
||||
environment:
|
||||
<<: *app-environment
|
||||
CLAMD_HOST: scanner
|
||||
secrets: *app-secrets
|
||||
networks: [backend, egress]
|
||||
read_only: true
|
||||
tmpfs: [/tmp]
|
||||
init: true
|
||||
cap_drop: [ALL]
|
||||
security_opt: [no-new-privileges:true]
|
||||
healthcheck:
|
||||
test: [CMD, python, -c, "import urllib.request; urllib.request.urlopen('http://localhost:8002/health',timeout=3)"]
|
||||
interval: 15s
|
||||
timeout: 5s
|
||||
retries: 12
|
||||
start_period: 90s
|
||||
stop_grace_period: 60s
|
||||
deploy:
|
||||
<<: *rolling
|
||||
replicas: 1
|
||||
update_config:
|
||||
parallelism: 1
|
||||
order: stop-first
|
||||
failure_action: rollback
|
||||
monitor: 60s
|
||||
rollback_config:
|
||||
parallelism: 1
|
||||
order: stop-first
|
||||
failure_action: pause
|
||||
monitor: 60s
|
||||
resources:
|
||||
limits: {cpus: "1.5", memory: 2G}
|
||||
reservations: {cpus: "0.25", memory: 512M}
|
||||
|
||||
site:
|
||||
image: ${WEB_IMAGE:?set WEB_IMAGE}:${IMAGE_TAG:-latest}
|
||||
environment:
|
||||
WEB_INDEX: index.html
|
||||
PUBLIC_HOST: ${PUBLIC_HOST:?set PUBLIC_HOST}
|
||||
S3_PUBLIC_ENDPOINT: ${R2_PUBLIC_ENDPOINT:?set R2_PUBLIC_ENDPOINT}
|
||||
networks: [backend]
|
||||
ports:
|
||||
- target: 8080
|
||||
published: ${SITE_PORT:-8080}
|
||||
protocol: tcp
|
||||
mode: ingress
|
||||
read_only: true
|
||||
tmpfs:
|
||||
- /tmp:uid=101,gid=101,mode=0750
|
||||
- /var/cache/nginx:uid=101,gid=101,mode=0750
|
||||
- /var/run:uid=101,gid=101,mode=0750
|
||||
- /etc/nginx/conf.d:uid=101,gid=101,mode=0750
|
||||
cap_drop: [ALL]
|
||||
security_opt: [no-new-privileges:true]
|
||||
healthcheck:
|
||||
test: [CMD-SHELL, 'wget -q --header="Host: $$PUBLIC_HOST" -O /dev/null http://127.0.0.1:8080/health']
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 12
|
||||
start_period: 15s
|
||||
deploy:
|
||||
<<: *rolling
|
||||
replicas: 2
|
||||
resources:
|
||||
limits: {cpus: "0.5", memory: 256M}
|
||||
reservations: {cpus: "0.1", memory: 64M}
|
||||
|
||||
kanban:
|
||||
image: ${WEB_IMAGE:?set WEB_IMAGE}:${IMAGE_TAG:-latest}
|
||||
environment:
|
||||
WEB_INDEX: kanban.html
|
||||
PUBLIC_HOST: ${KANBAN_HOST:?set KANBAN_HOST}
|
||||
S3_PUBLIC_ENDPOINT: ${R2_PUBLIC_ENDPOINT:?set R2_PUBLIC_ENDPOINT}
|
||||
networks: [backend]
|
||||
ports:
|
||||
- target: 8080
|
||||
published: ${KANBAN_PORT:-8081}
|
||||
protocol: tcp
|
||||
mode: ingress
|
||||
read_only: true
|
||||
tmpfs:
|
||||
- /tmp:uid=101,gid=101,mode=0750
|
||||
- /var/cache/nginx:uid=101,gid=101,mode=0750
|
||||
- /var/run:uid=101,gid=101,mode=0750
|
||||
- /etc/nginx/conf.d:uid=101,gid=101,mode=0750
|
||||
cap_drop: [ALL]
|
||||
security_opt: [no-new-privileges:true]
|
||||
healthcheck:
|
||||
test: [CMD-SHELL, 'wget -q --header="Host: $$PUBLIC_HOST" -O /dev/null http://127.0.0.1:8080/health']
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 12
|
||||
start_period: 15s
|
||||
deploy:
|
||||
<<: *rolling
|
||||
replicas: 1
|
||||
resources:
|
||||
limits: {cpus: "0.5", memory: 256M}
|
||||
reservations: {cpus: "0.1", memory: 64M}
|
||||
|
||||
configs:
|
||||
clamd_config:
|
||||
file: ../local/clamd.conf
|
||||
|
||||
secrets:
|
||||
database_url: {external: true, name: "${DATABASE_URL_SECRET:?set DATABASE_URL_SECRET}"}
|
||||
database_admin_url: {external: true, name: "${DATABASE_ADMIN_URL_SECRET:?set DATABASE_ADMIN_URL_SECRET}"}
|
||||
db_admin_password: {external: true, name: "${DB_ADMIN_PASSWORD_SECRET:?set DB_ADMIN_PASSWORD_SECRET}"}
|
||||
app_db_password: {external: true, name: "${APP_DB_PASSWORD_SECRET:?set APP_DB_PASSWORD_SECRET}"}
|
||||
r2_access_key_id: {external: true, name: "${R2_ACCESS_KEY_ID_SECRET:?set R2_ACCESS_KEY_ID_SECRET}"}
|
||||
r2_secret_access_key: {external: true, name: "${R2_SECRET_ACCESS_KEY_SECRET:?set R2_SECRET_ACCESS_KEY_SECRET}"}
|
||||
operator_password: {external: true, name: "${OPERATOR_PASSWORD_SECRET:?set OPERATOR_PASSWORD_SECRET}"}
|
||||
payment_token: {external: true, name: "${PAYMENT_TOKEN_SECRET:?set PAYMENT_TOKEN_SECRET}"}
|
||||
payment_webhook_secret: {external: true, name: "${PAYMENT_WEBHOOK_SECRET:?set PAYMENT_WEBHOOK_SECRET}"}
|
||||
tiny_token: {external: true, name: "${TINY_TOKEN_SECRET:?set TINY_TOKEN_SECRET}"}
|
||||
whatsapp_token: {external: true, name: "${WHATSAPP_TOKEN_SECRET:?set WHATSAPP_TOKEN_SECRET}"}
|
||||
|
||||
volumes:
|
||||
postgres-data:
|
||||
external: true
|
||||
name: ${POSTGRES_VOLUME:?set POSTGRES_VOLUME}
|
||||
|
||||
networks:
|
||||
backend:
|
||||
driver: overlay
|
||||
internal: true
|
||||
egress:
|
||||
driver: overlay
|
||||
Reference in New Issue
Block a user