ЁЯПл The SchoolтА║ЁЯЫая╕П SREтА║ЁЯОЪя╕П рдзрдбрд╛ 06 тАФ Progressive delivery рдЖрдгрд┐ feature flags: exposure рдЖрдгрд┐ rollback рдЪрд╛ рд╡реЗрдЧ
ЁЯЦ╝я╕П See the drawing + lab ЁЯПа Course home ЁЯМ┐ Branch on GitHub тЬПя╕П View source
ЁЯЦ╝я╕П рдЖрдХреГрддреА рдЖрдгрд┐ labThe drawing + lab рдкреВрд░реНрдг рдкрд╛рдирд╛рд╡рд░ рдЙрдШрдбрд╛ тЖЧOpen full page тЖЧ

ЁЯОЪя╕П рдзрдбрд╛ 06 тАФ Progressive delivery рдЖрдгрд┐ feature flags: exposure рдЖрдгрд┐ rollback рдЪрд╛ рд╡реЗрдЧ

ЁЯУН рддреБрдореНрд╣реА рдЗрдереЗ рдЖрд╣рд╛рдд: 12 рдкреИрдХреА рдзрдбрд╛ 06 ┬╖ рдорд╛рдЧреЗ: lesson-05-canary-analysis ┬╖ рдкреБрдвреЗ: lesson-07-capacity-planning


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

рдзрдбрд╛ 05, рдЕрдзрд┐рдХ рд╡рд╛рдИрдЯ рдмрджрд▓рд╛рдЪреЗ рдЧрдгрд┐рдд. рд╡рд╛рдИрдЯ рдмрджрд▓ рдХрд┐рддреА requests рдирд╛ рддреНрд░рд╛рд╕ рджреЗрддреЛ рд╣реЗ рддреАрди рдЧреЛрд╖реНрдЯреАрдВрд╡рд░ рдЕрд╡рд▓рдВрдмреВрди рдЕрд╕рддреЗ: рддреЛ рдХрд┐рддреА users рдирд╛ рджрд┐рд╕рддреЛ (exposure), рддреБрдореНрд╣рд╛рд▓рд╛ рддреЛ рдХрд┐рддреА рд▓рд╡рдХрд░ рд▓рдХреНрд╖рд╛рдд рдпреЗрддреЛ (detection) рдЖрдгрд┐ рддреБрдореНрд╣реА рддреЛ рдХрд┐рддреА рд╡реЗрдЧрд╛рдиреЗ рдЙрд▓рдЯрд╡рддрд╛ (rollback). Progressive rollouts рдкрд╣рд┐рд▓реА рдЧреЛрд╖реНрдЯ рд▓рд╣рд╛рди рдХрд░рддрд╛рдд; feature flags рд╢реЗрд╡рдЯрдЪреА. sre/crew.py рдордзреАрд▓ rollout_damage рдЖрдгрд┐ sre/demo.py рдордзреАрд▓ rollout().

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

рдкрдердХ рд╢рд╛рд│реЗрддрд▓реЗ рджрд┐рд╡реЗ рдмрджрд▓рддреЗ. ЁЯТб рдирд╡реНрдпрд╛ рджрд┐рд╡реНрдпрд╛рдВрдордзреНрдпреЗ рдПрдХ рджреЛрд╖ рдЖрд╣реЗ: 10 рдкреИрдХреА 3 рдмрд▓реНрдм рд╡рд┐рдЭрддрд╛рдд.

рдкрд╣рд┐рд▓рд╛ рдЖрд░рд╛рдЦрдбрд╛: рдкреНрд░рддреНрдпреЗрдХ рд╡рд░реНрдЧ рдПрдХрджрдо рдмрджрд▓рд╛. рдкреНрд░рддреНрдпреЗрдХ рдЦреЛрд▓реАрдд рд╡рд┐рдЭрд▓реЗрд▓реЗ рдмрд▓реНрдм. рд▓рдХреНрд╖рд╛рдд рдпрд╛рдпрд▓рд╛ 5 рдорд┐рдирд┐рдЯреЗ рд▓рд╛рдЧрддрд╛рдд, рдордЧ рдирд╡реЗ рджрд┐рд╡реЗ рдХрд╛рдвреВрди рдЬреБрдиреЗ рдкрд░рдд рд▓рд╛рд╡рд╛рдпрд▓рд╛ 15 рдорд┐рдирд┐рдЯреЗ.

рджреБрд╕рд░рд╛ рдЖрд░рд╛рдЦрдбрд╛: рддреЗрдЪ, рдкрдг рдкреНрд░рддреНрдпреЗрдХ рдирд╡реНрдпрд╛ рджрд┐рд╡реНрдпрд╛рд▓рд╛ рдПрдХ switch рдЖрд╣реЗ рдЬреЛ рдЬреБрдирд╛ рджрд┐рд╡рд╛ рдкрд░рдд рдЪрд╛рд▓реВ рдХрд░рддреЛ. 5 рдорд┐рдирд┐рдЯрд╛рдВрдд рд▓рдХреНрд╖рд╛рдд рдпреЗрддреЗ, 1 рдорд┐рдирд┐рдЯрд╛рдд switch рдлрд┐рд░рд╡рд▓рд╛ рдЬрд╛рддреЛ. рдЦреВрдкрдЪ рдЪрд╛рдВрдЧрд▓реЗ.

рддрд┐рд╕рд░рд╛ рдЖрд░рд╛рдЦрдбрд╛: рдЖрдзреА рдлрдХреНрдд рд╢рдВрднрд░рд╛рддрд▓рд╛ рдПрдХ рд╡рд░реНрдЧ рдмрджрд▓рд╛. рдлрдХреНрдд рддреНрдпрд╛рдЪ рдЦреЛрд▓реАрдд рд╡рд┐рдЭрд▓реЗрд▓реЗ рдмрд▓реНрдм. рд▓рдХреНрд╖рд╛рдд рдпрд╛рдпрд▓рд╛ рдереЛрдбрд╛ рдЬрд╛рд╕реНрдд рд╡реЗрд│ рд▓рд╛рдЧрддреЛ (рдлрдХреНрдд рдереЛрдбреНрдпрд╛рдЪ рддрдХреНрд░рд╛рд░реА), рдкрдг рдлрд╛рд░рдЪ рдХрдореА рд╡рд┐рджреНрдпрд╛рд░реНрдерд┐рдиреАрдВрдирд╛ рддреНрд░рд╛рд╕ рдЭрд╛рд▓рд╛.

рдЪреМрдерд╛ рдЖрд░рд╛рдЦрдбрд╛: рд╢рдВрднрд░рд╛рддрд▓реА рдПрдХ рдЦреЛрд▓реА, рдЖрдгрд┐ switch. рд╕рдЧрд│реНрдпрд╛рдВрдд рдХрдореА рд╡рд┐рджреНрдпрд╛рд░реНрдерд┐рдиреА рдЕрдВрдзрд╛рд░рд╛рдд.

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

flowchart LR
    d["ЁЯТе bad change<br/>fails 30% of its requests<br/>1,000 requests/min"] --> bb["big bang + redeploy<br/>300 bad/min ├Ч (5 + 15) min<br/>6,000 failed"]
    d --> bf["big bang + flag off<br/>300 ├Ч (5 + 1)<br/>1,800"]
    d --> cr["1% canary + redeploy<br/>3 ├Ч (10 + 15)<br/>75"]
    d --> cf["1% canary + flag off<br/>3 ├Ч (10 + 1)<br/>33"]

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

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

ЁЯдФ рдХрд╛

рдХрд╛рд░рдг detection рдХрдзреАрдЪ рддрд╛рддреНрдХрд╛рд│ рдирд╕рддреЗ рдЖрдгрд┐ рдХрд╛рд╣реА рд╡рд╛рдИрдЯ рдмрджрд▓ canary рдкрд╛рд░ рдХрд░рддрд╛рдд. рддреБрдордЪреНрдпрд╛ рд╣рд╛рддрд╛рдд рд╣реЗ рдЖрд╣реЗ рдХреА рддреБрдореНрд╣рд╛рд▓рд╛ рдХрд│реЗрдкрд░реНрдпрдВрдд рд╢рд╛рд│реЗрдЪреНрдпрд╛ рдХрд┐рддреА рднрд╛рдЧрд╛рд▓рд╛ рдмрджрд▓ рджрд┐рд╕рддреЛ, рдЖрдгрд┐ рддреБрдореНрд╣реА рддреЛ рдХрд┐рддреА рд╡реЗрдЧрд╛рдиреЗ рдЙрд▓рдЯрд╡реВ рд╢рдХрддрд╛. Reliability рдЪреБрдХрд╛ рдХрдзреАрдЪ рди рдХрд░рдгреНрдпрд╛рддреВрди рдирд╡реНрд╣реЗ, рддрд░ рдЪреБрдХрд╛ рд▓рд╣рд╛рди рдЖрдгрд┐ рд╕реНрд╡рд╕реНрдд рдареЗрд╡рдгреНрдпрд╛рддреВрди рдпреЗрддреЗ.

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

sre/crew.py рдордзреАрд▓ rollout_damage(rpm, exposure, fail_rate, rollback_min, detect_errors=30, min_detect_min=5) (bad per minute, minutes to detect, total bad requests) рдкрд░рдд рджреЗрддреЗ: detection = 5 рдорд┐рдирд┐рдЯреЗ рдЖрдгрд┐ 30 рдЕрдкрдпрд╢реЗ рджрд┐рд╕рд╛рдпрд▓рд╛ рд▓рд╛рдЧрдгрд╛рд░реА рдорд┐рдирд┐рдЯреЗ рдпрд╛рдВрдкреИрдХреА рдЬреЗ рдореЛрдареЗ рддреЗ; total = рдорд┐рдирд┐рдЯрд╛рдорд╛рдЧрдЪреНрдпрд╛ bad ├Ч (detection + rollback). rollout() рдорд┐рдирд┐рдЯрд╛рд▓рд╛ 1,000 requests рдЖрдгрд┐ 30% failure rate рд╡рд░ рдЪрд╛рд░ рдЖрд░рд╛рдЦрдбреНрдпрд╛рдВрдЪреА рддреБрд▓рдирд╛ рдХрд░рддреЗ.

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

python3 sre/demo.py rollout
python3 - <<'EOF'
import sys; sys.path.insert(0, "sre"); from crew import rollout_damage
for exposure in (1.0, 0.25, 0.05, 0.01):
    row = []
    for rb in (15, 5, 1):
        bad, det, total = rollout_damage(1000, exposure, 0.30, rb)
        row.append(f"rollback {rb:>2} min тЖТ {total:>5,}")
    print(f"exposure {exposure:>4.0%} ┬╖ noticed after {det:>2} min ┬╖ " + " ┬╖ ".join(row))
EOF

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

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

   big bang, rollback = redeploy (15 min)     300 bad/min ┬╖ noticed after  5 min тЖТ 6,000 failed requests
   big bang, flag off (1 min)                 300 bad/min ┬╖ noticed after  5 min тЖТ 1,800 failed requests
   canary at 1%, rollback = redeploy            3 bad/min ┬╖ noticed after 10 min тЖТ    75 failed requests
   canary at 1%, flag off                       3 bad/min ┬╖ noticed after 10 min тЖТ    33 failed requests
   stages 1% тЖТ 5% тЖТ 25% тЖТ 100%, 30 min each: a full rollout takes 2 hours instead of 1 minute тАФ that is the price

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

exposure 100% ┬╖ noticed after  5 min ┬╖ rollback 15 min тЖТ 6,000 ┬╖ rollback  5 min тЖТ 3,000 ┬╖ rollback  1 min тЖТ 1,800
exposure  25% ┬╖ noticed after  5 min ┬╖ rollback 15 min тЖТ 1,500 ┬╖ rollback  5 min тЖТ   750 ┬╖ rollback  1 min тЖТ   450
exposure   5% ┬╖ noticed after  5 min ┬╖ rollback 15 min тЖТ   300 ┬╖ rollback  5 min тЖТ   150 ┬╖ rollback  1 min тЖТ    90
exposure   1% ┬╖ noticed after 10 min ┬╖ rollback 15 min тЖТ    75 ┬╖ rollback  5 min тЖТ    45 ┬╖ rollback  1 min тЖТ    33

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

100% рд╡рд░реВрди 1% exposure рд╡рд░ рдЧреЗрд▓реНрдпрд╛рдиреЗ рдиреБрдХрд╕рд╛рди 80 ├Ч рдХрдореА рдЭрд╛рд▓реЗ (6,000 тЖТ 75), detection рд▓рд╛ рджреБрдкреНрдкрдЯ рд╡реЗрд│ рд▓рд╛рдЧрд▓рд╛ рддрд░реАрд╣реА. рд╡реЗрдЧрд╡рд╛рди rollback рдиреЗ рддреЗ 3.3 ├Ч рдкрд░реНрдпрдВрдд рдХрдореА рдХреЗрд▓реЗ (6,000 тЖТ 1,800). рджреЛрдиреНрд╣реА рдорд┐рд│реВрди: 6,000 тЖТ 33, рдореНрд╣рдгрдЬреЗ рд╕реБрдорд╛рд░реЗ 180 ├Ч рдХрдореА failed requests. Exposure рд╣рд╛ рд╕рд░реНрд╡рд╛рдд рдореЛрдард╛ lever рдЖрд╣реЗ; rollback рдЪрд╛ рд╡реЗрдЧ рддреНрдпрд╛рдирдВрддрд░рдЪрд╛. рдХрд┐рдВрдордд рдореНрд╣рдгрдЬреЗ рд╡реЗрд│: 30 рдорд┐рдирд┐рдЯрд╛рдВрдЪреЗ рдЪрд╛рд░ рдЯрдкреНрдкреЗ рдореНрд╣рдгрдЬреЗ рдкреВрд░реНрдг rollout рд▓рд╛ 2 рддрд╛рд╕ тАФ рдореНрд╣рдгреВрдирдЪ teams рд╣реЗ рдЯрдкреНрдкреЗ automate рдХрд░рддрд╛рдд, рдореНрд╣рдгрдЬреЗ рддреНрдпрд╛ рдерд╛рдВрдмрдгреНрдпрд╛рдд рдХреЛрдгрд╛рдЪреЗрд╣реА рд▓рдХреНрд╖ рдЦрд░реНрдЪ рд╣реЛрдд рдирд╛рд╣реА.

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

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

Argo Rollouts тАФ canary steps рдЕрд╕рд▓реЗрд▓рд╛ Rollout, рдЖрдгрд┐ рдкрд╛рд░реНрд╢реНрд╡рднреВрдореАрдд рдзрдбрд╛ 05 рдордзреАрд▓ analysis рдЪрд╛рд▓реВ. рдЦрд▒реНрдпрд╛ account рд╡рд░:

apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata: {name: timetable}
spec:
  strategy:
    canary:
      analysis:
        templates: [{templateName: error-rate}]
        startingStep: 1
        args: [{name: service-name, value: timetable}]
      steps:
        - setWeight: 1
        - pause: {duration: 30m}
        - setWeight: 5
        - pause: {duration: 30m}
        - setWeight: 25
        - pause: {duration: 30m}

Traffic router рдирд╕реЗрд▓ рддрд░ Argo Rollouts pods рдЪреНрдпрд╛ рд╕рдВрдЦреНрдпреЗрдиреЗ weight рдЕрдВрджрд╛рдЬреЗ рд╕рд╛рдзрддреЗ; router рдЕрд╕реЗрд▓ (Istio, NGINX, AWS ALB рдЖрдгрд┐ рдЗрддрд░) рддрд░ рддреЗ рдиреЗрдордХреА рдЯрдХреНрдХреЗрд╡рд╛рд░реА рдард░рд╡реВ рд╢рдХрддреЗ. Rollout рдерд╛рдВрдмрд╡рд╛ рдЖрдгрд┐ рд▓рдЧреЗрдЪ рдкрд░рдд рдЬрд╛:

kubectl argo rollouts abort timetable        # traffic back to the stable version
kubectl rollout undo deployment/timetable    # plain Deployment: back to the previous ReplicaSet

OpenFeature Python SDK рд╡рд╛рдкрд░реВрди code рдордзреАрд▓ feature flag (provider тАФ LaunchDarkly, Unleash, flagd рдЖрдгрд┐ рдЗрддрд░ тАФ start-up рд▓рд╛ рдПрдХрджрд╛рдЪ configure рдХреЗрд▓рд╛ рдЬрд╛рддреЛ):

from openfeature import api
client = api.get_client()
if client.get_boolean_value("new-timetable-engine", False):   # default False if the flag service is down
    plan = new_engine(week)
else:
    plan = old_engine(week)

ЁЯПн рдкреНрд░рддреНрдпрдХреНрд╖ рд╡рд╛рдкрд░рд╛рдд рд╣реЗ рдХрд╛ рдорд╣рддреНрддреНрд╡рд╛рдЪреЗ рдЖрд╣реЗ: рддреБрдордЪреНрдпрд╛ рдЦрд▒реНрдпрд╛ rollback рдЪреА рд╡реЗрд│ рдореЛрдЬрд╛. рдЖрд░рд╛рдЦрдбрд╛ рдирд╡реНрд╣реЗ тАФ рдШрдбреНрдпрд╛рд│, рдПрдХрд╛ test рдордзреНрдпреЗ, "рдирд┐рд░реНрдгрдп" рдкрд╛рд╕реВрди "users рдЬреБрдиреНрдпрд╛ version рд╡рд░ рдкрд░рдд рдЖрд▓реЗ" рдкрд░реНрдпрдВрдд. рд╣рд╛ рдЖрдХрдбрд╛ 5 рдорд┐рдирд┐рдЯрд╛рдВрдкреЗрдХреНрд╖рд╛ рдЬрд╛рд╕реНрдд рдЕрд╕реЗрд▓, рддрд░ рдкреБрдвреЗ рджреБрд░реБрд╕реНрдд рдХрд░рд╛рдпрдЪреА рдЧреЛрд╖реНрдЯ рддреАрдЪ рдЖрд╣реЗ.

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

рдЖрддрд╛ рдмрджрд▓ рд╕реБрд░рдХреНрд╖рд┐рдд рдЖрд╣реЗрдд. рдкрдг рдкреБрдврдЪреНрдпрд╛ рд╕рддреНрд░рд╛рдд рдЗрдорд╛рд░рддреА рдкреБрд░реЗрд╢рд╛ рдореЛрдареНрдпрд╛ рдЕрд╕рддреАрд▓ рдХрд╛? рдкреБрдврдЪрд╛ рдзрдбрд╛ capacity рд╕рдВрдкрдгреНрдпрд╛рдЖрдзреАрдЪ рддрд┐рдЪрд╛ рдЖрд░рд╛рдЦрдбрд╛ рдХрд░рддреЛ.

git checkout lesson-07-capacity-planning

ЁЯОЪя╕П Lesson 06 тАФ Progressive delivery and feature flags: exposure and rollback speed

ЁЯУН You are here: Lesson 06 of 12 ┬╖ Previous: lesson-05-canary-analysis ┬╖ Next: lesson-07-capacity-planning


ЁЯУж What's in this branch

Lesson 05, plus the arithmetic of a bad change. How many requests a bad change hurts depends on three things: how many users see it (exposure), how fast you notice (detection) and how fast you undo it (rollback). Progressive rollouts shrink the first; feature flags shrink the last. rollout_damage in sre/crew.py and rollout() in sre/demo.py.

ЁЯзТ Explain like I'm 5

The crew changes the lights in the school. ЁЯТб The new lights have a fault: 3 bulbs in 10 go dark.

Plan one: change every classroom at once. Every room has dark bulbs. It takes 5 minutes to notice, then 15 minutes to take the new lights out and put the old ones back.

Plan two: same, but every new light has a switch that turns the old light back on. Notice in 5 minutes, flip the switch in 1 minute. Much better.

Plan three: change just one classroom in a hundred first. Only that room has dark bulbs. It takes a bit longer to notice (only a few complaints), but very few pupils were bothered.

Plan four: one room in a hundred, and a switch. The fewest pupils in the dark of all.

ЁЯЧ║я╕П Diagram

flowchart LR
    d["ЁЯТе bad change<br/>fails 30% of its requests<br/>1,000 requests/min"] --> bb["big bang + redeploy<br/>300 bad/min ├Ч (5 + 15) min<br/>6,000 failed"]
    d --> bf["big bang + flag off<br/>300 ├Ч (5 + 1)<br/>1,800"]
    d --> cr["1% canary + redeploy<br/>3 ├Ч (10 + 15)<br/>75"]
    d --> cf["1% canary + flag off<br/>3 ├Ч (10 + 1)<br/>33"]

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

тЭУ What

ЁЯдФ Why

Because detection is never instant and some bad changes pass the canary. What you control is how much of the school sees the change while you find out, and how fast you can undo it. Reliability comes from making mistakes small and cheap, not from never making them.

ЁЯФз How (in this repo)

rollout_damage(rpm, exposure, fail_rate, rollback_min, detect_errors=30, min_detect_min=5) in sre/crew.py returns (bad per minute, minutes to detect, total bad requests): detection = the larger of 5 minutes and the minutes needed to see 30 failures; total = bad per minute ├Ч (detection + rollback). rollout() compares four plans at 1,000 requests a minute and a 30% failure rate.

ЁЯзк Try it

python3 sre/demo.py rollout
python3 - <<'EOF'
import sys; sys.path.insert(0, "sre"); from crew import rollout_damage
for exposure in (1.0, 0.25, 0.05, 0.01):
    row = []
    for rb in (15, 5, 1):
        bad, det, total = rollout_damage(1000, exposure, 0.30, rb)
        row.append(f"rollback {rb:>2} min тЖТ {total:>5,}")
    print(f"exposure {exposure:>4.0%} ┬╖ noticed after {det:>2} min ┬╖ " + " ┬╖ ".join(row))
EOF

тЬЕ Verify тАФ what you should see

rollout prints:

   big bang, rollback = redeploy (15 min)     300 bad/min ┬╖ noticed after  5 min тЖТ 6,000 failed requests
   big bang, flag off (1 min)                 300 bad/min ┬╖ noticed after  5 min тЖТ 1,800 failed requests
   canary at 1%, rollback = redeploy            3 bad/min ┬╖ noticed after 10 min тЖТ    75 failed requests
   canary at 1%, flag off                       3 bad/min ┬╖ noticed after 10 min тЖТ    33 failed requests
   stages 1% тЖТ 5% тЖТ 25% тЖТ 100%, 30 min each: a full rollout takes 2 hours instead of 1 minute тАФ that is the price

Your snippet prints:

exposure 100% ┬╖ noticed after  5 min ┬╖ rollback 15 min тЖТ 6,000 ┬╖ rollback  5 min тЖТ 3,000 ┬╖ rollback  1 min тЖТ 1,800
exposure  25% ┬╖ noticed after  5 min ┬╖ rollback 15 min тЖТ 1,500 ┬╖ rollback  5 min тЖТ   750 ┬╖ rollback  1 min тЖТ   450
exposure   5% ┬╖ noticed after  5 min ┬╖ rollback 15 min тЖТ   300 ┬╖ rollback  5 min тЖТ   150 ┬╖ rollback  1 min тЖТ    90
exposure   1% ┬╖ noticed after 10 min ┬╖ rollback 15 min тЖТ    75 ┬╖ rollback  5 min тЖТ    45 ┬╖ rollback  1 min тЖТ    33

ЁЯПБ What you just proved

Going from 100% to 1% exposure cut the damage 80 ├Ч (6,000 тЖТ 75), even though detection took twice as long. A fast rollback cut it by up to 3.3 ├Ч (6,000 тЖТ 1,800). Together: 6,000 тЖТ 33, about 180 ├Ч fewer failed requests. Exposure is the biggest lever; rollback speed is the next. The price is time: four 30-minute stages mean 2 hours to full rollout тАФ which is why teams automate the steps, so the wait costs nobody's attention.

тЪая╕П Common mistakes

ЁЯПн In production

Argo Rollouts тАФ a Rollout with canary steps and the analysis from lesson 05 running in the background. On a real account:

apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata: {name: timetable}
spec:
  strategy:
    canary:
      analysis:
        templates: [{templateName: error-rate}]
        startingStep: 1
        args: [{name: service-name, value: timetable}]
      steps:
        - setWeight: 1
        - pause: {duration: 30m}
        - setWeight: 5
        - pause: {duration: 30m}
        - setWeight: 25
        - pause: {duration: 30m}

Without a traffic router, Argo Rollouts approximates the weight with the number of pods; with one (Istio, NGINX, AWS ALB and others) it can set the exact percentage. Stop a rollout and go back at once:

kubectl argo rollouts abort timetable        # traffic back to the stable version
kubectl rollout undo deployment/timetable    # plain Deployment: back to the previous ReplicaSet

A feature flag in code, with the OpenFeature Python SDK (the provider тАФ LaunchDarkly, Unleash, flagd and others тАФ is configured once at start-up):

from openfeature import api
client = api.get_client()
if client.get_boolean_value("new-timetable-engine", False):   # default False if the flag service is down
    plan = new_engine(week)
else:
    plan = old_engine(week)

ЁЯПн Why this matters in production: time your real rollback. Not the plan тАФ the clock, in a test, from "decide" to "users are back on the old version". If the number is over 5 minutes, that is the next thing to fix.

тПня╕П Next

Changes are safe now. But will the buildings be big enough next term? The next lesson plans capacity before it runs out.

git checkout lesson-07-capacity-planning
тЖР Previouscanary analysisNext тЖТcapacity planning

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