🚀 Xây Dựng & Vận Hành Nhiều Dịch Vụ với Docker Compose
📖 Tại Sao Nên Sử Dụng Docker Compose?
Khi bạn có nhiều microservices—chẳng hạn như một API Spring Boot, một frontend Node.js và một cơ sở dữ liệu PostgreSQL—việc xây dựng và khởi động các container một cách thủ công sẽ trở nên phức tạp. Docker Compose giúp giải quyết vấn đề này bằng cách cho phép bạn:
- Định nghĩa tất cả dịch vụ trong một tệp (
docker-compose.yml) - Tự động xây dựng hình ảnh từ các Dockerfile cục bộ
- Tạo một mạng chia sẻ để các container có thể giao tiếp với nhau thông qua tên
- Mở rộng và điều phối chúng với một lệnh duy nhất
🏗️ Cấu Trúc Dự Án
Dưới đây là một bố trí mở rộng cho hai dịch vụ Java và một cơ sở dữ liệu:
multi-service-app/
├─ service-a/
│ ├─ src/...
│ ├─ Dockerfile
│ └─ pom.xml
├─ service-b/
│ ├─ src/...
│ ├─ Dockerfile
│ └─ pom.xml
├─ database/
│ └─ init.sql
├─ .env
└─ docker-compose.yml
Mỗi dịch vụ độc lập, với Dockerfile và các sản phẩm xây dựng riêng.
🐳 Bước 1: Viết Dockerfile cho Dịch Vụ
Ví dụ về service-a/Dockerfile (Spring Boot JAR):
FROM eclipse-temurin:17-jdk-alpine
WORKDIR /app
COPY target/service-a.jar app.jar
ENTRYPOINT ["java", "-jar", "/app/app.jar"]
Lặp lại tương tự cho service-b.
Mẹo
- Sử dụng tệp
.dockerignoređể bỏ quatarget/hoặcnode_modules/nhằm tăng tốc độ xây dựng. - Ghi chú hình ảnh cơ sở của bạn với một thẻ cụ thể để đảm bảo tính khả reproducibility.
⚙️ Bước 2: Tạo Tệp .env
Tập trung các biến môi trường:
POSTGRES_USER=myuser
POSTGRES_PASSWORD=secret
POSTGRES_DB=mydb
SPRING_PROFILES_ACTIVE=prod
Docker Compose sẽ tự động tải tệp này.
📝 Bước 3: Tệp docker-compose.yml
Dưới đây là một tệp Compose chất lượng sản xuất mà:
- Xây dựng cả hai microservices từ mã nguồn
- Khởi động một cơ sở dữ liệu PostgreSQL
- Gán tên container rõ ràng
- Tạo một mạng cầu nối dành riêng
- Cấu hình kiểm tra sức khỏe và chính sách khởi động lại
version: "3.9"
networks:
app-net:
driver: bridge
volumes:
db-data:
services:
service-a:
container_name: service-a-container
build:
context: ./service-a
dockerfile: Dockerfile
image: myorg/service-a:latest
ports:
- "8081:8080"
networks:
- app-net
environment:
SPRING_PROFILES_ACTIVE: ${SPRING_PROFILES_ACTIVE}
DB_URL: jdbc:postgresql://db:5432/${POSTGRES_DB}
DB_USER: ${POSTGRES_USER}
DB_PASS: ${POSTGRES_PASSWORD}
depends_on:
db:
condition: service_healthy
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/actuator/health"]
interval: 30s
timeout: 10s
retries: 5
service-b:
container_name: service-b-container
build:
context: ./service-b
dockerfile: Dockerfile
image: myorg/service-b:latest
ports:
- "8082:8080"
networks:
- app-net
environment:
SPRING_PROFILES_ACTIVE: ${SPRING_PROFILES_ACTIVE}
SERVICE_A_URL: http://service-a:8080
depends_on:
service-a:
condition: service_healthy
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/actuator/health"]
interval: 30s
timeout: 10s
retries: 5
db:
container_name: postgres-container
image: postgres:15-alpine
environment:
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: ${POSTGRES_DB}
ports:
- "5432:5432"
volumes:
- db-data:/var/lib/postgresql/data
- ./database/init.sql:/docker-entrypoint-initdb.d/init.sql
networks:
- app-net
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER}"]
interval: 10s
timeout: 5s
retries: 5
🔑 Điểm Nổi Bật
- Mạng:
app-netgiúp mỗi dịch vụ có một tên máy chủ tương ứng với khóa dịch vụ của nó (ví dụ:db,service-a). - Volumes:
db-datalưu trữ dữ liệu cơ sở dữ liệu giữa các lần khởi động lại container. - depends_on với healthcheck đảm bảo các dịch vụ phụ thuộc chờ cho đến khi cơ sở dữ liệu hoặc các dịch vụ khác khỏe mạnh.
- Môi trường: được lấy từ
.envđể quản lý bí mật dễ dàng hơn.
▶️ Bước 4: Xây Dựng và Khởi Động
Từ thư mục gốc của dự án:
docker compose up --build -d
Điều này sẽ:
- Xây dựng hình ảnh
myorg/service-avàmyorg/service-b. - Khởi động ba container (
service-a-container,service-b-container,postgres-container). - Kết nối chúng vào mạng
app-net.
Kiểm tra mọi thứ đang chạy:
docker ps
docker compose logs -f
🔗 Giao Tiếp Giữa Các Dịch Vụ
Bên trong app-net:
service-bcó thể gọiservice-atại http://service-a:8080- Cả hai dịch vụ kết nối với Postgres tại jdbc:postgresql://db:5432/mydb
DNS nội bộ của Docker có nghĩa là không cần địa chỉ IP cứng.
🛠️ Các Lệnh Quản Lý Vòng Đời Hữu Ích
| Hành động | Lệnh |
|---|---|
| Dừng các container | docker compose stop |
| Khởi động lại với mã mới | docker compose up --build -d |
| Dọn dẹp mọi thứ | docker compose down |
| Xóa hình ảnh cũng vậy | docker compose down --rmi all |
| Xem nhật ký container | docker compose logs -f service-a |
| Mở rộng một dịch vụ (ví dụ: service-b) | docker compose up -d --scale service-b=3 |
🧰 Thực Hành Tốt Nhất & Mẹo Chuyên Nghiệp
-
Xây Dựng Nhiều Giai Đoạn:
Biên dịch trong một hình ảnh builder, chỉ sao chép sản phẩm cuối cùng vào một hình ảnh runtime nhỏ hơn để có hình ảnh nhỏ hơn. -
Quản Lý Bí Mật:
Đối với sản xuất, sử dụng bí mật Docker hoặc một kho lưu trữ thay vì.envthông thường. -
Giám Sát & Thống Kê:
Tích hợp Prometheus/Grafana bằng cách thêm dịch vụ vào cùng một mạng. -
Tích Hợp CI/CD:
Sử dụngdocker compose buildtrong pipeline để tạo hình ảnh có phiên bản và đẩy chúng lên một registry. -
Giới Hạn Tài Nguyên:
Thêmdeploy.resources.limitsđể kiểm soát việc sử dụng CPU và bộ nhớ trong Swarm hoặc Compose v3+.
✅ Tóm Tắt
Với thiết lập này, bạn có thể:
- Định nghĩa, xây dựng và chạy nhiều microservices và một cơ sở dữ liệu chỉ với một lệnh.
- Gán mỗi container một tên sạch, mạng riêng biệt và kiểm tra sức khỏe.
- Mở rộng theo chiều ngang và quản lý cấu hình môi trường một cách liền mạch.
Đây là một giải pháp chất lượng sản xuất để khởi động kiến trúc microservice của bạn với Docker Compose.
Chỉ cần thêm nhiều dịch vụ, sao chép mẫu và ngăn xếp của bạn sẽ phát triển một cách sạch sẽ và đáng tin cậy.