Skip to content

Lệnh docker-compose tạo và chạy các dịch vụ Docker

Phát triển một ứng dụng sẽ gồm rất nhiều container chạy trên các services khác nhau, việc khởi động và quản lý các container sẽ trở nên khó khăn. Vì vậy, Docker đã tạo ra một công cụ hữu ích giúp tăng tốc quá trình này - Docker Compose.

Docker Compose là một công cụ hỗ trợ xác định và chạy các ứng dụng multi-container . Docker Compose có thể xử lý đồng thời multi-container trong develop, staging, testing, release và cho CI/CD. Docker compose sử dụng cú pháp YAML để định nghĩa các services. Docker Compose hoạt động bằng cách áp dụng các quy tắc được xác định trong tệp docker-compose.yaml

Giả sử bạn đang phát triển một ứng dụng web có cấu trúc như sau:

  • Một container chứa ứng dụng web Node.js, chạy trên cổng 3000.
  • Một container chứa cơ sở dữ liệu MySQL, chạy trên cổng 3306.

Để triển khai ứng dụng này bằng Docker Compose, bạn cần tạo một tệp docker-compose.yml với các thông tin sau:

version: "3.9"
services:
  web:
    build: .
    ports:
      - "3000:3000"
    depends_on:
      - db
  db:
    image: mysql:latest
    environment:
      MYSQL_ROOT_PASSWORD: password
    ports:
      - "3306:3306"

Trong đó:

  • version: phiên bản của Docker Compose
  • services: danh sách các dịch vụ cần triển khai
  • web: định nghĩa dịch vụ web, bao gồm việc build image từ Dockerfile và ánh xạ cổng 3000 của container với cổng 3000 của máy host.
  • build: lấy image từ kết quả build dockerfile tại thư mục hiện tại.
  • depends_on cho biết dịch vụ này cần phụ thuộc vào dịch vụ db.
  • db: định nghĩa dịch vụ cơ sở dữ liệu MySQL, bao gồm việc sử dụng image MySQL, đặt mật khẩu cho root user và ánh xạ cổng 3306 của container với cổng 3306 của máy host.

Tham khảo: Lệnh docker-compose tạo và chạy các dịch vụ Docker

Các lệnh cơ bản trong Docker Compose

  • docker-compose up: Khởi động các container
  • docker-compose down: Dừng và xóa các container
  • docker-compose ps: Hiển thị trạng thái của các container
  • docker-compose build: Tạo image từ Dockerfile trong mỗi dịch vụ
  • docker-compose restart: Khởi động lại các container
  • docker-compose stop: Dừng các container
  • docker-compose rm: Xóa các container không sử dụng
  • docker-compose logs: Hiển thị các logs của các container
  • docker-compose config: Hiển thị các cấu hình của Docker Compose
  • docker-compose exec: Thực thi một lệnh trên một container
  • docker-compose port: Hiển thị các port của các container
  • docker-compose top: Hiển thị các process đang chạy trong các container

Lưu ý: Các lệnh trên phải được thực hiện trong thư mục chứa file docker-compose.yml

Cài đặt Docker-compose

# -- Có nhiều cách cài đặt, đây là 1 trong số đó, download trực tiếp từ server cung cấp
sudo curl -L "https://github.com/docker/compose/releases/download/1.22.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
# -- Apply executable permissions to the binary
sudo chmod +x /usr/local/bin/docker-compose
# -- View version
docker-compose --version
# => docker-compose version 1.23.1, build b02f1306

Cấu trúc của file docker-compose.yml

File docker-compose.yml được tổ chức gồm 4 phần:

Ý nghĩa
version Version của file docker-compose
services chứa các container. Với mỗi service là tên của một container
volumes Gắn đường dẫn trên host machine được sử dụng trên container
networks Sử dụng để cấu hình network cho ứng dụng

Với services có một số thành phần sau:

Ý nghĩa
image Chỉ ra image sẽ được dùng để build container.Tên image được chỉ định khi build một image trên máy host hoặc download từ Docker Hub
port Kết nối port của máy host đến port của container. Vd: 8080:80 (host_port:container_port)
volumes Gắn đường dẫn trên host machine được sử dụng trên container
enviroment Định nghĩa các biến môi trường được truyền vào Docker
depends_on Chọn các service được dùng là dependency cho container được xác định trong service hiện tại.
build Chỉ ra vị trị đường dẫn đặt Dockerfile.

Ví dụ file docker-compose.yml, giải thích các chỉ thị

version: '3.4'
# -- SERVICES
services:
  nginx:
    image: nginx:1.19.2-alpine
    ports:
      - 80:80
      - 443:443
    volumes:
      - ./docker/nginx/conf.d:/etc/nginx/conf.d
      - .:/var/www/
    links:
      - app
    depends_on:
      - app
    networks:
      - backend

  app:
    build:
      context: .
      dockerfile: docker/app/Dockerfile
    volumes:
      - .:/var/www/
    links:
      - db
      - redis
      - minio
    networks:
      - backend

  db:
    image: mysql:5.7
    command: mysqld --character-set-server=utf8 --collation-server=utf8_unicode_ci --skip-character-set-client-handshake
    ports:
      - "3389:3306"
    volumes:
      - mysqldata:/var/lib/mysql
      - ./docker/mysql/init/init-databases.sql:/docker-entrypoint-initdb.d/init-databases.sql
    environment:
      MYSQL_DATABASE: laravel
      MYSQL_ROOT_PASSWORD: secret
    networks:
      - backend

  redis:
    image: redis:latest
    restart: always
    volumes:
      - ./docker/logs/redis:/var/log/redis
    ports:
      - "6379:6379"
    networks:
      - backend
    command: redis-server --appendonly yes

  minio:
    image: minio/minio
    ports:
      - "9000:9000"
    volumes:
      - ./docker/minio:/data
    tty: true
    networks:
      - backend
    environment:
      MINIO_ACCESS_KEY: minio
      MINIO_SECRET_KEY: minio123
      MINIO_REGION_NAME: us-west-2
    command: ["minio", "server", "/data"]

# -- VOLUMES
volumes:
  mysqldata:
    driver: local

# -- NETWORK
networks:
  backend:
    driver: bridge

1. version

# -- Version docker compose sử dụng, trong ví dụ trên mình dùng version 3.4
version: '3.4'

2. services - Khai báo các service cần dùng cho ứng dụng

# -- Khai báo các service cần dùng cho ứng dụng, mỗi một server sẽ được chạy trong 1 container
services:
  nginx:
    ...
  app:
  ...

2.1 image - Khai báo docker image mà service dùng để build container

# -- Service nginx được build từ docker image nginx:1.19.2-alpine
image: nginx:1.19.2-alpine

2.2 ports - Chỉ định port của máy host và container

# -- Chỉ định port của host và container (HOST:CONTAINER)
# -- Truy cập vào port 81 của máy host đồng nghĩa với truy cập vào post 80 của container
ports:
    - 81:80

2.3 hostname - Khai báo hostname của container

# -- Khai báo hostname để các container khác có thể giao tiếp thông qua hostname, yêu cầu các container phải có cùng network
services:
    app:
  ...
    hostname: webserver
    ...

2.4 volumes - Mount data từ giữa container với máy host

# -- Mount data từ giữa container với máy host, dữ liệu được link giữa máy của bạn và container, giúp không bị mất dữ liệu mỗi lân stop container
# -- Mount code từ thư mục cùng cấp với file docker-compose, vào trong thư mục /var/www của container
volumes:
    - .:/var/www/

2.5 depends_on - Phục thuộc vào service nào đó

# -- Tùy chọn này cho biết thứ tự các service sẽ được build
# -- Theo như khai báo service 'app' sẽ được khởi chạy trước service 'nginx'
services:
  nginx:
  ...
    depends_on:
      - app
    ...      
app:
    # -- Từ service app có thể truy cập đến các service db, redis, minio
    links:
    - db
    - redis
    - minio

2.7 build - Build một server từ một dockerfile tự viết

# -- service app được build từ docker file trong thư mục docker/app/Dockerfile
# -- Nếu để cùng cấp file docker-compose.yml thì không cần dùng option 'dockerfile:'
app:
    build:
    context: .
    dockerfile: docker/app/Dockerfile

2.8 command - Lệnh command cần thực thi, trong container khi server được chạy

# -- Lệnh này sẽ chạy ngay sao khi copy các file yêu cầu vào container và build lên được container.
db:
    image: mysql:5.7
  command: mysqld --character-set-server=utf8 --collation-server=utf8_unicode_ci --skip-character-set-client-handshake

2.9 environment - Khai báo biến môi trường dùng cho cho container của service

# -- Để biết biến môi trường nào cần cho container thì phải đọc document image của nó ở Docker Hub
db:
    ...
    environment:
    MYSQL_DATABASE: laravel
    MYSQL_ROOT_PASSWORD: secret

2.10 restart - Tự động khởi động lại container

# -- 'restart' options:
# no: Containers won’t restart automatically.
# on-failure[:max-retries]: Restart the container if it exits with a non-zero exit code, and provide a maximum number of attempts for the Docker daemon to restart the container.
# always: Always restart the container if it stops.
# unless-stopped: Always restart the container unless it was stopped arbitrarily, or by the Docker daemon.
db:
    ...
    restart: always
    ...

3. volumes

# -- Khai báo một thư mục dùng chung cho toàn bộ services. Khi config option này thì mỗi lần stop container data của container đó sẽ không bị mất đi.
# -- VOLUMES
volumes:
  mysqldata:
    driver: local # Sử dụng thư mục hiện tại, nếu muốn thay đổi thêm option 'driver_opts'
# -- KHAI BÁO VOLUME (Nâng cao)
volumes:
  # volume không tạo mới, sử dụng volume đã có data1
  data1:
    external: true
  # volume không tạo mới, sử dụng volume đã có data-abc
  data2:
    external:
      name: data-abc
  # tạo volume local
  data3:
  # tạo volume có ánh xạ đường dẫn
  data4:
    driver: local
    driver_opts:
      type: none
      device: "/path/to/dir"
      o: bind
  # tạo volume mount từ NFS    
  data5:
    driver: local
    driver_opts:
      type: nfs
      o: addr=192.168.1.1,rw
      device: ":/path/to/dir"

4. networks

# -- Khai báo network sử dụng, network này sẽ được dùng trong các container, đảm bảo các container phải dùng chung 1 network thì mới có thể truy cập được đến nhau
# -- Khai báo một network có tên là backend, loại network là bridge
networks:
  backend:
    driver: bridge
# -- KHAI BÁO MẠNG (Nâng cao)
networks:
  # Tạo mạng cầu
  network1:
    driver: bridge
  # Mạng lấy bên ngoài, network2 là networkxyz đã có
  network2:
      external:
        name: networkxyz  
  # Tạo mạng overlay trên swam
  network3:
    driver: overlay
    name: my-overlay

Mẫu file docker-compose.yml

# VIẾT THEO CÚ PHÁP YAML, CHÚ Ý CHÍNH XÁC KHOẢNG TRẮNG ĐẦU CÁC DÒNG
# ------------------------------------
version: "3"                      # chọn viết theo bản 3 docs.docker.com/compose/compose-file/
# ------------------------------------
services:                         # CÁC DỊCH VỤ (CONTAINER) NĂM TRONG services
  pro-memcached:                  # (((1))) BẮT ĐẦU TẠO DỊCH VỤ THỨ NHẤT
    image: "memcached:latest"     # Image tạo ra dịch vụ
    container_name: c-memcached01 # Tên container khi chạy
    restart: always
    hostname: memcached
    networks:
      - my-network                # nối vào mạng my-network (tạo mạng này ở dưới)
    command:
      - "--conn-limit=2048"       # Giới hạn kết nối là 2048
      - "--memory-limit=2048"     # Giới hạn cho phép dùng tới 4096 MB bộ nhớ làm cache

  xtlab-apache:                   # (((2))) TẠO DỊCH VỤ HTTPD
    image: "httpd:version2"       # sử dụng image custome lại ở trên để tạo container
    container_name: c-httpd01     # tên khi chạy container HTTPD
    restart: always
    hostname: httpd01
    networks:
      - my-network
    ports:
      - "8080:80"                 # Mở cổng 8080 public, ánh xạ vào 80
      - "443:443"
    volumes:                      # Ánh xạ thự mục vào container
      - dir-site:/home/sites/     # Bind ổ đĩa - dir-site

  xtlab-mysql:                      # (((3))) TẠO DỊCH VỤ MYSQL
    image: "mysql:latest"
    container_name: mysql-product
    restart: always
    hostname: mysql01
    networks:
      - my-network
    environment:
      MYSQL_ROOT_PASSWORD: abc123   #Thiết lập password
    volumes:
      - /mycode/db:/var/lib/mysql  # thư mục lưu DB
      - /mycode/db/my.cnf:/etc/mysql/my.cnf  # ánh xạ file cấu hình

  xtlab-php:                         # (((4))) TẠO DỊCH VỤ PHP
    image: "php:version2"
    container_name: php-product      # tên container
    hostname: php01
    restart: always
    networks:
      - my-network
    volumes:
      - dir-site:/home/sites/        # Bind ổ đĩa - dir-site
# ------------------------------------
networks:                             # TẠO NETWORK
  my-network:
    driver: bridge
# ------------------------------------    
volumes:                              # TẠO Ổ ĐĨA
  dir-site:                           # ổ đĩa này lưu dữ liệu ở /mycode/
    driver_opts:
      device: /mycode/                # Hãy đảm bảo có thư mục /mycode/default
      o: bind

Nhìn vào kết quả thể hiện với mô hình hóa:

  • Có 4 container đang chạy (mysql-product, c-httpd01, php-product, c-memcached01), chúng được nối vào mạng my-network vậy giữa chúng có thể liên lạc với nhau qua cổng tương ứng (3304, 80, 9000, 11211). Riêng container có public cổng 8080 ánh xạ vào cổng 80 của nó, vậy bên ngoài mạng có thể kết nối tới nó qua cổng 8080.
  • Có ổ đĩa dir-site dữ liệu nó lưu tại máy HOST /mycode/, ổ đĩa này cũng nối vào container c-http01 ở thư mục /home/sites. Tương tự nó cũng nối vào php-product.
  • Container mysql-product cũng ánh xạ thư mục /mycode/db vào /var/lib/mysql
# -- Chạy lệnh sau để build các container
docker-compose up
# -- Nếu cần chỉnh sửa file thì down các container trước
docker-compose down

Mẫu docker-compose.yml (Với option nâng cao)

version: "3.7"

# KHAI BÁO CÁC DỊCH VỤ
services:
  # TÊN DỊCH VỤ
  web:                     
    # TÊN IMAGE TẠO TẠO CONTAINER                       
    image: busybox                                
    ## DÙNG IMAGE BUILD TỪ DOCKERFILE
    # build: 
    #   dockerfile: Dockerfile
    #   context: ./web

    # ÁNH XẠ CỔNG MÁY HOST VÀO CỔNG CONTAINER
    ports:                                       
      - 8099:80

    # MỞ CỔNG, KHÔNG ÁNH XẠ HOST, DÙNG NỘI BỘ MẠNG  
    # expose:
    #   - "3000"

    ## CHÍNH SÁCH KHỞI ĐỘNG
    restart: always

    # Nối vào mạng network1
    networks:
      - network1



    ## Thêm hoặc bỏ các quyền (Capability) trong container
    ## http://man7.org/linux/man-pages/man7/capabilities.7.html  
    # cap_add:
    #   - ALL
    # cap_drop:
    #   - NET_ADMIN
    #   - SYS_ADMIN

    # cap quyen root
    # privileged: true


    ## Ghi đè  entrypoint
    # entrypoint: /code/entrypoint.sh

    ## Ghi đè CMD mặc định nếu cần
    # command: ["bash", "ls"]

    ## ĐẶT TÊN CONTAINER 
    container_name: my-container

    ## THÊM BIẾN MÔI TRƯỜNG
    # environment:
    #   - TEN_BIEN1=giatri1
    #   - TEN_BIEN2=giatri2

    ## THÊM DỮ LIỆU HOST, TƯƠNG ĐƯƠNG KHAI BÁO TRONG /etc/hosts
    # extra_hosts:
    #    - "example.com:192.168.1.55"


    ## GẮN Ổ ĐĨA, THƯ MỤC VÀO CONTAINER
    # volumes:
    #   - /opt/data:/var/lib/mysql      
    #   - data3:/var/lib/mysql

    ## THIẾT LẬP LOG
    logging:
        options:
          max-size: "1m"
          max-file: "2"

    ## CẤU HÌNH DNS
    dns: 8.8.8.8      

    # CẤU HÌNH NẾU TRÊN SWARM
    deploy:
      replicas: 6                       # Số lượng container cho dịch vụ
      # placement:
      #   constraints:
      #       - node.role == manager    # Chỉ chạy ở node manager
      resources:                        # Thiết lập tài nguyên
        limits:                         # Giới hạn tài nguyên
          cpus: '0.50'                  # 50% của 1 Core
          memory: 50M 
        reservations:                   # Tài nguyên tối thiểu
          cpus: '0.25'
          memory: 20M

      restart_policy:
        condition: on-failure

#  ---------------------------------------------------------------------------------------
# KHAI BÁO VOLUME
volumes:
  # volume không tạo mới, sử dụng volume đã có data1
  data1:
    external: true
  # volume không tạo mới, sử dụng volume đã có data-abc
  data2:
    external:
      name: data-abc
  # tạo volume local
  data3:
  # tạo volume có ánh xạ đường dẫn
  data4:
    driver: local
    driver_opts:
      type: none
      device: "/path/to/dir"
      o: bind
  # tạo volume mount từ NFS    
  data5:
    driver: local
    driver_opts:
      type: nfs
      o: addr=192.168.1.1,rw
      device: ":/path/to/dir"
#  ---------------------------------------------------------------------------------------
# KHAI BÁO MẠNG
networks:
  # Tạo mạng cầu
  network1:
    driver: bridge

  # Mạng lấy bên ngoài, network2 là networkxyz đã có
  network2:
      external:
        name: networkxyz  
  # Tạo mạng overlay trên swam
  network3:
    driver: overlay
    name: my-overlay