CH05 운영 이슈 테스트

카오스 엔지니어링이란

용어 자체에 대한 설명이 레퍼런스마다 조금씩 다른데 마이크로소프트의 글에 정리된 내용이 제일 직관적인 것 같아서 첨부한다.

카오스 엔지니어링 은 프로덕션 오류에 대한 서비스를 강화하여 개발자가 일관된 안정성을 얻을 수 있도록 하는 방법론입니다.

카오스를 유발하는 일반적인 방법은 시스템 구성 요소가 실패하게 만드는 오류를 의도적으로 삽입하는 것입니다. 불리한 상황에서 시스템의 안정성을 관찰, 모니터링, 대응 및 개선하는 것이 목표입니다.

일부러 시스템에 운영 상황에서 발생할 수 있는 오류 상황을 유발 시켜서 시스템의 저항성을 테스트하고 이걸 키워 나가는 엔지니어링 방법론을 의미한다.

넷플릭스에서 카오스 엔지니어링 관련한 글을 쓴 것이 있는데 이걸 잘 번역해둔 포스팅이 있다. 책도 있는데 내용이 재미있을 것 같다.

카오스 엔지니어링의 목적

위에 링크를 첨부한 마이크로소프트의 글에 정리가 잘 되어있는데, 아래와 같다.

카오스 몽키

이번 강의에서 다룬 툴이다. AOP를 이용해서 래핑한 객체를 활용해서 의도적으로 장애 상황을 만들 수 있다. 몽키가 분탕질(?) 치는 컨셉으로 '카오스 몽키' 라고 네이밍을 한 것 같다.

다음은 강의 자료 일부이다.

공격 대상(watcher)과 공격 유형(Assaults)

공격 대상은 말 그대로 카오스 몽키를 이용해서 공격할 수 있는 대상들을 의미한다. 위 어노테이션들이 붙은 bean 들은 모두 공격 대상으로 삼을 수 있다. 공격 유형은 어떤 장애를 의도적으로 일으킬지(=카오스 몽키를 이용해서 어떤 장애를 일으킬 수 있는지) 그 종류를 말한다.

카오스 몽키 설정

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'
}
spring.profiles.active=chaos-monkey
management.endpoint.chaosmonkey.enabled=true
management.endpoints.web.exposure.include=health,info,chaosmonkey

위와 같이 설정하고 어플리케이션을 띄우면 로그에 아래와 같이 뜬다.

이렇게 뜨면 실행할 준비가 되었다. 아래와 같이 카오스몽키에 요청을 보내면 현재 상태에 대해서 알 수 있다.

http://localhost:8080/actuator/chaosmonkey
{
    "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": []
    }
}

공식문서에 여러 엔드포인트들에 대한 정보들이 나와있다.

카오스 몽키 실습

카오스 몽키 활성화

먼저 카오스 몽키를 활성화 해야하는데 일단 http://localhost:8080/actuator/chaosmonkey 를 통해서 전체적인 카오스 몽키의 세팅 상태를 확인한다.

{
    "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 활성화

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 가 활성화 된다. 공격 대상에 따라 커스텀해서 세팅하자.

공격 지시

일단 실습은 간단하게 레이턴시를 의도적으로 발생시키는 것을 했다.

curl --location 'http://localhost:8080/actuator/chaosmonkey/assaults' \
--header 'Content-Type: application/json' \
--data '{
  "level": 5,
  "latencyRangeStart": 2000,
  "latencyRangeEnd": 3000,
  "latencyActive": true
}'

절반의 확률로 2초 ~ 3초의 레이턴시를 발생시킨다. 각 프로퍼티에 대한 설명은 https://codecentric.github.io/chaos-monkey-spring-boot/latest/#_properties 에 자세히 나와있다.

아래와 같이 매우 단순한 핸들러에 카오스 몽키를 이용해서 레이턴시를 걸고 Jmeter 로 부하테스트를 해보자.

@RestController
public class SampleController {

    @GetMapping("/index")
    public String stressTest() {
        return "index";
    }

}

먼저 비교를 위해서 카오스 몽키로 레이턴시를 걸기 전의 데이터를 보자.

TPS 가 10.1 이 나왔다.

이제 카오스 몽키로 레이턴시를 의도적으로 발생시키도록 하고 다시 테스트해보자. 레이턴시는 아래와 같이 걸었다.

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을 초과해야하는 것 아닌가 싶다.)

아래와 같이 레이턴시 길이를 더 늘려서 다시 테스트 했다.

curl --location 'http://localhost:8080/actuator/chaosmonkey/assaults' \
--header 'Content-Type: application/json' \
--data '{
  "level": 5,
  "latencyRangeStart": 10000,
  "latencyRangeEnd": 15000,
  "latencyActive": true
}'

성능이 확실히 더 떨어졌음을 확인할 수 있다.

강의에서는 아래와 같이 런타임에러도 실습했다.

http POST localhost:8080/actuator/chaosmonkey/assaults level=3 latencyActive=false exceptionsActive=true exception.type=java.lang.RuntimeException

생각정리

잘 활용 하면 정말 좋은 툴이라고 생각이 든다. 예전에 강의를 볼때는 이런게 있구나 싶었었는데, 회사에서 운영 이슈를 실제로 마주하고서 다시 이 강의를 보니까 예전에 강의를 볼 때보다 활용성이 아주 좋아 보인다.

팀원끼리 모여서 실제로 일어날 수 있는 여러 장애 시나리오를 수립한 뒤 그 시나리오에 맞게 카오스 몽키를 이용해서 의도적으로 장애를 일으켜서 서버의 장애 내성을 파악하고 필요한 조치를 해두면 좋을 것 같다.

Last updated