Chaos Monkey

YoungJun Kim·2022년 10월 19일
0

Application Test

목록 보기
5/6

카오스 엔지니어링

프로덕션 환경에서는 굉장히 고려할 상황이 많다. 특히 MSA로 구성할 경우 각 개별 서비스가 올바르게 작동함에도 서비스 간의 상호 작용 과정에서 예측하지 못한 장애가 발생하여 분산 시스템에 혼란을 줄 수 있다. 예를 들어 잘못 설정한 타임 아웃으로 인해 서비스가 대량으로 재시작 된다던지, 단일 장애 지점의 충돌로 연쇄 장애가 발생한다던지 등의 예기치 못한 혼란이 있을 수 있다. 이러한 혼란(Chaos)을 최대한 방지하고 신뢰성 있는 시스템을 구축하기 위한 방법론이 카오스 엔지니어링이다.

카오스 엔지니어링은 4단계로 진행이 된다.
1. 정상 동작을 나타내는 시스템의 측정 가능 통계치로 ‘정상 상태’ 정의하기
2. 정상 상태가 대조군과 실험군 모두에서 계속 될 것이라고 가정하기
3. 서버 장애, 하드 디스크 오작동, 네트워크 끊김 등과 같은 실제 문제에 대한 변수 정의하기
4. 대조군과 실험군 사이의 정상 상태 차이를 조사하여 가설 검증하기

정상 상태를 방해하는 것이 어려울수록 결국 장애가 발생할 확률이 적다는 소리가 된다. 이러한 문제를 일부러 발생을 시켜보며 약점을 발견하고 실제 서비스에서 이러한 장애가 발생하기 전에 미리 개선을 진행하도록 한다. 보다 더 자세한 내용은 해당 포스팅을 참조하자.


Chaos Monkey for Spring Boot(SM4SB) 설정

의존성 추가

    implementation 'de.codecentric:chaos-monkey-spring-boot:2.6.1'
    implementation 'org.springframework.boot:spring-boot-starter-actuator'

우선 SM4SB를 사용하기 위해 해당 라이브러리를 추가하고, Spring Actuator를 통해 이를 조작할 수 있기 때문에 역시 추가시켜준다.

설정 추가

spring:
  profiles:
    active: chaos-monkey

management:
  endpoint:
    chaosmonkey:
      enabled: true
  endpoints:
    web:
      exposure:
        include: health, info, chaosmonkey

chaos-monkey 프로파일 설정을 가져와야 사용할 수 있다. 따라서 이를 추가해주고, Actuator의 엔드포인트에 chaosmonkey를 추가시켜주자.


CM4SB Watchers

우선 CM4SB에는 Watcher라는 컴포넌트를 제공하는데, 이는 스프링 빈을 스캔하는 역할을 한다. Watcher를 통해 스프링의 AOP를 이용하여 특정 스프링 빈에 Latency를 발생시킨다던지, Exception을 발생시킬 수 있다.

우선 CM4SB를 활성화 시키기 위해 공식 문서를 확인해서 지원하는 엔드포인트를 살펴보자.

/actuator/chaosmonkey/status로 들어가 보면 우선 enabled가 false로 되어 있을 것이다. 따라서 이를 enable하기 위해 /actuator/chaosmonkey/enable로 POST 요청을 보낸다.

이후 watchers가 등록 되어있는지 확인해보자. /actuator/chaosmonkey/watchers로 GET 요청을 보내면 확인할 수 있다.

{
  "controller": false,
  "restController": false,
  "service": false,
  "repository": false,
  "component": false,
  "restTemplate": false,
  "webClient": false,
  "actuatorHealth": false
}

처음에는 설정을 하지 않는 이상 watcher가 등록이 아무것도 안되어 있을 것이다. 따라서 스프링 빈을 스캔할 수 있도록 이를 등록시켜주어야 한다.

등록 방법은 properties에 직접 등록할 수도 있고, 아니면 엔드포인트로 요청을 날려도 된다.

chaos:
  monkey:
    watcher:
      repository: true

위의 방법은 property로 등록하는 방법인데, 자세한 내용은 공식 문서를 확인하자.

엔드포인트로도 등록을 할 수 있는데 /actuator/chaosmonkey/watchers 엔드포인트로 POST 요청을 보내면 된다. 가능한 필드는 아래와 같다.

{
  "controller": true,
  "restController": true,
  "service": true,
  "repository": true,
  "component": false,
  "restTemplate": false,
  "webClient": false,
  "actuatorHealth": false
}

만약 위와 같이 설정한다면 Controller, RestController, Service, Repository 어노테이션이 붙은 빈을 찾아 AOP를 적용시키게 된다.


CM4SB Assaults

이제 Watcher를 통해 스프링 빈을 찾았다면, Assaults를 통해 해당 빈에 대해 공격을 가할 수 있다. /actuator/chaosmonkey/assaults로 POST 요청을 보내면 된다.

{
  "level": 5,
  "latencyRangeStart": 2000,
  "latencyRangeEnd": 5000,
  "latencyActive": true,
  "exceptionsActive": true,
  "killApplicationActive": false
}

이 외에도 여러가지 설정을 할 수 있는 것들이 많다. 공식 문서의 엔드포인트 예시를 확인해서 적절하게 보내면 된다.

공격할 수 있는 종류는 지연 발생, 예외 발생, 애플리케이션 종료, 메모리 공격, CPU 공격이 있다. 여기서 Runtime Assaults로는 애플리케이션 종료, 메모리 공격, CPU 공격이 있는데 이는 Cron 표현식을 통해 설정을 할 수 있다고 한다. 자세한 사항은 공식 문서를 확인하자.

Latency 발생시키기

한번 Repository에 Latency를 발생시켜보자. 우선 Chaos Monkey가 enable 된 상태이고, Repository에 Watcher를 적용시킨 상황에서 진행해야 한다.

{
  "level": 10,  
  "latencyActive": true,
  "latencyRangeStart": 2000,
  "latencyRangeEnd": 5000
}

간단하게 위와 같이 설정할 수 있다.

  • level: 몇 건의 요청 당 공격을 진행할 것인지 설정 (10 -> 요청 10건 당 1번 공격)
  • latencyActive: 지연 공격을 활성화 할지 설정
  • latencyRangeStart: 지연 시간의 시작 범위 설정 (단위는 ms)
  • latencyRangeEnd: 지연 시간의 종료 범위 설정 (단위는 ms)

위의 설정은 결국 10번 요청당 1번 공격을 하는데, 2000ms에서 5000ms 사이로 지연을 발생시키겠다는 소리가 된다. 실제로 적용을 해보고 JMeter를 통해 테스트를 진행해보자.


얼추 10번당 1번의 지연을 발생시키고, 그 범위는 2000 ~ 5000ms 사이인 것을 확인할 수 있다.

만약 어떤 특정 메서드에 커스텀하게 Assult를 등록하고 싶다면 아래와 같이 요청 데이터를 보내면 된다.

{
  "level": 10,
  "latencyRangeStart": 2000,
  "latencyRangeEnd": 5000,
  "latencyActive": true,
  "watchedCustomServices": [
    "com.example.chaos.monkey.chaosdemo.controller.HelloController.sayHello",
    "com.example.chaos.monkey.chaosdemo.controller.HelloController.sayGoodbye"
  ]
}

예외 발생시키기

actuator/chaosmonkey/assaults로 POST 요청을 보낼 때 exceptionsActive 값을 true로 설정하면 된다.

{
  "exceptionsActive": true,
}

이러면 설정한 level에 맞게 예외를 발생시켜주게 된다. 커스텀하게 예외를 발생시킬 수도 있는데, 이는 공식 문서의 예시를 보도록 하자.

이렇게 예외를 발생시켜 테스트한다면 시스템의 견고함을 확인할 수 있다.

예를 들어, A라는 서비스가 B1라는 서비스와 통신을 하는데, 만약 B1 서버가 죽으면 B2 서버로 우회해서 통신을 보내도록 설정했다고 해보자.

이럴 경우 이런 테스트를 통해 B1 서버에 예외를 발생시킨 다음에, B2 서버로 요청을 우회해서 잘 보내는지 확인을 할 수가 있다. 따라서 보다 견고한, 신뢰성이 높도록 시스템을 개선할 수가 있는 것이다. (물론 지연도 어느정도 지연된다면 우회를 한다던지 등으로 개선할 수 있을 것이다.)

profile
반갑습니다. 주니어 백엔드 개발자 김영준입니다.

0개의 댓글