카오스 엔지니어링이란
용어 자체에 대한 설명이 레퍼런스마다 조금씩 다른데 이 제일 직관적인 것 같아서 첨부한다.
카오스 엔지니어링 은 프로덕션 오류에 대한 서비스를 강화하여 개발자가 일관된 안정성을 얻을 수 있도록 하는 방법론입니다.
카오스를 유발하는 일반적인 방법은 시스템 구성 요소가 실패하게 만드는 오류를 의도적으로 삽입하는 것입니다. 불리한 상황에서 시스템의 안정성을 관찰, 모니터링, 대응 및 개선하는 것이 목표입니다.
일부러 시스템에 운영 상황에서 발생할 수 있는 오류 상황을 유발 시켜서 시스템의 저항성을 테스트하고 이걸 키워 나가는 엔지니어링 방법론을 의미한다.
을 쓴 것이 있는데 이걸 이 있다. 책도 있는데 내용이 재미있을 것 같다.
카오스 엔지니어링의 목적
위에 링크를 첨부한 마이크로소프트의 글에 정리가 잘 되어있는데, 아래와 같다.
카오스 몽키
이번 강의에서 다룬 툴이다. AOP를 이용해서 래핑한 객체를 활용해서 의도적으로 장애 상황을 만들 수 있다. 몽키가 분탕질(?) 치는 컨셉으로 '카오스 몽키' 라고 네이밍을 한 것 같다.
다음은 강의 자료 일부이다.
공격 대상(watcher)과 공격 유형(Assaults)
공격 대상은 말 그대로 카오스 몽키를 이용해서 공격할 수 있는 대상들을 의미한다. 위 어노테이션들이 붙은 bean 들은 모두 공격 대상으로 삼을 수 있다. 공격 유형은 어떤 장애를 의도적으로 일으킬지(=카오스 몽키를 이용해서 어떤 장애를 일으킬 수 있는지) 그 종류를 말한다.
카오스 몽키 설정
Copy dependencies {
implementation 'org.springframework.boot:spring-boot-starter-actuator'
implementation 'de.codecentric:chaos-monkey-spring-boot:3.0.1'
implementation 'org.springframework.boot:spring-boot-starter-web'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
Copy spring.profiles.active=chaos-monkey
management.endpoint.chaosmonkey.enabled=true
management.endpoints.web.exposure.include=health,info,chaosmonkey
위와 같이 설정하고 어플리케이션을 띄우면 로그에 아래와 같이 뜬다.
이렇게 뜨면 실행할 준비가 되었다. 아래와 같이 카오스몽키에 요청을 보내면 현재 상태에 대해서 알 수 있다.
Copy http://localhost:8080/actuator/chaosmonkey
Copy {
"chaosMonkeyProperties": {
"enabled": false,
"lastEnabledToggleTimestamp": null,
"togglePrefix": "chaos.monkey"
},
"assaultProperties": {
"level": 1,
"deterministic": false,
"latencyRangeStart": 1000,
"latencyRangeEnd": 3000,
"latencyActive": false,
"exceptionsActive": false,
"exception": {
"type": "java.lang.RuntimeException",
"method": "<init>",
"arguments": [
{
"type": "java.lang.String",
"value": "Chaos Monkey - RuntimeException"
}
]
},
"killApplicationActive": false,
"killApplicationCronExpression": "OFF",
"memoryActive": false,
"memoryMillisecondsHoldFilledMemory": 90000,
"memoryMillisecondsWaitNextIncrease": 1000,
"memoryFillIncrementFraction": 0.15,
"memoryFillTargetFraction": 0.25,
"memoryCronExpression": "OFF",
"cpuActive": false,
"cpuMillisecondsHoldLoad": 90000,
"cpuLoadTargetFraction": 0.9,
"cpuCronExpression": "OFF"
},
"watcherProperties": {
"controller": false,
"restController": false,
"service": false,
"repository": false,
"component": false,
"restTemplate": false,
"webClient": false,
"actuatorHealth": false,
"beans": [],
"excludeClasses": []
}
}
카오스 몽키 실습
카오스 몽키 활성화
Copy {
"chaosMonkeyProperties": {
"enabled": false,
"lastEnabledToggleTimestamp": null,
"togglePrefix": "chaos.monkey"
},
"assaultProperties": {
"level": 1,
"deterministic": false,
"latencyRangeStart": 1000,
"latencyRangeEnd": 3000,
"latencyActive": false,
"exceptionsActive": false,
"exception": {
"type": "java.lang.RuntimeException",
"method": "<init>",
"arguments": [
{
"type": "java.lang.String",
"value": "Chaos Monkey - RuntimeException"
}
]
},
"killApplicationActive": false,
"killApplicationCronExpression": "OFF",
"memoryActive": false,
"memoryMillisecondsHoldFilledMemory": 90000,
"memoryMillisecondsWaitNextIncrease": 1000,
"memoryFillIncrementFraction": 0.15,
"memoryFillTargetFraction": 0.25,
"memoryCronExpression": "OFF",
"cpuActive": false,
"cpuMillisecondsHoldLoad": 90000,
"cpuLoadTargetFraction": 0.9,
"cpuCronExpression": "OFF"
},
"watcherProperties": {
"controller": false,
"restController": false,
"service": false,
"repository": false,
"component": false,
"restTemplate": false,
"webClient": false,
"actuatorHealth": false,
"beans": [],
"excludeClasses": []
}
}
일단 "enabled": false 인 것이 보인다. POST 로 /actuator/chaosmonkey/enable 를 통해 카오스 몽키를 활성화 시켜주고 enabled 가 true 가 되었는지 확인한다.
watcher 활성화
Copy curl --location 'http://localhost:8080/actuator/chaosmonkey/watchers' \
--header 'Content-Type: application/json' \
--data '{
"controller": true,
"restController": true,
"service": true,
"repository": true,
"component": false,
"restTemplate": false,
"webClient": false,
"actuatorHealth": false
}'
POST 를 통해 위와 같이 통신하면 data 에 세팅한 것과 같이 watcher 가 활성화 된다. 공격 대상에 따라 커스텀해서 세팅하자.
공격 지시
일단 실습은 간단하게 레이턴시를 의도적으로 발생시키는 것을 했다.
Copy curl --location 'http://localhost:8080/actuator/chaosmonkey/assaults' \
--header 'Content-Type: application/json' \
--data '{
"level": 5,
"latencyRangeStart": 2000,
"latencyRangeEnd": 3000,
"latencyActive": true
}'
아래와 같이 매우 단순한 핸들러에 카오스 몽키를 이용해서 레이턴시를 걸고 Jmeter 로 부하테스트를 해보자.
Copy @RestController
public class SampleController {
@GetMapping("/index")
public String stressTest() {
return "index";
}
}
먼저 비교를 위해서 카오스 몽키로 레이턴시를 걸기 전의 데이터를 보자.
TPS 가 10.1 이 나왔다.
이제 카오스 몽키로 레이턴시를 의도적으로 발생시키도록 하고 다시 테스트해보자. 레이턴시는 아래와 같이 걸었다.
Copy curl --location 'http://localhost:8080/actuator/chaosmonkey/assaults' \
--header 'Content-Type: application/json' \
--data '{
"level": 5,
"latencyRangeStart": 5000,
"latencyRangeEnd": 6000,
"latencyActive": true
}'
TPS가 6.9가 나왔다. Maximum 을 보아도 카오스 몽키 적용 전에 10이었는데 5951로 올랐다. 왜냐면 내가 레이턴시를 6000 으로 설정해줬기 때문이다.(그런데 적어도 6000을 초과해야하는 것 아닌가 싶다.)
아래와 같이 레이턴시 길이를 더 늘려서 다시 테스트 했다.
Copy curl --location 'http://localhost:8080/actuator/chaosmonkey/assaults' \
--header 'Content-Type: application/json' \
--data '{
"level": 5,
"latencyRangeStart": 10000,
"latencyRangeEnd": 15000,
"latencyActive": true
}'
성능이 확실히 더 떨어졌음을 확인할 수 있다.
강의에서는 아래와 같이 런타임에러도 실습했다.
Copy http POST localhost:8080/actuator/chaosmonkey/assaults level=3 latencyActive=false exceptionsActive=true exception.type=java.lang.RuntimeException
생각정리
잘 활용 하면 정말 좋은 툴이라고 생각이 든다. 예전에 강의를 볼때는 이런게 있구나 싶었었는데, 회사에서 운영 이슈를 실제로 마주하고서 다시 이 강의를 보니까 예전에 강의를 볼 때보다 활용성이 아주 좋아 보인다.
팀원끼리 모여서 실제로 일어날 수 있는 여러 장애 시나리오를 수립한 뒤 그 시나리오에 맞게 카오스 몽키를 이용해서 의도적으로 장애를 일으켜서 서버의 장애 내성을 파악하고 필요한 조치를 해두면 좋을 것 같다.