Roadmap Docker: Chạy Container với docker run – Hướng dẫn cho người mới bắt đầu

Chào mừng các bạn đã quay trở lại với series “Roadmap Docker”! Sau khi đã cùng nhau tìm hiểu Container là gì, phân biệt Container với VM và Bare Metal, hiểu về Docker và tiêu chuẩn OCI, cũng như trang bị các kiến thức nền tảng Linux, quản lý package, người dùng và quyền hạn, các lệnh shell, và shell scripting, giờ là lúc chúng ta chuyển sang bước tiếp theo cực kỳ quan trọng: làm thế nào để thực sự khởi chạy một container?

Nếu coi Docker Image là một bản thiết kế (blueprint) cho ứng dụng và môi trường chạy của nó, thì câu lệnh docker run chính là “nút bấm” để biến bản thiết kế đó thành hiện thực – một container đang hoạt động. Đây là lệnh cốt lõi, là điểm khởi đầu cho hầu hết các tương tác của bạn với Docker. Nắm vững docker run với các tùy chọn phổ biến sẽ mở ra cánh cửa để bạn có thể chạy hầu hết mọi ứng dụng đóng gói dưới dạng container.

Trong bài viết này, chúng ta sẽ đi sâu vào cú pháp của docker run và khám phá những tùy chọn quan trọng nhất mà một kỹ sư DevOps (đặc biệt là các bạn mới) cần phải biết để có thể chạy và quản lý container hiệu quả. Chúng ta sẽ bắt đầu từ những ví dụ đơn giản nhất và dần dần tìm hiểu cách kiểm soát container về mạng, lưu trữ, tài nguyên và hành vi.

Cú pháp Cơ Bản của docker run

Cú pháp tổng quát của lệnh docker run có thể trông khá dài dòng với nhiều tùy chọn, nhưng về cơ bản, nó tuân theo cấu trúc sau:

docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
  • docker run: Lệnh chính để tạo và chạy một container mới.
  • [OPTIONS]: Đây là nơi bạn chỉ định các tùy chọn cấu hình cho container, ví dụ: tên container, cổng mạng, volume dữ liệu, biến môi trường, v.v. Các tùy chọn này làm cho docker run trở nên cực kỳ linh hoạt.
  • IMAGE: Tên của Docker Image mà bạn muốn sử dụng để tạo container. Docker sẽ tìm image này trên máy cục bộ. Nếu không tìm thấy, nó sẽ cố gắng tải (pull) image từ Docker Hub hoặc một registry khác mà bạn cấu hình. Hiểu rõ cách Docker caching hoạt động và cách sử dụng image an toàn là rất quan trọng ở đây.
  • [COMMAND]: Lệnh mặc định được định nghĩa trong Dockerfile của image sẽ được chạy khi container khởi động. Bạn có thể ghi đè lệnh mặc định này bằng cách cung cấp một lệnh mới ở đây.
  • [ARG...]: Các đối số cho lệnh COMMAND.

Chạy Container Đầu Tiên: Hello World

Ví dụ đơn giản nhất để bắt đầu là chạy image hello-world. Image này được thiết kế chỉ để in ra một thông báo và sau đó thoát.

docker run hello-world

Khi bạn chạy lệnh này lần đầu tiên, Docker sẽ:

  1. Kiểm tra xem image hello-world có tồn tại trên máy cục bộ không.
  2. Nếu không, nó sẽ tải (pull) image này từ Docker Hub (registry mặc định).
  3. Tạo một container mới dựa trên image đó.
  4. Chạy lệnh được chỉ định trong image (ở đây là một script đơn giản in ra thông báo).
  5. Container sẽ dừng lại sau khi lệnh hoàn thành.

Kết quả bạn thấy trên màn hình sẽ là thông báo từ container, giải thích rằng Docker đã hoạt động thành công.

Những Tùy Chọn docker run Quan Trọng Nhất Cho Người Mới Bắt Đầu

Bây giờ, hãy cùng khám phá các tùy chọn phổ biến và hữu ích nhất khi sử dụng docker run.

Chạy Container ở Chế độ Nền (-d hoặc –detach)

Khi bạn chạy container như ví dụ hello-world, nó chạy ở chế độ foreground, tức là nó chiếm giữ terminal của bạn cho đến khi container dừng. Điều này không phù hợp với các ứng dụng dịch vụ như web server, database, cần chạy liên tục. Tùy chọn -d (hoặc --detach) sẽ giúp bạn chạy container ở chế độ nền (detached mode).

docker run -d nginx

Lệnh này sẽ tải image nginx (nếu chưa có), tạo và chạy một container dựa trên image đó ở chế độ nền. Docker sẽ in ra ID đầy đủ của container vừa được khởi tạo.

Lúc này, bạn có thể sử dụng lệnh docker ps để xem danh sách các container đang chạy. Bạn sẽ thấy container nginx của mình trong danh sách đó với trạng thái “Up”.

Tương Tác Trực Tiếp với Container (-it hoặc –interactive –tty)

Đôi khi bạn muốn “bước vào” bên trong container để chạy các lệnh, kiểm tra file hệ thống, hoặc debug. Tùy chọn -it là sự kết hợp của -i (interactive – giữ STDIN mở) và -t (pseudo-TTY – phân bổ một terminal giả). Kết hợp hai tùy chọn này cho phép bạn tương tác với container như thể bạn đang kết nối SSH vào một máy chủ Linux nhỏ.

docker run -it ubuntu bash

Lệnh này sẽ chạy một container từ image ubuntu và mở shell bash trong container đó. Bạn sẽ thấy prompt của shell bên trong container. Bây giờ bạn có thể chạy các lệnh Linux thông thường bên trong môi trường cô lập của container. Việc này đòi hỏi bạn có kiến thức cơ bản về Linuxcác lệnh shell cần thiết.

Để thoát khỏi shell và dừng container, bạn có thể gõ exit hoặc nhấn Ctrl+D. Nếu chỉ nhấn Ctrl+C, nó có thể chỉ dừng lệnh hiện tại chứ chưa chắc đã thoát khỏi container.

Đặt Tên cho Container (–name)

Docker tự động gán cho mỗi container một ID duy nhất (ví dụ: a3f1c2d4e5f6...) và một tên ngẫu nhiên dễ đọc (ví dụ: affectionate_hopper). Tuy nhiên, việc ghi nhớ ID hay tên ngẫu nhiên này là khó khăn khi bạn có nhiều container. Tùy chọn --name cho phép bạn đặt một tên dễ nhớ cho container của mình.

docker run --name my_custom_nginx -d nginx

Sau khi chạy lệnh này, bạn có thể sử dụng docker ps và sẽ thấy container nginx của bạn được liệt kê với tên my_custom_nginx thay vì tên ngẫu nhiên. Việc sử dụng tên giúp bạn dễ dàng tham chiếu container trong các lệnh Docker khác (ví dụ: docker stop my_custom_nginx, docker logs my_custom_nginx).

Lưu ý: Tên container phải là duy nhất trên cùng một host Docker. Nếu bạn cố gắng chạy một container mới với tên đã tồn tại, lệnh sẽ báo lỗi trừ khi bạn xóa container cũ hoặc sử dụng tùy chọn --rm (sẽ nói sau).

Mở Cổng Ra Thế Giới Bên Ngoài (-p hoặc –publish)

Các ứng dụng web server, API server, hoặc bất kỳ dịch vụ mạng nào chạy bên trong container đều cần giao tiếp với bên ngoài. Mặc định, các cổng (ports) bên trong container không thể truy cập được từ host hoặc mạng bên ngoài. Tùy chọn -p (hoặc --publish) được sử dụng để ánh xạ (map) một cổng trên host Docker tới một cổng bên trong container.

docker run -d -p 8080:80 nginx

Lệnh này:

  • Chạy container nginx ở chế độ nền (-d).
  • Ánh xạ cổng 8080 trên host (máy bạn đang chạy Docker) tới cổng 80 bên trong container nginx.

Nginx mặc định lắng nghe trên cổng 80 bên trong container. Với lệnh này, bạn có thể truy cập trang web mặc định của Nginx từ trình duyệt trên máy host bằng cách truy cập địa chỉ http://localhost:8080.

Cú pháp của -phost_port:container_port. Bạn cũng có thể chỉ định địa chỉ IP cụ thể để lắng nghe, ví dụ: -p 192.168.1.100:8080:80. Bạn có thể sử dụng nhiều tùy chọn -p để ánh xạ nhiều cổng.

Việc hiểu cách ánh xạ cổng là nền tảng khi làm việc với ứng dụng web container hóa.

Lưu Trữ Dữ Liệu Bền Vững (-v hoặc –volume)

Mặc định, hệ thống file bên trong container là tạm thời. Khi container bị xóa, mọi dữ liệu được tạo hoặc sửa đổi bên trong nó (ngoại trừ các lớp (layers) của image gốc) cũng sẽ bị mất. Điều này không phù hợp với các ứng dụng cần lưu trữ dữ liệu lâu dài như database, hoặc khi bạn muốn chia sẻ file cấu hình/mã nguồn từ host vào container.

Tùy chọn -v (hoặc --volume) cho phép bạn gắn kết (mount) các volume hoặc bind mount vào container. Đây là một chủ đề quan trọng và chi tiết đã được đề cập trong các bài viết trước: Lưu trữ Dữ liệu Bền vững trong DockerVolume Mounts và Bind Mounts.

Ví dụ Bind Mount (chia sẻ thư mục từ host):

docker run -d -p 8080:80 -v /path/to/your/nginx/conf:/etc/nginx/conf.d nginx

Lệnh này gắn kết thư mục cấu hình Nginx trên host vào thư mục cấu hình bên trong container. Bất kỳ thay đổi nào bạn thực hiện trong thư mục trên host sẽ ngay lập tức phản ánh trong container (và ngược lại).

Ví dụ Named Volume (volume được Docker quản lý):

docker volume create my_nginx_data
docker run -d -p 8080:80 -v my_nginx_data:/usr/share/nginx/html nginx

Đầu tiên, chúng ta tạo một named volume tên là my_nginx_data. Sau đó, chúng ta chạy container Nginx và gắn kết volume này vào thư mục chứa các file HTML mặc định của Nginx bên trong container. Dữ liệu lưu trong my_nginx_data sẽ tồn tại ngay cả khi container bị xóa và có thể được tái sử dụng bởi các container khác.

Sử dụng volume là cách tiêu chuẩn để chạy cơ sở dữ liệu trong Docker một cách an toàn và bền vững.

Tự Động Xóa Container Khi Dừng (–rm)

Khi một container dừng (hoàn thành tác vụ hoặc bị dừng thủ công), nó vẫn tồn tại trên hệ thống file của Docker (ở trạng thái Exited) cho đến khi bạn xóa nó bằng lệnh docker rm. Điều này hữu ích để kiểm tra log hoặc khởi động lại container. Tuy nhiên, đối với các container chỉ chạy một tác vụ ngắn hạn và không cần giữ lại (ví dụ: chạy một script, một bài test), việc tích tụ các container “chết” có thể gây lãng phí không gian đĩa.

Tùy chọn --rm sẽ tự động xóa container ngay sau khi nó dừng.

docker run --rm ubuntu echo "Công việc đã hoàn thành và container sẽ bị xóa."

Container này sẽ chạy lệnh echo trong image ubuntu, in ra thông báo, sau đó dừng lại và tự động bị xóa khỏi hệ thống Docker của bạn.

Truyền Biến Môi Trường (-e hoặc –env)

Nhiều ứng dụng hiện đại sử dụng biến môi trường (environment variables) để nhận cấu hình, ví dụ: thông tin kết nối database, API keys, chế độ debug, v.v. Tùy chọn -e (hoặc --env) cho phép bạn truyền các biến môi trường từ host vào container.

docker run -d -e MY_CONFIG_VAR=some_value -e ANOTHER_VAR=another_value my_app_image

Bên trong container chạy từ my_app_image, ứng dụng của bạn có thể đọc giá trị của MY_CONFIG_VARANOTHER_VAR từ môi trường của nó. Kỹ thuật này rất phổ biến khi chạy cơ sở dữ liệu (ví dụ: đặt mật khẩu root cho MySQL/PostgreSQL) hoặc cấu hình các dịch vụ khác.

Bạn có thể sử dụng nhiều tùy chọn -e cho mỗi biến môi trường cần truyền.

Cấu Hình Mạng (–network)

Docker cung cấp các cơ chế mạng mạnh mẽ để các container có thể giao tiếp với nhau và với thế giới bên ngoài. Mặc định, container được kết nối vào một mạng bridge mặc định, cho phép chúng truy cập Internet và giao tiếp với nhau thông qua IP nội bộ (mặc dù sử dụng Docker Compose hoặc user-defined networks được khuyến khích hơn cho giao tiếp giữa các container). Tùy chọn --network cho phép bạn chỉ định mạng mà container sẽ tham gia.

docker run -d --network my_custom_network my_app_image

Nếu bạn chỉ định một mạng chưa tồn tại, Docker sẽ báo lỗi. Bạn cần tạo mạng tùy chỉnh trước đó bằng docker network create. Sử dụng mạng tùy chỉnh giúp cô lập các nhóm container, cải thiện khả năng quản lý và độ phân giải tên (containers trong cùng mạng có thể truy cập nhau bằng tên container).

Một giá trị đặc biệt phổ biến cho --networkhost. Khi sử dụng --network host, container sẽ chia sẻ ngăn xếp mạng của host. Điều này có nghĩa là container sẽ sử dụng cùng địa chỉ IP và các cổng mạng của host. Ví dụ, nếu một ứng dụng trong container lắng nghe trên cổng 80, nó sẽ có thể truy cập được trực tiếp qua cổng 80 của host mà không cần ánh xạ cổng với -p. Tuy nhiên, tùy chọn này phá vỡ một phần sự cô lập của container và cần được sử dụng cẩn thận.

docker run -d --network host my_network_app_image

Kết Hợp Các Tùy Chọn

Trong thực tế, bạn sẽ thường kết hợp nhiều tùy chọn docker run để cấu hình container theo ý muốn. Ví dụ, để chạy một container web server Nginx ở chế độ nền, đặt tên là production_web, ánh xạ cổng 8080 trên host tới cổng 80 trong container, và gắn kết một volume dữ liệu cho nội dung website:

docker run --name production_web -d -p 8080:80 -v my_website_data:/usr/share/nginx/html nginx

Đây là một ví dụ điển hình cho thấy sức mạnh và tính linh hoạt của lệnh docker run.

Hiểu về Quá Trình Chạy của Container

Khi bạn thực thi lệnh docker run, Docker Engine thực hiện các bước sau:

  1. Tìm và Tải Image: Docker kiểm tra xem image được chỉ định có sẵn trên máy cục bộ không. Nếu không, nó sẽ tìm kiếm image trên registry mặc định (Docker Hub) hoặc registry được cấu hình và tải về (pull) image cùng với tất cả các image base mà nó phụ thuộc. Điều này liên quan đến cách Docker sử dụng UnionFS để quản lý các lớp image.
  2. Tạo Container: Dựa trên image và các tùy chọn bạn cung cấp (tên, mạng, volume, cổng, biến môi trường, v.v.), Docker sẽ tạo một lớp container có thể ghi (writable layer) trên đỉnh các lớp chỉ đọc (read-only layers) của image. Docker cũng cấu hình các Namespaces và cgroups để cô lập container về tài nguyên và môi trường.
  3. Khởi động Container: Docker khởi động container, thiết lập môi trường (gắn kết volume, cấu hình mạng, đặt biến môi trường).
  4. Chạy Lệnh Chính: Docker thực thi lệnh được chỉ định (hoặc lệnh mặc định trong Dockerfile) bên trong container. Đây là tiến trình chính của container (PID 1). Nếu tiến trình này kết thúc, container sẽ dừng lại (trừ khi được cấu hình để tự khởi động lại).

Bảng dưới đây tóm tắt các tùy chọn docker run quan trọng đã được đề cập:

Bảng Tóm Tắt Các Tùy Chọn Quan Trọng của docker run

Tùy Chọn Viết tắt Mô tả Mục đích Ví dụ
--detach -d Chạy container ở chế độ nền (background). Dùng cho các dịch vụ cần chạy liên tục (web server, database). docker run -d nginx
--interactive -i Giữ STDIN mở ngay cả khi không gắn liền. Dùng để tương tác trực tiếp với shell hoặc ứng dụng bên trong container. docker run -it ubuntu bash
--tty -t Phân bổ một pseudo-TTY.
--name <name> Đặt tên cho container. Giúp dễ dàng tham chiếu và quản lý container. docker run --name my_web image
--publish <host_port>:<container_port> -p Ánh xạ cổng host đến cổng container. Cho phép truy cập dịch vụ trong container từ host hoặc mạng ngoài. docker run -p 8080:80 nginx
--volume <source>:<destination> -v Gắn kết volume hoặc bind mount. Lưu trữ dữ liệu bền vững, chia sẻ file giữa host và container. docker run -v my_volume:/data image
docker run -v /host/path:/container/path image
--rm Tự động xóa container khi nó dừng. Dùng cho các container chạy tác vụ ngắn hạn để giải phóng tài nguyên. docker run --rm ubuntu echo "Done"
--env <key>=<value> -e Đặt biến môi trường trong container. Cấu hình ứng dụng bên trong container mà không cần sửa image. docker run -e DB_HOST=mydb image
--network <network_name> Kết nối container vào một mạng cụ thể. Kiểm soát cách container giao tiếp với các container khác và mạng bên ngoài. docker run --network my_app_net image

Lời Kết

Lệnh docker run là xương sống của việc làm việc với Docker. Nó cho phép bạn đưa các image tĩnh vào cuộc sống dưới dạng các container đang chạy, với khả năng kiểm soát mạnh mẽ về môi trường, mạng, lưu trữ và hành vi. Việc nắm vững các tùy chọn cơ bản như -d, -it, --name, -p, -v, --rm, và -e là bước đệm vững chắc để bạn có thể triển khai hầu hết các loại ứng dụng với Docker.

Hãy dành thời gian thực hành với các lệnh ví dụ trong bài viết này trên máy Docker của bạn. Chạy thử các image khác nhau (như alpine, python, node), thử nghiệm các tùy chọn kết hợp. Càng thực hành, bạn càng làm quen và hiểu sâu hơn về cách Docker hoạt động.

Trong các bài viết tiếp theo của series “Roadmap Docker”, chúng ta sẽ tiếp tục khám phá các lệnh Docker khác để quản lý container (dừng, khởi động lại, xóa), quản lý image, mạng, volume, và cuối cùng là ghép nối nhiều container lại với nhau để chạy các ứng dụng phức tạp hơn. Việc viết Dockerfile tốt hơn cũng là một phần quan trọng để tạo ra các image hiệu quả mà docker run sử dụng.

Nếu bạn có bất kỳ câu hỏi nào hoặc gặp khó khăn trong quá trình thực hành, đừng ngần ngại để lại bình luận nhé. Hẹn gặp lại trong các bài viết tiếp theo!

Chỉ mục