# 🚀 Guia Prático: Orquestração com Docker Compose
**Disciplina:** Desenvolvimento de Sistemas Distribuídos  
**Ambiente:** Docker Desktop (Windows 10 / WSL 2)  

---

## 🎯 Objetivo da Aula
Aprender a orquestrar uma arquitetura distribuída composta por **3 serviços distintos e ultraleves**:
1. **`web`**: API em Python (Flask) que processa requisições HTTP e registra acessos.
2. **`redis`**: Banco de dados chave-valor em memória atuando como cache persistente.
3. **`redis-commander`**: Interface gráfica web para visualização e gerenciamento do Redis em tempo real.

---

## 📁 1. Estrutura do Projeto

Crie uma pasta no seu computador chamada `contador-distribuido` e insira os 3 arquivos descritos a seguir:

```text
contador-distribuido/
├── app.py
├── Dockerfile
└── docker-compose.yml
```

---

## 📄 2. Código dos Arquivos

### A) `app.py` (Aplicação Web em Python)
```python
from flask import Flask
import redis

app = Flask(__name__)

# O hostname 'redis' é resolvido automaticamente pelo DNS interno do Docker Compose
cache = redis.Redis(host='redis', port=6379)

@app.route('/')
def index():
    # Incrementa o contador no Redis
    visitas = cache.incr('contador_acessos')
    return f'''
    <div style="text-align:center; font-family:sans-serif; margin-top:50px;">
        <h1>🚀 Sistemas Distribuídos - Aula de Docker Compose</h1>
        <h2>Esta página foi acessada <b>{visitas}</b> vezes.</h2>
        <p>Serviço Web (Flask) <---> Cache (Redis) <---> Painel (Redis Commander)</p>
    </div>
    '''

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)
```

### B) `Dockerfile` (Imagem da Aplicação Web)
```dockerfile
FROM python:3.11-alpine

WORKDIR /app

# Instala apenas as dependências essenciais
RUN pip install --no-cache-dir flask redis

COPY app.py .

EXPOSE 5000

CMD ["python", "app.py"]
```

### C) `docker-compose.yml` (Orquestrador da Pilha de Serviços)
```yaml
version: '3.8'

services:
  # Serviço 1: Aplicação Web
  web:
    build: .
    ports:
      - "5000:5000"
    environment:
      - PYTHONUNBUFFERED=1
    depends_on:
      - redis
    networks:
      - rede-aula

  # Serviço 2: Cache Redis (Persistente)
  redis:
    image: redis:alpine
    ports:
      - "6379:6379"
    volumes:
      - dados-redis:/data
    networks:
      - rede-aula

  # Serviço 3: Interface Gráfica de Gerenciamento do Redis
  redis-commander:
    image: rediscommander/redis-commander:latest
    environment:
      - REDIS_HOSTS=local:redis:6379
    ports:
      - "8081:8081"
    depends_on:
      - redis
    networks:
      - rede-aula

# Volume para manter os dados salvos no disco
volumes:
  dados-redis:

# Rede privada onde os 3 contêineres conversam pelo nome do serviço
networks:
  rede-aula:
    driver: bridge
```

---

## 🛠️ 3. Sequência de Execução no Terminal

Abra o terminal (PowerShell ou WSL) dentro da pasta `contador-distribuido`:

1. **Subir toda a infraestrutura em segundo plano:**
   ```bash
   docker compose up -d
   ```

2. **Verificar os contêineres em execução:**
   ```bash
   docker compose ps
   ```
   *(Deverão aparecer os 3 serviços ativos: `web`, `redis` e `redis-commander`).*

3. **Testar os serviços no navegador:**
   * **Aplicação Web:** Acesse [http://localhost:5000](http://localhost:5000) (Atualize a página várias vezes para ver o contador aumentar).
   * **Painel Redis Commander:** Acesse [http://localhost:8081](http://localhost:8081) para visualização do cache em tempo real.

4. **Inspecionar os logs unificados da aplicação:**
   ```bash
   docker compose logs -f web
   ```
   *(Pressione `Ctrl + C` para sair da visualização dos logs).*

5. **Parar e remover todos os contêineres, redes e volumes:**
   ```bash
   docker compose down
   ```

---

## 💡 4. Conceitos Fundamentais para Explicar na Lousa
* **Service Discovery (Resolução de DNS Interno):** O contêiner `web` e o `redis-commander` encontram o banco de dados usando o nome de serviço `redis`, eliminando a necessidade de configurar endereços IP manualmente.
* **Isolamento de Redes (`networks`):** Todos os 3 contêineres fazem parte da `rede-aula`, permitindo comunicação direta e segura entre eles.
* **Persistência com Volumes (`volumes`):** O volume `dados-redis` garante que a contagem de acessos persista mesmo se os contêineres forem reiniciados ou reconstruídos.

---

## 📝 5. Questões de Fixação e Estudo Dirigido

### Questão 1: Arquitetura — Contêineres vs. Máquinas Virtuais
Qual a diferença fundamental na arquitetura de execução entre um contêiner Docker e uma Máquina Virtual (VM) tradicional? Como o Docker consegue ser mais leve e inicializar mais rápido?

---

### Questão 2: Imagens e Snapshots — Estrutura em Camadas e Copy-on-Write (CoW)
As imagens Docker são compostas por uma pilha de camadas somente leitura (read-only snapshots). Explique como funciona o mecanismo de *Copy-on-Write* (CoW) quando um contêiner em execução precisa modificar um arquivo existente na imagem base.

---

### Questão 3: Otimização de Build e Reutilização de Cache
Ao escrever um `Dockerfile`, por que é recomendado colocar instruções de cópia do código-fonte (`COPY . .`) após a instalação das dependências do projeto (ex: `RUN pip install ...`)? O que acontece com o cache de camadas se alterarmos o arquivo `app.py`?

---

### Questão 4: Docker Compose vs. Docker CLI
Qual é o papel principal do Docker Compose na engenharia de software distribuída? Explique a diferença entre gerenciar a infraestrutura via linha de comando individual (`docker run`) versus a abordagem declarativa usando o arquivo `docker-compose.yml`.

---

### Questão 5: Redes Docker — O Driver `bridge`
No arquivo `docker-compose.yml` da aula, definimos uma rede chamada `rede-aula` com o `driver: bridge`. Como o driver de rede *bridge* funciona? Qual é a diferença crucial entre a rede bridge padrão (`default bridge`) do Docker e uma rede bridge personalizada definida no Compose?

---

### Questão 6: Service Discovery e Resolução de Nomes (DNS Interno)
No código Python (`app.py`), a conexão com o banco de dados é feita via `cache = redis.Redis(host='redis', port=6379)`. Como o contêiner `web` consegue encontrar o IP do contêiner `redis` usando apenas a palavra `redis` como hostname? Quem é responsável por essa resolução de nomes?

---

### Questão 7: Mapeamento de Portas e Conflitos no Hospedeiro
Na definição do serviço `web`, temos a configuração `ports: - "5000:5000"`. Explique o significado do número à esquerda e do número à direita dos dois-pontos. O que aconteceria se tentássemos subir um segundo serviço no Docker Compose mapeando a porta `"5000:8000"`?

---

### Questão 8: Persistência de Dados — Volumes vs. Container Layer
Se o contêiner do `redis` for destruído (`docker rm`), o que acontece com os dados gravados se **não** utilizarmos volumes? De que forma a declaração `volumes: - dados-redis:/data` garante a durabilidade dos dados em sistemas distribuídos?

---

### Questão 9: Dependência entre Serviços — `depends_on`
A instrução `depends_on:` no serviço `web` garante que o contêiner `redis` seja iniciado antes dele. No entanto, em sistemas distribuídos reais, por que o `depends_on` sozinho pode não ser suficiente para evitar falhas de conexão na inicialização da aplicação?

---

### Questão 10: Ciclo de Vida — `docker compose stop` vs. `docker compose down`
Qual é a diferença prática entre executar `docker compose stop` e `docker compose down` no terminal? O que acontece com os contêineres, as redes virtuais e os volumes persistentes em cada um desses comandos?

---

## 🔑 Gabarito e Respostas Esperadas

<details>
<summary><b>Clique para expandir o Gabarito das Questões</b></summary>

1. **Resposta Q1:** Máquinas Virtuais emulam um hardware completo e rodam um Sistema Operacional convidado (Guest OS) inteiro sobre um hipervisor. O Docker compartilha o Kernel do Sistema Operacional hospedeiro (Host OS) e isola os processos da aplicação utilizando recursos nativos do Kernel Linux (como *namespaces* e *cgroups*). Por não precisar inicializar um Kernel próprio nem emular hardware, o contêiner consome frações de RAM/CPU e inicia em milissegundos.
2. **Resposta Q2:** As camadas da imagem Docker são *read-only* (snapshots imutáveis). Quando um contêiner é iniciado, o Docker adiciona uma camada fina gravável (*container layer*) no topo da pilha. Se o contêiner tenta modificar um arquivo de uma camada inferior, o sistema de arquivos copia o arquivo original para a camada gravável (*Copy-on-Write*) e aplica a alteração ali. O arquivo original na imagem permanece intacto.
3. **Resposta Q3:** Cada instrução no `Dockerfile` gera uma camada em cache. Se uma camada muda, todas as camadas subsequentes têm seu cache invalidado. Colocar a cópia do código-fonte (que muda frequentemente) após a instalação de dependências (que mudam raramente) garante que o Docker reutilize o cache da instalação de pacotes, tornando os builds subsequentes muito mais rápidos.
4. **Resposta Q4:** O Docker Compose é uma ferramenta de orquestração declarativa multicontêiner. Em vez de rodar múltiplos comandos imperativos `docker run` com flags complexas no terminal, o Compose centraliza a definição da arquitetura (serviços, redes, volumes e variáveis de ambiente) em um único arquivo YAML, permitindo subir, parar e monitorar toda a infraestrutura com comandos unificados (`docker compose up / down`).
5. **Resposta Q5:** O driver `bridge` cria uma ponte de rede de software privada dentro do hospedeiro, isolando os contêineres da rede externa. A diferença fundamental é que a rede `default bridge` não possui servidor DNS interno habilitado (exigindo links manuais ou IPs), enquanto **redes bridge personalizadas** (criadas pelo Compose) possuem resolução automática de nomes por DNS interno habilitada por padrão.
6. **Resposta Q6:** O Docker Compose cria um servidor DNS interno automático na rede privada (`rede-aula`). Quando a aplicação busca por `host='redis'`, o DNS interno traduz o nome do serviço declarado no `docker-compose.yml` para o IP privado atual do contêiner `redis` na rede virtual.
7. **Resposta Q7:** O formato é `"PORTA_HOSPEDEIRO:PORTA_CONTAINER"`. `"5000:5000"` mapeia a porta 5000 da máquina física (Windows) para a porta 5000 interna do contêiner. Se outro contêiner tentar mapear `"5000:8000"`, ocorrerá um erro de conflito de porta (`bind: address already in use`), pois a porta 5000 do hospedeiro já estará ocupada.
8. **Resposta Q8:** Sem volumes, todas as alterações de dados são gravadas na camada temporária do contêiner e são permanentemente apagadas quando o contêiner é removido. Os `volumes` são diretórios gerenciados pelo Docker diretamente no sistema de arquivos do hospedeiro (fora da camada do contêiner), garantindo que os dados persistam independentemente do ciclo de vida dos contêineres.
9. **Resposta Q9:** O `depends_on` garante apenas a ordem de **inicialização do processo/contêiner** (a ordem em que o Docker dispara os contêineres), mas **não garante que o serviço interno (ex: banco de dados) esteja pronto para aceitar conexões de rede**. Se o Redis demorar alguns segundos para inicializar a memória, a aplicação Web pode tentar se conectar antes e falhar. Em produção, usa-se mecanismos de *healthcheck* ou reconexão resiliente no código da aplicação.
10. **Resposta Q10:** 
    * `docker compose stop`: Apenas interrompe a execução dos processos dos contêineres, mantendo a estrutura do contêiner, as redes e os volumes intactos no disco.
    * `docker compose down`: Interrompe e **remove** os contêineres e as redes virtuais criadas. Por padrão, preserva os volumes (a menos que seja passada a flag `-v`).

Após responder todas as questões, compare suas respostas com as do gabarito no arquivo .zip com senha 1234

</details>
