Platform

Azure 보안 네트워크와 고가용성 웹 애플리케이션 구성

Azure VNet, NSG, Bastion, Application Gateway, Load Balancer, VMSS, App Service를 하나의 웹 서비스 운영 흐름으로 정리합니다.

Azure 보안 네트워크와 고가용성 웹 애플리케이션 구성

Azure를 처음 다룰 때 가장 헷갈렸던 부분은 리소스가 많다는 점보다, 각 리소스가 트래픽 경로에서 어떤 위치를 차지하는지였다. VNet, Subnet, NSG, Bastion, Application Gateway, Load Balancer, VMSS, App Service는 각각 따로 만들 수 있지만, 실제 서비스에서는 하나의 요청 경로 안에서 함께 동작한다.

그래서 이 글은 Azure 서비스를 목록처럼 정리하기보다, 외부 사용자가 들어오는 경로, 운영자가 접속하는 경로, 백엔드가 보호되는 경로, 장애나 부하가 생겼을 때 확장되는 경로를 따라 정리했다. 실습에서 사용한 이름과 주소 대역도 일부 남겨 두었다. 리소스 이름 자체보다 중요한 것은 어떤 경계에서 어떤 트래픽을 허용하고 막았는지다.

네트워크는 주소 설계부터 흐름이 갈린다

VNet은 Azure 안에서 만드는 논리적인 네트워크다. 온프레미스 네트워크의 VLAN이나 사설망에 가깝게 생각할 수 있지만, 클라우드에서는 이 VNet 안에 서브넷, NIC, 프라이빗 IP, 퍼블릭 IP, DNS, Gateway, Load Balancer 같은 구성 요소가 붙는다.

예를 들어 실습에서는 vnet-krce10.0.0.0/16 대역으로 만들고, snet-krce-0110.0.1.0/24로 나누었다. 다른 환경에서는 vnet-hallofarmor-us 안에 Application Gateway용 snet-jarvis2-gw (10.16.1.0/24), 프런트엔드용 10.16.2.0/24, 백엔드용 snet-jarvis2-be (10.16.3.0/24)처럼 역할별 서브넷을 나누었다.

주소 대역을 나누는 이유는 보기 좋게 정리하기 위해서가 아니다. 나중에 NSG, Route Table, Bastion, NAT Gateway, Load Balancer를 붙일 때 “어떤 계층이 외부에 노출되는가”와 “어떤 계층은 내부 통신만 허용하는가”가 서브넷 단위로 결정되기 때문이다.

vnet-hallofarmor-us: 10.16.0.0/16

10.16.1.0/24  snet-jarvis2-gw    Application Gateway
10.16.2.0/24  front-end subnet    Web VM / Web VMSS
10.16.3.0/24  snet-jarvis2-be     API / Backend VM / Internal LB

운영 환경에서는 처음부터 완벽한 주소 설계를 하기는 어렵다. 그래도 최소한 Gateway, Web/API, Data, Management 영역은 분리해 두는 편이 낫다. 처음에는 단순한 웹 서버 한 대여도, 나중에 VMSS, Private Endpoint, Database, AKS 같은 구성으로 확장할 때 네트워크를 다시 쪼개는 비용이 커지기 때문이다.

NSG는 방화벽 규칙이 아니라 의도를 기록하는 장치다

NSG(Network Security Group)는 Subnet이나 NIC에 붙여 인바운드와 아웃바운드 트래픽을 제어한다. 규칙은 Priority가 낮은 숫자부터 평가되고, 우선순위 범위는 100부터 4096까지다. 규칙에는 Source, Source Port, Destination, Destination Port, Protocol, Action이 들어간다.

기본 규칙도 중요하다. Azure는 기본적으로 VNet 내부 통신을 허용하고, Azure Load Balancer의 헬스 체크를 허용하며, 나머지 인바운드는 차단한다. 아웃바운드는 VNet과 Internet 방향이 기본 허용되는 구성이 많다.

Inbound default rules
- AllowVNetInBound
- AllowAzureLoadBalancerInBound
- DenyAllInbound

Outbound default rules
- AllowVNetOutBound
- AllowInternetOutBound
- DenyAllOutbound

실습 단계에서는 SSH나 HTTP를 빠르게 열어 확인하기 쉽지만, 글을 쓰며 다시 정리해 보니 핵심은 “포트를 열었다”가 아니라 “누구에게 열었는가”였다. 예를 들어 Application Gateway 뒤에 있는 VM은 모든 인터넷에서 직접 HTTP를 받을 필요가 없다. Application Gateway나 Front Door 같은 앞단에서 들어오는 트래픽만 허용하면 된다.

Azure에서는 Service Tag와 ASG(Application Security Group)를 이용해 규칙을 더 읽기 쉽게 만들 수 있다. Internet, VirtualNetwork, AzureLoadBalancer, AppGateway, Sql 같은 Service Tag를 쓰면 IP 대역을 직접 관리하는 부담이 줄어든다. ASG는 VM을 애플리케이션 역할별로 묶어 규칙을 표현할 수 있어서, IP 주소보다 “web tier에서 api tier로 443 허용” 같은 문장에 가깝게 관리할 수 있다.

Rule: allow-app-gateway-to-web
Priority: 200
Source: AppGateway
Destination: web-asg
Destination port: 80
Protocol: TCP
Action: Allow

Rule: deny-direct-web-from-internet
Priority: 300
Source: Internet
Destination: web-asg
Destination port: 80
Protocol: TCP
Action: Deny

NSG를 Subnet과 NIC 양쪽에 붙일 수 있다는 점도 실수하기 쉽다. 한쪽에서는 허용했는데 다른 쪽에서 막으면 통신은 실패한다. 그래서 장애를 볼 때는 VM 자체보다 NIC, Subnet, Route Table, Load Balancer health probe, NSG Flow Logs를 함께 확인해야 한다.

AzureDiagnostics
| where Category contains "NetworkSecurityGroup"
| project TimeGenerated, Resource, OperationName, msg_s
| order by TimeGenerated desc
| take 20

관리 접속은 서비스 트래픽과 분리한다

초기 실습에서는 Public Subnet에 Bastion Host VM을 두고, Private Subnet의 VM으로 SSH를 이어 들어가는 방식도 구성했다. bastion-vnet 안에 public-subnetprivate-subnet을 만들고, bastion-host는 공용 IP를 가지며 protected-resource는 공용 IP 없이 사설 IP만 가지는 구조다.

scp -i ./bastion-host_key.pem ./protected-resource_key.pem \
  azureuser@4.205.181.27:/home/azureuser

ssh -i ./bastion-host_key.pem azureuser@4.205.181.27
chmod 400 ./protected-resource_key.pem
ssh -i ./protected-resource_key.pem azureuser@10.0.1.4

이 방식은 구조를 이해하기 좋지만, 운영 환경에서는 개인키를 Bastion Host에 복사하는 흐름이 부담스럽다. Azure Bastion은 이 문제를 줄이기 위한 관리형 접속 서비스다. VM에 공용 IP를 붙이지 않고도 브라우저 기반 RDP/SSH 접속을 제공한다. 조건은 명확하다. VNet 안에 AzureBastionSubnet이라는 이름의 전용 서브넷이 필요하고, 최소 /27 크기를 확보해야 한다. Public IP는 Standard SKU를 사용한다.

az network bastion create \
  --name bastion-krce \
  --resource-group rg-camp1-krce \
  --vnet-name vnet-krce \
  --public-ip-address pip-bastion-krce \
  --location koreacentral

관리 접속을 Bastion으로 분리하면 VM의 공용 IP를 없앨 수 있다. 이 차이는 작아 보이지만, 보안 모델이 바뀐다. 더 이상 “SSH 포트를 어디까지 열 것인가”가 아니라 “누가 Azure Portal과 Bastion을 통해 어떤 VM에 접속할 수 있는가”를 Entra ID, RBAC, NSG, 로그로 관리하게 된다.

VM 구성은 접속 확인보다 하드닝 확인까지 이어져야 한다

Linux VM에서는 Ubuntu 24.04 LTS 기반으로 LAMP나 NGINX를 설치해 웹 응답을 확인했다. 단순히 80번 포트가 열렸는지 보는 것에서 끝내지 않고, 로그 위치와 접근 실패를 같이 확인했다.

sudo su
sudo apt update -y && sudo apt upgrade -y
sudo apt install lamp-server^ -y

tail -n 10 /var/log/apache2/access.log
tail -n 10 /var/log/apache2/error.log

NGINX 기반 VM은 다음처럼 간단한 HTML을 내려주도록 구성했다.

sudo apt-get -y update
sudo apt-get -y install nginx
sudo sh -c 'echo "<html><body><h1>Ubuntu web node</h1></body></html>" > /var/www/html/index.html'

보안 테스트에서는 Fail2Ban과 AppArmor도 확인했다. Fail2Ban은 반복적인 비정상 요청을 로그 기반으로 탐지해 차단하는 도구이고, AppArmor는 프로세스가 접근할 수 있는 파일 범위를 제한한다. 예를 들어 Apache가 /etc/passwd 같은 파일을 읽으려 할 때 커널 로그에서 거부 이벤트를 확인할 수 있다.

sudo apt install fail2ban -y
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local

sudo systemctl restart fail2ban
sudo systemctl enable fail2ban
sudo fail2ban-client status

curl -A "EmailCollector" http://<VM_PUBLIC_IP>
sudo apparmor_parser -r /etc/apparmor.d/usr.sbin.apache2
sudo aa-status
journalctl -k | grep DENIED

Windows VM에서는 Windows Server 2019 또는 2022에 IIS를 설치하고, 서비스 자동 시작과 기본 웹 페이지 응답을 확인했다.

Install-WindowsFeature -Name Web-Server -IncludeManagementTools
Set-Service -Name W3SVC -StartupType Automatic

이 과정에서 Directory Browsing, Windows Authentication, Windows Defender 테스트, IPBan 같은 항목도 함께 확인했다. 하나씩 보면 작은 보안 설정이지만, 웹 서버 운영에서는 “서버가 응답한다”와 “응답해도 되는 방식으로만 응답한다” 사이에 차이가 있다.

Application Gateway와 내부 Load Balancer는 역할이 다르다

부하 분산이라는 단어가 같아도 Application Gateway와 Load Balancer는 위치와 계층이 다르다. Application Gateway는 HTTP/HTTPS를 이해하는 L7 진입점이다. 경로 기반 라우팅, 호스트 기반 라우팅, WAF 같은 기능을 붙일 수 있다. 반면 Azure Load Balancer는 L4 기반으로 TCP/UDP 트래픽을 분산한다.

실습에서는 agw-jarvis2fe라는 Application Gateway를 만들었다. 지역은 East US 2, 계층은 Standard V2, 자동 크기 조정은 최소 2개에서 최대 10개로 설정했다. Gateway 전용 서브넷은 snet-jarvis2-gw (10.16.1.0/24)였고, 프런트엔드는 공용 IP pip-agw-jarvis2fe를 사용했다. 백엔드 풀에는 vmjarvis2fe01 (10.16.2.4), vmjarvis2fe02 (10.16.2.5)를 넣었다.

Application Gateway
- name: agw-jarvis2fe
- tier: Standard V2
- subnet: snet-jarvis2-gw (10.16.1.0/24)
- frontend: public IPv4, pip-agw-jarvis2fe
- backend pool: vmjarvis2fe01, vmjarvis2fe02
- listener: HTTP :80
- rule priority: 10
- backend setting: HTTP :80

백엔드 영역에는 내부 Load Balancer를 별도로 두었다. lbi-jarvis2be는 Standard SKU의 internal Load Balancer이고, 프런트엔드 IP는 10.16.3.99로 고정했다. 백엔드 풀에는 vmjarvis2be01 (10.16.3.4), vmjarvis2be02 (10.16.3.5)를 넣고, HTTP / 경로에 대해 5초 간격으로 health probe를 수행했다.

Internal Load Balancer
- name: lbi-jarvis2be
- frontend IP: 10.16.3.99
- backend pool: vmjarvis2be01, vmjarvis2be02
- probe: HTTP /, port 80, interval 5s
- rule: TCP 80 -> 80

이 구조에서 Application Gateway는 외부 요청을 웹 계층으로 받는 역할을 하고, 내부 Load Balancer는 웹 계층 뒤의 백엔드 서비스를 사설 네트워크 안에서 분산한다. NAT Gateway는 반대로 백엔드 서브넷의 아웃바운드 경로를 안정적으로 만드는 데 사용했다. 외부에서 백엔드로 직접 들어오게 하는 리소스가 아니라, 백엔드가 패치나 외부 API 호출을 위해 나갈 때 사용할 고정된 아웃바운드 경로에 가깝다.

Azure Firewall은 중앙 제어 지점을 만든다

서브넷마다 NSG를 붙이면 기본적인 트래픽 제어는 가능하다. 하지만 여러 서브넷, 여러 VNet, 여러 워크로드가 생기면 “어떤 트래픽이 어디로 나가는지”를 한 곳에서 관찰하고 통제하고 싶어진다. 이때 Azure Firewall이나 NVA(Network Virtual Appliance)를 hub 쪽에 두는 구조를 검토한다.

Firewall을 붙인다고 모든 보안 문제가 해결되는 것은 아니다. NSG는 여전히 subnet이나 NIC 수준의 1차 방어선이고, Firewall은 중앙 정책과 로깅 지점에 가깝다.

NSG
  - subnet/NIC 가까운 곳에서 허용 범위를 좁힌다
  - source, destination, port 중심의 기본 필터링

Azure Firewall
  - 중앙 egress/ingress 정책을 관리한다
  - application rule, network rule, DNAT rule을 분리한다
  - 로그를 모아 감사와 분석에 사용한다

Firewall을 경유시키려면 route table도 함께 설계해야 한다. 예를 들어 private subnet의 기본 경로 0.0.0.0/0을 Azure Firewall private IP로 보내면, 해당 subnet의 아웃바운드 트래픽은 Firewall 정책을 통과하게 된다.

Route table for private subnet
address prefix: 0.0.0.0/0
next hop type: Virtual appliance
next hop IP: Azure Firewall private IP

이 구성에서 자주 생기는 실수는 라우팅만 바꾸고 DNS, 로그, 예외 정책을 준비하지 않는 것이다. OS 패치, container image pull, package registry 접근, external API 호출이 모두 Firewall 정책에 걸릴 수 있다. 그래서 운영 적용 전에는 어떤 FQDN과 포트가 필요한지 목록화하고, 차단 로그를 보면서 정책을 좁혀야 한다.

VMSS는 서버 수가 아니라 운영 정책을 다룬다

VMSS(Virtual Machine Scale Sets)는 동일한 역할의 VM을 묶어서 배포하고 확장하는 서비스다. 단순히 VM을 여러 대 만드는 기능이라기보다, 이미지, 디스크, 네트워크, 업그레이드, 스케일링 정책을 하나로 관리하는 단위에 가깝다.

프런트엔드 VMSS 실습에서는 vmssfridayfe를 North Europe 지역에 만들고, 가용성 영역 1과 2를 사용했다. 기본 인스턴스 수는 1, 최소 1, 최대 5로 두고, CPU가 75%를 넘으면 1대를 늘리고 30% 아래로 내려가면 1대를 줄이는 정책을 설정했다. 공용 Load Balancer lbe-vmssfriday는 TCP 80을 백엔드 80으로 전달했고, RDP 접속을 위해 프런트엔드 포트 범위 10000부터 백엔드 3389로 매핑하는 NAT 규칙도 만들었다.

Front-end VMSS
- name: vmssfridayfe
- region: North Europe
- zones: 1, 2
- image: Windows Server 2022 Datacenter Azure Edition
- size: Standard_DS1_v2
- subnet: snet-friday-fe (192.168.2.0/24)
- autoscale: min 1, max 5
- scale out: CPU > 75%, +1
- scale in: CPU < 30%, -1
- load balancer: TCP 80 -> 80

백엔드 VMSS는 vmssfridaybe로 만들고, Ubuntu Server 22.04 LTS 이미지를 사용했다. 서브넷은 snet-friday-be (192.168.3.0/24)였고, 외부 Load Balancer는 붙이지 않았다. 외부 트래픽을 받는 프런트엔드와 내부에서만 호출되는 백엔드를 같은 방식으로 노출하지 않는다는 점이 중요했다.

Back-end VMSS
- name: vmssfridaybe
- image: Ubuntu Server 22.04 LTS
- subnet: snet-friday-be (192.168.3.0/24)
- load balancer: none
- access: private network / Bastion

VMSS를 구성할 때는 스케일 아웃 조건만큼 스케일 인 조건도 조심해야 한다. 트래픽이 잠깐 줄었다고 오래된 인스턴스를 바로 제거하면 세션, 로그 수집, 배포 중인 작업에 영향을 줄 수 있다. 헬스 체크, graceful shutdown, 로그 전송, 배포 전략까지 같이 봐야 VMSS가 단순한 자동 증설 기능을 넘어 운영 가능한 구조가 된다.

Traffic Manager는 요청을 직접 프록시하지 않는다

Traffic Manager는 DNS 기반 트래픽 분산 서비스다. Application Gateway나 Load Balancer처럼 요청 경로에 직접 들어와 프록시하는 리소스가 아니다. 클라이언트가 어떤 엔드포인트로 갈지 DNS 응답을 통해 유도한다.

이 차이를 이해하면 라우팅 방식을 고르기 쉬워진다.

Priority     active-passive failover
Weighted     canary, blue-green, gradual migration
Performance  latency-based routing
Geographic   country or region based routing
MultiValue   multiple healthy endpoints in DNS response
Subnet       source IP range based routing

예를 들어 신규 버전을 일부 사용자에게만 보내고 싶으면 Weighted가 어울린다. 장애 시 보조 리전으로 넘기는 구조라면 Priority가 단순하다. 여러 지역에 같은 서비스를 두고 사용자 지연 시간을 줄이고 싶다면 Performance를 검토할 수 있다. 다만 DNS 기반이기 때문에 TTL, 클라이언트 캐시, 중간 DNS 캐시의 영향을 받는다. 즉시 전환되는 L7 프록시처럼 기대하면 안 된다.

멀티 리전 구성은 트래픽 전환보다 데이터 경계가 먼저다

Traffic Manager 앞에 두 개 이상의 엔드포인트를 두면 겉으로는 멀티 리전 구성이 완성된 것처럼 보인다. 하지만 실제 장애 전환에서 먼저 깨지는 부분은 대개 라우팅이 아니라 상태와 데이터다. 애플리케이션이 stateless인지, 세션을 어디에 저장하는지, 데이터베이스가 어느 리전에 primary를 두는지, 파일 업로드가 어느 스토리지 계정으로 들어가는지부터 결정해야 한다.

이 구조에서 Web App 두 개를 배포하는 것은 비교적 단순하다. 어려운 지점은 Korea Central에서 정상 처리되던 요청이 East US 2로 넘어갔을 때도 사용자가 같은 데이터를 볼 수 있는가다. 읽기 전용 대시보드라면 복제 지연을 어느 정도 허용할 수 있지만, 결제나 주문처럼 강한 정합성이 필요한 흐름이라면 단순 DNS failover로 해결하기 어렵다.

멀티 리전 설계에서 최소한 아래 항목은 먼저 확인한다.

multi-region readiness

1. application runtime is stateless or session storage is externalized
2. database replication, backup, and failover ownership are defined
3. object storage replication or fallback path is prepared
4. Key Vault secrets and application settings exist in each region
5. health probe path checks real dependencies, not only HTTP 200
6. DNS TTL and client-side caching behavior are understood
7. deployment pipeline can release both regions without manual drift

네트워크 주소도 같은 방식으로 미리 나눠둔다. 리전이 늘어난 뒤에 CIDR이 겹치면 VNet peering, VPN, Private Endpoint 설계가 모두 복잡해진다.

Korea VNet        10.10.0.0/16
  app subnet      10.10.1.0/24
  data subnet     10.10.2.0/24
  gateway subnet  10.10.255.0/27

East US VNet      10.20.0.0/16
  app subnet      10.20.1.0/24
  data subnet     10.20.2.0/24
  gateway subnet  10.20.255.0/27

On-premises       172.16.0.0/16

헬스 체크도 단순 /health 엔드포인트 하나로 끝내지 않는다. 애플리케이션 프로세스가 살아 있어도 데이터베이스 연결, 외부 API 의존성, 큐 적체, 인증 토큰 검증이 실패하면 사용자 입장에서는 장애다. 반대로 모든 의존성을 헬스 체크에 포함하면 작은 외부 장애에도 전체 리전이 빠질 수 있다. 그래서 readiness와 liveness를 분리하고, Traffic Manager가 보는 health probe는 "이 리전으로 실제 사용자 트래픽을 받아도 되는가"에 가까워야 한다.

App Service와 Storage는 네트워크 밖의 간단한 배포가 아니다

VM 기반 웹 서버를 구성한 뒤 App Service와 Storage 기반 이미지 업로드 앱도 배포했다. 리소스 그룹은 rg-myManagedPlatform, Storage Account는 imgstorkty00, 컨테이너는 images로 만들었다. 백엔드 Web App은 imgapikty, 프런트엔드 Web App은 imgwebkty로 구성했다.

초기 실습에서는 빠른 확인을 위해 Storage의 퍼블릭 액세스를 열고 연결 문자열을 App Service 환경변수에 넣었다.

StorageConnectionString=DefaultEndpointsProtocol=https;AccountName=imgstorkty00;AccountKey=...;EndpointSuffix=core.windows.net
ApiUrl=https://imgapikty.azurewebsites.net/

배포는 Azure CLI의 zip deploy를 사용했다.

az login
az group list -o table
az webapp list --resource-group rg-myManagedPlatform -o table

az webapp deployment source config-zip \
  --resource-group rg-myManagedPlatform \
  --src api.zip \
  --name imgapikty

az webapp deployment source config-zip \
  --resource-group rg-myManagedPlatform \
  --src web.zip \
  --name imgwebkty

API 쪽에서는 Blob 컨테이너를 가져오고, 이미지 목록 조회와 업로드를 처리했다. 코드는 단순했지만 운영 관점에서는 연결 문자열을 어디에 두는지, 업로드 파일 크기를 어떻게 제한하는지, Storage 접근을 public으로 둘지 private endpoint로 제한할지, App Service의 outbound 통신을 어떻게 제어할지가 더 중요했다.

[HttpGet]
public async Task<IEnumerable<string>> Get()
{
    var container = GetCloudBlobContainer();
    var blobs = await container.ListBlobsSegmentedAsync(null);
    return blobs.Results.Select(blob => blob.Uri.ToString());
}

[HttpPost]
public async Task<IActionResult> Post(IFormFile file)
{
    var container = GetCloudBlobContainer();
    var blob = container.GetBlockBlobReference(file.FileName);
    await blob.UploadFromStreamAsync(file.OpenReadStream());
    return Ok(blob.Uri.ToString());
}

이 구성을 VM 기반 구조와 분리해서 보면 “관리형 서비스에 배포했다” 정도로 끝난다. 하지만 실제로는 같은 질문을 다시 해야 한다. 사용자는 어디로 들어오는가, API는 어디까지 노출되는가, Blob은 누가 읽고 쓸 수 있는가, 장애가 났을 때 어느 로그를 먼저 볼 것인가. App Service를 쓰더라도 네트워크와 권한 모델이 사라지는 것은 아니다.

관측은 마지막에 붙이는 기능이 아니다

네트워크와 고가용성 구성을 만들면서 자주 느낀 점은, 장애 분석의 시작점이 리소스 화면이 아니라 로그와 상태라는 것이다. Application Gateway는 backend health를 확인해야 하고, Load Balancer는 probe 상태를 봐야 한다. NSG는 Flow Logs나 진단 로그를 봐야 하며, VM은 /var/log/apache2/access.log, Windows Event Log, 서비스 상태를 같이 봐야 한다.

Azure Firewall이나 NSG 로그는 Log Analytics로 보내 KQL로 확인할 수 있다.

AzureDiagnostics
| where Category == "AzureFirewallNetworkRule"
| project TimeGenerated, msg_s, action_s, protocol_s
| order by TimeGenerated desc
| take 10

Application Gateway나 App Service도 진단 설정을 통해 Access Log, Performance Log, Firewall Log를 남길 수 있다. 운영 중인 시스템에서는 “무엇을 만들었는가”보다 “문제가 생겼을 때 어디서부터 확인할 수 있는가”가 더 오래 남는다.

정리

Azure의 보안 네트워크와 고가용성 구성을 하나씩 따라가며 가장 크게 바뀐 관점은, 리소스를 개별 제품으로 보지 않게 된 것이다. VNet은 주소 공간이 아니라 경계이고, NSG는 포트 목록이 아니라 통신 의도이며, Bastion은 접속 편의 기능이 아니라 관리 경로를 분리하는 장치다.

Application Gateway, Internal Load Balancer, VMSS, Traffic Manager, App Service, Storage도 마찬가지다. 각각은 따로 배울 수 있지만, 서비스 요청이 들어오고 처리되고 저장되고 관측되는 흐름 안에 놓였을 때 의미가 선명해진다.

다음에 같은 구조를 다시 설계한다면 먼저 리소스를 만들기보다 요청 경로를 그릴 것이다. 사용자의 진입점, 관리자의 진입점, 내부 서비스 간 호출, 데이터 저장소 접근, 아웃바운드 통신, 로그 수집 경로를 먼저 나누고 나서 Azure 리소스를 배치하는 편이 훨씬 덜 흔들린다.

이 글의 무단 복제, 재배포, 자동 수집을 허용하지 않습니다. 인용이 필요한 경우 출처와 원문 링크를 함께 남겨주세요.