API Gateway를 사용한 Fallback 구현
November 10, 2023
API 복원력은 API가 오류, 높은 트래픽 또는 부분적인 시스템 장애에 직면했을 때 빠르게 실패하거나 실패 후에도 계속 작동할 수 있는 능력을 말합니다. 이는 재시도, 타임아웃, 서킷 브레이커, 페일오버, 폴백과 같은 일반적인 API 복원력 설계 패턴을 구현하는 것을 포함합니다. API Gateway를 사용한 폴백은 API의 대체 계획입니다. 기본 API 서비스가 실패할 때 API Gateway는 트래픽을 보조 서비스로 리디렉션하거나 미리 정의된 응답을 반환할 수 있습니다. 이 글에서는 기존 폴백 기술의 문제점과 APISIX API Gateway를 사용하여 이를 효율적으로 구현하는 방법을 탐구해 보겠습니다.
APISIX로 폴백 구현하기
APISIX로 폴백 메커니즘을 구현하려면 내장된 업스트림 우선순위 기능을 사용하거나 response-rewrite 플러그인을 사용하여 서비스 호출이 실패할 때 미리 정의된 응답을 반환할 수 있습니다. 다음은 두 가지 폴백 방법을 설정하는 단계별 예제입니다.
전제 조건
이 가이드는 다음과 같은 도구가 로컬에 설치되어 있다고 가정합니다:
- 시작하기 전에 APISIX에 대한 기본적인 이해가 있으면 좋습니다. API 게이트웨이 및 라우트, 업스트림, Admin API, 플러그인, HTTP 프로토콜과 같은 주요 개념에 익숙하면 도움이 됩니다.
- Docker는 컨테이너화된 etcd와 APISIX를 설치하는 데 사용됩니다.
- 서비스에 요청을 보내어 검증하기 위해 cURL을 설치합니다.
APISIX Docker 프로젝트 시작하기
이 프로젝트는 기존의 사전 정의된 Docker Compose 구성 파일을 활용하여 APISIX, etcd, Prometheus 및 기타 서비스를 단일 명령으로 설정, 배포 및 실행합니다. 먼저 GitHub에서 apisix-docker 리포지토리를 클론하고, 선호하는 편집기에서 열고, example
폴더로 이동한 후 프로젝트 루트 폴더에서 터미널에 docker compose up
명령을 실행하여 프로젝트를 시작합니다.
프로젝트를 시작하면 Docker는 실행에 필요한 이미지를 다운로드합니다. 또한 두 개의 예제 백엔드 서비스 web1
과 web2
를 실행합니다. docker-compose.yaml 파일에서 전체 서비스 목록을 확인할 수 있습니다.
APISIX 업스트림 우선순위를 사용한 폴백
각 업스트림 노드에 특정 수준의 우선순위를 설정하여 활성화할 수 있습니다. 더 높은 우선순위의 노드 엔드포인트가 실패하면 API Gateway는 더 낮은 우선순위의 보조 노드로 트래픽을 리디렉션할 수 있습니다. 모든 노드의 기본 우선순위는 0이며, 음수 우선순위를 가진 노드는 백업으로 구성할 수 있습니다.
두 서비스에 대한 라우트를 생성하고 각 업스트림 서비스 노드에 우선순위 속성을 구성합니다:
curl "http://127.0.0.1:9180/apisix/admin/routes" -H "X-API-KEY: edd1c9f034335f136f87ad84b625c8f1" -X PUT -d '
{
"id":"backend-service-route",
"methods":[
"GET"
],
"uri":"/get",
"upstream":{
"nodes":[
{
"host":"web1",
"port":80,
"weight":1,
"priority":0
},
{
"host":"web2",
"port":80,
"weight":1,
"priority":-1
}
]
}
}'
methods
: 이 라우트가 매칭할 HTTP 메서드를 지정합니다. 이 경우GET
요청에 매칭됩니다.uri
: 이 라우트가 매칭할 경로입니다. 따라서/get
에 대한GET
요청은 이 라우트에 의해 처리됩니다.nodes
: 백엔드 서버의 배열입니다. 배열의 각 객체는host
,port
,weight
를 가진 서버를 나타냅니다.weight
는 로드 밸런싱에 사용됩니다. 이 경우 두 서버 모두1
의 가중치를 가지므로 일반적으로 트래픽을 균등하게 공유합니다.priority
: 두 노드(web1
,web2
)에 대한 추가 구성입니다.priority
필드는 노드가 선택되는 순서를 결정하는 데 사용됩니다. 더 낮은 우선순위(더 높은 음수)를 가진 노드는 더 높은 우선순위(더 낮은 음수 또는 양수)를 가진 노드가 모두 사용 불가능할 때만 사용됩니다.
라우트에 요청을 보내면 web1
서비스에서만 응답을 받는지 확인합니다:
curl "http://127.0.0.1:9080/get"
다음과 유사한 응답이 표시됩니다:
hello web1
이는 web1
이 작동 중이므로 먼저 실행되었음을 의미합니다. 이제 web1
서비스 컨테이너를 중지하여 APISIX가 web2
서비스로 폴백하는지 확인합니다.
docker container stop example-web1-1
이제 다시 라우트에 요청을 보내면 지정한 폴백 서비스에서 응답을 받게 됩니다.
curl "http://127.0.0.1:9080/get"
hello web2
기본적으로 요청이 서비스 1로 먼저 가고 사용 불가능할 경우 서비스 2로 폴백하는 데 60초가 걸립니다. Upstream 객체의 timeout
속성을 설정하여 이 시간을 변경할 수도 있습니다. 또 다른 폴백 전략은 릴리스 중에 사용할 수 있습니다. API의 새 버전에 버그가 있는 경우 APISIX의 트래픽 분할 기능을 사용하여 대기 중인 이전 버전으로 트래픽을 라우팅할 수 있습니다. 새 버전에 문제가 있는 경우 이전 버전으로 폴백합니다. 이 폴백 방법은 업스트림 상태 확인과도 잘 작동합니다.
APISIX 응답 재작성 플러그인을 사용한 폴백
APISIX response-rewrite 플러그인을 사용하면 클라이언트에게 반환하기 전에 응답 상태 코드, 본문 및 헤더를 수정할 수 있습니다. 이는 업스트림 서비스가 실패할 때 기본 응답을 제공하여 폴백 메커니즘을 구현하는 데 특히 유용합니다.
첫 번째 접근 방식을 따랐다면 Docker에서 web1
서비스 컨테이너를 다시 실행합니다:
docker container start example-web1-1
폴백을 위해 response-rewrite 플러그인을 사용하려면 라우트에서 이를 구성해야 합니다. 다음은 curl
명령을 사용하여 플러그인을 활성화하는 예제입니다:
curl "http://127.0.0.1:9180/apisix/admin/routes" -H "X-API-KEY: edd1c9f034335f136f87ad84b625c8f1" -X PUT -d '
{
"id":"backend-service-route",
"methods":[
"GET"
],
"uri":"/get",
"plugins":{
"response-rewrite":{
"status_code":200,
"body":"{\"message\":\"This is a fallback response when the service is unavailable\"}",
"vars":[
[
"status",
"==",
503
]
]
}
},
"upstream":{
"nodes":{
"web1:80":1
}
}
}'
위 예제에서 우리는 이 라우트가 매칭될 때 트래픽이 전달되어야 하는 단일 백엔드 서비스(web1:80
)를 정의했습니다. 업스트림 서비스(web1:80
)가 503 Service Unavailable
상태로 응답하면 response-rewrite
플러그인은 응답을 200 OK
상태와 사용자 정의 JSON 본문으로 수정합니다. 이는 업스트림 서비스가 사용 불가능할 때 폴백 응답을 효과적으로 생성합니다.
"vars": [["status", "==", 503]]
: 이 조건은 플러그인이 응답의 원래 상태 코드가503 Service Unavailable
일 때만 재작성을 적용하도록 지시합니다.
이제 라우트에 요청을 보내면 수정된 응답을 받아야 합니다:
curl "http://127.0.0.1:9080/get"
{"message":"This is a fallback response when the service is unavailable"}
폴백 메커니즘 구현의 문제점
폴백은 복원력 있는 시스템 설계의 중요한 구성 요소입니다. 그러나 잘못 구현되면 더 많은 문제를 초래할 수 있습니다. 폴백 전략을 논의할 때, 단일 머신 환경과 분산 시스템 간에 직면하는 문제점이 다를 수 있습니다. APISIX를 사용하여 이를 피하는 방법을 예제와 함께 살펴보겠습니다.
폴백 로직 테스트의 어려움
단일 머신 환경에서 데이터베이스 장애와 같은 애플리케이션 실패 조건을 정확하게 시뮬레이션하기는 어렵습니다. 분산 시스템에서 폴백 전략을 테스트하는 것은 여러 머신과 서비스가 관여하기 때문에 더 복잡해져 모든 가능한 실패 모드를 복제하기 어렵습니다. 예를 들어, 로컬 서버의 API는 데이터베이스에 접근할 수 없을 때 캐시된 응답으로 폴백합니다. 이 시나리오를 테스트하려면 데이터베이스 다운타임을 시뮬레이션해야 하는데, 이는 정기적인 테스트의 일부가 아닐 수 있어 실제 프로덕션 부하에서 폴백 코드가 테스트되지 않을 수 있습니다.
APISIX는 폴백 조건을 포함한 다양한 시나리오를 시뮬레이션하기 위해 트래픽을 라우팅하도록 구성할 수 있습니다. 이를 통해 폴백 로직을 보다 현실적으로 테스트할 수 있으며, 폴백 서비스가 프로덕션 트래픽을 처리할 수 있는지 확인할 수 있습니다.
폴백 자체가 실패할 수 있음
폴백 솔루션이 예상만큼 복원력이 없다면, 폴백이 작동할 때 증가하는 부하에 실패하여 연쇄적인 실패를 초래할 수 있습니다. 또한, 덜 효율적인 서비스로의 폴백은 응답 시간과 부하를 증가시켜 시스템 전체의 느려짐 또는 중단을 초래할 수 있습니다. 예를 들어, API는 원격 로깅 서비스가 사용 불가능할 때 로컬 파일 시스템에 로그를 기록하도록 폴백할 수 있습니다. 이는 동기식 파일 I/O 작업으로 인해 성능이 느려질 수 있습니다.
APISIX를 사용하면 중요한 요청이 먼저 처리되도록 트래픽을 우선순위화할 수 있습니다. 이는 폴백 서비스가 과부하되지 않도록 하여 시스템 성능을 악화시키는 것을 방지할 수 있습니다.
폴백에는 운영상의 위험이 있음
폴백을 구현하면 보조 데이터베이스와 같은 새로운 실패 지점이 도입될 수 있으며, 이는 기본 데이터베이스와 동기화되지 않아 데이터 불일치를 초래할 수 있습니다.
APISIX의 로깅, 메트릭, 추적과 같은 관찰 기능은 기본 및 폴백 서비스의 상태와 성능을 모니터링할 수 있습니다. 이 실시간 모니터링은 폴백 전략과 관련된 위험을 식별하고 완화하는 데 도움이 될 수 있습니다.
폴백에는 잠재적이고 증폭된 버그가 있음
폴백 코드 경로에는 특정 실패 조건에서만 발생하는 비활성 버그가 포함될 수 있으며, 이는 자주 발생하지 않아 예측하기 어렵고 몇 달 또는 몇 년 동안 발견되지 않을 수 있습니다. 예를 들어, ID 서비스 중단 시 다른 인증 방법으로 전환하는 API의 폴백 메커니즘에는 폴백이 트리거될 때만 나타나는 버그가 포함될 수 있으며, 이는 드문 이벤트일 수 있습니다.
APISIX는 지속적인 A/B 테스트와 카나리 릴리스를 지원하여 팀이 프로덕션에서 소량의 트래픽으로 폴백 경로를 테스트할 수 있도록 합니다. 이 지속적인 노출은 중요한 버그가 되기 전에 잠재적인 버그를 발견하는 데 도움이 될 수 있습니다.
폴백은 자주 사용되지 않음
폴백 메커니즘은 자주 사용되지 않으므로 트리거될 때 예상대로 작동하지 않을 수 있습니다. 예를 들어, 지리적 데이터를 제공하는 API는 동적 데이터 소스가 사용 불가능할 때 정적 데이터 세트로 폴백할 수 있습니다. 이 폴백이 자주 사용되지 않으면 활성화될 때 오래된 정보를 제공할 수 있습니다. 이는 정기적으로 업데이트되거나 테스트되지 않기 때문입니다.
반면, APISIX는 동적 라우팅을 구성할 수 있으며, 주기적으로 트래픽의 일부를 폴백 서비스로 리디렉션하는 데 사용할 수 있습니다. 이는 폴백 경로가 정기적으로 실행되고 사용 준비가 되어 있는지 확인합니다.
결론
폴백은 API에 문제가 발생했을 때의 안전망입니다. APISIX의 업스트림 구성 또는 response-rewrite 플러그인을 사용하여 개발자는 시스템이 작동 상태를 유지하고 사용자와의 신뢰를 유지할 수 있는 사려 깊고 사용자 친화적인 응답을 제공할 수 있습니다. 핵심은 잠재적인 실패 지점을 예상하고 상황에 따라 최상의 경험을 제공할 수 있는 폴백을 설계하는 것입니다.