У чому різниця між CMD і ENTRYPOINT?
CMD і ENTRYPOINT це дві Dockerfile-інструкції, що визначають, що крутиться при старті container. Різниця проявляється у момент, коли користувач передає аргументи у docker run.
Теорія
TL;DR
CMD ["prog", "arg"]= дефолтна команда, повністю замінна.docker run img otherCmdповністю її замінює.ENTRYPOINT ["prog"]= фіксована перша частина команди. Все, що йде післяdocker run img ...стає його аргументами (абоCMD, якщо нічого не передаєш).- Зв'язка
ENTRYPOINT + CMDце стандартний патерн для CLI-image: фіксований entrypoint, дефолтні args, які можна перевизначити. - Бери exec-форму (
["prog", "arg"], JSON-масив). Shell-форма (prog arg) загортає все у/bin/sh -c, що ламає обробку сигналів. --entrypointуdocker runперевизначає самENTRYPOINT;CMDперевизначається просто аргументами після імені image.
Швидкий приклад
FROM alpine:3.21
ENTRYPOINT ["echo"]
CMD ["hello"]$ docker build -t demo .
$ docker run --rm demo
hello # ENTRYPOINT (echo) + CMD (hello)
$ docker run --rm demo bye
bye # ENTRYPOINT (echo) + новий CMD (bye)
$ docker run --rm --entrypoint /bin/sh demo -c 'ls /'
bin
lib
usr
# ↑ ENTRYPOINT явно перевизначено; CMD стає argv для /bin/shТри запуски, три поведінки. Розпил ENTRYPOINT + CMD саме і дає таку гнучкість.
Чотири поширених патерни
Патерн 1: тільки CMD — сервіс без аргументів
FROM nginx:1.27-alpine
CMD ["nginx", "-g", "daemon off;"]$ docker run myimg -> nginx -g "daemon off;"
$ docker run myimg sh -> sh (CMD повністю замінено)Бери це, коли image крутить один фіксований сервіс і користувач не має потреби міняти команду. Більшість сервіс-image.
Патерн 2: тільки ENTRYPOINT — image це CLI-інструмент
FROM alpine:3.21
RUN apk add --no-cache curl
ENTRYPOINT ["curl"]$ docker run myimg https://example.com
# -> curl https://example.com
$ docker run myimg --help
# -> curl --helpImage стає командою. Користувачі передають curl-флаги прямо у docker run.
Патерн 3: ENTRYPOINT + CMD — фіксована команда з дефолтними args
FROM alpine:3.21
RUN apk add --no-cache curl
ENTRYPOINT ["curl"]
CMD ["--help"]$ docker run myimg -> curl --help (дефолт)
$ docker run myimg https://example.com
# -> curl https://example.com (override)Канонічний патерн «image як CLI з корисною дефолтною поведінкою».
Патерн 4: shell-форма (уникай у проді)
CMD nginx -g "daemon off;" # shell-формаDocker тихо обгортає це як /bin/sh -c 'nginx -g "daemon off;"'. Реальний PID 1 у container це /bin/sh, не nginx. SIGTERM іде на sh, не на твій застосунок. Результат: docker stop чекає увесь grace-період і потім SIGKILL.
Завжди бери exec-форму (["prog", "arg"]) у прод-image.
Таблиця порівняння
| Аспект | тільки CMD | тільки ENTRYPOINT | ENTRYPOINT + CMD |
|---|---|---|---|
docker run img запускає | CMD | ENTRYPOINT | ENTRYPOINT + CMD |
docker run img foo bar запускає | foo bar | ENTRYPOINT + foo bar | ENTRYPOINT + foo bar |
--entrypoint X img foo bar запускає | X foo bar | X foo bar | X foo bar |
| Підходить для | сервісів без args | image-як-CLI | CLI з дефолтними args |
Типові помилки
Shell-форма і дивуватися, чому docker stop повільний
# НЕПРАВИЛЬНО: shell-форма, sh це PID 1, сигнали не доходять до nginx
CMD nginx -g "daemon off;"
# ПРАВИЛЬНО: exec-форма, nginx це PID 1, SIGTERM доходить
CMD ["nginx", "-g", "daemon off;"]З shell-формою docker stop шле SIGTERM на /bin/sh, який його ігнорує. Через 10 секунд Docker шле SIGKILL. Твій застосунок помирає жорстко, без graceful cleanup.
Спроба розгорнути env-змінні у exec-формі
# НЕПРАВИЛЬНО: exec-форма НЕ розгортає $VAR
CMD ["echo", "$HOME"] # виведе літерал $HOME
# ПРАВИЛЬНО 1: shell-форма (приймай trade-off PID 1 або --init)
CMD echo $HOME
# ПРАВИЛЬНО 2: явно виклич shell у exec-формі
CMD ["/bin/sh", "-c", "echo $HOME"]Exec-форма базується на execve() і не робить shell-парсингу. Якщо треба розгортання env-змінних, маєш сам викликати shell.
Два рядки CMD або ENTRYPOINT
Працює лише останній. Попередні тихо ігноруються.
CMD ["echo", "first"]
CMD ["echo", "second"]
# Container виконає: echo secondЖодної помилки, жодного попередження. Легко проґавити у довгому Dockerfile.
Забути, що docker run img sh замінює CMD, не ENTRYPOINT
ENTRYPOINT ["my-app"]
CMD ["--default-flag"]$ docker run img sh # запустить: my-app sh (НЕ shell)
$ docker run --entrypoint sh img # реально shellЯкщо є ENTRYPOINT, ти не можеш просто кинутися у shell trailing-аргументом. Потрібен --entrypoint.
Реальне застосування
- Сервіс-image (
nginx,postgres,redis): тількиCMD. Дефолтна команда стартує daemon; просунуті юзери перевизначають черезdocker run img <custom-args>, якщо треба. - CLI-image (
alpine/git,peter-evans/dockerhub-description,aws-cli):ENTRYPOINTвстановлено на бінар. Image є CLI. - Гібридні утиліти (
postgres:16чий entrypoint крутить init-скрипти і exec'ить postgres):ENTRYPOINT ["docker-entrypoint.sh"]+CMD ["postgres"]. Entrypoint-скрипт це обгортка, що робить setup, а потімexec "$@", щоб запустити CMD. - CI/CD-friendly image:
ENTRYPOINT ["my-tool"], тож джоби запускаютьdocker run myimg <flags>без запам'ятовування імені бінаря.
Питання для поглиблення
Q: Яка різниця між exec-формою і shell-формою?
A: Exec-форма ["prog", "arg"] запускає бінар напряму через execve(), твоя програма це PID 1, сигнали до неї доходять, без додаткового shell-процесу. Shell-форма prog arg обгортає команду у /bin/sh -c '...', тому /bin/sh стає PID 1. Прод-image мають завжди використовувати exec-форму.
Q: Чому мій CMD не стартує container?
A: CMD запускається лише коли у docker run не передано команди. Якщо ти робиш docker run img bash, CMD замінено на bash. Також: CMD не запускається, якщо є ENTRYPOINT, що ігнорує свої аргументи. І рахується лише останній CMD у Dockerfile.
Q: Що робить патерн docker-entrypoint.sh?
A: Це shell-скрипт, поставлений як ENTRYPOINT. Всередині робить setup (валідація env-змінних, init директорій, міграції), потім exec "$@", щоб замінити себе реальним процесом застосунку. exec робить застосунок PID 1 (тож сигнали працюють). Використовується у postgres, mysql, redis і багатьох офіційних image.
Q: Чи можна перевизначити і ENTRYPOINT, і CMD одночасно?
A: Так: docker run --entrypoint /bin/sh myimg -c 'echo hi'. --entrypoint міняє entrypoint; все після імені image стає новими args (оригінальним CMD).
Q: (Senior) Коли б ти НЕ використав патерн ENTRYPOINT + CMD навіть для CLI-image?
A: Коли користувачам реально треба запускати непов'язані команди всередині того самого image, наприклад debug-image, де docker run img sh має просто працювати. З жорстким ENTRYPOINT для цього потрібен --entrypoint sh. Для вузько-фокусованих CLI entrypoint правильний; для general-purpose image лишити entrypoint порожнім (або [""]) тримає docker run img <будь-що> гнучким.
Приклади
Сервіс-image — тільки CMD
FROM nginx:1.27-alpine
COPY nginx.conf /etc/nginx/nginx.conf
COPY html/ /usr/share/nginx/html/
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]$ docker run -d -p 8080:80 mywebsite # використає CMD
$ docker run -it mywebsite sh # CMD замінено на shImage крутить nginx за замовчуванням, але лишається придатним для дебагу.
CLI-image — ENTRYPOINT + CMD
FROM alpine:3.21
RUN apk add --no-cache curl
ENTRYPOINT ["curl"]
CMD ["--help"]$ docker run --rm mycurl -> curl --help
$ docker run --rm mycurl https://api.github.com -> curl https://api.github.com
$ docker run --rm --entrypoint sh mycurl -> shell, для дебагуImage є curl. Дефолтна поведінка (показ help) робить перший запуск інформативним; передача args дає реальний інструмент.
Postgres-style entrypoint-скрипт
FROM postgres:16-alpine
# Успадковує:
# ENTRYPOINT ["docker-entrypoint.sh"]
# CMD ["postgres"]
# Entrypoint-скрипт (з base image) робить:
# - валідацію POSTGRES_PASSWORD/POSTGRES_DB env-змінних
# - запуск initdb, якщо data-dir порожня
# - виконання /docker-entrypoint-initdb.d/* SQL-файлів при першому старті
# - exec "$@" # стає: exec postgres$ docker run -e POSTGRES_PASSWORD=dev postgres:16-alpine
# entrypoint крутить init-логіку, потім exec'ить postgres, що стає PID 1Патерн: entrypoint = setup-обгортка, CMD = реальна команда, exec "$@" в кінці, тож реальна команда стає PID 1 і отримує сигнали правильно.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.
Коментарі
Ще немає коментарів