포스트

AWS ML CI/CD와 CDC 파이프라인 구축 (5) Terraform 코드화와 마무리

콘솔로 수동 구축했던 AWS 리소스를 Terraform 7개 파일로 코드화한 과정부터 terraform validate 문법 검증, 종료 후 리소스 정리까지 프로젝트 마무리 작업과 시리즈 전체에서 배운 것을 정리합니다.

AWS ML CI/CD와 CDC 파이프라인 구축 (5) Terraform 코드화와 마무리

AWS ML CI/CD와 CDC 파이프라인 구축 프로젝트 시리즈의 5편입니다. 프로젝트 소개는 0편에 있습니다.

마무리 단계에서 한 일

2026-07-25부터 2026-07-26까지 프로젝트 마지막 단계로 문서화와 인프라 코드화를 진행했습니다. 산출물은 세 갈래입니다.

  • 설계 문서 3종: docs/00-overview.md(두 파이프라인을 연결한 통합 아키텍처), docs/01-ml-cicd-pipeline.md(ML CI/CD 설계와 AWS 실증 기록), docs/02-cdc-pipeline.md(CDC 설계와 로컬 실측 기록)
  • ml-cicd/infra/의 Terraform 파일 7개: 2편3편에서 콘솔로 수동 구축한 AWS 리소스의 코드화
  • STUDY.md: 날짜별 작업과 트러블슈팅 기록

이 글에서는 Terraform 코드화를 중심으로 정리하고, 마지막에 시리즈 전체를 마무리합니다.


왜 코드화했나

3편에서 ECR 리포지토리 이름이 buildspec과 한 글자 어긋나서 Build-Image 단계가 실패한 일이 있었습니다. 콘솔 수동 구축에서는 같은 이름을 사람이 두 곳에 따로 입력하기 때문에 이런 오타를 시스템이 잡아 주지 못합니다.

Terraform에서는 이름이 variables.tf 한 곳에만 있고, 참조하는 쪽은 리소스의 속성을 직접 가리킵니다. codebuild.tf에서 ml-build 프로젝트에 ECR 리포지토리 이름을 넘기는 부분입니다.

1
2
3
4
environment_variable {
  name  = "ECR_REPO"
  value = aws_ecr_repository.ml_training.name
}

buildspec은 환경변수 ECR_REPO를 읽고, 그 값은 Terraform이 만든 리소스의 실제 이름에서 나옵니다. 이름을 두 번 입력할 일이 없으니 불일치가 구조적으로 발생하지 않습니다. ml-deploy 프로젝트의 SAGEMAKER_EXECUTION_ROLE_ARN도 마찬가지로, 콘솔에서는 ARN을 복사해 붙여 넣었지만 코드에서는 aws_iam_role.sagemaker_execution.arn을 참조합니다.


파일 7개의 역할

파일정의하는 것
main.tfECR 리포지토리(최근 20개 이미지만 유지하는 lifecycle 정책 포함), ML 산출물 S3 버킷, CodePipeline 아티팩트 S3 버킷. 버킷 두 개 모두 public access block 적용
iam.tfSageMaker 실행 역할, CodeBuild 서비스 역할 3개(test, build, deploy 단계별 분리), CodePipeline 서비스 역할과 정책
codebuild.tfCodeBuild 프로젝트 3개. ml-test, ml-build(privileged 모드), ml-deploy
codepipeline.tfCodeConnections 연결과 CodePipeline 본체. Source, Test, Build-Image, Approve, Deploy 5개 스테이지
variables.tf리전(ap-northeast-2), 소스 저장소, 트리거 브랜치(main), 버킷 이름, ECR 리포지토리 이름
outputs.tfECR 리포지토리 URL, SageMaker 실행 역할 ARN(pipeline.py의 –role-arn에 넘길 값), 파이프라인 이름, 연결 승인 안내 문구
versions.tfTerraform 1.5 이상, AWS provider 5.x 버전 고정과 리전 설정

IAM 역할은 단계별로 분리해서 각 단계가 필요한 권한만 갖게 했습니다. ECR push 권한은 build 역할에만, SageMaker와 S3 권한은 deploy 역할에만 붙습니다. 콘솔에서 하나씩 권한을 보강하며 겪었던 AccessDenied들이 이 경계의 실증이었습니다.


콘솔 구성과 달라진 점

수동 구축을 그대로 옮기지 않고 세 가지를 바꿨습니다.

  1. 과금 게이트를 수동 승인 스테이지로: 콘솔에서는 Build-Image와 Deploy 사이 transition을 Disable해서 SageMaker 학습 시작을 사람이 결정하게 했는데, 코드에서는 같은 목적을 Manual Approval 액션으로 명시했습니다. 운영 안정화 후 완전 자동화하려면 이 스테이지만 제거하면 됩니다.
1
2
3
4
5
6
7
8
9
10
stage {
  name = "Approve"
  action {
    name     = "ApproveTraining"
    category = "Approval"
    owner    = "AWS"
    provider = "Manual"
    version  = "1"
  }
}
  1. 파이프라인 역할 권한을 처음부터 ml-* 전체로: 콘솔 마법사가 만드는 서비스 역할은 생성 시점의 리소스에만 권한이 발급되어, 나중에 추가한 스테이지가 codebuild:StartBuild 거부로 실패했습니다(3편의 첫 번째 트러블슈팅). 코드에서는 project/ml-* 패턴으로 처음부터 허용해 그 사고를 차단합니다.

  2. CodeConnections도 리소스로 선언: 다만 apply 직후에는 PENDING 상태로 생성되고, 콘솔에서 한 번 GitHub 앱 승인을 해야 Available이 됩니다. OAuth 승인은 사람 개입이 필요해서 IaC로 자동화할 수 없는 유일한 지점이고, outputs.tf에 이 안내를 출력으로 넣어 두었습니다.


terraform validate 문법 검증

brew로 terraform을 설치하고 문법 검증을 돌렸습니다.

1
2
3
4
brew install awscli
brew tap hashicorp/tap && brew install hashicorp/tap/terraform
cd ml-cicd/infra
terraform init -backend=false && terraform validate   # 자격증명 없이 문법 검증

-backend=false는 state 저장소 연결 없이 provider 플러그인만 받아 오는 옵션이라, AWS 자격증명 없이도 리소스 정의의 문법과 참조 관계를 검증할 수 있습니다.

실제 terraform apply는 하지 않았습니다. AWS 리소스는 이미 콘솔로 구축되어 동작 중이었고, 이 코드는 그 수동 구성을 코드로 옮긴 스켈레톤입니다. state도 로컬 기준으로 두었고, 팀 운영이라면 S3 backend와 DynamoDB 잠금으로 교체해야 합니다.


최종 커밋

지금까지와 같은 GitHub Flow로 마지막 커밋을 병합했습니다.

1
2
3
4
5
6
git switch main && git pull
git switch -c feature/docs
git add .
git commit -m "docs: 설계 문서 3종, CDC 로컬 데모, infra Terraform 스켈레톤 추가"
git push -u origin feature/docs
# → GitHub 웹에서 PR 생성 → Merge → git switch main && git pull

main은 항상 동작하는 상태로 유지한다는 전제를 마지막 커밋까지 그대로 지켰습니다. main 병합이 곧 파이프라인 트리거이고, Deploy는 게이트에서 대기하는 구조입니다.


종료 후 정리

로컬은 두 명령으로 정리했습니다.

1
2
cd cdc && docker compose down -v      # CDC 컨테이너와 데이터 정리
docker image prune                    # 안 쓰는 이미지 정리

AWS 리소스는 2편에서 확인한 대로 존재만으로는 사실상 0원이라 그대로 두었습니다. 과금은 서버가 떠 있는 시간 기준이고, IAM 역할이나 빈 버킷, CodeBuild 프로젝트 같은 설정류 리소스는 실행하지 않으면 비용이 없습니다. 완전히 정리하려면 파이프라인, CodeBuild 프로젝트, ECR 리포지토리, S3 버킷, IAM 역할을 삭제하면 됩니다.


시리즈를 마치며

2026-07-23부터 2026-07-26까지 나흘 동안 ML 코드 작성, 로컬 검증, AWS CI/CD 구축, CDC 파이프라인 구축, 문서화와 코드화를 진행했습니다. 각 편에서 하나씩 배운 것을 표로 남깁니다.

배운 것
1편joblib 모델은 저장한 환경과 같은 환경에서 읽어야 한다. 평가 스텝을 학습과 같은 커스텀 이미지로 바꿔 해결했다
2편과금은 API 호출 횟수가 아니라 서버가 떠 있는 시간 기준이다. 설정류 리소스는 존재만으로는 0원이다
3편파이프라인 서비스 역할의 권한은 생성 시점의 리소스에만 발급된다. 나중에 추가한 스테이지는 권한을 따로 열어 줘야 한다
이번 편리소스 이름은 IaC로 코드와 함께 관리한다. 이름을 한 곳에 두고 참조하면 불일치가 발생하지 않는다

CDC 파이프라인의 스냅샷과 스키마 변경 실험은 4편에 있습니다. CI/CD와 AWS 개념 자체가 처음이라면 CI/CD 기초 시리즈AWS 해부 시리즈를 먼저 읽는 편이 이 시리즈를 따라오기 수월합니다. 여기까지 읽어 주셔서 감사합니다.

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.