chaos-http๋ ์ธ ๊ฐ์ ๋ ๋ฆฝ์ ์ธ ๋ชจ๋๋ก ๊ตฌ์ฑ๋ฉ๋๋ค.
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ ๊ฐ๋ฐ์ ๋ก์ปฌ ํ๊ฒฝ โ
โ โ
โ โโโโโโโโโโโโโโโโ HTTP โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ :18080 โ chaos-proxy โ โ
โ โ Spring Boot โโโโโโโโโโโโโโโโบโ (Netty ๊ธฐ๋ฐ ๋ฆฌ๋ฒ์ค ํ๋ก์) โ โ
โ โ ์ ํ๋ฆฌ์ผ์ด์
โ โ โ โ
โ โ โ โ โโโโโโโโโโโโโโโโโโโโโโโโ โ โ
โ โโโโโโโโโโโโโโโโ โ โ Rule Engine โ โ โ
โ โ โ โโโโโโโโโโโโโโโโโโ โ โ โ
โ โโโโโโโโโโโโโโโโ HTTP โ โ โ Latency Rule โ โ โ โ
โ โ โ :9090 โ โ โ Error Rule โ โ โ โ
โ โ ์น ๋ธ๋ผ์ฐ์ โโโโโโโโโโโโโโโโโ โ โ Timeout Rule โ โ โ โ
โ โ (Dashboard) โ โ โ โ Modify Rule โ โ โ โ
โ โ โ โ โ โโโโโโโโโโโโโโโโโโ โ โ โ
โ โโโโโโโโโโโโโโโโ โ โโโโโโโโโโโโโโโโโโโโโโโโ โ โ
โ โ โ โ โ
โ โโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโ โ
โ โ ๋ฃฐ ํต๊ณผ ์ โ
โ โผ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ ์ค์ ์ธ๋ถ API โ โ
โ โ https://api.kakao.com โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
์ญํ : HTTP ์์ฒญ์ ๊ฐ๋ก์ฑ ์ฅ์ ๋ฃฐ์ ์ ์ฉํ ๋ค ์ค์ ํ๊ฒ์ผ๋ก ํฌ์๋ฉ
๊ธฐ์ ์ ํ ์ด์
| ๊ธฐ์ | ์ ํ ์ด์ |
|---|---|
| Spring Boot 4 | Auto-configuration, Actuator ํตํฉ, ์ํ๊ณ |
| Netty (via Reactor Netty) | ๋ ผ๋ธ๋กํน I/O, ๋์ ๋์ ์ฐ๊ฒฐ ์ฒ๋ฆฌ, Spring WebFlux ๊ธฐ๋ฐ |
| Spring WebFlux | ๋ฆฌ์กํฐ๋ธ ํ๋ก์ ๊ตฌํ์ ์ต์ |
| WebClient | ๋ ผ๋ธ๋กํน HTTP ํด๋ผ์ด์ธํธ, ํ์์์ ์ ์ด ์ฉ์ด |
์ฒ๋ฆฌ ํ๋ฆ (Request Pipeline)
์ ์ฒด ํ์ดํ๋ผ์ธ์ CLAUDE.md ์ฐธ์กฐ
์ธ๋ฐ์ด๋ ์์ฒญ (:18080)
โ 1. RequestLoggingFilter (์์ฒญ ๊ธฐ๋ก)
โ 2. RuleMatchingFilter (์ ์ฉํ ๋ฃฐ ํ์ + ์กฐ๊ฑด ๋งค์นญ)
โ 3. RateLimitFilter (Rate Limit ํ์ธ)
โ 4. LatencyFilter (์ง์ฐ ์ ์ฉ - Mono.delay, ๋
ผ๋ธ๋กํน)
โ 5. ErrorInjectionFilter (์๋ฌ ํ๋ฅ ํ๋จ โ ์ฆ์ ์๋ฌ ์๋ต or ํต๊ณผ)
โ 6. RequestModifyFilter (ํค๋/๋ฐ๋ ๋ณ์กฐ)
โ 7. ProxyForwardFilter (์ค์ ํ๊ฒ์ผ๋ก ํฌ์๋ฉ - WebClient)
โ 8. BandwidthThrottleFilter (์๋ต ๋์ญํญ ์ ํ)
โ 9. TruncateFilter (๋ถ๋ถ ์๋ต ์ ๋จ)
โ 10. ResponseModifyFilter (์๋ต ๋ณ์กฐ)
โ 11. ResponseLoggingFilter (์๋ต ๊ธฐ๋ก)
โ ํด๋ผ์ด์ธํธ์ ์๋ต ๋ฐํ
์ญํ : ๋ค์ด์จ ์์ฒญ์ ์ด๋ค ์ฅ์ ๋ฅผ ์ด๋ป๊ฒ ์ ์ฉํ ์ง ๊ฒฐ์ ํ๋ ํต์ฌ ๋ก์ง
๋ฃฐ ๋งค์นญ ์๊ณ ๋ฆฌ์ฆ
์์ฒญ: GET /api/user/123/profile
๋ฑ๋ก๋ ๋ฃฐ ์์๋๋ก ํ๊ฐ:
Rule 1: path="/api/**" โ โ
๋งค์นญ (์ฐ์ ์์ 100)
Rule 2: path="/api/user/**" โ โ
๋งค์นญ (์ฐ์ ์์ 90)
Rule 3: path="/api/user/*/profile" โ โ
๋งค์นญ (์ฐ์ ์์ 80)
โ ๊ฐ์ฅ ๊ตฌ์ฒด์ ์ธ Rule 3 ์ ์ฉ (๊ฒฝ๋ก ํจํด์ด ๊ฐ์ฅ ์ข์ ๊ฒ ์ฐ์ )
๋ง์ฝ ์ฐ์ ์์๊ฐ ๊ฐ๋ค๋ฉด ๋ฑ๋ก ์์๋๋ก ์ ์ฉ
๋ฃฐ ๋๋ฉ์ธ ๋ชจ๋ธ
์ ์ฒด ๋๋ฉ์ธ ๋ชจ๋ธ ์์ธ๋ CLAUDE.md ์ฐธ์กฐ
ํต์ฌ ํ๋: pathPattern (Ant-style), method, enabled, priority, ๊ทธ๋ฆฌ๊ณ nullableํ ์ฅ์ ์ค์ ๋ค(latency, error, timeout, responseModify, rateLimit, bandwidth, truncate, stateful)๋ก ๊ตฌ์ฑ๋ Java Record์
๋๋ค.
์ญํ : ์ฌ๋ฌ ๋ฃฐ์ ๋ฌถ์์ ํ์ผ๋ก ์ ์ฅ/๋ถ๋ฌ์ค๊ธฐํ์ฌ ํ ๊ฐ ๊ณต์ ๊ฐ๋ฅํ๊ฒ ํจ
์๋๋ฆฌ์ค ํ์ผ ๊ตฌ์กฐ
# scenarios/toss-payments.yml
metadata:
name: "ํ ์คํ์ด๋จผ์ธ ์ฅ์ ์๋ฎฌ๋ ์ด์
"
description: "๊ฒฐ์ API ํ์์์ ๋ฐ ๊ฐํ์ ์คํจ ์๋๋ฆฌ์ค"
author: "Max"
version: "1.0.0"
target: "https://api.tosspayments.com"
rules:
- id: "payment-timeout"
name: "๊ฒฐ์ ์น์ธ ํ์์์"
path: "/v1/payments/confirm"
method: POST
enabled: false # ๊ธฐ๋ณธ ๋นํ์ฑํ
priority: 10
timeout:
after_ms: 30000 # 30์ด ํ์์์ (์ค์ ์นด๋์ฌ ํ์์์ ์๋ฎฌ๋ ์ด์
)
- id: "payment-error"
name: "๊ฐํ์ ๊ฒฐ์ ์คํจ"
path: "/v1/payments/**"
method: ALL
enabled: false
priority: 20
error:
rate: 0.1 # 10% ํ๋ฅ ๋ก ์คํจ
status_code: 500
body: |
{
"code": "PAYMENT_FAILED",
"message": "๊ฒฐ์ ์ฒ๋ฆฌ ์ค ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค."
}
- id: "slow-webhook"
name: "์นํ
์ง์ฐ"
path: "/v1/webhooks"
method: POST
enabled: false
priority: 30
latency:
range_ms: [2000, 8000] # 2~8์ด ๋๋ค ์ง์ฐ์ญํ : ์๋๋ฆฌ์ค/๋ฃฐ์ CRUD ๋ฐ ์ค์๊ฐ ์์ฒญ ๋ก๊ทธ ๋ชจ๋ํฐ๋ง ์น UI
๊ธฐ์ ์คํ: React 19 + TypeScript + Vite
์ปดํฌ๋ํธ ๊ตฌ์กฐ
App
โโโ Header (ํ์ดํ + ์ฐ๊ฒฐ ์ํ)
โโโ RuleList (๋ฃฐ ๋ชฉ๋ก ํจ๋)
โ โโโ RuleCard (๋ฃฐ ON/OFF ํ ๊ธ, ์ญ์ , ํ๊ทธ, ์์ธ)
โโโ LogTable (์ค์๊ฐ ์์ฒญ ๋ก๊ทธ ํ
์ด๋ธ)
์ปค์คํ Hooks
| Hook | ์ญํ |
|---|---|
useRules |
๋ฃฐ CRUD + toggle (REST API) |
useLogStream |
SSE ์ค์๊ฐ ๋ก๊ทธ ์คํธ๋ฆผ (์๋ ์ฌ์ฐ๊ฒฐ) |
useRuleStream |
SSE ๋ฃฐ ๋ณ๊ฒฝ ์ด๋ฒคํธ |
์ค์๊ฐ ํต์ ๋ฐฉ์
Dashboard (React)
โ
โ SSE (Server-Sent Events)
โ GET /api/v1/logs/stream
โ
โผ
chaos-proxy (Spring WebFlux)
โ
โ Flux<LogEntry> (๋
ผ๋ธ๋กํน ์คํธ๋ฆผ)
โ
โผ
Sinks.Many<LogEntry> (์ธ๋ฉ๋ชจ๋ฆฌ ์ด๋ฒคํธ ๋ฒ์ค)
SSE๋ฅผ ์ ํํ ์ด์ : ์์ฒญ ๋ก๊ทธ๋ ์๋ฒโํด๋ผ์ด์ธํธ ๋จ๋ฐฉํฅ ์คํธ๋ฆผ์ด๋ฏ๋ก WebSocket์ ๋ณต์ก์ฑ์ด ๋ถํ์ํฉ๋๋ค. SSE๋ HTTP ๊ธฐ๋ฐ์ด๋ผ CORS, ์ธ์ฆ ๋ฑ ๊ธฐ์กด Spring Security ์ค์ ์ ๊ทธ๋๋ก ์ฌ์ฌ์ฉํ ์ ์์ต๋๋ค.
Client App chaos-proxy Rule Engine Target API
โ โ โ โ
โ GET /api/user โ โ โ
โโโโโโโโโโโโโโโโโโโบโ โ โ
โ โ matchRules(req) โ โ
โ โโโโโโโโโโโโโโโโโโโโบโ โ
โ โ [LatencyRule] โ โ
โ โโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ โ
โ โ Mono.delay(3s) โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ (3์ด ๋๊ธฐ) โ โ
โ โ โ โ
โ โ โ GET /api/user โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโบ
โ โ โ โ
โ โ โ 200 OK โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โ โ โ
โ 200 OK (3024ms) โ โ โ
โโโโโโโโโโโโโโโโโโโโ โ โ
โ โ โ โ
์์ฒญ ๋์ฐฉ
โ
โผ
ErrorRule ์ ์ฉ ์ฌ๋ถ ํ์ธ
โ
โโโ ๋ฃฐ ์์ ๋๋ ๋นํ์ฑํ
โ โ
โ โโโโบ ํ๊ฒ์ผ๋ก ํฌ์๋ฉ (์ ์ ํ๋ฆ)
โ
โโโ ErrorRule ํ์ฑํ
โ
โผ
Math.random() < rule.rate ?
โ
โโโ YES (์๋ฌ ์ฃผ์
)
โ โ
โ โโโโบ ์ค์ ๋ status/body๋ก ์ฆ์ ์๋ต
โ (ํ๊ฒ์ ์์ฒญํ์ง ์์)
โ
โโโ NO (์ ์ ์ฒ๋ฆฌ)
โ
โโโโบ ํ๊ฒ์ผ๋ก ํฌ์๋ฉ
chaos-http๋ ์ธ๋ฉ๋ชจ๋ฆฌ ์ํ ๊ด๋ฆฌ๋ฅผ ๊ธฐ๋ณธ์ผ๋ก ํฉ๋๋ค. ์ฌ์์ ์ ํ์ผ์์ ์๋๋ฆฌ์ค๋ฅผ ๋ค์ ๋ก๋ํฉ๋๋ค.
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ InMemoryRuleStore โ
โ โ
โ ConcurrentHashMap<String, โ
โ ChaosRule> โ
โ โ
โ - thread-safe ์ฝ๊ธฐ/์ฐ๊ธฐ โ
โ - ์ฌ์์ ์ scenario.yml ์ฌ๋ก๋ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ ๋ณ๊ฒฝ ๋ฐ์ ์
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Sinks.Many<RuleEvent> โ
โ (SSE๋ก Dashboard์ ์ค์๊ฐ ํธ์) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
์ค๊ณ ๊ฒฐ์ : ์๊ตฌ ์ ์ฅ(DB)์ ์๋์ ์ผ๋ก ์ ์ธํ์ต๋๋ค. chaos-http๋ ๋ก์ปฌ ๊ฐ๋ฐ ๋๊ตฌ๋ก, ๋จ์์ฑ์ด ํต์ฌ ๊ฐ์น์ ๋๋ค. ์๋๋ฆฌ์ค ์๊ตฌ ์ ์ฅ์ YAML ํ์ผ๋ก ์ถฉ๋ถํฉ๋๋ค.
| ์ํฉ | ๋ชฉํ ์ถ๊ฐ ์ง์ฐ |
|---|---|
| ๋ฃฐ ์์ (ํจ์ค์ค๋ฃจ) | < 1ms |
| ๋ฃฐ ๋งค์นญ๋ง (์ง์ฐ ๋ฏธ์ ์ฉ) | < 2ms |
| ์ง์ฐ ๋ฃฐ ์ ์ฉ | ์ค์ ๊ฐ ยฑ 10ms |
์์ฒญ ๋ก๊ทธ๋ ๋ง ๋ฒํผ(Circular Buffer) ๋ก ๊ด๋ฆฌํฉ๋๋ค.
// ์ต๊ทผ 10,000๊ฑด๋ง ์ธ๋ฉ๋ชจ๋ฆฌ ๋ณด๊ด
private final Deque<RequestLog> logBuffer =
new ArrayDeque<>(MAX_LOG_SIZE);
// ์ด๊ณผ ์ ๊ฐ์ฅ ์ค๋๋ ๊ฒ๋ถํฐ ์ ๊ฑฐ
if (logBuffer.size() >= MAX_LOG_SIZE) {
logBuffer.pollFirst();
}chaos-http๋ ๊ฐ๋ฐ/ํ ์คํธ ํ๊ฒฝ ์ ์ฉ์ ๋๋ค.
- ๊ธฐ๋ณธ๊ฐ์ผ๋ก
localhost(127.0.0.1)์์๋ง ์ ์ ํ์ฉ - ํ๋ก๋์ ํ๊ฒฝ ์ฌ์ฉ์ ๋ง๋ ๊ฒฝ๊ณ ๋ฉ์์ง ํ์
- CORS ๊ธฐ๋ณธ ์ค์ :
localhost์ค๋ฆฌ์ง๋ง ํ์ฉ
# application.yml
chaos:
security:
allow-origins:
- http://localhost:9090
- http://127.0.0.1:9090
warn-if-not-localhost: true # ์ธ๋ถ IP ๋ฐ์ธ๋ฉ ์ ๊ฒฝ๊ณ ์์ฒญ ๋ก๊ทธ์์ ๋ฏผ๊ฐํ ํค๋๋ ์๋ ๋ง์คํน๋ฉ๋๋ค.
chaos:
logging:
mask-headers:
- Authorization
- Cookie
- X-Api-KeyAI ๊ธฐ๋ฅ์ ํต์ฌ ํ๋ก์ ์์ง๊ณผ ์์ ํ ๋ถ๋ฆฌ๋ ๋ ๋ฆฝ ๋ ์ด์ด์ ๋๋ค. Spring AI ์คํํฐ ์์กด์ฑ์ ์ถ๊ฐํ์ง ์์ผ๋ฉด AI ์ฝ๋๋ ์ ํ ์คํ๋์ง ์์ต๋๋ค.
AI ๋ ์ด์ด์ ์์ธ ๊ตฌ์กฐ, ํ๋ก๋ฐ์ด๋ ์ค์ , ํ๋กฌํํธ ์์ง๋์ด๋ง์ AI_INTEGRATION.md ์ฐธ์กฐ
ํต์ฌ ์ค๊ณ ์์น:
- Spring AI (
ChatModel) ํ์ฉ โ ํ๋ก๋ฐ์ด๋ ์ ํ =application.yml์ค์ ๋ณ๊ฒฝ๋ง์ผ๋ก ์๋ฃ - Spring AI์ ์๋ ๊ตฌ์ฑ์ผ๋ก ์ธ์ฆ, HTTP ํธ์ถ, ์ง๋ ฌํ๋ฅผ ํ๋ ์์ํฌ๊ฐ ์ฒ๋ฆฌ
- AI ๋นํ์ฑํ ์ AI API ์๋ํฌ์ธํธ๋ 503 ๋ฐํ (ํต์ฌ ํ๋ก์ ๊ธฐ๋ฅ์ ์ํฅ ์์)
- ํ๋กฌํํธ ๋ฒ์ ๊ด๋ฆฌ, confidence ์ ์ + alternatives๋ก ๋ถํ์ค์ฑ ํํ
Java SPI(Service Provider Interface)๋ฅผ ํตํด ์ปค์คํ ์ฅ์ ํํฐ๋ฅผ ํ์ฅํ ์ ์์ต๋๋ค.
public interface ChaosFilter {
/** ํ๋ฌ๊ทธ์ธ ๊ณ ์ ์ด๋ฆ */
String getName();
/** ํํฐ ์ฒด์ธ ๋ด ์คํ ์์ (๋ฎ์์๋ก ๋จผ์ ) */
int getOrder();
/** ์ด ๋ฃฐ์ ๋ํด ํํฐ๋ฅผ ์ ์ฉํ ์ง ์ฌ๋ถ */
boolean supports(ChaosRule rule);
/** ์ฅ์ ์ฃผ์
๋ก์ง */
Mono<ServerResponse> apply(ServerRequest request, ChaosRule rule,
ChaosFilterChain chain);
}1. ๋นํธ์ธ (Spring Bean)
@Component
public class NthRequestFailFilter implements ChaosFilter {
private final AtomicInteger counter = new AtomicInteger(0);
@Override
public Mono<ServerResponse> apply(ServerRequest request, ChaosRule rule,
ChaosFilterChain chain) {
int n = rule.getExtension("nthRequest.failFirst", Integer.class);
if (counter.incrementAndGet() <= n) {
return ServerResponse.status(500).bodyValue("nth-fail");
}
return chain.next(request, rule);
}
}2. ์ธ๋ถ JAR ํ๋ฌ๊ทธ์ธ
plugins/
โโโ my-custom-filter-1.0.jar
โโโ META-INF/services/net.chimaek.chaosproxy.plugin.ChaosFilter
chaos-http ์์ ์ plugins/ ๋๋ ํ ๋ฆฌ์ JAR์ ์๋ ์ค์บํ์ฌ ๋ก๋ํฉ๋๋ค.
| ํ๋ฌ๊ทธ์ธ | ์ค๋ช |
|---|---|
NthRequestFailFilter |
N๋ฒ์งธ ์์ฒญ๊น์ง๋ง ์คํจ โ ์ดํ ์ฑ๊ณต (Retry ํ ์คํธ) |
CronScheduleFilter |
cron ํํ์ ๊ธฐ๋ฐ ์๊ฐ๋๋ณ ํ์ฑํ |
CorrelationIdFilter |
ํน์ ํธ๋ ์ด์ค ID๋ฅผ ๊ฐ์ง ์์ฒญ๋ง ์ฅ์ ์ฃผ์ |
GradualDegradationFilter |
์๊ฐ์ ๋ฐ๋ผ ์๋ฌ์จ ์ ์ง์ ์ฆ๊ฐ (0% โ 100%) |
JitterFilter |
์๋ต ์๊ฐ์ ๋๋ค ์งํฐ ์ถ๊ฐ (๋คํธ์ํฌ ๋ถ์์ ์๋ฎฌ๋ ์ด์ ) |
ํ๋ก์๋ฅผ ํต๊ณผํ๋ ์ค์ ํธ๋ํฝ์ ์บก์ฒํ๊ณ ์๋๋ฆฌ์ค๋ก ๋ณํํ ์ ์์ต๋๋ค.
โโโโโโโโโโโโ ์์ฒญ โโโโโโโโโโโโโโโโ ํฌ์๋ฉ โโโโโโโโโโโโ
โ Client โโโโโโโโโโโโโโบโ chaos-proxy โโโโโโโโโโโโโโโโบโ Target โ
โ App โโโโโโโโโโโโโโโ (๋
นํ ๋ชจ๋) โโโโโโโโโโโโโโโโโ API โ
โโโโโโโโโโโโ ์๋ต โโโโโโโโฌโโโโโโโโ ์๋ต โโโโโโโโโโโโ
โ
โ ์์ฒญ+์๋ต ์ ๊ธฐ๋ก
โผ
โโโโโโโโโโโโโโโโ
โ RecordStore โ
โ (์ธ๋ฉ๋ชจ๋ฆฌ) โ
โโโโโโโโฌโโโโโโโโ
โ ๋ด๋ณด๋ด๊ธฐ
โผ
โโโโโโโโโโโโโโโโโโโโโโโ
โ scenario.yml โ
โ ๋๋ HAR ํ์ผ โ
โโโโโโโโโโโโโโโโโโโโโโโ
โโโโ Pod โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โ
โ โโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโ โ
โ โ App โโโโโโบโ chaos-http โโโโโโโโบ External API
โ โ Container โ โ (sidecar) โ โ
โ โโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโ โ
โ โ โ
โ ConfigMap/CRD โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Central Dashboard โ
โ (์ค์ ๊ด๋ฆฌ ์ฝ์) โ
โโโโโโโโโโโโฌโโโโโโโโโโโโโโโ
โ Redis pub/sub
โโโโโโโโผโโโโโโโโโโโ
โผ โผ โผ
โโโโโโโโ โโโโโโโโ โโโโโโโโ
โProxy โ โProxy โ โProxy โ
โ #1 โ โ #2 โ โ #3 โ
โโโโโโโโ โโโโโโโโ โโโโโโโโ
์ฌ๋ฌ chaos-http ์ธ์คํด์ค ๊ฐ ๋ฃฐ์ ๋๊ธฐํํ์ฌ ๋ง์ดํฌ๋ก์๋น์ค ํธ์ถ ์ฒด์ธ ์ ์ฒด์ ๊ฑธ์น ์ฅ์ ์๋๋ฆฌ์ค๋ฅผ ์คํํ ์ ์์ต๋๋ค.
๋จ์ ๋ฃฐ ์ ์ฉ์ ๋์ด, ์๊ฐ ์์์ ๋ฐ๋ฅธ ์ฅ์ ์ํ์ค๋ฅผ ์๋ ์คํํ๋ ์ค์ผ์คํธ๋ ์ด์ ์์ง์ ๋๋ค.
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ ExperimentExecutor โ
โ โ
โ 1. Steady-State ๊ฒ์ฆ (์ฌ์ ์กฐ๊ฑด ํ์ธ) โ
โ โโ ํ๊ฒ API ํฌ์ค์ฒดํฌ, ์๋ฌ์จ < 1% ํ์ธ โ
โ โ
โ 2. ์ฅ์ ์ํ์ค ์คํ โ
โ โโ Step 1: ์ง์ฐ 500ms ์ ์ฉ โ 30์ด ์ ์ง โ
โ โโ Step 2: ์๋ฌ์จ 10%๋ก ์ฆ๊ฐ โ 60์ด ์ ์ง โ
โ โโ Step 3: ํ์์์ 5s ์ ์ฉ โ 30์ด ์ ์ง โ
โ โ
โ 3. Steady-State ์ฌ๊ฒ์ฆ (์ฌํ ์กฐ๊ฑด ํ์ธ) โ
โ โโ ์๋ฌ์จ ๋ณต์ ํ์ธ, ์๋ต์๊ฐ ์ ์ํ ํ์ธ โ
โ โ
โ 4. ๊ฒฐ๊ณผ ๋ฆฌํฌํธ ์์ฑ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
experiment:
name: "๊ฒฐ์ API ๋ณต์๋ ฅ ํ
์คํธ"
timeout: 300s # ์ ์ฒด ์คํ ์ ํ ์๊ฐ
dry-run: false # true๋ฉด ์ค์ ์ ์ฉ ์์ด ๊ฒ์ฆ๋ง
steady-state:
- type: health-check
target: "/v1/payments/health"
expect: 200
- type: error-rate
threshold: "< 1%"
steps:
- name: "์ง์ฐ ์ฃผ์
"
rule: { path: "/v1/payments/**", latency: { fixedMs: 500 } }
duration: 30s
- name: "์๋ฌ ์ฃผ์
"
rule: { path: "/v1/payments/**", error: { rate: 0.1, statusCode: 500 } }
duration: 60s
rollback: auto # ์คํจ ์ ์๋ ๋กค๋ฐฑ์นด์ค์ค ์์ง๋์ด๋ง์์ ๊ฐ์ฅ ์ค์ํ ๊ฒ์ ํต์ ๋ ์คํ์ ๋๋ค. ์ฅ์ ์ฃผ์ ์ด ์์ ๋ฒ์๋ฅผ ๋ฒ์ด๋์ง ์๋๋ก ๋ค์ค ์์ ์ฅ์น๋ฅผ ์ ๊ณตํฉ๋๋ค.
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Safety Layer โ
โ โ
โ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โ Kill Switch โ โ Auto-Rollback โโ
โ โ (๊ธด๊ธ ์ค์ง) โ โ (์๋ฌ์จ ์๊ณ์น ์ด๊ณผ ์ โโ
โ โ POST /safetyโ โ ์๋์ผ๋ก ๋ชจ๋ ๋ฃฐ ๋นํ์ฑํ) โโ
โ โ /kill-switchโ โ โโ
โ โโโโโโโโฌโโโโโโโ โโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโ
โ โ โ โ
โ โผ โผ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ InMemoryRuleStore โ โ
โ โ ๋ชจ๋ ๋ฃฐ enabled=false ์ฆ์ ์ ํ โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โ โโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโ โ
โ โ Experiment โ โ Dry-Run Mode โ โ
โ โ Timeout โ โ (์ค์ ์ ์ฉ ์์ด โ โ
โ โ (์คํ ์ ํ ์๊ฐ โ โ ์ํฅ ๋ฒ์๋ง ํ์) โ โ
โ โ ์ด๊ณผ ์ ์๋ ์ค์ง)โ โ โ โ
โ โโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
| ์์ ์ฅ์น | ์ค๋ช | ํธ๋ฆฌ๊ฑฐ |
|---|---|---|
| Kill Switch | ๋ชจ๋ ๋ฃฐ์ ์ฆ์ ๋นํ์ฑํ (1 API ํธ์ถ) | ์๋ (๋์๋ณด๋ ๋ฒํผ/API) |
| Auto-Rollback | ์๋ฌ์จ์ด ์ค์ ์๊ณ์น๋ฅผ ์ด๊ณผํ๋ฉด ์๋ ๋ณต์ | ์๋ (๋ฉํธ๋ฆญ ๊ธฐ๋ฐ) |
| Experiment Timeout | ์คํ์ด ์ต๋ ์๊ฐ์ ์ด๊ณผํ๋ฉด ์๋ ์ค์ง | ์๋ (ํ์ด๋จธ) |
| Dry-Run Mode | ์ฅ์ ๋ฅผ ์ค์ ๋ก ์ ์ฉํ์ง ์๊ณ ์ํฅ ๋ฒ์๋ง ํ์ | ์๋ (์ค์ ) |
| Steady-State ๊ฒ์ฆ | ์คํ ์ ํ ์์คํ ์ํ๋ฅผ ๋น๊ตํ์ฌ ์์ ํ์ธ | ์๋ (์คํ ํ๋ฆ) |