Dia 30: Projecto Final no Linux — Stack Web, DB e CI/CD
Chegámos ao último dia do curso Linux 30 Dias. Ao longo de 29 dias cobrimos desde a linha de comandos básica até à administração de sistemas, redes, segurança, automação, backups, contentores e orquestração. Hoje juntamos tudo num projecto final: uma stack completa com servidor web, base de dados, monitorização e CI/CD, implantada com Docker Compose e automatizada com GitHub Actions.
ℹ Neste artigo:
1. Introdução — Revisão dos 29 Dias
Antes de mergulhar no projecto final, vale a pena recapitular o que aprendemos ao longo deste curso de 30 dias. Cada dia construiu uma competência que hoje se combina num deploy completo.
| Dias | Tema | Competências-chave |
|---|---|---|
| 1–5 | Fundamentos | Shell, ficheiros, permissões, processos, pacotes |
| 6–10 | Administração | systemd, utilizadores, rede, firewall, SSH |
| 11–15 | Armazenamento | LVM, RAID, backups, NFS, quotas |
| 16–20 | Segurança e Rede | DNS, SSL/TLS, SELinux, VPN, auditoria |
| 21–25 | Automação | Shell scripts, cron, Ansible, Terraform, Git |
| 26–30 | Contentores e DevOps | Docker, Compose, Kubernetes, monitorização, CI/CD |
O projecto final vai usar especificamente competências dos dias 21–30: Docker, Docker Compose, Prometheus, Grafana e GitHub Actions. Mas o conhecimento dos dias anteriores — rede, systemd, SSH, segurança — é o que permite operar esta stack em produção de forma fiável.
✓ Objectivo do Dia 30: Implantar uma stack de produção com Nginx + PostgreSQL + Prometheus + Grafana, com pipeline CI/CD que testa e faz a implantação automaticamente a cada push.
2. Arquitectura da Stack
A stack final tem cinco componentes que comunicam entre si através de uma rede Docker dedicada. Cada componente tem uma responsabilidade clara:
| Componente | Imagem | Função | Porta |
|---|---|---|---|
| Nginx | nginx:alpine | Servidor web / reverse proxy | 8080 |
| App | python:3.12-slim | Aplicação Flask com healthcheck | 5000 |
| PostgreSQL | postgres:16-alpine | Base de dados relacional | 5432 |
| Prometheus | prom/prometheus | Recolha de métricas | 9090 |
| Grafana | grafana/grafana | Dashboards e alertas | 3000 |
O fluxo de pedidos é o seguinte: o cliente liga ao Nginx (porta 8080), que faz proxy reverso para a aplicação Flask (porta 5000). A aplicação lê e escreve no PostgreSQL (porta 5432). O Prometheus faz scraping de métricas da aplicação a cada 15 segundos. O Grafana visualiza os dados recolhidos pelo Prometheus.
⚠ Nota: Em produção real, as portas do PostgreSQL e da aplicação não devem estar expostas ao hospedeiro. Apenas o Nginx (8080), Prometheus (9090) e Grafana (3000) precisam de acesso externo. As restantes comunicam apenas na rede interna Docker.
3. Docker Compose — Web + DB
O docker-compose.yml define todos os serviços, redes e volumes. Começamos pelos serviços de aplicação e base de dados.
A aplicação Flask expõe um endpoint /metrics no formato Prometheus, e um endpoint /health para verificar se está viva.
# docker-compose.yml
version: "3.9"
services:
db:
image: postgres:16-alpine
container_name: stack_db
environment:
POSTGRES_USER: appuser
POSTGRES_PASSWORD: \${DB_PASSWORD}
POSTGRES_DB: appdb
volumes:
- pgdata:/var/lib/postgresql/data
networks:
- internal
healthcheck:
test: ["CMD-SHELL", "pg_isready -U appuser -d appdb"]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
app:
build: ./app
container_name: stack_app
depends_on:
db:
condition: service_healthy
environment:
DATABASE_URL: postgresql://appuser:\${DB_PASSWORD}@db:5432/appdb
networks:
- internal
expose:
- "5000"
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:5000/health"]
interval: 15s
timeout: 5s
retries: 3
restart: unless-stopped
web:
image: nginx:alpine
container_name: stack_web
depends_on:
- app
ports:
- "8080:80"
volumes:
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
networks:
- internal
restart: unless-stopped
volumes:
pgdata:
networks:
internal:
driver: bridge
A configuração do Nginx faz proxy reverso para a aplicação e bloqueia acessos directos à base de dados:
# nginx/default.conf
server {
listen 80;
server_name localhost;
location / {
proxy_pass http://app:5000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location /metrics {
proxy_pass http://app:5000/metrics;
allow 172.16.0.0/12;
deny all;
}
}
ℹ Dica: O bloco allow/deny no /metrics garante que só a rede interna Docker (172.16.0.0/12) consegue aceder às métricas, impedindo exposição pública de dados de monitorização.
4. Monitorização — Prometheus + Grafana
O monitorização é adicionado ao mesmo docker-compose.yml. O Prometheus recolhe métricas da aplicação via scraping HTTP, e o Grafana visualiza essas métricas em dashboards.
A configuração do Prometheus define os alvos de scraping (targets):
# prometheus/prometheus.yml
global:
scrape_interval: 15s
evaluation_interval: 15s
scrape_configs:
- job_name: "app"
static_configs:
- targets: ["app:5000"]
metrics_path: /metrics
- job_name: "prometheus"
static_configs:
- targets: ["localhost:9090"]
Adicionamos os serviços de monitorização ao Compose:
# Adicionar ao docker-compose.yml
prometheus:
image: prom/prometheus:latest
container_name: stack_prometheus
ports:
- "9090:9090"
volumes:
- ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro
- promdata:/prometheus
networks:
- internal
restart: unless-stopped
grafana:
image: grafana/grafana:latest
container_name: stack_grafana
depends_on:
- prometheus
ports:
- "3000:3000"
environment:
GF_SECURITY_ADMIN_PASSWORD: \${GRAFANA_PASSWORD}
volumes:
- grafanadata:/var/lib/grafana
networks:
- internal
restart: unless-stopped
volumes:
pgdata:
promdata:
grafanadata:
Métricas essenciais a monitorizar nesta stack:
| Métrica | Componente | Alerta sugerido |
|---|---|---|
| http_requests_total | App | Taxa de pedidos > 1000/s |
| http_request_duration_seconds | App | P99 > 2s |
| pg_up | PostgreSQL | Valor = 0 (DB em baixo) |
| container_memory_usage_bytes | Docker | > 80% do limite |
| up | Prometheus | Target down por > 1min |
✓ Boa prática: Configurar alertas no Grafana para enviar notificações via Slack ou email quando uma métrica ultrapassa o limiar definido. Ver documentação oficial do Grafana Alerting.
5. CI/CD com GitHub Actions
O pipeline de CI/CD automatiza o teste e o deploy. A cada push para o ramo main, o GitHub Actions executa os testes da aplicação, constrói a imagem Docker e faz a implantação no servidor.
O workflow tem três jobs: test, build e deploy. Cada job só corre se o anterior passar.
# .github/workflows/ci-cd.yml
name: CI/CD Pipeline
on:
push:
branches: [main]
pull_request:
branches: [main]
env:
IMAGE_NAME: stack-app
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- run: pip install -r app/requirements.txt pytest
- run: cd app && python -m pytest tests/ -v
build:
needs: test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
with:
username: \${{ secrets.DOCKER_USER }}
password: \${{ secrets.DOCKER_PASS }}
- uses: docker/build-push-action@v5
with:
context: ./app
push: true
tags: |
\${{ env.IMAGE_NAME }}:latest
\${{ env.IMAGE_NAME }}:\${{ github.sha }}
deploy:
needs: build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Deploy via SSH
uses: appleboy/ssh-action@v1
with:
host: \${{ secrets.DEPLOY_HOST }}
username: \${{ secrets.DEPLOY_USER }}
key: \${{ secrets.DEPLOY_KEY }}
script: |
cd /opt/stack
export DB_PASSWORD=\$(cat /opt/stack/.env | grep DB_PASSWORD | cut -d= -f2)
export GRAFANA_PASSWORD=\$(cat /opt/stack/.env | grep GRAFANA_PASSWORD | cut -d= -f2)
docker compose pull
docker compose up -d --force-recreate
docker image prune -f
| Job | Trigger | Acção |
|---|---|---|
| test | push + PR | Executa pytest |
| build | push main | Constroi e publica imagem |
| deploy | push main | SSH + docker compose up |
⚠ Segurança: Os secrets DEPLOY_KEY, DOCKER_PASS e DB_PASSWORD devem ser configurados em Settings > Secrets and variables > Actions no repositório GitHub. Nunca commitar credenciais no código. Ver GitHub Actions Secrets.
6. Implantação e Teste
Com os ficheiros prontos, o a implantação é simples. No servidor, clonamos o repositório, configuramos as variáveis de ambiente e arrancamos a stack.
# 1. Clonar o repositório no servidor
git clone https://github.com/utilizador/stack-final.git /opt/stack
cd /opt/stack
# 2. Criar ficheiro .env com credenciais
cat > .env << EOF
DB_PASSWORD=alterar123
GRAFANA_PASSWORD=alterar456
EOF
# 3. Arrancar a stack completa
docker compose up -d
# 4. Verificar se todos os containers estão a correr
docker compose ps
# 5. Ver logs em tempo real
docker compose logs -f --tail=50
Verificar cada serviço individualmente:
# Testar o servidor web (Nginx)
curl -s http://localhost:8080/ | head -5
# Verificar health da aplicação
curl -s http://localhost:8080/health
# Esperado: {"status":"ok"}
# Confirmar que o Prometheus recolhe métricas
curl -s http://localhost:9090/api/v1/targets | python3 -m json.tool | grep health
# Esperado: "health": "up"
# Aceder ao Grafana (browser)
# http://SERVIDOR:3000 (admin / GRAFANA_PASSWORD)
Comandos de gestão do dia a dia:
# Reiniciar um serviço específico
docker compose restart app
# Actualizar imagem após novo build
docker compose pull app && docker compose up -d app
# Ver consumo de recursos por container
docker stats --no-stream
# Backup da base de dados
docker exec stack_db pg_dump -U appuser appdb > backup_$(date +%F).sql
# Parar e remover todos os containers (mantém volumes)
docker compose down
# Parar e remover volumes (CUIDADO: apaga dados)
docker compose down -v
ℹ Teste de CI/CD: Para testar o pipeline sem esperar por um push, forçar manualmente o workflow em Actions > CI/CD Pipeline > Run workflow. Isto é útil para validar mudanças de configuração antes de as integrar no main.
7. Conclusão e Próximos Passos
Parabéns por chegar ao fim do curso Linux 30 Dias. Neste último dia juntámos Docker Compose, Prometheus, Grafana e GitHub Actions numa stack completa e pronta para produção. O resultado é um sistema onde cada commit no repositório desencadeia testes automáticos, build de imagem e deploy — exactamente o que uma equipa DevOps faz no dia a dia.
Resumo do que esta stack implementa:
- Servidor web com Nginx como reverse proxy e SSL termination
- Aplicação Flask com healthcheck e endpoint de métricas Prometheus
- Base de dados PostgreSQL com volume persistente e healthcheck
- Monitorização com Prometheus (recolha) e Grafana (dashboards + alertas)
- CI/CD com GitHub Actions — test, build e implantação automáticos
- Rede isolada — apenas portas necessárias expostas ao hospedeiro
Próximos passos para evoluir este projecto:
| Melhoria | Benefício |
|---|---|
| Migrar para Kubernetes | auto-escala, rollouts e resiliência multi-nó |
| Adicionar certificados TLS | HTTPS automático com Let's Encrypt / Certbot |
| Centralizar logs | ELK stack ou Loki + Promtail |
| Configurar alertas | Alertmanager + Slack/Telegram |
| Blue-green implantação | Zero downtime em actualizações |
✓ Conclusão: Em 30 dias passámos de ls e cd a uma stack completa de produção com CI/CD. O caminho de aprendizagem não para aqui — Kubernetes, service mesh, observabilidade avançada e segurança em contentores são os próximos degraus. Continua a praticar, a automatizar e a documentar.
Obrigado por acompanhar o curso Linux 30 Dias. Se tiveres dúvidas ou quiseres partilhar o teu projecto final, deixa um comentário abaixo. Boas implantações!