"테라폼으로 시작하는 IaC"책을 기준으로 7주차 정리 내용입니다.
# 8.1 규모에 따른 워크플로
테라폼 워크플로는 Write > Plan > Apply 로 구성된다.
워크스페이스 별로 접근 권한을 관리하고 중앙에서 관리되는 실행 환경을 설계하여 규모에 맞는 워크플로 설계가 필요하다.
# 8.1.1 개인 워크플로

출처 - https://www.freecodecamp.org/news/terraform-workflow-working-individually-and-in-a-team/
- Write : 프로비저닝하려는 목적에 따라 테라폼 코드를 작성
- 개인 작업이더라도 반복적인 사용성을 고려
- 인수에 할당되는 값을 입력 변수화
- 반복적인 구조는 모듈화
- Plan : 적용하기 위한 실행 계획을 통해 리뷰
- 테라폼의 Plan뿐 아니라, terraform fmt를 통해 코드 형태를 포멧팅하고 변경되는 리소스를 리뷰
- 또한 테라폼과 함께 동작하는 tfsec이나 terrascan 같은 보안 취약성 점검 툴 등을 활용
- Apply : 코드로 실제 인프라를 프로비저닝
- 실행 계획상으로는 정상이지만 실제 프로비저닝하는 단계에서 인수 값, 생성 순서, 종속성에 따라 오류가 발생 가능
- 성공적인 완료를 위해 Write > Plan > Apply 단계를 반복하고 성공하는 경우 코드 관리를 위해 VCS에 코드를 병합
# 8.1.2 다중 작업자 워크플로

출처 - https://www.freecodecamp.org/news/terraform-workflow-working-individually-and-in-a-team/
- Write
- 여러 작업자의 테라폼 코드가 충돌하지 않도록 VCS와 같은 형상관리 도구를 사용
- 작업자는 작업 전에 미리 원격 저장소의 코드를 받고 깃에서는 브랜치를 활용해 개별적으로 작업
- 개인의 워크플로에서 고려한 변수화와 더불어 패스워드와 인증서 같은 민감 데이터가 포함되지 않도록 코드를 설계
- 또한 개인 작업 환경에서만 사용되는 변수는 공유하지 않는다.
- 깃을 사용한다면 작업자 개인의 변수는 terraform.tfvars 에 선언하고 .gitignore에 추가해 개별적인 테스트가 가능
- 이 단계에서 개별 작업자는 작은 단위의 개별 워크플로(Write > Plan > Apply)를 반복해야 한다.
- 개별 작업 환경과 별개로 병합되는 코드가 실제 운영 중인 인프라에 즉시 반영되면 실행 후 발생할 오류 예측이 어려워 부담이 될 수 있다.
- 이를 보완하기 위해 프로비저닝 대상의 환경을 검증과 운영, 또는 그 이상의 환경으로 구성 가능하도록 구조화
- 이때 사용하는 방식은 디렉터리 기반 격리와 깃 기반의 브랜치 격리
- Plan
- 둘 이상의 작업자는 프로비저닝 이전에 팀원 간 리뷰를 거쳐 변경된 내역을 확인하고 공통 저장소에 병합해야 한다.
- 리뷰 단계에서는 추가, 삭제, 수정된 내역을 관련 작업자가 검증, 질의, 배움의 단계를 거쳐 복기하여 코드 상태를 개선 유지하고 작업자 간에 의도를 공유
- 코드 자체 외에도 테라폼의 Plan 결과를 풀 리퀘스트 단계에 같이 제공하면 영향을 받는 리소스와 서비스 중단에 대한 예측이 더 쉬워진다.
- CI 툴과 연계하거나 Terraform Cloud/Enterprise의 VCS 통합 기능으로 자동화할 수 있다.
- Apply
- 코드가 최종 병합되면 인프라 변경이 수행됨을 알리고 변경되는 대상 환경의 중요도에 따라 승인이 필요할 수 있다.
- 또한 변경하는 코드가 특정 기능, 버그 픽스, 최종 릴리즈를 위한 병합인가에 따라 이 단계에 추가로 코드 병합이 발생할 수 있다.
- 관리하는 단위를 나누는 기준은 조직 R&R, 서비스, 인프라 종류 등으로 구분
# 8.1.3 다수 팀의 워크플로
- R&R이 분리된 다수 팀 또는 조직의 경우 테라폼의 프로비저닝 대상은 하나이지만 관리하는 리소스가 분리된다.
- 단일 팀의 워크플로가 유지되고 그 결과에 대해 공유해야 하는 핵심 워크플로가 필요하다.
- Write
- 대상 리소스가 하나의 모듈에서 관리되지 않고 R&R에 의해 워크스페이스가 분리된다.
- 서로 다른 워크스페이스에서 구성된 리소스 데이터를 권한이 다른 팀에게 공유하기 위해, 저장된 State 접근 권한을 제공하고 output을 통해 공유 대상 데이터를 노출한다.
- 테라폼 코드 작성 시 다른 워크스페이스에서의 변경 사항을 데이터 소스로 받아 오는 terraform_remote_state 또는 별도 KV-store를 활용하는 코드 구성이 요구된다.
- 또한 관리 주체가 다른 곳에서 생긴 변경 사항의 영향을 최소화하도록 리모트 데이터 소스의 기본값을 정의하거나 코드적인 보상 로직을 구현하는 작업이 필요하다.
- Plan
- 코드 기반으로 진행되는 리뷰는 반영되는 다른 팀의 인프라를 VCS상의 코드 리뷰만으로도 공유받고 영향도를 검토할 수 있다.
- 병합을 승인하는 단계에 영향을 받는 다른 팀의 작업자도 참여해야 한다.
- Apply
- 프로비저닝 실행과 결과에 대한 안내가 관련 팀에 알려져야 하므로 파이프라인 구조에서 자동화하는 것을 추천한다.
- 실행 후의 영향도가 여러 팀이 관리하는 리소스에 전파될 수 있으므로 코드 롤백 훈련이 필요하다.
- 생성된 결과에 다른 워크스페이스에서 참조되는 output 값의 업데이트된 내용을 다른 팀이 확인하는 권한 관리가 필요하다
# 8.2 격리 구조
테라폼 수준의 격리는 State를 분리하는 데 목적이 있다.
- 테라폼은 파일이나 하위 모듈로 구분하더라고 동작 기준은 실행하는 루트 모듈에서 코드를 통합하고 하나의 State로 관리한다.
- 애플리케이션 구조가 모놀리식(+아키텍처)에서 MSA로 변화하는 과정은 테라폼의 IaC 특성과도 결부된다.
@ Monolithic Architecture
- 장점
- 단일, 소수 인원으로 개발 편리
- 통합된 시나리오 검증이 쉬운
- 배포 간편
- 단점
- 규모가 커지면 코드 추가, 수정, 삭제가 어려움
- 신규 작업자가 전체를 리뷰해야 함
- 부분적인 오류가 전체에 영향을
@ Micro Service Architecture
- 장점
- 소규모 기능 단위로 배포와 테스트가 용이
- 단위별 새로운 구성 적용이 수월
- 서비스가 독립적으로 실행
- 단점
- 다수의 배포를 위한 프로세스 구현 필요
- 단위별 연계를 위한 로직 구현 필요
- 나누는 기준 마련
- 테라폼 또한 사용하는 리소스가 적고 구조가 단순하면 모놀리식 방식으로 구성하는 것이 인프라 프로비저닝 구축 속도는 빠를 수 있다.
- 하지만 유지 보수, 인수인계, 운영의 관점에서는 프로비저닝 단위별로 분류하는, 마치 MSA와도 같은 분산된 설계가 매몰 비용과 기술 부채를 줄이는 데 효과적이다.
- 규모가 큰 워크플로를 만들기 위해서는 간단하고 조합 가능한 부분들이 모여 집합을 이루어야 한다.
- 이러한 집합에서 발생하는 정보는 다른 집합과 교환할 수 있지만, 각 집합은 독립적으로 실행되며 다른 집합에 영향을 받지 않는 격리된 구조가 필요하다.
- 초기 테라폼 적용 단계에서 단일 또는 소수의 작업자는 단일 대상에 대해 IaC를 적용하고 하나의 루트 모듈에 많은 기능을 포함시킬 가능성이 높다.
# 8.2.1 루트 모듈 격리(파일/디렉토리)
- 단일 작업자가 테라폼으로 프로비저닝을 하는 많은 경우에 관리 편의성 및 배포 단순화를 위해 하나의 루트 디렉터리에 파일로 리소스들을 구분하거나, 디렉터리를 생성하고 하위에 구성 파일 묶음을 위치시켜 루트 모듈에서 하위 디렉터리를 모듈로 읽는 구조를 사용한다.
- 작업자가 관리하는 영역 또는 프로비저닝되는 리소스 묶음의 독립적인 실행을 위해 단일 루트 모듈 내의 리소스를 다수의 루트 모듈로 분리하고 각 모듈의 State를 참조하도록 격리한다.
- 관리적인 측면으로는 작업자들의 관리 영역을 분리시키고 깃 기준의 리모트 저장소도 접근 권한을 관리할 수 있다.
- 협업과 관련해 작업자별로 특정 루트 모듈을 선정해 구성 작업을 진행해 코드 충돌을 최소화하는 환경을 구성하고 인수인계 과정에서 리뷰하는 영역을 최소화할 수 있다.
# 8.2.2 환경 격리 - 깃 브랜치
- 서비스의 테스트, 검증, 운영 배포를 위해 테라폼으로 관리하는 리소스가 환경별로 격리되어야 한다면 디렉터리 구조로 분리하는 방안을 고려할 수 있다.
- 디렉터리별로 각 환경을 나누는 것은 개인의 관리 편의성은 높지만, 환경의 아키텍처를 고정시키고 코드 수준의 승인 체계를 만들기 위해서는 최종 형상에 대한 환경별 브랜치를 구성하기를 권장한다.
- 디렉터리 구조만으로는 환경에 따라 사용자를 격리할 수 없다. 이때 깃의 브랜치 기능을 활용하면 환경별로 구별된 작업과 협업이 가능하다.

출처 - https://betterprogramming.pub/a-simple-git-repository-strategy-93a0c7450f23
- main : 운영 코드가 관리되며 이곳에는 직접적으로 구성 변경을 수행하지 않음
- QA : 검증 대상 인프라를 구성하는 코드로, 메인 브랜치와 같이 직접적인 구성 변경을 수행하지 않음
- DEV : QA 전 단계로 메인 코드 구성과 기능 브랜치의 병합을 담당
- feature : 새로운 리소스를 추가하고 구현하며 여러 개가 될 수 있음
- 관리의 편의성을 고려해 Hot-fix와 Release 브랜치를 추가할 수도 있지만 인프라의 특성상 개발, 검증, 운영으로 나눈다.
- 환경 간에 프로비저닝이 되는 리소스를 갖추고 있다면 운영을 위한 프로비저닝 환경을 안정적으로 유지할 수 있다는 장점이 있다.
- 디렉터리 구조로 관리하는 환경별 디렉터리 구성 방식에서는 개발할 때 작성한 구성을 다시 복사해 검증 또는 운영에 반영하므로 환경별로 구성이 다른 상황이 발생할 여지가 높고, 모든 디렉터리에 접근 가능할 경우 검증과 운영을 위한 구성을 직접 수정하는 일이 발생할 가능성이 높다.
- 따라서 작업자가 다수의 환경을 동시에 관리한다면 디렉터리로 구분하더라도 각 디렉터리마다 동일한 깃 저장소의 브랜치별 리모트 구성을 하는 것이 바람직하다.
# 디렉터리 격리에 깃 브랜치 연결
이 방식은 동일한 브랜치를 변경해가면 작업해 발생하는 실수를 줄일 수 있고, 각 브랜치가 연결되어 있으므로 단일 작업자가 다수의 환경을 관리하는 이점과 각 환경별로 리소스 구성이 동일하게 유지되는 장점이 있다.
# 8.3 프로비저닝 파이프라인 설계 - 깃허브
실제 서비스가 실행되는 대상을 프로비저닝하면 테라폼의 Plan과 Apply 과정상에 추가로 코드 검증, 실행 계획 검증, 실행 후 결과 확인과 같은 추가 동작을 자동화해 연계할 필요성이 생긴다.
백엔드 활성화를 진행하자
- Remote Repo를 Local 환경에 복제
MyGit=skwwnl
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

- main.tf의 terraform 블록에서 사용자의 TFC 설정 organization으로 변경
cat ~/.terraform.d/credentials.tfrc.json | jq

terraform {
cloud {
organization = "<MY_ORG_NAME>"
hostname = "app.terraform.io"
workspaces {
name = "terraform-aws-github-action"
}
}

- .github/workflow/action.yml 내용 수정
env:
MY_PREFIX: DEV
TF_VERSION: 1.2.6
git add main.tf
git add .github/workflow/action.yml
git commit -m "init"
git push
push 하는 도중 에러 발생!!

해결은 아래와 같이 토큰의 권한 설정에서 workflow를 체크해준다.


해결 !!
- 지정된 Terraform Cloud 백엔드 활성화를 위해 terraform init을 수행
terraform init
tree .terraform
워크스페이스가 생성된다.

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

- Secrets 정보 등록

# 연습 문제1 (실패)
- 브랜치 생성 : add-env-variable
git branch
**git branch -M add-env-variable**
resource "aws_vpc" "hashicat" {
cidr_block = var.address_space
enable_dns_hostnames = true
tags = {
name = "${var.prefix}-vpc-${var.region}"
environment = var.environment
}
}
variable "environment" {
type = string
description = "Define infrastructure’s environment"
default = "dev"
validation {
condition = contains(["dev", "qa", "prod"], var.environment)
error_message = "The environment value must be dev, qa, or prod."
}
}
terraform fmt
git add .
git commit -m "add env variable"
git push origin HEAD

- PR 날리기


실패가 뜬다..
해결 : 레포지토리 Settings -> Actions -> General -> 최하단 Workflow permissions을 read and write permissions로 변경

성공!!

EC2까지 잘 생성되었다.
워크플로 구성 환경 제공, Github Action 보다 자유도는 낮지만 VCS연동, 변수 구성, RBAC, 원격 실행 환경 등의 기능 활용
저장소 포크 : https://github.com/terraform101/terraform-aws-tfc-workflow
MyGit=<>
MyGit=skwwnl
git clone https://github.com/$MyGit/terraform-aws-tfc-workflow
cd terraform-aws-tfc-workflow
MyTfcOrg=<각자 자신의 TFC 조직명>
MyTfcOrg=t102test
sed -i -e "s/<MY-ORG>/$MyTfcOrg/g" main.tf
git add main.tf
git commit -m "init"
git push
terraform init

원격에서 plan을 진행하고 있으며, 보고 싶으면 아래 주소에 방문해서 볼 수 있다.

prefix의 기본 값도 없어서 에러가 발생.
- 입력 변수 : TFC에서는 실행 후 인라인으로 넣을 수 없기 때문이며, 기존 terraform.tfvars 를 대체하는 역할을 수행함.
- 따라서 기존 terraform.tfvars 파일은 동작하지 않는다

직접 prefix와 내 닉네임을 넣어서 변수를 생성한다.

추가로 AWS ID와 Secret을 추가하고, sensitive를 체크하여 Value를 숨길 수 있다.

다시 Plan하면 정상적으로 나온다.

TFC에서 apply를 직접 진행한다.

최종적으로 EC2 Instance가 생성됨을 볼 수 있습니다.

마무리는 모든 리소스를 지우는 Destroy를 진행합니다.
좋은 글 감사합니다. 자주 올게요 :)