Terraform으로 Kubernetes YAML 파일을 코드로 관리하기

SeungHyuk Shin·2025년 10월 30일

Terraform으로 Kubernetes YAML 관리 자동화하기

들어가며

여러분의 팀은 몇 개의 환경에 동일한 애플리케이션을 배포하고 있나요?

AS, HN, JP처럼 여러 리전이나 환경에 같은 애플리케이션을 배포하다 보면, 각 환경마다 거의 비슷하지만 조금씩 다른 Kubernetes YAML 파일을 관리하게 됩니다. 처음에는 단순히 파일을 복사하고 일부 값만 수정하면 되기 때문에 큰 문제가 없어 보입니다.

하지만 환경이 늘어나고 설정이 복잡해질수록 수동 YAML 관리는 점점 부담이 됩니다.

저희 GOA 팀도 비슷한 문제를 겪었습니다. POS Admin 백엔드 서비스를 AS, HN 등 여러 리전에 배포하면서 수십 개의 YAML 파일을 직접 관리해야 했습니다. 하나의 공통 설정을 바꾸려면 모든 환경의 YAML을 일일이 수정해야 했고, 이 과정에서 오타나 누락으로 인한 배포 오류도 발생했습니다.

이 글에서는 Terraform을 사용해 Kubernetes 리소스를 코드로 관리하고, 멀티 환경 YAML 생성을 자동화한 경험을 공유합니다.


문제 상황

기존 디렉토리 구조

기존에는 환경별로 Deployment, Service, Ingress, VIP Service YAML을 각각 관리했습니다.

k8s/prod/
├── as-deployment.yaml
├── as-service.yaml
├── as-ingress.yaml
├── as-vip.yaml
├── hn-deployment.yaml
├── hn-service.yaml
├── hn-ingress.yaml
└── hn-vip.yaml

기존 방식의 한계

1. 중복 코드 증가

각 환경별 YAML은 대부분 동일한 구조를 가지고 있었습니다.

차이가 나는 부분은 보통 다음 정도였습니다.

  • 애플리케이션 이름
  • nodeSelector
  • replicas
  • ingressClassName
  • serviceName
  • 일부 환경별 변수

하지만 파일은 환경마다 따로 존재했기 때문에, 같은 설정이 여러 파일에 반복해서 들어갔습니다.

2. 일관성 관리의 어려움

APM 설정, 리소스 제한, Java 옵션, 포트 설정처럼 모든 환경에 동일하게 적용되어야 하는 설정을 변경하려면 모든 YAML 파일을 수정해야 했습니다.

예를 들어 JAVA_TOOL_OPTIONS 값을 변경해야 한다면 AS, HN, JP 등 모든 환경의 Deployment YAML을 열어 수정해야 했습니다.

이런 방식은 시간이 오래 걸릴 뿐 아니라, 특정 환경만 수정이 누락될 가능성도 높았습니다.

3. 확장성 부족

새로운 환경을 추가하려면 기존 환경의 YAML 파일을 복사한 뒤, 필요한 값을 직접 바꿔야 했습니다.

예를 들어 JP 환경을 추가한다면 최소한 다음 작업이 필요했습니다.

cp as-deployment.yaml jp-deployment.yaml
cp as-service.yaml jp-service.yaml
cp as-ingress.yaml jp-ingress.yaml
cp as-vip.yaml jp-vip.yaml

이후 각 파일을 열어 이름, nodeSelector, ingress class, service name 등을 직접 수정해야 했습니다.

4. 휴먼 에러 발생 가능성

수동으로 많은 파일을 수정하다 보면 다음과 같은 실수가 발생하기 쉽습니다.

  • 서비스 이름 오타
  • ingress class 누락
  • node group 잘못 지정
  • 환경 변수 일부 누락
  • 공통 설정의 일부 환경 미반영

결국 문제의 핵심은 비슷한 YAML을 수동으로 반복 관리하고 있다는 점이었습니다.


해결 방향

저희는 Terraform의 templatefile 함수와 for_each 메타 인자를 활용해 Kubernetes YAML 생성을 자동화했습니다.

이 방식의 목표는 Kubernetes 리소스를 Terraform으로 직접 배포하는 것이 아니라, 환경별 Kubernetes YAML 파일을 일관된 방식으로 생성하는 것이었습니다.

핵심 아이디어

구분설명
템플릿 분리Kubernetes YAML을 .tftpl 템플릿 파일로 분리
변수 추상화환경마다 달라지는 값만 변수로 관리
공통 설정 집중화모든 환경에 공통으로 적용되는 설정은 locals.tf에 정의
선언적 환경 정의locals 블록에서 환경 목록을 Map 형태로 관리
자동 파일 생성Terraform local_file 리소스로 YAML 파일 생성

구현 과정

1단계. 디렉토리 구조 설계

처음에는 모든 Terraform 코드를 한 파일에 넣으려고 했습니다. 하지만 그렇게 하면 코드가 금방 복잡해지고, 다른 애플리케이션에 재사용하기도 어려워집니다.

그래서 애플리케이션 단위로 모듈을 분리했습니다.

k8s/prod/terraform/
├── main.tf
├── modules/
│   └── app/
│       └── pos-admin/
│           ├── variables.tf
│           ├── locals.tf
│           ├── main.tf
│           ├── deployment.yaml.tftpl
│           ├── service.yaml.tftpl
│           ├── ingress.yaml.tftpl
│           └── vip-service.yaml.tftpl
└── result/
    └── pos-admin/
        ├── as-deployment.yaml
        ├── as-service.yaml
        ├── as-ingress.yaml
        ├── as-vip.yaml
        └── ...

구조 설명

파일역할
main.tf환경 목록 정의 및 모듈 호출
variables.tf모듈 입력 변수 정의
locals.tf공통 환경 변수, 리소스 제한, APM 설정 등 정의
main.tf템플릿 렌더링 및 YAML 파일 생성
*.yaml.tftplKubernetes YAML 템플릿
result/Terraform으로 생성된 YAML 파일 저장 위치

모듈을 분리하니 POS Admin 외에 다른 애플리케이션에도 같은 구조를 확장하기 쉬워졌습니다.


2단계. 환경 정의

루트 main.tf에서는 배포 환경을 Map 형태로 정의했습니다.

locals {
  environments = {
    as = {
      node_group = "as-worker"
      replicas   = 5
    }

    hn = {
      node_group = "hn-worker"
      replicas   = 5
    }
  }
}

그리고 for_each를 사용해 환경별로 모듈 인스턴스를 생성했습니다.

module "pos_admin_back" {
  for_each = local.environments

  source = "./modules/app/pos-admin"

  # Common
  app_name  = "pos-admin-${each.key}"
  app_image = "idock.daumkakao.io/goa/store-api:admin-latest"
  replicas  = each.value.replicas

  node_selector = {
    "dkosv3.9rum.cc/node-group" = each.value.node_group
  }

  # Environment
  environment = each.key

  # Outputs
  output_deployment_yaml_path = "${path.module}/result/pos-admin/${each.key}-deployment.yaml"
  output_service_yaml_path    = "${path.module}/result/pos-admin/${each.key}-service.yaml"
  output_ingress_yaml_path    = "${path.module}/result/pos-admin/${each.key}-ingress.yaml"
  output_vip_yaml_path        = "${path.module}/result/pos-admin/${each.key}-vip.yaml"

  # Ingress
  hostname              = "pos-admin.kakaosecure.net"
  tls_secret_name       = "2023-kakaosecure-net"
  frontend_service_name = "pos-admin-frontend"
  backend_service_name  = "pos-admin-${each.key}-service"
  ingress_class_name    = "${each.key}-ingress"
}

핵심 포인트

표현의미
for_each = local.environments환경 Map의 각 항목마다 모듈 생성
each.key환경 이름. 예: as, hn
each.value환경별 설정 값
each.value.node_group각 환경의 node group
each.value.replicas각 환경의 replica 수

이 구조를 사용하면 새로운 환경을 추가할 때 YAML 파일을 복사할 필요가 없습니다.

환경 Map에 항목만 추가하면 됩니다.

locals {
  environments = {
    as = {
      node_group = "as-worker"
      replicas   = 5
    }

    hn = {
      node_group = "hn-worker"
      replicas   = 5
    }

    jp = {
      node_group = "jp-worker"
      replicas   = 3
    }
  }
}

3단계. 공통 설정 집중화

환경마다 동일하게 적용되는 값은 모듈 내부의 locals.tf에 모았습니다.

locals {
  default_env_vars = [
    {
      name  = "SPRING_PROFILES_ACTIVE"
      value = "prod"
    },
    {
      name  = "JAVA_TOOL_OPTIONS"
      value = "-XX:MaxRAMPercentage=85 -Dsun.net.inetaddr.ttl=0 ..."
    }
  ]

  default_resources = {
    limits = {
      cpu    = "2"
      memory = "2Gi"
    }

    requests = {
      cpu    = "500m"
      memory = "512Mi"
    }
  }

  apm_config = {
    image           = "idock.daumkakao.io/kakaoapm/apm-pod-init:1.5.0-rc"
    volume_dir      = "/share-vol/apm"
    application_key = "e557951153b4471abd5c24e9a5418bd3"
  }

  default_ports = [
    {
      containerPort = 8080
    },
    {
      containerPort = 8090
    }
  ]
}

이렇게 분리한 이유

locals.tf에는 모든 환경에 동일하게 적용되는 설정만 둡니다.

예를 들면 다음과 같습니다.

  • 공통 환경 변수
  • JVM 옵션
  • APM 설정
  • 기본 포트
  • CPU, Memory request/limit
  • 공통 label 또는 annotation

반대로 환경마다 달라질 수 있는 값은 variable로 받습니다.

  • replicas
  • node_selector
  • environment
  • ingress_class_name
  • backend_service_name

이렇게 역할을 나누면 공통 설정을 한 번만 수정해도 모든 환경에 동일하게 반영할 수 있습니다.


4단계. Deployment 템플릿 작성

기존 Kubernetes YAML을 .tftpl 템플릿 파일로 변환했습니다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: ${app_name}
  labels:
    name: ${app_name}
spec:
  replicas: ${replicas}
  selector:
    matchLabels:
      app: ${app_name}
  template:
    metadata:
      labels:
        app: ${app_name}
    spec:
      nodeSelector:
%{ for key, value in node_selector ~}
        "${key}": "${value}"
%{ endfor ~}
      containers:
        - name: ${app_name}
          image: ${app_image}
          ports:
%{ for port in ports ~}
            - containerPort: ${port.containerPort}
%{ endfor ~}
          resources:
            limits:
              cpu: ${resources.limits.cpu}
              memory: ${resources.limits.memory}
            requests:
              cpu: ${resources.requests.cpu}
              memory: ${resources.requests.memory}
          env:
%{ for env in env_vars ~}
            - name: ${env.name}
              value: "${env.value}"
%{ endfor ~}

템플릿 문법

문법설명
${app_name}변수 치환
%{ for item in list }리스트 반복
%{ endfor }반복문 종료
%{ if condition }조건부 렌더링
~불필요한 공백 제거

5단계. YAML 생성 로직 작성

모듈 내부 main.tf에서는 templatefilelocal_file 리소스를 사용해 YAML 파일을 생성했습니다.

resource "local_file" "deployment_yaml" {
  content = templatefile("${path.module}/deployment.yaml.tftpl", {
    app_name      = var.app_name
    app_image     = var.app_image
    replicas      = var.replicas
    node_selector = var.node_selector
    env_vars      = local.default_env_vars
    ports         = local.default_ports
    resources     = local.default_resources
    apm_config    = local.apm_config
  })

  filename = var.output_deployment_yaml_path
}

Service YAML도 같은 방식으로 생성합니다.

resource "local_file" "service_yaml" {
  content = templatefile("${path.module}/service.yaml.tftpl", {
    app_name     = var.app_name
    service_name = var.backend_service_name
  })

  filename = var.output_service_yaml_path
}

Ingress YAML은 ingress 관련 변수를 전달합니다.

resource "local_file" "ingress_yaml" {
  content = templatefile("${path.module}/ingress.yaml.tftpl", {
    app_name              = var.app_name
    hostname              = var.hostname
    tls_secret_name       = var.tls_secret_name
    frontend_service_name = var.frontend_service_name
    backend_service_name  = var.backend_service_name
    ingress_class_name    = var.ingress_class_name
  })

  filename = var.output_ingress_yaml_path
}

VIP Service YAML도 별도 파일로 생성합니다.

resource "local_file" "vip_service_yaml" {
  content = templatefile("${path.module}/vip-service.yaml.tftpl", {
    environment = var.environment
  })

  filename = var.output_vip_yaml_path
}

설계 원칙

  • Kubernetes 리소스별로 local_file을 분리합니다.
  • 템플릿에는 필요한 변수만 전달합니다.
  • 환경별 차이는 루트 main.tf에서 관리합니다.
  • 공통 설정은 모듈 내부 locals.tf에서 관리합니다.

이렇게 하면 템플릿과 변수의 결합도를 낮출 수 있습니다.


실전 문제 해결 사례

문제 1. 무한 재귀 참조

에러

Error: Failed to remove local module cache
...pos_admin_back.pos_admin_back.pos_admin_back...

원인

모듈의 source 경로가 자기 자신을 참조하고 있었습니다.

source = "../base" # 잘못된 예시

해결

올바른 모듈 경로를 지정했습니다.

source = "./modules/app/pos-admin"

교훈

Terraform 모듈 경로는 반드시 현재 작업 디렉토리 기준으로 확인해야 합니다.

특히 상대 경로를 사용할 때는 pwd를 확인하고, 모듈이 자기 자신을 참조하지 않는지 주의해야 합니다.


문제 2. 변수 참조 오류

에러

Error: Invalid reference
backend_service_name = "${app_name}-service"

원인

모듈 호출부에서 모듈 내부 변수를 직접 참조하려고 했습니다.

backend_service_name = "${app_name}-service"

Terraform에서는 모듈 외부에서 모듈 내부 변수를 직접 참조할 수 없습니다.

해결

루트 모듈에서 같은 규칙으로 값을 직접 구성했습니다.

backend_service_name = "pos-admin-${each.key}-service"

교훈

Terraform의 변수 스코프를 명확히 이해해야 합니다.

모듈 내부 변수는 모듈 외부에서 사용할 수 없고, 필요한 값은 모듈 호출 시 명시적으로 전달해야 합니다.


문제 3. 템플릿 문법 오타

에러

Error: Missing newline after argument
type = map(string)ㅍㅁ

원인

Terraform 파일에 한글 오타가 들어갔습니다.

type = map(string)ㅍㅁ

해결

불필요한 문자를 제거했습니다.

type = map(string)

교훈

Terraform 파일은 작은 문법 오류에도 실행이 중단됩니다.

다음과 같은 방법으로 예방할 수 있습니다.

  • IDE 문법 검사 기능 사용
  • terraform fmt 실행
  • terraform validate 실행
  • 코드 리뷰 시 특수문자나 한글 오타 확인

문제 4. 중복 변수 정의

에러

Error: Missing required argument
The argument "output_yaml_path" is required

원인

output_yaml_pathoutput_deployment_yaml_path처럼 비슷한 변수가 중복으로 정의되어 있었습니다.

결과적으로 모듈에서 요구하는 변수명과 호출부에서 전달하는 변수명이 달라졌습니다.

해결

출력 파일 경로 변수명을 리소스별로 명확하게 통일했습니다.

variable "output_deployment_yaml_path" {}
variable "output_service_yaml_path" {}
variable "output_ingress_yaml_path" {}
variable "output_vip_yaml_path" {}

교훈

변수명은 초기에 컨벤션을 정하고 일관되게 유지하는 것이 중요합니다.

특히 출력 경로처럼 비슷한 변수가 많은 경우에는 구체적인 이름을 사용하는 편이 좋습니다.


결과

Before: 수동 YAML 관리

JP 환경을 추가하려면 기존 파일을 복사하고 각 파일을 직접 수정해야 했습니다.

cp as-deployment.yaml jp-deployment.yaml
vim jp-deployment.yaml

cp as-service.yaml jp-service.yaml
vim jp-service.yaml

cp as-ingress.yaml jp-ingress.yaml
vim jp-ingress.yaml

cp as-vip.yaml jp-vip.yaml
vim jp-vip.yaml

이 과정에서 이름, node group, ingress class, service name 등을 모두 직접 수정해야 했습니다.


After: Terraform으로 자동 생성

이제는 환경 Map에 JP 항목만 추가하면 됩니다.

locals {
  environments = {
    as = {
      node_group = "as-worker"
      replicas   = 5
    }

    hn = {
      node_group = "hn-worker"
      replicas   = 5
    }

    jp = {
      node_group = "jp-worker"
      replicas   = 3
    }
  }
}

이후 Terraform을 실행하면 환경별 YAML이 자동으로 생성됩니다.

terraform init
terraform plan
terraform apply

개선 효과

항목BeforeAfter개선 효과
새 환경 추가 시간약 30분약 2분약 93% 감소
공통 설정 변경 시간약 20분약 5분약 75% 감소
휴먼 에러 가능성높음낮음크게 감소
코드 중복도높음낮음중복 제거
유지보수성낮음높음개선

정성적 효과

  • 모든 환경이 동일한 템플릿에서 생성되어 일관성을 유지할 수 있습니다.
  • Git을 통해 인프라 설정 변경 이력을 추적할 수 있습니다.
  • YAML 변경도 코드 리뷰 대상이 되어 배포 전 검증이 쉬워졌습니다.
  • 새로운 환경을 추가하는 작업이 단순해졌습니다.
  • Terraform 코드 자체가 인프라 문서 역할을 하게 되었습니다.

베스트 프랙티스

1. 모듈은 애플리케이션 단위로 분리하기

좋은 예시는 다음과 같습니다.

modules/
├── pos-admin/
├── delivery-api/
└── store-api/

반대로 하나의 모듈에 모든 애플리케이션을 넣으면 재사용성과 가독성이 떨어집니다.

modules/
└── all-apps/

모듈은 가능한 한 단일 책임을 가지는 것이 좋습니다.


2. 재사용 가능한 변수명 사용하기

특정 애플리케이션에 종속된 변수명보다는 일반적인 변수명을 사용하는 것이 좋습니다.

variable "app_name" {}
variable "replicas" {}
variable "node_selector" {}

반대로 다음과 같은 이름은 재사용성을 떨어뜨립니다.

variable "pos_admin_name" {}
variable "pos_admin_replicas" {}

3. 출력 파일 경로 변수는 명확하게 작성하기

출력 파일이 여러 개라면 변수명을 구체적으로 작성하는 것이 좋습니다.

output_deployment_yaml_path
output_service_yaml_path
output_ingress_yaml_path
output_vip_yaml_path

다음처럼 모호한 이름은 나중에 혼란을 만들 수 있습니다.

output_yaml_path

4. locals와 variables의 역할 구분하기

locals에는 모든 환경에 공통으로 적용되는 설정을 둡니다.

locals {
  apm_config        = { ... }
  default_resources = { ... }
  default_env_vars  = [ ... ]
}

variables에는 환경별로 달라질 수 있는 값을 둡니다.

variable "replicas" {}
variable "node_selector" {}
variable "environment" {}

이 기준을 정해두면 코드 구조가 훨씬 명확해집니다.


5. 템플릿에는 조건부 렌더링을 활용하기

항상 값이 존재한다고 가정하고 YAML을 렌더링하면 불필요한 필드가 생성될 수 있습니다.

%{ if node_selector != null ~}
nodeSelector:
%{ for key, value in node_selector ~}
  ${key}: ${value}
%{ endfor ~}
%{ endif ~}

이런 방식으로 조건부 렌더링을 사용하면 값이 없을 때 불필요한 YAML 블록이 생성되는 것을 방지할 수 있습니다.


추가 개선 아이디어

1. 환경별 tfvars 분리

환경 설정이 더 복잡해진다면 tfvars 파일로 분리할 수 있습니다.

environments/
├── prod.tfvars
├── staging.tfvars
└── dev.tfvars

예시는 다음과 같습니다.

# prod.tfvars
environments = {
  as = {
    replicas = 10
  }

  hn = {
    replicas = 10
  }
}
# dev.tfvars
environments = {
  as = {
    replicas = 1
  }

  hn = {
    replicas = 1
  }
}

실행 시에는 다음처럼 파일을 지정할 수 있습니다.

terraform apply -var-file=environments/prod.tfvars

2. Validation 추가

잘못된 값이 들어오는 것을 막기 위해 변수에 validation을 추가할 수 있습니다.

variable "replicas" {
  type        = number
  description = "Number of replicas"

  validation {
    condition     = var.replicas > 0 && var.replicas < 100
    error_message = "Replicas must be between 1 and 99."
  }
}

환경 이름도 제한할 수 있습니다.

variable "environment" {
  type        = string
  description = "Environment name"

  validation {
    condition     = contains(["as", "hn", "jp"], var.environment)
    error_message = "Environment must be one of: as, hn, jp."
  }
}

3. CI/CD 통합

Terraform 실행을 GitHub Actions나 사내 CI에 연결하면 YAML 생성 과정을 자동화할 수 있습니다.

name: Generate K8s YAML

on:
  push:
    paths:
      - "k8s/prod/terraform/**"

jobs:
  terraform:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - uses: hashicorp/setup-terraform@v3

      - run: terraform init

      - run: terraform plan

      - run: terraform apply -auto-approve

      - uses: actions/upload-artifact@v4
        with:
          name: k8s-yaml
          path: result/

4. GitOps와 연계

현재 방식은 YAML을 생성하는 단계까지 자동화한 것입니다.

실제 배포까지 자동화하려면 다음 방식도 고려할 수 있습니다.

  • Argo CD와 연동
  • 생성된 YAML을 GitOps Repository에 반영
  • CI에서 Terraform 실행 후 Pull Request 생성
  • Terraform Kubernetes Provider 사용

다만 운영 환경에서는 단순히 자동 배포하기보다, 리뷰와 승인 단계를 어떻게 둘 것인지도 함께 고민해야 합니다.


주의사항과 한계

1. Terraform이 kubectl을 대체하는 것은 아니다

이 방식은 Kubernetes YAML을 생성하는 자동화입니다.

즉, Terraform을 실행한다고 해서 리소스가 Kubernetes 클러스터에 바로 배포되는 것은 아닙니다.

terraform apply
kubectl apply -f result/

직접 배포까지 Terraform으로 관리하고 싶다면 Terraform Kubernetes Provider를 사용할 수 있습니다.

하지만 기존 운영 방식이 YAML 기반이라면, 우선 YAML 생성 자동화부터 도입하는 것도 충분히 현실적인 접근입니다.


2. State 파일 관리가 필요하다

Terraform을 사용하면 state 파일이 생성됩니다.

.terraform/
.terraform.lock.hcl
terraform.tfstate
terraform.tfstate.backup

일반적으로 다음 파일들은 Git에 올리지 않는 것이 좋습니다.

.terraform/
terraform.tfstate*
result/

팀 단위로 협업한다면 S3, GCS, Terraform Cloud 같은 원격 Backend를 사용하는 것이 좋습니다.

원격 Backend를 사용하면 다음 장점이 있습니다.

  • State 파일 중앙 관리
  • State Lock을 통한 동시 실행 방지
  • 팀 협업 안정성 향상

3. 학습 곡선이 있다

Terraform과 HCL 문법에 익숙하지 않다면 처음에는 다소 어렵게 느껴질 수 있습니다.

추천 학습 순서는 다음과 같습니다.

  1. Terraform 기본 개념
  2. variable, local, resource, output
  3. 모듈 시스템
  4. for_each
  5. templatefile
  6. State 관리
  7. Remote Backend

4. 모든 상황에 필요한 방식은 아니다

단일 환경만 존재하거나 YAML 변경이 거의 없다면 오히려 오버엔지니어링일 수 있습니다.

이 방식은 다음 상황에서 특히 효과적입니다.

  • 3개 이상의 유사한 환경이 존재하는 경우
  • 공통 설정이 자주 변경되는 경우
  • YAML 중복이 많은 경우
  • 팀원 여러 명이 같은 인프라 설정을 관리하는 경우
  • 신규 환경 추가가 반복적으로 발생하는 경우

반대로 다음 상황에서는 도입 효과가 크지 않을 수 있습니다.

  • 단일 환경만 운영하는 경우
  • 설정 변경이 거의 없는 경우
  • YAML 파일 수가 매우 적은 경우

마무리

Terraform을 사용한 Kubernetes YAML 관리는 단순한 자동화 작업을 넘어, 인프라 설정을 코드로 다루는 경험이었습니다.

가장 큰 변화는 반복 작업이 줄어든 것이었습니다. 이전에는 환경을 추가하거나 공통 설정을 바꿀 때 여러 YAML 파일을 직접 수정해야 했지만, 이제는 환경 정의와 공통 설정을 한 곳에서 관리할 수 있게 되었습니다.

핵심 교훈

1. 중복 제거는 유지보수성으로 이어진다

비슷한 YAML을 여러 개 관리하는 방식은 시간이 지날수록 부담이 커집니다.

중복을 제거하고 템플릿화하면 변경 지점이 줄어들고, 운영 안정성도 높아집니다.

2. 선언적 관리가 운영 부담을 줄인다

환경별 차이를 Map으로 선언하고, Terraform이 반복 생성을 담당하게 하면 작업 방식이 단순해집니다.

중요한 것은 “무엇을 만들 것인가”를 코드에 선언하고, 반복 작업은 도구에 맡기는 것입니다.

3. 모듈화는 확장성을 만든다

처음부터 완벽한 구조를 만들기는 어렵습니다.

하지만 애플리케이션 단위로 모듈을 나누면, 이후 다른 서비스에도 같은 패턴을 적용하기 쉬워집니다.

4. 작은 개선부터 시작하는 것이 좋다

처음부터 Terraform Cloud, GitOps, CI/CD까지 모두 도입하려고 하면 부담이 큽니다.

먼저 중복 YAML을 템플릿화하고, 환경별 YAML을 자동 생성하는 것부터 시작해도 충분히 큰 효과를 볼 수 있습니다.


다음 단계

앞으로는 다음 개선을 진행해볼 수 있습니다.

  • Terraform Cloud 또는 원격 Backend 도입
  • 환경별 tfvars 파일 분리
  • CI/CD 파이프라인 통합
  • Argo CD와 연계한 GitOps 구성
  • 더 많은 애플리케이션에 모듈 적용
  • Terraform validation 추가
  • PR 기반 YAML 변경 리뷰 자동화

참고 자료

  • Terraform 공식 문서
  • Terraform templatefile 함수
  • Terraform for_each 메타 인자
  • Terraform local_file Provider
  • Kubernetes 공식 문서

0개의 댓글