В чому різниця між CMD та ENTRYPOINT?
Обидві інструкції визначають, що буде запущено при старті контейнера, але у них різні задачі:
🟢 Junior Level
Просте пояснення
Обидві інструкції визначають, що буде запущено при старті контейнера, але у них різні задачі:
- ENTRYPOINT — це основна програма контейнера (наприклад,
java) - CMD — це аргументи за замовчуванням для цієї програми (наприклад,
-jar app.jar)
RUN – виконується при ЗБІРЦІ образу. CMD/ENTRYPOINT – при ЗАПУСКУ контейнера.
Аналогія
Уявіть виклик функції в програмуванні:
ENTRYPOINT— це ім’я функціїCMD— це параметри за замовчуванням
java -jar app.jar
^^^^^ ^^^^^^^^^^^^
| |
ENTRYPOINT CMD
Як вони працюють разом
ENTRYPOINT ["java", "-jar", "/app.jar"]
CMD ["--server.port=8080"]
При запуску docker run myapp виконається: java -jar /app.jar --server.port=8080
При запуску docker run myapp --server.port=9090 виконається: java -jar /app.jar --server.port=9090
Дві форми запису
Exec form (рекомендується) — у вигляді JSON-масиву:
ENTRYPOINT ["java", "-jar", "/app.jar"]
Shell form — як звичайний рядок:
ENTRYPOINT java -jar /app.jar
Завжди використовуйте Exec form — вона правильно обробляє сигнали завершення.
Що запам’ятати
ENTRYPOINT— фіксована команда,CMD— параметри за замовчуванням- Їх можна використовувати разом
CMDлегко перевизначити при запуску,ENTRYPOINT— ні- Завжди використовуйте Exec form
["cmd", "arg1", "arg2"] - В Dockerfile може бути лише одна
CMDі однаENTRYPOINT
🟡 Middle Level
Детальна поведінка
ENTRYPOINT (Точка входу)
Визначає основну виконувану програму контейнера:
- Стабільність: її складно перевизначити випадково (потрібен прапорець
--entrypoint) - Призначення: перетворює контейнер на «виконуваний бінарник»
- Аргументи: усі аргументи, передані в
docker run, дописуються в кінець командиENTRYPOINT
CMD (Команда за замовчуванням)
Визначає команду за замовчуванням або параметри для ENTRYPOINT:
- Гнучкість: дуже легко перевизначити. Будь-які аргументи в кінці
docker runповністю замінюютьCMD - Призначення: надати стандартну поведінку, яку користувач може легко змінити
Три сценарії використання
Сценарій 1: Тільки CMD
CMD ["java", "-jar", "/app.jar"]
docker run myapp → java -jar /app.jar
docker run myapp echo hello → echo hello (CMD повністю замінено)
Сценарій 2: Тільки ENTRYPOINT
ENTRYPOINT ["java", "-jar", "/app.jar"]
docker run myapp → java -jar /app.jar
docker run myapp --debug → java -jar /app.jar --debug
Сценарій 3: ENTRYPOINT + CMD (рекомендується)
ENTRYPOINT ["java", "-jar", "/app.jar"]
CMD ["--server.port=8080"]
docker run myapp → java -jar /app.jar --server.port=8080
docker run myapp --server.port=9090 → java -jar /app.jar --server.port=9090
Сценарій 3 (ENTRYPOINT + CMD) – рекомендований. Сценарій 1 – для простих скриптів. Сценарій 2 – коли контейнер має бути «виконуваним файлом».
Типові помилки
| Помилка | Наслідок | Як уникнути |
|---|---|---|
Shell form ENTRYPOINT java -jar |
Сигнали не доходять до додатку | Exec form ["java", "-jar"] |
| Кілька CMD в Dockerfile | Враховується лише остання | Одна CMD на файл |
| CMD без ENTRYPOINT | CMD легко випадково замінити | Використовуйте пару ENTRYPOINT + CMD |
| ENTRYPOINT без CMD | Розробник не може перевизначити для налагодження | Додайте CMD з параметрами за замовчуванням |
| Плутанина з перевизначенням | Контейнер запускає не те | Розумійте, що docker run myapp arg замінює CMD |
Перевизначення при запуску
# Перевизначити CMD — просто передайте аргументи
docker run myapp --custom-arg
# Перевизначити ENTRYPOINT — використайте прапорець
docker run --entrypoint /bin/bash myapp
Shell form vs Exec form: критичний нюанс
Exec form — команда запускається безпосередньо як процес з PID 1. Правильно обробляє сигнали завершення. Важливо для Kubernetes і Docker Stop.
Shell form — команда запускається як підпроцес /bin/sh -c. Сигнали ОС приходять в оболонку sh, а не в додаток. Додаток може бути «вбито» жорстко без завершення транзакцій.
Що запам’ятати
- Використовуйте Exec form для обох інструкцій
ENTRYPOINTдля незмінної частини командиCMDдля параметрів, які можуть бути змінені- Лише одна
CMDі однаENTRYPOINTвраховуються (останні у файлі) - Без proper signal handling додаток не зможе коректно завершитися
Коли НЕ використовувати ENTRYPOINT
Не використовуйте ENTRYPOINT, якщо образ призначений для різних команд (наприклад, образ curl/wget для різних запитів). В цьому випадку CMD гнучкіший.
🔴 Senior Level
Архітектурний дизайн ENTRYPOINT і CMD
Різниця між ENTRYPOINT і CMD — це не просто синтаксис, це патерн проєктування для створення перевикористовуваних контейнерних образів.
Патерн «Executable Container»
ENTRYPOINT перетворює контейнер на виконуваний файл:
ENTRYPOINT ["java"]
CMD ["-jar", "/app.jar"]
Контейнер поводиться як бінарник:
docker run myapp -version # java -version
docker run myapp -jar other.jar # java -jar other.jar
Проблема PID 1 і обробка сигналів
В Linux процес з PID 1 має особливу поведінку:
- Не отримує сигнали за замовчуванням (ігнорує SIGTERM)
- Відповідає за «усиновлення» осиротілих процесів
- Не завершується автоматично при unhandled signals
PID 1 – головний процес в контейнері. Якщо він не вміє завершуватися коректно (обробляти SIGTERM), додаток буде «вбито» жорстко через SIGKILL.
Shell form проблема:
ENTRYPOINT java -jar /app.jar
Процеси в контейнері:
PID 1: /bin/sh -c "java -jar /app.jar"
PID 7: java -jar /app.jar
SIGTERM приходить в /bin/sh, який не знає, що робити з java. Результат: docker stop чекає 10 секунд і посилає SIGKILL. Додаток не виконує graceful shutdown — транзакції перериваються, з’єднання не закриваються.
Рішення:
- Exec form (рекомендується):
ENTRYPOINT ["java", "-jar", "/app.jar"]Додаток — PID 1, отримує сигнали безпосередньо.
- Tini (init system):
ENTRYPOINT ["tini", "--", "java", "-jar", "/app.jar"]Tini — мінімальний init, коректно forwarding сигнали і reap’ить zombie-процеси.
- Docker –init прапорець:
docker run --init myapp
Взаємодія з Kubernetes
В Kubernetes ENTRYPOINT і CMD мапляться на поля контейнера:
| Docker | Kubernetes |
|---|---|
ENTRYPOINT |
command |
CMD |
args |
containers:
- name: app
image: myapp
command: ["java", "-jar"] # перевизначає ENTRYPOINT
args: ["--server.port=9090"] # перевизначає CMD
Важливо: якщо в Kubernetes вказані command і args, вони повністю замінюють Docker ENTRYPOINT і CMD.
Trade-offs
| Підхід | Плюс | Мінус |
|---|---|---|
| Тільки CMD | Гнучкість для розробника | Легко випадково замінити |
| Тільки ENTRYPOINT | Стабільність | Складно перевизначити для налагодження |
| ENTRYPOINT + CMD | Найкраще з обох | Потрібно розуміти взаємодію |
| Entrypoint-скрипт | Гнучка ініціалізація | Ще один шар, потрібно підтримувати |
| Tini | Правильні сигнали + zombie reap | Додаткова залежність |
Edge Cases
- Override в Kubernetes:
commandв K8s маніфесті повністю замінює ENTRYPOINT. Якщо розробник очікує, що entrypoint-скрипт виконає міграції, а K8s перевизначивcommand— міграції не запустяться. - Наслідування ENTRYPOINT: якщо базовий образ має ENTRYPOINT, а дочірній Dockerfile — ні, наслідується ENTRYPOINT батька. Це може призвести до неочікуваної поведінки.
- JSON array parsing:
ENTRYPOINT ["java", "-jar", "/app.jar"]— це JSON. Помилка в лапках або комах призведе до падіння збірки. - CMD як shell form + ENTRYPOINT як exec form:
ENTRYPOINT ["java"]+CMD -jar app.jar— CMD виконається як/bin/sh -c "java -jar app.jar", ENTRYPOINT буде проігноровано.
Патерн «Entry Point Script»
Для складних додатків використовують entrypoint-скрипт:
COPY docker-entrypoint.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/docker-entrypoint.sh
ENTRYPOINT ["docker-entrypoint.sh"]
CMD ["java", "-jar", "/app.jar"]
#!/bin/sh
# docker-entrypoint.sh
# Виконуємо міграції
if [ "$RUN_MIGRATIONS" = "true" ]; then
java -jar /app.jar --migrate
exit 0
fi
# Запускаємо додаток (exec замінює процес скрипта)
exec "$@"
exec "$@" замінює процес shell-скрипта на цільову команду, зберігаючи PID 1 і сигнали. Без exec скрипт залишиться PID 1 і сигнали не дійдуть до додатку.
Антипатерни
- Shell form без обробки сигналів:
ENTRYPOINT java -jar /app.jar— graceful shutdown не працює. - Кілька CMD/ENTRYPOINT: враховується лише остання.
- ENTRYPOINT без CMD в образах для розробників: неможливо перевизначити для налагодження.
- Entrypoint-скрипт без
exec: скрипт залишається PID 1, сигнали втрачаються. - Секрети в CMD/ENTRYPOINT:
CMD ["java", "-jar", "app.jar", "--db-password=secret"]— видно вdocker inspectіps.
Продуктивність
| Підхід | Час shutdown | Graceful shutdown | Zombie reap |
|---|---|---|---|
| Shell form | 10s (SIGKILL timeout) | Ні | Ні |
| Exec form | < 1s (SIGTERM) | Так | Ні |
| Exec form + tini | < 1s | Так | Так |
| Docker –init | < 1s | Так | Так |
Резюме
- Використовуйте Exec form для обох інструкцій — критично для signal handling.
ENTRYPOINT+CMDразом створюють гнучкий і перевикористовуваний образ.- Розумійте маппінг на Kubernetes
command/args. - Проблема PID 1 реальна: використовуйте exec form або tini.
- Entrypoint-скрипти з
exec "$@"— стандарт для складних сценаріїв ініціалізації. - В Dockerfile може бути лише одна інструкція
CMDі однаENTRYPOINT(враховується остання).
🎯 Шпаргалка для інтерв’ю
Обов’язково знати:
- ENTRYPOINT — фіксована команда (PID 1), CMD — параметри за замовчуванням
- ENTRYPOINT + CMD разом — рекомендований патерн («executable container»)
- Exec form
["cmd", "arg"]обов’язкова для коректної обробки сигналів - Docker CMD легко перевизначити (
docker run image args), ENTRYPOINT — через--entrypoint - В K8s: Docker ENTRYPOINT →
command, CMD →args(повна заміна) - Проблема PID 1: процес без обробника сигналів не робить graceful shutdown
- Рішення PID 1: exec form, tini, або
docker run --init
Часті уточнюючі питання:
- «Що відбувається при
docker run myapp arg?» —argзамінює CMD, дописується до ENTRYPOINT - «Чому shell form небезпечна в K8s?» — Сигнали приходять в
/bin/sh, не в додаток; graceful shutdown не працює - «Що робить
exec "$@"в entrypoint-скрипті?» — Замінює процес скрипта на цільову команду, зберігаючи PID 1 - «Чи можна мати кілька CMD в Dockerfile?» — Так, але враховується лише остання
Червоні прапорці (НЕ говорити):
- «CMD і ENTRYPOINT — одне і те ж» (різна семантика і перевизначаємість)
- «Використовую shell form для зручності» (graceful shutdown зламаний)
- «В K8s command і args доповнюють Docker ENTRYPOINT/CMD» (повністю замінюють!)
- «PID 1 не має значення в контейнері» (критичний для signal handling)
Пов’язані теми:
- [[Які основні інструкції використовуються в Dockerfile]] — усі інструкції Dockerfile
- [[Що таке Pod в Kubernetes]] — контейнери в K8s (signal handling важливий)
- [[Як організувати rolling update в Kubernetes]] — graceful shutdown при оновленні