Skip to content

Commit d73a8bd

Browse files
authored
Merge pull request #8 from studies-org/docs/readme
docs: README completo
2 parents 5b7538b + 37f0ef4 commit d73a8bd

1 file changed

Lines changed: 197 additions & 1 deletion

File tree

‎README.md‎

Lines changed: 197 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,197 @@
1-
# studies-lab-multicloud-terraform
1+
<h1 align="center">
2+
Lab Multicloud com Terraform: site estático com load balancer na AWS e na Azure
3+
</h1>
4+
5+
<p align="center">
6+
<img src="docs/demo.webp" alt="Página do site estático: a cada recarga o load balancer entrega uma instância diferente (web-01 a web-04), com tema claro e escuro" />
7+
</p>
8+
9+
<p align="center">
10+
<a href="https://skillicons.dev">
11+
<img src="https://skillicons.dev/icons?i=terraform,aws,azure,githubactions,bash,html,css,docker,nginx" alt="Stacks" />
12+
</a>
13+
</p>
14+
15+
## Qual a finalidade do projeto?
16+
17+
Laboratório de estudo de **Terraform multicloud**, que nasceu dos estudos na graduação em Cloud da FIAP. A ideia é subir **a mesma aplicação nas duas nuvens**: um site estático servido por **4 servidores web atrás de um load balancer**, uma vez na **AWS** e outra na **Azure**, com o código organizado do mesmo jeito nas duas.
18+
19+
Cada nuvem tem a sua raiz Terraform com **três módulos simétricos** (`network`, `compute` e `lb`). O site é uma página só, que cada servidor preenche no primeiro boot com os próprios dados (nome, região, zona, hostname e IP), então dá para ver o balanceamento acontecendo: a cada recarga, outra instância responde.
20+
21+
## Arquitetura
22+
23+
<p align="center">
24+
<img src="docs/arch.gif" alt="Arquitetura: usuários acessam o ALB na AWS (4 EC2 em duas subnets públicas) e o Load Balancer na Azure (4 VMs em duas subnets); GitHub Actions dispara o Terraform" />
25+
</p>
26+
27+
## O que foi construído
28+
29+
### Módulos (iguais nas duas nuvens)
30+
31+
| Módulo | AWS (`terraform/aws`) | Azure (`terraform/azure`) |
32+
|---|---|---|
33+
| `network` | VPC, Internet Gateway, 2 subnets públicas (us-east-1a e us-east-1c), route table | VNet, 2 subnets, NSG com HTTP 80 (e SSH opcional) |
34+
| `compute` | Security group, 4 EC2 Amazon Linux 2023 com Apache, IMDSv2 obrigatório | Availability set, 4 NICs, 4 VMs Ubuntu 22.04 com Apache, login só por chave SSH |
35+
| `lb` | Application Load Balancer, target group com health check, listener HTTP 80 | IP público e Load Balancer Standard, probe HTTP, regra de entrada e regra de saída |
36+
37+
### Entradas principais
38+
39+
| AWS | Azure | Padrão |
40+
|---|---|---|
41+
| `instance_count` | `vm_count` | `4` |
42+
| `instance_type` | `vm_size` | `t3.micro` / `Standard_B1s` |
43+
| `public_subnets` | `subnets` | 2 subnets `/24` |
44+
| `ssh_allowed_cidrs` | `ssh_allowed_cidrs` | `[]` (SSH fechado) |
45+
| `key_name` | `admin_ssh_public_key` | opcional / obrigatório |
46+
| · | `dns_label` | obrigatório (nome DNS do IP público) |
47+
48+
### Saídas
49+
50+
| Output | O que traz |
51+
|---|---|
52+
| `site_url` | URL do site pelo load balancer (DNS do ALB ou FQDN do IP público da Azure) |
53+
| `instances` / `vms` | Mapa `nome => IP privado` dos 4 servidores |
54+
55+
### Segurança
56+
57+
| Ponto | Como ficou |
58+
|---|---|
59+
| Senha das VMs | Não existe: a Azure usa só chave SSH (`disable_password_authentication = true`) |
60+
| SSH | Fechado por padrão nas duas nuvens; abre só para os CIDRs de `ssh_allowed_cidrs` |
61+
| HTTP nas EC2 | Liberado só a partir do security group do ALB |
62+
| Metadados da EC2 | IMDSv2 obrigatório (`http_tokens = "required"`) |
63+
| Estado | Backend remoto com configuração parcial (`backend.hcl`, fora do git); `*.tfvars` e `*.tfstate` no `.gitignore` |
64+
| Pipeline | `apply` e `destroy` só por disparo manual |
65+
66+
### Pipelines
67+
68+
| Workflow | Quando roda | O que faz |
69+
|---|---|---|
70+
| `ci.yml` | todo push e PR | `terraform fmt -check`, `init -backend=false` e `validate` na AWS e na Azure; `shellcheck` do script do site; sobe a simulação local e confere que as 4 instâncias respondem |
71+
| `deploy.yml` | só `workflow_dispatch` | Escolhe a nuvem e a ação (`plan`, `apply` ou `destroy`) |
72+
73+
## Tecnologias utilizadas
74+
75+
- **Terraform** (`>= 1.5`) com os providers **AWS** (`>= 5.70`) e **AzureRM** (`>= 4.5`);
76+
- **AWS:** VPC, EC2, Application Load Balancer;
77+
- **Azure:** Virtual Network, NSG, Linux Virtual Machines, Load Balancer Standard;
78+
- **Apache httpd** nas instâncias, configurado por **user data / cloud-init**;
79+
- **HTML e CSS** puros no site (sem dependências externas, com tema escuro automático);
80+
- **GitHub Actions** para CI e deploy manual;
81+
- **Docker Compose + nginx** para a simulação local do balanceamento.
82+
83+
## Estrutura do repositório
84+
85+
```text
86+
studies-lab-multicloud-terraform/
87+
├── terraform/
88+
│ ├── aws/
89+
│ │ ├── versions.tf # Providers e backend S3 (parcial)
90+
│ │ ├── variables.tf · main.tf · outputs.tf
91+
│ │ ├── terraform.tfvars.example · backend.hcl.example
92+
│ │ └── modules/
93+
│ │ ├── network/ # VPC, IGW, subnets, route table
94+
│ │ ├── compute/ # SG, EC2 e user-data.sh.tftpl
95+
│ │ └── lb/ # ALB, target group, listener
96+
│ └── azure/
97+
│ ├── versions.tf # Provider e backend azurerm (parcial)
98+
│ ├── variables.tf · main.tf · outputs.tf
99+
│ ├── terraform.tfvars.example · backend.hcl.example
100+
│ └── modules/
101+
│ ├── network/ # VNet, subnets, NSG
102+
│ ├── compute/ # Availability set, NICs, VMs e cloud-init.sh.tftpl
103+
│ └── lb/ # IP público, LB, probe, regras
104+
├── site/
105+
│ ├── index.html # Página com marcadores {{...}}
106+
│ └── render.sh # Preenche os marcadores no boot
107+
├── local/
108+
│ ├── docker-compose.yml # 4 Apache + nginx em round-robin
109+
│ └── nginx.conf
110+
├── .github/workflows/ # ci.yml e deploy.yml
111+
└── docs/ # arch.gif e demo.webp
112+
```
113+
114+
## Fluxo de funcionamento
115+
116+
1. O Terraform lê `site/index.html` e `site/render.sh` e embute os dois (em base64) no user data de cada EC2 e no `custom_data` de cada VM.
117+
2. No primeiro boot, o script instala o Apache, consulta o serviço de metadados da nuvem (IMDSv2 na AWS, IMDS na Azure) para saber região e zona, e roda o `render.sh`, que troca os marcadores pelo nome, hostname e IP da máquina.
118+
3. O load balancer checa a saúde de cada servidor com `GET /` na porta 80 e só manda tráfego para os saudáveis.
119+
4. O usuário abre o `site_url`; a cada requisição o balanceador escolhe um servidor, e a página mostra qual respondeu.
120+
5. Na Azure as VMs não têm IP público: entram pelo LB e saem para a internet (para o `apt`) pela regra de outbound do próprio LB.
121+
122+
## Como rodar
123+
124+
### Simulação local (sem conta em nuvem)
125+
126+
```bash
127+
docker compose -f local/docker-compose.yml up -d --wait
128+
# abra http://localhost:8080 e recarregue a página
129+
docker compose -f local/docker-compose.yml down
130+
```
131+
132+
Sobe 4 containers Apache com a mesma página, preenchida pelo mesmo `render.sh`, atrás de um nginx em round-robin fazendo o papel do load balancer. É daí que vem a demo do topo. A porta muda com `LOCAL_PORT=9090`.
133+
134+
### AWS
135+
136+
```bash
137+
cd terraform/aws
138+
cp backend.hcl.example backend.hcl # bucket e tabela de lock do tfstate
139+
cp terraform.tfvars.example terraform.tfvars
140+
terraform init -backend-config=backend.hcl
141+
terraform apply
142+
terraform output site_url
143+
```
144+
145+
### Azure
146+
147+
```bash
148+
az login
149+
export ARM_SUBSCRIPTION_ID="<id-da-assinatura>"
150+
cd terraform/azure
151+
cp backend.hcl.example backend.hcl # storage account do tfstate
152+
cp terraform.tfvars.example terraform.tfvars # dns_label único
153+
export TF_VAR_admin_ssh_public_key="$(cat ~/.ssh/id_ed25519.pub)"
154+
terraform init -backend-config=backend.hcl
155+
terraform apply
156+
terraform output site_url
157+
```
158+
159+
Para só testar sem backend remoto: `terraform init -backend=false`. Ao terminar: `terraform destroy`.
160+
161+
### Pelo GitHub Actions
162+
163+
O `deploy.yml` roda pela aba **Actions → Deploy (manual)**, escolhendo a nuvem e a ação. Ele usa o environment com o nome da nuvem e espera estes secrets:
164+
165+
| Secret / variável | Nuvem |
166+
|---|---|
167+
| `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_SESSION_TOKEN` | AWS |
168+
| `ARM_CLIENT_ID`, `ARM_CLIENT_SECRET`, `ARM_TENANT_ID`, `ARM_SUBSCRIPTION_ID` | Azure |
169+
| `ADMIN_SSH_PUBLIC_KEY` (secret) e `AZURE_DNS_LABEL` (variável) | Azure |
170+
| `BACKEND_HCL`: conteúdo do `backend.hcl` da nuvem | as duas |
171+
172+
## Como validar a entrega
173+
174+
Sem credenciais (é o que o CI faz):
175+
176+
```bash
177+
TF="docker run --rm -v $PWD:/w -w /w hashicorp/terraform:1.9"
178+
$TF fmt -check -recursive
179+
for c in aws azure; do
180+
docker run --rm -v "$PWD":/w -w /w/terraform/$c hashicorp/terraform:1.9 init -backend=false
181+
docker run --rm -v "$PWD":/w -w /w/terraform/$c hashicorp/terraform:1.9 validate
182+
done
183+
```
184+
185+
Simulação local: recarregar `http://localhost:8080` 8 vezes deve mostrar `web-01`, `web-02`, `web-03` e `web-04`.
186+
187+
Com a infraestrutura no ar:
188+
189+
- `terraform output site_url` devolve o endereço do ALB (AWS) ou `<dns_label>.brazilsouth.cloudapp.azure.com` (Azure);
190+
- `curl -s <site_url> | grep -o 'web-<em>[0-9]*'` (ou `vm-`) muda de instância entre as chamadas. O ALB distribui por requisição; o Load Balancer da Azure distribui por conexão (hash de 5 tuplas), então no navegador, que reaproveita a conexão, a troca aparece melhor com `curl` ou numa aba anônima;
191+
- o target group da AWS e o probe da Azure mostram os 4 servidores saudáveis;
192+
- a página mostra a região e a zona reais lidas do serviço de metadados;
193+
- `terraform destroy` remove tudo ao final.
194+
195+
## Autor
196+
197+
**William Coelho** · RM 556336 · [@willtechdev](https://github.com/willtechdev)

0 commit comments

Comments
 (0)