Azure 랜딩 존(Landing Zone) 문서를 처음 열어보면 대부분 창을 닫습니다. 관리 그룹 계층이 5단계고, 구독이 여덟 개쯤 나오고, Hub-Spoke 네트워크에 Azure Firewall이 중앙에 박혀 있습니다. 수백 명 규모 조직을 전제로 쓰인 문서이기 때문입니다.
1인 개발자나 5~10명 팀에는 그 구조가 맞지 않습니다. 그렇다고 구독 하나에 전부 몰아넣으면 6개월 뒤에 반드시 후회합니다. 이 글은 지금 감당 가능하면서도 나중에 확장할 때 갈아엎지 않아도 되는 최소 구조를 정리한 것입니다.
먼저: 구독 하나로 시작하면 무엇이 문제인가
“리소스 그룹으로 나누면 되지 않나”라는 생각이 가장 먼저 듭니다. 실제로 초기에는 문제가 없습니다. 문제가 드러나는 시점은 대개 이 네 가지 중 하나가 발생할 때입니다.
| 상황 | 구독 1개일 때 생기는 일 |
|---|---|
| 개발 환경에서 실수로 리소스 삭제 | 운영과 같은 구독이라 권한 분리가 안 됨. 운영까지 지울 수 있음 |
| 이번 달 개발 비용이 얼마인지 묻는 질문 | 태그가 완벽하지 않으면 답을 못 함. 청구서가 한 덩어리 |
| 테스트로 만든 것들 정리 | 무엇이 테스트용인지 구분이 안 됨. 지우기가 무서워짐 |
| 구독 한도 도달 (VM 코어, 공용 IP 등) | 개발용 리소스가 운영 배포를 막음 |
핵심은 구독이 Azure에서 권한·할당량·청구의 경계선이라는 점입니다. 리소스 그룹은 이 셋 중 어느 것도 나누지 못합니다. 리소스 그룹은 “묶음”이고 구독은 “경계”입니다. 이 차이를 알고 나면 최소 구조가 왜 그렇게 생겼는지 이해가 쉽습니다.
최소 구조: 관리 그룹 2단계 + 구독 2개
제가 소규모에 권하는 출발점은 이것입니다. 더 줄이면 위 문제가 생기고, 더 늘리면 관리 비용이 효용을 넘습니다.
Tenant Root Group
└─ mg-platform (관리 그룹 - 정책을 여기에 건다)
├─ sub-prod (구독 1: 운영)
│ ├─ rg-prod-network
│ ├─ rg-prod-app
│ └─ rg-prod-data
└─ sub-nonprod (구독 2: 개발 + 테스트)
├─ rg-dev-app
└─ rg-test-app
설계 의도를 하나씩 짚겠습니다.
왜 관리 그룹을 하나라도 두는가
구독이 두 개뿐이면 관리 그룹이 불필요해 보입니다. 하지만 정책을 붙일 자리가 필요합니다. 관리 그룹 없이 구독마다 정책을 걸면, 구독이 세 개 네 개로 늘 때마다 같은 정책을 반복해서 붙여야 하고 반드시 하나를 빠뜨립니다.
더 중요한 건 Tenant Root Group에 직접 정책을 걸면 안 된다는 점입니다. 루트는 향후 만들어질 모든 구독에 영향을 주고, 실수로 잘못 걸면 되돌리기가 까다롭습니다. 중간에 관리 그룹을 하나 두고 거기에 거는 습관을 처음부터 들이는 편이 안전합니다.
왜 개발과 테스트를 한 구독에 두는가
이론적으로는 셋을 다 나누는 게 맞습니다. 하지만 소규모에서 구독을 셋으로 나누면 네트워크 피어링·정책 할당·권한 부여를 세 번씩 해야 합니다. 개발과 테스트는 중단돼도 되는 환경이라는 성격이 같으므로, 리소스 그룹으로만 나눠도 충분합니다.
반드시 갈라야 하는 건 운영과 비운영입니다. 이 선 하나만 지켜도 앞의 표에 있는 문제 대부분이 사라집니다.
구축 1단계 — 관리 그룹과 구독 만들기
#!/usr/bin/env bash
set -euo pipefail
# 관리 그룹 생성
az account management-group create \
--name mg-platform \
--display-name "Platform"
# 기존 구독을 관리 그룹 아래로 이동
SUB_PROD="00000000-0000-0000-0000-000000000001"
SUB_NONPROD="00000000-0000-0000-0000-000000000002"
az account management-group subscription add \
--name mg-platform --subscription "$SUB_PROD"
az account management-group subscription add \
--name mg-platform --subscription "$SUB_NONPROD"
# 계층 확인
az account management-group show \
--name mg-platform --expand --recurse \
--query "children[].{name:displayName, type:type}" -o table
구독 자체를 새로 만드는 건 CLI로 안 되는 경우가 많습니다. 계약 형태(EA, MCA, 종량제)에 따라 다르며, 종량제 계정은 포털에서 수동으로 추가해야 합니다. 관리 그룹 배치는 만든 뒤에 위 명령으로 하면 됩니다.
구축 2단계 — 이름 규칙을 먼저 정한다
리소스를 만들기 전에 정해야 합니다. 나중에 바꾸려면 대부분의 Azure 리소스는 이름 변경이 안 돼서 다시 만들어야 합니다.
<리소스종류>-<워크로드>-<환경>-<리전>-<일련번호>
예시:
vm-api-prod-krc-01
st logapiprodkrc01 (스토리지는 하이픈 불가, 소문자 24자 제한)
kv-api-prod-krc-01
vnet-hub-prod-krc-01
rg-api-prod-krc
실무에서 걸리는 지점 두 가지를 미리 말씀드립니다.
- 스토리지 계정은 하이픈을 못 쓰고, 소문자·숫자만 3~24자이며, 전 세계에서 유일해야 합니다. 규칙을 통일하려다 24자를 넘기는 경우가 많으니 스토리지만 별도 규칙을 두는 편이 낫습니다.
- Key Vault는 삭제해도 소프트 삭제 기간(기본 90일) 동안 이름이 점유됩니다. 지우고 같은 이름으로 다시 만들면 실패합니다.
az keyvault purge로 완전 삭제해야 재사용됩니다.
구축 3단계 — 최소한의 정책 3개
랜딩 존 문서에는 정책이 수십 개 나오지만, 소규모에서 지금 당장 효과가 나는 건 이 세 개입니다. 나머지는 필요해질 때 추가하면 됩니다.
정책 1 — 허용 리전 제한
가장 먼저 걸어야 할 정책입니다. 실수로 다른 리전에 리소스를 만들면 리전 간 트래픽 요금이 발생하고, 나중에 찾기도 어렵습니다.
az policy assignment create \
--name "allowed-locations" \
--scope "/providers/Microsoft.Management/managementGroups/mg-platform" \
--policy "e56962a6-4747-49cd-b67b-bf8b01975c4c" \
--params '{"listOfAllowedLocations":{"value":["koreacentral","koreasouth"]}}'
정책 2 — 태그 필수화
비용 추적의 전제 조건입니다. 태그 없이 만들어진 리소스는 나중에 “이게 누구 건지” 알 수 없습니다.
# 리소스 그룹의 태그를 리소스에 자동 상속 (권장)
az policy assignment create \
--name "inherit-owner-tag" \
--scope "/providers/Microsoft.Management/managementGroups/mg-platform" \
--policy "cd3aa116-8754-49c9-a813-ad46512ece54" \
--params '{"tagName":{"value":"Owner"}}' \
--mi-system-assigned --location koreacentral
Deny보다 Modify(상속) 정책을 먼저 쓰는 것을 권합니다. 태그가 없으면 생성을 막는 Deny 정책은 강력하지만, 초기에 걸면 Terraform 배포가 계속 실패해서 개발이 막힙니다. 리소스 그룹에만 태그를 달면 하위 리소스가 자동으로 물려받는 상속 방식이 마찰이 적습니다.
정책 3 — 비싼 VM 크기 차단
비운영 구독에만 겁니다. 개발 환경에서 실수로 대형 VM을 띄우는 사고를 막습니다.
az policy assignment create \
--name "allowed-vm-sizes-nonprod" \
--scope "/subscriptions/$SUB_NONPROD" \
--policy "cccc23c7-8427-4f53-ad12-b6a63eb452b3" \
--params '{"listOfAllowedSKUs":{"value":[
"Standard_B2s","Standard_B2ms","Standard_D2s_v5","Standard_D4s_v5"
]}}'
정책을 더 확장하고 싶으시면 태그와 Policy로 거버넌스 잡기에 이니셔티브 구성과 규정 준수 확인 방법을 정리해두었습니다.
구축 4단계 — 예산 알림부터 걸고 시작한다
리소스를 만들기 전에 예산 알림을 먼저 거는 것을 권합니다. 순서가 뒤바뀌면 첫 청구서에서 놀라게 됩니다.
az consumption budget create \
--budget-name "nonprod-guard" \
--amount 300 \
--time-grain Monthly \
--category Cost \
--start-date 2026-08-01 \
--end-date 2027-08-01 \
--subscription "$SUB_NONPROD"
포털에서 알림 조건을 추가할 때 실제 80%와 예측 100% 두 개를 거세요. 예측 알림이 있어야 월 중반에 미리 알 수 있습니다. 자세한 내용은 신규 구독 첫 주 점검 5가지에 정리해두었습니다.
네트워크는 지금 만들지 마세요
랜딩 존 문서를 보고 가장 많이 하는 과잉 투자가 Hub-Spoke 네트워크를 처음부터 구성하는 것입니다. 소규모에서는 대개 불필요합니다.
| 구성 | 필요해지는 시점 | 그 전에는 |
|---|---|---|
| Hub-Spoke + 피어링 | 구독이 3개 이상이고 공유 리소스가 생길 때 | 구독별 단일 VNet으로 충분 |
| Azure Firewall | 아웃바운드 트래픽을 중앙 통제해야 할 때 | NSG로 충분. NSG·Firewall·WAF 비교 참고 |
| VPN / ExpressRoute | 온프레미스와 상시 연결이 필요할 때 | Bastion으로 관리 접속만 해결 |
| Private Endpoint 전면 적용 | 규정상 공용 엔드포인트 차단이 요구될 때 | 서비스 엔드포인트로 시작 |
Azure Firewall은 배포만 해도 시간당 고정 요금이 붙습니다. 트래픽이 없어도 나갑니다. 소규모 구독에서 이것 하나가 전체 비용의 절반을 차지하는 경우를 종종 봅니다. NSG로 충분한 단계에서는 NSG를 쓰는 게 맞습니다.
다만 VNet 주소 대역만은 지금 정해두세요. 나중에 피어링할 때 대역이 겹치면 한쪽을 통째로 다시 만들어야 합니다. 겹치지 않게 미리 나눠두는 데는 비용이 들지 않습니다.
운영 : 10.10.0.0/16
비운영 : 10.20.0.0/16
향후예비: 10.30.0.0/16 ~ 10.90.0.0/16
온프레미스 연결 대비: 192.168.0.0/16 은 사용하지 않음
확장할 때 무엇이 바뀌는가
이 구조의 장점은 다음 단계로 갈 때 기존 것을 안 부순다는 점입니다. 조직이 커지면 이렇게 자라납니다.
- 구독 분리 — sub-nonprod를 sub-dev / sub-test로 쪼갬. 관리 그룹은 그대로
- 관리 그룹 계층화 — mg-platform 아래 mg-prod / mg-nonprod 추가. 정책을 환경별로 다르게
- 공유 서비스 구독 추가 — sub-shared를 만들어 Hub VNet·Log Analytics·ACR을 이관
- Hub-Spoke 전환 — 이미 대역을 안 겹치게 잡아뒀으므로 피어링만 걸면 됨
1~4번 어디에서도 리소스를 다시 만들 일이 없습니다. 처음에 관리 그룹 하나를 두고 VNet 대역을 안 겹치게 잡아둔 것만으로 확보되는 여유입니다.
시작 체크리스트
- 관리 그룹 1개를 만들고 정책은 루트가 아니라 여기에 건다
- 구독은 운영 / 비운영 2개로 나눈다 (개발·테스트는 리소스 그룹으로)
- 이름 규칙을 리소스 만들기 전에 정한다 (스토리지·Key Vault 예외 주의)
- 정책 3개: 허용 리전, 태그 상속, 비운영 VM 크기 제한
- 예산 알림을 실제 80% + 예측 100%로 건다
- VNet 대역만 미리 나누고, Hub-Spoke·Firewall은 만들지 않는다
랜딩 존의 목적은 완벽한 구조를 처음부터 짓는 게 아니라, 나중에 후회할 결정을 지금 피하는 것입니다. 되돌리기 어려운 것(구독 경계, 이름 규칙, 네트워크 대역)만 신경 쓰고 나머지는 필요할 때 붙이면 충분합니다.