GitOps และ CI/CD สำหรับ Kubernetes
ไอเดียในหนึ่งประโยค
หัวข้อที่มีชื่อว่า “ไอเดียในหนึ่งประโยค”GitOps ทำให้ Git repository เป็น single source of truth ของ desired state ของ cluster โดยมี reconciler ที่รันอยู่ใน cluster คอย pull แล้ว apply state นั้น แทนที่จะให้ CI job push state เข้ามาจากภายนอก
Push-based CI/CD เทียบกับ pull-based GitOps
หัวข้อที่มีชื่อว่า “Push-based CI/CD เทียบกับ pull-based GitOps”CI/CD pipeline แบบดั้งเดิมเป็น push-based CI job build artifact เสร็จแล้วรัน kubectl apply หรือ helm upgrade ตรงเข้า cluster เลย โดยใช้ credential ของ cluster ที่ตัว CI system เก็บไว้เอง ทำงานได้ก็จริง แต่หมายความว่า CI system ซึ่งบ่อยครั้งเป็น SaaS ของบุคคลที่สามที่อยู่นอก infrastructure boundary ของเรา ต้องมี access เข้า cluster แบบถาวรและมักเป็น privileged access ด้วย
GitOps กลับทิศทางนี้ reconciler อย่าง Argo CD หรือ Flux รันอยู่ ภายใน cluster ที่จัดการ คอย watch Git repository ที่เก็บ manifest หรือ Helm values ที่ต้องการอยู่ตลอด พอ repository เปลี่ยน reconciler จะ pull desired state ใหม่แล้ว apply เอง cluster เป็นฝ่ายเอื้อมไปหา Git ไม่ใช่ Git เอื้อมเข้ามาใน cluster
# Push-based: CI applies directly, using credentials CI itself holdskubectl apply -f manifests/helm upgrade myapp ./mychart -f values-prod.yaml
# Pull-based (GitOps): CI's job stops at committing to Git;# an in-cluster reconciler (Argo CD / Flux) takes it from theregit commit -am "bump myapp image tag to 1.5.0"git push origin mainทำไม pull-based model ถึงคุ้มค่า
หัวข้อที่มีชื่อว่า “ทำไม pull-based model ถึงคุ้มค่า”ย้ายขั้นตอน apply เข้าไปอยู่ใน cluster ได้ประโยชน์จริงสามอย่าง
- ไม่มี external system ตัวไหนต้องถือ credential ระดับ cluster-admin งานของ CI system จบแค่
git pushมีแค่ reconciler ที่อยู่ใน cluster ซึ่งเราคุมเองเท่านั้นที่ authenticate กับ API server ด้วยสิทธิ์เขียน - ตรวจจับ drift ได้ เพราะ reconciler เทียบสถานะจริงของ cluster กับสถานะที่ Git ประกาศไว้อยู่ตลอด การแก้แบบ out-of-band ด้วย
kubectl editหรือkubectl scaleจะถูกจับได้ และสามารถ revert กลับอัตโนมัติ แทนที่จะเบี่ยงเบนไปจากสิ่งที่ Git บอกไว้แบบเงียบ ๆ - มีประวัติที่ตรวจสอบได้ในตัว ทุกการเปลี่ยน desired state คือ Git commit หนึ่งอัน ใครเปลี่ยนอะไร เมื่อไหร่ ทำไม (ผ่าน commit message) อยู่ในประวัติเดียวกับที่เราใช้ review code อยู่แล้ว
รูปแบบ pipeline ที่ใช้งานจริง
หัวข้อที่มีชื่อว่า “รูปแบบ pipeline ที่ใช้งานจริง”การ build กับ deploy แบบ GitOps แยกเป็นสอง repository (หรือสอง stage ที่แยกกันชัดเจน) หนึ่งสำหรับ build แอป อีกหนึ่งสำหรับประกาศว่าควรรันอะไรอยู่
# Stage 1 — application repo CI: build and publish the imagedocker build -t myregistry/myapp:1.5.0 .docker push myregistry/myapp:1.5.0
# Stage 2 — update the manifests/values repo that GitOps watches# (a small CI step, or a bot, edits the tag and commits)sed -i 's/tag: .*/tag: "1.5.0"/' values-prod.yamlgit commit -am "myapp: bump image tag to 1.5.0"git push origin main
# From here, the GitOps reconciler (not CI) notices the commit and applies it.# In a push-based setup, stage 2 would instead run directly against the cluster:# helm upgrade myapp ./mychart -f values-prod.yamlใน flow แบบ GitOps ตัว CI system ไม่เคยรัน kubectl หรือ helm upgrade เข้า cluster จริงเลย หน้าที่ของตัวเองจบแค่ build image แล้ว commit desired state ใหม่เข้า Git
flowchart LR dev["Developer"] -->|"push manifest / values change"| git["Git repository (desired state)"] ci["CI pipeline"] -->|"build & push image"| registry["Container registry"] git -->|"watched / polled"| reconciler["In-cluster GitOps reconciler (Argo CD / Flux)"] reconciler -->|"apply diff"| cluster["Live cluster state"] cluster -->|"pulls referenced image"| registry