Terraform이란

Terraform은 HashiCorp에서 만든 오픈 소스 코드형 인프라(IaC) 툴입니다. Terraform을 사용하면 개발자가 사람이 읽을 수 있는 구성 파일을 작성하여 가상 머신 및 클러스터와 같은 온프레미스 및 클라우드 인프라 구성 요소를 프로비저닝, 업데이트 및 삭제할 수 있습니다.(IBM)

  • workflow

    terraform repo에 DeepLX Lambda proxy 인프라를 추가하고, Terraform state backend 준비부터 build -> test -> apply -> verify -> destroy까지 실제로 끝까지 실행해 본 작업이다.

왜 이 작업을 시작했는지

비어 있던 terraform에 단순 예제 수준이 아니라, 실제로 배포 가능한 기준 구성이 필요했다. 목표는 ALB + Lambda + artifact bucket + Terraform state backend를 한 번에 설명할 수 있는 형태를 만드는 것이었다.
또 하나의 목적은, “Terraform으로 리소스를 만들었다”가 아니라 “어떻게 실패했고, 어떤 제약 때문에 설계를 조정했는지”까지 남겨서 포트폴리오와 면접 답변에 재사용 가능한 기록으로 만드는 것이었다.

먼저 개념부터 정리한 부분

처음부터 코드보다 운영 제약을 먼저 봤다. 이번 작업에서 먼저 정리한 개념은 아래였다.

  • Terraform S3 backend와 DynamoDB lock의 역할 분리
  • ALB가 Lambda target group과 연결되는 방식
  • Lambda를 ALB 뒤에 둘 때 health check 제약
  • artifact bucket과 state bucket이 서로 다른 책임을 가진다는 점
  • VPC를 만들더라도 이번 실습에서는 HTTP only로 단순화하고, HTTPS와 custom domain은 의도적으로 제외한다는 점

특히 이번 작업은 애플리케이션 코드보다도 “배포 단위가 어디서부터 어디까지인가”를 명확히 잡는 게 중요했다.
backend.hcl은 state 저장 위치, terraform.tfvars는 실제 인프라 입력값으로 분리했고, 둘 다 로컬 전용 파일로 관리했다.

참고한 자료

이번 작업은 업스트림 구현과 AWS 제약을 같이 읽어야 했다.

  • 업스트림 인프라: OrigamiDream/deeplx-lambda-proxy/infra
  • AWS Lambda VPC / internet access 문서
  • AWS ALB Lambda target 문서
  • S3 bucket naming rules 문서
  • Terraform S3 backend 문서
  • 내부 문서:
    • docs/deeplx-lambda-proxy-apply-plan.md
    • docs/aws-manual-inputs.md
    • docs/terraform-state-bucket-setup.md
    • docs/deeplx-proxy-runbook.md

업스트림은 전체 구조를 잡는 기준으로 썼고, AWS 문서는 실패 원인을 좁히는 데 필요했다. 특히 ALB health check와 S3 권한 문제는 문서를 다시 확인하지 않으면 감으로 넘기기 쉬운 부분이었다.

직접 해본 내용

작업은 두 단계로 진행했다. 먼저 Terraform 뼈대를 만들고, 그다음 실제 앱과 배포 모듈을 채웠다.

1차로는 infra/envs/prod 아래에 backend, provider, version, root module 구조를 만들고 예시 입력 파일을 추가했다.

  • infra/envs/prod/backend.hcl.example
  • infra/envs/prod/terraform.tfvars.example

그다음 실제 동작하는 FastAPI Lambda 앱과 Terraform module을 채웠다.

  • 앱:
    • app/service/main.py
    • app/service/routers.py
    • app/service/models.py
  • 스크립트:
    • scripts/build-lambda.sh
    • scripts/local-run.sh
  • 인프라:
    • infra/modules/deeplx_proxy/alb.tf
    • infra/modules/deeplx_proxy/lambda.tf
    • infra/modules/deeplx_proxy/network.tf
    • infra/modules/deeplx_proxy/s3.tf
    • infra/modules/deeplx_proxy/dns.tf
  • 검증:
    • tests/test_app.py
    • .github/workflows/validate.yml

실제 실행 순서는 아래로 고정했다.

PYTHON_BIN=python3.13 bash scripts/build-lambda.sh
PYTHONPATH=app pytest -q
terraform -chdir=infra/envs/prod init -backend-config=backend.hcl
terraform -chdir=infra/envs/prod validate
terraform -chdir=infra/envs/prod plan
terraform -chdir=infra/envs/prod apply -auto-approve

배포 후에는 ALB endpoint에 대해 바로 확인했다.

  • /v0/health -> {"status":"ok","function_index":0}
  • /v1/health -> {"status":"ok","function_index":1}
  • /v0/commit -> 실제 proxy 응답 확인

이번 실습에서 사용한 주요 리소스 이름도 명확히 남겼다.

  • state bucket: juwon-terraform-state-455021421504
  • lock table: terraform-locks
  • artifact bucket: juwon-deeplx-proxy-artifacts-455021421504
  • ALB: deeplx-proxy-prod-alb
  • Lambda: deeplx-proxy-prod-0, deeplx-proxy-prod-1
  • Lambda layer: deeplx-proxy-prod-deps-73c634b1

막혔던 부분과 트러블슈팅

가장 먼저 막힌 건 AWS credential이었다. aws sts get-caller-identityInvalidClientTokenId로 실패했다. Terraform 이전에 인증 계층부터 깨져 있었던 셈이다.

그다음은 backend bootstrap 권한 문제였다. state bucket을 만들거나 DynamoDB lock table을 조회하는 단계에서 AccessDenied가 났다. 원인은 tf-ecs-user에 backend bootstrap용 권한이 없었던 것이었다.

세 번째는 artifact bucket 권한이었다. Terraform apply 도중 head-bucket403 Forbidden으로 실패했고, 이어서 PutBucketVersioning, PutBucketPublicAccessBlockAccessDenied가 났다. 처음에는 state bucket 권한만 열어두면 될 거라고 생각했는데, artifact bucket은 별도 S3 권한 셋이 필요했다.

가장 기술적으로 중요한 문제는 ALB target group health check였다. terraform apply 시점에 아래 제약에 걸렸다.

ValidationError: Health check interval must be greater than the timeout.

여기에 더해 Lambda target group에서 health_check.protocol 설정은 앞으로 에러가 될 수 있다는 경고도 보였다. 즉, 단순 문법 문제가 아니라 AWS 리소스 제약과 provider 동작을 같이 맞춰야 하는 상황이었다.

마지막으로 destroy 이후에도 backend가 완전히 비워지지 않았다. Terraform 리소스는 사라졌지만 versioning이 켜진 state bucket은 객체 버전이 남아서 바로 삭제되지 않았고, terraform-locks 테이블도 별도로 정리해야 했다.

해결하거나 이해한 내용

해결은 단계별로 했다. 먼저 AWS CLI credential을 재설정하고 계정 식별부터 다시 확인했다. 그다음 tf-ecs-user에 backend bootstrap용 inline policy를 추가해 state bucket과 lock table을 만들 수 있게 했다.

입력 파일도 정리했다.

  • infra/envs/prod/backend.hcl
  • infra/envs/prod/terraform.tfvars

이 둘을 로컬 전용으로 두고, us-east-1 기준 테스트 값을 반영했다. 이렇게 해두니 state 위치와 실제 리소스 입력을 분리해서 다루기 쉬웠다.

ALB health check는 infra/modules/deeplx_proxy/alb.tf에서 수정했다. 핵심은 AWS 제약에 맞게 interval = 35, timeout = 30으로 조정한 것이었다. 이 변경 후 terraform validate를 다시 돌렸고, 이후 apply가 통과했다.

배포가 끝난 뒤에는 ALB 경유로 실제 엔드포인트를 검증했다.

  • /v0/health
  • /v1/health
  • /v0/commit

특히 /v0/commit에서 아래 형태의 응답을 확인한 점이 중요했다.

{"responses":[{"unique_id":"1","status_code":200,"response":{"code":200,"id":1662450002,"data":"https://linux.do/t/topic/111737","alternatives":[]}}]}

즉, 이번 작업은 Terraform apply 성공에서 끝난 게 아니라, ALB path routing과 Lambda permission 연결, 앱 응답까지 실제 요청으로 확인한 데 의미가 있었다.

마지막에는 아래 순서로 자원을 회수했다.

terraform -chdir=infra/envs/prod destroy -auto-approve

이후 backend bucket의 versioned object를 비우고, terraform-locks를 삭제하고, 임시 IAM 권한까지 회수했다.
최종적으로 deeplx-proxy-prod* 계열 ALB, Lambda, VPC, artifact bucket은 모두 정리된 상태로 남겼다.

이번 작업에서 배운 점

이번 작업에서 가장 크게 배운 점은 Terraform 실습의 경계가 apply가 아니라는 점이다. 실제로는 state backend 준비 -> artifact build -> local test -> apply -> endpoint verify -> destroy -> 권한 회수까지 닫혀야 재현 가능한 실험이 된다.

또 하나는 AWS에서 실패 원인을 좁힐 때, Terraform 코드만 보면 안 된다는 점이다.
이번 이슈들은 대부분 문법 오류가 아니라 AWS 계정 상태, IAM 권한, 리소스 제약에서 왔다. InvalidClientTokenId, 403 Forbidden, ValidationError를 각각 다른 층위의 문제로 보고 분리해서 처리해야 했다.

설계 측면에서는 단순화도 중요했다. 이번에는 HTTPS와 custom domain을 일부러 넣지 않고 HTTP only로 먼저 닫았다. 범위를 줄인 덕분에 ALB-Lambda path routing, backend/state, artifact packaging 같은 핵심 문제에 집중할 수 있었다.

정리

이 작업은 Terraform 문법을 아는지보다, 배포 가능한 구조를 끝까지 운영할 수 있는지를 보여준다.
구체적으로는 다음 역량을 설명하기 좋다.

  • Terraform으로 state backend -> build -> deploy -> verify -> teardown 전체 흐름을 설계하고 실행한 경험
  • ALB Lambda target 구성에서 path 기반 라우팅과 Lambda permission 연결을 맞춘 경험
  • S3 backend, DynamoDB lock, artifact bucket, Lambda layer처럼 운영에 필요한 주변 리소스를 같이 다룬 경험
  • 배포 실패를 로그와 AWS 제약으로 좁히고, alb.tf 설정 변경으로 해결한 트러블슈팅 경험
  • 실습 후 리소스와 임시 권한까지 정리해 재현 가능한 실험으로 마무리한 태도

진행한 작업에 대해서 정리한다면, “Terraform으로 인프라를 만들었다”보다 “어디서 실패했고, 왜 그 실패가 났고, 어떤 기준으로 범위를 줄여 해결했는가”에 초점을 맞춰 이야기할 수 있다. 그 점에서 이번 작업은 단순한 배포 성공 사례보다 운영에 있어 어떤 부분이 필요할지 생각해본 작업이다.