Elasticsearch 입문 37편 IaC. Terraform·CDK·Pulumi·ECK Helm·GitOps 자동화 패턴.
이 글은 Elasticsearch 입문에서 운영까지 시리즈 38편 중 37편이에요. 35편이 AWS 환경의 OpenSearch, 36편이 Elastic 사 직접 매니지드 4가지 라인업 을 봤다면, 37편은 그 클러스터를 어떻게 코드로 정의·재현·관리 할지의 자리예요. 클릭 UI 로 만든 deployment 는 6개월 뒤 누가 어떤 옵션으로 만들었는지 아무도 모르는 사고가 잦아서, 이걸 git repo 한 줄로 추적 가능 하게 바꾸는 작업이 IaC.
이 글은 Terraform AWS provider·elastic provider·AWS CDK·ECK Operator 공식 docs 를 한국어 학습 노트로 풀어쓴 자료예요.
로컬에 LocalStack 또는 작은 Elastic Cloud trial deployment 한 개를 띄워 두고 terraform plan 한 번만 돌려 봐도 본문이 머리에 훨씬 잘 박혀요.
36편까지의 manual operation, 37편의 자동화
36편까지의 흐름은 Console 또는 kubectl 로 클러스터를 손으로 만든다 였어요. AWS OpenSearch Domain 생성 위저드 5분, Elastic Cloud Deployment 폼 5분, ECK 의 Elasticsearch CRD YAML 을 직접 kubectl apply — 셋 다 한 번은 빠른데 두 번째부터 무너지는 자리.
문제가 반복 에서 터져요. 개발·스테이징·프로덕션 세 환경에 같은 클러스터를 만들면, 같다고 믿는 세 클러스터 사이에 Hot 노드 사양·analyzer·alias·ILM 정책 이 미묘하게 어긋나요. 프로덕션에서만 한국어 검색이 깨지는 사고가 여기서 나옴.
해결의 출발은 단순해요. 클러스터·인덱스·ILM·alias·user 같은 모든 것을 코드로 선언 하고, git repo 가 단일 진실의 원천(SSOT, Single Source of Truth) 이 되는 구조. 이 자리에 표준 답이 IaC (Infrastructure as Code) 예요. 37편은 Terraform·AWS CDK·Pulumi·ECK Helm 네 갈래를 각각 어떤 자리에 쓰는지 정리합니다.
왜 IaC 인가 — 네 가지 무기
(1) Drift 방지
Drift = 코드와 실제 인프라가 어긋난 상태. 누군가 콘솔에서 손으로 노드 사양을 올린 뒤로 코드와 운영이 따로 놀기 시작해요. Terraform·CDK 는 plan 명령으로 실제 상태 vs 코드 상태 의 diff 를 보여 줘서, drift 가 생기는 즉시 잡혀요.
(2) 재현성
프로덕션 클러스터를 똑같이 staging 에 복제 하는 일이 terraform apply -var env=staging 한 줄로 끝나요. 사고 재현·성능 테스트·블루그린 배포가 진짜 같은 클러스터 위에서 가능.
(3) 코드 리뷰
인덱스 매핑 변경 또는 ILM 정책 변경 이 git PR 로 들어와요. 두 명 이상이 diff 를 읽고 승인 한 뒤에야 운영에 들어가서, mapping explosion 사고 가 사람이 한 번 더 보는 단계에서 잡혀요.
(4) Multi-env 일관성
dev·staging·prod 가 같은 모듈 + 다른 variable 로 만들어져요. 한국어 analyzer 설정 이 prod 에는 있고 staging 에는 빠지는 일이 구조적으로 막혀요. 8편(Mapping)·11편(Korean Analyzer) 결정이 IaC 코드 한 곳에서 모든 환경에 적용 됩니다.
Terraform — 가장 흔한 첫 선택
Terraform = HashiCorp 가 만든 오픈소스 IaC 도구. .tf 확장자의 HCL (HashiCorp Configuration Language) 로 인프라를 선언하고, plan → apply 두 단계로 실제 클라우드에 반영해요. AWS·GCP·Azure·Elastic Cloud 모두 provider 라는 플러그인으로 같은 문법에서 다룰 수 있어요.
Elasticsearch 자리에서 자주 같이 쓰는 provider 가 두 갈래.
- hashicorp/aws — AWS OpenSearch Service 생성·VPC·IAM·CloudWatch 알람.
- elastic/elasticstack — Elastic Cloud Deployment·인덱스·ILM·user·role·snapshot repository.
Terraform 이 가장 흔한 선택 인 이유 세 가지. 첫째, 멀티 클라우드 지원 — AWS OpenSearch 와 Elastic Cloud Hosted 를 한 코드베이스 에서 다뤄요. 둘째, state 파일 로 실제 상태 를 관리해서 drift 추적이 강해요. 셋째, 모듈·workspace 패턴이 멀티 env 표준 으로 굳어져 있어요.
State 관리가 진짜 운영 자리 의 핵심이에요. terraform.tfstate 파일을 로컬에 두면 팀 협업이 무너지니까, S3 + DynamoDB lock (AWS) 또는 GCS + Cloud Storage lock (GCP) 또는 Terraform Cloud (HashiCorp 호스팅) 셋 중 하나로 remote state 를 잡아 둡니다.
AWS OpenSearch + Terraform — 실제 예제 결
35편 AWS OpenSearch Service 를 Terraform 으로 표현하면 aws_opensearch_domain 리소스 한 개가 중심이에요. VPC·subnet·security group·IAM role·snapshot repository S3 까지 5~7개 리소스 가 한 모듈로 묶여요.
resource "aws_opensearch_domain" "main" {
domain_name = "search-prod"
engine_version = "OpenSearch_2.13"
cluster_config {
instance_type = "r6g.large.search"
instance_count = 3
zone_awareness_enabled = true
zone_awareness_config { availability_zone_count = 3 }
}
ebs_options {
ebs_enabled = true
volume_type = "gp3"
volume_size = 100
}
encrypt_at_rest { enabled = true }
node_to_node_encryption { enabled = true }
vpc_options {
subnet_ids = var.subnet_ids
security_group_ids = [aws_security_group.search.id]
}
advanced_security_options {
enabled = true
internal_user_database_enabled = true
master_user_options {
master_user_name = var.master_user
master_user_password = var.master_password
}
}
tags = { Environment = var.env, Service = "search" }
}
이 코드 한 덩어리가 3-AZ · 3-node · 100GB gp3 · VPC 내부 · 암호화 켬 · master user 를 한 번에 잡아요. terraform apply 후 30~40분쯤 기다리면 Console 에서 손으로 5분 클릭한 것 과 동일한 결과가 재현 가능한 코드 로 남아요.
IAM 정책·CloudWatch slow log·VPC endpoint 까지 같은 모듈에 묶어 두면, 36편 cross-region 사고 결을 코드 리뷰에서 region mismatch 로 미리 잡을 수 있어요.
Elastic Cloud + Terraform — elasticstack provider
Elastic Cloud Hosted·Serverless 를 Terraform 으로 다루는 자리는 elastic/elasticstack provider 가 표준이에요. 이 provider 가 Elastic Cloud API 와 Elasticsearch API 둘 다 들고 있어서, Deployment 생성 부터 인덱스 매핑·ILM·alias· user 까지 동일한 .tf 파일 에서 잡혀요.
terraform {
required_providers {
ec = { source = "elastic/ec" }
elasticstack = { source = "elastic/elasticstack" }
}
}
resource "ec_deployment" "main" {
name = "search-prod"
region = "aws-ap-northeast-2"
version = "8.13.0"
deployment_template_id = "aws-storage-optimized-v5"
elasticsearch = {
hot = { autoscaling = { max_size = "120g" } }
warm = { autoscaling = { max_size = "240g" } }
cold = { autoscaling = { max_size = "480g" } }
}
kibana = {}
}
resource "elasticstack_elasticsearch_index" "products" {
name = "products-v1"
mappings = jsonencode({
properties = {
name = { type = "text", analyzer = "nori" }
price = { type = "integer" }
created_at = { type = "date" }
}
})
number_of_shards = 3
number_of_replicas = 1
}
ec_deployment 가 36편 Hosted 라인을, elasticstack_elasticsearch_index 가 5편(Index 관리)·8편(Mapping) 결정을 코드로 박아요. ILM 정책·snapshot repository·ingest pipeline·search application 까지 전부 리소스 로 있어서, 클러스터 안의 거의 모든 객체 를 git repo 에서 관리할 수 있어요.
AWS CDK·CloudFormation — AWS 환경 1급
AWS CDK (Cloud Development Kit) 는 TypeScript·Python·Java·C#·Go 같은 진짜 프로그래밍 언어로 인프라를 정의 하는 도구예요. 내부적으로는 CloudFormation 템플릿으로 컴파일되고, AWS 가 그 템플릿을 실행해 인프라를 만들어요. AWS 전용 이지만 AWS 환경 1급 시민 자리에서 결정적 강점.
CDK 가 Terraform 대비 가지는 무기 두 가지. 첫째, 프로그래밍 언어 풀 표현력 — for 문·조건문·재사용 함수가 자연스러워서 복잡한 인프라가 더 간결. 둘째, AWS Construct Library — L2 Construct 라 부르는 높은 수준 추상화 가 합리적 기본값 을 박아 줘서 수십 줄 → 5줄 로 줄어드는 자리가 많아요.
import { Domain, EngineVersion } from "aws-cdk-lib/aws-opensearchservice";
import { InstanceType } from "aws-cdk-lib/aws-ec2";
new Domain(this, "SearchDomain", {
version: EngineVersion.OPENSEARCH_2_13,
capacity: {
dataNodes: 3,
dataNodeInstanceType: "r6g.large.search",
multiAzWithStandbyEnabled: true,
},
ebs: { volumeSize: 100, volumeType: EbsDeviceVolumeType.GP3 },
encryptionAtRest: { enabled: true },
nodeToNodeEncryption: true,
vpc,
zoneAwareness: { availabilityZoneCount: 3, enabled: true },
});
같은 AWS OpenSearch 3-AZ 클러스터 가 Terraform 30줄 → CDK 12줄로 줄어요. 약점은 AWS 외 자원 (Elastic Cloud 자체·외부 인덱스) 을 다루지 못한다는 점이라, AWS OpenSearch 만 쓰는 한국 회사 에는 강한 답, 멀티 클라우드 에는 Terraform 이 더 어울려요.
CloudFormation 은 CDK 가 컴파일하는 YAML/JSON 템플릿 그 자체예요. CDK 없이 직접 YAML 을 짜는 회사도 있지만, L2 Construct 의 합리적 기본값 을 못 누려서 200줄짜리 템플릿이 흔함. 신규 프로젝트는 CDK 가 표준 으로 굳고 있어요.
Pulumi — 멀티 클라우드 + 다언어 IaC
Pulumi 가 Terraform 의 멀티 클라우드 강점 + CDK 의 프로그래밍 언어 강점 을 한 번에 잡으려는 도구예요. TypeScript·Python·Go·Java·C#·YAML 로 인프라를 짜고, 내부적으로는 Terraform 과 비슷한 provider 모델을 써요.
import * as aws from "@pulumi/aws";
const domain = new aws.opensearch.Domain("search-prod", {
engineVersion: "OpenSearch_2.13",
clusterConfig: {
instanceType: "r6g.large.search",
instanceCount: 3,
zoneAwarenessEnabled: true,
zoneAwarenessConfig: { availabilityZoneCount: 3 },
},
ebsOptions: { ebsEnabled: true, volumeType: "gp3", volumeSize: 100 },
encryptAtRest: { enabled: true },
nodeToNodeEncryption: { enabled: true },
});
문법 결이 CDK 와 거의 같다 고 보면 됩니다. AWS·Elastic Cloud·GCP 어디든 같은 코드 결로 다루고, state 는 Pulumi Cloud (또는 자체 호스팅) 에서 관리해요.
한국 시장에서 Pulumi 사용은 아직 적은 편 이에요. 신규 회사는 Terraform 또는 CDK 둘 중 하나로 통일 이 압도적이라 Pulumi 학습 인력 풀이 얇다 는 게 실제 도입의 가장 큰 장벽. 단, AWS·GCP 동시 운영 자리에서 진짜 같은 코드로 다루고 싶으면 후보로 올라와요.
ECK — Elastic Cloud on Kubernetes 의 자리
ECK (Elastic Cloud on Kubernetes) 는 36편에서도 등장했어요. Kubernetes Operator 패턴이라 Elasticsearch·Kibana·APM Server·Beats 를 CRD (Custom Resource Definition) 로 정의하고, Operator 가 Pod·StatefulSet·Service·Secret 같은 K8s 자원을 자동으로 만들어요. K8s 표준 인프라 위에서는 가장 K8s-native 한 선택지.
ECK 의 IaC 두 갈래.
- Helm chart — elastic/eck-operator 와 elastic/eck-stack 두 차트. helm install 한 줄로 Operator + ES + Kibana 까지.
- Kustomize / 직접 YAML — Elasticsearch CRD YAML 을 git repo 에 두고 kubectl apply 또는 ArgoCD 로 동기화.
apiVersion: elasticsearch.k8s.elastic.co/v1
kind: Elasticsearch
metadata:
name: search-prod
spec:
version: 8.13.0
nodeSets:
- name: hot
count: 3
config:
node.roles: ["master", "data_hot", "ingest"]
podTemplate:
spec:
containers:
- name: elasticsearch
resources:
requests: { memory: 8Gi, cpu: 2 }
limits: { memory: 8Gi, cpu: 4 }
volumeClaimTemplates:
- metadata: { name: elasticsearch-data }
spec:
accessModes: ["ReadWriteOnce"]
resources: { requests: { storage: 200Gi } }
storageClassName: gp3
- name: warm
count: 2
config:
node.roles: ["data_warm"]
이 YAML 한 덩어리가 3-hot + 2-warm 노드 + 200GB gp3 PVC 클러스터를 그려요. 36편 Hosted 의 Hot/Warm 4-tier 가 ECK 위에서는 nodeSet 단위 로 자연스럽게 표현돼요.
ECK 자리의 강점이 두 개. 첫째, K8s 표준 인프라 와 같은 도구 (Helm·Kustomize·ArgoCD·Prometheus) 로 운영. 둘째, 온프레미스·멀티 클라우드 동시 표현 이 K8s 자체의 추상화 로 해결. 단, Operator-ES 버전 매트릭스 가 깐깐해서 (36편 사고 4 참조) Operator 부터 업그레이드 → ES 순서가 표준.
GitOps 패턴 — ArgoCD·Flux 로 ES manifest 자동 적용
ECK 까지 잡았으면 그 다음 단계가 GitOps 예요. git repo 의 YAML 변경 을 ArgoCD 또는 Flux 가 감지해서 kubectl apply 를 자동으로 돌려 주는 패턴. 손으로 kubectl apply 하는 자리가 사라져요.
표준 구성은 이렇게 흘러요. infra-repo 라는 git repo 가 ES Elasticsearch CRD·Kibana CRD·ILM 정책·index template 을 모두 담고 있고, ArgoCD Application 한 개가 infra-repo 의 prod/es/ 폴더를 cluster A 의 elastic namespace 에 sync. git PR merge → 5분 안에 클러스터 반영 자동.
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: elasticsearch-prod
spec:
destination:
namespace: elastic
server: https://kubernetes.default.svc
source:
repoURL: https://github.com/myorg/infra-repo
path: prod/es
targetRevision: main
syncPolicy:
automated: { prune: true, selfHeal: true }
selfHeal: true 가 결정적이에요. 누가 콘솔에서 손으로 노드 갯수를 바꾸면 ArgoCD 가 5분 안에 git 상태로 되돌려 놓아요. drift 가 구조적으로 막힌다 는 자리.
ArgoCD 의 App-of-Apps 패턴을 쓰면 Operator + ES + Kibana + index template + ILM 정책 까지 논리적 묶음 을 한 Application 으로 묶을 수 있어요. prod·staging·dev 세 환경이 각자의 Application + 같은 base manifest + 다른 overlay 로 풀려요.
자주 만나는 사고
사고 1 — Terraform state lock 영구화
원인 — terraform apply 도중 네트워크가 끊겨서 DynamoDB lock 이 풀리지 않은 채 남음. 그 뒤로 모든 plan/apply 가 "Error acquiring the state lock" 으로 막혀요.
해결 — terraform force-unlock
사고 2 — CDK Drift 미감지
원인 — CDK 는 내부 CloudFormation 으로 가니까 Console 에서 직접 변경한 사항 이 drift detection 명령을 따로 안 돌리면 안 보여요. Stack update 시 갑자기 사라지는 사고.
해결 — AWS CloudFormation Drift Detection 을 주 1회 자동 으로 돌리고, drift 가 감지되면 Slack·email 알림. DeletionPolicy: Retain 으로 데이터 노드 자체는 보호 도 같이 박아 둡니다.
사고 3 — Pulumi state key 분실
원인 — Pulumi 의 config 파일이 git 에 commit 되지 않았거나, 암호화 키 (passphrase) 가 로컬 환경 변수에만 있고 팀 공유 X. 담당자가 떠나면 클러스터 변경이 불가능 해져요.
해결 — Pulumi Cloud 또는 자체 S3 백엔드 에 state 를 두고, 암호화 키는 AWS KMS·HashiCorp Vault·1Password 같은 팀 공유 secret 저장소 에 두기. Pulumi.
사고 4 — ECK Operator CRD version mismatch
원인 — Operator 를 2.10 → 2.12 로 업그레이드 했는데, CRD 정의 가 같이 업데이트되지 않아서 기존 Elasticsearch 리소스 reconcile 이 실패. 클러스터가 unknown 상태 로 들어가요.
해결 — Operator 업그레이드 절차는 helm upgrade 만으로 끝나는 게 아니에요. kubectl apply -f crds.yaml --server-side 로 CRD 를 먼저 갱신한 뒤 Operator Pod 를 재시작. 공식 docs 의 upgrade matrix 와 CRD apply 단계 를 매번 확인.
사고 5 — Secret 평문 commit
원인 — master_user_password·Elastic Cloud API Key·S3 snapshot 자격증명 을 .tf 파일 또는 values.yaml 에 평문 으로 박아 git push. GitHub 공개 repo 면 몇 분 내 봇이 스캔.
해결 — AWS Secrets Manager·HashiCorp Vault·Sealed Secrets·SOPS 중 하나로 비밀을 분리. Terraform 은 aws_secretsmanager_secret_version data source, CDK 는 SecretValue.secretsManager, ECK 는 Sealed Secret 또는 ExternalSecret. pre-commit hook 으로 git-secrets 도 같이 박아 둠.
사고 6 — terraform destroy 의 데이터 노드 동시 삭제
원인 — staging 클러스터 를 정리하려고 terraform destroy 를 돌렸는데, snapshot 을 안 받은 채 모든 EBS 볼륨이 삭제. 데이터 복구 불가능.
해결 — aws_opensearch_domain 또는 ec_deployment 리소스에 lifecycle { prevent_destroy = true } 를 박아 실수로 지우는 자리 를 봉쇄. 정말 지워야 하면 prevent_destroy 를 먼저 git PR 로 떼고 → snapshot 확인 → destroy 의 3단계.
사고 7 — Index Mapping 변경의 immutability
원인 — elasticstack_elasticsearch_index 의 mappings 를 변경해서 terraform apply — Elasticsearch 가 기존 인덱스 매핑 변경 거의 불가능 이라 replace (= 인덱스 삭제 후 재생성) 로 가서 데이터가 전부 날아감.
해결 — index template + ILM rollover + alias 패턴으로 가요 (5편·6편). 새 매핑은 새 인덱스 (products-v2) 로, alias 만 갈아끼우기. Terraform 은 index 자체보다 index_template 을 관리하는 게 안전.
운영 권장 패턴
(1) 무엇이든 한 도구로 통일
프로덕션 클러스터를 Terraform 으로 만들고 인덱스만 ECK 로 관리 같은 두 도구 혼용 은 책임이 흐려지면서 drift 가 두 배. AWS only → CDK 또는 Terraform 하나. K8s 중심 → ECK Helm 하나. 멀티 클라우드 → Terraform 하나. 처음 결정이 향후 1~2년의 운영 패턴을 굳혀요.
(2) Module / Construct 단위로 환경 분리
dev·staging·prod 가 같은 모듈 + 다른 variable 로 풀려야 환경 간 차이가 명확. prod 만 zone_awareness = true 같은 암묵적 차이 가 코드 안에서 한 줄로 보이게.
(3) State / Drift 자동 점검
주 1회 cron 으로 terraform plan / cdk diff / pulumi preview 를 자동 실행하고, 변화가 있으면 Slack 알림. 콘솔 손작업 흔적이 즉시 가시화 돼요.
(4) Secret 은 IaC 코드 밖에서
평문 비밀 commit 사고의 90% 가 습관 때문에 일어나요. pre-commit hook (git-secrets·trufflehog) 을 모든 IaC repo 에 강제. 비밀은 Secrets Manager / Vault / Sealed Secrets 로만.
(5) Destroy / Replace 보호
aws_opensearch_domain · ec_deployment · Elasticsearch CRD 같은 데이터 컨테이너 리소스 는 prevent_destroy = true · finalizers · pre-delete hook 으로 실수 삭제 방지. snapshot 자동화 와 한 묶음.
(6) GitOps 로 마무리
ECK 쓰면 반드시 ArgoCD/Flux 로 손 kubectl apply 자리를 없애요. Terraform·CDK 도 Terraform Cloud · Atlantis · GitHub Actions 로 PR merge 시 자동 apply 를 묶어 두면 콘솔에서 손으로 하는 사고가 구조적으로 사라져요.
시험 직전 한 번 더 — 압축 노트
- IaC = 인프라를 코드로 선언. Drift 방지·재현·코드 리뷰·multi-env 일관성 4가지 무기.
- Terraform = HashiCorp HCL, 멀티 클라우드, state 파일, AWS·Elastic Cloud 한 코드.
- 두 provider — hashicorp/aws (OpenSearch), elastic/elasticstack (Elastic Cloud + ES 객체).
- State backend = S3 + DynamoDB · GCS · Terraform Cloud. 로컬 state X.
- AWS CDK = TypeScript/Python/Java 로 인프라, CloudFormation 컴파일, AWS 1급, L2 Construct 로 코드 짧음.
- Pulumi = Terraform 의 멀티 클라우드 + CDK 의 다언어, 한국 도입 적음.
- ECK = K8s Operator + CRD, Helm chart 표준, Operator-ES 버전 매트릭스 필수.
- GitOps = ArgoCD/Flux 가 git → kubectl apply 자동, selfHeal: true 로 drift 자동 복원.
- Terraform state lock · CDK drift 미감지 · Pulumi key 분실 · ECK CRD mismatch · Secret 평문 commit · destroy 데이터 삭제 · Mapping immutability 가 7대 사고.
- Mapping 변경 = 인덱스 replace 가 표준이라 index template + alias + rollover 패턴으로 우회.
- Secret 분리 = AWS Secrets Manager · Vault · Sealed Secrets · SOPS.
- prevent_destroy = true = 데이터 컨테이너 리소스 기본값.
- 선택 트리 — AWS only → CDK, K8s 표준 → ECK Helm, 멀티 클라우드·Elastic Cloud → Terraform, AWS+GCP 동시 다언어 → Pulumi.
- GitOps + IaC = git PR 한 곳 이 클러스터 변경 단일 진입점. 콘솔 손작업 사라짐.
시리즈 다른 편
- 이전 글 = 36편 Elastic Cloud — Hosted·Serverless·ECE·ECK
- 다음 글 = 38편 시리즈 마무리 — 결정 트리·체크리스트·자격증
- 5편 = Index 관리 — Create·Settings·Alias·Reindex
- 6편 = ILM — Hot/Warm/Cold/Frozen 4-tier
- 8편 = Mapping Deep — Static·Dynamic·Multi-field·Runtime
- 26편 = Cluster Operations — health·shard allocation·rolling restart
- 28편 = Snapshot — repository·restore·SLM
- 35편 = AWS OpenSearch Service — Domain·Serverless·Provisioned
한 줄 정리 — IaC = 클러스터를 코드로 선언 해 drift·재현·리뷰·multi-env 를 한 번에 잡는 도구. AWS only 는 CDK, K8s 중심은 ECK Helm + ArgoCD, 멀티 클라우드·Elastic Cloud 는 Terraform, 다언어·멀티 클라우드는 Pulumi. Secret 분리 + destroy 보호 + GitOps 셋이 콘솔 손작업 사고 를 구조적으로 막는 표준 패턴.