AWS 기술 블로그
Amazon ElastiCache 워크로드를 위한 카오스 실험
Amazon ElastiCache는 Valkey, Memcached 및 Redis OSS와 호환되는 완전관리형 서비스로서 99.99%의 가용성을 갖추고 있으며 현대적 애플리케이션의 성능을 최적의 비용으로 실시간으로 개선합니다. ElastiCache의 다양한 기능 중 다중 AZ(가용 영역)는 자체 ElastiCache 캐시를 설계할 때 가용성이 더욱 뛰어난 구성으로 실행할 수 있는 기능입니다. 특정 유형의 예정된 유지 관리를 수행하는 동안이나 노드(*node, 본 게시글에서는 ElastiCache의 단일 EC2 instance를 지칭하는 개념으로 사용 되었습니다.) 장애와 같이 예기치 않은 상황이 발생하면 ElastiCache는 자동으로 기본 노드(Primary)의 장애를 감지하고 읽기 전용 복제본(Read Replica)을 선택한 다음 이를 새로운 기본 노드로 승격합니다. 또한 ElastiCache는 승격된 읽기 전용 복제본의 DNS 변경 사항을 전파하기 때문에 애플리케이션이 기본 노드 엔드포인트에 쓸 때 엔드포인트를 변경하지 않아도 됩니다.
다만 ElastiCache의 다중 AZ를 활용해 운영하다보면 다음과 같은 고민을 가지게 될 수 있습니다.
“만약 실제로 장애가 발생하면, 그리고 특히 최악의 상황을 가정해서 AZ 장애가 발생한다면 어떤 상황이 발생하고 우리 팀은 어떻게 대처할 수 있을까? 그리고 이 상황을 어떻게 재현해 볼 수 있을까?”
이와 같은 고민과 궁금증을 해결할 수 있는 환경을 구성해주는 서비스가 AWS 내에도 분명 존재하며 AWS Fault Injection Service(FIS)라고 부릅니다.
AWS FIS는 장애 주입 실험을 수행하기 위해 사용하는 완전관리형 서비스로, 이 서비스를 사용하면 애플리케이션의 성능, 관찰성 및 복원력을 개선할 수 있습니다. FIS는 다양한 AWS 서비스에서 통제된 오류 주입 실험을 설정하고 수행하는 프로세스를 단순화하여 팀이 애플리케이션 동작에 대한 신뢰를 확보할 수 있도록 합니다. 특히 AZ의 전력이 완전히 중단될 때 발생할 수 있는 ElastiCache 장애를 확인해볼 수 있는 시나리오의 실행이 가능합니다.
본 글에서는 ElastiCache에서의 다중 AZ 필요성과 클러스터 모드 활성화 여부에 따른 장애 상황을 알아보고, FIS를 통한 카오스 실험을 실행하는 과정과 실험 후 점검 과정에 대해 알아보도록 하겠습니다. 참고로 본 글은 ElastiCache for Valkey, Redis OSS를 기준으로 작성되었습니다.
다중 AZ ElastiCache의 필요성
캐시 계층에서 고가용성 구성을 생각하고 있다면 다중 AZ는 필수적입니다. 만약 단일 AZ로 구성 후 캐시 계층을 운영하고 있었다면 AZ 장애 이후 캐시 계층이 무너지며 지연 시간, 데이터베이스 부하 급증과 같은 여러 이슈를 겪으며 서비스 장애를 겪게 됩니다.
지연 시간
캐시를 통해 조회하던 데이터들을 더 이상 캐시에서 조회할 수 없게 되었기 때문에, 자연스럽게 데이터베이스(혹은 데이터 내구성 계층)으로부터 읽어오게 됩니다. 이 과정에서 요청량이 급증함에 따라 지연 시간도 급증하게 되며, 요청이 캐시 계층을 활용하지 못하고 모두 데이터베이스로 전달되어 데이터베이스가 많은 작업을 수행하느라 지연시간, 특히 꼬리 지연 시간(P95, P99)이 점점 더 증폭될 수 있습니다.
데이터베이스 부하
만약 지연 시간 증폭에서 그치면 다행이지만, 캐시 계층이 트래픽의 높은 비율을 소화하고 있었다면 문제는 더욱 커질 수 있습니다. 데이터베이스는 캐시가 처리하던 많은 요청들에 갑작스럽게 대응하게 되며 따라서 데이터베이스에 가해지는 부하가 단 시간내에 큰 폭으로 증가하게 됩니다. 만약 데이터베이스가 증가한 부하를 견디지 못 하거나 빠른 시간 안에 부하 대응(e.g. 읽기 복제본 증가)을 하지 못했다면 캐시 장애가 데이터베이스 장애로 연달아 이어질 수 있습니다.
만약 캐시 계층에 일시적인 데이터만 있는 것이 아니라면,
중요한 비즈니스 로직이 캐시 계층을 조회해서 처리되고 있었을 가능성이 매우 높으므로 서비스 전체 장애로 이어지는 상황이 발생할 수 있습니다. 데이터 유실이 일어날 수 있을 뿐만 아니라, 캐시 계층을 복구하는 동안 해당 비즈니스 로직들은 계속 작동하지 않아 전체 장애가 길어지는 상황이 발생합니다.
우리는 이러한 장애를 겪을 수 있다는 것을 알고 있지만 AWS에서 AZ 장애가 나는 일은 매우 드물기 때문에 다중 AZ의 효용성을 실제로 체험해보는 것은 어렵습니다. 그럼에도 불구하고 우리가 운영하는 워크로드는 늘 발생할 수 있는 장애를 염두에 두고, 가용성에 대한 고민을 필요로 하기 때문에 다중 AZ를 생각하게 됩니다. 또한 이런 고민들을 통해 사전에 장애에 대처할 수 있는 방법들을 설계할 필요가 있습니다.
클러스터 모드 활성화 여부에 따른 AZ 장애 상황
ElastiCache에서 다중 AZ를 활성화 했더라도, 클러스터 모드 활성화 여부에 따라 구성이 달라지며 이에 따라 장애 상황이 달라질 수 있습니다. 단, 클러스터 모드 활성화와 비활성화 모두 기본 노드(Primary Node) 장애 시 읽기 전용 복제본 노드(Read Replica)를 기본 노드로 승격한다는 점은 동일합니다. 또한 읽기 전용 복제본 노드는 오직 “읽기”만 처리할 수 있기 때문에 “쓰기”는 기본 노드에서만 처리할 수 있습니다. 다음은 각 모드에 따른 기본 노드가 존재하는 AZ 장애 발생 상황이며, 두 모드 모두 AZ 3개, 읽기 전용 복제본 노드가 2개라고 가정하겠습니다.
클러스터 모드 비 활성화의 경우
그림 1. 클러스터 모드 비활성화
그림 2. 클러스터 모드 비활성화, AZ 장애로 인해 기본 노드에 문제가 생긴 경우
클러스터 모드를 비활성화 했다면 단일 샤드(Shard)를 가지게 되며 최대 5개의 읽기 전용 복제본 노드를 사용할 수 있습니다. 즉, 단일 샤드이기 때문에 기본 노드가 오직 1개라는 점을 고려해야 합니다. 만약 기본 노드가 존재하는 AZ에 문제가 발생했다면 자동 장애 조치로 다중 AZ가 활성화되어 있는 ElastiCache는 자동으로 장애를 감지하고 복제 지연이 가장 작은 읽기 전용 복제본 노드를 승격하게 됩니다. 이 과정에서 다음과 같은 현상이 발생합니다.
- 기본 노드가 1개이기 때문에 쓰기가 처리되지 않으며, 승격이 완료되기 전까지 전체 쓰기가 중단됩니다.
- 읽기 전용 복제본에서만 읽고 있었다면, 장애 이후 1개의 노드에서 읽기가 가능합니다.
클러스터 모드 활성화의 경우
그림 3. 클러스터 모드 활성화
그림 4. 클러스터 모드 활성화, AZ에 장애가 생긴 경우
클러스터 모드를 활성화했다면, 여러 개의 샤드를 가질 수 있습니다. 따라서 비활성화한 경우와 다르게 전체 쓰기가 중단되지 않습니다. 그림 3과 같이 3개의 샤드를 가지고 있는 클러스터라고 가정하며, 동일하게 AZ 3개, 읽기 전용 복제본 노드 2개라고 가정하겠습니다. 클러스터 모드 활성화도 동일하게 자동으로 장애를 감지하고 복제 지연이 가장 작은 읽기 전용 복제본 노드를 승격시키지만, 영향을 받은 기본 노드가 1개 밖에 없는 상황이라는 것이 중요합니다. 이 과정에서 다음과 같은 현상이 발생합니다.
- 기본 노드가 3개이며, AZ에 문제가 발생했더라도 승격이 완료되기 전까지 전체 쓰기가 아닌 일부 샤드에 대한 쓰기만 중단됩니다.
- 읽기 전용 복제본에서만 읽고 있었다면, 장애 이후 3개의 노드에서 읽기가 가능합니다.
AZ 장애 상황 가정을 통해 클러스터 모드 활성화 여부에 따른 다중 AZ 상황 및 자동 장애 조치를 살펴봤습니다. 무엇보다 가장 중요한 점은, 클러스터 모드 활성화와 별개로 다중 AZ 활성화와 2개 이상의 읽기 전용 복제본을 사용하면 기본적으로 전체 읽기 장애를 방지할 수 있다는 점입니다. 또한 클러스터 모드 활성화 시 기본 노드 혹은 기본 노드가 있는 AZ에 장애가 발생하더라도 전체 쓰기 장애를 막을 수 있기 때문에 클러스터 모드 활성화를 고려하는 것이 필요합니다. 참고로, 본 글에서 진행한 카오스 실험은 클러스터 모드가 활성화된 클러스터에 대해서만 진행합니다.
AWS FIS를 통한 카오스 실험
워크로드를 운영하고, 해당 워크로드의 사이즈가 점차 커져 대규모로 실행되고 있다면 워크로드의 복원력을 파악하는 것은 매우 중요합니다. 카오스 실험은 워크로드에 제어된 장애를 유도하여 워크로드 동작에 대한 확신을 얻을 수 있습니다. 이러한 실험을 통해 얻은 지혜를 지속적으로 피드백 주기에 반영하여 복원력을 개선할 수 있습니다.
AWS 고객은 AWS FIS를 통해 원하는 중단을 생성하는 사전 구축된 템플릿을 사용하여 신속하게 실험을 설정하고 수행할 수 있습니다. FIS는 본 글에서 다루는 ElastiCache 뿐만 아니라, Amazon RDS, DynamoDB, EC2, ECS와 같은 서비스들 그리고 네트워크 같이 다양한 카오스 실험을 할 수 있게 도와주는 서비스입니다. 이러한 실험을 통해 장애 발생 시 워크로드가 어떻게 동작할지 확인할 수 있으며, 실험 결과를 바탕으로 장애에 대응하는 장애 조치(Failover) 전략을 수립할 수 있습니다. 이런 전략 아래 팀은 숨겨진 이슈를 발견하고, 사각지대를 모니터링하고, 장애가 발생하더라도 능숙하게 장애 상황을 파악하고 대처할 수 있게 되어, 결론적으로 장애 시간이 감소합니다.
가장 중요한 점은, 카오스 실험은 1회성으로 이뤄지는 것이 아니라 지속적으로 이뤄져야 더 큰 의미를 가지게 됩니다. 운영 중인 워크로드는 하나의 생물처럼 지속적으로 변화하고 진화하기 때문에 이전 실험을 통해 얻은 결과도 변화하여 신뢰하기 어려워질 수 있습니다. 또한 워크로드 뿐만 아니라, 워크로드를 운영하는 팀도 항상 고정되어 있지 않으며 변경될 수 있습니다. 제어된 장애와 함께 진행되는 카오스 실험은 실제 장애에 비해 훨씬 안전하며 이를 1회가 아닌 “지속적”으로 진행해 워크로드와 연관된 모든 팀과 구성원들이 사전에 장애를 대비할 수 있도록 하는 것이 목적이라는 점을 기억해야 합니다.
구현 상세
카오스 실험을 위한 구성 요소들은 다음과 같습니다.
ElastiCache for Valkey
클러스터 모드 활성화로 실행된 ElastiCache for Valkey는 3개의 AZ에서 실행되며, 3개의 샤드와 각 샤드별 2개의 읽기 전용 복제본 노드를 가지는 구성으로 되어 있습니다. 그림 3과 같은 구성입니다. FIS Experiment에서 사용할 태그인 “fis-exp-target”을 설정해두었습니다. 또한 엔진 로그를 활성화하여 CloudWatch Logs에서 엔진 로그를 확인할 수 있도록 설정합니다.
FIS
FIS는 ElastiCache AZ 장애에 대한 실험 템플릿과 실험 템플릿을 기반으로 실행되는 실험을 사용합니다. 위에서 ElastiCache를 구성하며 사용하기로 한 태그인 “fis-exp-target”을 가지고 있는 ElastiCache를 장애 주입 타겟으로 설정할 수 있으며, 작업은 “aws:elasticache:interrupt-cluster-az-power”으로 특정 시간(예시: 30분)동안 진행합니다.
그림 5. AWS FIS에서 실험 액션과 타겟 설정
Valkey GLIDE 클라이언트
Valkey GLIDE는 오픈소스 Valkey 클라이언트 라이브러리로 애플리케이션 프로그래머는 GLIDE를 사용하여 애플리케이션을 Valkey 및 Redis OSS와 호환되는 서비스에 안전하고 안정적으로 연결할 수 있습니다. Valkey GLIDE에 대한 자세한 설명은 다음의 블로그를 참고하는 것을 권장드립니다. 본 글에서는 Python으로 작성된 클라이언트를 사용하지만, 현재 Java와 Node.js도 지원하고 있습니다.
테스트를 위해 읽기 전용 복제본으로부터 읽기를 수행하는 클라이언트와 쓰기를 수행하는 클라이언트를 만들고 실행합니다. 두 클라이언트를 통해 우리는 애플리케이션에서 어떤 로그가 남고, 어떤 동작이 일어나는지 파악할 것입니다. 또한 두 클라이언트는 AWS Systems Manager를 통해 사전에 작성된 실행, 종료, 상태 파악 쉘 스크립트를 실행하여 제어됩니다. 이를 통해 클라이언트 테스트를 확장하더라도 여러 인스턴스에서 실행 및 제어할 수 있도록 자동화했습니다.
읽기 클라이언트
읽기 전략은 총 3가지가 있습니다. 기본 노드에서만 읽어오는 PRIMARY, 읽기 전용 복제본을 우선시 하는 PREFER_REPLICA, AZ에 따라 정해지는 AZ_AFFINITY입니다. 본 코드에서는 PREFER_REPLICA를 사용하도록 작성했습니다. 자세한 설명은 다음의 블로그를 참고하시길 바랍니다.
쓰기 클라이언트
CloudWatch
카오스 실험이 진행되는 동안, 실제 실험에 따른 클러스터 상태를 확인할 수 있는 CloudWatch Alarm이 필요하며, 필요하다면 ElastiCache의 지표를 살펴볼 수 있는 CloudWatch Dashobard를 구성합니다. 또한 CloudWatch Logs에 적재되는 ElastiCache 엔진 로그를 통해 엔진 레벨의 상황을 확인합니다.
카오스 실험 실행
카오스 실험 실행 과정은 다음과 같습니다.
- FIS Experiment를 실행합니다.
- FIS에서 장애 주입이 시작된 것을 확인한 후, 다음 스텝으로 넘어갑니다.
- 다음 도구들을 통해 장애 상황 및 자동 장애 조치, 장애 여파를 확인합니다.
- CloudWatch Logs
- CloudWatch Dashboard, Alarm
- Valkey-GLIDE 클라이언트 로그
- 실험 결과를 바탕으로 대응 과정에서 미비했거나 밝혀진 사실에 대해 보충할 수 있는 계획을 수립합니다.
- 수립된 계획에 따라 보완을 성공적으로 수행했다면, 다시 1번으로 돌아가 카오스 실험을 실행합니다.
카오스 실험 결과
CloudWatch Logs
만약 AZ 장애로 인해 기본 노드가 영향을 받은 경우, 읽기 전용 복제본에서는 기본 노드와 연결이 끊겼다는 것을 알게 됩니다. 이후 Failover 과정에서 새로운 기본 노드를 선정하는 과정을 겪게 됩니다.
그림 6. AZ 장애로 인해 기본 노드가 영향을 받은 경우, 읽기 전용 복제본의 엔진 로그
만약 읽기 전용 복제본이 영향을 받은 경우라면, 기본 노드에서는 읽기 전용 복제본으로부터 연결이 끊겼다는 엔진 로그가 남게됩니다. 카오스 실험 중단 이후에는 다시 동기화를 진행하게 됩니다.
그림 7. AZ 장애로 인해 읽기 전용 복제본이 영향을 받은 경우, 기본 노드의 엔진 로그
CloudWatch Alarm
CloudWatch Alarm의 경우, IsMaster를 활용했습니다. 단, IsMaster 지표는 노드 장애를 비롯해 모종의 이유로 Failover가 일어나 승격되는 경우에도 변경될 수 있습니다. 그러나 Failover 중 문제를 겪고 있는 호스트에서는 호스트 수준 지표가 수집되지 않을 수 있다는 점을 고려하여 알람 설정을 위한 지표를 선택하는 것이 필요합니다. 물론 수집이 되지 않고 있다는 것도 하나의 정보로 인식할 수 있고 이러한 정보에 기반하여 그림 8의 알람처럼 설정할 수 있습니다. 그러나 클러스터 수준 메트릭 사용이 더 적절한지 고민이 필요하며, 이를 고려하여 팀 내에서 어떤 지표를 기준으로 삼을지 사전에 논의하는 것이 필요합니다.
그림 8. CloudWatch Alarm
Valkey-GLIDE 클라이언트 로그
읽기 클라이언트의 경우, 읽기 요청이 예외 없이 잘 진행됨을 확인할 수 있었습니다. 특히 Failover로 인해 승격이 일어나 2개의 노드가 사용할 수 없게 되더라도 샤드별로 존재하는 3개의 노드 중 1개는 읽기 요청을 처리할 수 있기 때문에 읽기 요청에는 문제가 발생하지 않았습니다.
쓰기 클라이언트의 경우, 승격이 진행되는 동안 AZ 장애로 인해 영향을 받은 특정 샤드에 대해서 처리가 중단됨을 확인할 수 있었습니다. 해당 샤드의 기본 노드가 AZ 장애에 영향을 받고 있다면 수 분 정도의 Failover가 진행되는 동안 쓰기가 영향을 받았습니다. 문제가 생긴 요청은 Timeout Exception이 발생했으며, 이는 requestTimeout과 reconnectStrategy 두 가지 요소에 영향을 받습니다. 기본적으로 requestTimeout은 250ms, reconnectStrategy는 지수 백오프(Exponential Backoff) 방식으로 구성되어 있습니다. 자세한 내용은 해당 링크를 참조하시길 바랍니다. 단, AZ 장애에 영향을 받지 않는 샤드들은 여전히 쓰기 요청을 정상적으로 처리했습니다.
쓰기 클라이언트 로그 예시
카오스 실험 후 점검
클라이언트 영향을 최소화하기
ElastiCache 클러스터 내부에서 Failover 작업이 진행된 이후 클러스터가 정상적으로 동작하더라도 클라이언트 애플리케이션이 정상 동작하지 않는 경우가 있습니다. 이 경우 높은 확률로 클라이언트에서 예외 처리 및 설정이 미흡하여 생겼을 가능성이 매우 높습니다. 일반적으로 클러스터 토폴로지에 대한 리프레시와 예외 처리, 재요청/연결에 대한 설정이 되어 있지 않은 경우입니다. 관련하여 클라이언트를 위한 모범 사례에 대해서는 다음 링크를 참조하시는 것을 추천드립니다.
비용과 관련해서 Valkey GLIDE의 경우 위에서 설명했던 것처럼 AZ Awareness(혹은 Affinity)라고 부르는 기능을 제공하고 있습니다. 클라이언트의 AZ와 동일한 AZ에 존재하는 Valkey 서버를 연결할 수 있게 돕는 기능으로, 다중 AZ을 사용하고 있다면 DTO 비용을 감소시킬 수 있는 기능입니다. 기본적으로 읽기 전용 복제본들에 요청을 전달하지만, 만약 Fallback이 필요하다면 기본 노드 혹은 다른 읽기 전용 복제본으로 요청을 전달합니다. 즉, AZ Awareness를 사용하고 있는 상황에서 해당 AZ에 문제가 발생하더라도 클라이언트 레벨에서 읽기에 대한 요청은 정상적으로 처리되도록 설계되어 있습니다. 또한 현재 Valkey GLIDE는 더 나은 관찰 가능성을 위해 클라이언트 관련 메트릭들에 대한 보강과 OpenTelemetry 지원을 목표로 하고 있으며, 이런 클라이언트 측 메트릭을 통해 클라이언트의 상황을 더 명확하게 파악할 수 있게 될 것입니다.
적절한 관찰가능성
카오스 실험을 진행하다보면 스스로 혹은 팀 내에서 가장 많이 하게 되는 질문이 있습니다. “이 현상은 무엇을 봐야 알 수 있을까?” 카오스 실험 결과를 확인한 것처럼 엔진 로그, 클라이언트 로그, 다양한 지표 등을 활용해서 우리는 분산 시스템 내에서의 복잡성을 관찰하고 다룰 수 있게 됩니다. 특히 AZ 장애가 미치는 영향력을 통제된 환경에서 관찰하는 것은 팀에게 매우 좋은 기회가 될 수 있습니다. AZ 수준의 장애는 팀이 겪을 수 있는 가장 치명적인 수준의 장애일 가능성이 매우 높으며, AZ 수준의 장애를 관찰할 수 있는 환경을 구축하게 된다면 다른 장애 시 발생할 수 있는 많은 요소와 상황들을 파악할 수 있게 됩니다. 예시로, 지표가 수집되지 않는 것 자체도 상황을 파악하는 좋은 정보가 될 수 있습니다. 문제 상황 시 어떤 것들을 살펴봐야 하며, 문제를 탐색할 수 있는 환경인지 팀 내에서 논의하고 지속적으로 발전시켜나가는 것이 중요합니다.
지속적인 카오스 실험
점검 과정에서 다른 무엇보다 중요한 것은, 이와 같은 장애가 발생할 수 있음을 인지하고 사전에 이를 어떻게 대응할지 팀 내, 혹은 여러 팀과 함께 논의해 볼 수 있다는 점입니다. 일반적으로 장애가 발생하게 되면 장애에 영향을 미친 원인을 조사하고 식별하기 위해 수행되는 프로세스를 문서화한 플레이북과 문제 해결을 위해 필요한 조치들을 정의한 런북을 활용해 장애 대응을 수행하게 됩니다. 카오스 실험은 이런 플레이북과 런북을 만들고 보강하기 위한 아주 좋은 사전 실험입니다. 이러한 카오스 실험은 주기적, 반복적으로 수행하는 것이 매우 중요합니다. 워크로드와 팀 모두 지속적으로 변화하는 상황 속에서, 장애를 통제된 환경에서 확인할 수 있다는 것은 장애로 인한 서비스 중단, 비즈니스 손실을 손쉽게 예방할 수 있다는 것을 의미합니다.
결론
지금까지 Amazon ElastiCache 워크로드를 더 안정적으로 운영하기 위해 AWS FIS를 활용하여 카오스 실험을 진행하고, 결과를 살펴보며, 점검해야 하는 부분들에 대해 알아봤습니다. 카오스 실험은 우리가 운영하는 워크로드라는 배로 미지의 세계를 탐험할 때 각 부속품들의 동작을 확인할 수 있게 돕습니다. 그런 의미에서 카오스 실험은 일회성으로 운영되지 않는 것이 아닌 검증이 필요할 때마다 팀이 선택할 수 있는 유용한 도구입니다. 가설을 세우고, 실험을 진행하고, 결과를 확인하고, 점검하는 과정에서 팀 내에서 많은 논의를 통해 워크로드를 더 잘 파악하게 되며, 파악된 지식을 통해 런북과 플레이북을 보강할 수 있게 되고, 이는 결국 워크로드의 탄력적인 변경으로 이어집니다.
전반적인 Amazon ElastiCache의 모범 사례에 대해서는 다음의 링크를 참조하시는 것을 권장드립니다.