Пошук уроків, статей та іншого контенту
Дві інструкції Dockerfile, що визначають команду запуску контейнера, — і як вони доповнюють одна одну.
CMD і ENTRYPOINTCMD та ENTRYPOINT — інструкції Dockerfile, які визначають, що саме запускається під час старту контейнера.
На перший погляд вони схожі, але мають різне призначення:
CMD задає команду або аргументи за замовчуванням;
ENTRYPOINT визначає основну програму контейнера;
разом вони дають змогу створити контейнер, який запускає фіксовану програму, але приймає змінні аргументи.
Наприклад, контейнер може завжди запускати певний застосунок, а під час запуску отримувати інше ім’я файлу або режим роботи.
CMDCMD задає команду за замовчуванням, яку буде виконано при старті контейнера.
FROM alpine:3.20
CMD ["echo", "Привіт із контейнера"]Якщо запустити образ без додаткових аргументів:
docker build -t greeting .
docker run --rm greetingРезультат:
Привіт із контейнераАле команду, визначену через CMD, легко замінити під час запуску:
docker run --rm greeting echo "Інше повідомлення"Тепер Docker виконає передану команду замість CMD.
CMDCMD зручно використовувати, коли:
контейнер має стандартну команду запуску;
користувач повинен легко її перевизначати;
образ може використовуватися для різних сценаріїв.
У Dockerfile може бути кілька інструкцій CMD, але працюватиме лише остання. Тому зазвичай у файлі визначають одну CMD.
ENTRYPOINTENTRYPOINT задає основну виконувану програму контейнера.
FROM alpine:3.20
ENTRYPOINT ["echo"]Запуск без аргументів:
docker run --rm greetingЗапуск із аргументом:
docker run --rm greeting "Привіт"У другому випадку echo залишається точкою входу, а рядок "Привіт" передається їй як аргумент.
Фактично команда буде еквівалентна:
echo ПривітНа відміну від CMD, аргументи після імені образу не замінюють ENTRYPOINT, а додаються до нього.
CMD і ENTRYPOINT разомНайпоширеніший сценарій — використовувати ENTRYPOINT для програми, а CMD — для аргументів за замовчуванням.
FROM alpine:3.20
ENTRYPOINT ["echo"]
CMD ["Стандартне повідомлення"]Без аргументів Docker запустить:
echo Стандартне повідомленняКоманда:
docker run --rm greetingвиведе:
Стандартне повідомленняЯкщо передати власний аргумент:
docker run --rm greeting "Повідомлення користувача"Docker запустить:
echo Повідомлення користувачаУ цьому випадку:
ENTRYPOINT — echo;
CMD — аргумент "Стандартне повідомлення";
аргумент із docker run замінює CMD.
Це головна відмінність від використання лише CMD: основна програма залишається незмінною.
CMD та ENTRYPOINT підтримують дві форми:
exec form;
shell form.
Exec form записується як JSON-масив:
ENTRYPOINT ["node", "server.js"]
CMD ["--port", "3000"]Це рекомендований варіант для більшості випадків.
Переваги:
аргументи передаються без додаткового розбору оболонкою;
пробіли та спеціальні символи обробляються передбачувано;
процес програми може бути головним процесом контейнера;
сигнали, зокрема SIGTERM, коректніше передаються застосунку.
Для exec form важливо використовувати подвійні лапки, оскільки запис має бути коректним JSON:
CMD ["python", "app.py"]Такий запис не є правильним:
CMD ['python', 'app.py']Shell form виглядає як звичайна команда:
CMD echo "Привіт"Зазвичай така команда запускається через оболонку, наприклад /bin/sh -c.
Shell form може бути зручною для коротких команд або коли потрібні можливості shell:
CMD echo "$MESSAGE"Проте вона має важливі нюанси:
команда запускається через shell;
змінні середовища та оператори shell можуть оброблятися оболонкою;
застосунок може не стати процесом із PID 1;
сигнали можуть передаватися не так, як очікується.
Для довготривалих процесів, вебсерверів і застосунків зазвичай краще використовувати exec form.
Розглянемо Dockerfile:
FROM alpine:3.20
ENTRYPOINT ["printf"]
CMD ["%s\n", "Стандартний текст"]Без додаткових аргументів:
docker run --rm formatterВиконується приблизно така команда:
printf "%s\n" "Стандартний текст"Якщо передати аргументи:
docker run --rm formatter "%s\n" "Новий текст"Docker замінить CMD і сформує:
printf "%s\n" "Новий текст"Загальне правило для exec form:
ENTRYPOINT + CMDА аргументи, передані після імені образу, замінюють CMD:
ENTRYPOINT + аргументи docker runENTRYPOINTАргументи після імені образу не замінюють ENTRYPOINT. Щоб змінити його, потрібно використати параметр --entrypoint:
docker run --rm --entrypoint sh my-imageЦе запустить sh замість програми, визначеної в ENTRYPOINT.
Такий підхід часто використовують для діагностики контейнера:
docker run --rm -it --entrypoint sh my-imageПісля запуску можна перевірити файли, змінні середовища або доступність команд усередині образу.
Припустімо, образ має запускати Python-застосунок. Основний файл завжди один, але порт можна змінювати:
FROM python:3.12-slim
WORKDIR /app
COPY app.py .
ENTRYPOINT ["python", "app.py"]
CMD ["--port", "8000"]Стандартний запуск:
docker run --rm my-python-appЕквівалентна команда:
python app.py --port 8000Запуск на іншому порту:
docker run --rm my-python-app --port 9000Еквівалент:
python app.py --port 9000У цьому прикладі користувач може змінювати параметри застосунку, але не змінює саму програму запуску.
CMD у shell formКомбінація shell form може поводитися не так, як очікується.
Наприклад:
ENTRYPOINT ["echo"]
CMD helloТут CMD hello має shell form. Docker не обов’язково передасть hello як звичайний аргумент до exec-form ENTRYPOINT так, як це відбувається для JSON-масиву. Змішування форм ускладнює передбачення фактичної команди.
Тому для комбінації ENTRYPOINT і CMD краще використовувати exec form в обох інструкціях:
ENTRYPOINT ["my-app"]
CMD ["--config", "/app/config.yml"]Перший процес у контейнері має PID 1. Саме він відповідає за обробку сигналів і завершення процесів.
Якщо застосунок запускається безпосередньо через exec form:
ENTRYPOINT ["./server"]він зазвичай є головним процесом контейнера та може коректно обробити сигнал завершення.
Якщо ж запуск відбувається через shell:
ENTRYPOINT ./serverміж Docker і застосунком може з’явитися оболонка. Вона не завжди передає сигнали дочірньому процесу так, як потрібно. Це може призвести до затримок під час зупинки контейнера або некоректного завершення застосунку.
Для production-контейнерів рекомендовано:
використовувати exec form;
не запускати основний процес через зайвий shell без потреби;
переконатися, що застосунок коректно обробляє сигнали завершення.
CMDВикористовуйте CMD, якщо:
команда є лише значенням за замовчуванням;
користувач повинен повністю замінювати команду;
образ призначений для різних завдань;
потрібно передати стандартні параметри програмі.
Приклад:
FROM alpine:3.20
CMD ["sh"]Такий образ за замовчуванням запускає shell, але користувач може виконати будь-яку іншу команду:
docker run --rm alpine-image ls -laENTRYPOINTВикористовуйте ENTRYPOINT, якщо:
контейнер має виконувати конкретну програму;
образ працює як виконуваний інструмент;
основна команда не повинна випадково замінюватися;
аргументи користувача потрібно передавати цій програмі.
Приклад:
FROM alpine:3.20
ENTRYPOINT ["grep"]Тепер контейнер поводиться подібно до команди grep:
docker run --rm grep-image "error" /logs/app.logУсі аргументи передаються до grep.
ENTRYPOINT npm startТакий запис запускає команду через shell. Для основного процесу краще використовувати форму, яка безпосередньо запускає програму:
ENTRYPOINT ["npm", "start"]ENTRYPOINT звичайними аргументамиКоманда:
docker run --rm my-image shне замінює ENTRYPOINT. Вона передає sh як аргумент до наявної точки входу.
Для повної заміни використовуйте:
docker run --rm --entrypoint sh my-imageНе варто без потреби писати:
ENTRYPOINT ["python app.py --port 8000"]Docker спробує знайти виконуваний файл із назвою python app.py --port 8000.
Правильний варіант:
ENTRYPOINT ["python", "app.py"]
CMD ["--port", "8000"]Exec form використовує JSON-синтаксис, тому потрібні подвійні лапки:
CMD ["node", "server.js"]CMDУ Dockerfile може бути кілька інструкцій CMD, але активною буде лише остання. Попередні значення не об’єднуються.
Погано:
CMD ["echo", "Перший запуск"]
CMD ["echo", "Другий запуск"]У результаті спрацює лише друга інструкція.
Якщо параметр уже визначено в ENTRYPOINT, не варто без потреби повторювати його в CMD:
ENTRYPOINT ["my-app", "--config", "/app/config.yml"]
CMD ["--config", "/app/config.yml"]Це може призвести до дублювання аргументів. Краще розділити постійні параметри та параметри за замовчуванням:
ENTRYPOINT ["my-app", "--config", "/app/config.yml"]
CMD ["--verbose"]Можна користуватися простим правилом:
потрібна команда за замовчуванням, яку легко замінити — використовуйте CMD;
потрібна фіксована програма з аргументами користувача — використовуйте ENTRYPOINT;
потрібна фіксована програма зі стандартними аргументами — використовуйте ENTRYPOINT разом із CMD;
для production-застосунків переважно використовуйте exec form.
Типова структура:
FROM base-image
WORKDIR /app
COPY . .
ENTRYPOINT ["my-application"]
CMD ["--environment", "production"]Така конфігурація означає:
my-application є основною програмою;
--environment production — параметри за замовчуванням;
аргументи docker run можуть замінити ці параметри;
--entrypoint може повністю замінити основну програму для діагностики.
CMD і ENTRYPOINT обидві впливають на запуск контейнера, але виконують різні ролі:
CMD задає команду або аргументи за замовчуванням;
ENTRYPOINT задає основну програму контейнера;
аргументи після імені образу замінюють CMD;
параметр --entrypoint замінює ENTRYPOINT;
поєднання ENTRYPOINT і CMD підходить для фіксованої програми зі змінними параметрами;
exec form зазвичай безпечніша та передбачуваніша для основних процесів контейнера.
У більшості прикладних образів доречно почати з такого шаблону:
ENTRYPOINT ["my-app"]
CMD ["--port", "8000"]Він чітко розділяє основну програму та її параметри за замовчуванням.