저자 주 – 이 블로그 게시물은 시리즈의 여섯 번째 글입니다:
- NGINX의 마이크로서비스 참조 아키텍처 소개
- MRA, 2부: 프록시 모델
- MRA, 3부: 라우터 메시 모델
- MRA, 4부: 패브릭 모델
- MRA, 5부: 마이크로서비스에 맞게 12요소 앱 조정하기
- MRA, 6부: NGINX Plus로 서킷 브레이커 패턴 구현하기(이 게시물)
6편의 블로그 글과 마이크로서비스 애플리케이션용 웹 프런트엔드<.htmla>에 관한 블로그 글을 무료 전자책 한 권으로 엮었습니다.
마이크로서비스에 관한 다음 NGINX 자료도 확인해 보세요:
- Chris Richardson이 집필한 매우 유용하고 인기 있는 시리즈: 마이크로서비스 애플리케이션 설계
- Chris Richardson의 글을 한데 모은 무료 전자책으로, NGINX 및 NGINX Plus를 사용해 마이크로서비스를 구현하는 데 유용한 추가 팁도 제공합니다.
- 기타 마이크로서비스 블로그 게시물
- 마이크로서비스 웨비나
마이크로서비스 애플리케이션 설계는 애플리케이션의 작동 방식을 완전히 바꾸어 놓았습니다. 마이크로서비스 아키텍처에서 이제 ‘애플리케이션’은 작업을 수행하고 기능을 제공하기 위해 서로 의존하는 서비스들의 집합입니다. 복잡한 애플리케이션에서는 서비스 그래프가 상당히 깊어질 수 있으며, 다양한 서비스 간에 여러 상호 의존 관계가 존재할 수 있습니다.
예를 들어 사용자 서비스는 해당 서비스가 제공하는 데이터에 의존하는 여러 다른 서비스에서 핵심적인 역할을 할 수 있습니다. 이 경우 사용자 서비스에 장애가 발생하면 애플리케이션 전체에 연쇄적인 장애가 발생할 수 있습니다.
The 서킷 브레이커 패턴은 Martin Fowler가 널리 알린 용어로, 연쇄적인 서비스 장애를 방지하기 위한 애플리케이션 설계 패턴으로서 마이크로서비스 아키텍트들 사이에서 점차 주목받고 있습니다. 서킷 브레이커 패턴의 핵심은 장애를 방지하기 위해 애플리케이션 서비스와 서비스 간에 흐르는 트래픽을 모니터링하고, 장애가 발생하더라도 애플리케이션에 미치는 영향을 최소화하는 것입니다.
마이크로서비스에서 서킷 브레이커 패턴은 상향식 복원력을 제공한다는 점에서 특히 유용합니다. 올바르게 구현하면 서비스가 가용하지 않은 상황에서도 서비스 연속성을 보장하여 연쇄 장애를 방지하는 데 도움이 됩니다. 서킷 브레이커 패턴은 Netflix가 가장 대표적으로 도입했으며 애플리케이션 설계 철학의 핵심 요소로 활용하고 있습니다.
실패를 피하지 말고 받아들이세요
현대적인 애플리케이션 설계의 핵심 원칙 중 하나는 장애가 발생할 수밖에 없다는 것입니다. 클라우드에서 호스팅되는 가상 머신부터 컨테이너, 애플리케이션 라이브러리, 동적 네트워킹에 이르기까지 현대적인 애플리케이션이 의존하는 계층 구조를 고려하면, 모든 애플리케이션에는 수많은 가변 요소가 존재합니다. 따라서 언젠가는 애플리케이션의 하나 이상의 구성 요소에 어떤 형태로든 장애가 발생할 것이라고 가정해야 합니다. 장애를 예상하고 그 영향을 완화할 수 있는 메커니즘을 구축하면 애플리케이션의 복원력을 크게 높일 수 있습니다.
회로 차단기 패턴의 가장 중요한 목표 중 하나는 장애가 발생하기 전에 이를 방지하는 것입니다. 메모리 부족과 같은 일부 오류 상황에서는 장애가 임박했음을 감지하고 이를 예방하기 위한 조치를 취할 수 있습니다. 일반적으로 서비스가 비정상 상태임을 알리면 회로 차단기가 요청 수를 제한하거나 요청을 완전히 다른 곳으로 라우팅하여 서비스가 복구할 기회를 주는 방식으로 이루어집니다. 서비스가 복구된 후에는 즉시 과부하가 걸려 다시 비정상 상태가 되는 것을 방지하기 위해 회로 차단기가 해당 서비스로 보내는 요청을 서서히 늘리는 것이 바람직합니다.
In the NGINX 마이크로서비스 참조 아키텍처에는 리사이저라는 서비스가 있습니다. 대용량 사진이 시스템에 업로드되면 리사이저는 사진의 압축을 풀고 회전을 보정한 다음, 두 가지 크기로 축소하여 보정된 원본 이미지와 크기가 조정된 이미지 두 개를 객체 스토리지에 저장합니다. 이러한 처리 특성으로 인해 리사이저는 애플리케이션에서 프로세서와 메모리를 가장 많이 사용하는 부분입니다.
여러 이미지의 크기를 동시에 조정하면 리사이저의 메모리가 부족해질 수 있으며, 경우에 따라 완전히 작동을 멈출 수도 있습니다. 이러한 문제를 방지하기 위해 리사이저 서비스 인스턴스와 이미지를 전달하는 업로더 서비스 사이에 서킷 브레이커를 배치했습니다. 업로더는 리사이저 인스턴스의 상태를 정기적으로 조회합니다. 이 조회를 받으면 리사이저는 여러 상태 점검 항목 중 가용 메모리의 80%를 초과해 사용했는지 등을 평가한 후, 업로더에 상태를 응답합니다.
리사이저 인스턴스가 비정상 상태임을 나타내면 업로더는 그림 1과 같이 요청을 다른 인스턴스로 라우팅하는 한편, 해당 리사이저 인스턴스가 복구되었는지 계속 확인합니다. 리사이저 인스턴스가 다시 정상 상태임을 나타내면 로드 밸런싱 풀에 다시 추가되고, 업로더는 트래픽을 서서히 늘려 해당 인스턴스의 최대 용량에 도달하도록 합니다. 이 설계는 리사이저 인스턴스가 완전히 중단되는 것을 방지하고, 작업이 시작되었지만 완료되지 않는 상황을 막으며, 그렇지 않았다면 프로세스 실패를 겪었을 사용자가 지나치게 오래 기다리는 일을 방지하고, 시스템이 유입되는 요청 스트림을 최대한 효과적으로 처리할 수 있도록 지원합니다.

서킷 브레이커 패턴으로 일관성 향상
NGINX 수준에서 서킷 브레이커를 구현하면 마이크로서비스 애플리케이션 전반의 서킷 브레이커를 관리할 수 있는 범용적이고 일관되며 매우 유연한 계층을 구축할 수 있다는 이점이 있습니다. 이러한 범용성과 일관성 덕분에 언어별 서킷 브레이커 라이브러리의 미묘한 차이와 비일관성을 일일이 관리하거나 이에 맞춰 시스템을 구축할 필요가 없습니다.
대부분의 회로 차단기 기능을 각 서비스의 코드에서 분리하고 대신 NGINX Plus에서 구현하면 다음과 같은 여러 이점을 얻을 수 있습니다:
- 예를 들어 Java로 작성된 서비스의 회로 차단기는 PHP로 작성된 서비스의 회로 차단기와 동일하며, 필요에 따라 회로 차단기 자체는 또 다른 언어로 작성할 수도 있습니다.
- 각 서비스에서 사용하는 다양한 언어와 지원 라이브러리마다 서킷 브레이커 기능을 다시 구현할 필요가 없습니다
- 따라서 회로 차단기 코드를 포함할 필요가 없는 각 서비스는 더 단순해지며, 더 빠르게 실행되고 작성, 디버깅, 실행 및 유지 관리도 더 쉬워집니다.
- 각 서비스의 지원 코드가 간소화되므로, 사용하는 라이브러리와 시스템의 조합은 해당 서비스의 핵심 기능만을 반영할 수 있습니다.
- 서킷 브레이커 코드는 단순해집니다. 한 곳에만 존재하므로 각 로컬 환경을 고려할 필요 없이 핵심 요소만 남길 수 있습니다.
- 서킷 브레이커 코드는 캐싱과 같은 NGINX Plus 기능을 활용할 수 있어 훨씬 더 강력해집니다.
- NGINX Plus 수준의 회로 차단기 코드를 세밀하게 조정한 후 다른 애플리케이션과 온프레미스, 다양한 클라우드 플랫폼, 혼합 환경 등의 여러 배포 플랫폼에서 재사용할 수 있습니다
하지만 NGINX Plus만으로는 회로 차단기를 구현할 수 없다는 점에 유의해야 합니다. 진정한 회로 차단기를 구현하려면 서비스가 지정된 URI(일반적으로 /health)에서 자체 상태를 점검하는 능동형 상태 확인 기능을 제공해야 합니다. 상태 확인은 해당 서비스의 구체적인 요구 사항에 적합해야 합니다.
상태 확인을 개발할 때는 서비스의 장애 특성과 데이터베이스 연결 실패, 메모리 부족, 디스크 공간 부족, CPU 과부하 등 장애를 일으킬 수 있는 조건의 유형을 파악해야 합니다. 이러한 조건은 상태 확인 프로세스에서 평가되며, 그 결과 정상 또는 비정상이라는 이진 상태가 제공됩니다.
서킷 브레이커 패턴이 제공하는 유연성
여기에서 설명한 대로 NGINX 수준에서 회로 차단기 패턴을 구현하면, 서비스 인스턴스가 비정상 상태임을 알릴 때 해당 상황을 처리하는 것은 NGINX Plus의 역할입니다. 이때 선택할 수 있는 방법은 여러 가지입니다.
첫 번째 옵션은 요청을 정상 상태인 다른 인스턴스로 리디렉션하는 동시에 비정상 인스턴스가 복구되었는지 계속 확인하는 것입니다. 두 번째 옵션은 서비스를 요청하는 클라이언트에 캐시된 응답을 제공하여 서비스가 제공되지 않는 경우에도 안정성을 유지하는 것입니다. 이 솔루션은 콘텐츠 서비스와 같이 읽기 중심의 서비스에 적합합니다.
또 다른 방법은 대체 데이터 소스를 제공하는 것입니다. 예를 들어, 당사의 한 고객은 프로필 데이터를 사용하여 사용자에게 맞춤형 광고를 제공하는 개인화 광고 서버를 운영하고 있습니다. 개인화 광고 서버가 중단되면 사용자 요청은 모든 사람에게 적합한 일반 광고 세트를 제공하는 백업 서버로 리디렉션됩니다. 이러한 대체 데이터 소스 접근 방식은 매우 효과적일 수 있습니다.
마지막으로, 서비스의 장애 특성을 매우 명확하게 파악하고 있다면 서킷 브레이커에 속도 제한을 추가하여 장애를 완화할 수 있습니다. 요청은 서비스가 처리할 수 있는 속도로만 전달됩니다. 이렇게 하면 서킷 브레이커 내부에 버퍼가 생겨 트래픽 급증을 흡수할 수 있습니다.
속도 제한은 라우터 메시 모델과 같은 중앙 집중식 부하 분산 환경에서 특히 강력한 효과를 발휘할 수 있습니다. 이러한 환경에서는 애플리케이션 트래픽이 소수의 부하 분산 장치를 통해 라우팅되므로 사이트 전체의 트래픽 사용량을 정확히 파악할 수 있습니다.
NGINX Plus에서 회로 차단기 패턴 구현
앞서 설명했듯이, 서킷 브레이커 패턴은 비정상 서비스로 향하는 트래픽을 줄이거나 요청을 다른 곳으로 라우팅하여 장애가 발생하기 전에 이를 방지할 수 있습니다. 이를 위해서는 각 서비스의 내부 상태를 점검하는 상태 모니터에 연결된 능동 상태 확인이 필요합니다. 안타깝게도 수동 상태 확인은 장애 발생 여부만 확인하므로 이 역할을 수행할 수 없습니다. 장애가 감지된 시점에는 이미 예방 조치를 취하기에 너무 늦기 때문입니다. 이러한 이유로 NGINX Open Source는 수동 상태 확인만 지원하므로 서킷 브레이커 패턴을 구현할 수 없습니다.
하지만 NGINX Plus는 상태 문제를 확인하고 대응하기 위한 다양한 옵션을 갖춘 강력한 능동 상태 점검 시스템을 제공합니다. 마이크로서비스 참조 아키텍처의 일부 서비스 유형 구현을 살펴보면 회로 차단기를 구현할 때 활용할 수 있는 옵션과 사용 사례를 잘 이해할 수 있습니다.
먼저 리사이저에 연결되는 업로더 서비스부터 살펴보겠습니다. 업로더는 이미지를 객체 저장소에 저장한 다음, 리사이저에 이미지를 열어 보정하고 크기를 조정하도록 지시합니다. 이 작업에는 많은 컴퓨팅 리소스와 메모리가 필요합니다. 리사이저가 실행 중인 호스트를 실제로 중단시킬 수도 있으므로, 업로더는 리사이저의 상태를 모니터링하고 과부하가 걸리지 않도록 해야 합니다.
먼저 리사이저 상태 확인 전용 location 블록을 생성합니다. 이 블록은 internal 위치이므로 서버의 표준 URL( http://example.com/health-check-resizer)로 요청을 보내 접근할 수 없습니다. 대신 상태 확인 정보를 위한 자리 표시자 역할을 합니다. health_check 지시문은 3초마다 /health URI로 상태 확인 요청을 보내고, conditions라는 match 블록에 정의된 테스트를 사용하여 서비스 인스턴스의 상태를 확인합니다. 서비스 인스턴스가 상태 확인에 한 번이라도 응답하지 않으면 비정상 상태로 표시됩니다. proxy_* 지시문은 지정된 HTTP 헤더를 null로 설정하고 HTTP 1.1 기반 TLS 1.2를 사용하여 resizer 업스트림 그룹으로 상태 확인 요청을 보냅니다.
location /health-check-resizer { internal;
health_check uri=/health match=conditions fails=1 interval=3s;
proxy_pass https://resizer;
proxy_ssl_session_reuse on;
proxy_ssl_protocols TLSv1.2;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header Accept-Encoding "";
}다음 단계는 정상 및 비정상 상태를 나타내는 응답을 지정하기 위해 conditions match 블록을 만드는 것입니다. 첫 번째로 응답 상태 코드를 확인합니다. 상태 코드가 200에서 399 사이이면 다음 평가 문으로 진행합니다. 두 번째로 Content-Type이 application/json인지 확인합니다. 마지막으로 세 번째는 deadlocks, Disk, Memory 메트릭 값이 정규식과 일치하는지 확인합니다. 이 메트릭이 모두 정상이면 서비스가 정상인 것으로 판단합니다.
match conditions { status 200-399;
header Content-Type ~ "application/json";
body ~ '{
"deadlocks":{"healthy":true},
"Disk":{"healthy":true},
"Memory":{"healthy":true}
}';
}NGINX Plus 회로 차단기/상태 확인 시스템에는 느린 시작 기능도 있습니다. slow_start 매개변수는 server 지시문에서 크기 조정 서비스에 사용되며, upstream 블록 내에서 크기 조정 인스턴스가 비정상 상태에서 처음 복구될 때 NGINX Plus가 트래픽 흐름을 조절하도록 합니다. 정상 서비스에 전송되는 것과 동일한 수의 요청을 해당 서비스에 한꺼번에 보내는 대신, 복구 중인 서비스로 향하는 트래픽을 slow_start 매개변수로 지정한 기간(이 경우 30초)에 걸쳐 정상 속도까지 서서히 늘립니다. 느린 시작은 서비스가 완전한 처리 능력을 회복할 가능성을 높이는 동시에, 복구하지 못할 경우의 영향을 줄여 줍니다.
upstream resizer { server resizer slow_start=30s;
zone backend 64k;
least_time last_byte;
keepalive 300;
}요청 제한은 서비스로 유입되는 요청 흐름을 관리하고 조절합니다. 애플리케이션의 장애 특성을 충분히 파악하여 특정 시점에 처리할 수 있는 요청 수를 알고 있다면, 요청 제한을 구현하는 것이 프로세스에 큰 도움이 될 수 있습니다. 하지만 이 기능이 제대로 작동하려면 NGINX Plus가 서비스로 전달되는 전체 연결 수를 완전히 파악하고 있어야 합니다. 따라서 Fabric 모델처럼 서비스와 함께 컨테이너에서 실행되는 NGINX Plus 인스턴스나 클러스터의 모든 트래픽을 관리하는 중앙 집중식 로드 밸런서에 요청 제한 회로 차단기를 구현하는 것이 가장 효과적입니다.
다음 구성 코드 조각은 컨테이너에서 실행되는 리사이저 서비스 인스턴스에 적용할 요청 속도 제한을 정의합니다. limit_req_zone 지시문은 속도 제한을 초당 100개 요청으로 정의합니다. $server_addr 변수가 키로 사용되므로 리사이저 컨테이너로 들어오는 모든 요청이 제한에 포함됩니다. 영역 이름은 moderateReqs이며 요청 수를 유지하는 기간은 1분입니다. limit_req 지시문을 사용하면 NGINX Plus가 최대 150개의 요청 폭주를 버퍼링할 수 있습니다. 이 수를 초과하면 클라이언트는 503 오류 코드를 수신합니다. 이는 limit_req_status 지시문에 지정된 것으로, 서비스를 사용할 수 없음을 나타냅니다.
http { # Moderated delivery
limit_req_zone $server_addr zone=moderateReqs:1m rate=100r/s;
# ...
server {
# ...
limit_req zone=moderateReqs burst=150;
limit_req_status 503;
# ...
}
}NGINX Plus 내에서 회로 차단기를 실행할 때 얻을 수 있는 또 다른 강력한 이점은 캐싱을 통합하고 캐시된 데이터를 중앙에서 관리하여 시스템 전반에서 사용할 수 있다는 것입니다. 이는 백엔드에서 읽어 오는 데이터가 자주 변경되지 않는 콘텐츠 서버와 같은 읽기 중심 서비스에 특히 유용합니다.
proxy_cache_path /app/cache levels=1:2 keys_zone=oauth_cache:10m max_size=10m inactive=15s use_temp_path=off;
upstream user-manager {
server user-manager;
zone backend 64k;
least_time last_byte;
keepalive 300;
}
server {
listen 443 ssl;
location /v1/users {
proxy_pass http://user-manager;
proxy_cache oauth_cache;
proxy_cache_valid 200 30s;
proxy_cache_use_stale error timeout invalid_header updating
http_500 http_502 http_503 http_504;
}
}그림 2에서 볼 수 있듯이, 데이터를 캐싱하면 많은 고객 데이터 요청이 마이크로서비스 인스턴스에 도달하지 않으므로 이전에 수신되지 않은 요청을 처리할 수 있는 용량이 확보됩니다.

하지만 사용자 관리 서비스처럼 데이터가 변경될 수 있는 서비스에서는 캐시를 신중하게 관리해야 합니다. 그렇지 않으면 사용자가 자신의 프로필을 변경했는데도 데이터가 캐시되어 있어 일부 상황에서 이전 데이터가 표시될 수 있습니다. 적절한 제한 시간을 설정하고 최종적 일관성을 기반으로 한 고가용성 원칙을 수용하면 이러한 문제를 해결할 수 있습니다.
NGINX 캐시의 유용한 기능 중 하나는 서비스가 완전히 중단된 경우에도 캐시된 데이터를 계속 제공할 수 있다는 것입니다. 위의 코드 조각에서는 서비스가 가장 일반적인 네 가지 500번대 오류 코드 중 하나로 응답하는 경우가 이에 해당합니다.
서버가 다운된 상황에서도 클라이언트에 응답하는 방법은 캐싱만 있는 것이 아닙니다. 서킷 브레이커 패턴이 제공하는 유연성에서 언급했듯이, 한 고객은 개인 맞춤형 광고 서버가 다운될 경우에도 서비스를 지속할 수 있는 복원력 있는 솔루션이 필요했으며, 캐시된 응답은 적합한 해결책이 아니었습니다. 대신 개인 맞춤형 서버가 다시 온라인 상태가 될 때까지 일반 광고 서버에서 범용 광고를 제공하기를 원했습니다. 이는 backup 매개변수를 server 지시문에 사용하면 쉽게 구현할 수 있습니다. 다음 코드 조각은 personal-ad-server 도메인에 정의된 모든 서버를 사용할 수 없을 때 대신 generic-ad-server 도메인에 정의된 서버를 사용하도록 지정합니다.
upstream personal-ad-server { server personal-ad-server;
server generic-ad-server backup;
zone backend 64k;
least_time last_byte;
keepalive 300;
}마지막으로 NGINX가 서비스의 응답 코드를 평가하여 각 코드에 개별적으로 대응하도록 할 수 있습니다. 다음 코드 조각에서는 서비스가 503 오류를 반환하면 NGINX Plus가 요청을 대체 서비스로 전달합니다. 예를 들어 리사이저에 이 기능이 있고 로컬 인스턴스가 과부하 상태이거나 작동을 멈추면 요청이 리사이저의 다른 인스턴스로 전송됩니다.
location / { error_page 503 = @fallback;
}
location @fallback {
proxy_pass http://alternative-backend;
}결론
서킷 브레이커 패턴은 마이크로서비스 애플리케이션의 복원력과 제어 능력을 강화하는 강력한 도구입니다. NGINX Plus는 환경에 서킷 브레이커를 구현할 수 있도록 다양한 기능과 옵션을 제공합니다. 서킷 브레이커 패턴을 구현할 때는 보호하려는 서비스의 장애 특성을 파악한 다음, 가능한 경우 장애를 최대한 예방하고 장애 발생 시 그 영향을 가장 효과적으로 완화할 수 있는 옵션을 선택하는 것이 중요합니다.
NGINX Plus를 사용해 보려면 지금 30일 무료 평가판을 시작하거나 사용 사례에 대해 논의하려면 문의해 주세요.
관련 블로그 게시물
시청하기: KubeCon North America 2023에서의 NGINX Gateway Fabric
이 영상에서는 KubeCon의 NGINX 부스를 찾아가, 저희가 공개한 Kubernetes Gateway API 구현을 살펴봅니다.
F5 NGINX로 대규모 WAF 플릿을 확인하고 관리하는 2가지 방법
앱 보안을 강화하는 두 가지 방법을 소개합니다. NGINX Management Suite를 통해 WAF 운영에 대한 가시성을 높이고, NGINX App Protect WAF를 활용해 코드형 보안을 구현하는 것입니다.
Ingress 컨트롤러 선택 가이드, 4부: NGINX Ingress Controller 옵션
개발 주체, 개발 철학, 프로덕션 환경 준비도, 보안 및 지원을 기준으로 어떤 NGINX Ingress 컨트롤러가 가장 적합한지 알아보세요.
진화하는 공격 환경에 NGINX App Protect Denial of Service가 적응하는 방법
NGINX App Protect Denial of Service(DoS)가 계층 7 DoS 공격을 방어하기 위해 완화 방법을 실시간으로 조정하는 방식을 설명합니다. 정상적인 사용자 행동과 서버 상태에 대한 모델을 생성하여 공격을 나타내는 이상 징후를 감지하고 맞춤형 완화 시그니처를 적용할 수 있습니다.
NGINX Instance Manager를 활용한 대규모 구성 업데이트 및 인증서 관리
NGINX Instance Manager 버전 1.0에는 두 가지 새로운 기능이 도입되었습니다. NGINX 인스턴스에 태그와 사용자 역할을 지정하면 전체 그룹에 구성을 한 번에 적용할 수 있습니다. 인증서 검사는 만료 예정인 인증서를 감지하여 서비스 중단을 방지할 수 있도록 인증서를 교체할 수 있게 해줍니다.
Netflix가 자사 CDN의 핵심으로 NGINX를 선택한 이유
Netflix가 글로벌 콘텐츠 전송 네트워크인 Open Connect의 스트리밍 미디어 기술로 NGINX를 선택한 이유를 NGINX 개발자 Gleb Smirnoff에게서 알아보세요.