Docker RUN vs CMD vs ENTRYPOINT

Tham chiếu bài gốc tại: https://goinbigdata.com/docker-run-vs-cmd-vs-entrypoint/
Hiểu ngắn gọn là:

  • RUN thực thi (các) lệnh trong một layer mới và tạo một image mới. Ví dụ: nó thường được sử dụng để cài đặt các gói phần mềm.
  • CMD đặt lệnh và/hoặc là đặt các tham số mặc định trong dockerfile, lệnh hoặc các tham số mặc định này có thể được ghi đè từ dòng lệnh khi docker container chạy.
  • ENTRYPOINT cấu hình một container sẽ chạy như một executable.

Tóm tắt thì là như vậy, còn để hiểu sau thì mời mọi người cùng đọc phần bên dưới, với một vài ví dụ khá dễ hiểu 😃)

Docker images and layers

Khi Docker chạy một container, nó chạy một image bên trong nó. Image này thường được xây dựng bằng cách thực hiện các hướng dẫn (instruction) trong dockerfile, nó sẽ thêm các layer mới trên một base image đã tồn tại tạo ra một image mới.

Shell and Exec forms

Cả ba lệnh (RUN, CMD và ENTRYPOINT) có thể được chỉ định ở dạng shell form hoặc dạng exec form. Trước tiên ta cùng làm quen với các biểu mẫu này:

Shell form
<instruction> <command>

Ex:

RUN apt-get install python3
CMD echo "Hello world"
ENTRYPOINT echo "Hello world"

Khi instruction được thực thi trong shell form nó sẽ gọi xử lý /bin/sh -c <command> bên dưới và xử lý shell xảy ra. Ví dụ một đoạn mẫu trong dockerfile như sau

ENV name John Dow
ENTRYPOINT echo "Hello, $name" # executed shell

khi run docker run -it < image > sẽ cho ra output:

Hello, John Dow    

Biến $name được thay thế bằng giá trị set trong ENV

Exec form

Exec form được ưa thích sử dụng hơn với các hướng dẫn CMD và ENTRYPOINT

<instruction> ["executable", "param1", "param2", ...]

Ex:

RUN ["apt-get", "install", "python3"]
CMD ["/bin/echo", "Hello world"]
ENTRYPOINT ["/bin/echo", "Hello world"]

Khi lệnh thực thi ở dạng exec form, nó gọi thực thi trực tiếp và xử lý shell không xảy ra. Ví dụ: đoạn mã sau trong Dockerfile

ENV name John Dow
ENTRYPOINT ["/bin/echo", "Hello, $name"]

khi container chạy docker run -it < image > sẽ cho output

Hello, $name

Biến $name không được thay thế

How to run bash?

Nếu muốn run bash thì ta sử dụng exec form với /bin/bash như một executable. Khi đó xử lý shell bình thường sẽ được thực thi. Ví dụ

ENV name John Dow
ENTRYPOINT ["/bin/bash", "-c", "echo Hello, $name"]

khi container chạy output sẽ in ra biến $name

Hello, John Dow

RUN

Run cho phép ta cài đặt ứng dụng (các layer cần thiết cho ứng dụng) trên image ban đầu và tạo ra image mới bằng cách commit các layer mới được cài đặt thêm (refer docker commit).
Thường thì ta sẽ thấy nhiều lệnh RUN trong dockerfile. RUN có 2 cấu trúc

- RUN < command > (shell form)
- RUN ["executable", "param1", "param2"]  (exec form)

Một minh họa tốt về hướng dẫn RUN cài đặt nhiều package:

RUN apt-get update && apt-get install -y \
  bzr \
  cvs \
  git \
  mercurial \
  subversion

Note: apt-get update và apt-get install được thực thi trong cùng một lệnh RUN. Điều này đảm bảo rằng các package mới nhất sẽ được cài đặt. Nếu apt-get install được chạy ở một lệnh RUN riêng thì nó sẽ sử dụng lại một layer được thêm bởi lệnh apt-get update, mà có thể đã được tạo ra từ trước đó. Lên lưu ý là hãy sử dụng apt-get update và apt-get install trong cùng một lệnh RUN.

CMD

CMD cho phép ta set default command, có nghĩa là command này sẽ chỉ được chạy khi run container mà không chỉ định một command.
Nếu docker run với một command thì default command sẽ được ignore. Nếu dockerfile có nhiều hơn một lệnh CMD thì tất cả sẽ bị ignore ngoại trừ lệnh CMD cuối cùng.
CMD có 3 dạng form:

- CMD ["executable", "param1", "param2"]   (exec forrm, preferred)
- CMD ["param1", "param2"]  (đặt các tham số mặc định cho ENTRYPOINT ở dạng exec form)
- CMD command param1 param2   (shell form)

Cách thứ 2 được sử dụng cùng với ENTRYPOINT trong exec form. Nó set default parameters được thêm vào ENTRYPOINT nếu container chạy mà không truyền đối số nào, ngược lại nó sẽ bị ignore. Ví dụ sử dụng đoạn mã dưới trong dockerfile

CMD echo "Hello world" 

khi container chạy docker run -it < image > sẽ ra output

Hello world

nhưng khi chạy docker run -it < image > /bin/bash, CMD sẽ bị ignored và bash sẽ được thay thế, output:

[email protected]:/#

ENTRYPOINT

Lệnh ENTRYPOINT cho phép ta cấu hình container sẽ chạy dưới dạng thực thi. Nó tương tự như CMD, vì nó cũng cho phép ta chỉ định một lệnh với các tham số. Sự khác biệt là lệnh ENTRYPOINT và các tham số không bị ignore khi Docker container chạy với các tham số dòng lệnh. (Có một cách để bỏ qua ENTTRYPOINT, mọi người google search nhé)
ENTRYPOINT có 2 dạng form:

- ENTRYPOINT ["executable", "param1", "param2"] (exec form, preferred)
- ENTRYPOINT command param1 param2 (shell form)

Exec form của ENTRYPOINT cho phép ta đặt các lệnh và tham số và sau đó sử dụng một trong hai dạng : một là CMD để đặt các tham số bổ sung (có nhiều khả năng sẽ được thay đổi), hai là các đối số ENTRYPOINT (luôn được sử dụng), trong khi các đối số CMD có thể được ghi đè bằng các tham số dòng lệnh được cung cấp khi Docker container chạy. Ví dụ sử dụng đoạn mã dưới trong dockerfile

ENTRYPOINT ["/bin/echo", "Hello"]
CMD ["world"]

khi chạy docker với lệnh docker run -it < image > sẽ cho output:

Hello world

nhưng khi chạy với lệnh docker run -it < image > John sẽ cho output:

Hello John

đối số dòng lệnh John đã thay thế đối số world được set ở CMD

Shell form của ENTRYPOINT ignore tất CMD và các tahm số dòng lệnh

Tổng kết

RUN được sử dụng để cài đặt các layer, build image mới theo nhu cầu của ứng dụng.
ENTRYPOINT và CMD sử dụng khi ta cần một lệnh luôn được thực thi. Ngoài ra, hãy sử dụng CMD kết hợp với ENTRYPOINT nếu ta cần cung cấp thêm các đối số mặc định có thể được ghi đè từ dòng lệnh khi docker container chạy.
Chọn CMD nếu ta cần cung cấp một lệnh mặc định và / hoặc các tham số có thể được ghi đè từ dòng lệnh khi docker container chạy.

All Rights Reserved