Ostatnio jeden z czytelników bloga zadał mi pytanie: Co powinienem użyć w moim Dockerfile? ENTRYPOINT czy CMD? Postanowiłem, że odpowiem na to pytanie w formie artykułu – tak byś i Ty mógł/mogła z tego skorzystać.

Post dołącza do serii A vs B, gdzie w jednym z poprzednich artykułów omawialiśmy różnicę pomiędzy ADD i COPY. Link do tego artykułu znajdziesz TUTAJ.

Jak sam tytuł wskazuje, dzisiaj przyjrzymy się bardziej szczegółowo instrukcjom ENTRYPOINT oraz CMD.

Wprowadzenie

Kiedy uruchamiamy kontener, uruchamiamy go na podstawie obrazu. Dzięki temu jesteśmy w stanie przenosić zachowanie kontenerów pomiędzy środowiskami. To, co znajdzie się wewnątrz obrazu oraz jak będzie się zachowywać kontener po uruchomieniu, określamy w pliku Dockerfile. Plik Dockerfile to zbiór instrukcji, gdzie zwykle każda z instrukcji dodaje kolejną warstwę do finalnego obrazu.

Z reguły w pierwszej warstwie obrazu znajdują się pliki systemowe. Kolejne polecenia takie jak RUN, COPY, ADD tworzą następne warstwy, by finalnie stworzyć kompletny obraz naszej aplikacji.

Przykładowo — nasz obraz może być zbudowany na podstawie Ubuntu (polecenie FROM). W kolejnej warstwie instalujemy dodatkowe pakiety, które są niezbędne do działa naszej aplikacji. Następnie kopiujemy artefakty aplikacji i wskazujemy proces startowy kontenera za pomocą polecenia CMD.

TL;DR;

A czy możemy użyc tutaj ENTRYPOINT zamiast CMD?

Odpowiedź brzmi: TAK, możemy. Nawet powinniśmy!

FROM ubuntu:18.04
RUN apt update && apt install python3
COPY my_script.py /
CMD ["python", "my_script.py"]

Instrukcje ENTRYPOINT oraz CMD pozwalają określić punkt startowy dla kontenera, ale istnieje znaczna różnica pomiędzy nimi. Może zatem pojawić się dylemat, którą instrukcję wybrać albo która instrukcja jest rekomendowana.


Shell vs Exec

Zarówno instrukcja CMD, jak i ENTRYPOINT mogą występować w dwóch formach. Shell oraz Exec.

Exec

Składnia formy Exec (rekomendowanej) jest następująca:

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

FROM ubuntu:18.04
CMD ["/bin/ping", "localhost"]

Gdy polecenie jest wykonywane, następuje odwołanie bezpośrednio do pliku wykonywalnego. W tym przypadku jest to /bin/ping localhost.

Shell

Składnia formy Shell jest następująca:

<instruction> <command>

FROM ubuntu:18.04
CMD ping localhost

Podczas wykonywania instrukcji zbudowanej w formie Shell, zamiast bezpośredniego odwołania do pliku wykonywalnego, wywoływane jest procesowanie przez shell:
/bin/sh -c 'ping localhost'

Abyś mógł lepiej zrozumieć różnicę, uruchomiłem dwa kontenery i nadałem im nazwy odpowiadające formom Shell i Exec. Całość wygląda następująco:

CONTAINER ID        COMMAND                    STATUS              NAMES
dac330dd8ff6        "/bin/sh -c 'ping lo…"     Up 30 seconds       shell-form
b83174ca6fb0        "/bin/ping localhost"      Up 40 seconds       exec-form

Jeżeli chcesz poznać najlepsze praktyki tworzenia Dockerfile, zachęcam do lektury
„10 Najlepszych Praktyk Tworzenia Dockerfile”


CMD

Instrukcja CMD pozwala na określenie domyślnego polecenia, które zostanie wykonane tylko podczas uruchamiania kontenera bez podawania dodatkowych argumentów. Jeżeli uruchomisz kontener z dodatkowym argumentem, domyślne polecenie zostanie zignorowane. Jeżeli Twój Dockerfile posiada więcej niż jedną instrukcję CMD, tylko ostatnia jest brana pod uwagę. Pozostałe są ignorowane.

CMD może występować w trzech formach:

  • CMD ["executable","param1","param2"] (forma Exec, rekomendowana)
  • CMD ["param1","param2"] (pozwala na przekazanie dodatkowych parametrów do instrukcji ENTRYPOINT)
  • CMD command param1 param2 (forma Shell)

Załóżmy następujący Dockerfile:

FROM ubuntu:18.04
CMD echo "Hello world"

Gdy uruchomimy kontener bez przekazywania dodatkowych argumentów, za pomocą polecenia:

docker run -it <image> otrzymamy następujący rezultat:

Hello world

Jeżeli natomiast dodamy dodatkowy argument /bin/bash i uruchomimy kontener za pomocą polecenia docker run -it <image> /bin/bash, polecenie echo „Hello world” zostaje zignorowane i uruchamiany jest terminal.

root@8cb0763a478d:/#


ENTRYPOINT

Instrukcja ENTRYPOINT również służy do określenia głównego procesu kontenera. Wygląda bardzo podobnie do CMD, ponieważ pozwala na określenie polecenia wraz z parametrami.
Istnieje jednak znacząca różnica w działaniu ENTRYPOINT. Mianowicie, po uruchomieniu kontenera z dodatkowymi parametrami, domyślne polecenie NIE jest pomijane, jak w przypadku CMD.

ENTRYPOINT występuje w dwóch formach:

  • ENTRYPOINT ["executable", "param1", "param2"] (forma Exec)
  • ENTRYPOINT command param1 param2 (forma Shell)

Exec

Exec jest rekomendowanym podejściem. Dlaczego tak jest?

Forma exec instrukcji ENTRYPOINT pozwala na zdefiniowanie dodatkowych parametrów polecenia, które w zależności od potrzeb mogą ulec zmianie. Odbywa się to za pomocą komendy CMD.


Jak to wygląda? Argumenty zawarte w instrukcji ENTRYPOINT zawsze się wykonają. Z kolei argumenty instrukcji CMD są opcjonalne i mogą zostać nadpisane podczas uruchamiania kontenera.

ENTRYPOINT ["/bin/echo", "Hej"]
CMD ["Damian"]

Gdy uruchomimy kontener bez przekazywania dodatkowych argumentów, za pomocą polecenia:

docker run -it <image> otrzymamy następujący rezultat:

Hej Damian

Jeżeli natomiast dodamy dodatkowy argument ‚Filip’ i uruchomimy kontener za pomocą polecenia docker run -it <image> Filip, otrzymamy:

Hej Filip

Shell

Postać Shell instrukcji ENTRYPOINT nie bierze pod uwagę argumentów przekazanych przez CMD.

Omówmy sobie to na tym samym przykładzie:

ENTRYPOINT echo Hej
CMD Damian

Gdy uruchomimy kontener bez przekazywania dodatkowych argumentów, za pomocą polecenia:

docker run -it <image> otrzymamy następujący rezultat:

Hej

Dodając argument ‚Filip’ do polecenia docker run -it <image> Filip, otrzymamy nadal to samo:

Hej

Podsumowanie

Jeżeli masz dylemat czy użyć CMD, czy ENTRYPOINT jako punkt startowy twojego kontenera, odpowiedz sobie na następujące pytanie.

Czy zawsze moje polecenie MUSI się wykonać?

Jeśli odpowiedź brzmi tak, użyj ENTRYPOINT. Co więcej, jeśli potrzebujesz przekazać dodatkowe parametry, które mogą być nadpisane podczas uruchomienia kontenera — użyj również instrukcji CMD.


Dołącz do newslettera

Już ponad 600 osób zapisało się na mój newsletter (zero spamu! - tylko informacje o nowych artykułach oraz ciekawe nowinki ze świata konteneryzacji).

Zapisując się, jako bonus prześlę Ci "21 Praktycznych Przykładów Użycia Dockera Dla Developerów" oraz zbiór "10 Najlepszych Praktyk Tworzenia Dockerfile"



Damian Naprawa

Praktykujący pasjonat konteneryzacji. Lubi dzielić się wiedzą, prowadząc warsztaty i szkolenia. Uczestnik globalnego programu partnerskiego Docker Enablement. Pracuje z Dockerem na co dzień od kilku lat. Odpowiedzialny za tworzenie i utrzymanie systemów działających w oparciu o kontenery. Fan automatyzacji oraz podejścia "As a Code".

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *