이 시리즈의 하중을 받치는 두 가정 모두 오늘, 관리형 서비스로, 한 오후에 테스트할 수 있다. AWS Fault Injection Service는 "AZ Availability: Power Interruption"이라는 시나리오를 제공하는데, 이것은 한 가용 영역의 태그된 모든 인스턴스를 정지시키고, 그것들을 교체하려는 Auto Scaling 그룹에 InsufficientInstanceCapacity 오류를 먹이며, EBS 볼륨 IO를 일시 정지하고, 서브넷 트래픽을 끊은 뒤, 결함을 걷어 내고 정지시켰던 것을 다시 시작한다. 3부의 컨트롤 플레인 가정에는 기성품 결함이 없지만, 그것의 정직한 근사는 JSON 파일 하나와 IAM deny 하나에 들어간다. 거의 아무도 어느 쪽도 돌리지 않으며, 그 이유는 도구가 아니다. 이유는 테스트는 실패할 수 있지만 가정은 실패할 수 없다는 것이다.

이 글은 Nothing Fails Alone의 4부, 마지막이다. 1부는 5월 us-east-1 사건을 다루며 쿼럼이 그 안에서 죽는 동안 존 경계가 버티는 것을 보여 줬다. 2부는 3월 me-central-1 이벤트를 다루며 하나의 원인이 모든 격리 보장을 지나 18시간 간격으로 두 존에 닿는 것을 보여 줬다. 3부는 두 공급자에 걸친 세 컨트롤 플레인 사건을 다루며 복구 계획이 그것이 돌아가는 계층과 함께 죽는 것을 보여 줬다. 시리즈의 척추를 두 문장으로. 당신이 구매하는 가용성은 공급자가 약속한 것의 경계에서 끝나고, 그 선 위의 모든 것은 당신이 소유하는 아키텍처다. 당신이 소유하면서 결코 훈련하지 않는 것은 아키텍처가 아니다. 그것은 당신의 이름이 붙은 가정이다. 이 부는 그것을 훈련한 뒤 시리즈를 채점한다.

AZ 훈련은 기성품으로 나온다

FIS 시나리오 라이브러리의 AZ Availability: Power Interruption은 한 존을 잃는 것의 문서화된 증상을 유도하는 게시된 실험 템플릿이며, 그 게시된 JSON은 일곱 가지 결함 유형에 걸쳐 여덟 개의 액션을 담는다(인스턴스 정지가 두 번 나타난다. 한 번은 독립 인스턴스용, 한 번은 Auto Scaling 그룹 자체용). 대상 존의 태그된 실행 중 인스턴스는 정지됐다가 설정된 시간 뒤에 다시 시작된다. 태그된 Auto Scaling 그룹의 실행 요청은 그 시간 동안 InsufficientInstanceCapacity를 받고, 당신이 지정한 모든 IAM 역할의 용량 호출도 마찬가지다. 시나리오 페이지에 따르면 실제 전원 중단 중에 "EC2 API calls made by the Auto Scaling control plane to recover lost capacity in the AZ will fail"이기 때문이다. EBS 볼륨 IO는 일시 정지된다. 서브넷 트래픽은 deny 규칙으로 가득 찬 복제된 네트워크 ACL을 끼워 넣어 거부된다. 존에 라이터가 있는 RDS 클러스터는 페일오버되고, ElastiCache 복제 그룹은 그 존의 전원이 중단된다. 그것이 5월 사건이 use1-az4에 한 일의 대부분이다. Coinbase를 하룻밤 다운시킨 그 장애는 이제 라이브러리 항목이다.

그것이 아닌 것에 대해 정확하자. 인스턴스는 정문으로, StopInstances API로 정지된다. 정연한 셧다운이지 전원 차단이 아니므로, 크래시 일관성에 관한 것은 아무것도 테스트되지 않는다. Fargate 태스크는 커버되지 않는다. 읽기 가능한 스탠바이가 둘인 RDS Multi-AZ 클러스터는 지원되지 않는다. 드리프트 하나. 시나리오 페이지는 복구 액션 aws:arc:start-zonal-autoshift를 설명하는데, 그 자신의 JSON 스냅샷에는 그것이 담겨 있지 않다. 콘솔의 라이브러리는 최신 리비전을 담고, 이 랩은 게시된 JSON을 따른다.

랩은 시리즈 저장소의 04-game-day이며, 시리즈의 read-only 규칙을 두 번째이자 마지막으로 깬다. --inject는 당신의 자격 증명이 닿는 어떤 계정에 대해서든 실제 실험을 시작하므로, 샌드박스에, 3부의 랩 스택을 겨냥해 두어야 하며, 프로덕션 근처에는 두지 말아야 한다.

이름, ID, 그리고 비어 있는 채로 배송된 정지 조건

2부는 존 이름 대 존 ID를 두고 소란을 피웠고, 게임 데이는 그 소란이 값을 하는 곳이다. 사건 보고는 ID로 말한다. 5월 보고는 use1-az4라고 했고, 3월 업데이트는 mec1-az2 그다음 mec1-az3라고 했다. 당신의 계정은 이름으로, AWS가 계정마다 무작위화하는 별칭으로 말한다. 그리고 그 도구는 대부분 이름을 요구한다. 인스턴스와 서브넷 대상은 Placement.AvailabilityZone으로 필터링하는데, 이는 당신의 계정 로컬 이름과 매칭되고, 게시된 시나리오는 EBS, RDS, ElastiCache 대상 파라미터도 이름으로 채운다. 정확히 두 액션이 물리적 ID를 받는다. 용량 부족 쌍 aws:ec2:api-insufficient-instance-capacity-erroraws:ec2:asg-insufficient-instance-capacity-error인데, 그것들의 availabilityZoneIdentifiers 파라미터는 이름뿐 아니라 존 ID도 받는 것으로 문서화돼 있다. 그곳에서조차 게시된 시나리오는 그 필드를 이름 us-east-1a로 채운다.

따라서 게임 데이는 2부가 랩을 배송한 그 번역 문제로 시작한다. 러너는 그것을 맨 위에서 한 번 해결한다. run.py가 받는 유일한 존 플래그는 --az-id, 즉 물리적 존, 사건 보고가 지칭하는 그것이다. 러너는 DescribeAvailabilityZones로 계정 로컬 이름을 해석하고, 둘 다 출력하며, 각 플레이스홀더를 그 소비자가 요구하는 형태로 채운다. 당신은 사건 보고가 붙였을 이름으로 장애를 리허설하고, 그 리허설은 당신의 계정이 우연히 말하는 방언으로 스스로를 구성한다.

러너가 물려받기를 거부하는 두 번째 것은 더 조용하다. 게시된 시나리오의 정지 조건 블록은 그대로 이렇게 생겼다.

"stopConditions": [
    {
        "source": "aws:cloudwatch:alarm",
        "value": ""
    }
]

안에 아무것도 없는 가드레일의 형태이며, 페이지의 한계 섹션은 정지 조건은 당신이 추가할 몫이라고 말한다. AWS가 당신의 정상 상태를 알 수 없으므로 방어할 만한 기본값이지만, 동시에 장전된 것이기도 하다. JSON이 깔끔하게 붙여 넣어지고, 빈 문자열은 누락된 안전 시스템처럼 보이지 않기 때문이다. 랩의 입장은 이렇다. run.py--alarm-arn이 제공되지 않으면 아무것도 만들기 전에 종료한다. 의미 있는 정지 조건 없는 실험은 테스트가 아니라 베팅이기 때문이다. 정상 상태는 결함 이전에 당신이 약속하는 숫자다. 그렇지 않으면 게임 데이는 실패할 수 없고, 실패할 수 없는 게임 데이는 당신에게 아무것도 알려 줄 수 없다.

통과가 어떻게 생겼는지, 측정된 것으로

3부의 두 경로 스택에 대해 실험을 돌려, 두 존 중 하나를 실패시켜라.

python3 run.py experiments/az-power-interruption.json \
  --az-id euw1-az1 \
  --fis-role-arn arn:aws:iam::111111111111:role/fis-game-day \
  --alarm-arn arn:aws:cloudwatch:eu-west-1:111111111111:alarm:game-day-abort \
  --target-group-arn ... \
  --inject

--inject 없이 같은 명령은 드라이 런이다. 해석된 템플릿을 출력하고 아무것도 시작하지 않는다. 그것과 함께라면 러너는 실험을 만들고 시작한 뒤, 종료 상태까지 폴링하며, 각 액션의 상태를 로깅하고 측정으로서 타깃 그룹의 건강 카운트를 샘플링한다. 그 타임라인이 산출물이다. 뒤따르는 것은 통과의 형태이지 내가 얻은 결과가 아니다. 그것을 얻어 낼 프로덕션 형태의 계정이 내게 없기 때문이다. 정적으로 안정된 경로 B는 장애 후 요구 사항인 건강한 타깃 둘 밑으로 결코 떨어지지 않는다. 살아남은 존이 이미 부하를 감당하기 때문이다. 경로 A는 절반으로 떨어져 결함 지속 시간 내내 거기 머문다. 그것의 인스턴스를 정지시킨 바로 그 실험이 그것의 교체 실행에 InsufficientInstanceCapacity를 먹이고 있기 때문이다. 3부의 측정 스크립트는 협조적인 컨트롤 플레인에 대해 경로 A의 복구를 쟀다. 게임 데이는 비협조적인 것에 대한 같은 경로를 보여 주며, 그 두 숫자의 차이가 당신의 DR 계획이 하고 있던 가정의 크기다.

기록할 가치가 있는 숫자가 넷이다. 건강한 타깃 샘플에서 나오는, 복구된 용량까지의 시간. 결함 창 동안 당신의 런북을 걸어가며 센, 실패했을 컨트롤 플레인 호출을 필요로 한 런북 단계의 수. 3부의 협조적 숫자와 게임 데이의 비협조적 숫자 사이의 간극. 그리고 1부 디텍터의 각 발견 항목의 운명. 그것이 출력하는 모든 행은 게임 데이가 판결할 수 있는 예측이기 때문이다. 단일 AZ Auto Scaling 그룹은 정말로 용량 오류 뒤에 자신의 용량을 좌초시키고, 기본 경로가 실패한 존으로 넘어가는 건강한 존의 서브넷은 정말로 이그레스를 잃는다. 디텍터 발견 항목은 가설이다. 게임 데이는 실험이다.

FIS가 줄 수 없는 컨트롤 플레인 훈련

3부의 가정은 더 어렵고, 여기서 정직한 문장이 먼저 온다. FIS에는 컨트롤 플레인 상실 액션이 없다. 전체 액션 레퍼런스는 그 표현을 결코 담고 있지 않으며, 그 안의 어떤 것도 컨트롤 플레인을 앗아가지 않는다. 가장 가까운 것은 당신이 지정한 호출자로 범위가 좁혀진 오류 주입이다. 위의 용량 부족 쌍, 그리고 지정된 IAM 역할이 하는 요청에 실패를 주입하는 세 가지 일반 API 오류 액션(internal, throttle, unavailable), EC2와 Kinesis 네임스페이스에만 해당된다. 플레인은 유지되고, 선택된 호출자가 거절당한다. 당신이 빌릴 수 있는 어떤 것도 STS가 세 차례 따로 실패하는 동안 콘솔이 504를 서빙하는 것을 재현하지 못한다.

그래서 랩의 두 번째 템플릿은 근사이며, 파일 자체 안의 최상위 approximation 키에서 그렇게 라벨된다. 라벨이 그것이 라벨하는 것으로부터 드리프트할 수 없도록. FIS 절반은 워크로드 ASG의 실행과 배포 역할의 용량 호출, 즉 RunInstances, CreateFleet, StartInstances, CreateCapacityReservation에 실패한 존에서 20분 동안 InsufficientInstanceCapacity를 주입한다. 나머지 절반은 FIS가 전혀 아니다. ec2:RunInstances, autoscaling:*, cloudformation:*에 대한 수동의, 범위가 좁혀진, 시간이 정해진 IAM deny로, 이것이 런북의 "그냥 긴급 스택을 푸시하라" 단계를 2023년 6월에 되었을 그 장애로 바꾸는 것이다. run.py는 적용 및 제거 명령을 출력하고 결코 실행하지 않으며, 시간 상자는 정책이 붙기 전에 결정되고, deny는 그것을 제거하는 데 당신이 필요로 할 역할에는 결코 걸리지 않는다. 그 마지막 규칙은 현학이 아니다. 그것이 훈련과 사건의 차이다.

근사가 재현하지 않는 것, 파일 자신의 목록에서. 실제 컨트롤 플레인 저하는 지연과 브라운아웃이지 깔끔한 오류인 경우는 드물다. 파일이 결코 지명하지 않는 서비스들, 즉 ELB, Route 53, IAM 자체, 콘솔은 여기서 계속 작동하는데 실제 이벤트에서는 아닐 수 있다. 읽기는 계속 성공하므로 당신의 대시보드는 실제보다 더 건강해 보인다. TerminateInstances와 로드 밸런서 구성 변경 같은 비용량 변형은 계속 성공하므로, 이것은 용량 생성을 거부하지 컨트롤 플레인을 거부하지 않는다. 그리고 이것은 한 존의 용량인데, 실제 이벤트는 리전 규모일 수 있다. 이 훈련에서 한 가지를 더 측정하라. 탐지까지의 시간. 사람이 수정이 작동하지 않는다는 것을 알아채는 데 얼마나 걸렸는가? 누군가 컨트롤 플레인 상실을 시뮬레이션했다고 말하면, 그 다섯 간극 중 어느 것을 커버했는지 물어라. 내 것도 그중 어느 것도 커버하지 않는다. 그것은 당신의 아키텍처가 애초에 첫 한 시간 동안 거부된 호출을 필요로 하지 않는다는 더 좁은 주장을 커버한다.

시리즈 채점

네 사건, 네 랩, 하나의 척추. 시리즈가 자신의 기준에 부쳐 확립한 것. 주장은 운영자의 게시된 말에 근거할 때에만 유효하다. 첫째, 존 격리는 그것이 설계된 결함을 상대로 실재한다. 5월의 열 이벤트는 use1-az4 안에 머물렀고, 그날 밤 실패한 아키텍처는 AWS가 아니라 자신의 사후 분석의 인정으로 실패했다. 둘째, 존 장애는 독립 사건이 아니다. 하나의 원인이 mec1-az2와 mec1-az3에 18시간 간격으로 닿았고, 잃은 두 존이 리전 서비스들을 그것들의 명시된 한 존 허용치 너머로 밀어냈다. 셋째, 컨트롤 플레인은 데이터 플레인이 계속 돌아가는 동안 실패하며, 이는 한 공급자 이상에서 그렇고, 컨트롤 플레인 작업인 복구 계획은 그것과 함께 실패한다. AWS 자신의 사후 이벤트 요약에 실린 2023년과 2025년 10월, Azure의 것에 실린 2026년 2월.

이제 다른 열. 승리만 세는 회고는 마케팅이기 때문이다. 가정으로 남은 것. 당신에 관한 모든 정량적인 것. 나는 이 랩들을 프로덕션 플릿에 대해 결코 돌리지 않았으므로, 시리즈의 모든 숫자는 출력의 형태다. 정직하게 라벨됐지만 여전히 형태다. 대규모 페일오버의 썬더링 허드 비용, 즉 AWS의 "longer than usual provisioning times"는 두 사건에 나타나며 AWS에 의해서도 나에 의해서도 결코 숫자가 주어진 적이 없다. 3부의 정적 안정성 보험료는 실제 플릿의 청구서가 아니라 산술이었다. 그리고 당신의 브레이크 글라스 경로가 작동하는지는 여기서 알 수 없다.

내가 다르게 했을 것. 게임 데이를 마지막이 아니라 먼저 돌리는 것. 시리즈는 랩들을 교육적으로 배열했다. 탐지, 번역, 측정, 주입. 그리고 그 순서는 운영적으로는 거꾸로다. 한 번의 주입 실행이 디텍터가 예측만 할 수 있는 발견 목록을 만들어 낸다. 그리고 나는 2차 출처를 더 일찍 불신했을 것이다. 규칙, 즉 운영자 자신의 페이지에서만 인용하라는 규칙은 끝까지 값을 했다. 그것은 틀린 기간, 반나절 어긋난 타임스탬프, 그리고 그 겉보기 출처가 가져와 보니 담고 있지 않은 주장들을 잡아냈다. 테스트되지 않은 가정에 관한 시리즈가 여럿을 하마터면 배송할 뻔했으니, 이는 그 자신의 논지에 대한 내가 마련할 수 있었던 만큼 깔끔한 시연이다.

게임 데이도 실패한다

도구는 그것이 측정하는 것과 같은 회의를 받을 자격이 있으므로, 게임 데이가 당신에게 거짓말하는 세 가지 방식으로 끝맺는다.

  • 테스트가 돌았다는 것만 증명하는 테스트. FIS는 대상을 태그로 해석하고, 태그 불일치는 실험을 실패시키지 않는다. AZ 시나리오는 대상이 아무것도 해석되지 않는 액션을 건너뛰고, 랩은 부분 스택도 돌 수 있도록 그 동작을 유지한다(emptyTargetResolutionMode: skip). 그 대가. 실험이 아무것도 정지시키지 않고 completed에 도달할 수 있다. 초록색 결과, 주입된 결함은 0. 상태 줄이 아니라 액션별 표를 읽어라. 컨트롤 플레인 템플릿은 대신 fail을 설정하는데, 거기서는 빈 대상이 실행을 무의미하게 만들기 때문이다. 종료 코드도 일치한다. completed는 0으로 종료하고, stopped 실험은 정지가 당신의 알람이 작동했다는 뜻인데도 1로 종료한다. 가드레일이 발동하는 것은 가드레일이 작동하는 것이며, 그럼에도 통과는 아니다.
  • 너무 일찍 발동하는 정지 조건. 알람을 빠듯한 임계값의 내부 지표에 묶으면 실험은 흥미로운 장애 이전, 2분 만에 정지한다. 당신은 알람이 발동한다는 것만 배우고, 그 뒤의 유혹은 그것을 느슨하게 하거나 제거하는 것인데, 이것이 시나리오의 빈 문자열로 스스로 되돌아가는 길이다. 해법은 가드레일을 줄이는 것이 아니다. 알람을 사용자 대면 정상 상태 지표에, 실제 사건을 정지시키고 싶을 임계값에 묶는 것이다.
  • 프로덕션이 아닌 계정. 샌드박스 통과는 메커니즘을 증명하지 결과를 증명하지 않는다. 프로덕션 트래픽 없음, 데이터 중력 없음, 시끄러운 이웃 없음, 새벽 3시에 깬 운영자 없음. 주입 자체의 무름, 즉 브라운아웃 대신 정연한 셧다운과 깔끔한 오류를 더하면, 정직한 증거 사슬은 이렇게 읽힌다. 디텍터가 예측하고, 샌드박스 게임 데이가 그 예측의 메커니즘을 테스트하며, 프로덕션은 라벨된 외삽을 물려받는다. 그것은 아무것도 없는 것보다 훨씬 낫고 증명보다 훨씬 못하며, 아닌 척하는 것이 "우리는 카오스 엔지니어링을 했다"가 하나 더의 테스트되지 않은 가정이 되는 길이다.

거기서 시리즈는 끝난다. 아무것도 홀로 실패하지 않는다. 모든 부가 장애를 그 곁의 무언가와 얽힌 채로 발견했다. 건물과 얽힌 쿼럼, 전쟁과 얽힌 존, 컨트롤 플레인과 얽힌 수정. 아무것도 홀로 통과하지도 않는다. 모든 통과는 그것을 낳은 환경과 얽혀 있고, 그렇다고 말하는 라벨이 리포트에서 가장 하중을 받치는 줄이다.

다음으로 읽을 글

AWS, 클라우드 아키텍처, 복원력 리뷰, 플랫폼 작업에 대한 컨설팅은 ercanermis.com에서 시작하라.

참고 자료