메인 콘텐츠로 건너뛰기
page thumbnail

Terraform으로 AWS OpenSearch 클러스터 및 Bedrock 연동 자동화 방법

설탕사과
설탕사과
조회수 126
요약

핵심 요약

Terraform와 OpenSearch Provider를 이용해 AWS OpenSearch Service 클러스터, 사용자, 인덱스, Bedrock 연동까지 한 번에 관리하는 방법을 정리한 내용이다. VPC 제약, 커스텀 도메인, 내부 사용자·IAM 역할 매핑, 인덱스 스키마 설계 같은 실전에서 바로 부딪히는 포인트들을 중심으로 이해하면 좋다.

프로젝트 전체 그림

이 글의 목표는 "코드만으로" OpenSearch 환경 전체를 만드는 것이다. 구체적으로는 AWS OpenSearch Managed Service 클러스터, Dev/Staging/Prod 인덱스, 내부 사용자와 역할, IAM 기반 접근 제어, AWS Bedrock과의 연동에 필요한 설정을 Terraform으로 정의한다.

Bedrock이 현재 VPC 내부의 Managed OpenSearch를 공식 지원하지 않는다는 제약 때문에, 클러스터를 퍼블릭 도메인으로 생성해야 한다는 점이 핵심 조건이다. 또한 Terraform의 Bedrock Knowledge Base 리소스가 아직 OPENSEARCH_MANAGED를 지원하지 않으므로, Bedrock 쪽은 일부를 수동으로 구성하고 나중에 import하는 전략을 사용한다.

Terraform 기본 구조와 환경 분리

프로젝트는 하나의 terraform 디렉터리 아래에 공통 코드와 환경별 변수 파일을 두는 구조를 사용한다. providers.tf, variables.tf, data.tf, locals.tf, outputs.tf, versions.tf 등은 공통 설정을 담고, envs/prod/prod.tfvars 처럼 환경별 .tfvars 파일로 값만 바꿔 끼운다.

Makefile을 사용해 init/plan/apply 시 환경별 tfvars를 쉽게 지정할 수 있게 해 두는 것도 실무에서 매우 편하다. 이 구조를 모듈화(예: modules/opensearch)까지 하면 재사용성이 올라가지만, 우선은 단일 코드베이스 + tfvars로 MVP를 구성한다는 접근이다.

클러스터 설계: 인스턴스, 노드 수, 파라미터

OpenSearch 클러스터는 Anton Babenko의 terraform-aws-opensearch 모듈을 사용해 구성한다. 변수 cluser_options(오타지만 그대로 사용)에 인스턴스 타입, 노드 수, 디스크 크기, 엔진 버전, 자동 업데이트 여부 등을 묶어 전달한다.

예시 구성은 다음과 같은 성격을 가진다.

  • t3.small.search, 3노드: 작은 프로젝트, 약 2GiB 데이터 기준으로 충분한 스펙

  • 각 노드 50GiB EBS, gp3

  • 엔진 버전은 OpenSearch_2.19 (문서 확인 후 최신 버전 선택)

  • 세 노드 구성으로 클러스터 안정성을 확보하고, 추후 필요하면 전용 클러스터 매니저 노드를 고려할 수 있다.

t3 계열은 비용은 저렴하지만 Auto-tune 미지원 등 제약이 있으며, 공식적으로는 프로덕션에 권장되지 않는다는 점을 인지해야 한다. 운영 환경에서는 지원되는 C 계열이나 M 계열로 전환하는 계획을 가져가는 것이 좋다.

세분화된 접근 제어와 루트 사용자 비밀번호 관리

클러스터는 Fine-grained Access Control(FGAC)과 내부 사용자 데이터베이스를 활성화하여 구성한다. 이를 통해 OpenSearch Dashboards에 기본 루트 계정(os_root)으로 로그인하고, 이후 추가 사용자와 역할을 세밀하게 제어할 수 있다.

마스터 사용자 비밀번호는 Terraform random_password로 생성한 뒤, AWS Systems Manager Parameter Store의 SecureString으로 저장한다. 값 변경에 따른 매번의 diff를 피하기 위해 lifecycle.ignore_changes로 password 값 변경을 무시하도록 설정하는 것이 실전 팁이다.

이렇게 해 두면:

  • 코드에는 실제 비밀번호가 남지 않고

  • 애플리케이션이나 운영자가 SSM에서 안전하게 읽어 사용할 수 있으며

  • 나중에 비밀번호를 교체해도 Terraform이 리소스를 재생성하려 들지 않는다.

VPC 대신 퍼블릭 도메인: Bedrock 제약 이해하기

원래 의도는 VPC 내부 프라이빗 서브넷에 클러스터를 배치하고, 보안 그룹과 VPC 엔드포인트로 내부에서만 접근하게 만드는 것이었다. 하지만 Amazon Bedrock Knowledge Base가 현재 VPC 보호된 Managed OpenSearch를 지원하지 않기 때문에, 이 방식은 사용할 수 없다.

Bedrock의 요구사항은 "클러스터가 퍼블릭 네트워크 뒤에 있어야 한다"는 것이다. 그래서 다음과 같은 타협을 한다.

  • OpenSearch는 퍼블릭 도메인으로 생성

  • 도메인 액세스 정책으로 IP 제한 또는 IAM 기반 접근을 최대한 좁힘

  • VPC 보안 그룹 대신, IAM Role + OpenSearch 역할 매핑으로 보안을 강화

장기적으로 Bedrock이 VPC의 Managed OpenSearch를 지원한다면, 새 도메인을 만들어 옮겨야 한다는 점도 함께 고려해야 한다. 이미 생성된 도메인을 VPC 모드로 전환하는 것은 지원되지 않는다.

커스텀 도메인과 ACM/Route53 연동

운영 환경에서 OpenSearch 기본 엔드포인트를 그대로 사용하는 것보다, 사람이 읽기 쉬운 커스텀 도메인을 붙이는 것이 관리에 유리하다. locals에서 opensearch.prod.example.co 같은 도메인 이름을 정의하고, 이를 기준으로 ACM 인증서와 Route53 레코드를 설정한다.

구체적으로는:

  • terraform-aws-modules/acm 모듈로 DNS 검증 방식의 ACM 인증서 생성

  • 모듈의 출력 acm_certificate_arn을 OpenSearch 도메인의 domain_endpoint_options.custom_endpoint_certificate_arn에 연결

  • Route53에 CNAME 레코드를 생성하여 커스텀 도메인을 OpenSearch 기본 엔드포인트로 포인팅

중요한 함정 하나는, Terraform OpenSearch Provider에서는 이 커스텀 도메인(FQDN)을 url로 써야 한다는 것이다. 기본 도메인(endpoint)을 url로 넣으면 403 Forbidden이 나오는 문제가 있어서, 실제 작업에서는 https://opensearch.example.co 와 같은 커스텀 URL을 사용해야 한다.

OpenSearch Provider와 403 에러의 원인

opensearch provider 설정에서 url에 module.opensearch.domain_endpoint (기본 엔드포인트)를 넣었을 때 사용자 생성 중 403 Forbidden이 발생했다. 이는 OpenSearch 보안 설정과 엔드포인트/도메인 매칭 이슈로 인해 발생하는 케이스로, 커스텀 도메인을 통해 접속하는 것이 안정적으로 동작했다.

실무에서 기억해야 할 포인트는 다음과 같다.

  • provider "opensearch"의 url은 custom endpoint FQDN을 사용한다.

  • username/password는 FGAC에서 설정한 마스터 계정(os_root, SSM에 저장된 비밀번호)을 사용한다.

  • healthcheck 옵션을 false로 두어 초기 연결에 쓸데없는 검사로 인한 실패를 줄일 수 있다.

이 설정이 맞으면, opensearch_user, opensearch_role, opensearch_index 같은 리소스가 정상적으로 생성된다.

내부 사용자, 역할, 비밀번호 자동 생성

애플리케이션(예: Kubernetes 백엔드 API)이 OS 인덱스를 직접 사용할 경우, 단순하고 분리된 내부 계정을 환경별로 제공하는 패턴이 유용하다. 여기서는 dev, staging, prod 세 환경 각각에 대해 사용자, 역할, 비밀번호를 모두 Terraform에서 루프를 돌며 생성한다.

구성 요소는 다음과 같이 나뉜다.

  • random_password + aws_ssm_parameter: 각 환경의 앱 계정(os_kraken_dev 등) 비밀번호 생성 및 저장

  • opensearch_user: os_kraken_dev, os_kraken_staging, os_kraken_prod 같은 내부 사용자 생성

  • opensearch_role: 환경별 인덱스(kraken-kb-index-dev 등)에 대한 접근 권한을 가진 역할 생성

cluster_permissions에는 Bedrock이 요구하는 indices:data/read/msearch 등 클러스터 전체 인덱스 관련 권한도 포함된다. index_permissions에는 각 환경별 인덱스 패턴과 허용 액션을 정의해 최소 권한으로 접근하도록 설계한다.

나중에 이 계정 대신 IAM Role을 사용하게 되더라도, 개발 초기에는 이 패턴이 디버깅과 실험에 매우 편리하다.

IAM 사용자와 OpenSearch 역할 매핑

운영자나 백엔드 팀원이 직접 OpenSearch Dashboards에 접속해 로그·인덱스·권한을 확인해야 할 수 있다. 이때 각자 로컬 계정을 만들기보다, 기존 IAM User를 OpenSearch 역할에 매핑하는 방식이 깔끔하다.

절차는 다음과 같다.

첫째, variables.tf에 backend_team_users_arns 리스트를 정의하고, prod.tfvars에 팀원의 IAM User ARN을 나열한다. 둘째, opensearch_roles_mapping 리소스로 OpenSearch의 기존 역할(all_access 등)에 이 ARN 리스트를 사용자로 연결한다.

중요한 점은, IAM User는 "백엔드 역할"이 아니라 "사용자 목록"에 넣어야 한다는 것이다. 추가 마스터 사용자 설정 문서에서도 IAM User를 마스터 사용자로 추가할 때 Users에 등록하도록 설명하고 있다.

IAM 측에서는 각 사용자에게 AmazonOpenSearchServiceFullAccess 같은 관리형 정책을 부여하거나, 이를 포함한 그룹에 넣어 OpenSearch API 접근을 허용한다. MVP 단계에서는 관리형 정책을 그대로 쓰되, 이후에는 도메인별 최소 권한 커스텀 정책을 만들어 교체하는 것이 좋다.

Bedrock용 IAM Role과 OpenSearch 역할 매핑

Bedrock Knowledge Base가 OpenSearch를 백엔드로 사용하려면, Bedrock가 Assume할 수 있는 IAM Role과, 그 Role에 대응하는 OpenSearch Role이 필요하다. 여기서도 dev/staging/prod를 루프로 돌며 환경별 구성을 만든다.

우선 IAM 쪽에서:

  • aws_iam_role.knowledge_base_role: 서비스 주체 bedrock.amazonaws.com, SourceAccount/SourceArn 조건으로 Bedrock KB에만 AssumeRole 허용

  • aws_iam_policy.knowledge_base_opensearch_policy: es:* 를 해당 도메인 ARN 및 하위 리소스에 허용

  • aws_iam_role_policy_attachment: 위 정책을 Role에 연결

그 다음 OpenSearch 쪽에서:

  • opensearch_role.os_bedrock_roles: 각 환경별 인덱스에 대한 index_permissions와 필수 cluster_permissions 정의

  • locals.knowledge_base_role_arns: 환경별 IAM Role ARN을 맵 형태로 정리

  • opensearch_roles_mapping.os_bedrock_role_mappings: OpenSearch Role과 IAM Role ARN을 backend_roles로 매핑

이 매핑을 통해 Bedrock가 Assume한 IAM Role이 OpenSearch에서는 특정 인덱스에 대한 권한을 가진 주체로 인식된다. 권한 부족 시 "no permissions for indices:data/read/msearch"와 같은 에러가 발생하므로, 필요한 cluster_permissions를 빠뜨리지 않는 것이 중요하다.

인덱스 설계: KNN 벡터와 Bedrock 메타데이터

Knowledge Base용 인덱스는 단순한 텍스트 인덱스가 아니라 벡터 검색을 위한 구조를 가져야 한다. Terraform opensearch_index 리소스를 사용하면서 mappings에 JSON 템플릿을 넣어 인덱스를 정의한다.

핵심 필드 구성은 대략 다음과 같다.

  • bedrock-knowledge-base-default-vector:

    • type: knn_vector

    • dimension: 1024

    • method: hnsw + faiss 엔진, l2 거리, m/ef_construction 파라미터 설정

  • AMAZON_BEDROCK_METADATA: 텍스트지만 index=false로 검색 대상은 아니고 메타데이터 저장용

  • AMAZON_BEDROCK_TEXT_CHUNK: 실제 검색 대상이 되는 텍스트 조각, type=text, index=true

또한 Bedrock가 새로운 메타데이터 필드를 자동으로 생성하기 때문에, Terraform 입장에서는 mappings가 계속 변하는 "드리프트"처럼 보인다. 이를 막기 위해 lifecycle.ignore_changes = [mappings]를 지정해, 초기 생성 후의 매핑 변경은 무시하도록 설정하는 것이 실용적인 해결책이다.

환경별 인덱스 이름을 kraken-kb-index-dev, -staging, -prod처럼 구분해 두면, 각 환경을 독립적으로 운영하고 마이그레이션도 유연하게 할 수 있다.

인사이트

이 구성에서 가장 중요한 포인트는 "모든 것을 코드로 관리하되, AWS와 OpenSearch의 현실적인 제약을 인정하는 것"이다. Bedrock의 VPC 미지원, Terraform 리소스의 미완성 상태, OpenSearch 보안 모델 등은 우리가 이상적으로 VPC 내부 완전 폐쇄형을 만들고 싶어도 당장 그렇게 못 하게 만든다.

실무에서 적용할 때는 다음을 권장한다.

  • 초기에라도 Terraform 모듈/locals/변수 구조를 잘 잡아두면, 나중에 스펙 변경과 환경 추가가 훨씬 쉽다.

  • 비밀번호, 토큰 등은 항상 SSM Parameter Store나 Secrets Manager를 활용해 Terraform 코드에 노출되지 않게 관리한다.

  • OpenSearch Provider의 url, Bedrock가 바라보는 도메인, IAM Role 매핑 관계를 그림으로 그려서 팀에 공유하면 사고를 줄일 수 있다.

  • 운영 단계에서 모니터링(CloudWatch 로그, 지연·에러 지표, 디스크 사용량)을 빠르게 붙여, "이미 한 번 죽어본" 경험을 반복하지 않도록 한다.

이 글의 내용을 따라가며 자신의 계정과 네이밍, 보안 요구사항에 맞게 조금씩 수정해 보면, "OpenSearch + Bedrock + Terraform" 스택을 실전에서 다루는 감각을 빠르게 익힐 수 있을 것이다.

출처 및 참고 : Terraform: AWS OpenSearch 서비스 클러스터 및 사용자 생성 | Arseny Zinchenko(setevoy) | ITNEXT --- Terraform: creating an AWS OpenSearch Service cluster and users | by Arseny Zinchenko (setevoy) | ITNEXT

운영 단계에서의 모니터링·백업 전략

운영 환경에서 OpenSearch + Bedrock 구성을 안정적으로 유지하려면, 인프라 코드만큼이나 모니터링·백업 전략을 함께 설계해야 한다.

우선 모니터링은 크게 두 축으로 나눌 수 있다. OpenSearch 자체의 상태(클러스터 헬스, 샤드 상태, JVM 메모리, 디스크 사용률)와, AWS 리소스 관점에서의 메트릭(도메인 CPU, 검색/색인 지연, 에러율)이다. OpenSearch 도메인 메트릭은 CloudWatch로 자동 수집되므로, terraform-aws-cloudwatch-alarm 모듈 등을 사용해 디스크 사용률 80% 이상, 클러스터 상태 red/yellow 지속 등 기준으로 경보를 설정해 두는 것이 좋다.

백업은 스냅샷 리포지터리를 기반으로 주기적인 스냅샷을 남기는 방식으로 구성한다. AWS Managed OpenSearch에서는 자동 스냅샷 기능이 제공되지만, 장기 보관이나 특정 시점 복원 요구가 있다면 별도의 S3 스냅샷 리포지터리를 만들고, 수동·스케줄 기반 스냅샷 정책을 함께 운영하는 것이 안전하다. 다만 스냅샷 리포지터리와 스냅샷 정책은 현재 Terraform OpenSearch Provider에서 직접 지원이 제한적일 수 있어, 초기에는 콘솔/스크립트로 구성한 후 추후 지원이 확장되면 코드화하는 전략을 취할 수 있다.

Bedrock Knowledge Base 측면에서는, KB 자체의 메트릭(쿼리 실패율, 레이턴시, 토큰 사용량)을 별도의 대시보드로 모니터링하면서, OpenSearch 지표와 함께 보는 습관을 들이는 것이 좋다. 예를 들어 Bedrock KB의 검색 지연이 급증했을 때, 동시에 OpenSearch의 CPU나 디스크 I/O가 치솟았는지 한 눈에 파악할 수 있도록 구성하면 원인 분석 속도가 크게 빨라진다.

Terraform 상태 관리와 팀 협업 패턴

OpenSearch + Bedrock 인프라는 여러 환경과 리소스가 얽혀 있기 때문에, Terraform 상태(state) 관리 전략이 중요하다. 기본 원칙은 환경별로 분리된 state 파일을 유지하고, 원격 백엔드(예: S3 + DynamoDB locking)를 사용하는 것이다. 이를 통해 여러 팀원이 동시에 작업하더라도 state 충돌을 방지하고, 변경 이력 롤백도 용이해진다.

실무에서는 다음과 같은 패턴이 많이 사용된다. 하나의 리포지터리 아래에 envs/dev, envs/staging, envs/prod 별로 backend 설정과 tfvars를 두고, Makefile이나 스크립트에서 terraform init 시 backend-config를 환경별로 주입한다. 이렇게 하면 코드 구조는 공통을 유지하면서도, 상태와 실제 리소스는 완전히 분리된다. 특히 OpenSearch 도메인은 환경별로 이름·도메인·스펙이 다를 수 있으므로, state 혼선을 절대 허용하지 않는 것이 중요하다.

협업 측면에서는, plan 결과를 PR에 첨부하여 코드 리뷰 시 리소스 변경 사항을 함께 검토하는 프로세스를 강제하는 것이 좋다. 예를 들어 OpenSearch 도메인 재생성(파괴/재생성)이 발생할 수 있는 변경을 사전에 인지하고, 데이터 백업·다운타임 계획을 논의한 뒤에만 apply가 진행되도록 팀 규칙을 정해두면 사고를 크게 줄일 수 있다.

스펙 변경과 마이그레이션 전략

OpenSearch는 한 번 배포한 뒤에도 인스턴스 타입, 디스크 크기, 노드 수 등의 요구사항이 바뀌기 쉽다. 그러나 모든 속성이 안전하게 인플레이스 변경 가능한 것은 아니며, 일부 변경은 도메인 재생성을 유발할 수 있다. Terraform에서는 이런 변경이 plan 단계에서 드러나지만, 실제 운영 데이터가 있는 도메인이라면 자동 재생성은 매우 위험하다.

실무적인 전략은 다음과 같다. 먼저, 변경이 재생성을 유발하는지 항상 plan에서 확인하고, 재생성이 필요한 경우에는 "새 도메인 생성 → 데이터 마이그레이션 → 트래픽 전환 → 기존 도메인 제거"의 롤링 전략을 택한다. 데이터 마이그레이션은 스냅샷/리스토어, logstash/reindex API, 임시 파이프라인 등 다양한 방식이 있을 수 있으며, Bedrock KB 인덱스의 경우 재색인(문서 재업로드) 비용과 시간을 고려해 전략을 결정해야 한다.

Dev/Staging 환경에서는 공격적으로 스펙을 조정하며 경험을 쌓고, Prod에서는 충분히 검증된 변경만 반영하는 것도 좋은 패턴이다. 이때 Terraform 코드에서 locals나 변수에 "현재 프로덕션용 스펙"을 명시적으로 적어두고, 변경 시 PR 템플릿에 "Prod 스펙 변경"을 강조하는 등 프로세스적으로도 사고를 줄이는 장치를 넣을 수 있다.

보안 심화: 네트워크·인증·감사 로그 관점

Bedrock 제약 때문에 퍼블릭 도메인으로 OpenSearch를 열어야 하지만, 그렇다고 네트워크 보안이 느슨해져서는 안 된다. 기본 도메인 액세스 정책에서 허용 IP 범위를 최소화하고, 가능한 한 IAM 기반 접근과 FGAC(내부 사용자·역할)를 조합해 "다중 방어" 구조를 유지해야 한다. 특히 Dashboards 접근은 회사 VPN 또는 특정 사무실 IP로 제한하고, 애플리케이션·Bedrock 접근은 IAM Role + OpenSearch 역할 매핑으로 제어하는 식으로 층을 나누는 것이 좋다.

인증 측면에서는, 마스터 계정(os_root) 사용을 실제 운영에서 최소화하고, 운영자별·애플리케이션별 전용 계정을 만들어 역할 기반 권한을 부여하는 것이 중요하다. 마스터 계정은 비밀번호를 가장 강력한 정책으로 관리하고, 비상 상황(예: 잘못된 역할 설정으로 모두 잠긴 경우)에서만 사용하는 "브레이크 글래스" 계정처럼 취급하는 것이 이상적이다.

감사 로그(아udit logging)는 실제 사고 분석과 포렌식에서 핵심 역할을 한다. OpenSearch 보안 플러그인의 감사 로그 기능을 활성화하면, 누가 언제 어떤 인덱스에 어떤 종류의 요청을 했는지 추적할 수 있다. 이 로그를 별도 로그 인덱스나 CloudWatch Logs로 보관하고, 민감한 이벤트(예: 인덱스 삭제 시도, 권한 없는 액세스 반복)에 대해 알람을 붙여두면, 침해 징후를 조기에 포착하는 데 도움이 된다.

트러블슈팅 팁: 자주 만나는 오류와 대처 방법

OpenSearch + Terraform + Bedrock 조합에서는 특정 패턴의 오류가 반복적으로 나타난다. 이들을 미리 알고 있으면 문제 해결 시간을 크게 줄일 수 있다.

가장 흔한 것은 권한 관련 에러다. 예를 들어 "no permissions for indices:data/read/msearch" 같은 메시지는 Bedrock 또는 내부 애플리케이션이 필요한 index_permissions 또는 cluster_permissions를 갖지 못했을 때 발생한다. 이 경우, OpenSearch 역할 정의에서 해당 인덱스 패턴과 액션이 제대로 포함되어 있는지, 그리고 roles_mapping에서 IAM Role 또는 내부 사용자가 정확히 매핑되어 있는지 두 단계 모두를 확인해야 한다.

또 다른 자주 보는 문제는 인덱스 매핑 변경과 관련된 Terraform 드리프트다. Bedrock가 새로운 메타데이터 필드를 추가하면서 인덱스 매핑이 변경되면, Terraform plan에서 매번 diff가 발생하고, 심하면 apply 시 에러가 날 수 있다. 이를 예방하기 위해 lifecycle.ignore_changes = [mappings]를 설정해 둔 것은 이미 언급된 대로 실용적인 해결이다. 인덱스 구조의 "진짜 변경"이 필요한 경우에는, 새로운 인덱스를 정의하고 reindex를 수행한 뒤, 기존 인덱스를 폐기하는 패턴을 사용하는 것이 안전하다.

마지막으로, OpenSearch Provider 초기 연결 오류(예: 403, TLS/도메인 mismatch)는 대부분 URL이나 인증 정보 설정 문제인 경우가 많다. 커스텀 도메인(FQDN) 사용, healthcheck 비활성화, FGAC 마스터 계정 자격 증명 재확인 등의 순서로 점검하면 대부분의 연결 이슈를 빠르게 해결할 수 있다.

Terraform로 AWS OpenSearch Service 클러스터와 사용자 구성하기

이 노트는 요약·비평·학습 목적으로 작성되었습니다. 저작권 문의가 있으시면 에서 알려주세요.