3 sai lầm tôi đã mắc phải khi mới bắt đầu sử dụng Docker

Tác giả Starlink, T.M.Một 14, 2025, 09:00:08 CHIỀU

« Chủ đề trước - Chủ đề tiếp »

0 Thành viên và 1 Khách đang xem chủ đề.

Docker có thể khá khó sử dụng, nhưng có nhiều cách để sử dụng nó dễ dàng hơn—tôi có ba cách dành cho bạn.

Bạn đang bắt đầu sử dụng Docker và cảm thấy hơi choáng ngợp? Các lệnh có thể khá khó sử dụng, và các phương pháp hay nhất không được giải thích rõ ràng. Tôi có ba điều ước mình biết khi bắt đầu sử dụng Docker, có thể sẽ hữu ích cho bạn.


Các dự án phần mềm lớn như Docker thường ẩn giấu những phương pháp hay nhất và cảnh báo quan trọng sâu trong tài liệu kỹ thuật. Người mới bắt đầu phải đối mặt với một loạt chi tiết kỹ thuật nhưng lại thiếu những hướng dẫn rõ ràng và súc tích để giúp họ vạch ra lộ trình học tập. Khi mới bắt đầu, tôi không hiểu cách quản lý nhiều dịch vụ phụ thuộc, xử lý các lệnh phức tạp, hay những nguy cơ cụ thể khi chạy các quy trình container dưới dạng root. Nếu những chi tiết quan trọng này được nêu rõ ràng, tôi đã có thể tiết kiệm rất nhiều thời gian và tránh được những sai lầm tiềm ẩn gây tốn kém.

1. Không sử dụng Docker Compose cho cấu hình đa dịch vụ

Docker nổi tiếng với khả năng chạy các dịch vụ đơn lẻ được đóng gói trong container; bạn chỉ cần cung cấp cho nó một lệnh và các tùy chọn cần thiết, và nó sẽ tự động chạy dịch vụ của bạn. Nhưng điều có lẽ ít người mới biết đến là nó có thể phối hợp nhiều dịch vụ với nhau bằng Docker Compose. Docker Compose có thể giúp việc kéo và cấu hình một hoặc nhiều dịch vụ trở nên đơn giản hơn nhiều, đặc biệt là khi chúng phụ thuộc lẫn nhau.

Docker Compose thực chất là một lệnh con của Docker, và nó sử dụng tệp cấu hình YAML (gọi là docker-compose.yaml) để chỉ định các dịch vụ. Sau đây là một đoạn trích từ tài liệu chính thức của Gitea (một máy chủ Git):

Mã nguồn [Chọn]
# docker-compose.yaml
volumes:
  postgres-data:
  gitea-data:

services:
  server:
    image: docker.gitea.com/gitea:nightly
    volumes:
      - gitea-data:/data
    depends_on:
      - db

  db:
    image: docker.io/library/postgres:14
    volumes:
      - postgres-data:/var/lib/postgresql/data

Ví dụ trước sử dụng bản dựng Nightly (chưa ổn định, đang trong giai đoạn phát triển) của Gitea, bản dựng này hoạt động vào tháng 11 năm 2025, nhưng có thể sẽ không hoạt động với Postgres 14 vào một thời điểm nào đó trong tương lai. Tệp YAML chỉ mang tính chất minh họa.

Ví dụ trước hướng dẫn Docker tạo hai dịch vụ, trong đó một dịch vụ "phụ thuộc" vào dịch vụ kia. Đây là một ví dụ không hoạt động vì thiếu một số tùy chọn quan trọng. Tuy nhiên, ví dụ sau đây hoạt động đầy đủ.

Mã nguồn [Chọn]
# docker-compose.yaml
networks:
  gitea:
    external: false

volumes:
  postgres-data:
  gitea-data:

services:
  server:
    image: docker.gitea.com/gitea:nightly
    container_name: gitea
    environment:
      - USER_UID=1000
      - USER_GID=1000
      - GITEA__database__DB_TYPE=postgres
      - GITEA__database__HOST=db:5432
      - GITEA__database__NAME=gitea
      - GITEA__database__USER=gitea
      - GITEA__database__PASSWD=gitea
      - TZ=America/New_York
    restart: always
    networks:
      - gitea
    volumes:
      - gitea-data:/data
    ports:
      - "3000:3000"
      - "2222:22"
    depends_on:
      - db

  db:
    image: docker.io/library/postgres:14
    restart: always
    environment:
      - POSTGRES_USER=gitea
      - POSTGRES_PASSWORD=gitea
      - POSTGRES_DB=gitea
    networks:
      - gitea
    volumes:
      - postgres-data:/var/lib/postgresql/data

Có một số điều đáng chú ý từ tệp Compose đó:

    Các biến môi trường cung cấp các tùy chọn cấu hình cho từng dịch vụ—ví dụ: tạo thông tin xác thực cho cơ sở dữ liệu và cung cấp thông tin xác thực đó cho Gitea.
    Dịch vụ Gitea lưu trữ dữ liệu kho lưu trữ trong thư mục /data bên trong vùng chứa và chúng tôi gắn kết dữ liệu đó dưới dạng ổ đĩa cố định trên máy chủ.
    Dịch vụ Postgres lưu trữ dữ liệu trong thư mục /var/lib/postgresql/data bên trong vùng chứa và chúng tôi gắn kết thư mục đó dưới dạng ổ đĩa cố định trên máy chủ.

Một ổ đĩa cố định lưu trữ dữ liệu bên ngoài vùng chứa, trong /var/lib/docker/volumes.

Bây giờ, hãy điều hướng đến thư mục chứa tệp docker-compose.yaml và chạy lệnh docker compose up. Docker sẽ kéo các hình ảnh cần thiết và khởi động cả hai dịch vụ, giúp chúng hoạt động liền mạch cùng nhau. Sử dụng Docker Compose giúp đơn giản hóa quy trình chỉ định một ngăn xếp ứng dụng phức tạp, giúp việc này dễ dàng và đơn giản hơn so với việc viết một tập lệnh Bash.

2. Chạy các tiến trình chứa trong container với tư cách là root

Khi bắt đầu sử dụng Docker, tôi chỉ cho rằng các container được cô lập hoàn toàn; rằng việc chạy một tiến trình rootful bên trong container đồng nghĩa với việc nó không thể gây hại. Tôi đã sai.

Theo mặc định, các tiến trình chạy bên trong vùng chứa Docker chia sẻ cùng không gian tên UID với máy chủ. Nếu bạn chạy một tiến trình gốc bên trong vùng chứa, nó sẽ có UID 0 trên máy chủ nhưng bị hạn chế khả năng. Tuy nhiên, điều này vẫn tạo ra một rủi ro bảo mật nghiêm trọng, vì kẻ tấn công có thể khai thác lỗ hổng để thoát khỏi vùng chứa và giành quyền truy cập root vào máy chủ.

Các quy trình được cô lập với máy chủ cục bộ cũng không hoàn toàn an toàn trước rủi ro. Nếu các ứng dụng được chứa trong container của bạn xử lý dữ liệu bên ngoài theo bất kỳ cách nào (ví dụ: giám sát mạng, đọc tài liệu, nhận lưu lượng truy cập, v.v.), thì hệ thống của bạn có nguy cơ bị dữ liệu độc hại và khai thác. Ít nhất, bạn nên coi các container gốc như các quy trình cấp gốc và tránh hoàn toàn các hành động rủi ro với chúng.

Giải pháp là chạy Docker ở chế độ không cần root. Nếu bạn làm theo các bước được cung cấp trong tài liệu hướng dẫn của Docker, việc chạy một tiến trình cấp root bên trong container sẽ ánh xạ nó đến một người dùng không có đặc quyền trên máy chủ. Điều tương tự cũng áp dụng cho daemon Docker—cả hai đều chạy các tiến trình mà không cần quyền root thực sự.

Nếu bạn không muốn làm điều đó, ít nhất bạn nên chạy các tiến trình với cờ --user hoặc chỉ thị USER trong Dockerfile. Tuy nhiên, đôi khi điều đó là không đủ, vì tiến trình được chứa trong container của bạn cần phải sửa đổi các vùng được bảo vệ bên trong container.

Cá nhân tôi dùng Podman thay vì Docker vì nó chạy container với tư cách người dùng không có đặc quyền theo mặc định. Podman là một giải pháp thay thế Docker, tương thích 100% với Docker, và còn có cả Podman Desktop — tôi thực sự khuyên dùng Podman.

3. Không sử dụng UI, hoàn thiện shell hoặc bí danh

Các lệnh Docker có thể khá dài, và nếu bạn giống tôi, bạn sẽ phải thực thi chúng thường xuyên. Điều này dẫn đến tình huống việc nhập hàng chục lệnh sẽ làm gián đoạn quy trình làm việc của bạn.

Ví dụ, hãy lấy lệnh Pi-hole (như được đề xuất trong tài liệu chính thức):

Mã nguồn [Chọn]
docker run \
  --name pihole \
  -p 53:53/tcp \
  -p 53:53/udp \
  -p 80:80/tcp \
  -p 443:443/tcp \
  -e TZ=Europe/London \
  -e FTLCONF_webserver_api_password="correct horse battery staple" \
  -e FTLCONF_dns_listeningMode=all \
  -v./etc-pihole:/etc/pihole \
  -v./etc-dnsmasq.d:/etc/dnsmasq.d \
  --cap-add NET_ADMIN \
  --restart unless-stopped \
  pihole/pihole:latest

Trên thực tế, để thực thi lệnh đó, bạn sẽ sử dụng Docker Compose hoặc dịch vụ systemd. Tuy nhiên, các lệnh Docker thường khá cồng kềnh, và điều đó không thể phủ nhận.

Để quản lý các lệnh dài này, chúng ta có ba tùy chọn: giao diện người dùng (UI), hoàn thiện shell và bí danh.

Giao diện người dùng (UI) là lựa chọn ưa thích của tôi, và mặc dù Docker Desktop thường được đề xuất, cá nhân tôi vẫn thích giao diện người dùng terminal hơn — lazydocker. Cả hai đều là lựa chọn tuyệt vời, và lazydocker phù hợp hơn với hầu hết mọi người. Tuy nhiên, nếu bạn thường xuyên sử dụng phím tắt, hãy thử lazydocker.


Tuy nhiên, sẽ có những lúc bạn cần thực thi trực tiếp các lệnh Docker. Với shell completion, việc chọn tùy chọn không chỉ đơn giản mà còn dễ dàng chọn tên container—hãy nhập lệnh Docker và nhấn phím Tab khi bạn muốn nó cung cấp danh sách các tùy chọn để lựa chọn.

Bí danh (aliases) là một lựa chọn tuyệt vời khác, cả bên trong và bên ngoài container. Bạn có thể ánh xạ bí danh Docker trên máy chủ hoặc ánh xạ bí danh dịch vụ bên trong container. Bí danh giúp rút ngắn các lệnh Docker; bí danh giúp thực thi nội dung của container một cách biểu cảm hơn.

Docker không hề đơn giản như mọi người nghĩ. Nó rất tuyệt vời trong việc đóng gói một công cụ hoặc dịch vụ, nhưng cũng đi kèm với những cạm bẫy và đôi khi khó sử dụng. Khi tôi mới bắt đầu sử dụng Docker, những mẹo này chưa có sẵn ngay lập tức, vì vậy tôi sẽ chia sẻ chúng cho bạn. Về cơ bản:

    Sử dụng Docker Compose cho các cấu hình phức tạp.
    Đảm bảo bạn chạy các tiến trình với tư cách là người dùng bị giới hạn.
    Sử dụng các công cụ để quản trị Docker dễ dàng hơn.