Docker Compose là một công cụ thay đổi cuộc chơi dành cho các nhà phát triển đang làm việc với ứng dụng đa container. Nó đơn giản hóa việc khởi chạy các dịch vụ, mạng và volume chỉ với một tệp YAML duy nhất. Nhưng ngoài lệnh docker-compose up
cơ bản, còn rất nhiều mẹo khác giúp bạn sử dụng Docker Compose mượt mà, nhanh chóng và mạnh mẽ hơn. Dưới đây là 7 mẹo ít được biết đến mà bạn có thể áp dụng ngay.
1. Sử dụng Profiles để bật/tắt dịch vụ theo điều kiện
Profiles trong Docker Compose cho phép bạn chỉ chạy những dịch vụ cần thiết, giúp môi trường phát triển gọn nhẹ hơn. Thay vì phải quản lý nhiều tệp YAML hoặc ghi chú/xóa dịch vụ, bạn có thể nhóm các dịch vụ vào một profile và kích hoạt chúng bằng một lệnh đơn giản.
Cách hoạt động
Định nghĩa profile trong docker-compose.yml bằng khóa profiles trong từng service. Sau đó, dùng cờ --profile để kích hoạt. Những service không có profile sẽ luôn chạy mặc định, còn các service có profile chỉ khởi động khi được gọi cụ thể.
Ví dụ
Hãy tưởng tượng một dự án có ứng dụng web, cơ sở dữ liệu và công cụ gỡ lỗi như Adminer. Bạn không phải lúc nào cũng cần Adminer, vì vậy hãy đưa nó vào hồ sơ.
version: '3.8'
services: web: image: nginx:latest ports: - "8080:80" db: image: postgres:13 environment: POSTGRES_USER: user POSTGRES_PASSWORD: password adminer: image: adminer:latest profiles: - debug ports: - "8081:8080"
Chạy dịch vụ mặc định (web và db):
docker-compose up
Chạy kèm với Adminer:
docker-compose --profile debug up
Tại sao nó lại hữu ích?
- Tiết kiệm tài nguyên: Bỏ qua những dịch vụ nặng khi không cần thiết.
- Tệp YAML gọn gàng: Không cần xóa hoặc ghi chú dịch vụ thủ công.
2. Ghi đè biến môi trường với tệp .env
Sử dụng .env
là cách sạch sẽ để cấu hình dịch vụ mà không cần hardcode giá trị. Bạn có thể tùy biến theo từng môi trường hoặc từng lần chạy.
Cách hoạt động
Docker Compose tự động đọc tệp .env từ thư mục gốc của dự án. Bạn có thể sử dụng biến đó trong docker-compose.yml với cú pháp ${TEN_BIEN}. Để ghi đè, tạo một tệp .env khác và truyền vào với --env-file.
Ví dụ
Sau đây là thiết lập với ứng dụng Node.js và cổng có thể cấu hình.
.env
APP_PORT=3000
NODE_ENV=development
docker-compose.yml
version: '3.8'
services: app: image: node:16 command: npm start ports: - "${APP_PORT}:3000" environment: - NODE_ENV=${NODE_ENV} volumes: - ./app:/usr/src/app working_dir: /usr/src/app
app/index.js
const express = require('express');
const app = express();
app.get('/', (req, res) => res.send('Hello, Docker!'));
app.listen(3000, () => console.log('Server running on port 3000'));
Chạy nó:
docker-compose up
Ghi đè cho môi trường production: Tạo một tệp prod.env
:
APP_PORT=4000
NODE_ENV=production
Chạy với:
docker-compose --env-file prod.env up
Output: Ứng dụng chạy trên cổng 3000 với .env
, hoặc 4000 với prod.env
; NODE_ENV
thay đổi tương ứng.
Tại sao nó lại hữu ích?
- Linh hoạt: Chuyển đổi môi trường mà không sửa file YAML.
- Bảo mật: Không cần đẩy thông tin nhạy cảm lên Git.
3. Tối ưu hóa build bằng cache và context
Docker Compose hỗ trợ cấu hình build để tận dụng cache và context, giúp giảm thời gian build hình ảnh (image).
Cách hoạt động
Dùng khóa build với context và cache_from để xác định nơi lấy file và reuse các layer đã cache.
Ví dụ
Dockerfile
FROM python:3.9-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
CMD ["python", "app.py"]
docker-compose.yml
version: '3.8'
services: app: build: context: . dockerfile: Dockerfile cache_from: - myapp:latest image: myapp:latest ports: - "8000:8000"
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello(): return "Hello from Docker!"
if __name__ == "__main__": app.run(host="0.0.0.0", port=8000)
requirements.txt
flask==2.0.1
Build:
docker-compose build --pull
Tại sao nó lại hữu ích?
- Nhanh hơn: Tận dụng cache từ các lần build trước.
- Chính xác: Xác định rõ vùng context cần thiết.
4. Quản lý phụ thuộc với healthcheck
healthcheck
đảm bảo một service chỉ khởi động khi các service phụ thuộc đã sẵn sàng.
Cách hoạt động
Dùng healthcheck
cho service phụ thuộc, kết hợp với depends_on
và điều kiện service_healthy
.
Ví dụ
Một ứng dụng web cần có cơ sở dữ liệu MySQL khỏe mạnh.
docker-compose.yml
version: '3.8'
services: web: image: nginx:latest ports: - "8080:80" depends_on: db: condition: service_healthy db: image: mysql:8.0 environment: MYSQL_ROOT_PASSWORD: rootpass MYSQL_DATABASE: mydb healthcheck: test: ["CMD", "mysqladmin", "ping", "-h", "localhost"] interval: 10s timeout: 5s retries: 5
Chạy nó:
docker-compose up
Output: Dịch vụ web chờ cho đến khi MySQL hoạt động hoàn toàn và phản hồi mysqladmin ping
.
Tại sao nó lại hữu ích?
- Ổn định hơn: Tránh lỗi do kết nối quá sớm.
- Tùy chỉnh: Ghi đè healthcheck theo nhu cầu.
5. Đơn giản hóa log nhiều container bằng tên tùy chỉnh
Quản lý log từ nhiều container rất dễ rối. Docker Compose cho phép đặt container_name
và cấu hình driver log để dễ đọc hơn.
Cách hoạt động
Dùng container_name
để đặt tên, cấu hình logging
để định dạng đầu ra.
Ví dụ
Thiết lập với ứng dụng web và Redis, có tên rõ ràng và nhật ký JSON.
docker-compose.yml
version: '3.8'
services: web: image: nginx:latest container_name: myapp-web ports: - "8080:80" logging: driver: json-file options: max-size: "10m" max-file: "3" cache: image: redis:latest container_name: myapp-redis logging: driver: json-file options: max-size: "5m" max-file: "2"
Chạy và kiểm tra nhật ký:
docker-compose up -d
docker-compose logs
Tại sao nó lại hữu ích?
- Dễ đọc: Tên rõ ràng giúp scan log nhanh hơn.
- Kiểm soát: Giới hạn kích thước log, tránh đầy đĩa.
6. Sử dụng named volumes để lưu dữ liệu bền vững
Named volumes cho phép lưu dữ liệu giữa các lần restart container và chia sẻ giữa các dịch vụ.
Cách hoạt động
Khai báo ở phần volumes
cấp cao và sử dụng trong services
.
Ví dụ
version: '3.8'
services: db: image: postgres:13 environment: POSTGRES_USER: user POSTGRES_PASSWORD: password volumes: - db-data:/var/lib/postgresql/data
volumes: db-data:
Chạy nó:
docker-compose up
Tại sao nó lại hữu ích?
- Dễ di chuyển: Không phụ thuộc đường dẫn máy chủ.
- Tái sử dụng: Dùng chung volume giữa các dự án.
7. Mở rộng file Compose để dễ quản lý
Tách nhiều tệp Compose giúp modular hóa và quản lý dự án lớn dễ hơn.
Cách hoạt động
Tạo tệp docker-compose.base.yml
và tệp mở rộng theo môi trường dùng extends
.
Ví dụ
Một tệp cơ sở cho ứng dụng web và ghi đè phát triển.
docker-compose.base.yml
version: '3.8'
services: web: image: nginx:latest ports: - "8080:80"
docker-compose.dev.yml
version: '3.8'
services: web: extends: file: docker-compose.base.yml service: web volumes: - ./nginx.conf:/etc/nginx/nginx.conf
nginx.conf
events {}
http { server { listen 80; server_name localhost; location / { root /usr/share/nginx/html; index index.html; } }
}
Chạy nó:
docker-compose -f docker-compose.dev.yml up
Tại sao nó lại hữu ích?
- Tái sử dụng: Chia sẻ cấu hình chung giữa các môi trường.
- Dễ bảo trì: Giữ riêng phần cấu hình đặc thù cho từng môi trường.
Bước tiếp theo cho hành trình Docker Compose của bạn
Những mẹo như profile, biến môi trường, caching, healthcheck, logging, named volumes và file mở rộng có thể biến cách bạn dùng Docker Compose. Chúng tiết kiệm thời gian, giảm lỗi và làm quy trình làm việc linh hoạt hơn. Hãy thử áp dụng một vài mẹo trong dự án tiếp theo — bắt đầu với profile
hoặc healthcheck
để thấy hiệu quả ngay lập tức. Truy cập tài liệu chính thức của Docker Compose để khám phá thêm!
Cảm ơn các bạn đã theo dõi!