Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -143,3 +143,6 @@ dmypy.json
cython_debug/

/data

# terraform
.terraform*
54 changes: 53 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,13 @@ VENV_DIR = .venv

# Define o nome do comando para o Python
PYTHON = python3
DOCKER_IMAGE_NAME = eml-api
REPO_NAME = $(DOCKER_IMAGE_NAME)
AWS_REGION = us-east-1
ACCOUNT_ID = $(shell aws sts get-caller-identity --query Account --output text)
REPO_URI = $(ACCOUNT_ID).dkr.ecr.$(AWS_REGION).amazonaws.com/$(REPO_NAME)
API_PORT = 8000
TERRAFORM_BUCKET = eml-terraform-bucket-$(ACCOUNT_ID)

.PHONY: venv
venv:
Expand Down Expand Up @@ -42,7 +49,7 @@ install-pre-commit: install-dependencies
@echo "Instalando hooks de pre-commit"
$(VENV_DIR)/bin/poetry run pre-commit install --hook-type pre-push --hook-type post-checkout --hook-type pre-commit

## [PADRÃO] Prepara todo o repositório com o poetry e pre-commit
## Prepara todo o repositório com o poetry e pre-commit
.PHONY: init
init: install-pre-commit
$(VENV_DIR)/bin/poetry run dvc remote add -f s3bucket s3://framework-eml-a3data/dvc/
Expand All @@ -69,6 +76,51 @@ lint:
format:
$(VENV_DIR)/bin/poetry run ruff format

## Builda a imagem docker da API
.PHONY: build-image
build-image:
docker build -t $(DOCKER_IMAGE_NAME) -f deployment/docker/Dockerfile .


.PHONY: create-ecr
create-ecr:
chmod +x deployment/scripts/create_ecr.sh
./deployment/scripts/create_ecr.sh $(REPO_NAME) $(AWS_REGION)

## Faz o push da última versão da imagem para o repositório ECR
.PHONY: push-image
push-image: create-ecr
docker tag "$(DOCKER_IMAGE_NAME):latest" "$(REPO_URI):latest"
aws ecr get-login-password --region "$(AWS_REGION)" | docker login --username AWS --password-stdin "$(REPO_URI)"
docker push "$(REPO_URI):latest"

## Inicia a API localmente
.PHONY: api
api: build-image
@echo "Lançando a API localmente..."
docker run -p $(API_PORT):$(API_PORT) $(DOCKER_IMAGE_NAME):latest


.PHONY: create-bucket
create-bucket:
@aws s3api head-bucket --bucket $(TERRAFORM_BUCKET) 2>/dev/null || \
(aws s3api create-bucket --bucket $(TERRAFORM_BUCKET) --region $(AWS_REGION); \
echo "Bucket $(TERRAFORM_BUCKET) criado.")

## Cria toda a infraestrutura do deploy da API, desconsiderando a parte do Docker
.PHONY: create-infra
create-infra: create-bucket
terraform -chdir="deployment/infrastructure/terraform" init -backend-config="bucket=$(TERRAFORM_BUCKET)" -backend-config="region=$(AWS_REGION)"
terraform -chdir="deployment/infrastructure/terraform" apply -auto-approve

## Faz todo o deploy, desde de construir a imagem docker até provisionar toda infraestrutura
.PHONY: deploy
deploy: build-image push-image create-infra

## Deleta toda infraestrutura provisionada anteriormente
.PHONY: destroy
destroy:
terraform -chdir="deployment/infrastructure/terraform" destroy -auto-approve

#################################################################################
# Self Documenting Commands #
Expand Down
67 changes: 55 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Este repositório apresenta um pipeline de Machine Learning completo utilizando o dataset Iris. Ele cobre as etapas de download de dados, pré-processamento, treinamento de modelos e predição.

## Estrutura do repositório
## Estrutura do repositório (desatualizada)

```
├── api # Código da API para interagir com o modelo
Expand Down Expand Up @@ -65,7 +65,16 @@ Este repositório apresenta um pipeline de Machine Learning completo utilizando

Antes de começar, certifique-se de ter as seguintes dependências instaladas:

- Python 3.8+
- WSL (caso voce esteja usando Windows):
- Abra um powershell e execute os comandos abaixo na ordem.
- `wsl --set-default-version 2`
- `wsl --install -d Ubuntu`
- `wsl --set-default Ubuntu`
- Daqui em diante, execute todos os comandos dentro do terminal do wsl.
- Python 3.10+ - https://www.python.org/downloads/
- VSCode - https://code.visualstudio.com/download
- O VSCode permite instalar extensões, é recomendado instalar ao menos a extensão que integra o VSCode ao WSL.
- Cole o seguinte ID na aba de extensões: `ms-vscode-remote.remote-wsl`

## Passo a Passo para preparar o ambiente e executar treino

Expand Down Expand Up @@ -120,14 +129,42 @@ Você pode realizar previsões de duas formas:
#### Previsão em batch

Para realizar previsões em batch utilizando um arquivo CSV de entrada:

```
python -m src.pipelines.predict predict-batch caminho/para/arquivo.csv

```
#### Previsão via linha de comando

Para realizar previsões passando as features diretamente pela linha de comando:

```
python -m src.pipelines.predict predict 5.1 3.5 1.4 0.2
```

## API - Como testar local e como fazer deploy na nuvem

Caso você ainda não tenha ativado, ative o ambiente virtual
```
source venv/bin/activate
```

Para lançar a API basta executar o comando do make, lembrando que é necessário ter o Docker instalado (https://docs.docker.com/desktop/install/windows-install/).
```
make api
```

Você pode fazer qualquer alteração nos arquivos dentro da pasta `api`, só é necessário que no arquivo main.py dentro da pasta esteja o objeto `FastAPI` com o nome `app`, que é representado pela linha: `app = FastAPI()`

Após ficar satisfeito com a API você pode fazer o deploy dela na nuvem.

Para isso, algumas ferramentas precisam ser baixadas e configuradas, faça a instalação no WSL caso esteja usando o Windows, então siga as instruções do Linux:
1. AWS CLI v2: https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html
2. Terraform: https://developer.hashicorp.com/terraform/install?product_intent=terraform

Na AWS será necessário configurar as credenciais da conta que será provisionado a infraestrutura, utilize o comando `aws configure` e coloque as chaves.

Com essas configurações pontas, para fazer o deploy faça:
```
make deploy
```

## Features do repositório

Expand Down Expand Up @@ -157,13 +194,19 @@ O DVC (Data Version Control) é uma ferramenta open-source que facilita o contro
## Comandos do Makefile

O projeto inclui um Makefile para facilitar o gerenciamento do ambiente e das dependências. Alguns comandos disponíveis:

- **make init**: Configura o ambiente virtual, instala as dependências e configura o pre-commit e o DVC.
- **make clean**: Remove o ambiente virtual e desinstala o pre-commit.
- **make update**: Atualiza as dependências do Poetry.
- **make lint**: Executa o lint no código-fonte com o Ruff.
- **make format**: Formata o código-fonte com o Ruff.
```
make init Prepara todo o repositório com o poetry e pre-commit
make clean Remove todo o ambiente virtual e desconfigura o pre-commit
make update Atualiza as dependências no poetry, útil quando alterar bibliotecas em pyproject.toml
make lint Lint usando ruff (use `make format` para formatação)
make format Formata o código fonte com ruff
make build-image Builda a imagem docker da API
make push-image Faz o push da última versão da imagem para o repositório ECR
make api Inicia a API localmente
make create-infra Cria toda a infraestrutura do deploy da API, desconsiderando a parte do Docker
make deploy Faz todo o deploy, desde de construir a imagem docker até provisionar toda infraestrutura
```

## Boas Práticas para Commits com Pré-commit

- Durante o commit, o Pré-commit verifica e corrige a formatação do código, mas não inclui essas alterações automaticamente no commit. É necessário executar `git add` novamente para registrar essas modificações. O Git não faz isso automaticamente para que você tenha controle total sobre o que será comitado.
- Durante o commit, o Pré-commit verifica e corrige a formatação do código, mas não inclui essas alterações automaticamente no commit. É necessário executar `git add` novamente para registrar essas modificações. O Git não faz isso automaticamente para que você tenha controle total sobre o que será comitado.
26 changes: 26 additions & 0 deletions deployment/docker/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Use a imagem base do Python
FROM python:3.10-slim

# Define a pasta de trabalho dentro do container
WORKDIR /app

# Instala o Poetry via pip
RUN pip install poetry

# Copia o arquivo pyproject.toml para instalar dependências
COPY pyproject.toml .

# Instala as dependências do projeto sem utilizar o poetry.lock
RUN poetry lock \
&& poetry install --no-dev

COPY artifacts/ artifacts/
COPY api/ api/
COPY src/ src/
COPY config/ config/

# Expõe a porta 8000 para o servidor FastAPI
EXPOSE 8000

# Comando para rodar a aplicação FastAPI com Uvicorn
CMD ["poetry", "run", "uvicorn", "api.app.main:app", "--host", "0.0.0.0", "--port", "8000"]
28 changes: 28 additions & 0 deletions deployment/infrastructure/terraform/ec2-iam.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
resource "aws_iam_role" "ecs_instance_role" {
name = "${local.default_prefix}-ecs-instance-role"

assume_role_policy = jsonencode({
Version = "2012-10-17",
Statement = [{
Effect = "Allow",
Principal = {
Service = "ec2.amazonaws.com"
},
Action = "sts:AssumeRole"
}]
})

tags = {
Name = "${local.default_prefix}-ecs-instance-role"
}
}

resource "aws_iam_role_policy_attachment" "ecs_instance_role_policy" {
role = aws_iam_role.ecs_instance_role.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceforEC2Role"
}

resource "aws_iam_instance_profile" "ecs_instance_profile" {
name = "${local.default_prefix}-ecs-instance-profile"
role = aws_iam_role.ecs_instance_role.name
}
43 changes: 43 additions & 0 deletions deployment/infrastructure/terraform/ec2.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
resource "tls_private_key" "private_key" {
algorithm = "RSA"
rsa_bits = 2048
}

resource "aws_key_pair" "generated_key" {
key_name = "${local.default_prefix}-ec2-key"
public_key = tls_private_key.private_key.public_key_openssh
}


data "aws_ami" "ecs_ami" {
most_recent = true
owners = ["amazon"]

filter {
name = "name"
values = ["amzn2-ami-ecs-hvm-*-x86_64-ebs"]
}
}

resource "aws_instance" "ecs_instance" {
ami = data.aws_ami.ecs_ami.id
instance_type = var.ec2_type
key_name = aws_key_pair.generated_key.key_name
iam_instance_profile = aws_iam_instance_profile.ecs_instance_profile.name

user_data = <<-EOF
#!/bin/bash
echo ECS_CLUSTER=${aws_ecs_cluster.eml_cluster.name} >> /etc/ecs/ecs.config
EOF

subnet_id = data.aws_subnets.default.ids[0]

vpc_security_group_ids = [aws_security_group.app_sg.id]

associate_public_ip_address = true

tags = {
Name = "${local.default_prefix}-ecs-instance"
}
depends_on = [aws_ecs_cluster.eml_cluster]
}
82 changes: 82 additions & 0 deletions deployment/infrastructure/terraform/ecs-iam.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
resource "aws_iam_role" "ecs_execution_role" {
name = "${local.default_prefix}-execution-role"
assume_role_policy = jsonencode({
Version = "2012-10-17",
Statement = [
{
Effect = "Allow",
Principal = {
Service = "ecs-tasks.amazonaws.com"
},
Action = "sts:AssumeRole"
}
]
})
}

resource "aws_iam_role" "ecs_task_role" {
name = "${local.default_prefix}-task-role"
assume_role_policy = jsonencode({
Version = "2012-10-17",
Statement = [
{
Effect = "Allow",
Principal = {
Service = "ecs-tasks.amazonaws.com"
},
Action = "sts:AssumeRole"
}
]
})
}

resource "aws_iam_policy" "ecr_policy" {
name = "${local.default_prefix}_ECR_Access_Policy"
description = "Allows ECS execution role to pull from ECR"
policy = jsonencode({
Version = "2012-10-17",
Statement = [
{
Effect = "Allow",
Action = [
"ecr:GetAuthorizationToken",
"ecr:BatchCheckLayerAvailability",
"ecr:GetDownloadUrlForLayer",
"ecr:BatchGetImage"
],
Resource = "*"
}
]
})
}

resource "aws_iam_policy" "logs_service_policy" {
name = "${local.default_prefix}_Logs_Service_Policy"
description = "Allows service to log"
policy = jsonencode({
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": [
"${aws_cloudwatch_log_group.ecs_service_logs.arn}:*"
]
}
]
})
}

resource "aws_iam_role_policy_attachment" "ecr_policy_attachment" {
role = aws_iam_role.ecs_execution_role.name
policy_arn = aws_iam_policy.ecr_policy.arn
}

resource "aws_iam_role_policy_attachment" "log_policy_attachment" {
role = aws_iam_role.ecs_execution_role.name
policy_arn = aws_iam_policy.logs_service_policy.arn
}
Loading