# Guia de Migração: App Service → Container Apps

> **MORPH-SPEC Framework**
> Quando seu projeto crescer além do Free Tier

---

## 📊 Quando Migrar?

### Sinais de que é hora de migrar

| Indicador | Descrição | Ação |
|-----------|-----------|------|
| 🔴 **CPU Quota Esgotada** | Erro `CPU quota exceeded` frequente | Migrar imediatamente |
| 🔴 **Cold Starts Críticos** | App dormindo afeta UX/negócio | Avaliar impacto → Migrar |
| 🟡 **Tráfego Crescente** | >500 req/dia consistente | Planejar migração |
| 🟡 **SSL Customizado** | Necessita domínio próprio com SSL | Avaliar alternativas |
| 🟢 **Background Jobs** | Precisa rodar Hangfire 24/7 | Migrar ou separar worker |

### Matriz de Decisão

```
Tráfego < 100 req/dia       → App Service Free F1
Tráfego 100-1000 req/dia    → App Service B1 ($13/mês)
Tráfego > 1000 req/dia      → Container Apps (~$5-15/mês)
Background Jobs 24/7        → Container Apps com minReplicas: 1
SSL Customizado             → Container Apps ou App Service B1+
```

---

## 🗺️ Roadmap de Migração

### Fase 1: Preparação (1-2 dias)

#### 1.1. Auditoria do Código

**Checklist:**
- [ ] App usa configuração via `IConfiguration`
- [ ] Não há paths hardcoded (usar `IWebHostEnvironment`)
- [ ] Logging para stdout/stderr (não arquivos locais)
- [ ] Health checks implementados (`/health`, `/health/ready`)
- [ ] Secrets em Key Vault (não `appsettings.json`)

**Verificar:**
```csharp
// ✅ BOM: Configuração via IConfiguration
var connectionString = configuration["ConnectionStrings:Default"];

// ❌ RUIM: Hardcoded
var path = "C:\\Data\\uploads";

// ✅ BOM: Usar IWebHostEnvironment
var path = Path.Combine(webHostEnv.ContentRootPath, "uploads");
```

#### 1.2. Criar Dockerfile

```dockerfile
# ============================================
# MORPH-SPEC - .NET 8 Blazor Container
# ============================================

FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
WORKDIR /app
EXPOSE 8080
EXPOSE 8081

# Create non-root user
RUN adduser --disabled-password --gecos '' appuser && chown -R appuser /app
USER appuser

FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src
COPY ["YourApp/YourApp.csproj", "YourApp/"]
RUN dotnet restore "YourApp/YourApp.csproj"
COPY . .
WORKDIR "/src/YourApp"
RUN dotnet build "YourApp.csproj" -c Release -o /app/build

FROM build AS publish
RUN dotnet publish "YourApp.csproj" -c Release -o /app/publish /p:UseAppHost=false

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .

# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD curl -f http://localhost:8080/health || exit 1

ENTRYPOINT ["dotnet", "YourApp.dll"]
```

#### 1.3. Implementar Health Checks

```csharp
// Program.cs
builder.Services.AddHealthChecks()
    .AddSqlServer(
        connectionString: builder.Configuration["ConnectionStrings:Default"],
        name: "sql",
        timeout: TimeSpan.FromSeconds(3))
    .AddCheck("self", () => HealthCheckResult.Healthy());

var app = builder.Build();

app.MapHealthChecks("/health", new HealthCheckOptions
{
    Predicate = _ => true,
    ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
});

app.MapHealthChecks("/health/ready", new HealthCheckOptions
{
    Predicate = check => check.Tags.Contains("ready")
});

app.MapHealthChecks("/health/live", new HealthCheckOptions
{
    Predicate = _ => false
});
```

---

### Fase 2: Setup Azure Container Registry (ACR)

#### 2.1. Criar ACR via Bicep

```bicep
// acr.bicep
resource acr 'Microsoft.ContainerRegistry/registries@2023-01-01-preview' = {
  name: 'acr${uniqueString(resourceGroup().id)}'
  location: location
  sku: {
    name: 'Basic'  // $5/mês
  }
  properties: {
    adminUserEnabled: false  // Usar Managed Identity
  }
}
```

#### 2.2. Deploy ACR

```bash
# Via Azure CLI
az deployment group create \
  --resource-group rg-myapp-dev \
  --template-file acr.bicep

# Obter ACR login server
ACR_NAME=$(az acr list -g rg-myapp-dev --query "[0].name" -o tsv)
ACR_LOGIN_SERVER=$(az acr show -n $ACR_NAME --query loginServer -o tsv)
```

#### 2.3. Build e Push Imagem

**Via Azure DevOps Pipeline:**
```yaml
# staging-pipeline.yml
- task: Docker@2
  displayName: 'Build and push container'
  inputs:
    containerRegistry: 'ACR-Connection'
    repository: 'myapp'
    command: 'buildAndPush'
    Dockerfile: 'Dockerfile'
    tags: |
      v1.0.0
      staging-latest
```

**Via Azure CLI (local):**
```bash
# Build local e push para ACR
az acr build \
  --registry $ACR_NAME \
  --image myapp:latest \
  --image myapp:v1.0.0 \
  --file Dockerfile \
  .
```

---

### Fase 3: Deploy Container Apps

#### 3.1. Atualizar Parameters

**parameters.dev.json**
```json
{
  "hostingType": {
    "value": "containerapp"
  },
  "containerImage": {
    "value": "acrXXXX.azurecr.io/myapp:latest"
  }
}
```

#### 3.2. Deploy Infraestrutura

**Via Azure DevOps Pipeline:**
```yaml
# Use o template pronto
- template: templates/infra-deploy.yml
  parameters:
    azureSubscription: 'Azure-Staging-Connection'
    resourceGroupName: 'rg-myapp-staging'
    bicepTemplateFile: 'content/.morph/templates/infra/main.bicep'
    parametersFile: 'content/.morph/templates/infra/parameters.staging.json'
```

**Via Azure CLI (local):**
```bash
# Deploy via Azure CLI
az deployment group create \
  --resource-group rg-myapp-dev \
  --template-file main.bicep \
  --parameters @parameters.dev.json
```

#### 3.3. Configurar Managed Identity para ACR

```bash
# Obter Container App Managed Identity
CA_PRINCIPAL_ID=$(az containerapp show \
  -n ca-myapp-dev \
  -g rg-myapp-dev \
  --query identity.principalId -o tsv)

# Dar permissão AcrPull
az role assignment create \
  --assignee $CA_PRINCIPAL_ID \
  --role AcrPull \
  --scope $(az acr show -n $ACR_NAME --query id -o tsv)
```

---

### Fase 4: Teste e Validação

#### 4.1. Smoke Tests

```bash
# Obter URL do Container App
CA_URL=$(az containerapp show \
  -n ca-myapp-dev \
  -g rg-myapp-dev \
  --query properties.configuration.ingress.fqdn -o tsv)

# Testar health checks
curl https://$CA_URL/health
curl https://$CA_URL/health/ready

# Testar funcionalidade principal
curl https://$CA_URL/api/status
```

#### 4.2. Load Test (opcional)

```bash
# Instalar hey (HTTP load generator)
go install github.com/rakyll/hey@latest

# Simular carga
hey -z 30s -c 10 https://$CA_URL/
```

#### 4.3. Validar Auto-Scaling

```bash
# Gerar carga
hey -z 2m -c 50 https://$CA_URL/

# Monitorar réplicas
watch -n 2 "az containerapp revision list \
  -n ca-myapp-dev \
  -g rg-myapp-dev \
  --query '[0].properties.replicas' -o tsv"
```

---

### Fase 5: Cutover (Mudança de DNS)

#### 5.1. Estratégia Blue-Green

```bash
# Manter App Service rodando (blue)
# Container Apps já rodando (green)

# Testar green profundamente
# Quando confiante, mudar DNS para green
# Aguardar 24h
# Se OK, desligar blue (App Service)
```

#### 5.2. Atualizar DNS

Se usando domínio customizado:

```
# Old (App Service)
myapp.azurewebsites.net → CNAME → myapp.com

# New (Container Apps)
myapp.region.azurecontainerapps.io → CNAME → myapp.com
```

---

## 💰 Comparativo de Custos

### Antes (App Service Free F1)
```
App Service F1:       $0/mês
Azure SQL Free:       $0/mês
App Insights Free:    $0/mês
────────────────────────────
TOTAL:                $0/mês
```

### Depois (Container Apps)
```
Container Apps:       $0-8/mês   (scale-to-zero, uso variável)
ACR Basic:            $5/mês
Azure SQL Free:       $0/mês
App Insights Free:    $0/mês
────────────────────────────
TOTAL:                $5-13/mês
```

### Alternativa (App Service B1)
```
App Service B1:       $13/mês    (always-on)
Azure SQL Free:       $0/mês
App Insights Free:    $0/mês
────────────────────────────
TOTAL:                $13/mês
```

**Análise:**
- Container Apps mais econômico que B1 para tráfego variável
- Container Apps melhor auto-scaling que App Service
- Container Apps suporta múltiplos containers (arquitetura futura)

---

## 🔄 Rollback Plan

### Se algo der errado

#### Opção 1: Rollback via DNS (mais rápido)
```bash
# Reverter CNAME para App Service
# Tempo: ~5 minutos (propagação DNS)
```

#### Opção 2: Rollback via código
```bash
# Atualizar parameters.dev.json
{
  "hostingType": {
    "value": "appservice"
  }
}

# Redeploy
az deployment group create \
  --resource-group rg-myapp-dev \
  --template-file main.bicep \
  --parameters @parameters.dev.json
```

---

## ✅ Checklist Final

### Antes de Desligar App Service

- [ ] Container Apps rodando estável por 7+ dias
- [ ] Todos os health checks passando
- [ ] Monitoramento no App Insights funcionando
- [ ] Logs sendo coletados corretamente
- [ ] Performance igual ou melhor que App Service
- [ ] Custo dentro do esperado
- [ ] Backup do código e configuração
- [ ] Equipe treinada no novo ambiente

### Pós-Migração

- [ ] Deletar App Service Plan (economizar recursos)
- [ ] Deletar deployment slots antigos
- [ ] Atualizar documentação interna
- [ ] Atualizar `.morph/project.md` com nova infra
- [ ] Arquivar configurações antigas

---

## 🔄 Automatizar com CI/CD

### Azure DevOps Pipelines

Após migração manual bem-sucedida, automatize com pipelines:

**1. Configurar Workload Identity:**
```bash
# Ver guia completo em: .azure/docs/azure-devops-setup.md

# Criar App Registration
az ad app create --display-name "myapp-staging-pipeline"

# Configurar federated credential
az ad app federated-credential create \
  --id <APP_ID> \
  --parameters @federated-credential.json
```

**2. Usar Pipeline Pronto:**
```bash
# Copiar pipeline para seu projeto
cp .azure/pipelines/staging-pipeline.yml azure-pipelines.yml

# Configurar variáveis no Azure DevOps:
# - ACR_NAME
# - APP_NAME
# - SUBSCRIPTION_ID

# Commit e push
git add azure-pipelines.yml
git commit -m "ci: add staging pipeline"
git push origin main
```

**3. Deploy Automático:**
- Dev: Triggado automaticamente em push para `develop`
- Staging: Triggado automaticamente em push para `main`
- Prod: Manual ou via tag `v*`

**Pipelines inclusos no framework:**
- `.azure/pipelines/dev-pipeline.yml` - App Service Free F1
- `.azure/pipelines/staging-pipeline.yml` - Container Apps (scale-to-zero)
- `.azure/pipelines/prod-pipeline.yml` - Container Apps (always-on, com approval)

---

## 📚 Referências

- [Azure Container Apps Docs](https://learn.microsoft.com/azure/container-apps/)
- [ACR Best Practices](https://learn.microsoft.com/azure/container-registry/container-registry-best-practices)
- [Dockerfile Best Practices](https://docs.docker.com/develop/dev-best-practices/)
- [Health Checks ASP.NET](https://learn.microsoft.com/aspnet/core/host-and-deploy/health-checks)
- [Azure DevOps Workload Identity](https://learn.microsoft.com/azure/devops/pipelines/library/connect-to-azure)
- [MORPH-SPEC Azure DevOps Setup](.azure/docs/azure-devops-setup.md)

---

## 🆘 Troubleshooting

### Container não inicia

```bash
# Ver logs do container
az containerapp logs show \
  -n ca-myapp-dev \
  -g rg-myapp-dev \
  --follow

# Ver eventos do container
az containerapp revision list \
  -n ca-myapp-dev \
  -g rg-myapp-dev \
  --query '[0].properties.provisioningState'
```

### Erro de autenticação ACR

```bash
# Verificar permissões Managed Identity
az role assignment list \
  --assignee $CA_PRINCIPAL_ID \
  --scope $(az acr show -n $ACR_NAME --query id -o tsv)

# Recriar permissão se necessário
az role assignment create \
  --assignee $CA_PRINCIPAL_ID \
  --role AcrPull \
  --scope $(az acr show -n $ACR_NAME --query id -o tsv)
```

### App lento ou timeout

```bash
# Verificar recursos
az containerapp show \
  -n ca-myapp-dev \
  -g rg-myapp-dev \
  --query 'properties.template.containers[0].resources'

# Aumentar CPU/memória se necessário
az containerapp update \
  -n ca-myapp-dev \
  -g rg-myapp-dev \
  --cpu 0.5 \
  --memory 1Gi
```

---

*MORPH-SPEC by Polymorphism Tech*
