한 줄 정의
스택 파라미터 는 재사용 가능한 스택의 인스턴스별 차이를 정의하는 값이며, 이 값을 어디에 저장하고 어떻게 스택 도구에 전달하느냐에 따라 6가지 패턴(과 1가지 안티패턴)으로 나뉩니다.
쉽게 말하면
틀 하나로 붕어빵을 굽기로 했다면(재사용 가능한 스택), 남은 문제는 “이번 판은 팥 몇 개, 슈크림 몇 개”라는 주문을 주방에 어떻게 전달하느냐입니다.
굽는 사람이 그때그때 기억해서 외치면(수동 파라미터) 언젠가 반드시 헷갈립니다.
주방 벽에 레시피 종이를 붙여두면(스크립트·구성 파일) 실수는 없지만, 메뉴가 바뀔 때마다 종이를 새로 붙여야 합니다.
중앙 주문 전광판(파라미터 레지스트리)을 두면 모든 지점이 같은 주문을 보지만, 전광판이 꺼지면 아무도 굽지 못합니다.
어느 방식이든 주문은 “팥이냐 슈크림이냐” 수준으로 단순해야 합니다. 주문서가 복잡해지는 순간 같은 틀에서 다른 빵이 나오기 시작합니다.
왜 중요한가?
재사용 가능한 스택 패턴은 “인스턴스 간 차이를 간단한 파라미터로 유지할 수 있다”는 전제 위에 서 있습니다. 파라미터를 관리하고 전달하는 방법이 이 전제의 실행 가능성을 결정합니다.
전달 방법을 잘못 고르면 자동화가 막히거나(수동 입력), 비밀 정보가 코드와 스크립트에 스며들거나, 파이프라인이 단일 장애점이 됩니다.
구성 가능성 자체도 양날의 검입니다. 스택의 구성 가능성이 높아질수록 인스턴스의 작동을 이해하고 테스트하기 어려워지고, 모든 인스턴스에 변경을 신뢰할 수 있게 딜리버리하기가 어려워집니다.
핵심 내용
설계 원칙 — 파라미터를 단순하게 유지한다
인프라를 코드로 정의하는 주요 이유는 시스템을 일관되게 구성하기 위함인데, 구성 가능한 스택 코드는 그 일관성을 흔들 수 있습니다. 그래서 파라미터는 단순하게 유지해야 합니다.
- 문자열·숫자·리스트·키-값 맵 같은 간단한 파라미터 유형 만 사용하고, 복잡한 데이터 구조 전달은 피합니다
- 파라미터 수를 최소화하고 당장 필요한 경우에만 추가합니다 (나중에 필요하면 그때 추가하면 됩니다)
- 파라미터에 조건문을 사용하지 않습니다. 서비스 프로비저닝 여부 같은 불리언 파라미터는 변경이 반영된 인프라에 중대한 차이를 만들어 복잡도를 키웁니다
이 조언을 따르기 어렵다면 스택 코드가 너무 많은 일을 하고 있다는 신호이므로, 리팩터링으로 여러 스택 프로젝트로 분할해야 합니다.
파라미터의 두 가지 쓰임
고유 식별자 생성
동일한 프로젝트로 두 번째 인스턴스를 만들면 고유해야 하는 리소스 ID가 충돌합니다.
> stack up environment=staging --source mystack/src
FAILURE: server 'appserver' already exists in another stack
파라미터를 ID에 포함시켜 해결합니다.
server:
id: appserver-${environment}
subnet_id: appserver-subnet-${environment}
인스턴스별 크기 구성
환경마다 리소스 규모를 다르게 잡습니다. 이 장의 예제 스택은 컨테이너 클러스터를 다음 값으로 구성합니다.
| 스택 인스턴스 | environment | cluster_minimum | cluster_maximum |
|---|---|---|---|
| cluster_test | test | 1 | 1 |
| cluster_staging | staging | 2 | 3 |
| cluster_production | production | 2 | 6 |
파라미터 관리 패턴
핵심 질문은 파라미터값이 어디에 사는가 입니다. 사람의 기억 → 스크립트 → 파일 → 파이프라인 → 중앙 레지스트리 순으로 값의 위치가 코드에서 멀어지며, 각 위치마다 다른 트레이드오프가 생깁니다.
안티패턴 — 수동 스택 파라미터
명령줄에 값을 직접 입력합니다. 도구를 배우거나 테스트할 때는 유용하지만, 실수하기 쉽고 입력할 값을 기억하기 어렵습니다.
여러 명이 하나의 스택에서 작업하면 모두가 인스턴스별 값을 기억할 것이라 기대할 수 없고, CI/CD처럼 인프라 코드를 자동으로 적용하는 환경에는 적합하지 않습니다.
패턴 — 스택 환경 변수
파라미터값을 환경 변수로 설정해 두고, 스택 코드가 직접 읽거나(${ENV("STACK_CLUSTER_MINIMUM")}) 오케스트레이션 스크립트가 읽어 커맨드라인으로 전달합니다.
대부분의 플랫폼과 도구가 환경 변수를 지원하므로 사용하기 쉽지만, 반쪽짜리 패턴입니다. “환경 변수를 누가 설정하느냐”는 결국 이 장의 다른 패턴에 떠넘겨야 합니다.
스택 코드가 환경 변수를 직접 읽으면 스택 코드가 런타임 환경에 너무 밀접하게 결합됩니다. 비밀 정보를 환경 변수에 설정하면 같은 시스템의 다른 프로세스에 노출될 수도 있습니다.
패턴 — 스크립트 파라미터
스택 도구를 실행하는 스크립트에 인스턴스별 값을 하드코딩합니다. 단일 스크립트에 환경별 case 분기를 두거나, 환경마다 별도 스크립트(bin/test.sh, bin/staging.sh, bin/production.sh)를 둡니다.
값이 버전 관리에 기록되므로 각 환경에서 일관되게 사용된다는 확신을 얻고 변경을 추적할 수 있습니다. 자주 변경되지 않는 고정된 환경 집합에 적합합니다.
프로비저닝 스크립트는 시간이 지나며 지저분하게 복잡해지는 경향이 있고, 비밀 정보는 하드코딩할 수 없으므로 스크립트가 비밀 관리자에서 가져오게 합니다.
SSL_CERT_PASSPHRASE=$(some-tool get-secret id="/ssl_cert_passphrase/${ENV}")
패턴 — 스택 구성 파일
환경마다 별도의 파라미터 파일(environments/test.properties 등)을 스택 코드와 함께 버전 관리하고, 실행 시 경로를 전달합니다.
stack up --source ./src --config ./environments/staging.properties
소스 저장소에 커밋되므로 “프로덕션 클러스터 최대 크기가 얼마인가”, “언제, 누가 변경했는가”를 바로 확인할 수 있습니다.
대신 새 인스턴스마다 파일을 추가해야 하므로 임시 환경을 즉시 자동 생성할 수 없고, 구성 변경도 파이프라인 전체 단계를 거쳐야 해서 다운스트림 환경에 적용되기까지 시간이 걸립니다. 환경별 기본값 상속 모델을 만들기 시작하면 복잡하고 혼란스러워집니다.
시스템이 여러 스택으로 구성되면 구성 파일을 “각 스택 프로젝트 안에 환경별로” 둘지 “모든 스택의 구성을 한 곳에” 모을지 정렬 문제가 생기는데, 어느 쪽이든 규모가 커지면 지저분해집니다.
패턴 — 래퍼 스택
인스턴스마다 별도의 인프라 스택 프로젝트(래퍼)를 두고, 모든 인스턴스가 공유하는 스택 코드 모듈을 가져옵니다. 래퍼 프로젝트는 그 인스턴스의 파라미터값만 정의합니다. Terragrunt 가 이 패턴을 구현하는 대표 도구입니다.
스택 도구의 모듈 버전 관리·의존성 관리 기능을 딜리버리 파이프라인에 활용할 수 있고, 별도 스크립트 언어 없이 인프라 정의와 같은 언어로 구성 로직을 작성할 수 있습니다.
대가는 복잡성 계층입니다. 래퍼 스택과 컴포넌트(모듈)라는 두 레벨을 관리해야 하고, 인스턴스마다 코드 프로젝트가 있으므로 사용자 정의 로직이 스며들기 쉽습니다. 래퍼에 파라미터 외의 로직이 들어가는 순간 복사-붙여넣기 환경으로 퇴화합니다. 파라미터가 소스 제어에 있으므로 비밀 정보 관리에도 쓸 수 없습니다.
패턴 — 파이프라인 스택 파라미터
딜리버리 파이프라인의 스테이지 구성에서 인스턴스별 값을 정의합니다. 파이프라인 도구 자체가 코드로 구성된다면 파라미터값도 버전 관리에 저장됩니다.
stage: apply-test-stack
input_artifacts: container_cluster_stack
commands:
unpack ${input_artifacts}
stack up --source ./src environment=test cluster_minimum=1 cluster_maximum=1
stack test environment=test
이미 파이프라인으로 인프라 코드를 적용하는 팀이라면 자연스럽지만, 구성값을 파이프라인에 많이 정의할수록 파이프라인 밖에서 스택 도구를 실행하기 어려워집니다. 파이프라인이 단일 장애점(single point of failure)이 되어, 복구 전까지는 비상 상황에서 환경을 수정·복구·리빌드할 수 없습니다.
그래서 파이프라인 구성은 작고 간단하게 유지하고, 대부분의 로직은 파이프라인에서 호출하는 스크립트에 두는 것이 좋습니다.
CI 서버, 파이프라인, 비밀 정보
해커가 회사 네트워크에 접근할 때 가장 먼저 찾는 것이 CI/CD 서버입니다. 대부분의 CI/CD 도구는 강력한 보안 모델을 제공하지 않으므로, 파이프라인 도구에 접근하거나 도구에서 실행되는 코드를 수정할 수 있는 사람은 저장된 모든 비밀 정보에 접근할 수 있다고 가정해야 합니다. 도구가 비밀을 암호화해도 도구에서 명령을 실행할 수 있으면 복호화가 가능합니다.
패턴 — 스택 파라미터 레지스트리
스택 코드가 아닌 중앙 레지스트리에서 파라미터값을 관리하고, 스택 도구가 인스턴스에 코드를 적용할 때 관련 값을 검색합니다. 스택 도구에는 환경 파라미터 하나만 전달하고, 코드가 이를 키로 사용해 레지스트리를 참조합니다.
cluster:
id: container_cluster-${environment}
minimum: ${get_value("/env/${environment}/cluster/min")}
maximum: ${get_value("/env/${environment}/cluster/max")}
구성과 구현이 분리되어 다른 언어·도구에서도 값을 설정·조회할 수 있고, 스택 프로젝트를 변경하지 않고 인스턴스 구성을 바꿀 수 있습니다. 도구에 구애받지 않는 인프라 구성의 진정한 출처로서 구성 관리 데이터베이스(CMDB) 역할을 하며 감사 보고서 생성에도 유용합니다.
대신 레지스트리가 스택의 의존성이자 잠재적 실패 포인트가 됩니다. 레지스트리를 사용할 수 없으면 복원 시점까지 인프라를 다시 프로비저닝하거나 업데이트하는 것이 불가능할 수 있어, 실패 복구 시나리오 작성이 어려워집니다.
스택 코드가 레지스트리를 직접 참조하면 코드 실행·테스트에 항상 레지스트리가 필요해 무거워집니다. 스크립트가 레지스트리에서 값을 가져와 일반 파라미터로 스택 코드에 전달하면 이 결합을 완화하고, 코드 사용자에게 다른 방식으로 값을 설정할 여지를 줍니다.
구성 레지스트리 구현 옵션
| 옵션 | 예시 | 트레이드오프 |
|---|---|---|
| 인프라 자동화 도구의 레지스트리 | Chef Infra Server, PuppetDB, Ansible Tower, Salt Mine | 툴체인에 포함되어 편리하지만, 다른 도구의 데이터까지 저장하면 해당 도구 체인에 의존성이 생김 |
| 범용 구성 레지스트리 제품 | Zookeeper, etcd, Consul, doozerd | 특정 도구 체인 의존을 피하지만, 데이터 구조를 직접 정의하고 통합 코드를 유지보수하며 배포·운영 업무가 추가됨 |
| 플랫폼 레지스트리 서비스 | AWS SSM Parameter Store | 설치·유지보수가 필요 없지만 클라우드 벤더에 종속됨 |
| DIY 구성 레지스트리 | S3 버킷, 버전 관리 시스템, 웹 서버 + 구성 파일 | 기존 서비스 활용으로 빠르게 구현 가능. 구성을 .deb/.rpm 패키지로 만들어 내부 저장소로 배포하는 변형도 있음 |
‘하나의 레지스트리가 모든 것을 지배한다’는 싱글 레지스트리는 매력적이지만, 크고 이기종(heterogeneous)인 환경에서는 항상 실용적이지 않습니다. 많은 도구가 이미 자체 레지스트리를 갖고 있어 단일화하면 모든 도구 업데이트마다 통합 유지 작업이 생깁니다. 데이터의 실제 출처가 어느 시스템인지 확인하고, 그 서비스에서 직접 가져오는 편이 나을 수 있습니다.
파라미터를 사용한 비밀 정보 처리
비밀 정보는 새 도구를 배우는 학습용 코드에조차 직접 입력해서는 안 됩니다. 개발자가 소스 저장소를 안전한 공간으로 판단해 비밀이 포함된 코드를 올렸다가 기업이 막대한 비용을 부담한 사례가 많습니다.
| 방법 | 내용 |
|---|---|
| 비밀 정보 암호화 | git-crypt, blackbox, sops, transcrypt로 저장소 내 비밀을 암호화. 복호화 키는 저장소 밖에 두어야 함 |
| 비밀 없는 권한 부여 | IAM 프로필을 EC2 인스턴스에 할당하듯, 컴퓨팅 인스턴스 자체를 인증된 것으로 표시해 비밀 자체를 없앰 |
| 실시간 비밀 정보 주입 | 로컬 개발은 버전 관리 밖 로컬 비밀 파일, 무인 에이전트(CI/CD)는 비밀 관리 서비스에서 실행 시점에 가져옴 |
| 일회성 비밀 정보 사용 | 프로비저닝 시 자동 생성해 필요한 코드에만 전달하고 어디에도 저장하지 않음. HashiCorp Vault는 인증마다 새 비밀을 만드는 원타임 방식 지원 |
비밀 없는 권한 부여는 해커의 공격 경로를 옮길 뿐이라는 한계가 있지만(인스턴스에 접근한 사람은 그 권한을 쓸 수 있음), 저장된 비밀은 다른 곳에서도 악용될 수 있으므로 비밀을 완전히 제거하는 편이 일반적으로 좋습니다.
비교 / 트레이드오프
| 패턴 | 값이 사는 곳 | 강점 | 대가 |
|---|---|---|---|
| 수동 스택 파라미터 (안티패턴) | 사람의 기억 | 학습·테스트에 간단 | 실수 잦음, 자동화 불가 |
| 스택 환경 변수 | 실행 환경 | 어디서나 지원 | 설정 주체를 다른 패턴에 의존, 런타임 결합 |
| 스크립트 파라미터 | 실행 스크립트 | 값이 버전 관리됨, 단순 | 스크립트 복잡화, 비밀 하드코딩 불가 |
| 스택 구성 파일 | 커밋된 환경별 파일 | 확인·추적·감사 용이 | 임시 환경 즉시 생성 불가, 파일 정렬 문제 |
| 래퍼 스택 | 인스턴스별 프로젝트 | 도구의 모듈·버전 기능 활용 | 두 레벨 복잡성, 커스텀 로직 유입 위험 |
| 파이프라인 스택 파라미터 | 파이프라인 스테이지 구성 | 기존 파이프라인에 자연스러움 | 파이프라인이 단일 장애점, 밖에서 실행 곤란 |
| 스택 파라미터 레지스트리 | 중앙 레지스트리 | 구성-구현 분리, CMDB·감사 | 레지스트리가 의존성·실패 포인트 |
패턴들은 배타적이지 않고 결합됩니다. 환경 변수 패턴은 값을 설정할 다른 패턴이 필요하고, 스크립트가 레지스트리에서 값을 꺼내 전달하기도 하며, 비밀 정보만 별도 패턴으로 처리하는 조합이 흔합니다.
내 생각
- 실무 기본 조합은 ‘구성 파일 + 레지스트리(비밀만)‘입니다. Terraform의 환경별 tfvars 파일이 스택 구성 파일 패턴이고, 비밀만 SSM Parameter Store나 Vault에서 실행 시점에 주입하는 구성이 가장 흔한 착지점입니다.
- Terragrunt를 쓴다면 래퍼 스택의 퇴화 경고를 기억해야 합니다. 인스턴스별 프로젝트에 파라미터 외의 로직이 들어가기 시작하면, Ch06에서 경고한 복사-붙여넣기 환경의 구성 드리프트가 그대로 재현됩니다.
- CI/CD 서버가 보물창고라는 경고는 과장이 아닙니다. 파이프라인에 비밀을 넣는 순간 “파이프라인 접근 권한 = 전체 비밀 접근 권한”이 됩니다. IAM 역할 같은 비밀 없는 권한 부여를 먼저 검토할 이유입니다.
관련 개념
- Ch06 스택으로 환경 구축하기 — 재사용 가능한 스택 패턴, 이 장의 전제
- Ch05 코드로 인프라 스택 구축하기 — 스택의 정의와 변경의 폭발 반경