Build & CI/CD Pipeline cho Microservices — Từ code đến deploy (BMS)

Phong

Mở đầu

Ảnh: Lukas Blazek — Pexels

Khi bạn có một monolith, chuyện build và deploy tương đối đơn giản: một pipeline, một artifact, một lần deploy. Nhưng khi hệ thống được chia thành hàng chục (hay hàng trăm) microservices, bài toán build & CI/CD trở nên phức tạp hơn nhiều. Làm sao để build nhanh? Chọn monorepo hay multirepo? Quản lý artifact thế nào để không bị lộn xộn?

Chương 7 của Building Microservices (Sam Newman) bàn về những quyết định này — và dưới đây là những điểm chính rút ra được.

Monorepo vs Multirepo — Cuộc chiến không hồi kết

Ảnh: Brett Sayles — Pexels

Một trong những quyết định đầu tiên khi tổ chức code cho microservices là: gom tất cả vào một repo (monorepo) hay mỗi service một repo riêng (multirepo). Cả hai đều có tradeoff, và không có câu trả lời đúng tuyệt đối.

Multirepo — mỗi service có repo riêng, pipeline riêng, team riêng. Ưu điểm lớn nhất là isolation: một service build fail không ảnh hưởng đến service khác, team có toàn quyền với repo của mình, và không có chuyện "ai đó vô tình break code của mình". Nhược điểm: khó quản lý dependency chung, khó thực hiện refactor xuyên service, và mỗi lần thay đổi API contract lại phải phối hợp nhiều repo.

Monorepo — tất cả code trong một repo. Google, Meta, Uber đều dùng monorepo với quy mô khổng lồ. Lợi ích: dễ chia sẻ code, dễ refactor global, có một version duy nhất của mọi thư viện. Nhược điểm: tooling phải cực kỳ mạnh (Google phải tự build Bazel), pipeline phải thông minh để chỉ build những service bị ảnh hưởng, và ai cũng có quyền truy cập vào code của nhau.

Quan điểm của Sam Newman: đa số tổ chức nên bắt đầu với multirepo trừ khi bạn có đội ngũ tooling riêng để xây dựng hệ thống build cho monorepo. Multirepo đơn giản hơn, dễ maintain hơn cho quy mô vừa và nhỏ.

Build Artifact — Đóng gói đúng cách

Ảnh: Mumtaz Niazi — Pexels

Sau khi build xong, artifact cần được đóng gói và lưu trữ ở một nơi đáng tin cậy — không phải trên máy dev. Các lựa chọn phổ biến:

Container image (Docker). Đây là chuẩn mới cho microservices. Mỗi service build thành một Docker image, push lên container registry (Docker Hub, ECR, GCR, GitLab Registry). Image là immutable: mỗi lần build ra một tag mới, không ghi đè lên tag cũ. Điều này cho phép rollback dễ dàng — chỉ cần deploy lại image tag cũ.

Package repository. Một số team chọn đóng gói thành JAR, wheel, npm package và push lên repository riêng (Nexus, Artifactory). Cách này phù hợp khi microservices chia sẻ thư viện chung dạng binary.

Immutable artifact. Nguyên tắc quan trọng: artifact chỉ được tạo một lần và dùng cho mọi môi trường (dev → staging → production). Không rebuild lại artifact cho từng môi trường — vì rebuild có thể ra kết quả khác (different binary). Build một lần, promote artifact qua các môi trường.

CI/CD Pipeline cho Microservices

Với mỗi service, lý tưởng nhất là có một pipeline CI/CD riêng biệt. Pipeline đó sẽ:

  • Build — compile code, chạy unit test, phân tích tĩnh (lint, type check)
  • Package — tạo artifact (Docker image), push lên registry
  • Test — chạy integration test, contract test (Pact), smoke test
  • Deploy — deploy lên môi trường (staging → production)

Mỗi service deploy độc lập là mục tiêu cuối cùng. Nhưng trong thực tế, không phải lúc nào cũng làm được — có những thay đổi cross-service cần phối hợp. Giải pháp: dùng feature flagsbackward-compatible API changes để giảm thiểu coupling trong deployment.

Build Dependencies & Versioning

Một vấn đề thường gặp: service A phụ thuộc vào thư viện X, service B cũng phụ thuộc vào X. Khi X thay đổi, cả A và B đều bị ảnh hưởng. Cách quản lý:

  • Dùng semantic versioning cho thư viện chung — major version bump cho breaking change
  • Hạn chế shared library — copy-paste code còn tốt hơn là tạo dependency hell
  • Test cả downstream — CI pipeline của thư viện nên test với tất cả services dùng nó (multirepo) hoặc chỉ build những services bị ảnh hưởng (monorepo với build graph)

Key Takeaways

  • Monorepo phù hợp với tổ chức lớn có team tooling riêng (Google, Meta); đa số nên bắt đầu với multirepo
  • Mỗi service nên có pipeline CI/CD riêng — build, test, package, deploy độc lập
  • Artifact là immutable — build một lần, promote qua các môi trường, không rebuild
  • Container image là chuẩn đóng gói cho microservices — tag version rõ ràng, rollback dễ dàng
  • Giảm shared library dependency — nếu phải dùng thì semantic versioning + downstream testing

Glossary

Thuật ngữÝ nghĩa
MonorepoMột repository chứa code của tất cả services
MultirepoMỗi service có một repository riêng
ArtifactSản phẩm đầu ra của quá trình build (Docker image, JAR, binary)
Immutable artifactArtifact không thay đổi sau khi tạo — dùng cho mọi môi trường
CI/CD PipelineQuy trình tự động: build → test → deploy
Feature FlagCờ bật/tắt tính năng, cho phép deploy code chưa active
Container RegistryNơi lưu trữ Docker images (Docker Hub, ECR, GCR)

Kết

Build & CI/CD là một trong những khía cạnh thực tế nhất khi làm microservices. Chọn sai cách tổ chức repository hoặc pipeline có thể làm team chậm đi đáng kể. Nguyên tắc chung: giữ đơn giản (multirepo + container image + pipeline riêng), chỉ phức tạp hoá khi thực sự cần.

Chapter tiếp theo sẽ bàn về deployment patterns — cách deploy microservices lên production mà không làm gián đoạn người dùng: blue-green deployment, canary release, rolling update.