ЁЯПл The SchoolтА║ЁЯЫая╕П SREтА║ЁЯРд рдзрдбрд╛ 05 тАФ Release engineering: canary рдЪрд╛ рдирд┐рд░реНрдгрдп рдЖрдХрдбреНрдпрд╛рдВрд╡рд░реВрди рдШреНрдпрд╛
ЁЯЦ╝я╕П See the drawing + lab ЁЯПа Course home ЁЯМ┐ Branch on GitHub тЬПя╕П View source
ЁЯЦ╝я╕П рдЖрдХреГрддреА рдЖрдгрд┐ labThe drawing + lab рдкреВрд░реНрдг рдкрд╛рдирд╛рд╡рд░ рдЙрдШрдбрд╛ тЖЧOpen full page тЖЧ

ЁЯРд рдзрдбрд╛ 05 тАФ Release engineering: canary рдЪрд╛ рдирд┐рд░реНрдгрдп рдЖрдХрдбреНрдпрд╛рдВрд╡рд░реВрди рдШреНрдпрд╛

ЁЯУН рддреБрдореНрд╣реА рдЗрдереЗ рдЖрд╣рд╛рдд: 12 рдкреИрдХреА рдзрдбрд╛ 05 ┬╖ рдорд╛рдЧреЗ: lesson-04-on-call ┬╖ рдкреБрдвреЗ: lesson-06-progressive-delivery


ЁЯУж рдпрд╛ рдмреНрд░рдБрдЪрдордзреНрдпреЗ рдХрд╛рдп рдЖрд╣реЗ

рднрд╛рдЧ 2 рд╕реБрд░реВ рд╣реЛрддреЛ: рд╕реБрд░рдХреНрд╖рд┐рддрдкрдгреЗ рдмрджрд▓ рдХрд░рдгреЗ. рдмрд╣реБрддреЗрдХ outages рдПрдЦрд╛рджреНрдпрд╛ рдмрджрд▓рд╛рдкрд╛рд╕реВрди рд╕реБрд░реВ рд╣реЛрддрд╛рдд тАФ рдирд╡рд╛ code, рдирд╡рд╛ config, рдирд╡рд╛ flag. рд╣рд╛ рдзрдбрд╛ рдПрдЦрд╛рджрд╛ рдмрджрд▓ рдЖрдзреА traffic рдЪреНрдпрд╛ рдЫреЛрдЯреНрдпрд╛ рднрд╛рдЧрд╛рдХрдбреЗ рдкрд╛рдард╡рддреЛ (canary) рдЖрдгрд┐ рддреНрдпрд╛рдЪреА baseline рд╢реА рддреБрд▓рдирд╛ рдХрд░рддреЛ, dashboard рдХрдбреЗ рдбреЛрд│реНрдпрд╛рдВрдиреА рдмрдШрдгреНрдпрд╛рдРрд╡рдЬреА рдЖрдХрдбреНрдпрд╛рдВрдЪрд╛ рд╡рд╛рдкрд░ рдХрд░реВрди: promote, rollback, рдХреА рдЖрдгрдЦреА рдерд╛рдВрдмрд╛рдпрдЪреЗ. sre/crew.py рдордзреАрд▓ canary_judge рдЖрдгрд┐ sre/demo.py рдордзреАрд▓ canary().

ЁЯзТ 5 рд╡рд░реНрд╖рд╛рдВрдЪреНрдпрд╛ рдореБрд▓рд╛рд▓рд╛ рд╕рдордЬрд╛рд╡рд▓реНрдпрд╛рд╕рд╛рд░рдЦреЗ

рдкрдердХрд╛рд▓рд╛ рдкреНрд░рддреНрдпреЗрдХ рд╡рд░реНрдЧрд╛рдд рдирд╡рд╛ heating boiler рдмрд╕рд╡рд╛рдпрдЪрд╛ рдЖрд╣реЗ. ЁЯФе рдкрдг рдирд╡рд╛ boiler рдЦрд░рд╛рдм рдирд┐рдШрд╛рд▓рд╛ рддрд░?

рдореНрд╣рдгреВрди рддреЗ рддреЛ рдЖрдзреА рдПрдХрд╛рдЪ рд╡рд░реНрдЧрд╛рдд рдмрд╕рд╡рддрд╛рдд. рдЖрдгрд┐ тАФ рд╣реА рд╣реБрд╢рд╛рд░реАрдЪреА рдЧреЛрд╖реНрдЯ рдЖрд╣реЗ тАФ рддреЗ рд╢реЗрдЬрд╛рд░рдЪреНрдпрд╛ рд╡рд░реНрдЧрд╛рдд рдПрдХ рддрд╛рдЬрд╛ рдЬреБрдирд╛ boiler рд╕реБрджреНрдзрд╛ рдмрд╕рд╡рддрд╛рдд, рддреЗрд╡рдвреНрдпрд╛рдЪ рдЖрдХрд╛рд░рд╛рдЪрд╛, рддреЗрд╡рдвреНрдпрд╛рдЪ рд╡рд┐рджреНрдпрд╛рд░реНрдерд┐рдиреАрдВрд╕рд╛рдареА. рдЖрддрд╛ рддреЗ boiler рд╕реЛрдбреВрди рдмрд╛рдХреА рд╕рдЧрд│реНрдпрд╛ рдмрд╛рдмрддреАрдд рд╕рд╛рд░рдЦреНрдпрд╛ рдЕрд╕рд▓реЗрд▓реНрдпрд╛ рджреЛрди рд╡рд░реНрдЧрд╛рдВрдЪреА рддреБрд▓рдирд╛ рдХрд░реВ рд╢рдХрддрд╛рдд.

рдПрдХрд╛ рджрд┐рд╡рд╕рд╛рдирдВрддрд░ рдРрд╢реНрд╡рд░реНрдпрд╛ рддрдХреНрд░рд╛рд░реА рдореЛрдЬрддреЗ. рдЬреБрдирд╛ boiler: 11 рддрдХреНрд░рд╛рд░реА. рдирд╡рд╛ boiler: 42. рд╣реЗ рджреБрд░реНрджреИрд╡ рдирд╛рд╣реА; рд╣рд╛ рдЦрд░рд╛рдм boiler рдЖрд╣реЗ. рддреЛ рдХрд╛рдвреВрди рдЯрд╛рдХрд╛.

рджреБрд╕рд▒реНрдпрд╛ рд╡реЗрд│реА: рдЬреБрдирд╛ 11, рдирд╡рд╛ 13. рд╣рд╛ рдЫреЛрдЯрд╛ рдлрд░рдХ рд░реЛрдЬрдЪрд╛ рд╕рд╛рдорд╛рдиреНрдп рдЪрдврдЙрддрд╛рд░ рдЖрд╣реЗ. рдирд╡рд╛ boiler рд╕рдЧрд│реАрдХрдбреЗ рдмрд╕рд╡рд╛.

рдЖрдгрд┐ рдЖрдЬ рддреНрдпрд╛ рд╡рд░реНрдЧрд╛рдд рдлрдХреНрдд рдХрд╛рд╣реАрдЪ рд╡рд┐рджреНрдпрд╛рд░реНрдерд┐рдиреА рдЖрд▓реНрдпрд╛ рдЕрд╕рддреАрд▓ рддрд░? "рд╕рд╛рдВрдЧрд╛рдпрд▓рд╛ рдЕрдЬреВрди рд▓рд╡рдХрд░ рдЖрд╣реЗ. рдЖрдгрдЦреА рдерд╛рдВрдмрд╛."

ЁЯЧ║я╕П рдЖрдХреГрддреА

flowchart LR
    t["ЁЯЪж traffic"] --> b["baseline<br/>old version, fresh copy<br/>11 errors / 10,000 ┬╖ p99 180 ms"]
    t --> c["canary<br/>new version, same size"]
    b & c --> j{"тЪЦя╕П judge<br/>z-test on errors<br/>p99 ratio"}
    j -->|"A: 13 errors ┬╖ z 0.41"| p["тЬЕ PROMOTE"]
    j -->|"B: 42 errors ┬╖ z 4.26"| r["тЖйя╕П ROLLBACK"]
    j -->|"C: p99 260 ms ┬╖ ├Ч1.44"| r
    j -->|"D: only 400 requests"| e["тП│ EXTEND"]

ЁЯЧ║я╕П рдХрд╛рдврд▓реЗрд▓реА рдЖрд╡реГрддреНрддреА + рдПрдХ lab: https://school-edh.pages.dev/sre/lesson-diagrams.html#l05

тЭУ рдХрд╛рдп

ЁЯдФ рдХрд╛

рдХрд╛рд░рдг рдмрджрд▓ рд╣реЗ outages рдЪреЗ рд╕рд░реНрд╡рд╛рдд рдореЛрдареЗ рдХрд╛рд░рдг рдЖрд╣реЗ, рдЖрдгрд┐ canary рд╡рд╛рдИрдЯ рдмрджрд▓ рдХрд┐рддреА users рдкрд░реНрдпрдВрдд рдкреЛрд╣реЛрдЪрддреЛ рддреЗ рдорд░реНрдпрд╛рджрд┐рдд рдХрд░рддреЗ (рдзрдбрд╛ 06 рддреЗ рдореЛрдЬрддреЛ). рдкрдг canary рддрд┐рдЪреНрдпрд╛ judge рдЗрддрдХреАрдЪ рдЪрд╛рдВрдЧрд▓реА рдЕрд╕рддреЗ. Dashboard рд╡рд░рдЪреНрдпрд╛ рджреЛрди рд░реЗрд╖рд╛рдВрдХрдбреЗ рдбреЛрд│реНрдпрд╛рдВрдиреА рдмрдШрд┐рддрд▓реНрдпрд╛рд╕, рджреЛрдиреНрд╣реА рдЕрдЧрджреА рд▓рд╣рд╛рди рдЕрд╕рддрд╛рдирд╛ 3├Ч error rate рд╕реБрдЯрддреЛ, рдЖрдгрд┐ рд╕рд╛рдорд╛рдиреНрдп рдЧреЛрдВрдЧрд╛рдЯрд╛рдиреЗ рдШрдмрд░рд╛рдЯ рд╣реЛрддреЗ. рд▓рд┐рдЦрд┐рдд, automatic рдирд┐рдпрдо рдкреНрд░рддреНрдпреЗрдХ рд╡реЗрд│реА рд╕рд╛рд░рдЦрд╛рдЪ рдирд┐рд░реНрдгрдп рдШреЗрддреЛ, рдЖрдгрд┐ рддреЛ рдорд┐рдирд┐рдЯрд╛рдВрдд рдХреГрддреА рдХрд░реВ рд╢рдХрддреЛ.

ЁЯФз рдХрд╕реЗ (рдпрд╛ repo рдордзреНрдпреЗ)

sre/crew.py рдордзреАрд▓ canary_judge(base, canary, min_requests=1000, z_limit=3.0, p99_ratio=1.2) рдкреНрд░рддреНрдпреЗрдХ рдмрд╛рдЬреВрд╕рд╛рдареА (errors, requests, p99_ms) рдШреЗрддреЗ рдЖрдгрд┐ (verdict, z, p99 ratio) рдкрд░рдд рджреЗрддреЗ. рддреЗ рдЖрдзреА sample рдЪрд╛ рдЖрдХрд╛рд░ рддрдкрд╛рд╕рддреЗ, рдордЧ error rate рд╡рд░ z-test, рдордЧ p99 ratio. sre/demo.py рдордзреАрд▓ canary() рдПрдХрд╛ baseline рд╡рд┐рд░реБрджреНрдз рдЪрд╛рд░ canaries рдЪрд╛ рдирд┐рд░реНрдгрдп рдШреЗрддреЗ.

ЁЯзк рдХрд░реВрди рдкрд╛рд╣рд╛

python3 sre/demo.py canary
python3 - <<'EOF'
import sys; sys.path.insert(0, "sre"); from crew import canary_judge
base = (11, 10000, 180)
for errors in (11, 20, 25, 30, 42):
    v, z, r = canary_judge(base, (errors, 10000, 180))
    print(f"canary {errors:>2} errors / 10,000 тЖТ z {z:5.2f} тЖТ {v}")
for n in (500, 5000, 50000):
    v, z, r = canary_judge((n * 11 // 10000, n, 180), (n * 22 // 10000, n, 180))
    print(f"double the error rate, {n:>6,} requests each тЖТ {v}" + ("" if z is None else f" (z {z:.2f})"))
EOF

тЬЕ рддрдкрд╛рд╕рд╛ тАФ рддреБрдореНрд╣рд╛рд▓рд╛ рдХрд╛рдп рджрд┐рд╕рд╛рдпрд▓рд╛ рд╣рд╡реЗ

canary рд╣реЗ print рдХрд░рддреЗ:

   canary A: 13 errors / 10,000 ┬╖ p99 185 ms тЖТ z   0.41 ┬╖ p99 ├Ч1.03 тЖТ PROMOTE
   canary B: 42 errors / 10,000 ┬╖ p99 182 ms тЖТ z   4.26 ┬╖ p99 ├Ч1.01 тЖТ ROLLBACK
   canary C: 12 errors / 10,000 ┬╖ p99 260 ms тЖТ z   0.21 ┬╖ p99 ├Ч1.44 тЖТ ROLLBACK
   canary D:  3 errors /    400 ┬╖ p99 179 ms тЖТ z    n/a ┬╖ p99 ├Ч0.99 тЖТ EXTEND  (first minutes: baseline 1 / 400)

рддреБрдордЪрд╛ snippet рд╣реЗ print рдХрд░рддреЛ:

canary 11 errors / 10,000 тЖТ z  0.00 тЖТ PROMOTE
canary 20 errors / 10,000 тЖТ z  1.62 тЖТ PROMOTE
canary 25 errors / 10,000 тЖТ z  2.34 тЖТ PROMOTE
canary 30 errors / 10,000 тЖТ z  2.97 тЖТ PROMOTE
canary 42 errors / 10,000 тЖТ z  4.26 тЖТ ROLLBACK
double the error rate,    500 requests each тЖТ EXTEND
double the error rate,  5,000 requests each тЖТ PROMOTE (z 1.50)
double the error rate, 50,000 requests each тЖТ ROLLBACK (z 4.29)

ЁЯПБ рддреБрдореНрд╣реА рдЖрддреНрддрд╛рдЪ рдХрд╛рдп рд╕рд┐рджреНрдз рдХреЗрд▓реЗ

11 рдРрд╡рдЬреА 30 errors рдЕрд╕рд▓реЗрд▓реА canary тАФ рдЬрд╡рд│рдкрд╛рд╕ 3├Ч рд╡рд╛рдИрдЯ тАФ promote рдЭрд╛рд▓реА, рдХрд╛рд░рдг рдЗрддрдХреНрдпрд╛ рд▓рд╣рд╛рди error rates рд╕рд╛рдареА 10,000 requests рд╣рд╛ z = 3.0 рд╡рд░ рдкреБрд░реЗрд╕рд╛ рдкреБрд░рд╛рд╡рд╛ рдирд╛рд╣реА. рддреЛрдЪ рджреБрдкреНрдкрдЯ error rate 5,000 requests рд╡рд░ pass рдЭрд╛рд▓рд╛ рдЖрдгрд┐ 50,000 рд╡рд░ fail рдЭрд╛рд▓рд╛. рдореНрд╣рдгрдЬреЗ canary рдЪрд╛ рдЖрдХрд╛рд░ рдЖрдгрд┐ рдерд╛рдВрдмрдгреНрдпрд╛рдЪрд╛ рдХрд╛рд▓рд╛рд╡рдзреА рд╣реЗ рдард░рд╡рддрд╛рдд рдХреА judge рд▓рд╛ рдХрд╛рдп рджрд┐рд╕реВ рд╢рдХрддреЗ: рд▓рд╣рд╛рди error rates рд╕рд╛рдареА рднрд░рдкреВрд░ traffic рд▓рд╛рдЧрддреЗ. Canary C рджреБрд╕рд░рд╛ рдзрдбрд╛ рджрд╛рдЦрд╡рддреЗ тАФ error rate рдареАрдХ рд╣реЛрддрд╛ рдкрдг latency 44% рд╡рд╛рдИрдЯ рд╣реЛрддреА, рдореНрд╣рдгреВрди рдПрдХ metric рдкреБрд░реЗрд╕рд╛ рдирд╛рд╣реА.

тЪая╕П рдиреЗрд╣рдореАрдЪреНрдпрд╛ рдЪреБрдХрд╛

ЁЯПн рдкреНрд░рддреНрдпрдХреНрд╖ рд╡рд╛рдкрд░рд╛рдд

Argo Rollouts рдПрдХрд╛ AnalysisTemplate рдордзреВрди analysis рдЪрд╛рд▓рд╡рддреЗ: рдПрдХ query, рддреА рдХрд┐рддреА рд╡реЗрд│рд╛ рдЪрд╛рд▓рд╡рд╛рдпрдЪреА рдЖрдгрд┐ рдпрд╢ рдХрд╢рд╛рд▓рд╛ рдореНрд╣рдгрд╛рдпрдЪреЗ. рдЦрд▒реНрдпрд╛ account рд╡рд░ тАФ Prometheus рд╡рд┐рд░реБрджреНрдз error-rate check:

apiVersion: argoproj.io/v1alpha1
kind: AnalysisTemplate
metadata: {name: error-rate}
spec:
  args: [{name: service-name}]
  metrics:
    - name: error-rate
      interval: 5m
      failureLimit: 1
      successCondition: result[0] < 0.005
      provider:
        prometheus:
          address: http://prometheus.monitoring:9090
          query: |
            sum(rate(http_requests_total{service="{{args.service-name}}",code=~"5.."}[5m]))
            / sum(rate(http_requests_total{service="{{args.service-name}}"}[5m]))

рд╣реЗ template рдард░рд╛рд╡рд┐рдХ threshold рддрдкрд╛рд╕рддреЗ, рддреБрд▓рдирд╛ рдирд╛рд╣реА. рдЦрд▒реНрдпрд╛ baseline-рд╡рд┐рд░реБрджреНрдз-canary рддреБрд▓рдиреЗрд╕рд╛рдареА Argo Rollouts baseline рдЖрдгрд┐ canary experiments рд╢реЗрдЬрд╛рд░реА рд╢реЗрдЬрд╛рд░реА рд╕реБрд░реВ рдХрд░реВ рд╢рдХрддреЗ, рдЖрдгрд┐ Kayenta рд╕рд╣ Spinnaker рддреБрдордЪреНрдпрд╛рд╕рд╛рдареА statistical рддреБрд▓рдирд╛ рдЖрдгрд┐ score рдХрд░рддреЗ. Flagger Istio, Linkerd, NGINX рдЖрдгрд┐ рдЗрддрд░ routers рд╕рд╛рдареА threshold-рдЖрдзрд╛рд░рд┐рдд analysis рдХрд░рддреЗ.

ЁЯПн рдкреНрд░рддреНрдпрдХреНрд╖ рд╡рд╛рдкрд░рд╛рдд рд╣реЗ рдХрд╛ рдорд╣рддреНрддреНрд╡рд╛рдЪреЗ рдЖрд╣реЗ: рддреБрдордЪреНрдпрд╛ рдкреБрдврдЪреНрдпрд╛ release рд╕рд╛рдареА рдЖрдзреАрдЪ рд▓рд┐рд╣реВрди рдареЗрд╡рд╛: рдХреЛрдгрддреЗ metrics, canary рдХрд┐рддреА рдореЛрдареА, рдХрд┐рддреА рдХрд╛рд│, рдЖрдгрд┐ рдХреЛрдгрддреЗ рдЖрдХрдбреЗ рдореНрд╣рдгрдЬреЗ rollback. рд╣реЗ рдХрд░рддрд╛ рдЖрд▓реЗ рдирд╛рд╣реА, рддрд░ рддреБрдордЪреА canary рд╣реА test рдирд╕реВрди рдлрдХреНрдд рдПрдХ рдЖрд╢рд╛ рдЖрд╣реЗ.

тПня╕П рдкреБрдвреЗ

Judge рдореНрд╣рдгрддреЛ "rollback рдХрд░рд╛". рддреБрдореНрд╣реА рддреЗ рдХрд┐рддреА рд╡реЗрдЧрд╛рдиреЗ рдХрд░реВ рд╢рдХрддрд╛? рдЖрдгрд┐ рддреБрдореНрд╣реА рддреЗ рдХрд░рдгреНрдпрд╛рдЖрдзреА рдХрд┐рддреА users рдирд╛ рддреНрд░рд╛рд╕ рдЭрд╛рд▓рд╛? рдкреБрдврдЪрд╛ рдзрдбрд╛ рддреЗ рдореЛрдЬрддреЛ.

git checkout lesson-06-progressive-delivery

ЁЯРд Lesson 05 тАФ Release engineering: judge the canary with numbers

ЁЯУН You are here: Lesson 05 of 12 ┬╖ Previous: lesson-04-on-call ┬╖ Next: lesson-06-progressive-delivery


ЁЯУж What's in this branch

Part 2 begins: changing things safely. Most outages start with a change тАФ new code, new config, a new flag. This lesson sends a change to a small slice of traffic first (the canary) and compares it with a baseline, using numbers instead of eyeballing a dashboard: promote, roll back, or keep waiting. canary_judge in sre/crew.py and canary() in sre/demo.py.

ЁЯзТ Explain like I'm 5

The crew wants to put a new heating boiler in every classroom. ЁЯФе But what if the new boiler is bad?

So they put it in one classroom first. And тАФ this is the clever part тАФ they also put a fresh old boiler in the classroom next door, same size, same number of pupils. Now they can compare two classrooms that are the same in every way except the boiler.

After a day, Aishwarya counts the complaints. Old boiler: 11 complaints. New boiler: 42. That is not bad luck; that is a bad boiler. Take it out.

Another time: old 11, new 13. That small difference is normal day-to-day wobble. Put the new boiler everywhere.

And if only a few pupils used the room today? "Too early to tell. Wait longer."

ЁЯЧ║я╕П Diagram

flowchart LR
    t["ЁЯЪж traffic"] --> b["baseline<br/>old version, fresh copy<br/>11 errors / 10,000 ┬╖ p99 180 ms"]
    t --> c["canary<br/>new version, same size"]
    b & c --> j{"тЪЦя╕П judge<br/>z-test on errors<br/>p99 ratio"}
    j -->|"A: 13 errors ┬╖ z 0.41"| p["тЬЕ PROMOTE"]
    j -->|"B: 42 errors ┬╖ z 4.26"| r["тЖйя╕П ROLLBACK"]
    j -->|"C: p99 260 ms ┬╖ ├Ч1.44"| r
    j -->|"D: only 400 requests"| e["тП│ EXTEND"]

ЁЯЧ║я╕П Drawn version + a lab: https://school-edh.pages.dev/sre/lesson-diagrams.html#l05

тЭУ What

ЁЯдФ Why

Because change is the top cause of outages, and the canary limits how many users a bad change reaches (lesson 06 counts them). But a canary is only as good as its judge. Eyeballing two lines on a dashboard misses a 3├Ч error rate when both are tiny, and panics at normal noise. A written, automatic rule decides the same way every time, and it can act in minutes.

ЁЯФз How (in this repo)

canary_judge(base, canary, min_requests=1000, z_limit=3.0, p99_ratio=1.2) in sre/crew.py takes (errors, requests, p99_ms) for each side and returns (verdict, z, p99 ratio). It checks the sample size first, then the z-test on the error rate, then the p99 ratio. canary() in sre/demo.py judges four canaries against one baseline.

ЁЯзк Try it

python3 sre/demo.py canary
python3 - <<'EOF'
import sys; sys.path.insert(0, "sre"); from crew import canary_judge
base = (11, 10000, 180)
for errors in (11, 20, 25, 30, 42):
    v, z, r = canary_judge(base, (errors, 10000, 180))
    print(f"canary {errors:>2} errors / 10,000 тЖТ z {z:5.2f} тЖТ {v}")
for n in (500, 5000, 50000):
    v, z, r = canary_judge((n * 11 // 10000, n, 180), (n * 22 // 10000, n, 180))
    print(f"double the error rate, {n:>6,} requests each тЖТ {v}" + ("" if z is None else f" (z {z:.2f})"))
EOF

тЬЕ Verify тАФ what you should see

canary prints:

   canary A: 13 errors / 10,000 ┬╖ p99 185 ms тЖТ z   0.41 ┬╖ p99 ├Ч1.03 тЖТ PROMOTE
   canary B: 42 errors / 10,000 ┬╖ p99 182 ms тЖТ z   4.26 ┬╖ p99 ├Ч1.01 тЖТ ROLLBACK
   canary C: 12 errors / 10,000 ┬╖ p99 260 ms тЖТ z   0.21 ┬╖ p99 ├Ч1.44 тЖТ ROLLBACK
   canary D:  3 errors /    400 ┬╖ p99 179 ms тЖТ z    n/a ┬╖ p99 ├Ч0.99 тЖТ EXTEND  (first minutes: baseline 1 / 400)

Your snippet prints:

canary 11 errors / 10,000 тЖТ z  0.00 тЖТ PROMOTE
canary 20 errors / 10,000 тЖТ z  1.62 тЖТ PROMOTE
canary 25 errors / 10,000 тЖТ z  2.34 тЖТ PROMOTE
canary 30 errors / 10,000 тЖТ z  2.97 тЖТ PROMOTE
canary 42 errors / 10,000 тЖТ z  4.26 тЖТ ROLLBACK
double the error rate,    500 requests each тЖТ EXTEND
double the error rate,  5,000 requests each тЖТ PROMOTE (z 1.50)
double the error rate, 50,000 requests each тЖТ ROLLBACK (z 4.29)

ЁЯПБ What you just proved

A canary with 30 errors instead of 11 тАФ almost 3├Ч worse тАФ was promoted, because 10,000 requests with error rates this small is not enough evidence at z = 3.0. The same doubled error rate passed with 5,000 requests and failed with 50,000. So the size of the canary and the length of the wait decide what the judge can see: small error rates need lots of traffic. Canary C shows the other lesson тАФ the error rate was fine but latency was 44% worse, so one metric is not enough.

тЪая╕П Common mistakes

ЁЯПн In production

Argo Rollouts runs analysis from an AnalysisTemplate: a query, how often to run it and what counts as success. On a real account тАФ an error-rate check against Prometheus:

apiVersion: argoproj.io/v1alpha1
kind: AnalysisTemplate
metadata: {name: error-rate}
spec:
  args: [{name: service-name}]
  metrics:
    - name: error-rate
      interval: 5m
      failureLimit: 1
      successCondition: result[0] < 0.005
      provider:
        prometheus:
          address: http://prometheus.monitoring:9090
          query: |
            sum(rate(http_requests_total{service="{{args.service-name}}",code=~"5.."}[5m]))
            / sum(rate(http_requests_total{service="{{args.service-name}}"}[5m]))

This template checks a fixed threshold, not a comparison. For a true baseline-vs-canary comparison, Argo Rollouts can start baseline and canary experiments side by side, and Spinnaker with Kayenta does the statistical comparison and scoring for you. Flagger does threshold-based analysis for Istio, Linkerd, NGINX and other routers.

ЁЯПн Why this matters in production: for your next release, write down in advance: which metrics, what size canary, how long, and what numbers mean rollback. If you cannot, your canary is a hope, not a test.

тПня╕П Next

The judge says "roll back". How fast can you? And how many users were hurt before you did? The next lesson counts them.

git checkout lesson-06-progressive-delivery
тЖР Previouson callNext тЖТprogressive delivery

This page is the lesson's README from the lesson-05-canary-analysis branch, shown here so the whole School stays on one site. Code files open on GitHub at the same branch.