Tối ưu Docker Compose: Dùng YAML Anchors và Merge Keys để dọn dẹp “Code rác”

Docker tutorial - IT technology blog
Docker tutorial - IT technology blog

Nỗi ám ảnh mang tên “Copy-Paste” trong Docker Compose

Mở một file docker-compose.yml dài 500 dòng nhưng 80% là mã lặp lại? Chắc hẳn ai làm DevOps cũng từng ngán ngẩm cảnh này. Khi dự án mở rộng từ vài container lên hệ thống microservices phức tạp, việc lặp lại environment, networks hay logging trở thành một gánh nặng thực sự.

Thử tưởng tượng bạn có 10 dịch vụ dùng chung một cấu hình log. Mỗi lần muốn đổi dung lượng log từ 10MB lên 50MB, bạn phải đi sửa ở 10 chỗ khác nhau. Chỉ cần sót một chỗ, hệ thống sẽ chạy không đồng nhất ngay. Đây chính là lúc YAML Anchors (&)Merge Keys (<<:) thể hiện sự lợi hại. Chúng giúp chúng ta tái sử dụng code thông minh ngay trong file YAML mà không cần công cụ bên thứ ba.

Quick Start: Dọn dẹp file Compose trong 5 phút

Thay vì viết đi viết lại, chúng ta định nghĩa một “khuôn mẫu” chung. Hãy xem cách mình tối ưu file cấu hình dưới đây:

version: "3.9"

# 1. Tạo khuôn mẫu bằng Extension Fields (bắt đầu bằng x-)
x-common-setup: &common-config
  restart: always
  networks:
    - backend-network
  logging:
    driver: "json-file"
    options:
      max-size: "10m"
      max-file: "3"

services:
  web-api:
    # 2. Dùng Merge Key để kéo cấu hình vào
    <<: *common-config
    image: my-api:latest
    ports:
      - "8080:8080"

  worker-process:
    <<: *common-config
    image: my-worker:latest
    environment:
      - NODE_ENV=production

networks:
  backend-network:
    driver: bridge

Kết quả thật bất ngờ. File cấu hình giờ đây cực kỳ gọn gàng. Nếu cần thay đổi max-size của log, mình chỉ việc sửa đúng một dòng duy nhất tại x-common-setup.

Giải mã bộ ba: Anchors, Aliases và Merge Keys

1. Anchor (&) – Điểm đánh dấu

Dấu & giống như việc bạn đặt tên cho một biến. Bạn đặt nó ngay trước khối dữ liệu muốn tái sử dụng (ví dụ: &my-config). Docker sẽ ghi nhớ toàn bộ nội dung trong khối này để dùng cho các bước sau.

2. Alias (*) – Lấy dữ liệu

Dấu * đóng vai trò gọi lại biến đã lưu. Khi viết *my-config, YAML tự hiểu bạn muốn sao chép toàn bộ nội dung từ Anchor đã khai báo vào vị trí đó.

3. Merge Key (<<:) – Trộn cấu hình

Đây là tính năng đáng giá nhất. Nếu chỉ dùng Alias (*), bạn sẽ thay thế toàn bộ giá trị của key. Nhưng với <<:, bạn có thể trộn các thuộc tính từ Anchor vào service hiện tại. Bạn vẫn thoải mái thêm cấu hình riêng hoặc ghi đè các giá trị cũ nếu cần.

services:
  auth-service:
    <<: *common-config # Lấy cấu hình chung
    environment:
      - SERVICE_NAME=auth # Thêm biến môi trường riêng
    restart: on-failure # Ghi đè thuộc tính 'always'

Kỹ thuật nâng cao cho dự án microservices lớn

Trong thực tế, mình thường chia nhỏ các Anchor theo mục đích sử dụng thay vì gộp chung một chỗ. Sử dụng Extension Fields (các key bắt đầu bằng x-) là một mẹo cực hay. Docker Compose sẽ bỏ qua các key này khi chạy, biến chúng thành nơi chứa template lý tưởng.

x-env-base: &env-base
  environment:
    - DB_HOST=postgres
    - REDIS_URL=redis://cache:6379

x-deploy-limits: &deploy-limits
  deploy:
    resources:
      limits:
        cpus: '0.50'
        memory: 512M

services:
  payment-service:
    <<: [*env-base, *deploy-limits] # Trộn nhiều Anchor cùng lúc
    image: payment:v1

Khi làm việc với các file YAML phức tạp, việc debug đôi khi rất nhức mắt. Để kiểm tra kết quả merge có đúng ý không, mình thường dùng các công cụ định dạng dữ liệu trực tuyến. Bạn có thể thử paste cấu hình vào JSON Formatter tại Toolcraft để soi lỗi nhanh hơn. Cách này giúp mình phát hiện ngay các key bị ghi đè sai mà không cần chạy thử container.

3 lưu ý “xương máu” để tránh lỗi

  • Thứ tự cực kỳ quan trọng: Bạn phải định nghĩa Anchor (&) ở phía trên trước khi gọi Alias (*) ở phía dưới. Nếu đặt ngược lại, Docker sẽ báo lỗi không tìm thấy tham chiếu.
  • Cạm bẫy với Mảng (Arrays): Merge Key hoạt động hoàn hảo với kiểu key-value (maps). Tuy nhiên, với danh sách (arrays), nó sẽ ghi đè toàn bộ chứ không cộng dồn. Nếu Anchor có 3 biến môi trường và service cũng định nghĩa environment, danh sách cũ sẽ biến mất hoàn toàn.
  • Ưu tiên Docker Compose V2: Hãy dùng lệnh docker compose thay vì bản cũ để tận dụng khả năng xử lý YAML tốt nhất.

Lời kết từ kinh nghiệm thực chiến

Đừng lạm dụng Anchors cho những thứ quá đơn giản. Nếu một thuộc tính chỉ xuất hiện 2 lần, việc tạo Anchor có thể làm file khó đọc hơn. Mình thường chỉ áp dụng kỹ thuật này cho cấu hình Logging, biến môi trường Database hoặc các giới hạn tài nguyên (CPU/RAM). Một file docker-compose.yml chuyên nghiệp là file thể hiện rõ cấu trúc hệ thống, chứ không phải một mê cung của những đoạn mã lặp lại.

Share: