본문 바로가기
Tech/Terraform

[T101_3기] 5주차 - 워크플로

by 구름_쟁이 2023. 9. 30.

본 시리즈는 가시다님의 T101(테라폼으로 시작하는 IaC) 3기 진행 내용입니다. (가시다님 노션)

 

도서 정보

https://www.yes24.com/Product/Goods/119179333

 

테라폼으로 시작하는 IaC - 예스24

“현업에서 요구하는 진짜 IaC 사용법”테라폼으로 배우는 인프라 운영의 모든 것IaC는 효율적인 데브옵스와 클라우드 자동화 구축을 위해 꼭 필요한 기술로 각광받고 있다. 그중에서도 테라폼

www.yes24.com

실습 코드

https://github.com/terraform101

 

목차

1. 워크플로
1.1. 규모에 따른 워크플로
1.2. 격리 구조
1.3. 프로비저닝 파이프라인 설계 - 깃허브

 

 

 


1.1. 규모에 따른 워크플로

 

테라폼 워크플로 :

Write → Plan → Apply, 워크스페이스 별로 접근 권한을 관리하고 중앙에서 관리되는 실행 환경을 설계하여 규모에 맞는 워크플로 설계가 필요

개인 워크플로 :

개인이 테라폼으로 일하는 방식의 예

 

  • Write : 프로비저닝하려는 목적에 따라 테라폼 코드를 작성
    • 개인 작업이더라도 반복적인 사용성을 고려하자.
    • 인수에 할당되는 값을 입력 변수화하고 반복적인 구조가 발생하는 경우 리소스 단위별로 반복문을 사용할지 다수의 리소스를 모듈화할지 결정한다.
  • Plan : 적용하기 위한 실행 계획을 통해 리뷰
    • 테라폼의 Plan뿐 아니라, terraform fmt를 통해 코드 형태를 포멧팅하고 변경되는 리소스를 리뷰한다.
    • 또한 테라폼과 함께 동작하는 tfsec이나 terrascan 같은 보안 취약성 점검 툴 등을 활용하는 것도 좋은 방안이다.
  • Apply : 코드로 실제 인프라를 프로비저닝
    • 실행 계획상으로는 정상이지만 실제 프로비저닝하는 단계에서 인수 값, 생성 순서, 종속성에 따라 오류가 발생할 수 있다.
    • 성공적인 완료를 위해 Write > Plan > Apply 단계를 반복하고 성공하는 경우 코드 관리를 위해 VCS에 코드를 병합한다.

 

다중 작업자 워크플로

  • Write
    • 여러 작업자의 테라폼 코드가 충돌하지 않도록 VCS와 같은 형상관리 도구에 익숙해져야 한다.
    • 작업자는 작업 전에 미리 원격 저장소코드를 받고 깃에서는 브랜치를 활용해 개별적으로 작업한다.
    • 개인의 워크플로에서 고려한 변수화와 더불어 패스워드와 인증서 같은 민감 데이터가 포함되지 않도록 코드를 설계한다.
    • 또한 개인 작업 환경에서만 사용되는 변수는 공유하지 않는다.
    • 깃을 사용한다면 작업자 개인의 변수terraform.tfvars 에 선언하고 .gitignore에 추가해 개별적으로 테스트할 수 있는 환경을 구성할 수 있다
    • 이 단계에서 개별 작업자는 **작은 단위의 개별 워크플로(**Write > Plan > Apply)를 반복해야 한다.
    • 개별 작업 환경과 별개로 병합되는 코드가 실제 운영 중인 인프라에 즉시 반영되면 실행 후 발생할 오류 예측이 어려워 부담이 될 수 있다.
    • 이를 보완하기 위해 프로비저닝 대상의 환경을 검증운영, 또는 그 이상의 환경으로 구성 가능하도록 구조화한다.
    • 이때 사용하는 방식은 디렉터리 기반 격리깃 기반의 브랜치 격리다.
  • Plan
    • 둘 이상의 작업자는 프로비저닝 이전에 팀원 간 리뷰를 거쳐 변경된 내역을 확인하고 공통 저장소에 병합해야 한다.
    • 리뷰 단계에서는 추가, 삭제, 수정된 내역을 관련 작업자가 검증, 질의, 배움의 단계를 거쳐 복기함으로써 코드 상태를 개선 유지하고 작업자 간에 의도를 공유한다.
    • 코드 자체 외에도 테라폼의 Plan 결과를 풀 리퀘스트 단계에 같이 제공하면 영향을 받는 리소스와 서비스 중단에 대한 예측이 더 쉬워진다.
    • CI 툴과 연계하거나 Terraform Cloud/Enterprise의 VCS 통합 기능으로 자동화할 수 있다.
  • Apply
    • 코드가 최종 병합되면 인프라 변경이 수행됨을 알리고 변경되는 대상 환경의 중요도에 따라 승인이 필요할 수 있다.
    • 또한 변경하는 코드가 특정 기능, 버그 픽스, 최종 릴리즈를 위한 병합인가에 따라 이 단계에 추가로 코드 병합이 발생할 수 있다.
    • 관리하는 단위를 나누는 기준은 조직 R&R, 서비스, 인프라 종류 등으로 구분된다.

 

다수 팀의 워크플로 :

R&R이 분리된 다수 팀 또는 조직의 경우

  • R&R이 분리된 다수 팀 또는 조직의 경우 테라폼의 프로비저닝 대상은 하나이지만 관리하는 리소스가 분리된다.
  • 단일 팀의 워크플로가 유지되고 그 결과에 대해 공유해야 하는 핵심 워크플로가 필요하다.

  • Write
    • 대상 리소스가 하나의 모듈에서 관리되지 않고 R&R에 의해 워크스페이스가 분리된다.
    • 서로 다른 워크스페이스에서 구성된 리소스 데이터를 권한이 다른 팀에게 공유하기 위해, 저장된 State 접근 권한을 제공하고 output을 통해 공유 대상 데이터를 노출한다.
    • 테라폼 코드 작성 시 다른 워크스페이스에서의 변경 사항을 데이터 소스로 받아 오는 terraform_remote_state 또는 별도 KV-store를 활용하는 코드 구성이 요구된다.
    • 또한 관리 주체가 다른 곳에서 생긴 변경 사항의 영향을 최소화하도록 리모트 데이터 소스의 기본값을 정의하거나 코드적인 보상 로직을 구현하는 작업이 필요하다.
  • Plan
    • 코드 기반으로 진행되는 리뷰는 반영되는 다른 팀의 인프라를 VCS상의 코드 리뷰만으로도 공유받고 영향도를 검토할 수 있다.
    • 병합을 승인하는 단계에 영향을 받는 다른 팀의 작업자도 참여해야 한다.
  • Apply
    • 프로비저닝 실행과 결과에 대한 안내가 관련 팀에 알려져야 하므로 파이프라인 구조에서 자동화하는 것을 추천한다.
    • 실행 후의 영향도가 여러 팀이 관리하는 리소스에 전파될 수 있으므로 코드 롤백 훈련이 필요하다.
    • 생성된 결과에 다른 워크스페이스에서 참조되는 output 값의 업데이트된 내용을 다른 팀이 확인하는 권한 관리가 필요하다

 

 


1.2. 격리 구조

테라폼 수준의 격리 목표 : State를 분리

  • 테라폼은 파일이나 하위 모듈로 구분하더라고 동작 기준은 실행하는 루트 모듈에서 코드를 통합하고 하나의 State로 관리한다.
  • 애플리케이션 구조가 모놀리식(+아키텍처)에서 MSA로 변화하는 과정은 테라폼의 IaC 특성과도 결부된다.

 

  Monolithic Architecture MSA(Micro Service Architecture)
장점 단일, 소수 인원으로 개발 편리하며,
통합된 시나리오 검증이 쉽고
배포가 간편하다.
소규모 기능 단위로 배포와 테스트가 용이.
단위별로 새로운 구성 적용이 수월하다.
서비스가 독립적으로 실행된다.
단점 규모가 커지면 코드 추가, 수정, 삭제가 어렵다.
신규 작업자가 전체를 리뷰해야 함
부분적인 오류가 전체에 영향을 줌
다수의 배포를 위한 프로세스 구현이 필요하다.
단위별 연계를 위한 로직 구현 필요
나누는 기준 마련 필요
(ex, 비즈니스 도메인)

 

  • 테라폼 또한 사용하는 리소스가 적고 구조가 단순하면 모놀리식 방식으로 구성하는 것이 인프라 프로비저닝 구축 속도는 빠를 수 있다.
  • 하지만 유지 보수, 인수인계, 운영의 관점에서는 프로비저닝 단위별로 분류하는, 마치 MSA와도 같은 분산된 설계가 매몰 비용과 기술 부채를 줄이는 데 효과적이다.
  • 규모가 큰 워크플로를 만들기 위해서는 간단하고 조합 가능한 부분들이 모여 집합을 이루어야 한다.
  • 이러한 집합에서 발생하는 정보는 다른 집합과 교환할 수 있지만, 각 집합은 독립적으로 실행되며 다른 집합에 영향을 받지 않는 격리된 구조가 필요하다.
  • 초기 테라폼 적용 단계에서 단일 또는 소수의 작업자는 단일 대상에 대해 IaC를 적용하고 하나의 루트 모듈에 많은 기능을 포함시킬 가능성이 높다.

 

루트 모듈 격리(파일/디렉터리)

모놀리식 구조의 파일/디렉토리 구조 격리 설계

  • 단일 작업자가 테라폼으로 프로비저닝을 하는 많은 경우에 관리 편의성 및 배포 단순화를 위해 하나의 루트 디렉터리에 파일로 리소스들을 구분하거나, 디렉터리를 생성하고 하위에 구성 파일 묶음을 위치시켜 루트 모듈에서 하위 디렉터리를 모듈로 읽는 구조를 사용한다.
  • 작업자가 관리하는 영역 또는 프로비저닝되는 리소스 묶음의 독립적인 실행을 위해 단일 루트 모듈 내의 리소스를 다수의 루트 모듈로 분리하고 각 모듈의 State를 참조하도록 격리한다.
  • 관리적인 측면으로는 작업자들의 관리 영역을 분리시키고 깃 기준의 리모트 저장소도 접근 권한을 관리할 수 있다.
  • 협업과 관련해 작업자별로 특정 루트 모듈을 선정해 구성 작업을 진행해 코드 충돌을 최소화하는 환경을 구성하고 인수인계 과정에서 리뷰하는 영역을 최소화할 수 있다.

 

환경 격리 - 깃 브랜치

  • 서비스의 테스트, 검증, 운영 배포를 위해 테라폼으로 관리하는 리소스가 환경별로 격리되어야 한다면 디렉터리 구조로 분리하는 방안을 고려할 수 있다.
  • 디렉터리별로 각 환경을 나누는 것은 개인의 관리 편의성은 높지만, 환경의 아키텍처를 고정시키고 코드 수준의 승인 체계를 만들기 위해서는 최종 형상에 대한 환경별 브랜치를 구성하기를 권장한다.
  • 디렉터리 구조만으로는 환경에 따라 사용자를 격리할 수 없다. 이때 깃의 브랜치 기능을 활용하면 환경별로 구별된 작업과 협업이 가능하다.

  • main : 운영 코드가 관리되며 이곳에는 직접적으로 구성 변경을 수행하지 않음
  • QA : 검증 대상 인프라를 구성하는 코드로, 메인 브랜치와 같이 직접적인 구성 변경을 수행하지 않음
  • DEV : QA 전 단계로 메인 코드 구성과 기능 브랜치의 병합을 담당
  • feature : 새로운 리소스를 추가하고 구현하며 여러 개가 될 수 있음
  • 관리의 편의성을 고려해 Hot-fix와 Release 브랜치를 추가할 수도 있지만 인프라의 특성상 개발, 검증, 운영으로 나눈다.
  • 환경 간에 프로비저닝이 되는 리소스를 갖추고 있다면 운영을 위한 프로비저닝 환경을 안정적으로 유지할 수 있다는 장점이 있다.
  • 디렉터리 구조로 관리하는 환경별 디렉터리 구성 방식에서는 개발할 때 작성한 구성을 다시 복사해 검증 또는 운영에 반영하므로 환경별로 구성이 다른 상황이 발생할 여지가 높고, 모든 디렉터리에 접근 가능할 경우 검증과 운영을 위한 구성을 직접 수정하는 일이 발생할 가능성이 높다.

  • 이 방식은 동일한 브랜치를 변경해가면 작업해 발생하는 실수를 줄일 수 있고, 각 브랜치가 연결되어 있으므로 단일 작업자가 다수의 환경을 관리하는 이점과 각 환경별로 리소스 구성이 동일하게 유지되는 장점이 있다.

 

 

 


1.3. 프로비저닝 파이프라인 설계 - 깃허브

프로비저닝 파이프라인 + Github Action 준비

  • 실제 서비스가 실행되는 대상을 프로비저닝하면 테라폼의 Plan과 Apply 과정상에 추가로 코드 검증, 실행 계획 검증, 실행 후 결과 확인과 같은 추가 동작을 자동화해 연계할 필요성이 생긴다.
  • 도구 : 젠킨스, Github Action, TFC/TFE
  • Github Action : 깃허브 환경에서 제공하는 CI/CD 자동화 도구 - 워크플로를 설계하고 다양한 라이브러리들을 이용해 다양한 작업 구성이 가능
  • 저장소 포크 : https://github.com/terraform101/terraform-aws-github-action
  • Github Action은 별도의 State 저장소를 제공하지 않기 때문에 테라폼 실행으로 생성되는 State가 항상 초기화되어 프로비저닝 결과를 유지할 수 없다.
  • 따라서 아래의 과정으로 백엔드를 활성화

1. 리모트 저장소를 로컬 환경에 복제

#
MyGit=<각자 자신의 깃허브 계정>
MyGit=gasida
git clone https://github.com/$MyGit/terraform-aws-github-action

# 확인
tree terraform-aws-github-action
cd terraform-aws-github-action
git remote get-url origin

2. main.tf의 terraform 블록에서 사용자의 TFC 설정 organization으로 변경

# terraform login 설정 토큰 확인
cat ~/.terraform.d/credentials.tfrc.json | jq
    • main.tf 내용 수정
terraform {
  cloud {
    organization = "<MY_ORG_NAME>"         # 생성한 ORG 이름 지정
    hostname     = "app.terraform.io"      # default

    workspaces {
      name = "terraform-aws-github-action"
    }
  }
...
    • .github/workflow/action.yml 내용 수정
env:
  MY_PREFIX: DEV
  TF_VERSION: 1.2.6 # 1.2.5에서 변경
    • push
git add main.tf
git add .github/workflow/action.yml
git commit -m "init"
git push
  • .github/workflow/action.yml 파일 push 시 에러 발생 시 → 해당 토큰에 workflow 권한 추가 후 다시 push 할 것

3. 지정된 Terraform Cloud 백엔드 활성화를 위해 terraform init을 수행

#
terraform init
tree .terraform

4. 생성된 TFC 워크스페이스의 실행 모드 Execution mode를 Local로 수정

  • terraform-aws-github-action 워크스페이스(State 백엔드 역할만 수행) 확인 → 선택 후 좌측에 Settings 클릭 → General
  • 실행 모드 변경 : Execution Mode 에서 Local 선택 → 하단의 Save settings 클릭하여 변경 적용

실습에서 사용되는 Github Action에 정의된 동작의 설명

Job ‘Scan’ : 테라폼 코드 검증

Job ‘Terraform’ : 테라폼 실행 ← Job ‘Scan’ 이후 실행

  • Check out code : 검증을 위해 저장소의 코드를 체크아웃
  • Configure AWS credentials : AWS 환경을 프로비저닝하기 위한 Credential 설정
  • Terraform Fmt : 표준 스타일 수정 대상 확인
  • Terraform init : 테라폼 실행을 위한 init 수행
  • Terraform validate : 코드 문법 오류 검사
  • Terraform plan : 실행 계획 확인
    • env.TF_LOG: info : 로그 수즌을 info로 출력해 실행 디버깅
  • Plan output : 풀 리퀘스트인 경우 실행 계획을 정리해 출력
  • Terraform apply : 메인 브랜치 변경 시에만 Apply 수행

예시의 동작 외에도 프로비저닝 이후의 테스트를 위한 terratest 도구와 비용 예측을 위한 terracost, infracost 도구들도 추가해볼 수 있다.

 

코드 내용 설명

action.yml : Github Action의 구성은 .github/workflows의 yml 파일 형태로 작성

name: Terraform DEV

on:
  push:
    branches:
      - main
  pull_request:

env:
  MY_PREFIX: DEV
  TF_VERSION: 1.2.5

jobs:
  SCAN:
    name: SCAN
    runs-on: ubuntu-latest
    # env:
    #   working-directory: terraform
    #   TF_WORKSPACE: my-workspace
    steps:
      # - name: Configure AWS credentials
      #   uses: aws-actions/configure-aws-credentials@v1
      #   with:
      #     aws-region: eu-west-1

      - name: Check out code
        uses: actions/checkout@v3
        
      - name: Run Terrascan
        id: terrascan
        uses: tenable/terrascan-action@main
        with:
          iac_type: 'terraform'
          iac_version: 'v14'
          policy_type: 'aws'
          only_warn: true
          sarif_upload: true

      - name: Upload SARIF file
        uses: github/codeql-action/upload-sarif@v2
        with:
          sarif_file: terrascan.sarif  
  Terraform:
    needs: SCAN
    name: Terraform
    runs-on: ubuntu-latest
    steps:
      - name: Check out code
        uses: actions/checkout@v3

      - uses: hashicorp/setup-terraform@v2
        with:
          terraform_version: $TF_VERSION
          cli_config_credentials_token: ${{ secrets.TF_API_TOKEN }}

      - name: Terraform Fmt
        id: fmt
        run: terraform fmt -recursive -check
        continue-on-error: true

      - name: Terraform init
        id: init
        run: terraform init -upgrade
        # working-directory: ${{ env.working-directory }}

      - name: Terraform validate
        id: validate
        run: terraform validate -no-color

      - name: Terraform plan
        id: plan
        run: terraform plan -no-color -var=prefix="$MY_PREFIX"
        # working-directory: ${{ env.working-directory }}
        env:
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          TF_LOG: info

      - name: Plan output
        id: output
        uses: actions/github-script@v3
        if: github.event_name == 'pull_request'
        env:
          PLAN: "terraform\n${{ 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 Plan 📖\`${{ steps.plan.outcome }}\`
            <details><summary>Show Plan</summary>
            \`\`\`hcl
            ${process.env.PLAN}
            \`\`\`
            </details>
            **Pusher**: @${{ github.actor }}
            **Action**: ${{ github.event_name }}
            `;
            github.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: output
            })

      - name: Terraform apply
        id: apply
        if: github.ref == 'refs/heads/main' && github.event_name == 'push'
        run: terraform apply -auto-approve -var=prefix="$MY_PREFIX" -input=false
        env:
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}

main.tf : 프로비저닝의 실행은 작업자나 Github Action에서 발생하더라도 동일한 State유지를 위해 백엔드 구성 추가

terraform {
  cloud {
    organization = "<MY_ORG_NAME>"         # 생성한 ORG 이름 지정
    hostname     = "app.terraform.io" # default

    workspaces {
      name = "terraform-aws-github-action"
    }
  }
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 4.0"
    }
  }
}

provider "aws" {
  region = var.region
  default_tags {
    tags = {
      Project = "Coffee-Mug-Cake"
      Owner   = "jerry & tom"
    }
  }
}

resource "aws_vpc" "hashicat" {
  cidr_block           = var.address_space
  enable_dns_hostnames = true

  tags = {
    name        = "${var.prefix}-vpc-${var.region}"
    environment = "Production"
  }
}

resource "aws_subnet" "hashicat" {
  vpc_id     = aws_vpc.hashicat.id
  cidr_block = var.subnet_prefix

  tags = {
    name = "${var.prefix}-subnet"
  }
}

resource "aws_security_group" "hashicat" {
  name = "${var.prefix}-security-group"

  vpc_id = aws_vpc.hashicat.id

  ingress {
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  ingress {
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  ingress {
    from_port   = 443
    to_port     = 443
    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"]
    prefix_list_ids = []
  }

  tags = {
    Name = "${var.prefix}-security-group"
  }
}

resource "aws_internet_gateway" "hashicat" {
  vpc_id = aws_vpc.hashicat.id

  tags = {
    Name = "${var.prefix}-internet-gateway"
  }
}

resource "aws_route_table" "hashicat" {
  vpc_id = aws_vpc.hashicat.id

  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.hashicat.id
  }
}

resource "aws_route_table_association" "hashicat" {
  subnet_id      = aws_subnet.hashicat.id
  route_table_id = aws_route_table.hashicat.id
}

data "aws_ami" "ubuntu" {
  most_recent = true
  owners      = ["099720109477"] # Canonical

  filter {
    name   = "name"
    values = ["ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"]
  }

  filter {
    name   = "virtualization-type"
    values = ["hvm"]
  }
}

resource "aws_eip" "hashicat" {
  instance = aws_instance.hashicat.id
  vpc      = true
}

resource "aws_eip_association" "hashicat" {
  instance_id   = aws_instance.hashicat.id
  allocation_id = aws_eip.hashicat.id
}

resource "aws_instance" "hashicat" {
  ami                         = data.aws_ami.ubuntu.id
  instance_type               = var.instance_type
  key_name                    = aws_key_pair.hashicat.key_name
  associate_public_ip_address = true
  subnet_id                   = aws_subnet.hashicat.id
  vpc_security_group_ids      = [aws_security_group.hashicat.id]

  tags = {
    Name = "${var.prefix}-hashicat-instance"
  }
}

# We're using a little trick here so we can run the provisioner without
# destroying the VM. Do not do this in production.

# If you need ongoing management (Day N) of your virtual machines a tool such
# as Chef or Puppet is a better choice. These tools track the state of
# individual files and can keep them in the correct configuration.

# Here we do the following steps:
# Sync everything in files/ to the remote VM.
# Set up some environment variables for our script.
# Add execute permissions to our scripts.
# Run the deploy_app.sh script.
resource "null_resource" "configure-cat-app" {
  depends_on = [aws_eip_association.hashicat]

  // triggers = {
  //   build_number = timestamp()
  // }

  provisioner "file" {
    source      = "files/"
    destination = "/home/ubuntu/"

    connection {
      type        = "ssh"
      user        = "ubuntu"
      private_key = tls_private_key.hashicat.private_key_pem
      host        = aws_eip.hashicat.public_ip
    }
  }

  provisioner "remote-exec" {
    inline = [
      "sudo apt -y update",
      "sleep 15",
      "sudo apt -y update",
      "sudo apt -y install apache2",
      "sudo systemctl start apache2",
      "sudo chown -R ubuntu:ubuntu /var/www/html",
      "chmod +x *.sh",
      "PLACEHOLDER=${var.placeholder} WIDTH=${var.width} HEIGHT=${var.height} PREFIX=${var.prefix} ./deploy_app.sh",
      "sudo apt -y install cowsay",
      "cowsay Mooooooooooo!",
    ]

    connection {
      type        = "ssh"
      user        = "ubuntu"
      private_key = tls_private_key.hashicat.private_key_pem
      host        = aws_eip.hashicat.public_ip
    }
  }
}

resource "tls_private_key" "hashicat" {
  algorithm = "RSA"
}

locals {
  private_key_filename = "${var.prefix}-ssh-key.pem"
}

resource "aws_key_pair" "hashicat" {
  key_name   = local.private_key_filename
  public_key = tls_private_key.hashicat.public_key_openssh
}

Github Action 과정에서 필요로 하는 State 공유를 위한 Terraform Cloud의 토큰, AWS 프로비저닝을 위한 AWS Credential과 같은 민감 데이터를 저장소에서 민감 변수로 처리할 수 있다.

 

(옵션) AWS IAM 계정 생성 및 자격증명 획득

aws iam create-user --user-name testuser
aws iam create-access-key --user-name testuser
aws iam attach-user-policy --policy-arn arn:aws:iam::aws:policy/AdministratorAccess --user-name testuser

"AccessKeyId": "AKIA5I**********",
"SecretAccessKey": "1K/h1h231kvf*************"

해당 저장소의 [Setting] → [Secrets and variables - Actions] 선택 ⇒ 새로운 민감 변수 등록 [New repository secret]

  • Name : TF_API_TOKEN
  • Secret : <각자 자신의 TFC Token>

설정된 민감 변수는 Github Acction 정의 파일에서 ${{ secrets. 변수이름 }} 으로 호출된다

grep secrets .github/workflows/action.yml
cli_config_credentials_token: ${{ secrets.TF_API_TOKEN }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
github-token: ${{ secrets.GITHUB_TOKEN }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
  • TF_API_TOKEN : TFC의 기존 사용 토큰(credentials.tfrc.json 에 저장됨) 또는 신규 토큰을 생성해 입력
  • AWS_ACCESS_KEY_ID : AWS Access Key 입력
  • AWS_SECRET_ACCESS_KEY : AWS Secret Access Key 입력

포크되어 비활성화되어 있는 저장소의 [Actions] 탭으로 이동해 [I understand my workflows, go ahead and enable them] 버튼을 클릭, 활성화한다

 

해당 저장소에 Actions - Workflow permissions 수정

 

연습 문제 1 : p259, 동작 확인을 위해 다음의 조건으로 브랜치를 생성하고 새로운 입력 변수를 적용한다

아래 Automate Terraform with GitHub Actions 가이드 참고해서 토큰 생성 하고 실습 진행하자

https://developer.hashicorp.com/terraform/tutorials/automation/github-actions#prerequisites

 

Automate Terraform with GitHub Actions | Terraform | HashiCorp Developer

Automate infrastructure deployments with CI/CD using Terraform Cloud and GitHub Actions

developer.hashicorp.com

  • 조건
    • 브랜치 이름 : add-env-variable
    • 테라폼 입력 변수 추가
      • 이름 : environment
      • 설명 : Define infrastructure’s environment
      • 타입 : string
      • 기본값 : dev
      • 변수 확인
        • 조건 : dev, qa, prod 인 경우 허용
        • 에러 메시지 : The environment value must be dev, qa, or prod.
    • aws_vpctags.environment를 입력 변수 environment 값으로 선언
  • 새로운 브랜치의 변경 내용을 커밋, 푸시하고 깃허브 웹페이지에서 풀 리퀘스트를 생성한다 ← 주의: 자신의 저장소로 요청하는지 꼭 확인
  • Github Action의 동작 조건은 메인 브랜치에 푸시가 발생하거나 풀 리퀘스트가 발생하는 경우로 정의되어 있다
# Github Action의 실행 조건 : ./github/workflows/action.yml
on:
  push:
    branches:
      - main
  pull_request:
..

문제풀이

  • 브랜치 생성 : add-env-variable

  • main.tf 내용 수정
resource "aws_vpc" "hashicat" {
  cidr_block           = var.address_space
  enable_dns_hostnames = true

  tags = {
    environment = var.environment
  }
}
    • variable.tf 내용 추가

정규표현식 확인 (https://regexr.com/)

    • push
  • Github에 Action 확인 : 아래 Terraform Job 클릭 시 상세 동작 확인(terraform plan 까지 동작, apply 는 x)

 

  • 동작 성공 시 → Main RP 병합 수행 ⇒ Github Action 에서 Apply 수행
  • AWS 서울 리전에 리소스 생성 확인 : ec2, vpc 등 확인

테라폼 결과로 output된  ec2-54-190-23-15.us-west-2.compute.amazonaws.com:8080 주소로 접속

  • Github Action 확인 : 아래 Terraform Job 클릭 시 상세 동작 확인(terraform apply 까지 동작)

 

리소스 삭제 (사실 제일 중요)

Destroy 역시 Plan이 먼저 돌아간다. 기다려보자.

Confirm 해주자~

마지막으로 Confirm 해줘야 끝이난다. Quickly! Quickly!

 

 

 

결론

5주차도 이렇게 끝이라니...

(끝은 아니고 못 다한 것들을 다 끝내자!)

단순히 한번 스터디하고 끝나는 것이 아닌 모자란 부분은 더 더 학습하자

 

 

댓글