한 줄 정의
인프라를 코드로 정의하면 누가 무엇을 작성하고 어디서 적용하며 언제 검토받는지가 전부 재편되고, 그 재편의 핵심은 코드를 적용하는 자리를 개인 책상에서 공용 파이프라인으로 옮기는 것입니다.
쉽게 말하면
여러 사람이 함께 쓰는 공동 주방을 떠올리면 이 장이 한 줄로 꿰어집니다.
각자 집에서 만든 재료를 들고 와 공용 냄비에 직접 넣기 시작하면 주방은 곧 엉망이 됩니다. 누가 뭘 넣었는지 알 수 없고, 뒤에 온 사람이 앞사람 재료를 덮어씁니다. 불을 하나만 쓰도록 잠금장치를 달아도 소용없습니다. 순서만 정해질 뿐, 서로 다른 레시피가 같은 냄비에 들어가는 건 여전하기 때문입니다.
그래서 규칙을 바꿉니다. 재료는 반드시 공용 창고에 먼저 넣고, 조리는 정해진 한 라인에서만 합니다. 간을 보고 싶으면 각자 자기 작은 냄비에서 시험합니다. 위생 검사관은 완성된 요리가 나가기 직전에 붙잡는 대신, 레시피와 조리 공정 자체에 검사 항목을 심어 둡니다.
이 장은 이 세 가지 규칙을 인프라 코드에 그대로 옮긴 이야기입니다.
왜 중요한가?
인프라 작업은 원래 역할이 잘게 쪼개져 있습니다. 설계자가 그리고, 빌더가 만들고, 테스터가 검증하고, 거버넌스가 승인하고, 지원팀이 고칩니다. 여기에 네트워킹·스토리지·서버 같은 인프라 도메인과 보안·규정 준수·아키텍처 같은 거버넌스 도메인이 겹치면 미세 전문분야(micro-specialty)로 잘게 나뉜 바로크(baroque) 조직 구조 가 만들어집니다.
책에 나오는 국제 은행 사례가 이 구조의 끝을 보여 줍니다. 릴리스 단계마다 별도 팀이 붙어 12개 팀이 하나의 릴리스에 관여했는데, 일부 팀은 다른 팀이 존재한다는 사실조차 몰랐습니다. 그 결과 릴리스 프로세스 전반에 시스템 지식이 거의 공유되지 않았고 일관성도 없었습니다.
인프라를 코드로 정의하면 이 경계를 다시 그을 수 있습니다. 코드와 자동화된 테스트가 지식의 전달 매체가 되기 때문입니다. 이 장이 말하는 좋은 워크플로의 조건은 결국 하나로 모입니다. 자동화된 프로세스가 변경을 수행하는 가장 쉽고 자연스러운 방법이 되는 것. 수동 경로가 더 빠르고 편한 순간, 앞의 모든 노력이 우회됩니다.
핵심 내용
인프라 작업의 역할
자동화 여부와 관계없이 인프라 시스템에는 몇 가지 역할이 존재합니다. 역할과 사람은 일대일로 매핑되지 않습니다. 한 사람이 여러 역할을 하기도 하고, 여러 사람이 한 역할을 나눠 갖기도 합니다.
| 역할 | 하는 일 |
|---|---|
| 사용자 | 인프라를 직접 사용. 애플리케이션을 개발하거나 서드파티 애플리케이션을 구성·관리하는 팀 |
| 거버넌스 전문가 | 보안·규정 준수·아키텍처·성능·비용·정확성 영역의 정책 설정 |
| 설계자 | 인프라를 설계. 조직에 따라 네트워킹·스토리지 같은 도메인으로 나뉨 |
| 도구 개발자 | 다른 팀이 환경을 구축·실행할 때 쓰는 서비스·도구·컴포넌트 제공 |
| 빌더 | 인프라를 구축·변경. 스크립트나 도구를 콘솔·인터페이스로 직접 실행 |
| 테스터 | 인프라를 검증. QA뿐 아니라 보안·성능 같은 거버넌스 도메인 검토자도 포함 |
| 지원 | 시스템이 계속 올바르게 실행되는지 확인하고 문제를 수정 |
고전적인 구조에서는 이 역할들이 워크플로 각 부분을 하나씩 전담합니다. 필요 → 설계 → 빌드 → 테스트 → 사용의 흐름을 서로 다른 팀이 이어 받고, 규정 준수는 설계에, 지원은 사용 뒤에 붙습니다.
누가 인프라 코드를 작성할까?
코드를 누가 쓰느냐는 단순한 역할 분담이 아니라 셀프 서비스가 가능한지 를 결정하는 선택입니다.
| 주체 | 작성 방식 | 함의 |
|---|---|---|
| 빌더 | 사용자가 환경을 요청하면 빌드팀이 도구·스크립트로 만들어 줌 | 전통적 팀 구조를 그대로 둔 채 빌드팀 내부만 최적화. 빌드팀 속도가 올라가도 요청부터 완료까지의 전체 리드 타임은 잘 줄지 않음 |
| 사용자 | 애플리케이션팀이 자기가 쓸 인프라를 직접 정의 | 요구를 솔루션에 맞게 조정할 수 있음. 대신 팀마다 인프라 전문지식이나 정의를 단순화해 주는 도구가 있어야 함 |
| 도구 개발자 | 사용자가 인프라를 정의할 수 있게 해 주는 플랫폼·라이브러리·도구를 만듦 | 사용자는 코드보다 구성(configuration) 을 더 많이 작성하게 됨 |
| 거버넌스·테스터 | 다른 사람이 자기 코드를 스스로 확인할 수 있는 도구를 만듦 | 정책을 문서가 아니라 실행 가능한 검사로 배포 |
빌더와 도구 개발자의 차이는 셀프 서비스에 있습니다. 빌더팀은 “만들어 달라”는 요청에 응답해 코드를 쓰고, 도구 개발자팀은 사용자가 스스로 만들 수 있는 코드를 씁니다.
어디를 최적화할 것인가
가치 흐름 매핑(value stream mapping)은 리드 타임이 어디로 가는지 보여 줍니다. 서버 프로비저닝을 8시간에서 10분으로 줄이면 98% 감소지만, 요청 티켓이 대기열에서 평균 8일을 기다린다면 전체 리드 타임은 10%밖에 줄지 않습니다. 명백히 비효율적이지만 총 리드 타임에 거의 영향이 없는 구간을 최적화하는 실수 가 그만큼 흔합니다.
코드를 어디서 적용할 것인가
인프라 자동화를 시작할 때 대부분 자기 워크스테이션 커맨드라인에서 도구를 실행합니다. 아무도 쓰지 않는 테스트 인스턴스라면 문제없지만, 공유 인스턴스에 이 방식을 쓰면 무너집니다.
flowchart TB subgraph L["로컬 적용 — 각자 자기 코드로"] LR1["소스 저장소"] --> LA["니타 로컬<br/>(미푸시 변경)"] LR1 --> LB["알리 로컬<br/>(이전 버전 기반)"] LA --> LS["공유 인스턴스"] LB -->|"니타 변경을<br/>되돌림"| LS end subgraph C["중앙 적용 — 저장소에서 통합 후"] CA["니타 변경"] --> CR["소스 저장소<br/>(모든 차이 해결)"] CB["알리 변경"] --> CR CR --> CS["중앙 서비스가 적용"] --> CI["공유 인스턴스"] end
로컬 적용의 문제는 순서 문제가 아니라 버전 문제 입니다. 로컬에서 고친 코드를 푸시하지 않고 적용하면 다른 사람은 그 버전에 접근할 수 없고, 이후 그 사람이 이전 버전을 적용하는 순간 앞사람 변경이 사라집니다. Terraform의 상태 잠금(state locking)은 두 사람이 동시에 적용하는 것만 막을 뿐, 서로 다른 버전이 번갈아 적용되는 것은 막지 못합니다.
중앙 집중식 서비스에서 적용하면 얻는 것이 세 가지입니다.
- 모든 변경이 소스 저장소에서 통합되므로, 두 사람의 코드 차이가 적용 전에 해결됩니다
- 매번 동일한 버전의 도구·스크립트·유틸리티가 실행됩니다. 사람이 실수하지 않으리라 가정할 필요가 없습니다
- 전체 프로세스의 자동화가 강제됩니다. 워크스테이션에서 실행하면 앞뒤로 수동 단계를 슬쩍 남겨 두기 쉽지만, 중앙 서비스는 완전히 자동화하는 것 외에 선택지가 없습니다
개인 인프라 인스턴스
코드를 공유 저장소에 푸시하기 전에 테스트할 수 있어야 파이프라인이 온라인 테스트까지 도달하기를 기다리지 않아도 되고, 실패한 변경이 다른 사람의 작업을 막는 일도 줄어듭니다. 그 전제가 개인 인스턴스입니다.
- 인프라 코드를 다루는 사람이 개인 인프라 인스턴스를 만들 수 있는지 확인합니다. 클라우드 플랫폼 없이 로컬에서 테스트할 수 있는 범위는 제한적이므로, 공유 dev 인스턴스로 대신하려는 유혹이 생기는데 그러면 다시 로컬 적용의 문제로 돌아갑니다
- 사용하지 않는 개인 인스턴스를 삭제할 수 있는 방법 을 만듭니다
- 인프라를 작게 유지합니다. 시스템 전체를 띄워야만 테스트할 수 있다면 개인 인스턴스는 성립하지 않습니다
- 개인 인스턴스와 파이프라인이 동일한 도구와 스크립트 를 씁니다. 여러 위치에서 쓸 수 있는 패키지를 만들게 되는 부수 효과가 있습니다
개인 인스턴스도 중앙에서 관리할 수 있습니다
개인 인스턴스를 워크스테이션에서 직접 적용하면 저장소에 없는 로컬 코드로 만들어진 인스턴스가 생깁니다. 휴가 간 사람이 남겨 둔 인스턴스를 팀이 지우지 못해 고생하는 상황이 여기서 나옵니다. 각자 변경을 개인 브랜치에 푸시 하고 중앙 서비스가 그 브랜치를 개인 인스턴스에 적용하게 하면, 로컬처럼 쓰면서도 코드가 중앙에서 보입니다.
브랜치 전략보다 통합 빈도
브랜치 전략에서 갈리는 지점은 두 가지입니다. 프로덕션으로 가는 경로를 브랜치로 표현할 것인가(릴리스 브랜치·환경 브랜치), 그리고 작업을 언제 통합할 것인가(기능 브랜치 vs 메인라인 통합)입니다.
특정 패턴보다 훨씬 중요한 것은 통합 빈도입니다. DORA Accelerate 연구는 팀 내 코드 통합 빈도가 상업적 성과와 상관관계가 있음을 보였고, 결론은 모든 구성원이 적어도 하루에 한 번 모든 코드를 통합하라는 것입니다.
병합(merge)은 통합(integration)이 아닙니다
빌드 서버가 브랜치에서 자동으로 테스트를 실행하는 것을 지속적 통합으로 착각하는 경우가 많습니다. 성과와 상관관계가 있는 쪽은 모든 사람이 코드베이스의 모든 변경을 완전히 통합 하는 것입니다.
기능 브랜치에서 메인을 자주 가져와 병합해도, 자기 작업을 메인으로 돌려보내지 않으면 다른 사람의 기능 브랜치 코드와는 여전히 만나지 못합니다. 통합은 양방향입니다. 메인에서 내 브랜치로, 내 브랜치에서 메인으로 — 최소 하루 한 번 이 왕복이 일어나야 합니다.
구성 드리프트를 막는 네 가지 방법
구성 드리프트는 대개 팀이 작업 방식을 완전히 바꾸지 않은 채 이전 방식의 일부만 자동화할 때 생깁니다.
자동화 지연 최소화하기
자동화 지연(automation lag) 은 자동화된 프로세스를 실행하는 인스턴스 사이의 시간 간격입니다. 마지막 실행 이후 시간이 길수록 실패 가능성이 올라갑니다. 코드를 바꾸지 않았어도 상황은 시간이 지나면서 바뀌기 때문입니다.
- 누군가 의존성 같은 다른 부분을 바꿨는데, 코드를 다시 적용할 때만 그 영향이 드러남
- 도구·서비스 업그레이드나 구성 변경이 기존 코드와 호환되지 않음
- 변경되지 않은 코드를 적용해도 OS 패키지 같은 타동적 의존성이 업데이트됨
- 누군가 수동으로 고친 뒤 코드에 반영하는 것을 잊어서, 다음 적용 때 그 변경이 사라짐
결론은 뒤집으면 명확합니다. 자주 적용할수록 실패 가능성이 낮아집니다. 장애가 나도 마지막 성공 이후 변경된 것이 적어 원인을 빨리 찾습니다.
임시방편 피하기
인프라 코드로 새 인프라를 프로비저닝하면서, 기존 시스템의 특정 부분만 일회성으로 바꾸려고 코드를 적용하는 방식입니다. 코드를 쓰긴 쓰는데 코드가 바뀔 때만 적용 하는 습관이 남습니다. 이것이 자동화 지연과 구성 드리프트를 함께 만듭니다.
지속적으로 코드 적용하기
앞의 두 문제를 정면으로 해결하는 전략입니다. 코드가 변경되지 않았더라도 인스턴스에 계속 적용합니다. Chef·Puppet 같은 서버 구성 도구가 매시간 일정에 따라 구성을 다시 적용하도록 설계된 이유가 이것이고, GitOps는 소스 코드 브랜치의 코드를 각 환경에 지속적으로 적용합니다. 이 방식은 중앙 서비스를 전제로 합니다.
불변 인프라 사용하기
접근을 뒤집습니다. 구성 코드를 자주 재적용하는 대신 인스턴스를 만들 때 한 번만 적용 하고, 코드가 바뀌면 새 인스턴스로 교체합니다. 그래서 불변 인프라를 쓰는 팀은 피닉스(phoenix) 서버처럼 인스턴스를 자주 리빌드합니다. 다운타임 없이 교체하려면 정교한 기술이 필요하고 모든 사례에 적용할 수도 없어서, 자동화 지연은 여전히 잠재적 문제로 남습니다.
GitOps
소스 코드 브랜치에서 환경으로 코드를 지속적으로 동기화 하는 코드형 인프라의 변형입니다. 딜리버리 아티팩트를 권장하지 않고, 대신 코드 변경을 소스 브랜치에 병합하는 것으로 승격을 표현합니다. 빌드 서버나 파이프라인 단계에서 “변경될 때 적용”하는 대신 코드와 시스템을 지속적으로 비교해 드리프트를 줄이는 것이 핵심입니다.
지속적 동기화 없이 환경 브랜치만 구현해 놓고 GitOps라 부르는 팀도 있는데, 이 경우 임시 변경 프로세스와 복사-붙여넣기 안티패턴에 쉽게 빠집니다.
파이프라인 기반 거버넌스
거버넌스는 불필요한 마찰로 여겨지기 쉽지만, 본래 의미는 조직의 정책에 따라 일이 책임감 있게 수행되도록 하는 것입니다. 문제는 그것이 프로세스 끝단의 검토·승인 게이트 로만 구현될 때 생깁니다.
책임 재편성
시스템을 코드로 정의하면 거버넌스 전문가의 책임을 앞단으로 옮길 수 있는 근거가 생깁니다.
| 요소 | 무엇이 달라지는가 |
|---|---|
| 재사용 | 이미 설계·검토된 코드를 재사용하면 신규 서버·환경마다 장황한 설계·검토·승인이 필요 없음 |
| 작업 코드 | 설계와 사양을 두고 입씨름하는 대신 실제 동작하는 코드와 예제 인프라를 보고 판단. 더 빠르고 정확한 피드백 루프 |
| 일관성 | 코드는 체크리스트를 따르는 사람보다 훨씬 일관된 환경을 만듦. 그래서 초기 환경에서의 검토가 후반부 검토보다 신뢰할 만함 |
| 자동화된 테스트 | 보안·규정 준수 검사를 테스트로 만들면 전문가를 개입시키지 않고도 일반적인 문제를 수정 가능 |
| 품질의 민주화 | 전문가가 만든 도구·테스트로 비전문가도 민감한 영역의 코드를 안전하게 변경. 전문가는 코드·테스트 보고서·작업 인스턴스를 직접 보며 검토 |
왼쪽으로 이동하기
코드는 프로세스 다이어그램의 왼쪽 끝 에서 엄격하게 테스트됩니다. 그래서 조직은 프로덕션 적용 직전인 오른쪽 끝의 무거운 프로세스에 시간을 쓰지 않아도 됩니다. 거버넌스와 테스터의 초점이 사후 검토에서 구현 중의 협력·도구 제공·조기 테스트로 옮겨갑니다.
인프라 코드베이스와 파이프라인 자체가 거버넌스 채널이 됩니다. 보안 정책 변경은 덜 민감한 영역의 변경에는 필요 없는 검토·승인 단계를 거치도록 파이프라인을 구성할 수 있기 때문입니다.
거버넌스가 포함된 프로세스의 모습
ShopSpinner 예제가 이 구조를 요약합니다. 기술 리더십 그룹이 애플리케이션 서버 인프라가 만족해야 할 CFR(주문 수와 빈도, 인터페이스 응답 시간, 서버 오류 복구 시간)을 정의하고, 인프라팀과 애플리케이션팀이 SRE·QA와 함께 이 CFR을 검증하는 자동화된 테스트를 파이프라인 여러 단계에 심습니다.
그 결과 엔지니어가 네트워킹 구성을 바꿔도 별도 검토 제출이 필요 없습니다. 파이프라인이 프로덕션 적용 전에 CFR 충족 여부를 자동 확인하고, 위반하면 몇 분 안에 빨간불로 알려 줍니다. 자동화된 테스트가 잡지 못한 문제가 고객 인스턴스에서 나면 비난 없는 포스트모템(blameless postmortem)을 수행하는데, 여기서 나오는 결론은 대개 CFR을 고치거나 목록에 추가해야 한다 는 것입니다. 거버넌스 자체가 학습하며 갱신되는 구조입니다.
긴급 변경 프로세스는 정상 프로세스의 신호등입니다
많은 팀이 긴급 변경용 별도 프로세스를 둡니다. 그런데 더 빠른 변경을 위해 별도 프로세스가 필요하다는 것 자체가 정상 프로세스를 개선할 수 있다는 신호 입니다.
긴급 프로세스가 속도를 내는 방법은 둘 중 하나입니다. 불필요한 단계를 생략하거나, 필요한 단계를 생략하거나. 앞쪽이라면 그 단계는 평소에도 필요 없었던 것이고, 뒤쪽이라면 위험이 가장 클 때 안전장치를 빼는 셈입니다. 건너뛸 수 없을 만큼 위험한 단계라면 더 효율적으로 처리할 방법을 찾아 매번 그 방법을 수행해야 합니다.
비교 / 트레이드오프
| 로컬 워크스테이션에서 적용 | 중앙 집중식 서비스에서 적용 | |
|---|---|---|
| 적용되는 코드 | 푸시되지 않은 로컬 버전일 수 있음 | 항상 저장소의 통합된 버전 |
| 동시 변경 | 잠금으로 순서만 정리, 버전 충돌은 남음 | 저장소에서 차이를 해결한 뒤 적용 |
| 도구 버전 | 사람마다 다를 수 있음 | 매번 동일 |
| 수동 단계 | 앞뒤로 남겨 두기 쉬움 | 완전 자동화 외에 선택지 없음 |
| 적합한 대상 | 아무도 쓰지 않는 개인·테스트 인스턴스 | 공유 인스턴스 전체(dev·SIT·프로덕션) |
구성 드리프트 대응책도 성격이 갈립니다.
| 전략 | 접근 | 남는 문제 |
|---|---|---|
| 지속적으로 코드 적용 | 변경 여부와 무관하게 주기적으로 재적용 | 재적용이 안전하도록 코드가 멱등해야 함 |
| 불변 인프라 | 생성 시 한 번만 적용하고 교체로 갱신 | 무중단 교체 기술이 필요하고 모든 사례에 적용 불가 |
내 생각
- “자동화된 경로가 가장 쉬운 경로”라는 조건이 사실상 전부입니다. 파이프라인을 잘 만들어 놓아도 콘솔에서 직접 고치는 게 3분이고 파이프라인이 20분이면, 급할 때 반드시 콘솔로 갑니다. 그 순간 코드와 실제가 갈라지고 드리프트가 시작됩니다.
- 잠금(state locking)을 동시성 해결책으로 오해하는 경우가 많습니다. 잠금은 “동시에 두 명이 적용”만 막습니다. 진짜 문제인 “서로 다른 버전이 번갈아 적용”은 코드가 저장소에서 통합된 뒤 한 곳에서만 적용될 때 사라집니다.
- 개인 인스턴스를 만들 수 있느냐가 인프라 설계 품질의 리트머스입니다. 혼자 띄울 수 없다면 그건 개인 인스턴스 도구가 없어서가 아니라 시스템이 너무 크고 결합돼 있다는 신호입니다. 공유 dev 환경에 사람이 몰리는 팀은 대부분 여기에 걸려 있습니다.
- 기능 브랜치를 오래 들고 있으면서 CI를 돌린다고 지속적 통합이라 부르는 건 흔한 착각입니다. 메인을 자주 당겨오는 건 내 브랜치를 지키는 행위지 팀 전체가 통합되는 행위가 아닙니다. 인프라 코드는 특히 충돌 해결 비용이 커서, 하루 단위 왕복이 실무적으로도 이득입니다.
- 긴급 변경 프로세스는 조직의 정상 프로세스를 진단하는 리포트입니다. 긴급 경로가 상시로 쓰이는 팀은 이미 정상 경로가 못 쓸 만큼 무겁다는 뜻이고, 그때 고칠 것은 긴급 절차의 승인 양식이 아니라 정상 파이프라인의 소요 시간입니다.
- 거버넌스를 테스트로 바꾸는 작업이 실제로는 가장 정치적입니다. 보안팀이 검토 권한을 내려놓는 게 아니라 검토 기준을 코드로 명시하는 것이라는 합의가 먼저 필요합니다. 그 합의 없이 자동화만 붙이면 파이프라인 통과 후에 또 사람 승인이 붙어서 단계만 늘어납니다.
관련 개념
- Ch02 클라우드 시대 인프라의 원칙 — 구성 드리프트가 무엇이고 왜 생기는지
- Ch08 코드를 지속적으로 테스트하고 딜리버리한다 — 파이프라인 설계와 프로그레시브 테스트
- Ch09 인프라 스택 테스트하기 — 테스트 픽스처로 스택을 단독 테스트하는 방법
- Ch12 서버 변경 관리 — 불변 서버와 지속적 구성 동기화
- Ch15 시스템을 작고 간단하게 빌드한다 — 개인 인스턴스를 가능하게 하는 크기와 결합도
- Ch19 인프라 코드 딜리버리하기 — 딜리버리 저장소, 통합 시점, 래퍼 스크립트