Object Storage na Magalu Cloud com Terraform

[Update 5 de Junho] este tutorial foi adaptado e publicado na documentação oficial da Magalu Cloud!

Infraestrutura como Código

Infraestrutura como código (IaC) é o nome dado a prática de utilizar arquivos de configuração em repositórios de código para descrever a infraestrutura de recursos computacionais (servidores, redes, armazenamento, bancos de dados, acessos, DNS, etc).

É comum em empresas que consomem serviços de nuvem o uso da ferramenta Terraform para organizar configurações de infraestrutura e para automatizar os provisionamentos dela.

O surgimento da Lu-vem

Por aqui, temos uma nova nuvem em construção, a MagaluCloud, que é também a empresa onde eu atualmente trabalho. É um projeto ambicioso, uma nova cloud pública, com duas regiões no Brasil, com suporte em português, preços em Reais e com muita gente boa no time.

Um dos primeiros produtos oferecidos por esta nuvem é o de Object Storage, que é uma solução escalável para armazenamento de dados compatível com o padrão AWS S3 da Amazon.

OpenTofu + S3 + Magalu Cloud

Abaixo segue um pequeno tutorial de como utilizar o OpenTofu, que é uma alternativa livre ao Terraform, para a criação e gerenciamento de recursos de Object Storage.

Pérrequisitos

Este tutorial assume que você tenha:

Projeto e Módulo Raíz

Vamos começar criando uma pasta para nosso projeto e um módulo de configuração raíz:

mkdir tutorial-tofu  
cd tutorial-tofu  
echo "terraform {}" > main.tf  
tofu validate  

Apesar de válida, esta configuração raíz está vazia. Módulos de Terraform utilizam plugins chamados de Providers que são extensões instaláveis que permitem o uso de diferentes produtos de diferentes nuvens.

Para este tutorial, vamos utilizar o provider hashicorp/aws (licença MPL v2.0), que habilita o uso de fornecedores de Object Storage compatíveis com o padrão S3. Para isto vamos aumentar nosso main.tf para ficar assim:

# main.tf

terraform {  
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "5.50.0"
    }
  }
}

provider "aws" {  
  # Configuration options
}

E utilizar o comando abaixo para instalar este plugin:

tofu init  

Voltaremos na linha comentada acima após criarmos um "profile aws" com credenciais de acesso no passo a seguir.

Configuração das credenciais

Por ser compatível com S3, o Object Storage da nuvem da Magalu funciona também com autenticação por API Key, no console você pode criar uma chave destas indo em Início > Object Storage > API Key, botão "Criar API Key"

Esta chave possui duas informações importantes para serem configuradas na sua máquina: ID e Secret. Para este tutorial, vamos declarar estas variáveis num arquivo de configuração de perfis da aws, que deve estar localizado no caminho ~/.aws/credentials, vamos dar o nome deste perfil de tutorial-tofu, substitua os textos em caixa alta COLE_AQUI_O... no exemplo abaixo pelo conteúdo do ID e do SECRET da chave recém criada:

mkdir -p ~/.aws  
cat << EOF >> ~/.aws/credentials  
[tutorial-tofu]
aws_access_key_id = COLE_AQUI_O_ID  
aws_secret_access_key = COLE_AQUI_O_SECRET

EOF  

O padrão destes perfis de aws é utilizar a nuvem da Amazon. Para funcionar com a nuvem da Magalu, é preciso mudar os campos endpoint_url e region, neste exemplo vou utilizar a região "Brasil - Nordeste 1", a br-ne1:

cat << EOF >> ~/.aws/config  
[profile tutorial-tofu]
endpoint_url = https://br-ne1.magaluobjects.com/  
region = br-ne1

EOF  

Se tudo deu certo, podemos voltar ao módulo raíz do nosso projeto e incluir este nome de perfil como o campo profile do bloco que configura o provider aws:

provider "aws" {  
  profile                     = "tutorial-tofu"
  skip_region_validation      = true
  skip_requesting_account_id  = true
  skip_credentials_validation = true
}

(este alinhamento dos sinais de igual é culpa do tofu fmt, não me xinguem)

Estes outros três atributos (skip isto, skip aquilo, skip aquilo outro) serão necessários também para o nosso caso, detalhes sobre cada um deles podem ser consultados na documentação do plugin.

Primeiro Bucket

Finalmente, podemos criar o primeiro recurso de nossa infraestrutura: um bucket para armazenarmos objetos dentro, vamos criar um novo módulo de configuração, um arquivo: resources.tf

# resources.tf

resource "aws_s3_bucket" "first_bucket" {  
  bucket_prefix = "tutorial-bucket"
}

Nota: Utilizei o campo bucket_prefix ao invés do campo bucket para nomear nosso primeiro contêiner pois, assim como na AWS, os nomes de buckets na Magalu Cloud também são globais e devem ser únicos, portanto ao invés de inventar um nome único, forneci só um prefixo e deixei para o provider a tarefa de preencher o resto do nome com um numero aleatório único.

Neste ponto, já podemos utilizar o comando tofu plan para planejar o provisionamento:

tofu plan  

Este comando lista as mudanças nos recursos que serão necessárias para refletir o estado final descrito nas configurações declarativas do projeto. No nosso caso, ele listará a criação de um bucket: "# aws_s3_bucket.first_bucket will be created"

E posteriormente aplicar este plano:

tofu apply -compact-warnings  

Ele vai exibir o plano novamente e pedir para você digitar yes.

Se tudo der certo este processo deve terminar com uma mensagem parecida com esta:

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

O estado deste novo recurso pode ser consultado com os comandos tofu state list e tofu state show

A criação deste bucket pode ser verificada também pela listagem do console via navegador:

Lembre-se de mudar a região para "Brasil -Nordeste 1" caso seu console esteja em outra região:

Mais recursos: objetos

Agora que temos um resource do tipo aws_s3_bucket criado, vamos utilizar um resource do tipo aws_s3_object para adicionar dois arquivos dentro deste bucket:

# resources.tf

resource "aws_s3_bucket" "first_bucket" {  
  bucket_prefix = "tutorial-bucket"
}

resource "aws_s3_object" "first_bucket_objects" {  
  for_each = tomap({
    file1 = "./main.tf"
    file2 = "./resources.tf"
  })
  bucket = aws_s3_bucket.first_bucket.id
  key    = each.value
  source = each.value
}

Um tofu plan vai nos dizer:

Plan: 2 to add, 0 to change, 0 to destroy.  

E o posterior tofu apply:

Apply complete! Resources: 2 added, 0 changed, 0 destroyed.  

Pronto, os dois arquivos estão na nuvem. Um tofu state list deve mostrar agora 3 resources:

$ tofu state list
aws_s3_bucket.first_bucket  
aws_s3_object.first_bucket_objects["file1"]  
aws_s3_object.first_bucket_objects["file2"]  

State do segundo object:

$ tofu state show 'aws_s3_object.first_bucket_objects["file2"]'
# aws_s3_object.first_bucket_objects["file2"]:
resource "aws_s3_object" "first_bucket_objects" {  
    arn                = "arn::s3:::tutorial-bucket20240523203657781600000001/resources.tf"
    bucket             = "tutorial-bucket20240523203657781600000001"
    bucket_key_enabled = false
    content_type       = "application/octet-stream"
    etag               = "01d689431df57be26625e8fb41ac04d2"
    force_destroy      = false
    id                 = "./resources.tf"
    key                = "./resources.tf"
    source             = "./resources.tf"
    storage_class      = "STANDARD"
    tags_all           = {}
}

Desprovisionando Tudo

Tão importante como saber criar coisas na nuvem, é saber como limpar tudo para no fim do mês não receber uma surpresa na conta do cartão de crédito. Este tutorial não estaria completo se não ensinasse como apagar estes recursos todos, por mais baratos que sejam:

tofu destroy  
Destroy complete! Resources: 3 destroyed.  

Configuração e Estado

Como visto acima, o comando tofu state tem funções para mostrar o que o sistema sabe sobre a instalação real da infraestrutura descrita no código dos módulos de configuração. Ter a declaração em arquivos de como as máquinas deveriam estar e saber como elas de fato estão são duas coisas diferentes, esta segunda parte é o "state", o estado da instalação.

Na minha config eu declarei que a instalação tinha que ter um bucket com um certo prefixo no nome e dois objetos dentro, com o caminho dos arquivos locais para cada objeto.

O estado vai ter bem mais informação que isto, como por exemplo o nome completo que o bucket recebeu, as etags de cada objeto e vários outros detalhes.

Um ls no diretório do projeto vai mostrar a existência de dois arquivos gerados que descrevem o estado atual e o estado anterior da minha instalação:

terraform.tfstate  terraform.tfstate.backup  

Um less no arquivo atual, depois do destroy, vai mostrar que o estado atual não possui nenhum recurso por exemplo:

$ less -F terraform.tfstate
{
  "version": 4,
  "terraform_version": "1.7.1",
  "serial": 7,
  "lineage": "51390cb1-af8e-d82b-99d6-1bb5da876657",
  "outputs": {},
  "resources": [],
  "check_results": null
}

Compartilhando o estado com o time

Num cenário onde várias pessoas vão trabalhar na mesma infra, ter as configs e este estado apenas local na máquina de quem executou o plano por último não é o ideal. Ao mesmo tempo, por conter informações sensíveis, não é recomendado colocar os arquivos tfstate.* num repositório git, especialmente se este repositório for público.

Uma possível saída para coordenar este trabalho em grupo e compartilhar de maneira privada o estado da instalação, é usar a funcionalidade de estado remoto, que suporta várias opções de onde armazenar este estado, implementados por diferentes backends.

Vamos então finalizar este tutorial com um exemplo de como configurar o OpenTofu para armazenar o estado remotamente num bucket de S3 da Magalu Clloud, utilizando o backend "s3".

Primeiro, vamos criar um bucket novo só para isto, num arquivo novo state-bucket.ft:

# state-bucket.tf

resource "aws_s3_bucket" "state_bucket" {  
  bucket_prefix = "tutorial-state-bucket"
}
tofu apply  

Agora vamos exibir na tela o nome do bucket gerado para copiar e usar na config do backend depois:

tofu state show aws_s3_bucket.state_bucket  

E finalmente, de volta no main.tf adicionamos este bloco backend "s3" ao bloco terraform

# main.tf

terraform {  
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "5.50.0"
    }
  }

  backend "s3" {
    bucket                      = "NOME_DO_SEU_BUCKET_DE_STATE_AQUI"
    key                         = "the_state"
    region                      = "br-ne1"
    profile                     = "tutorial-tofu"
    skip_region_validation      = true
    skip_requesting_account_id  = true
    skip_credentials_validation = true
    skip_s3_checksum            = true
  }
}

provider "aws" {  
  profile                     = "tutorial-tofu"
  skip_region_validation      = true
  skip_requesting_account_id  = true
  skip_credentials_validation = true
}

E para instalar o backend:

tofu init  

Nota num cenário mais real talvez o bucket de state não viveria no mesmo projeto, ou teria alguma prevenção tipo lifecycle { prevent_destroy = true }, ou seria criado manualmente. Eu criei junto aqui apenas para facilitar o tutorial.

Considerações finais

IaC e OpenTofu podem ajudar na organização e manutenção de sistemas na nuvem.

Esta introdução, apesar de ser na cloud da Magalu, utilizou um provider genérico de S3, optei por fazer assim para demonstrar compatibilidade, porém existe um provider de Terraform da MagaluCloud, que inclui outros recursos, como máquinas virtuais por exemplo, documentação aqui.

O tema é vasto, e eu não sou nenhum expert no assunto, escrevi este texto por estar também conhecendo este mundo. Se houver demanda posso continuar esta série, Terragrunt por exemplo é uma ferramenta que eu já ouvi falar mas nunca testei pessoalmente.

Remixe

Se achou parte deste artigo útil, por favor replique, este texto está licenciado sob a Creative Commons Atribuição 4.0.


Foto do cabeçalho:
Homemade Tofu - Cocoro Cafe, QVM AUD10 set by Alpha, on Flickr


se você gostou deste post e quiser receber novas atualizações deste blog por email, clique aqui

Fabricio Campos Zuardi

Read more posts by this author.