[SE100 #058] Infrastructure as Code
소프트웨어 공학 100 주제 시리즈의 58번째 글이다. (카테고리: 형상 관리와 전달)
한 줄 요약
Infrastructure as Code(IaC)는 서버·네트워크·클러스터 같은 인프라를 소스 코드로 정의하고, 그 코드를 일반 소프트웨어와 똑같이 버전 관리·리뷰·테스트·지속적 전달의 대상으로 다루는 접근이다. 핵심은 도구가 아니라 “사람이 서버에 로그인해 손으로 고치지 않는다” 는 규율이다.
왜 필요한가
Terraform 의 선언·상태·계획·드리프트 같은 기본기는 Infrastructure as Code — Terraform에서, Git 을 원하는 상태의 원천으로 삼는 운영은 GitOps 와 ArgoCD에서 다뤘다. 이 글은 도구 아래의 원칙, 즉 왜 인프라를 코드로 다뤄야 하는지와 그때 지켜야 할 공학적 실천에 집중한다.
손으로 관리한 서버는 시간이 지날수록 각자 다른 상태가 된다. 누군가 급하게 커널 파라미터를 바꾸고, 다른 누군가 패키지를 하나 더 설치하고, 그 기록은 아무 데도 없다. 이 서버가 죽으면 똑같이 다시 만들 수 없다. 형상 관리(SE100 #051 에서 다룬다)의 관점에서 보면 이것은 식별도 통제도 기록도 되지 않는 형상 항목이다.
핵심 개념
정의와 실천
Martin Fowler 의 InfrastructureAsCode(2016)는 IaC 를 “컴퓨팅과 네트워크 인프라를 소스 코드로 정의하고, 그것을 다른 소프트웨어 시스템과 똑같이 다루는 접근” 으로 정의한다. 그 코드는 소스 관리에 두어 감사 가능성과 재현 가능한 빌드를 얻고, 테스트 실천과 지속적 전달의 규율 전체를 적용받는다. Fowler 는 이 분야의 핵심 문헌으로 Kief Morris 의 책 Infrastructure as Code(O’Reilly)를 꼽는다. 글이 정리한 실천은 다음과 같다.
| 실천 | 내용 |
|---|---|
| 정의 파일 사용 | 모든 설정은 실행 가능한 정의 파일(셸 스크립트, Ansible 플레이북, Chef 레시피, Puppet 매니페스트 등)로. 누구도 서버에 로그인해 즉석에서 고치지 않는다 |
| 자기 문서화 | 사람이 읽고 수행하는 문서 대신, 더 정확하고 일관되게 실행되는 코드 |
| 모든 것을 버전 관리 | 모든 설정과 변경이 감사용으로 기록됨 |
| 지속적 테스트 | 인프라 코드에도 배포 파이프라인을 두고 지속적 전달 |
| 큰 묶음 대신 작은 변경 | 변경이 클수록 오류가 많고 찾기 어렵다 |
| 서비스의 지속적 가용성 | 블루그린, 병행 변경 같은 기법으로 무중단 갱신 |
눈송이, 불사조, 불변 서버
IaC 의 동기는 Fowler 의 bliki 세 편으로 잘 설명된다.
SnowflakeServer(2012): 오랫동안 손으로 조금씩 고쳐 온 서버는 세상에 하나뿐인 눈송이가 된다. 스키장에는 좋지만 데이터센터에는 나쁘다. 재현하기 어렵고, 디스크 이미지를 떠 두어도 그 안의 어떤 설정이 중요한지 모른다.
PhoenixServer(2012): 처방은 서버를 정기적으로 가상으로 불태우는 것이다. 서버는 재에서 다시 태어나는 불사조처럼, 정의 코드로부터 언제든 새로 만들 수 있어야 한다. 주된 이점은 설정 드리프트(configuration drift)를 막는 것이다.
ImmutableServer(Kief Morris, 2013): 불사조를 끝까지 밀면, 한 번 배포된 서버는 절대 수정하지 않고 새 인스턴스로 교체만 하는 불변 서버가 된다.
가변 인프라(mutable) 불변 인프라(immutable)
┌────────┐ 패치 ┌────────┐ 설정변경 ┌────────┐ 교체 ┌────────┐
│ srv v1 │ ────▶ │ srv v1'│ ────▶ ... │ img v1 │ ──────▶ │ img v2 │
└────────┘ └────────┘ └────────┘ └────────┘
상태 = 초기 + 모든 변경 이력의 합 상태 = 이미지 정의 그 자체
→ 드리프트 누적 → 드리프트 원천 차단, 롤백 = 이전 이미지
컨테이너 이미지와 Kubernetes 파드 교체가 일상이 된 오늘날, 불변 인프라는 애플리케이션 계층에서는 사실상 기본값이다. 하지만 VM, 네트워크 장비, 관리형 DB 설정처럼 여전히 가변으로 다루는 계층이 남아 있고, IaC 의 전장은 주로 그쪽이다.
명령형과 선언형, 그리고 그 사이
Kubernetes Object Management 문서는 객체를 관리하는 세 가지 기법을 비교한다. IaC 전반에도 그대로 적용되는 구분이다.
| 기법 | 예 | 특징 |
|---|---|---|
| 명령형 커맨드 | kubectl create deployment ... |
빠르지만 기록이 남지 않음 |
| 명령형 객체 설정 | kubectl create -f app.yaml, kubectl replace -f |
파일을 Git 에 둘 수 있음. 동작이 단순 |
| 선언형 객체 설정 | kubectl apply -f dir/ |
원하는 상태를 선언, 도구가 차이를 계산. 디렉터리 단위로 동작 |
문서는 선언형 객체 설정의 장점으로, 설정 파일에 반영되지 않은 운영 객체의 직접 변경도 유지된다는 점을 든다. 거꾸로 말하면 그 직접 변경은 코드에 없는 상태로 남는다는 뜻이고, 이것이 드리프트다. 그래서 IaC 운영은 “누가 손으로 바꾼 것” 을 탐지하고 되돌리는 장치(정기 plan, GitOps 의 자동 동기화)를 함께 둔다.
도구 생태계와 라이선스 변화
| 계층 | 대표 도구 |
|---|---|
| 프로비저닝(클라우드 리소스 생성) | Terraform, OpenTofu, Pulumi, CloudFormation |
| 구성 관리(서버 내부 설정) | Ansible, Chef, Puppet, Salt |
| 이미지 굽기 | Packer, Dockerfile |
| 클러스터 내부 상태 | Kubernetes 매니페스트, Helm, Kustomize, Argo CD |
2023년의 변화도 알아 둘 만하다. OpenTofu 선언문에 따르면 Terraform 은 2014년 MPL 2.0 으로 공개되었는데, 2023년 8월 10일 HashiCorp 가 라이선스를 오픈소스가 아닌 Business Source License(BUSL) 1.1 로 바꾸었다. 이에 반발한 커뮤니티가 마지막 MPL 버전을 포크해 Linux Foundation 산하의 OpenTofu로 이어 가고 있다. 도구 선택이 형상 관리의 일부라는 점, 그리고 핵심 인프라 도구의 라이선스도 의존성 위험이라는 점을 보여 준 사건이다.
실무 적용
IaC 파이프라인의 단계
PR 생성
├─ fmt / validate 문법·형식
├─ lint (tflint 등) 흔한 실수
├─ 정책 검사 (OPA/Conftest 등) "퍼블릭 버킷 금지", "태그 필수"
├─ 보안 스캔 취약 설정 탐지
└─ plan → PR 코멘트로 게시 사람이 "무엇이 바뀌는가" 를 리뷰
머지
└─ apply (저장된 plan 그대로) 리뷰한 plan 과 적용 내용 일치 보장
정기 실행
└─ plan (드리프트 탐지) 변경 사항이 있으면 경보
리뷰 대상은 코드 diff 보다 plan 출력이다. 코드 한 줄 변경이 리소스 교체(삭제 후 재생성)를 일으킬 수 있고, 그것은 plan 에서만 보인다. Terraform plan은 계획을 파일로 저장해 나중에 그대로 적용하는 방식을 지원한다.
정책을 코드로 (Rego 예)
# OPA 1.0 (Rego v1) 문법
package terraform.s3
# 퍼블릭 읽기 ACL 을 가진 S3 버킷 생성을 거부한다
deny contains msg if {
rc := input.resource_changes[_]
rc.type == "aws_s3_bucket_acl"
rc.change.after.acl == "public-read"
msg := sprintf("%s: public-read ACL 금지", [rc.address])
}
원칙 체크리스트
[ ] 운영 환경에 사람이 쓰기 권한으로 접근하는 경로가 "비상용" 으로만 존재하고, 사용 시 기록·사후 코드 반영이 강제되는가
[ ] 상태 파일은 원격 저장 + 잠금 + 암호화, 비밀 값은 상태에 남지 않게
[ ] 환경(dev/stg/prod)은 같은 모듈 + 다른 변수, 복사-붙여넣기 금지
[ ] 모듈과 공급자(provider) 버전을 잠금 파일로 고정
[ ] 파괴적 변경(replace/destroy)은 plan 단계에서 별도 승인
[ ] 드리프트 탐지가 정기적으로 돈다
[ ] 전체 환경을 코드로부터 새로 만드는 연습을 해 본 적이 있다
마지막 항목이 가장 중요한 시험이다. 한 번도 처음부터 다시 만들어 본 적이 없는 인프라는, 코드가 있어도 여전히 눈송이일 수 있다.
흔한 오해와 함정
- “Terraform 파일이 있으면 IaC.” 콘솔에서 손으로 바꾸는 일이 계속되면 코드는 현실과 갈라진 문서일 뿐이다.
- “선언형이면 멱등하고 안전하다.” 선언형 도구도 리소스 교체, 순서 의존, 외부 API 의 최종 일관성 때문에 실패하거나 예상 밖 동작을 한다. plan 리뷰가 필요한 이유다.
- 거대한 단일 상태. 모든 인프라를 하나의 상태로 관리하면 plan 이 느려지고, 한 실수의 폭발 반경이 전체가 된다. 수명 주기와 소유 팀 기준으로 나눈다.
- 비밀을 코드에. 저장소에 들어간 비밀은 이력에서 지우기 어렵다. 비밀 저장소 참조만 코드에 둔다.
- 테스트 없는 인프라 코드. 정적 검사, 정책 검사, 임시 환경에 실제로 적용해 보는 통합 테스트까지, 애플리케이션 코드와 같은 수준의 검증이 필요하다.
확인 문제
- Fowler 가 정리한 IaC 실천 중 “정의 파일 사용” 이 금지하는 행동은 무엇이고, 왜 금지하는가?
- 눈송이 서버, 불사조 서버, 불변 서버를 각각 한 문장으로 설명하라.
- Kubernetes 의 세 가지 객체 관리 기법 중 선언형 객체 설정의 장점이 동시에 드리프트의 원인이 되는 이유는?
- IaC 의 PR 리뷰에서 코드 diff 보다 plan 출력이 중요한 이유는?
- Terraform 라이선스 변경 사건이 형상 관리 관점에서 주는 교훈은?
풀이
- 서버에 로그인해 즉석에서 설정을 바꾸는 것. 그런 변경은 기록되지 않아 서버를 재현할 수 없게 만들고(눈송이), 코드와 실제 상태를 갈라놓는다.
- 눈송이: 손으로 오래 고쳐 와서 재현 불가능한 유일한 서버. 불사조: 정의 코드로부터 정기적으로 새로 만들어 드리프트를 없애는 서버. 불변 서버: 배포 후 절대 수정하지 않고 새 인스턴스로 교체만 하는 서버.
- 설정 파일에 반영되지 않은 운영 객체의 직접 변경이 유지되므로, 코드에 없는 상태가 운영에 남아 코드와 현실이 어긋난다.
- 같은 코드 변경이라도 실제로 어떤 리소스가 생성·수정·교체·삭제되는지는 현재 상태와의 차이로 결정되며, 그것은 plan 에서만 보인다. 특히 교체(삭제 후 재생성)는 데이터 손실이나 장애로 이어질 수 있다.
- 핵심 도구 자체도 형상 항목이자 외부 의존성이다. 버전을 고정하고, 라이선스와 거버넌스 변화를 의존성 위험으로 관리하며, 대체 경로를 알고 있어야 한다.
더 읽을거리 (References)
- Martin Fowler, InfrastructureAsCode, 2016
- Martin Fowler, SnowflakeServer, PhoenixServer, 2012
- Kief Morris, ImmutableServer, 2013
- Kief Morris, Infrastructure as Code, O’Reilly (서지 정보)
- Kubernetes, Kubernetes Object Management
- HashiCorp, Terraform Language, terraform plan
- Open Policy Agent, Policy Language (Rego)
- OpenTofu, The OpenTofu Manifesto, Documentation
- CS300: Infrastructure as Code — Terraform, GitOps 와 ArgoCD