Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
June 22, 2022 08:28 pm GMT

Pipeline IaC com Terraform e GitHub Actions

Introduo

Com a difuso da cultura DevOps nos ltimos anos algumas prticas para entrega de novos produtos ou novas funcionalidades no ambiente produtivo mudaram bastante. Nesse artigo vou exemplificar em um pequeno laboratrio um processo de pipeline CD/CD para entrega de uma infraestrutura utilizando Terraform, GitHub Actions e AWS como cloud pblica.

Terraform

Terraform uma ferramenta de infraestrutura como cdigo (IaC) que permite criarmos recursos em clouds pblicas ou privadas utilizando uma linguagem simples e declarativa, podendo assim reutilizar e versionar o cdigo da sua infraestrutura assim como feito com a aplicao. Voc pode ento usar um workflow consistente para provisionar e gerenciar toda a sua infraestrutura ao longo de seu ciclo de vida.

GitHub Actions

GitHub Actions uma plataforma de integrao contnua e entrega contnua (CI/CD) que permite automatizar sua compilao de cdigo, testar e entregar de forma simples e rpida.

Mo na massa!

A ideia do laboratrio provisionar uma instncia EC2 com ip pblico executando um webserver nginx rodando o famoso jogo da cobrinha .

Preparando o GitHub para acessar a AWS com o Terraform

A primeira etapa que precisa ser feita configura as secrets AWS_ACCESS_KEY_ID e AWS_SECRET_ACCESS_KEY para que o GitHub Actions consiga acessar esses valores durante a execuo do workflow e ter acesso a sua conta da AWS.

Dentro do repositrio v em: Settings -> Secrets -> Actions e adicionei as chaves de acesso da AWS.

pipeline-step-1

Criando os workflows do GitHub Actions

Para termos as actions do GitHub rodando de acordo com cada evento precisar criar o diretrio .github/workflows na raiz do projeto. Neste caso vamos criar 3 arquivos de workflow, um para o executar o terraform plan quando for criada um pull request, outro para executar o terraform apply quando o merge for feito na branch main e o ltimo e no menos importante para executar o terraform destroy, esse ser executado somente manualmente quando for necessrio destruir a infraestrutura.

Eu utilizei como base para a criao desses workflows a action oficial da HashiCorp - Setup Terraform

Dica: No site HashiCorp Learn temos um tutorial de como criar uma pipeline com GitHub Actions porm utilizando Terraform Cloud.

pipeline-step-2

plan.yml

name: "Plan"on:  pull_request:env:  AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}  AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}jobs:  terraform:    name: "Terraform"    runs-on: ubuntu-latest    steps:      - name: Checkout        uses: actions/checkout@v3      - name: Setup Terraform        uses: hashicorp/setup-terraform@v1        with:          terraform_version: 0.15.5      - name: Terraform Init        id: init        run: terraform init      - name: Terraform Validate        id: validate        run: terraform validate -no-color      - name: Terraform Plan        id: plan        run: terraform plan -no-color -input=false        continue-on-error: true      - uses: actions/github-script@v6        if: github.event_name == 'pull_request'        env:          PLAN: "terraform
${{ steps.plan.outputs.stdout }}" with: github-token: ${{ secrets.GITHUB_TOKEN }} script: | const output = `#### Terraform Format and Style \`${{ steps.fmt.outcome }}\` #### Terraform Initialization \`${{ steps.init.outcome }}\` #### Terraform Validation \`${{ steps.validate.outcome }}\` #### Terraform Plan \`${{ steps.plan.outcome }}\` <details><summary>Show Plan</summary> \`\`\`
${process.env.PLAN} \`\`\` </details> *Pushed by: @${{ github.actor }}, Action: \`${{ github.event_name }}\`*`; github.rest.issues.createComment({ issue_number: context.issue.number, owner: context.repo.owner, repo: context.repo.repo, body: output }) - name: Terraform Plan Status if: steps.plan.outcome == 'failure' run: exit 1 - name: Terraform Apply if: github.ref == 'refs/heads/main' && github.event_name == 'push' run: terraform apply -auto-approve -input=false

apply.yml

name: "Apply"on:  push:    branches:      - mainenv:  AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}  AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}jobs:  terraform:    name: "Terraform"    runs-on: ubuntu-latest    steps:      - name: Checkout        uses: actions/checkout@v3      - name: Setup Terraform        uses: hashicorp/setup-terraform@v1        with:          terraform_version: 0.15.5      - name: Terraform Init        id: init        run: terraform init      - name: Terraform Validate        id: validate        run: terraform validate -no-color      - name: Terraform Plan        id: plan        run: terraform plan -no-color -input=false        continue-on-error: true      - uses: actions/github-script@v6        if: github.event_name == 'pull_request'        env:          PLAN: "terraform
${{ steps.plan.outputs.stdout }}" with: github-token: ${{ secrets.GITHUB_TOKEN }} script: | const output = `#### Terraform Format and Style \`${{ steps.fmt.outcome }}\` #### Terraform Initialization \`${{ steps.init.outcome }}\` #### Terraform Validation \`${{ steps.validate.outcome }}\` #### Terraform Plan \`${{ steps.plan.outcome }}\` <details><summary>Show Plan</summary> \`\`\`
${process.env.PLAN} \`\`\` </details> *Pushed by: @${{ github.actor }}, Action: \`${{ github.event_name }}\`*`; github.rest.issues.createComment({ issue_number: context.issue.number, owner: context.repo.owner, repo: context.repo.repo, body: output }) - name: Terraform Plan Status if: steps.plan.outcome == 'failure' run: exit 1 - name: Terraform Apply if: github.ref == 'refs/heads/main' && github.event_name == 'push' run: terraform apply -auto-approve -input=false

destroy.yml

name: "Destroy"on:  workflow_dispatch:env:  AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}  AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}jobs:  destroy:    name: "terraform destroy"    runs-on: ubuntu-latest        steps:      - name: Checkout        uses: actions/checkout@v3      - name: Setup Terraform        uses: hashicorp/setup-terraform@v1        with:          terraform_version: 0.15.5      - name: Terraform Init        id: init        run: terraform init      - name: Terraform Validate        id: validate        run: terraform validate -no-color      - name: Terraform Destroy        run: terraform destroy -auto-approve

Cdigo da infra no Terraform

No vou me aprofundar muito nos detalhes do cdigo do terraform isso pode ficar para outro artigo. Basicamente temos criao de 3 recursos principais dentro da AWS so eles: VPC, Security Group e a Instncia EC2.

Para a criao da VPC j com subnet pblica, AZs e NAT Gateway estou utilizando o mdulo vpc. Abaixo o trecho de cdigo:

module "vpc" {  source = "terraform-aws-modules/vpc/aws"  name = "snake-vpc"  cidr = "10.200.0.0/16"  azs             = ["us-east-1a", "us-east-1b"]  public_subnets  = ["10.200.101.0/24", "10.200.102.0/24"]  enable_nat_gateway = true  tags = {    Terraform   = "true"    Environment = "prod"  }}

Para a criao do Security Group estou utilizando o resource aws_security_group, onde configuramos a liberao geral na porta 80, padro HTTP. Abaixo o trecho de cdigo:

resource "aws_security_group" "game_snake_sg" {  name        = "instances-snake-sg"  description = "SG for Instances Snake Security Group"  vpc_id      = module.vpc.vpc_id  ingress {    description = "Game Snake port 80"    from_port   = 80    to_port     = 80    protocol    = "tcp"    cidr_blocks = ["0.0.0.0/0"]  }  egress {    from_port        = 0    to_port          = 0    protocol         = "-1"    cidr_blocks      = ["0.0.0.0/0"]    ipv6_cidr_blocks = ["::/0"]  }  tags = {    Terraform = "true"    Environment = "prod"  }}

Para finalizar criamos a instncia EC2 tambm utilizamos o mdulo ec2_instance, onde o servidor com a imagem Amazon Linux 2, tipo t1.micro, anexamos o Security Group criando anteriormente na instncia, anexamos tambm a instncia dentro da Subnet pblica que foi criada junto com a VPC e por fim passamos um arquivo userdata, que basicamente um shell script que ser executando quando a instncia for iniciada.

module "ec2_instance" {  source = "terraform-aws-modules/ec2-instance/aws"  name                   = "snake-game"  ami                    = "ami-0cff7528ff583bf9a"  instance_type          = "t1.micro"  vpc_security_group_ids = [aws_security_group.game_snake_sg.id]  subnet_id              = module.vpc.public_subnets[0]  user_data              = file("userdata.sh")  tags = {    Name = "snake-game-ec2"    Terraform = "true"    Environment = "prod"    Team = "gamer-development"    Application = "snake-game"    Language = "javascript"  }}

Desta forma o arquivo do terraform finalizado fica da seguinte maneira:

main.tf

module "vpc" {  source = "terraform-aws-modules/vpc/aws"  name = "snake-vpc"  cidr = "10.200.0.0/16"  azs             = ["us-east-1a", "us-east-1b"]  public_subnets  = ["10.200.101.0/24", "10.200.102.0/24"]  enable_nat_gateway = true  tags = {    Terraform   = "true"    Environment = "prod"  }}resource "aws_security_group" "game_snake_sg" {  name        = "instances-snake-sg"  description = "SG for Instances Snake Security Group"  vpc_id      = module.vpc.vpc_id  ingress {    description = "Game Snake port 80"    from_port   = 80    to_port     = 80    protocol    = "tcp"    cidr_blocks = ["0.0.0.0/0"]  }  egress {    from_port        = 0    to_port          = 0    protocol         = "-1"    cidr_blocks      = ["0.0.0.0/0"]    ipv6_cidr_blocks = ["::/0"]  }  tags = {    Terraform = "true"    Environment = "prod"  }}module "ec2_instance" {  source = "terraform-aws-modules/ec2-instance/aws"  name                   = "snake-game"  ami                    = "ami-0cff7528ff583bf9a"  instance_type          = "t1.micro"  vpc_security_group_ids = [aws_security_group.game_snake_sg.id]  subnet_id              = module.vpc.public_subnets[0]  user_data              = file("userdata.sh")  tags = {    Name = "snake-game-ec2"    Terraform = "true"    Environment = "prod"    Team = "gamer-development"    Application = "snake-game"    Language = "javascript"  }}

Enviando um Pull Request, fazendo o Merge e executando o deploy da infraestrutura

Vamos criar uma nova branch no projeto chamada deployment e fazer uma alterao no arquivo README.md e criar uma Pull Request. Assim que Pull Request for criada a action de plan ser disparada e poderemos verificar o que o terraform ir executar dentro da AWS.

Pull Request

pipeline-step-3

Veja que aps a execuo do workflow de plan que criamos nos comentrios da Pull Request o GitHub Actions informa o resultado na execuo, incrvel n?!

pipeline-step-4

Merge

Agora vamos aprovar o Merge da Pull Request na branch main. Com essa estratgia possvel revisar o que exatamente o terraform ir aplicar antes ser executado.

pipeline-step-5

Aps a execuo podemos ir no step Terraform Apply do workflow e verificar se tudo foi executado com sucesso.

pipeline-step-6

Validando a infraestrutura na AWS

Verificando a conta da AWS vemos que toda a infraestrutura foi provisionada com sucesso e a aplicao do jogo da cobrinha est online.

pipeline-step-7

Executando o Destroy

Caso seja necessrio deletar toda a infraestrutura de foi provisionada devemos executar o Workflow Destroy.

pipeline-step-8

Aps a execuo do Destroy toda os recursos da AWS foram deletados.

pipeline-step-9

Concluso

Utilizando pipelines CD/CD possvel entregar ambiente completos de forma automtica e rpida. Se voc notou criamos todos os recurso dentro da AWS sem precisar dar ao menos um clique dentro do painel administrativo da cloud. Use a abuse da infraestrutura como cdigo (IaC). Espero que tenha gostado desse hands-on, at a prxima!


Original Link: https://dev.to/santospedroh/pipeline-iac-com-terraform-e-github-actions-k3p

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To