Skip to content

Latest commit

ย 

History

History
581 lines (460 loc) ยท 23.9 KB

File metadata and controls

581 lines (460 loc) ยท 23.9 KB

ARCHITECTURE.md โ€” chaos-http ์‹œ์Šคํ…œ ์•„ํ‚คํ…์ฒ˜


์ „์ฒด ๊ตฌ์กฐ ๊ฐœ์š”

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   โ”‚   โ”‚
โ”‚                                  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

๋ชจ๋“ˆ ์ƒ์„ธ ์„ค๋ช…

1. chaos-proxy (ํ•ต์‹ฌ ์—”์ง„)

์—ญํ• : 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   (์‘๋‹ต ๊ธฐ๋ก)
โ†’ ํด๋ผ์ด์–ธํŠธ์— ์‘๋‹ต ๋ฐ˜ํ™˜

2. Rule Engine

์—ญํ• : ๋“ค์–ด์˜จ ์š”์ฒญ์— ์–ด๋–ค ์žฅ์• ๋ฅผ ์–ด๋–ป๊ฒŒ ์ ์šฉํ• ์ง€ ๊ฒฐ์ •ํ•˜๋Š” ํ•ต์‹ฌ ๋กœ์ง

๋ฃฐ ๋งค์นญ ์•Œ๊ณ ๋ฆฌ์ฆ˜

์š”์ฒญ: 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์ž…๋‹ˆ๋‹ค.


3. Scenario System

์—ญํ• : ์—ฌ๋Ÿฌ ๋ฃฐ์˜ ๋ฌถ์Œ์„ ํŒŒ์ผ๋กœ ์ €์žฅ/๋ถˆ๋Ÿฌ์˜ค๊ธฐํ•˜์—ฌ ํŒ€ ๊ฐ„ ๊ณต์œ  ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•จ

์‹œ๋‚˜๋ฆฌ์˜ค ํŒŒ์ผ ๊ตฌ์กฐ

# 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์ดˆ ๋žœ๋ค ์ง€์—ฐ

4. Dashboard (React SPA)

์—ญํ• : ์‹œ๋‚˜๋ฆฌ์˜ค/๋ฃฐ์˜ 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-Key


AI ๋ ˆ์ด์–ด (์„ ํƒ์  ์˜ต์…˜)

AI ๊ธฐ๋Šฅ์€ ํ•ต์‹ฌ ํ”„๋ก์‹œ ์—”์ง„๊ณผ ์™„์ „ํžˆ ๋ถ„๋ฆฌ๋œ ๋…๋ฆฝ ๋ ˆ์ด์–ด์ž…๋‹ˆ๋‹ค. Spring AI ์Šคํƒ€ํ„ฐ ์˜์กด์„ฑ์„ ์ถ”๊ฐ€ํ•˜์ง€ ์•Š์œผ๋ฉด AI ์ฝ”๋“œ๋Š” ์ „ํ˜€ ์‹คํ–‰๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

AI ๋ ˆ์ด์–ด์˜ ์ƒ์„ธ ๊ตฌ์กฐ, ํ”„๋กœ๋ฐ”์ด๋” ์„ค์ •, ํ”„๋กฌํ”„ํŠธ ์—”์ง€๋‹ˆ์–ด๋ง์€ AI_INTEGRATION.md ์ฐธ์กฐ

ํ•ต์‹ฌ ์„ค๊ณ„ ์›์น™:

  • Spring AI (ChatModel) ํ™œ์šฉ โ€” ํ”„๋กœ๋ฐ”์ด๋” ์ „ํ™˜ = application.yml ์„ค์ • ๋ณ€๊ฒฝ๋งŒ์œผ๋กœ ์™„๋ฃŒ
  • Spring AI์˜ ์ž๋™ ๊ตฌ์„ฑ์œผ๋กœ ์ธ์ฆ, HTTP ํ˜ธ์ถœ, ์ง๋ ฌํ™”๋ฅผ ํ”„๋ ˆ์ž„์›Œํฌ๊ฐ€ ์ฒ˜๋ฆฌ
  • AI ๋น„ํ™œ์„ฑํ™” ์‹œ AI API ์—”๋“œํฌ์ธํŠธ๋Š” 503 ๋ฐ˜ํ™˜ (ํ•ต์‹ฌ ํ”„๋ก์‹œ ๊ธฐ๋Šฅ์— ์˜ํ–ฅ ์—†์Œ)
  • ํ”„๋กฌํ”„ํŠธ ๋ฒ„์ „ ๊ด€๋ฆฌ, confidence ์ ์ˆ˜ + alternatives๋กœ ๋ถˆํ™•์‹ค์„ฑ ํ‘œํ˜„

ํ”Œ๋Ÿฌ๊ทธ์ธ ์‹œ์Šคํ…œ (SPI)

Java SPI(Service Provider Interface)๋ฅผ ํ†ตํ•ด ์ปค์Šคํ…€ ์žฅ์•  ํ•„ํ„ฐ๋ฅผ ํ™•์žฅํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ChaosFilter ์ธํ„ฐํŽ˜์ด์Šค

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 ํŒŒ์ผ       โ”‚
                    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

ํ™•์žฅ ์•„ํ‚คํ…์ฒ˜ (ํ–ฅํ›„)

Kubernetes ์‚ฌ์ด๋“œ์นด ๋ชจ๋“œ

โ”Œโ”€โ”€โ”€ Pod โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                                          โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”     โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚
โ”‚  โ”‚ App       โ”‚โ”€โ”€โ”€โ”€โ–บโ”‚ chaos-http       โ”‚โ”€โ”€โ”€โ”€โ”€โ”€โ–บ External API
โ”‚  โ”‚ Container โ”‚     โ”‚ (sidecar)        โ”‚ โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜     โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚
โ”‚                            โ”‚             โ”‚
โ”‚                     ConfigMap/CRD        โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

๋ถ„์‚ฐ ์นด์˜ค์Šค ์กฐ์œจ

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚   Central Dashboard     โ”‚
โ”‚   (์ค‘์•™ ๊ด€๋ฆฌ ์ฝ˜์†”)       โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
           โ”‚ Redis pub/sub
    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
    โ–ผ      โ–ผ          โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚Proxy โ”‚ โ”‚Proxy โ”‚ โ”‚Proxy โ”‚
โ”‚ #1   โ”‚ โ”‚ #2   โ”‚ โ”‚ #3   โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

์—ฌ๋Ÿฌ chaos-http ์ธ์Šคํ„ด์Šค ๊ฐ„ ๋ฃฐ์„ ๋™๊ธฐํ™”ํ•˜์—ฌ ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค ํ˜ธ์ถœ ์ฒด์ธ ์ „์ฒด์— ๊ฑธ์นœ ์žฅ์•  ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


์‹คํ—˜ ์˜ค์ผ€์ŠคํŠธ๋ ˆ์ด์…˜ (Experiment Orchestration)

๋‹จ์ˆœ ๋ฃฐ ์ ์šฉ์„ ๋„˜์–ด, ์‹œ๊ฐ„ ์ˆœ์„œ์— ๋”ฐ๋ฅธ ์žฅ์•  ์‹œํ€€์Šค๋ฅผ ์ž๋™ ์‹คํ–‰ํ•˜๋Š” ์˜ค์ผ€์ŠคํŠธ๋ ˆ์ด์…˜ ์—”์ง„์ž…๋‹ˆ๋‹ค.

์‹คํ—˜ ํ๋ฆ„

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                    ExperimentExecutor                        โ”‚
โ”‚                                                             โ”‚
โ”‚  1. Steady-State ๊ฒ€์ฆ (์‚ฌ์ „ ์กฐ๊ฑด ํ™•์ธ)                        โ”‚
โ”‚     โ””โ”€ ํƒ€๊ฒŸ API ํ—ฌ์Šค์ฒดํฌ, ์—๋Ÿฌ์œจ < 1% ํ™•์ธ                    โ”‚
โ”‚                                                             โ”‚
โ”‚  2. ์žฅ์•  ์‹œํ€€์Šค ์‹คํ–‰                                          โ”‚
โ”‚     โ”œโ”€ Step 1: ์ง€์—ฐ 500ms ์ ์šฉ โ†’ 30์ดˆ ์œ ์ง€                   โ”‚
โ”‚     โ”œโ”€ Step 2: ์—๋Ÿฌ์œจ 10%๋กœ ์ฆ๊ฐ€ โ†’ 60์ดˆ ์œ ์ง€                  โ”‚
โ”‚     โ””โ”€ Step 3: ํƒ€์ž„์•„์›ƒ 5s ์ ์šฉ โ†’ 30์ดˆ ์œ ์ง€                   โ”‚
โ”‚                                                             โ”‚
โ”‚  3. Steady-State ์žฌ๊ฒ€์ฆ (์‚ฌํ›„ ์กฐ๊ฑด ํ™•์ธ)                      โ”‚
โ”‚     โ””โ”€ ์—๋Ÿฌ์œจ ๋ณต์› ํ™•์ธ, ์‘๋‹ต์‹œ๊ฐ„ ์ •์ƒํ™” ํ™•์ธ                   โ”‚
โ”‚                                                             โ”‚
โ”‚  4. ๊ฒฐ๊ณผ ๋ฆฌํฌํŠธ ์ƒ์„ฑ                                          โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

์‹คํ—˜ ์ •์˜ (YAML)

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 Mechanisms)

์นด์˜ค์Šค ์—”์ง€๋‹ˆ์–ด๋ง์—์„œ ๊ฐ€์žฅ ์ค‘์š”ํ•œ ๊ฒƒ์€ ํ†ต์ œ๋œ ์‹คํ—˜์ž…๋‹ˆ๋‹ค. ์žฅ์•  ์ฃผ์ž…์ด ์˜ˆ์ƒ ๋ฒ”์œ„๋ฅผ ๋ฒ—์–ด๋‚˜์ง€ ์•Š๋„๋ก ๋‹ค์ค‘ ์•ˆ์ „์žฅ์น˜๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

์•ˆ์ „์žฅ์น˜ ์•„ํ‚คํ…์ฒ˜

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚              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 ๊ฒ€์ฆ ์‹คํ—˜ ์ „ํ›„ ์‹œ์Šคํ…œ ์ƒํƒœ๋ฅผ ๋น„๊ตํ•˜์—ฌ ์•ˆ์ „ ํ™•์ธ ์ž๋™ (์‹คํ—˜ ํ๋ฆ„)