Skip to content

บทที่ 18 — Deployment (Canary + Blue-Green + Feature Flag + GitOps)

← บทที่ 17: Service Mesh | สารบัญ | บทที่ 19: Polyglot Example →

📖 ศัพท์บทนี้ (อ่านก่อนเริ่ม):

  • canary (คะ-แน-รี่ = นกขมิ้น) — มาจาก idiom "canary in the coal mine" (เหมืองถ่านหิน — คนงานเหมืองพานกขมิ้นลงไปเช็คก๊าซพิษก่อนคนเข้า) — ในที่นี้ = "ทดสอบกับ user ส่วนน้อยก่อน"
  • progressive delivery — แนวคิดส่ง release แบบค่อยเป็นค่อยไป (ปล่อยให้ user ทีละกลุ่ม + watch metric)
  • progressive rollout — action จริงของการปล่อยเป็นขั้น รวมทุก strategy ที่ค่อยเป็นค่อยไป (canary + feature flag + blue-green)
  • Feature Flag (ธงเปิด-ปิด feature) — config ที่เปิด/ปิด feature ได้โดยไม่ต้อง deploy ใหม่
  • kill switch (สวิตช์ดับฉุกเฉิน) — feature flag พิเศษที่ปิด feature ทันทีเมื่อเกิดปัญหา
  • GitOps (กิต-อ๊อปส์) — pattern ที่ใช้ Git เป็น "แหล่งความจริงเดียว" ของ infrastructure: เขียน YAML ใน Git → tool sync เข้า cluster อัตโนมัติ
  • Argo CD (อา-โก ซี-ดี) — GitOps controller ของ Intuit (ตอนนี้ CNCF graduated)
  • Flux (ฟลักซ์) — GitOps controller ของ Weaveworks (CNCF graduated)
  • Kargo (คาร์-โก) — tool promote artifact ข้าม stage (dev → staging → prod)
  • immutable infrastructure (infra ที่เปลี่ยนไม่ได้) — ห้าม SSH ไปแก้ pod ที่รันอยู่; ถ้าต้องแก้ ให้ build image ใหม่แล้ว deploy ใหม่
  • CNCF (ซี-เอ็น-ซี-เอฟ = Cloud Native Computing Foundation) — องค์กรที่ดูแล open-source project สำคัญ เช่น Kubernetes, Prometheus, Jaeger, Argo CD, Flux — project ที่ "CNCF graduated" หมายถึงผ่านการ review ว่า production-ready และ community แข็งแกร่ง
  • shift left (เลื่อนซ้าย) — idiom DevOps: เลื่อน activity ตรวจสอบ ทดสอบ security ให้เกิดเร็วขึ้นใน pipeline (ซ้ายของ pipeline = ใกล้ dev มากขึ้น) — เช่น แทนที่จะ scan security ตอน deploy (ปลาย pipeline) ให้ scan code ตอน dev commit (ต้น pipeline) = shift left

TL;DR: บทนี้ตอบ "deploy หลาย service ต่อวันแบบไม่ทำ user เห็น downtime" — Rolling Update / Blue-Green / Canary (Argo Rollouts + analysis), Feature Flag (OpenFeature CNCF), GitOps (Argo CD + Flux), Kargo multi-stage promotion. ข้ามได้ถ้า: ตั้ง progressive delivery (= ส่ง release แบบค่อยเป็นค่อยไป) + feature flag + GitOps เป็นนิสัย

เมื่ออัพเดต app — ถ้า restart พร้อมกันทุก instance จะเกิด downtime; deployment strategy คือวิธีเปลี่ยนเวอร์ชันโดยไม่หยุดให้ user เห็น แต่ละแบบมี trade-off ระหว่าง ความเร็ว / ความเสี่ยง / resource ที่ใช้ บทนี้สอน 4 strategy หลักและ tooling รอบ ๆ

ใน monolith — deploy 1 ครั้งทั้งแอป ใน microservices — deploy หลาย service / วัน → ต้อง safe + fast + observable

อัพเดต 2026: K8s 1.33 เป็น baseline (Helm chart pin ทุกตัว), Argo CD / Flux = GitOps controller มาตรฐาน, Argo Rollouts + Flagger เป็นมาตรฐาน progressive delivery, OpenFeature (CNCF) กลายเป็น vendor-neutral standard ของ feature flag, Kargo สำหรับ multi-stage promotion, CloudNativePG สำหรับ Postgres on K8s, Strimzi operator สำหรับ Kafka on K8s, RabbitMQ Cluster Operator สำหรับ RabbitMQ

ใช้เวลา 3-4 ชั่วโมง


Part 1: Deployment Strategies

1.1 Rolling Update (default K8s)

text
v1: [pod1] [pod2] [pod3] [pod4]

Step 1: kill pod1 (kill = สั่ง pod หยุดทำงาน), start v2 pod1'
v1: [    ] [pod2] [pod3] [pod4]
v2: [pod1']

Step 2: kill pod2 ...
...

Done:
v2: [pod1'] [pod2'] [pod3'] [pod4']

✅ ง่าย, K8s default ❌ ทุก traffic ผสมระหว่าง v1+v2 ระหว่าง rollout — ถ้า v2 buggy ก็เปื้อน

1.2 Blue-Green

Blue-Green รัน 2 เวอร์ชันคู่กัน — Blue (เก่า) รับ traffic จริง 100% ส่วน Green (ใหม่) deploy ไว้ทดสอบเงียบ ๆ พอมั่นใจก็สลับ traffic ไป Green แบบทันที (atomic) ข้อดีคือ rollback เร็วมาก (สลับกลับ) แต่ต้องใช้ resource สองเท่า:

📝 Convention: Blue = production ปัจจุบัน, Green = version ใหม่ที่กำลังทดสอบ · ชื่อสีไม่มีความหมายตรง ๆ — แค่จำว่า "Blue คือเก่า/รัน, Green คือใหม่/รอ" และหลังสลับแล้วบทบาทก็สลับด้วย (Green กลายเป็น production, Blue กลายเป็น standby)

text
v1 (Blue): running, receiving 100% traffic
v2 (Green): deployed, no traffic, test internally

Switch traffic Blue → Green (atomic):
v2 (Green): 100% traffic
v1 (Blue): standby for rollback

✅ Instant rollback (switch กลับ) ✅ Test v2 fully ก่อน switch ❌ ต้องใช้ resource × 2

1.3 Canary

Canary ปล่อยเวอร์ชันใหม่ให้ traffic ส่วนน้อยก่อน (เช่น 10%) แล้วเฝ้าดู metric (error rate, latency) ถ้าดีก็ค่อย ๆ เพิ่มสัดส่วนจนครบ — จำกัด "blast radius" ถ้า v2 มีบั๊กก็กระทบแค่ส่วนน้อย แลกกับ rollout ที่ช้ากว่า:

text
v1: 90% traffic
v2: 10% traffic ← canary
   ↓ monitor metrics (error rate, latency)
   ↓ ถ้า OK → ค่อย ๆ เพิ่ม %
v2: 25%, 50%, 75%, 100%

✅ Limited blast radius ✅ Catch bug ก่อน 100% ❌ Slower rollout (1-24 hr)

1.4 Feature Flag

Feature flag แยก "deploy" ออกจาก "release" — deploy โค้ดใหม่ขึ้นทุก pod แต่ปิด feature ไว้ แล้วค่อยเปิดให้ user ทีละกลุ่มผ่าน config (ไม่ต้อง deploy ใหม่) ข้อดีคือมี kill switch ปิดทันทีได้ และทุก stage ใช้ binary เดียวกัน:

text
v2 deployed ทุก pod แต่ feature ปิด
Toggle feature on 1% user
Monitor
Toggle on 100%

✅ Deploy + release แยก (deploy ไม่ release) ✅ Kill switch ทันที ✅ Same binary across stage


Part 2: Implementation

2.1 Rolling Update K8s

วิธี deploy default ของ K8s คือ rolling update — ค่อย ๆ แทน pod เก่าด้วยใหม่ทีละตัว โดยรอ readiness probe ผ่านก่อนค่อย kill ตัวเก่า (maxUnavailable: 0 = ไม่ให้มี downtime) ได้ zero-downtime โดยไม่ต้องใช้ resource สองเท่า:

yaml
apiVersion: apps/v1
kind: Deployment
metadata: { name: orders }
spec:
  replicas: 4
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1            # extra pod during update
      maxUnavailable: 0      # downtime tolerance
  template:
    spec:
      containers:
        - name: app
          image: orders:v2
          readinessProbe:
            httpGet: { path: /actuator/health/readiness, port: 8080 }
            initialDelaySeconds: 5
            periodSeconds: 5

K8s wait readiness → ค่อย kill v1 pod

📎 readinessProbe คืออะไร — K8s ใช้เช็คว่า pod พร้อมรับ traffic หรือยัง · ดูรายละเอียดที่ discovery-and-config.md §4

⚠️ Trade-off maxSurge: 1 + maxUnavailable: 0 + replicas: 4: ระหว่าง update จะมี 5 pod ชั่วครู่ (4 + 1 extra) → ต้องการ resource headroom บน node · ถ้า node capacity ตึง อาจมี pod pending รอ schedule · ถ้า resource จำกัด ลด maxSurge เป็น 25% หรือยอม maxUnavailable: 1 แทน

2.2 Blue-Green ด้วย K8s Service

ทำ Blue-Green บน K8s ได้ง่าย ๆ ด้วย label + Service selector — deploy 2 ชุด (blue/green) แล้วให้ Service ชี้ไป color เดียว สลับเวอร์ชันแค่ patch selector ของ Service (atomic) traffic จะย้ายไป green ทันที rollback ก็ patch กลับ:

yaml
# Both deployments
apiVersion: apps/v1
kind: Deployment
metadata: { name: orders-blue }
spec:
  selector: { matchLabels: { app: orders, color: blue } }
  template: { metadata: { labels: { app: orders, color: blue } } ... }

---
apiVersion: apps/v1
kind: Deployment
metadata: { name: orders-green }
spec:
  selector: { matchLabels: { app: orders, color: green } }
  template: { metadata: { labels: { app: orders, color: green } } ... }

---
# Service selects blue
apiVersion: v1
kind: Service
metadata: { name: orders }
spec:
  selector: { app: orders, color: blue }
  ports: [{ port: 8080 }]

Switch:

bash
kubectl patch service orders -p '{"spec":{"selector":{"color":"green"}}}'

atomic switch

2.3 Canary ด้วย Argo Rollouts

💡 Argo Rollouts คืออะไร — เครื่องมือ install แยก (ไม่ได้มากับ K8s) ที่เพิ่มความสามารถ progressive delivery ให้ K8s โดยใช้ CRD (Custom Resource Definition = ประเภท object ใหม่ที่เราสร้างเพิ่มใน K8s) แทน Deployment ปกติ · K8s Deployment ปกติทำ canary แบบ fine-grained (กำหนด % traffic ตาม step) ไม่ได้ Argo Rollouts จึงเข้ามาเติมส่วนนี้

K8s Deployment ปกติทำ canary ไม่ได้ดี — Argo Rollouts เป็น CRD ที่เพิ่มความสามารถนี้: กำหนด step การเพิ่ม % traffic, หยุดรอ (pause) ให้คนดู metric หรือ auto-analyze แล้วเดินหน้า/rollback อัตโนมัติ (ถ้ายังไม่คุ้น K8s Deployment resource พื้นฐาน ดู kubernetes/02-deployments.md ก่อน):

yaml
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata: { name: orders }
spec:
  replicas: 10
  strategy:
    canary:
      canaryService: orders-canary
      stableService: orders-stable
      trafficRouting:
        istio:
          virtualServices:
            - name: orders
              routes: [primary]
      steps:
        - setWeight: 10
        - pause: { duration: 5m }
        - analysis:
            templates:
              - templateName: error-rate-check
        - setWeight: 25
        - pause: { duration: 10m }
        - analysis: ...
        - setWeight: 50
        - pause: { duration: 10m }
        - setWeight: 100

Analysis template:

yaml
apiVersion: argoproj.io/v1alpha1
kind: AnalysisTemplate
metadata: { name: error-rate-check }
spec:
  metrics:
    - name: error-rate
      provider:
        prometheus:
          address: http://prometheus:9090
          # ⚠️ ชื่อ metric ขึ้นกับ stack:
          #   - Spring Boot / Micrometer: http_server_requests_seconds_count (status label: "status")
          #   - Generic Prometheus client: http_requests_total
          # ⚠️ "service" label = common tag — ไม่ใช่ default Micrometer label
          #   ต้องเพิ่มเองใน application.yml:
          #     management.metrics.tags.service=orders
          # ตัวอย่างนี้ใช้ Spring Boot (ตรงกับบท observability/02)
          query: |
            sum(rate(http_server_requests_seconds_count{service="orders-canary",status=~"5.."}[5m]))
            / sum(rate(http_server_requests_seconds_count{service="orders-canary"}[5m]) or vector(1))
      successCondition: result < 0.01

→ Auto rollback ถ้า error rate > 1%

⚠️ ระวัง traffic = 0 ระหว่าง analysis (canary เพิ่งเริ่ม หรืออยู่ระหว่าง pause step) — ถ้าไม่มี request เข้ามาเลยในช่วง 5 นาทีนั้น ตัวหารจะเป็น 0 → Prometheus คืนค่า no data/NaN แทนที่จะเป็นตัวเลข ซึ่ง successCondition เทียบกับ NaN ได้ผลไม่แน่นอน (Argo Rollouts อาจ error แทนที่จะ pass/fail ปกติ) — ตัวอย่างข้างบนใส่ or vector(1) กันหารด้วยศูนย์ไว้แล้ว แต่ควรพิจารณาเพิ่มเงื่อนไข minimum sample size (เช่น count() >= N) ก่อนเชื่อผลลัพธ์ด้วย

2.4 Canary ผ่าน Istio

ถ้ามี service mesh อยู่แล้วก็ทำ canary ได้โดยไม่ต้องมี Argo — กำหนด weight ของ traffic ระหว่าง subset v1/v2 ใน VirtualService แล้วค่อย ๆ ปรับสัดส่วนทีละ step ข้อดีคือควบคุม traffic ได้ละเอียดที่ระดับ network:

yaml
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata: { name: orders }
spec:
  hosts: [orders]
  http:
    - route:
        - destination: { host: orders, subset: v1 }
          weight: 90
        - destination: { host: orders, subset: v2 }
          weight: 10

ปรับ weight ทีละ step

2.5 Feature Flag — OpenFeature standard (CNCF, 2026)

ปัญหา: provider lock-in (LaunchDarkly, Unleash, Flagsmith — แต่ละเจ้า API ต่างกัน)

OpenFeature (CNCF Incubating, 2023) = spec กลาง + SDK ทุกภาษา → swap provider ไม่ต้องแก้ code

java
// pom.xml — เช็ค Maven Central ก่อนใช้ (เวอร์ชันด้านล่างคือ pinned baseline 2026)
// <dependency>dev.openfeature:sdk:1.13.0</dependency>
// <dependency>dev.openfeature.contrib.providers:flagd:0.8.9</dependency>

OpenFeatureAPI.getInstance().setProviderAndWait(new FlagdProvider());
Client client = OpenFeatureAPI.getInstance().getClient();

public void checkout(User user) {
    EvaluationContext ctx = new MutableContext(user.id())
        .add("country", user.country())
        .add("plan", user.plan());

    if (client.getBooleanValue("new-payment-flow", false, ctx)) {
        newCheckout();
    } else {
        oldCheckout();
    }
}

ตัว provider เปลี่ยนได้: flagd (open source), LaunchDarkly, Unleash, ConfigCat, Flagsmith

ตัวอย่าง flagd config (GitOps-friendly, store ใน K8s ConfigMap)

json
{
  "flags": {
    "new-payment-flow": {
      "state": "ENABLED",
      "variants": { "on": true, "off": false },
      "defaultVariant": "off",
      "targeting": {
        "if": [
          { "in": [{ "var": "country" }, ["TH", "VN"]] },
          "on",
          { "fractional": [
              { "var": "userId" },
              [["on", 10], ["off", 90]]
            ]
          }
        ]
      }
    }
  }
}

→ Targeting rule + percentage rollout, อ่านได้ทุก SDK ภาษา


Part 3: Database Migration

3.1 ปัญหา

ระหว่าง rolling update — v1 + v2 รันพร้อมกัน ถ้า v2 ใช้ schema ใหม่ที่ v1 ไม่รู้ → v1 พัง

3.2 Expand-Contract Pattern

เคล็ดลับเปลี่ยน schema โดยไม่ทำ app พังคือทำเป็นขั้น (expand-contract) — ขั้น expand เพิ่มของใหม่แบบเข้ากับของเก่าได้ (add column), ให้ v2 เขียนทั้งเก่า+ใหม่, backfill, แล้วค่อย contract (ลบของเก่า) ทุกขั้น v1 และ v2 ต้องรันร่วมกันได้:

3.3 Tools

มีเครื่องมือช่วยจัดการ migration — Flyway/Liquibase ดูแล versioning ของ schema (รัน migration ตามลำดับ, track ว่ารันถึงไหน) ส่วน gh-ost/pg-online-schema-change ช่วย ALTER ตารางใหญ่โดยไม่ lock:

  • Flyway / Liquibase — migration
  • gh-ost / pt-online-schema-change — MySQL online schema
  • pg-online-schema-change — Postgres

Part 4: GitOps

4.1 หลักการ

GitOps ใช้ Git เป็น "แหล่งความจริงเดียว" ของทั้ง config และ deployment — คนแก้สถานะระบบผ่านการ commit เข้า git แล้วมี controller (Argo CD/Flux) คอยซิงค์ให้ cluster ตรงกับ git เสมอ ได้ audit trail, rollback, drift detection ฟรีจาก git:

"Git = single source of truth ของ infrastructure + deployment"

4.2 Argo CD

Argo CD เป็น GitOps controller ยอดนิยม — นิยาม Application ที่ชี้ไป git repo + path แล้วเปิด automated sync (prune + selfHeal) จากนั้นทุกการเปลี่ยน manifest ใน git จะถูก apply เข้า cluster อัตโนมัติ และ revert การแก้มือที่หลุดจาก git:

yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata: { name: orders }
spec:
  source:
    repoURL: https://github.com/acme/k8s-config
    path: orders
    targetRevision: HEAD
  destination:
    server: https://kubernetes.default.svc
    namespace: production
  syncPolicy:
    automated:
      prune: true
      selfHeal: true

→ ทุก change ใน k8s-config/orders/*.yaml = auto apply

4.3 Benefits

ประโยชน์ของ GitOps ส่วนใหญ่ได้มาจากการที่ git เป็นแหล่งความจริง — audit trail คือ git history, rollback คือ git revert, multi-cluster คือ folder ต่อ env, และ controller คอย detect/heal drift ให้ ทำให้สถานะระบบโปร่งใสและย้อนกลับได้เสมอ:

  • Audit trail = git history
  • Rollback = git revert
  • Multi-cluster = same git, different env folder
  • Drift detection = controller compares
  • Self-healing = revert manual change

4.4 Repo Structure

โครงสร้าง repo GitOps ที่นิยมคือแยก base (นิยามร่วม) ออกจาก environments (ค่าเฉพาะ dev/staging/prod) แล้วใช้ Kustomize/Helm overlay ค่าต่าง — แก้ base ทีเดียวมีผลทุก env ส่วนค่าเฉพาะ env แยกชัด ลดการ copy-paste:

text
acme/k8s-config/
├── base/                       (shared definitions)
│   ├── orders/
│   │   ├── deployment.yaml
│   │   └── service.yaml
│   └── ...
├── environments/
│   ├── dev/
│   │   └── orders/kustomization.yaml
│   ├── staging/
│   └── production/
└── apps/
    └── orders-app.yaml         (Argo CD Application)

Use Kustomize หรือ Helm เพื่อ template


Part 5: Progressive Delivery

5.1 Tools เทียบกัน

Argo RolloutsFlaggerSpinnaker
EcosystemArgo (CNCF graduated)Flux (CNCF graduated)Netflix OSS
Traffic routingIstio, NGINX, ALB, SMI, Gateway APIIstio, Linkerd, Contour, Gloo, NGINX, Gateway APIMany
StrategyCanary, BlueGreenCanary, A/B, BlueGreen, MirrorAll
AnalysisPrometheus, Datadog, NewRelic, CloudWatch, WebSame + Slack notificationsSame + many
K8s native✅ CRD✅ CRD❌ separate service
Setup ง่าย★★★★★★★★★★

คำแนะนำ 2026:

  • ใช้ Argo CD แล้ว → ใช้ Argo Rollouts
  • ใช้ Flux → ใช้ Flagger
  • Spinnaker = legacy, ไม่แนะนำเริ่มใหม่

5.2 Flagger ตัวอย่าง (Flux/Linkerd users)

Flagger ทำ progressive delivery สำหรับฝั่ง Flux/Linkerd — นิยาม Canary ที่ผูกกับ Deployment แล้วมันจะค่อย ๆ เพิ่ม weight ตาม step วิเคราะห์ metric (success rate, latency) ทุกช่วง ถ้าเกิน threshold ก็ rollback อัตโนมัติ ทำให้ deploy ปลอดภัยโดยไม่ต้องเฝ้าเอง:

yaml
apiVersion: flagger.app/v1beta1
kind: Canary
metadata: { name: orders }
spec:
  targetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: orders
  progressDeadlineSeconds: 600
  service:
    port: 8080
    targetPort: 8080
    gateways: [public-gateway]
  analysis:
    interval: 1m
    threshold: 5                  # max fail ก่อน rollback
    maxWeight: 50
    stepWeight: 10
    metrics:
      - name: request-success-rate
        thresholdRange: { min: 99 }
        interval: 1m
      - name: request-duration
        thresholdRange: { max: 500 }
        interval: 1m
    webhooks:
      - name: load-test
        url: http://loadtester/
        metadata: { cmd: "hey -z 1m -q 10 -c 2 http://orders-canary:8080/" }

→ ทุก deploy: weight 10% → analyze → +10% → ... → ครบ 50% promote เป็น 100%

5.3 🆕 Kargo (multi-stage promotion, GA 2024)

ใน GitOps ปกติ dev/staging/prod คือ folder แยกใน repo — เมื่ออยากอัปเดต prod ต้องเปิด PR ไปแต่ละ folder แล้ว merge เอง ถ้ามี 10 service และ 3 env = เปิด PR เยอะมาก Kargo ทำให้กระบวนการ "ส่ง version ใหม่จาก dev → staging → prod" เป็นอัตโนมัติ + verify ทุกขั้น:

ปัญหา GitOps แบบดั้งเดิม: promote dev → staging → prod ทำด้วยมือ (PR หลายตัว)

Kargo = orchestrator ของ promotion ข้าม env (สร้างโดย Akuity ทีมเดียวกับ Argo)

yaml
apiVersion: kargo.akuity.io/v1alpha1
kind: Stage
metadata: { name: prod, namespace: orders }
spec:
  subscriptions:
    upstreamStages: [{ name: staging }]      # promote มาจาก staging
  promotionMechanisms:
    gitRepoUpdates:
      - repoURL: https://github.com/acme/k8s-config
        writeBranch: main
        kustomize:
          images:
            - image: ghcr.io/acme/orders
              path: environments/prod
    argoCDAppUpdates:
      - appName: orders-prod
        appNamespace: argocd
  verification:
    analysisTemplates: [{ name: smoke-test }]

→ "Freight" (= image + manifest version) ไหลผ่าน Stage อัตโนมัติ + verify ทุกขั้น


Part 5.5: 🆕 Knative Serverless (Scale-to-Zero + Function Deployment)

Knative = K8s extension ที่ให้ "serverless experience" — auto-scale (รวม scale-to-zero), event-driven deployment

CNCF Incubating, ปี 2024 เป็น standard ของ "serverless บน K8s ของตัวเอง" (vs vendor lock AWS Lambda / GCP Cloud Run)

5.5.1 Knative มี 2 ส่วน

Knative แบ่งเป็น 2 ส่วนที่ใช้แยกกันได้ — Serving (auto-scale workload รวมถึง scale-to-zero, จัดการ revision/traffic split) และ Eventing (trigger แบบ event-driven ด้วย CloudEvents) เลือกใช้เฉพาะส่วนที่ต้องการได้:

Componentทำอะไร
Knative ServingAuto-scale workload (รวม scale-to-zero), traffic split, revision management
Knative EventingEvent-driven trigger (CloudEvents native), broker, subscription

5.5.2 ทำไม Knative

Knative แก้ความเจ็บปวดหลายอย่างของ K8s ปกติ — HPA scale ลงถึง 0 ไม่ได้ (idle workload เปลือง CPU ตลอด), canary ต้องตั้งเครื่องมือเพิ่ม, event-driven ต้อง subscribe broker เอง Knative ให้ scale-to-zero, traffic split และ event trigger มาในตัว:

text
ก่อน Knative:
- HPA scale 1 → N (ไม่เคยลด 0) → idle workload เปลือง CPU
- Canary ต้อง Argo Rollouts + Service config
- Event-driven ต้อง subscribe Kafka เอง

ด้วย Knative:
- Service ไม่มี request → scale 0 → auto-up เมื่อ request เข้า (~1 sec cold start)
- Built-in traffic split ระดับ revision
- HTTP trigger จาก CloudEvents

5.5.3 Knative Service ตัวอย่าง

yaml
apiVersion: serving.knative.dev/v1
kind: Service
metadata: { name: orders }
spec:
  template:
    metadata:
      annotations:
        autoscaling.knative.dev/minScale: "0"          # scale-to-zero
        autoscaling.knative.dev/maxScale: "100"
        # default metric = "concurrency" → target = 100 concurrent req / pod
        # ถ้าอยาก target แบบ request-per-second ให้เพิ่ม:
        #   autoscaling.knative.dev/metric: "rps"
        autoscaling.knative.dev/target: "100"          # ค่านี้คือค่าที่เรากำหนดเองในตัวอย่างนี้ ไม่ใช่ default ที่ Knative ตั้งมาให้อัตโนมัติถ้าไม่ใส่ annotation (ตรวจสอบพฤติกรรม default ล่าสุดจาก Knative Serving docs ก่อนใช้จริง)
    spec:
      containers:
        - image: ghcr.io/acme/orders:v1.2.3
          resources: { requests: { cpu: 100m, memory: 128Mi } }
  traffic:
    - revisionName: orders-v1
      percent: 90
    - revisionName: orders-v2
      percent: 10                  # canary built-in!
      tag: canary                   # available at canary.orders.svc.cluster.local

ความสามารถที่ได้ฟรี:

  • HTTPS endpoint อัตโนมัติ (Let's Encrypt integration)
  • Scale 0 → N → 0 ตาม traffic (KPA — Knative Pod Autoscaler — เร็วกว่า HPA)
  • Revision history (เก็บ N versions, rollback ง่าย)
  • Traffic splitting + canary
  • mTLS ถ้าใช้กับ Istio/Linkerd

5.5.4 Knative Eventing — CloudEvents Trigger

Knative Eventing ทำให้ deploy service แบบ event-driven ได้ง่าย — Broker เป็น router ของ event, Trigger คือ "ฟัง event type นี้แล้วเรียก service นี้" เมื่อมี CloudEvents เข้าตรง filter Knative จะ POST ไปยัง service ให้เอง (และ scale up จาก 0 ถ้าจำเป็น):

yaml
# Broker = event router
apiVersion: eventing.knative.dev/v1
kind: Broker
metadata: { name: default }
spec:
  config:
    apiVersion: v1
    kind: ConfigMap
    name: kafka-broker-config

---
# Trigger = ฟัง event แล้วเรียก service
apiVersion: eventing.knative.dev/v1
kind: Trigger
metadata: { name: orders-on-payment }
spec:
  broker: default
  filter:
    attributes:
      type: com.acme.payment.captured       # CloudEvents type
      source: /payment-service
  subscriber:
    ref:
      apiVersion: serving.knative.dev/v1
      kind: Service
      name: orders                            # call orders service

→ Event เข้าตรงตาม filter → Knative call HTTP POST ไป service พร้อม CloudEvents binary header

5.5.5 ใช้เมื่อ vs ไม่ใช้

✅ ใช้ Knative เมื่อ:

  • Workload spiky (peak 10x average) — scale-to-zero ประหยัด
  • Function-style services (รับ event → process → return)
  • ต้องการ "managed Lambda" บน K8s ของตัวเอง (no vendor lock)
  • Multi-cloud — Knative รันได้ทุก K8s (vs Cloud Run = GCP only)

❌ ไม่ใช้เมื่อ:

  • Stateful service (databases, in-memory cache)
  • Low-latency p99 (cold start ~1-3 sec)
  • Long-running background worker (ใช้ Job/CronJob ดีกว่า)
  • ทีมไม่มี K8s expert (ดูแล Knative + Istio ยาก)

5.5.6 Knative vs Cloud Run vs AWS Lambda

Knative (self-host)Cloud RunAWS Lambda
VendorNoneGCPAWS
Container support✅ (Lambda Container)
Scale-to-zero
Cold start~1-3s (warm pool ลดได้)~1s~100-500ms
Multi-cloud✅ (K8s)
Event sourceCloudEvents brokerEventarcEventBridge
Costinfra costper-requestper-request
Max runtimeunlimited60 min15 min
Best forK8s-native shopquick deploy on GCPserverless purist on AWS

Part 5.6: 🆕 Backstage — Internal Developer Platform (IDP)

Backstage = Open-source IDP (Spotify, donated to CNCF 2020, Incubating)

ปี 2024-2026 เป็น enterprise standard สำหรับบริษัทที่มี 50+ services — แก้ปัญหา "ไม่มีใครรู้ว่ามี service อะไรบ้าง + ใครเป็นเจ้าของ"

5.6.1 ปัญหาที่ Backstage แก้

พอองค์กรมี service เป็นร้อย ๆ ตัว ความวุ่นวายเชิงองค์กรจะตามมา — ไม่มีใครรู้ว่ามี service อะไรบ้าง ใครเป็นเจ้าของ ใคร depend ใคร doc กระจัดกระจาย Backstage แก้ด้วยการรวมทุกอย่างไว้ที่ portal เดียว:

เมื่อบริษัทโตจาก 5 service → 500 service:

text
- ไม่รู้ว่ามี service อะไรบ้าง — ทีมใหม่ search GitHub 2 ชม.
- ไม่รู้ว่าใครเป็นเจ้าของ — service ตาย ตอน 3am ไม่มีใครรับสาย
- ไม่รู้ว่า service A depend on B — ปิด B = พัง 10 service โดยไม่รู้
- ทุกทีม "สร้าง template" ต่างกัน — onboard engineer ใหม่ 2 สัปดาห์
- Doc กระจาย — บน Confluence, Notion, GitHub README, Slack messages
- Cost ไม่รู้ — ใครจ่าย AWS bill ของ team X?

5.6.2 4 Pillars ของ Backstage

1. Service Catalog

yaml
# catalog-info.yaml (commit ใน repo ของแต่ละ service)
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
  name: orders-service
  description: Handles order placement and tracking
  annotations:
    github.com/project-slug: acme/orders-service
    pagerduty.com/service-id: PXXXXX
    grafana/dashboard-selector: "tag in [orders]"
spec:
  type: service
  lifecycle: production
  owner: team-commerce
  system: ecommerce
  dependsOn:
    - component:default/payment-service
    - component:default/inventory-service
    - resource:default/orders-db
  providesApis:
    - orders-api
  consumesApis:
    - payment-api

→ Auto-discover ผ่าน GitHub/GitLab org → render เป็น service catalog ที่ search ได้

2. Software Templates (Scaffolder)

yaml
# scaffolder-templates/spring-boot-microservice/template.yaml
apiVersion: scaffolder.backstage.io/v1beta3
kind: Template
metadata:
  name: spring-boot-microservice
  title: Spring Boot Microservice
spec:
  parameters:
    - title: Service info
      properties:
        name: { type: string, title: Service name }
        team: { type: string, title: Owner team }
  steps:
    - id: fetch
      action: fetch:template
      input:
        url: ./skeleton                  # template repo
        values:
          name: ${{ parameters.name }}
          team: ${{ parameters.team }}
    - id: publish
      action: publish:github
      input:
        repoUrl: github.com?owner=acme&repo=${{ parameters.name }}
    - id: register
      action: catalog:register
      input:
        repoContentsUrl: ${{ steps.publish.output.repoContentsUrl }}
        catalogInfoPath: /catalog-info.yaml

→ Engineer คลิก "Create Spring Boot Service" → Backstage:

  1. Clone template
  2. Replace variables
  3. สร้าง GitHub repo + push code
  4. Setup CI/CD
  5. Register ใน service catalog
  6. Add to team

ขั้นตอนที่เคยใช้ 2 สัปดาห์ → 10 นาที

3. TechDocs (Documentation-as-Code)

markdown
<!-- docs/index.md ใน repo ของ service -->
# Orders Service

## Overview
...

## Architecture
![diagram](./architecture.svg)
yaml
# catalog-info.yaml
metadata:
  annotations:
    backstage.io/techdocs-ref: dir:.

→ Backstage build MkDocs จาก markdown ใน repo → serve ใน portal → Doc อยู่กับ code → ไม่ stale

4. Plugins Ecosystem

Pluginทำอะไร
GitHub ActionsShow CI status + trigger build
PagerDuty / OpsgenieOn-call + incident
Grafana / DatadogEmbed dashboards
KubernetesPod/deployment status
ArgoCDDeployment status + sync
SentryError tracking
SonarQubeCode quality
Cost (AWS/GCP)Team cost breakdown
Tech RadarApproved tech list

→ 1 portal เห็นทุกอย่าง — ไม่ต้องเปิด 10 tabs

5.6.3 Architecture

Backstage ประกอบด้วย portal (React) ที่แสดง catalog/template/docs, backend (Node) ที่เก็บข้อมูล catalog ใน Postgres, และ plugin จำนวนมากที่ดึงข้อมูลจากระบบภายนอก (GitHub, K8s, ArgoCD, Grafana) มารวมในที่เดียว:

5.6.4 Adoption Pattern

อย่าพยายาม onboard ทุก service พร้อมกัน — Backstage ได้ผลดีเมื่อค่อย ๆ ขยาย: ตั้ง backend ก่อน, import service สำคัญ, ทำ template ตัวแรก, เพิ่ม plugin ทีละตัว แล้วสุดท้ายบังคับให้ service ใหม่ต้องมี catalog-info.yaml:

Week 1-2:   Setup Backstage backend + Postgres
Week 3-4:   Import 10 critical service ผ่าน catalog-info.yaml
Week 5-6:   ทำ 1 software template (เช่น "new microservice")
Week 7-8:   Integrate GitHub Actions + ArgoCD plugin
Week 9-12:  Onboard ทุก service ใหม่ผ่าน template
Month 4+:   Add TechDocs + ArgoCD + Grafana plugin
Month 6:    "ห้าม merge ไม่มี catalog-info.yaml"

5.6.5 ใช้ vs ไม่ใช้

✅ ใช้ Backstage เมื่อ:

  • บริษัท 50+ services
  • มี Platform team อย่างน้อย 2 คน full-time
  • Onboarding engineer ใหม่ใช้เวลานาน
  • Service discovery ใช้เวลามาก
  • ต้องการ "developer experience" เป็น culture

❌ ไม่ใช้เมื่อ:

  • < 20 services (overhead ไม่คุ้ม)
  • ไม่มี Platform team (Backstage ต้อง maintain)
  • Org structure เปลี่ยนบ่อย (catalog stale ตลอด)

5.6.6 Alternatives ปี 2026

Toolจุดเด่นLicense
Backstage (CNCF)Plugin ecosystem ใหญ่สุด, customizableApache 2.0
PortSaaS, no-code workflow, faster setupCommercial
HumanitecPlatform Orchestrator (เน้น app config)Commercial
CortexSaaS, scorecards/maturity trackingCommercial
OpsLevelSaaS, similar to CortexCommercial
RoadieManaged Backstage (no infra)Commercial

💡 เริ่มยังไง: ลอง demo.backstage.io ดูก่อน. ถ้าใช่ → npx @backstage/create-app แล้วลอง POC 1 สัปดาห์

5.6.7 ตัวอย่าง Production

Backstage ไม่ใช่ของทดลอง — บริษัทใหญ่ที่มี engineer หลักพันใช้จริงใน production (Spotify ผู้สร้างเอง, Netflix, Expedia, American Airlines ฯลฯ) เป็นหลักฐานว่า pattern IDP นี้ scale ได้จริงในองค์กรขนาดใหญ่:

text
✅ Spotify — สร้าง Backstage (5000+ engineers)
✅ Netflix — ใช้ + custom plugins
✅ Expedia, American Airlines, Box, Roblox — production
✅ HelloFresh, LinkedIn — adoption

Part 6: CI/CD Pipeline

text
1. PR opened:
   - Run unit + integration tests
   - Run security scans (deps, image, SAST)
   - Run lint + style
   - Build artifact + image
   - Deploy to ephemeral test env
   - Run e2e
   - Review approval

2. Merge to main:
   - Build image, tag latest + git sha
   - Push registry
   - Update manifest in k8s-config repo (PR or auto-commit)
   - Argo CD picks up → deploy dev

3. Promote to staging:
   - Manual or auto trigger
   - Update staging manifest
   - Argo CD applies

4. Promote to production:
   - Approval gate
   - Update production manifest
   - Argo CD applies canary
   - Auto analysis

6.1 Tools

มีเครื่องมือ CI/CD ให้เลือกหลายแบบ — แบบ hosted ทั่วไป (GitHub Actions, GitLab CI, Jenkins) หรือแบบ K8s-native ที่รัน pipeline เป็น pod (Tekton, Drone) เลือกตาม ecosystem ที่ทีมใช้และว่าต้องการรัน pipeline ใน cluster หรือไม่:

  • GitHub Actions / GitLab CI / Jenkins
  • Tekton (K8s-native pipelines)
  • Drone

Part 7: Versioning

7.1 Image Tag

การตั้ง tag ของ image สำคัญต่อความน่าเชื่อถือของ deployment — ใช้ tag ที่ระบุ version ชัดเจน (semver หรือ git sha) ที่ immutable กฎเหล็กคืออย่าใช้ latest ใน production เพราะ pod แต่ละตัวอาจได้คนละ version โดยไม่รู้ตัว และ rollback ไม่ได้:

text
orders:v1.2.3                    # semver
orders:v1.2.3-abc1234             # semver + sha
orders:abc1234                    # sha (immutable, recommended)

⚠️ อย่าใช้ latest ใน production — ไม่รู้ version

7.2 API Versioning

ระหว่าง rolling — v1 + v2 รัน → API ต้อง backward compatible

หรือใช้ URL versioning:

text
/v1/orders → v1 service
/v2/orders → v2 service

Part 8: Multi-environment

text
dev      — feature branch deployed
staging  — main branch, integration test
prod     — released versions

แต่ละ env ต่าง:

  • Replica count
  • Resource limit
  • Database
  • Feature flags
  • Secret

ใช้ Kustomize overlay หรือ Helm values


Part 9: Rollback

9.1 K8s

K8s เก็บประวัติ revision ของ Deployment ไว้ จึง rollback กลับเวอร์ชันก่อนได้ทันทีด้วย kubectl rollout undo (หรือระบุ revision ที่ต้องการ) เหมาะกับกรณีฉุกเฉินที่ deploy ใหม่แล้วพัง:

bash
kubectl rollout undo deployment/orders
kubectl rollout history deployment/orders
kubectl rollout undo deployment/orders --to-revision=3

9.2 GitOps

ในโลก GitOps การ rollback คือการ revert git แล้วปล่อยให้ controller ซิงค์กลับ — ได้ความสอดคล้องว่า git = สถานะจริงเสมอ และ rollback ก็ถูกบันทึกเป็น commit (audit ได้) ไม่ใช่การแก้ cluster ด้วยมือที่หลุดจาก git:

bash
git revert <commit>
git push
# Argo CD apply

9.3 Argo Rollouts

ถ้าใช้ Argo Rollouts ทำ canary ก็มีคำสั่งคุม rollout โดยตรง — abort หยุด+ย้อนกลับ canary ที่กำลังไป, promote เดินหน้าข้าม step, undo ย้อนเวอร์ชัน ทำให้ควบคุม progressive delivery ได้ละเอียดตอนเกิดปัญหา:

bash
kubectl argo rollouts abort orders
kubectl argo rollouts promote orders --skip-current-step
kubectl argo rollouts undo orders

9.4 Database

⚠️ DB migration อาจ rollback ไม่ได้ — drop column, data lost → ใช้ expand-contract เสมอ


Part 10: Production Checklist

text
☐ Image immutable tag (git sha)
☐ Rolling update with maxUnavailable=0
☐ Readiness probe + graceful shutdown
☐ Canary deployment สำหรับ critical service
☐ Auto rollback on error rate spike
☐ Database migration backward compatible
☐ Feature flag for risky feature
☐ GitOps (Argo CD / Flux)
☐ CI/CD with tests + scans
☐ Multi-env (dev, staging, prod)
☐ Approval gate before prod
☐ Rollback procedure tested
☐ Image vulnerability scan
☐ SBOM generated
☐ Image signed (Cosign)
☐ Resource limits set
☐ HPA (auto-scale) configured
☐ Pod disruption budget
☐ Network policy

Part 10.5: Production War Stories

🔥 Knight Capital (2012): bad deploy ทำขาดทุน $440M ใน 45 นาที

deploy ไป 7 server แต่ลืม 1 server → mixed code: server เก่ายังรัน flag เก่าในโค้ด (legacy trading flag ภายใน ไม่ใช่ feature flag แบบที่บทนี้สอน) ที่กลายเป็น "buy aggressively" → 4 ล้านออเดอร์ผิดใน 45 นาที → บริษัทล้มละลาย

Lesson:

  • Zero manual deploy: ทุก deploy ผ่าน CI/CD เท่านั้น
  • All-or-nothing: ใช้ deployment strategy ที่ atomic (Blue-Green/Canary) — ไม่ใช่ "deploy ทีละเครื่อง"
  • Dead code = bomb: ลบ flag/code ที่เลิกใช้ทันที (อย่าทิ้ง flag เก่าไว้เป็นปี)
  • Kill switch: ทุก deploy ใหญ่ต้องมี feature flag ที่ปิดได้ใน < 1 นาที

🔥 GitLab (2017): backup ไม่มี + db wiped ระหว่าง maintenance — สูญ 6 ชม. data

DBA พิมพ์ผิด rm -rf บน primary แทน replica → no recent backup ใช้งานได้ → restore จาก snapshot 6 ชม. ก่อน

Lesson:

  • Test backup จริง (restore drill) — แค่ตั้ง schedule ไม่พอ
  • ระวัง destructive command ใน production (sudo cooldown, 2-person rule)
  • GitOps ช่วย — config drift ตรวจได้ทันที

🔥 Roblox (2021): 73-hour outage จาก rolling update ที่ rollback ไม่ได้

Root cause จริงตาม postmortem "Roblox Return to Service" (Jan 2022) = Consul performance degradation + bug ใน streaming detail logger ของ Consul ที่กดให้ leader election ไม่ stable → routing พัง → rollback แต่ระบบ caching cluster อยู่ใน state ที่ไม่รู้จัก → ต้อง rebuild ทั้ง cluster (อ่าน postmortem ฉบับเต็มใน Roblox Tech Blog)

Lesson:

  • Test rollback ทุก deploy — ถ้า rollback ไม่ได้ deploy ก็ไม่ได้
  • Stateful infrastructure (cache, DB) ต้อง backward-compat schema
  • Progressive rollout (canary) ช่วยตรวจปัญหาก่อนกระทบ 100%

🔥 Atlassian (2022): script deploy ผิดทำลายข้อมูล ~775 site นาน 14 วัน

Script delete app ใช้ list "app to remove" — แต่อ่าน list ผิด field → delete site แทน app → ตาม public postmortem ของ Atlassian = ~775 customer sites หายทั้ง Jira/Confluence/Jira Service Management → restore manual ใช้เวลานานสุดถึง 14 วันสำหรับบาง tenant (auto-restore ไม่รองรับ multi-tenant scenario นี้)

Lesson:

  • Destructive script ต้องมี dry-run + 2-person review
  • Soft delete (tombstone) ไม่ใช่ hard delete
  • DR plan ต้องครอบ "bug ของตัว ops tool"

Part 10.6: 📋 Cheat Sheet

เลือก deployment strategy

text
Stateless web/API           → Rolling (K8s default)
Risky feature / new logic   → Canary + auto-analysis
Major DB migration          → Blue-Green + read replica switch
Cannot tolerate downtime    → Blue-Green
Cost-constrained            → Rolling + readiness gating
Multi-region                → Cell-based + per-cell canary

Production deployment checklist

yaml
# Deployment essentials
strategy:
  type: RollingUpdate
  rollingUpdate: { maxSurge: 25%, maxUnavailable: 0 }  # ⚠️ % แบบนี้ปัดเศษต่างกันตาม replica count จริง — เช่น replicas=2 → 25% ปัดเป็น 1 pod extra = รัน 3 pod พร้อมกัน ต้องเช็คกับ replica count จริง + PodDisruptionBudget ด้านล่าง (minAvailable: 80%) ว่าไม่ชนกัน
progressDeadlineSeconds: 600          # fail rollout if exceeded
template:
  spec:
    terminationGracePeriodSeconds: 60  # let in-flight req finish
    containers:
      - name: app
        image: ghcr.io/acme/orders@sha256:abc...   # digest, not tag
        readinessProbe: { httpGet: { path: /actuator/health/readiness, port: 8080 }, periodSeconds: 5 }
        livenessProbe:  { httpGet: { path: /actuator/health/liveness,  port: 8080 }, periodSeconds: 10, failureThreshold: 3 }
        # ⚠️ Spring Boot 3 / JVM อาจ warm นาน (60-120s) — ปรับ failureThreshold ให้ครอบ
        #    เช่น failureThreshold: 60 + periodSeconds: 5 = 5-นาที window
        startupProbe:   { httpGet: { path: /actuator/health/liveness,  port: 8080 }, failureThreshold: 30, periodSeconds: 5 }
        lifecycle:
          preStop: { exec: { command: ["sh", "-c", "sleep 15"] } }   # SIGTERM grace
        resources:
          requests: { cpu: 200m, memory: 256Mi }
          limits:   { cpu: 1000m, memory: 512Mi }
---
# PodDisruptionBudget — กัน K8s drain ตาย workload
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata: { name: orders }
spec:
  minAvailable: 80%
  selector: { matchLabels: { app: orders } }

Image security baseline

bash
# 1. SBOM (Software Bill of Materials)
syft orders:v1.2.3 -o spdx-json > sbom.json

# 2. Vulnerability scan
trivy image --severity HIGH,CRITICAL --exit-code 1 orders:v1.2.3

# 3. Sign image (Sigstore Cosign)
cosign sign --key cosign.key ghcr.io/acme/orders:v1.2.3

# 4. Verify in admission controller (Kyverno/OPA)
cosign verify --key cosign.pub ghcr.io/acme/orders:v1.2.3

Deployment commands

bash
# Argo Rollouts
kubectl argo rollouts get rollout orders --watch
kubectl argo rollouts pause orders
kubectl argo rollouts promote orders
kubectl argo rollouts abort orders
kubectl argo rollouts undo orders

# Flagger
kubectl describe canary orders
kubectl get events --field-selector involvedObject.kind=Canary

# Argo CD
argocd app sync orders --prune
argocd app rollback orders <revision>
argocd app diff orders

# Kargo
kargo promote --project=orders --stage=prod --freight=abc123

Anti-pattern → Fix

Anti-patternแก้
image: orders:latestimage digest (@sha256:...) หรือ git sha tag
ไม่มี readinessProbeเพิ่มทุก deploy + แยก liveness/readiness
terminationGracePeriodSeconds: 30 (default) + slow drainตั้ง 60-120s + preStop sleep ก่อน SIGTERM
Manual kubectl apply ใน prodGitOps only — drift detection จับ
Feature flag ค้าง 6+ เดือนQuarterly flag audit, set expirationDate ใน OpenFeature
ไม่ test rollbackDR drill ทุก quarter — ลอง rollback บน staging

Part 11: Pitfalls

  1. Use latest tag — ไม่รู้ version ที่ running
  2. No readiness probepod รับ traffic ก่อนพร้อม
  3. No graceful shutdown — request drop ระหว่าง deploy
  4. Big bang DB migration — break rolling
  5. No rollback test — production fail → ไม่รู้จะกลับยังไง
  6. Manual kubectl in prod — drift จาก git
  7. No canary — bug ถึง 100% ทันที
  8. Feature flag เก่าค้าง — code dead branch
  9. Untracked feature flag — config drift
  10. No PodDisruptionBudget — K8s drain ตาย workload

Part 12: Checkpoint

  1. Rolling update vs Blue-Green vs Canary — แต่ละแบบเหมาะกับ workload แบบใด?
  2. Argo Rollouts ทำอะไรเพิ่มจาก K8s Deployment?
  3. Argo Rollouts vs Flagger — เลือกตอนไหน?
  4. GitOps คือ? Argo CD vs Flux เลือกอย่างไร?
  5. OpenFeature มาแก้ปัญหาอะไรของ feature flag เดิม?
  6. Kargo ใช้ทำอะไร? ต่างจาก Argo CD ยังไง?
  7. Expand-Contract DB migration ทำงานยังไง? ทำไมต้องมีอย่างน้อย 3 deploys?
  8. ทำไม latest image tag (และ tag ที่ mutable ได้) เป็นอันตราย? Image digest แก้ได้ยังไง?
  9. Feature flag กับ deployment ต่างกันยังไง? Decouple deploy/release หมายความว่ายังไง?
  10. PodDisruptionBudget ทำอะไร? ตั้งเป็น minAvailable: 100% ดีไหม?
  11. Canary auto rollback ทำงานยังไง? ต้องมี metric อะไรบ้าง?
  12. Image signing (Cosign) + SBOM ป้องกัน supply chain attack ยังไง?
  13. จาก Knight Capital case — bug อะไรที่ทำให้บริษัทล้มละลาย? วิธีกัน?
  14. terminationGracePeriodSeconds ตั้งสั้นเกินไปจะเกิดอะไรขึ้น?

Part 13: สรุปบทนี้

  • Rolling = K8s default, simple
  • Blue-Green = atomic switch, fast rollback, × resource
  • Canary = progressive %, auto analysis (Argo Rollouts หรือ Flagger)
  • Feature Flag = decouple deploy/release → ใช้ OpenFeature (CNCF standard)
  • GitOps = git = source of truth (Argo CD / Flux)
  • 🆕 Kargo = multi-stage promotion (dev → staging → prod) อัตโนมัติ
  • Expand-Contract = backward-compat DB migration
  • CI/CD: test + SAST + SBOM + scan (Trivy) + sign (Cosign) + deploy
  • Immutable image = digest (@sha256:...) ดีกว่า tag
  • Production: canary + auto-rollback + readiness gate + PodDisruptionBudget
  • Learn from outages: Knight Capital, GitLab, Roblox, Atlassian — รากเดียวกัน: test rollback, no manual deploy, kill switch

บทถัดไป (สุดท้าย!) — Polyglot Example — รวมทุกอย่างเป็นระบบจริง


← บทที่ 17 | สารบัญ | บทที่ 19: Polyglot Example →