테라폼 및 GitHub Actions로 인프라 프로비저닝을 자동화 하십시오

xgro·2022년 8월 11일
1

Terraform

목록 보기
5/10

📌 Summary

  • 테라폼을 이용하여 인프라를 관리할 수 있다.
  • aws의 s3, dynamodb를 이용하여 테라폼 백엔드를 구축하여 비용 효율적으로 tfstate를 관리한다.
  • GitOps를 활용하여 인프라 프로비저닝을 자동화 할 수 있다.

📌 Terraform과 Github Action

Monolithic 서버를 제품 별 API로 분류하여 컨테이너 환경에서 동작시키는 작업을 진행하면서, 인프라 관리를 테라폼으로 관리하고자 하였습니다.

Github Action을 이용한 인프라 프로비저닝 워크플로우


👉  GitOps란 무엇입니까?

GitOps는 클라우드 네이티브 애플리케이션을 위한 지속적인 배포를 구현하는 방법입니다.

Git 및 지속적인 배포 도구를 포함하여 개발자가 이미 익숙한 도구를 사용하여 인프라를 운영할 때 개발자 중심 경험에 중점을 둡니다.

GitOps의 핵심 아이디어는 현재 프로덕션 환경에서 원하는 인프라에 대한 선언적 설명을 항상 포함하는 Git 저장소와 프로덕션 환경이 저장소의 설명된 상태와 일치하도록 하는 자동화된 프로세스를 갖는 것 입니다. 

새 응용 프로그램을 배포하거나 기존 응용 프로그램을 업데이트하려는 경우 저장소만 업데이트하면 됩니다.

다른 모든 것은 자동화된 프로세스가 처리합니다. 

프로덕션에서 애플리케이션을 관리하기 위한 순항 제어 기능이 있는 것과 같습니다.


출처 - https://s-core.co.kr/insight/view/데브옵스의-확장-모델-깃옵스gitops-이해하기-2/

✅ GitOps의 핵심 아이디어

  1. 배포에 관련된 모든 것을 선언형 기술서(Declarative Descriptions) 형태로 작성하여 Config Repository(혹은 Environment Repository)에서 관리합니다.
  2. Config Repository의 선언형 기술서와 운영 환경 간 상태 차이가 없도록 유지시켜주는 자동화 시스템을 구성합니다.

GitOps 환경에서 새로운 버전의 애플리케이션을 배포하거나 기존의 운영 환경을 바꾸고 싶다면 선언형 기술서를 수정한 뒤 Config Repository에 반영하기만 하면 된다. 나머지 과정은 자동화된 시스템이 알아서 수행합니다.


📌 How it works?

  1. Github Action
    a. [branch] main - push
    b. pull_request
  2. Github Action이 트리거 되며 인프라 관리 시작
  3. AWS credentials를 이용하여 발행된 액세스 키에 대해서 접속하여 필요한 서비스를 구축
  4. terraform init이 실행
    a. 등록된 S3 버킷의 tfstate 파일을 참조
    b. 등록된 DynamoDB의 Table의 lock 파일을 참조
    c. 인프라 생성에 필요한 리소스를 참조
  5. terraform plan이 실행되며 푸쉬된 테라폼 파일을 이용하여 정상적으로 인프라가 구축될 것인지 확인
  6. 이상이 없다면, terraform apply -auto-approve -input=false가 실행되어 인프라를 구축

💡 Differentiation
기본적으로 제공되는 terraform template에서 Configure AWS credentials을 추가하여 github secret을 이용하여 AWS 리소스에 엑세스한다.

# terraform.yaml
name: 'Terraform'

on:
  push:
    branches:
    - "main"
  pull_request:

env:
  AWS_REGION: ap-northeast-2   # set this to your preferred AWS region, e.g. us-west-1

permissions:
  contents: read

jobs:
  terraform:
    name: 'Terraform'
    runs-on: ubuntu-latest
    environment: production

    # Use the Bash shell regardless whether the GitHub Actions runner is ubuntu-latest, macos-latest, or windows-latest
    defaults:
      run:
        shell: bash

    steps:
    # Checkout the repository to the GitHub Actions runner
    - name: Checkout
      uses: actions/checkout@v3

    # AWS 리소스를 이용하기 위해서 추가
    - name: Configure AWS credentials
      uses: aws-actions/configure-aws-credentials@v1
      with:
        aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
        aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
        aws-region: ${{ env.AWS_REGION }}

    # Initialize a new or existing Terraform working directory by creating initial files, loading any remote state, downloading modules, etc.
    - name: Terraform Init
      run: terraform init

    # Checks that all Terraform configuration files adhere to a canonical format
    - name: Terraform Format
      run: terraform fmt -check

    # Generates an execution plan for Terraform
    - name: Terraform Plan
      run: terraform plan -input=false

      # On push to "main", build or change infrastructure according to Terraform configuration files
      # Note: It is recommended to set up a required "strict" status check in your repository for "Terraform Cloud". See the documentation on "strict" required status checks for more information: https://help.github.com/en/github/administering-a-repository/types-of-required-status-checks
    - name: Terraform Apply
#      if: github.ref == 'refs/heads/"main"' && github.event_name == 'push'
	  if: github.ref == 'refs/heads/main' && github.event_name == 'push'
      run: terraform apply -auto-approve -input=false

📌 Terraform Backend 란?

Backend는 Terraform이 state 데이터 파일을 저장하는 위치를 정의한다.

테라폼은 tfstate를 이용하여 현재 선언한 리소스를 추적한다.

tfstate에는 중요한 정보도 포함되기 때문에, Github 또는 외부로 노출되어있는 코드 저장소에 올라가지 않도록 주의하여야 한다.

이를 위해 aws 서비스인 S3와 DynamoDB를 이용해서 백엔드를 구축 시도하였다.

🔥 S3 및 DynamoDB로 백엔드를 관리할때의 장점

  • 완전 관리형 서비스이므로 운영 부담이 발생하지 않는다.
  • 현재 GitOps로 관리하고자 하는 인프라 역시 AWS 리소스를 주로 사용할 예정이므로 별도의 관리 포인트가 추가 되지 않는다.

👉 Backend

별도의 워크스페이스를 생성하여 백엔드를 위한 리소스를 미리 생성합니다.

# backend.tf

provider "aws" {
  region = "ap-northeast-2" # Please use the default region ID
}

# S3 버킷을 생성한다
resource "aws_s3_bucket" "for_tfstate" {
  bucket = "xgro-tfstate"
}

# S3 버킷의 버저닝 기능 활성화 선언한다.
resource "aws_s3_bucket_versioning" "tfstate" {
  bucket = aws_s3_bucket.for_tfstate.bucket

  versioning_configuration {
    status = "Enabled"
  }
}

# DynamoDB for terraform state lock
resource "aws_dynamodb_table" "terraform_state_lock" {
  name         = "terraform-lock"
  hash_key     = "LockID"
  billing_mode = "PAY_PER_REQUEST"

  attribute {
    name = "LockID"
    type = "S"
  }
}

👉 Workspace

위에서 백엔드를 위한 S3, DynamoDB가 정상적으로 생성되었다면, 테스트를 위한 별도의 테라폼 프로젝트를 생성합니다.

# main.tf

# Required providers configuration
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 4.6.0"
    }
  }

  backend "s3" {
    bucket         = "xgro-tfstate"
    key            = "terraform.tfstate"
    region         = "ap-northeast-2"
    dynamodb_table = "terraform-lock"
    encrypt        = true
  }

  required_version = ">= 1.0.11"
}

data "aws_region" "current" {}
data "aws_caller_identity" "current" {}

테스트는 vpc 모듈을 이용해서 리소스의 이름 및 설정을 변경하며 진행하였습니다.

# vpc.tf

module "vpc" {
  source = "terraform-aws-modules/vpc/aws"

  name = "final_mon1-vpc"
  cidr = "10.0.0.0/16"

  azs             = ["ap-northeast-2a", "ap-northeast-2b"]
  private_subnets = ["10.0.101.0/24", "10.0.102.0/24"]
  public_subnets  = ["10.0.1.0/24"]

  enable_nat_gateway = false
  enable_vpn_gateway = false
  tags = {
    Terraform   = "true"
    Environment = "dev"
  }
}

📌 Troubleshooting

2022.08.11 기준 Github action에서 제공하는 템플릿에 오류가 있었습니다.

      # On push to "main", build or change infrastructure according to Terraform configuration files
      # Note: It is recommended to set up a required "strict" status check in your repository for "Terraform Cloud". See the documentation on "strict" required status checks for more information: https://help.github.com/en/github/administering-a-repository/types-of-required-status-checks
    - name: Terraform Apply
      if: github.ref == 'refs/heads/"main"' && github.event_name == 'push'
      run: terraform apply -auto-approve -input=false

Terrafrom apply 태스크에서 브랜치를 보호하고자 하는 목적으로 작성된

if: github.ref == 'refs/heads/"main"' && github.event_name == 'push' 구문으로 인해서 워크플로우가 제대로 작동하지 않았습니다.

👉 해결 과정

Workflow에서 echo ${{variable}}을 사용하여 해당 값이 정상적으로 들어오는지 확인하였습니다.

  • 결과
    $ echo ${{github.ref}}
    > refs/heads/main
    
    $ echo ${{github.event_name}}
    > push

해당 변수들의 값은 정상적으로 출력되었습니다. 하지만 여전히 값이 true 임에도 terraform apply가 작동하지 않는 것을 확인 하였습니다.

조건문
if: github.ref == 'refs/heads/main' && github.event_name == 'push'

“main” → main 으로 변경

$ echo ${{github.ref}}
> refs/heads/main

$ echo ${{github.event_name}}
> push

동일한 값이 반환 되었으나, 이번에는 정상적으로 terraform apply가 실행됨을 볼 수 있었습니다.

📌 Conclusion

Git을 선언적 인프라 및 애플리케이션에 대한 단일 정보 소스(Source Of Truth, SOT)로 사용하여 GitOps 환경을 구축하였습니다.

GitOps를 직접 구축해본 결과, 테라폼 백엔드를 비용 효율적으로 공유 할 수 있는 방법을 찾을 수 있었습니다.

Github Action으로 인프라 프로비저닝을 자동화를 성공적으로 진행할 수 있게 되었습니다.

Gitgub Action workflow

AWS Console

✅ 참조 레퍼런스

profile
안녕하세요! DevOps 엔지니어 이재찬입니다. 블로그에 대한 피드백은 언제나 환영합니다! 기술, 개발, 운영에 관한 다양한 주제로 함께 나누며, 더 나은 협업과 효율적인 개발 환경을 만드는 과정에 대해 인사이트를 나누고 싶습니다. 함께 여행하는 기분으로, 즐겁게 읽어주시면 감사하겠습니다! 🚀

1개의 댓글

comment-user-thumbnail
2023년 7월 5일

깃 액션 코드에서 테라폼을 설치하는 명령어가 없는데 어떻게 terraform init 명령어가 실행되나요?

답글 달기