Kubernets com CI/CD pipeline na AWS

Lucas Moro
11 min readNov 23, 2020

--

Utilizado neste projeto:

  • kubectl
    kubectl (Kubernetes Control) é uma ferramenta de linha de comando para interagir com um cluster Kubernetes, executado localmente em sua máquina (usando minikube) ou na nuvem.
  • kops
    O projeto Kubernetes Operations (kops) fornece ferramentas para criar e operar clusters Kubernetes na nuvem. Atualmente, ele oferece suporte ao Google Cloud e AWS
  • Terraform
    Terraform é uma ferramenta de infraestrutura como código (IAC) que permite aos usuários definir a infraestrutura em uma linguagem de configuração de alto nível que pode então ser usada para construir infraestrutura em um provedor de serviços, como AWS ou Google Cloud Platform.
  • AWS Cli
    Ferramenta de linha de comando necessária para o kops e o Terraform interagirem com a AWS.
  • Travis Worker — Continuous Integration(CI)
    Serviço de integração contínua
  • GitOps — Continuous Delivery (CD)
    Gerenciador de configuração aplicada a um contexto específico e restrito, considerando que a única fonte de verdade é um repositório Git.

O projeto está rodando em uma máquina da AWS t2.large com rhel 7

VPC

Para iniciar o projeto, será utilizado o Kops, kubectl e Terraform para estrutura a VPC que irá suportar a aplicação que irá hospedar os clusters de Kubernetes

Diagrama da VPC final na AWS

Passo1:
Clonar o seguinte repositório
https://github.com/LucasBello/k8s.git

Estes arquivos foram criados a partir das bibliotecas do kops e do kubectl com alterações para serem aplicados com Terraform e Travis.

Passo 2:
Configuração do Route53

O cluster Kubernetes que será configurado usará um FQDN hospedado no Route53 para expor os pontos de extremidade do serviço e o plano de controle da API

Para isso, será utilizado o domínio itap.ga criado especialmente para este projeto em https://www.freenom.com/

Documentação para criação de zonas de hospedagem na AWS

Após configuradas, as zonas ficarão como a seguir

Print das zonas hospedadas

Passo3:
Criando os pré-requisitos necessários para o kops

Para que a kops construa o cluster, é necessário um armazenamento S3 para manter a configuração do cluster e uma conta de usuário IAM que tenha as seguintes políticas anexadas a ele:

AmazonEC2FullAccess
AmazonRoute53FullAccess
AmazonS3FullAccess
IAMFullAccess
AmazonVPCFullAcces

Ao rodar o prereqs / kops_pre_reqs.tf este usuário será criado automaticamente.

Este Script do Terraform também criará um bucket S3 que será usado como um armazenamento remoto para o estado do Terraform.

Isso permitirá que vários usuários trabalhem com um conjunto de infraestrutura como código sem causar conflitos.

Não esquecer de alterar o nome do S3

linha 38 do script preteqs/kops_pre-reqs.tf
linha 90 do script preteqs/kops_pre-reqs.tf

Dentro do repositório:

$ cd prereqs
$ terraform init
$ terraform plan
$ terraform apply

A partir deste momento poderá ser observado a criação do usuário kops na área de IAM da AWS e os buckets, sendo o bucket destinado a “terraform_state” será utilizado pelo kobs para inserir a pasta com os estados do Terraform para a utilização compartilhada.

Passo 4:
Usando o Kops para subir o Cluster

Na etapa anterior, fora criada uma conta IAM para kops.
Agora será configurado o cliente AWS CLI para usar essa conta.

Pode-se obter o ID kops IAM e a chave secreta do arquivo que o Terraform usa para armazenar o estado do que foi criado na etapa anterior.
Abrindo o terraform.tfstate com um editor de texto e procurando em uma sessão como a seguir:

ID, SECRET e SES_SMTP_PASSWORD_V4

Com os valores dos campos {iam_id} e {aws_secret_key} será executado o seguinte comando:

$ aws configure --profile kops
AWS Access Key ID [None]: {iam_id}
AWS Secret Access Key [None]: {aws_secret_key}
Default region name [None]: {escolha a região}
Default output format [None]: text

Em seguida, serão definidos algumas variáveis ​​ambientais para que kops saiba qual conta AWS IAM usar e onde deve colocar seu armazenamento de estado:

$ export AWS_PROFILE=kops
$ export KOPS_STATE_STORE=s3://{seu_bucket}

Será utilizado o kops para construir o cluster.
Executando o seguinte comando, substituindo sua região AWS, sua zona DNS e seu nome de cluster escolhido:

$ kops create cluster --cloud aws \
--bastion \
--node-count 3 \
--node-size t2.medium \
--master-size t2.medium \
--zones {coloque_a_regiao}a,{coloque_a_regiao}b,{coloque_a_regiao}c \
--master-zones {coloque_a_regiao}a,{coloque_a_regiao}b,{coloque_a_regiao}c \
--dns-zone {Zona_de_DNS} \
--topology private \
--networking calico \
--authorization RBAC \
--name {nome_do_cluster_até_11_caracteres} \
--out=k8s \
--target=terraform --yes

Este comando diz ao kops que será construido um cluster com as seguintes especificações

  • Pertencerá a AWS
  • Tem um node master de tamanho t2.medium em cada uma das zonas de disponibilidade especificadas
  • Possui 3 work nodes de tamanho t2.medium.
    O kops irá espalhar os nós de trabalho uniformemente em cada uma das zonas de disponibilidade
  • Usa uma topologia de rede privada, o que significa que todos os nós têm endereços IP privados e não são acessíveis diretamente da Internet pública
  • Usa Calico como uma interface de rede de contêiner substituindo kubenet como resultado dos requisitos da topologia de rede privada
  • Usa RBAC para permissões de acesso do Kubernetes
  • Está descrito em um arquivo de configuração Terraform a ser escrito no diretório especificado em “- -out”. No caso do exemplo “k8s”

O kops gera um conjunto de arquivos de configuração do Terraform no diretório k8s que será aplicado para criar o cluster.

Antes de construir o cluster, será adicionado um arquivo de configuração para dizer ao Terraform para manter seu armazenamento de estado no bucket S3 que fora criado.

Arquivos de estado do Terraform no S3

Executar o comando:

$ cd k8s
$ terraform init
$ terraform plan
$ terraform apply

Levará de 10 a 15 minutos para que o cluster fique disponível.
Podendo ter seu status verificado com o seguinte comando:

$ kops validate cluster
Mensagem de positivo.

Construindo o ambiente CI / CD

Para implementar o GitOps, será configurado um ambiente de CI/CD para monitorar o repositório e executar atualizações.
Nesta documentação o ambiente de CI /CD será configurado para executar as etapas de implantação em cada push para o branch master do repositório.
Isso somente será feito para fins de conveniência de demonstração; permitir que os desenvolvedores façam isso é definitivamente uma má prática.

Em um projeto da vida real, é recomentada uma estratégia de ramificação de recursos com uma revisão de código e uma etapa de aprovação do líder de desenvolvimento.

Será utilizado o TravisCI, um serviço de CI baseado em nuvem.
TravisCI é gratuito, desde que sejam utilizados repositórios públicos do Github.

Passo1:
Configurando contas e repositório de clones

  • Criado um repositório no GitHub chamado “k8s-ci”
  • Feito clone do repositório para a máquina local
git clone <URL>
  • Feita a integração do GitHub com o TravisCI.
  • Habilitado o TravisCI para no repositório do GitHub

Step 2:
Configurar os Triggers

Neste documento será implantado apenas solicitaões de pull aprovadas
Esta configuração é feita no Travis

More Options → Settings.

Travis irá pegar a grande maioria das instruções de um arquivo yaml armazenado no repositório.
Criar um arquivo vazio denominado ‘.travis.yml’ na raiz do epositório, iniciar a configuração com base no ‘.travis.yml’ do repositório:

  • Linha 1: especifica que queremos apenas que uma construção seja executada no branch master. Em um ambiente real, provavelmente também implantaríamos no envio de uma ramificação, mas em um ambiente de teste em vez de um ambiente de produção. Uma maneira de fazer isso é usando variáveis ​​de ambiente para aplicar lógica condicional ao script de implantação, mas isso está fora do escopo desta postagem.
  • Linha 4: especifica que exigimos permissões de root usando sudo para instalar nossas dependências
  • Linha 5: Este é o início do bloco onde definimos as permissões em cada um de nossos scripts para que sejam executáveis.
  • Linha 10: Este é o início do bloco onde especificamos os scripts que precisam ser executados primeiro para configurar o ambiente de CI antes de podermos executar os scripts que fazem a implantação real.
  • Linha 13: Este é o início do bloco onde especificamos os scripts que serão executados para executar as tarefas de implantação.

Passo3:
Gerenciamento das chaves

Não queremos manter nossos segredos da AWS em um repositório público em texto não criptografado; isso seria uma prática extremamente ruim de segurança da informação. Convenientemente, o Travis fornece uma ferramenta CLI que pode ser usada para armazenar seus segredos a serem injetados no momento da compilação. O Travis gera um novo par de chaves pública / privada para cada nova conta, e esses segredos serão criptografados usando esse par de chaves e injetados como variáveis ​​de ambiente cada vez que o build for executado. Para configurar isso, execute os seguintes comandos na raiz do seu repositório e faça o login usando os detalhes solicitados:

$ sudo gem install travis
$ travis login --org

Existem dois scripts criados no diretório de scripts de construção do repositório, prontos para que as chaves sejam inseridas.

Copie o diretório build_scripts de sua cópia local do projeto_Itau para o repositório k8s-ci e atualize da seguinte maneira:

  • large-secrets.txt: adicione suas chaves de acesso do Kubernetes.
    Eles podem ser encontrados em ~ / .kube / config
  • setup-secrets.sh: adicione sua senha do Kubernetes (novamente encontrada em ~ / .kube / config) e as chaves de acesso AWS de ~ / .aws / credentials

Em seguida, a partir da raiz do repositório, execute o script setup-secrets.sh usando o seguinte comando:

$ chmod 755 build-scripts/setup-secrets.sh
$ ./build-scripts/setup-secrets.sh

Anote o comando openssl que o script setup-secrets.sh retorna para mais tarde, pois precisaremos dele para descriptografar as chaves.

Este script criptografará as chaves usando Travis e atualizará o arquivo .travis.yml.

$ git add build-scripts/large-secrets.txt.enc .travis.yml
$ git commit -m "Enviando as chaves criptografadas"

Agora que as chaves estão protegidos no Travis, é sugerido removê-las de todos os arquivos e scripts.

Pode ser muito fácil enviar cahves acidentalmente para o controle de origem.

Para capturar qualquer commit acidental de chaves, utiliza-se o git-secrets. Que pode ser configurado usando as seguintes comandos:

$ brew install git-secrets
$ git-secrets --install

Etapa 4:
Instalar dependências

Como o TravisCI executa cada construção em um contêiner limpo do Docker, precisam ser instaladas as dependências todas as vezes.
Crie um arquivo chamado install-dependencies.sh na pasta build-scripts e cole a seguinte configuração nele:

Agora, envie o arquivo para o repositório:

$ git add install-dependencies.sh
$ git commit -m "Adicionando arquivo para instalar dependências"

Passo5:
Injetando chaves

Será utilizado um script para configurar as chaves no contêiner do Docker no qual as etapas de compilação acontecerão.

Crie um arquivo denominado inject-secrets.sh na pasta build-scripts e cole o script abaixo e atualize-o da seguinte maneira:

  • Substitua {URL do CLUSTER} pelo URL do seu cluster Kubernetes
  • Substitua o comando OpenSSL que anotamos na Etapa 3 desta seção por {Seu comando openssl do estágio de criptografia} anexando ./build-scripts/ antes de large-secrets.txt.enc
  • Substitua {Região da AWS} pela região AWS que está sendo utilizada

Este script irá puxar as chaves do ambiente Travis, descriptografá-los e injetá-los nos arquivos de configuração pertinentes.

Você notará no script acima que ele se refere a um arquivo no diretório build-scripts denominado kubeconfig — precisaremos criá-lo também. Cole o conteúdo abaixo, trocando a variável {URL do Cluster} pelo URL do seu cluster Kubernetes.

Dar um commit nos dois arquivos

$ git add inject-secrets.sh kubeconfig
$ git commit -m "Adicionando script de injeção de chaves"

Passo6:
Configuração do Ambiente

Antes de implantar os aplicativos, é necessário preparar o cluster implantando a configuração para kube2iam e external-dns.
A configuração de cada uma dessas ferramentas deve ser aplicada em uma ordem definida:

  • Aplique a configuração do Terraform para criar uma nova função AWS IAM (e as concessões de política necessárias para essa função) e uma relação de confiança de volta à função AWS IAM sob a qual os nós são executados. A relação de confiança permite que um nó assuma a nova função IAM.
  • Aplique a configuração RBAC do Kubernetes para criar uma conta de serviço, vincule-a a um escopo de permissão necessário e conceda as permissões necessárias a essa conta de serviço. Essa conta de serviço é então especificada como parte da configuração dos pods que fornecem cada um dos serviços específicos.
  • Aplique as configurações do Kubernetes para implantar os serviços. Dependendo do serviço que está sendo implantado, pode ser uma implantação do Kubernetes ou DaemonSet.

O script será construido para que o cluster seja sempre configurado primeiro.

Copie as pastas contendo os modelos de external-dns e kube2iam de nosso repositório para o seu repositório.

Primeiro, crie um script que aplicará a configuração do Terraform. Crie um arquivo chamado deploy-terraform.sh no diretório build-scripts e adicione o seguinte código a ele:

Este script percorrerá a estrutura de diretório do repositório e aplicará todos os arquivos de configuração do Terraform que encontrar.

Em um ambiente de produção real, adicionaríamos verificações em nosso pipeline de CI para garantir que o Terraform não seja usado de forma maliciosa.

Faça o commit no repositório:

$ git add deploy-terraform.sh
$ git commit -m "Adicionando arquivo de deploy do Terraform"

Agora será feito a atualização e envio da configuração do Terraform para cada um dos 3 serviços para o repositório:

  • Atualize external_dns / pod-role-trust-policy.json e substitua {sua-node-iam-role-arn} pelo IAM ARN para nodes Kubernetes do cluster.
    Isso pode ser encontrado executando o seguinte comando:
$ aws iam list-roles | grep node
captura da role do node
  • Atualize external_dns / main.tf para substituir {regiao do aws} pela região AWS em que você está trabalhando e {colocar o bucket TF} pelo nome do intervalo que você escolheu para manter o armazenamento de estado do Terraform.

Confirme a configuração do serviço em seu repositório:

$ git add external_dns/pod-role-trust-policy.json external_dns/external-dns-iam-setup.tf external_dns/external-dns-role-rights.json external_dns/main.tf
$ git commit -m "Adicionando serviço de configuração do Terraform para os clusters"

Travis está configurado para aplicar a configuração em cada push para o mestre.
Executar o push:

$ git push

Poderá ser observada todas as configurações do Terraform sendo aplicadas à conta AWS no registro de tarefas.

Agora serão criadas todas as funções de IAM e relações de confiança necessárias para nosso ambiente Kubernetes.

Será construido o script de implantação para os serviços Kubernetes.
Crie um arquivo denominado deploy-k8s.sh na pasta build-scripts e cole o seguinte:

kube2iam é implantado primeiro, pois external-dns fará chamadas para a API AWS usando kube2iam como corretor.

Agora envie seu repositório para o mestre e certifique-se de que não haja erros no registro de compilação:

$ git add build-scripts/deploy-k8s.sh
$ git commit -m "Updating Travis config to deploy k8s config"
$ git push

Pode-se verificar o cluster para ter certeza de que todos os serviços foram implantados corretamente.
External-dns é uma implantação, portanto, pode-se executar o seguinte comando para obter seu status:

$ kubectl get deployments --namespace=kube-system

O kube2iam é implantado como DaemonSet, pois precisa ser executado em todos os nodes para intermediar chamadas para a API AWS.
Execute o seguinte comando para obter seu status:

kubectl get ds --namespace=kube-system

passo7:
Impantar um aplicativo teste

Primeiro, adicione uma etapa de implantação ao script deploy-k8s.sh que implantará os aplicativos:

$ git add build-scripts/deploy-k8s.sh
$ git commit -m "Atualizando Travis para deploy do APP"
$ git push

Criar um arquivo chamado hello_app_deploy.yml:

https://gist.github.com/LucasBello/3fcf48d73977ee8e2bea5db8b0b3cc97

$ git add hello_app_deployment.yaml
$ git commit -m "Adicionando Aplicativo Teste"
$ git push -u origin testapp

Verificando pull

GitOps

--

--

Lucas Moro

Arquiteto de nuvem, Engenheiro de soluções e nomeado Lord em Wigtownshire na Escócia Ocidental