Skip to main content

Baremetal AWS Terraform

A ideia do projeto é com um clique provisionar todo o cluster kubernetes.

Requisitos

Terraform

É necessário o binário do Terraform. A instalação nada mais é do que mover o binário para o path do seu sistema que você tenha permissão, ou adicione o repo e faça a instalação.

wget -O- https://apt.releases.hashicorp.com/gpg | gpg --dearmor | sudo tee /usr/share/keyrings/hashicorp-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
sudo apt update && sudo apt install terraform

Conta na AWS

O projeto do Terraform criará recursos da AWS em seu nome, ele precisa ter acesso ao ID da chave de acesso e à chave de acesso secreta de um usuário do IAM em sua conta da AWS. Você precisa dessa variável de ambiente carregada em seu sistema quando for executar os comandos do terraform.

export AWS_ACCESS_KEY_ID=<AccessKeyID>
export AWS_SECRET_ACCESS_KEY=<SecretAccessKey>

No Console do AWS IAM, você também pode gerar um novo ID de chave de acesso e uma chave de acesso secreta para qualquer usuário do IAM se não conseguir recuperar o existente. É necessário que este usuário tenha as seguintes políticas aplicadas caso não seja o administrador da conta: AmazonEC2FullAccess, AmazonVPCFullAccess.

Chaves de Acesso SSH

Para acessar os nós do cluster depois de criado, é necessário instalar um par de chaves openssh dentro de cada uma das máquinas.

~/.ssh/id_rsa(chave privada)
~/.ssh/id_rsa.pub(chave pública)

Se você não tiver, pode gerar

ssh-keygen

Arquitetura do projeto

Poderia ser feito um projeto único que com um único clique iria provisionar toda a infra e o cluster? Sim

Mas, como eu gostaria de entregar um código de gente grande preferi separar os projetos de acordo com o que cada um faz. Se você estiver começando do zero poderá aproveitar esses módulos de fato para uso em produção.

Um detalhe importante para os entendedores é que se eu utilizar profiles no código para carregar as chaves talvez alguns não consigam utilizar, logo vamos utilizar as access key e secrets mesmo entregues pela aws.

Carregue as variaveis de ambiente e vamos começar...

export AWS_ACCESS_KEY_ID=<AccessKeyID>
export AWS_SECRET_ACCESS_KEY=<SecretAccessKey>

State Backend

O primeiro projeto é criar um backend para o terraform guardar os arquivos de estado gerados para cada um dos demais projetos. Este é o único projeto que o state de fato ficará dentro da pasta do projeto dele. Ele cria um bucket s3 e uma dynamodb para dar lock no projeto se mais de uma pessoa estiver mudando a infra.

Se você já tiver um state backend na sua cloud pule este projeto e substitua o seu backend nos demais projetos, se não tiver aproveite o código e siga em frente.

Se quiser guardar o tfstate localmente você pode, mas não é boa ideia.

Entre na pasta do projeto pelo terminal e execute.

terraform init
terraform apply

Pronto, vá na sua conta e verifique se o bucket foi criado. Observe também que foi criado um .tfstate e este deve ser mantido.

Atenção: comente as linhas abaixo do seu .gitignore desse projeto senão vc irá perder o arquivo se for utilizar no seu repo, nesse caso como estou subindo publicamente não mantive no repo.

#*.tfstate
#*.tfstate.*

Network

O módulo do kubernetes precisa de uma infra provisionada para saber onde vai subir o cluster em quais subnets, etc. O projeto network faz exatamente isso. Esse projeto chama um módulo mantido pela comunidade muito bom, então vamos aproveitá-lo.

Se vc já tiver tudo isso provisionado na sua infra, pode ir direto para o módulo de kubernetes, mas é necessários algumas tags mandatórias nas subnets public e private para o módulo kubernetes reconhecer estas subnets como sua, então fique atento a isso. Abaixo as tags necessárias:

    "kubernetes.io/cluster/${var.cluster_name}" = "shared"
"kubernetes.io/role/elb" = "1"

Geralmente a própria AWS sugere que se use subnets publica privada e database. Se vc fará o uso dessa subnet de database simplesmente descomente a linha abaixo comentada em locals.tf.

  network_resources = toset(
concat(
# [for resource in module.vpc.database_subnet_arns : resource],
[for resource in module.vpc.private_subnet_arns : resource],
[for resource in module.vpc.public_subnet_arns : resource]
)
)

Existe um movimento de se deployar tudo somente no kubernetes, inclusive o banco de dados em pods distribuídos, mas como eu não sou DBA (Database Administrator) pra segurar essa bomba prefiro utilizar com servidores dedicados.

Caso for usar o backend local para teste, simplesmente comente todo o conteúdo do backend.tf ou confira os dados ali descritos. Eu particulamente costumo usar na key a sequência abaixo, mas vc poderia usar qualquer nome aqui que fosse único para esse projeto. Se vc não alterar isso entre os projetos os tfstates irão se substituir e vc terá problemas.

Projeto + Região + Ambiente + terraform.tfstate

key            = "network/us-east-1/production/terraform.tfstate"

Esse é um projeto custoso que sobe uma infra estrutura de rede completa para o seu negócio inclusive com mult-az. Cuidado com o custo $$$$$$$$$$$. Analise a situação.

Observe o que sobe com este projeto.

subnet_kubernetes_tags

Os valores dos recursos criados que vamos expor fica no output.tf que pode ser lido por outros projetos.

Os valores de entrada estão em variables.tf, porém o terraforms.tfvars substitui os valores ali definidos como default.

cluster_name somente aceita letras minusculas, numeros e - pois alguns recursos que irão usar essa variável possuem limitações.

Entre na pasta do projeto pelo terminal e execute.

terraform init
terraform apply

Image para usar nos nodes do cluster

Se partirmos de uma imagem base do ubuntu em uma nova máquina que for criada quando precisar escalar, teremos que fazer todos os ajustes do sistema operacional, instalação do container runtime e os binários do kubernetes. Toda vez que a máquina subisse iria executar todos os passos. Para evitar esse processo demorado, poderíamos prepara uma imagem com tudo instalado e deixar no ponto certo para, ou iniciar o cluster ou entrar em um.

A melhor forma de cozinhar, o termo é esse mesmo, uma imagem é usando o Packer. Uma vez que vc tiver essa imagem pronta, poderemos avançar para o projeto final.

Instale o packer na sua máquina https://learn.hashicorp.com/tutorials/packer/get-started-install-cli

curl -fsSL https://apt.releases.hashicorp.com/gpg | sudo apt-key add -
sudo apt-add-repository "deb [arch=amd64] https://apt.releases.hashicorp.com $(lsb_release -cs) main"
sudo apt-get update && sudo apt-get install packer

O packer basicamente sobe uma ec2 com uma imagem base (no caso usamos um ubuntu minimal) executa todos os comandos que vc deseja, cria uma imagem, e salva. Os comandos podem ser através de vários métodos, mas geralmente eu acabo usando ou scripts ou ansible com seus playbooks. Como são muito poucos comandos, um script aqui resolveria o problema facilmente. Disponibilizei os dois jeitos, somente escolha o seu e segue.

Vale lembrar que o id da ami muda de acordo coma região base do ubuntu muda o id. Logo, se vc estiver em outra região que não for a do projeto precisa procurar o id da ami e alterar. Para localizar um ami use https://cloud-images.ubuntu.com/locator/ec2>

Packer com Shell Script

Já na pasta com os arquivos do packer

# Para fazer download dos plugins
packer init .
# Para buildar a imagem
packer build .
### ....
#==> Builds finished. The artifacts of successful builds are:
#--> amazon-ebs.node: AMIs were created:
#us-east-1: ami-09e94a872c3b5bc19 << ISSO QUE QUEREMOS

A saída mostrará o ami_id que usaremos no projeto abaixo.

Kubernetes_project (ainda nao obtive sucesso)

Debito tecnico. Com um master sómente funciona bem, com 2 master, quando um é encerrado, na hora de voltar ele não esta pegando as configuracoes do init do master que ficou. Ele inicia outro cluster.Se entrar duas ao mesmo tempo tá dando problema, acho que é na hora de pegar o ip, talvez o loop esta pegando ip da maquina que não tem certificado ainda. pois quando entra uma de cada vez funciona O ideal é ter 3 instancias masters com ips fixos. No auto scale, se vc para interromp uma maquina ele sobe outra aumentando o cluster.

Chegamos ao projeto principal. Este projeto lê os dados do projeto network e chama kubernetes_module passando todas as váriavies necessárias para a criação do cluster. Foi feito dessa forma para que o módulo passa ser reaproveitado futuramente para outras instalações somente passando as variáveis necessárias.

Módulo não é projeto. É como se fosse biblioteca de código que contém funções.

Um outro detalhe desse projeto é que vamo usar um recurso do terraform chamado remote state. Quando criamos um recurso no terraform ele guarda tudo o que de fato foi criado no arquivo .tfstate que subimos para o nosso backend. Podemos ler um backend de outro projeto para pegar os recursos e aproveitar nesse projeto e é isso que vamos fazer aqui. Vamos ler os recursos do projeto network para ter acessos aos subnets, vpc, etc... e chama o módulo passando os parâmetros.

Observe que o remote.tf aponta para o tfstate do projeto network. Se vc for fazer isso local precisa mudar para método abaixo. Simplesmente comente o remote do S3 e descomente o local.

data "terraform_remote_state" "remote" {
backend = "local"

config = {
path = "../network/terraform.tfstate"
}
}

As seguinte variaveis dentro de locals.tf pegam os valores do projeto de network:

  • vpc_id
  • vpc_cidr_blocks
  • cluster_name

Outras variaveis estão declaradas em variables.tf e são substituidas pelos valores em terraform.tfvars quando declaradas.

Uma breve explicação para arquivos do módulo

Secuutiy Group

O sec-groups.tf contem as permissões necessárias de comunicação entre os masters, nodes, bastion e load balancer como mostra na figura abaixo.

drawing

O arquivo iam.tf cria as permissões necessárias para a criação de nodes que o kubernetes irá executar. Foi utilizado como base as permissões do repositório do kops https://github.com/kubernetes/kops/tree/master/pkg/model/iam/tests

terraform init
terraform apply

Load Balancer

Masters e Workers

curl -sk https://api-company-cluster-10fbcbc105819c5f.elb.us-east-1.amazonaws.com:6443 | jq -r '.kind'

MASTERS=$(aws ec2 describe-instances --filters Name=tag-key,Values="kubernetes.io/cluster/company-cluster" --region us-east-1 | jq -r '.Reservations[].Instances[] | select (.State.Name == "running") | select(.Tags[].Key == "k8s.io/role/master") | .PrivateIpAddress')

mkdir -p /home/$USER/.kube
sudo cp -i /etc/kubernetes/admin.conf /home/$USER/.kube/config
sudo chown -R $(id -u $USER):$(id -g $USER) /home/$USER/.kube/