docs(template): swarm-stack-template.yml — modelo canonico para novas stacks (rede public, caddy labels, portainer API)
This commit is contained in:
@@ -0,0 +1,120 @@
|
|||||||
|
# Template Canônico — Stack Docker Swarm (Octal)
|
||||||
|
|
||||||
|
## Uso
|
||||||
|
```bash
|
||||||
|
# 1. Copiar template
|
||||||
|
cp runbooks/swarm-stack-template.yml runbooks/<NOME_STACK>.yml
|
||||||
|
|
||||||
|
# 2. Editar: services, imagens, réplicas, labels Caddy
|
||||||
|
vim runbooks/<NOME_STACK>.yml
|
||||||
|
|
||||||
|
# 3. Deploy
|
||||||
|
docker stack deploy -c runbooks/<NOME_STACK>.yml <NOME_STACK>
|
||||||
|
|
||||||
|
# 4. Aplicar labels Caddy via Docker CLI (obrigatório — Swarm não herda do compose)
|
||||||
|
docker service update \
|
||||||
|
--label-add 'caddy=<DOMINIO>' \
|
||||||
|
--label-add 'caddy.reverse_proxy={{upstreams <PORTA>}}' \
|
||||||
|
<NOME_STACK>_<SERVICE>
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Template Completo (v3.9)
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
version: '3.9'
|
||||||
|
|
||||||
|
networks:
|
||||||
|
public:
|
||||||
|
external: true # rede overlay swarm existente — NÃO criá-la aqui
|
||||||
|
|
||||||
|
services:
|
||||||
|
|
||||||
|
<service-name>:
|
||||||
|
image: <IMAGEM>:<TAG>
|
||||||
|
networks:
|
||||||
|
- public
|
||||||
|
deploy:
|
||||||
|
replicas: 1
|
||||||
|
endpoint_mode: dnsrr # DNS round-robin — necessário para reverse proxy
|
||||||
|
update_config: # zero-downtime deploy
|
||||||
|
parallelism: 1
|
||||||
|
delay: 10s
|
||||||
|
rollback_config: # rollback automático se falhar
|
||||||
|
parallelism: 1
|
||||||
|
delay: 10s
|
||||||
|
restart_policy:
|
||||||
|
condition: on-failure
|
||||||
|
max_attempts: 3
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Regras Obrigatórias
|
||||||
|
|
||||||
|
### 1. Rede `public`
|
||||||
|
- É a **única rede overlay attachable** do cluster
|
||||||
|
- Criada uma única vez por cluster: `docker network create --driver overlay --attachable public`
|
||||||
|
- Nunca declarar `external: false` em services — quebra Swarm
|
||||||
|
- Nunca criar nova rede por stack — causa fragmentação
|
||||||
|
|
||||||
|
### 2. Nomes de serviço
|
||||||
|
- **SEM prefixo** no compose: Swarm injeta `<stack>_` automaticamente
|
||||||
|
- Ex: `games-demo` no compose → `project_games-demo` no Swarm ✓
|
||||||
|
- Ex: `project_games-demo` no compose → `project_project_games-demo` ✗ (duplicado)
|
||||||
|
|
||||||
|
### 3. Labels Caddy
|
||||||
|
- **NÃO funcionam pelo compose** — `deploy.labels` não vira `Config.Labels`
|
||||||
|
- Aplicar **obrigatoriamente** via CLI após deploy:
|
||||||
|
`docker service update --label-add 'caddy=<DOMINIO>' <STACK>_<SERVICE>`
|
||||||
|
- Formato: `caddy=<dominio>` + `caddy.reverse_proxy={{upstreams <PORTA>}}`
|
||||||
|
|
||||||
|
### 4. Labels Traefik (fallback opcional)
|
||||||
|
- Funcionam pelo compose se colocadas em `deploy.labels`:
|
||||||
|
```yaml
|
||||||
|
deploy:
|
||||||
|
labels:
|
||||||
|
- traefik.enable=true
|
||||||
|
- traefik.http.routers.<NOME>.rule=Host(`<DOMINIO>`)
|
||||||
|
- traefik.http.services.<NOME>.loadbalancer.server.port=<PORTA>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. Registro no Portainer
|
||||||
|
```bash
|
||||||
|
# Login admin → JWT
|
||||||
|
JWT=$(curl -sk -X POST https://dock.octal.tec.br/api/auth \
|
||||||
|
-H 'Content-Type: application/json' \
|
||||||
|
--data-raw '{"Username":"admin","Password":"***"}' | python3 -c "import json,sys;print(json.loads(sys.stdin.read())['jwt'])")
|
||||||
|
|
||||||
|
# Criar stack (type=2 = SwarmStack)
|
||||||
|
curl -sk -X POST 'https://dock.octal.tec.br/api/stacks?method=string&type=2&endpointId=1' \
|
||||||
|
-H "Authorization: Bearer $JWT" \
|
||||||
|
-H 'Content-Type: application/json' \
|
||||||
|
-d '{"Name":"<NOME>","SwarmID":"plz2xbh64yzhgy88jb9stm0pc","StackFileContent":"'"$(cat <ARQUIVO>.yml)"'"}' | python3 -m json.tool
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Stacks Existentes (referência)
|
||||||
|
|
||||||
|
| Stack | ID | Serviços | Domínio |
|
||||||
|
|-------|-----|----------|---------|
|
||||||
|
| `git` | 9 | gitea | `git.octal.tec.br` |
|
||||||
|
| `bot` | 2 | beebot, redis | `ai.octal.tec.br` |
|
||||||
|
| `pro` | 7 | leantime, db | `manager.octal.tec.br` |
|
||||||
|
| `design` | 6 | penpot (7 svc) | — |
|
||||||
|
| `proxy` | — | caddy, test-octal | `test.octal.tec.br` |
|
||||||
|
| `project` | 12 | games-demo, projects-landing | `games.octal.tec.br` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
| Erro | Causa | Fix |
|
||||||
|
|------|-------|-----|
|
||||||
|
| `network public not manually attachable` | Rede `public` não existe ou `Attachable: False` | Criar com `docker network create --driver overlay --attachable public` |
|
||||||
|
| Serviços não aparecem no Portainer | Criado por `docker stack deploy` (CLI), não pela API | Registrar via API ou aceitar que está compatível Swarm nativo |
|
||||||
|
| Labels Caddy não aplicam | `deploy.labels` não vira `Config.Labels` no Swarm | Usar `docker service update --label-add` |
|
||||||
|
| Duplicação de nome (`project_project_X`) | Nomes com prefixo no compose | Remover prefixo, deixar Swarm injetar |
|
||||||
|
| 401 na API Portainer | Token `ptr_` não é JWT | Login `/api/auth` admin → JWT |
|
||||||
Reference in New Issue
Block a user