Deploy: variáveis de ambiente configuráveis + docs dos dois modos do Coolify
TZ (com tzdata), GUNICORN_TIMEOUT e GUNICORN_LOG_LEVEL agora têm default no Dockerfile e podem ser sobrescritos via Coolify → Environment Variables ou .env (modelo em .env.example). TZ corrige o carimbo "exportada em" (sessao.marcar_exportada), que sem isso saía em UTC. Workers continua literal em -w 1, de propósito — não é variável. Cabeçalhos do Dockerfile e docker-compose.yml reescritos para descrever os dois modos de deploy no Coolify (Application/Dockerfile, em produção desde 04/09, e Docker Compose) e o caminho de volume que de fato funciona: Directory mount em /data/coolify/applications/<uuid>/clientes, dados enviados por scp da pasta ses-mg/. .gitattributes fixa LF também em Dockerfile, docker-compose.yml e .env.example — o autocrlf do Windows já tinha reescrito o Dockerfile para CRLF uma vez nesta máquina. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
+35
-8
@@ -4,10 +4,23 @@
|
||||
# clientes/ses-mg/contrato/itens.yaml em tempo de execução (regras_sd.carregar_canonico),
|
||||
# consulta contrato/linhas-os.yaml se existir (fallback da linha de OS; ausente,
|
||||
# `linha_os` é exigido no SD.md) e grava SD.md de volta em
|
||||
# clientes/ses-mg/projetos/ (escrita_sd.gravar). Esses
|
||||
# são dados do cliente, versionados fora do git — entram por VOLUME montado em
|
||||
# /app/clientes. Sem esse volume, a imagem sobe e a tela inicial responde, mas o
|
||||
# primeiro upload falha com "Arquivo canônico ausente".
|
||||
# clientes/ses-mg/projetos/ (escrita_sd.gravar). Esses são dados do cliente,
|
||||
# versionados fora do git — entram por VOLUME montado em /app/clientes. Sem esse
|
||||
# volume, a imagem sobe e a tela inicial responde, mas o primeiro upload falha
|
||||
# com "Arquivo canônico ausente". E sem linhas-os.yaml no volume, o VPS se
|
||||
# comporta diferente do local: o fallback desliga e `linha_os` vira pendência.
|
||||
#
|
||||
# DOIS MODOS de deploy no Coolify — a imagem é a mesma, muda onde se configura:
|
||||
#
|
||||
# 1. Application (Dockerfile) — o que está em produção (2026-09-04). O Coolify
|
||||
# IGNORA o docker-compose.yml. Volume: Persistent storage → Add mount →
|
||||
# "Directory mount", Source /data/coolify/applications/<uuid>/clientes
|
||||
# (o Coolify sugere esse caminho; ele sobrevive a redeploy) → Destination
|
||||
# /app/clientes. Variáveis: aba Environment Variables. Réplicas: 1.
|
||||
# 2. Docker Compose — usa o docker-compose.yml; volume e variáveis vêm de lá.
|
||||
#
|
||||
# Por isso TODO default de variável mora AQUI (ENV + ${VAR:-default} no CMD),
|
||||
# e o compose só espelha: variável que existe só no compose não existe no VPS.
|
||||
#
|
||||
# Build local: docker build -t exporta-sd .
|
||||
# Run local: docker run --rm -p 5000:5000 -v "$PWD/clientes:/app/clientes" exporta-sd
|
||||
@@ -17,18 +30,31 @@ FROM python:3.13-slim
|
||||
# PYTHONUNBUFFERED: log do gunicorn sai na hora no painel do Coolify.
|
||||
# LANG/LC_ALL em UTF-8: identificadores de SD trazem "·" (P2·SD8) e o
|
||||
# escrita_sd monta nome de arquivo a partir deles.
|
||||
# TZ: sessao.marcar_exportada carimba datetime.now() na tela ("exportada em");
|
||||
# sem isto o container está em UTC e o horário sai 3h adiantado.
|
||||
#
|
||||
# Variáveis que o operador pode sobrescrever (Coolify → Environment Variables,
|
||||
# ou .env com o compose — ver .env.example). Os defaults valem nos dois modos:
|
||||
# PORT porta do gunicorn dentro do container
|
||||
# TZ fuso do carimbo de exportação
|
||||
# GUNICORN_TIMEOUT segundos por request (upload + análise da SD)
|
||||
# GUNICORN_LOG_LEVEL debug | info | warning | error
|
||||
ENV PYTHONUNBUFFERED=1 \
|
||||
PYTHONDONTWRITEBYTECODE=1 \
|
||||
LANG=C.UTF-8 \
|
||||
LC_ALL=C.UTF-8 \
|
||||
PORT=5000
|
||||
PORT=5000 \
|
||||
TZ=America/Sao_Paulo \
|
||||
GUNICORN_TIMEOUT=120 \
|
||||
GUNICORN_LOG_LEVEL=info
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# gosu: o entrypoint precisa iniciar como root (só root faz chown no volume
|
||||
# recém-montado) e depois derrubar privilégio para `sd` antes do gunicorn.
|
||||
# tzdata: zoneinfo para o TZ acima ter efeito (a slim não garante que venha).
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends gosu \
|
||||
&& apt-get install -y --no-install-recommends gosu tzdata \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Dependências em camada própria: mudar código não reinstala pacote.
|
||||
@@ -67,8 +93,9 @@ HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
|
||||
# é a global ATUAL no processo do servidor.py (uma SD por vez, em memória).
|
||||
# Com dois workers, dois requests do MESMO usuário caem em processos
|
||||
# diferentes e a sessão desaparece de forma aleatória. Pelo mesmo motivo, o
|
||||
# número de réplicas no Coolify tem de ficar em 1.
|
||||
# número de réplicas no Coolify tem de ficar em 1 — e é por isso que o número
|
||||
# de workers NÃO é variável de ambiente: não existe valor certo além de 1.
|
||||
#
|
||||
# --chdir app + servidor:app: o servidor.py insere ../scripts no sys.path a
|
||||
# partir de __file__, então funciona igual sob gunicorn e sob `python app/servidor.py`.
|
||||
CMD ["sh", "-c", "exec gunicorn --chdir app servidor:app -w 1 -b 0.0.0.0:${PORT:-5000} --timeout 120 --access-logfile - --error-logfile -"]
|
||||
CMD ["sh", "-c", "exec gunicorn --chdir app servidor:app -w 1 -b 0.0.0.0:${PORT:-5000} --timeout ${GUNICORN_TIMEOUT:-120} --log-level ${GUNICORN_LOG_LEVEL:-info} --access-logfile - --error-logfile -"]
|
||||
|
||||
Reference in New Issue
Block a user