테라폼 - CI/CD에서 Terraform은 어디까지 맡아야 할까?
Terraform과 CI/CD의 역할 분리로 안정적인 인프라 배포 만들기
이전 글에서는 Terraform State를 왜 나누어야 하는지 정리했다.
State를 나누는 이유는 단순히 파일을 분리하기 위해서가 아니었다.
변경 범위 줄이기
destroy 범위 제한하기
의존성 방향 강제하기
CI/CD 파이프라인 분리하기
이번 글에서는 이 흐름을 이어서, Terraform과 CI/CD의 역할을 어떻게 나누면 좋은지 정리해보려 한다.
지금까지의 글에서 계속 반복된 핵심은 다음이었다.
Terraform은 리소스를 만든다.
하지만 시스템은 Phase를 거쳐 완성된다.
그렇다면 실제 배포 파이프라인에서는 어디까지 Terraform이 담당하고, 어디부터 CI/CD가 담당해야 할까?
목차
- 1. Terraform만으로 모든 것을 처리하려 할 때의 문제
- 2. Terraform의 역할
- 3. CI/CD의 역할
- 4. Terraform과 CI/CD 역할 분리 기준
- 5. 기본 파이프라인 구조
- 6. GitHub Actions에서의 Terraform 흐름
- 7. Apply 이후 단계: Ready, Attach, Activate
- 8. Destroy 파이프라인 설계
- 9. 운영 환경에서 필요한 보호 장치
- 10. 전체 구조 정리
- 11. 마무리
1. Terraform만으로 모든 것을 처리하려 할 때의 문제
Terraform을 처음 사용할 때는 다음 흐름으로 충분해 보인다.
terraform init
terraform plan
terraform apply
소규모 환경에서는 이 방식만으로도 큰 문제가 없다.
하지만 인프라 규모가 커지고, ECS, RDS, CloudFront, Route53, Lambda, EventBridge 같은 리소스들이 연결되기 시작하면 이야기가 달라진다.
Terraform apply 한 번에 다음 작업을 모두 처리하려고 하면 문제가 생긴다.
리소스 생성
리소스 준비 대기
리소스 간 연결
서비스 실행
트래픽 유입
헬스체크
스모크 테스트
롤백 판단
이 모든 작업은 성격이 다르다.
그런데 이를 하나의 Terraform apply 안에 모두 넣으려고 하면, 실패 원인을 파악하기 어려워진다.
Terraform 코드 문제인지
AWS 리소스 준비 지연인지
연결 문제인지
애플리케이션 런타임 문제인지
배포 검증 실패인지
구분이 어려워진다.
따라서 Terraform과 CI/CD의 역할을 분리하는 것이 중요하다.
2. Terraform의 역할
Terraform의 핵심 역할은 인프라의 구조를 선언하고 관리하는 것이다.
즉, Terraform은 다음 작업에 강하다.
리소스 생성
리소스 변경
리소스 삭제
리소스 간 참조 관계 관리
State 관리
Plan 생성
예를 들어 다음과 같은 리소스들은 Terraform으로 관리하기 적합하다.
VPC
Subnet
Security Group
IAM Role
RDS
ECR
ECS Cluster
ALB
Target Group
CloudFront
Route53
Lambda
EventBridge
Secrets Manager
SSM Parameter
Terraform은 이 리소스들이 어떤 속성을 가져야 하는지 선언하고, 현재 state와 비교해서 변경 계획을 만든다.
현재 코드
+
현재 state
=
terraform plan
그리고 apply를 통해 실제 인프라를 선언한 상태에 맞춘다.
Terraform은 인프라의 구조를 만드는 도구다.
3. CI/CD의 역할
CI/CD는 Terraform이 만든 구조 위에서 실제 배포 흐름을 완성하는 역할을 한다.
특히 Terraform apply 이후 다음 작업들은 CI/CD에서 다루는 것이 자연스럽다.
리소스 준비 상태 확인
배포 순서 제어
서비스 활성화
헬스체크
스모크 테스트
트래픽 전환
롤백 판단
이전 글에서 사용한 Phase로 보면 다음과 같다.
Create → Ready → Attach → Activate
Terraform은 주로 Create를 담당한다.
CI/CD는 Ready, Attach, Activate를 보완한다.
Terraform → Create / Destroy
CI/CD → Ready / Attach / Activate / 검증
예를 들어 ECS 배포라면 Terraform은 ECS Service를 만들 수 있다. 하지만 새로운 버전의 컨테이너가 정상적으로 실행되는지, ALB Health Check를 통과했는지, 실제 API가 응답하는지는 CI/CD에서 검증하는 것이 더 적절하다.
4. Terraform과 CI/CD 역할 분리 기준
그렇다면 어떤 작업은 Terraform이 맡고, 어떤 작업은 CI/CD가 맡아야 할까?
다음 기준으로 나누면 이해하기 쉽다.
| 구분 | Terraform | CI/CD |
|---|---|---|
| 주요 목적 | 인프라 구조 관리 | 배포 실행과 검증 |
| 관심 대상 | 리소스 존재 여부 | 서비스 동작 여부 |
| 기준 | State | Runtime 상태 |
| 주요 작업 | Create / Update / Destroy | Ready Check / Activate / Test / Rollback |
| 실패 유형 | 리소스 생성 실패 | 런타임 장애 / 헬스체크 실패 |
정리하면 다음과 같다.
Terraform이 관리하기 좋은 것:
- 변하지 않는 구조
- 인프라 리소스
- 권한
- 네트워크
- 데이터 저장소
- 기본 연결 구조
CI/CD가 관리하기 좋은 것:
- 배포 순서
- 실행 여부
- desired_count
- 스케줄러 enable / disable
- 헬스체크
- 스모크 테스트
- 롤백
5. 기본 파이프라인 구조
Terraform과 CI/CD를 함께 사용한다면 기본 흐름은 다음과 같이 나눌 수 있다.
1. 코드 변경
2. terraform fmt
3. terraform validate
4. terraform plan
5. 승인
6. terraform apply
7. Ready Check
8. Attach 확인
9. Activate
10. Health Check
11. Smoke Test
이 흐름은 단순히 apply만 실행하는 것보다 훨씬 안정적이다.
각 단계의 역할은 다음과 같다.
| 단계 | 역할 |
|---|---|
| fmt | Terraform 코드 포맷 확인 |
| validate | 문법과 기본 구성 검증 |
| plan | 변경 사항 미리 확인 |
| approval | 운영 반영 전 승인 |
| apply | 인프라 변경 적용 |
| ready check | 리소스 준비 상태 확인 |
| activate | 서비스 실행 또는 트래픽 유입 |
| health check | 서비스 정상 여부 확인 |
| smoke test | 핵심 기능 최소 검증 |
6. GitHub Actions에서의 Terraform 흐름
GitHub Actions를 사용한다면 워크플로우를 크게 두 가지로 나눌 수 있다.
PR 단계
→ 검증 중심
main 반영 또는 수동 실행 단계
→ apply 중심
6.1 PR 단계
PR 단계에서는 실제 인프라를 변경하지 않고 검증만 수행한다.
terraform fmt
terraform validate
terraform plan
목적은 다음과 같다.
코드 포맷이 맞는가?
문법 오류는 없는가?
예상 변경 사항은 무엇인가?
위험한 삭제가 포함되어 있는가?
PR에서 plan 결과를 확인하면 merge 전에 변경 영향을 검토할 수 있다.
6.2 Apply 단계
main 브랜치에 병합되었거나 수동 실행을 통해 apply를 수행한다.
terraform init
terraform plan
terraform apply
운영 환경에서는 apply 전에 승인 단계를 두는 것이 좋다.
plan 생성
→ 변경 사항 확인
→ 승인
→ apply
특히 RDS, S3, Route53, CloudFront 같은 리소스가 포함되어 있다면 plan 확인이 매우 중요하다.
6.3 예시 Workflow 구조
아래는 개념적인 예시다.
name: terraform-apply
on:
workflow_dispatch:
jobs:
plan:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Terraform Init
run: terraform init
- name: Terraform Plan
run: terraform plan
apply:
runs-on: ubuntu-latest
needs: plan
environment: production
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Terraform Init
run: terraform init
- name: Terraform Apply
run: terraform apply -auto-approve
여기서 environment: production을 사용하면 GitHub Environment 승인 규칙과 연결할 수 있다.
즉, 운영 apply 전에 수동 승인을 요구할 수 있다.
7. Apply 이후 단계: Ready, Attach, Activate
Terraform apply가 끝났다고 해서 배포가 완전히 끝난 것은 아니다.
apply 이후에도 확인해야 할 단계가 있다.
7.1 Ready Check
리소스가 실제 사용 가능한 상태인지 확인한다.
예를 들어 RDS는 available 상태인지 확인할 수 있다.
aws rds describe-db-instances \
--db-instance-identifier app-db \
--query "DBInstances[0].DBInstanceStatus"
CloudFront는 Deployed 상태인지 확인할 수 있다.
aws cloudfront get-distribution \
--id DISTRIBUTION_ID \
--query "Distribution.Status"
7.2 Attach 확인
리소스 간 연결이 정상인지 확인한다.
ECS Service가 Target Group과 연결되었는가?
ALB Listener가 올바른 Target Group을 바라보는가?
Lambda Permission이 EventBridge Rule을 허용하는가?
CloudFront에 WAF가 연결되었는가?
Attach는 Terraform 코드 안에서 표현될 수도 있고, 별도 스크립트나 CLI를 통해 검증할 수도 있다.
7.3 Activate
서비스를 실제 실행 상태로 전환한다.
예를 들어 ECS Service를 scale up 한다.
aws ecs update-service \
--cluster app-cluster \
--service app-service \
--desired-count 2
EventBridge Rule을 활성화할 수도 있다.
aws events enable-rule \
--name worker-schedule
Route53 Record를 통해 실제 트래픽을 유입시키는 것도 Activate 단계로 볼 수 있다.
7.4 Health Check와 Smoke Test
Activate 이후에는 서비스가 실제로 정상 동작하는지 확인해야 한다.
헬스체크는 서비스가 살아있는지 확인하는 과정이다.
curl -f https://api.example.com/actuator/health
스모크 테스트는 배포 직후 핵심 기능이 최소한 정상 동작하는지 확인하는 테스트다.
curl -f https://api.example.com/api/posts
curl -f https://api.example.com/api/users/me
헬스체크와 스모크 테스트가 실패하면 배포를 성공으로 보지 않는 것이 좋다.
8. Destroy 파이프라인 설계
Terraform에서 destroy는 apply보다 더 조심해야 한다.
생성은 다음 흐름으로 보았다.
Create → Ready → Attach → Activate
삭제는 반대로 생각해야 한다.
Deactivate → Detach → Destroy
8.1 Deactivate
먼저 실행 중인 서비스를 멈춘다.
ECS desired_count = 0
Scheduler disable
트래픽 차단
서비스가 계속 실행 중인 상태에서 리소스를 삭제하려고 하면 오류가 발생할 수 있다.
8.2 Detach
그 다음 연결을 해제한다.
CloudFront에서 WAF 연결 해제
ECS Service에서 Target Group 연결 해제
Route53 Record 제거
Lambda Permission 정리
AWS 리소스는 다른 리소스와 연결된 상태에서는 삭제가 제한되는 경우가 많다.
8.3 Destroy
마지막으로 Terraform destroy를 수행한다.
terraform destroy
운영 환경에서는 destroy를 자동으로 실행하지 않는 것이 좋다.
최소한 다음 보호 장치를 두는 것이 안전하다.
workflow_dispatch 수동 실행
운영 environment 승인
plan -destroy 확인
중요 리소스 prevent_destroy
백업 또는 스냅샷 확인
9. 운영 환경에서 필요한 보호 장치
Terraform CI/CD를 운영 환경에 적용할 때는 보호 장치가 필요하다.
9.1 수동 승인
운영 apply나 destroy는 자동으로 실행하지 않고 승인 단계를 두는 것이 좋다.
plan
→ 검토
→ 승인
→ apply
9.2 prevent_destroy
중요 리소스는 lifecycle 설정으로 삭제를 막을 수 있다.
resource "aws_db_instance" "main" {
identifier = "app-db"
lifecycle {
prevent_destroy = true
}
}
RDS, S3, KMS, 중요한 Secret에는 고려할 만하다.
9.3 plan 결과 확인
운영 환경에서는 plan 결과를 반드시 확인해야 한다.
특히 다음 변경은 주의해야 한다.
destroy 발생
replacement 발생
RDS 변경
Route53 변경
CloudFront 변경
IAM 권한 변경
9.4 최소 권한 IAM
GitHub Actions에서 AWS에 접근할 때는 가능한 최소 권한을 사용하는 것이 좋다.
또한 장기 Access Key를 저장하기보다 OIDC 기반 Assume Role 방식을 사용하는 것이 더 안전하다.
GitHub Actions
→ OIDC
→ AWS IAM Role Assume
→ Terraform 실행
9.5 환경 분리
dev, staging, prod 환경은 분리하는 것이 좋다.
dev
staging
prod
환경별로 state와 변수, 승인 규칙을 분리하면 실수 가능성을 줄일 수 있다.
10. 전체 구조 정리
지금까지 내용을 정리하면 다음과 같다.
Terraform
- 인프라 구조 정의
- 리소스 생성
- 리소스 변경
- 리소스 삭제
- State 관리
CI/CD
- 실행 순서 제어
- Ready Check
- Attach 확인
- Activate
- Health Check
- Smoke Test
- Rollback 판단
즉, Terraform과 CI/CD는 경쟁 관계가 아니다.
서로 다른 역할을 가진다.
Terraform은 구조를 만들고, CI/CD는 시스템이 동작하는지 확인한다.
이 관점을 가지면 Terraform을 무리하게 만능 도구로 사용하지 않게 된다.
또한 배포 실패가 발생했을 때 어느 단계에서 문제가 생겼는지 더 명확하게 파악할 수 있다.
11. 마무리
Terraform은 인프라 자동화에서 매우 강력한 도구다.
하지만 Terraform이 모든 것을 담당해야 하는 것은 아니다.
Terraform은 인프라의 구조를 만들고 관리하는 데 강하다. 반면 실제 서비스의 실행과 검증은 CI/CD에서 다루는 것이 더 자연스러운 경우가 많다.
복잡한 인프라에서는 다음 관점이 중요하다.
Terraform apply 성공
≠
서비스 정상 동작
따라서 다음 흐름을 기준으로 배포를 설계하는 것이 좋다.
Plan
→ Apply
→ Ready Check
→ Attach 확인
→ Activate
→ Health Check
→ Smoke Test
Destroy도 마찬가지다.
Deactivate
→ Detach
→ Destroy
이렇게 역할을 나누면 Terraform은 더 단순해지고, CI/CD는 더 안정적인 배포 흐름을 만들 수 있다.
한 줄 정리
Terraform은 구조를 만들고, CI/CD는 시스템을 완성한다.
이 글로 Terraform 리소스 분류와 배포 흐름에 대한 개념 정리는 마무리한다.
이후 글에서는 초보자가 바로 사용할 수 있는 Terraform 코드 작성법과 AWS 기본 템플릿을 다뤄볼 예정이다.
'테라폼' 카테고리의 다른 글
| 4-1. 테라폼 - AWS 기본 프로젝트 템플릿 만들기 (0) | 2026.05.11 |
|---|---|
| 4-0. 테라폼 - 초보자를 위한 기본 코드 작성법 (0) | 2026.05.11 |
| 3-3. 테라폼 - State는 왜 나눠야 할까? (2) | 2026.05.05 |
| 3-2. 테라폼 - 순환 참조는 왜 발생하고 어떻게 끊어야 할까? (0) | 2026.05.05 |
| 3-1. 테라폼 - apply는 성공했는데 왜 서비스는 동작하지 않을까? (0) | 2026.05.05 |