#docker #docker-compose
#docker #docker-compose
Вопрос:
Насколько я понял docker
, и docker-compose
поток такой:
Dockerfile --build--> Docker Image --create--> Docker Container #1
--create--> Docker Container #2
--create--> ...
--create--> Docker Container #n
Таким Image
образом, выполняется командами в Dockerfile
, но является чисто какой-то «автономной» версией. Затем вы можете перевести его «в онлайн» в виде контейнера.
В моем понимании docker-compose up
этот двухэтапный процесс выполняется для нас с помощью service
определений внутри docker-compose.yml
, помимо некоторых других вещей, таких как монтирование томов, предоставление портов и так далее.
Мой первый вопрос:
Если я запускаю docker-compose exec some_command
, то я манипулирую контейнером docker — правильно?
Насколько я понял документацию, docker-compose run some_command
делает почти то же самое, но заранее создает новый «слой», так что sth. вот так:
Docker Container #n (layer 0) ----> Docker Container #n (layer 1)
В то время some_command
как затем выполняется на уровне 1 — правильно?
Кроме того, кажется run
, что он переходит some_command
к точке входа, в то время exec
как переопределяет определенную точку входа.
Мой второй вопрос:
Что происходит с новым слоем, созданным run
впоследствии? Есть ли способ выполнить команду без создания нового слоя? Я хотел бы знать это, потому что я добавил сценарий точки входа, подобный этому:
#!/bin/sh
case "$@" in
"update")
pip3 install -r requirements.txt
;;
"start")
python manage.py runserver
;;
*)
echo "executing $@"
exec "$@"
;;
esac
Я сделал это для того, чтобы иметь возможность запускать некоторые общие задачи, такие как настройка, обновление, … выполнив всего лишь короткую команду, например docker-compose run backend update
. Проблема в том, что слой, созданный с помощью run
, не используется, если я запускаю up
его впоследствии.
Ответ №1:
Сначала давайте проясним основные термины здесь. Цитирование из официальных документов:
Изображение — это шаблон, доступный только для чтения, с инструкциями по созданию контейнера Docker.
По сути, изображение — это просто архив файловой системы, содержащий файлы и структуру каталогов, а также некоторые дополнительные метаданные (например ENV
, or CMD
) для контейнера. Таким образом, вы также можете думать об этом как tar
zip
об архиве или.
Контейнер — это исполняемый экземпляр изображения.
Чтобы запустить новый контейнер из изображения docker
, распаковывает изображение, настраивает соответствующие пространства имен и запускает процесс, используя распакованный образ в качестве корня файловой системы.
Слои обычно относятся к слоям в изображении, созданным в процессе сборки, и используются для кэширования и повторного использования общих файлов / слоев в нескольких изображениях. Поэтому вам нужно будет заботиться о них только при оптимизации ваших шагов сборки в Dockerfile
.
Вместо извлечения / копирования содержимого изображения для каждого нового контейнера docker
будет добавлен только тонкий записываемый слой поверх изображения, где хранятся изменения для этого контейнера. Точная механика этого является деталью реализации и зависит от используемых драйверов хранилища! По большей части при использовании docker
вам не нужно беспокоиться об этом.
Теперь об использовании docker-compose
: то, что он в основном делает под капотом, — это просто создавать и выполнять соответствующие docker
команды * для запуска и управления контейнерами, необходимыми для ваших служб, на основе вашего docker-compose.yml
файла конфигурации. Итак, давайте рассмотрим отношения docker-compose
к соответствующим docker
командам:
docker-compose build
: выполнит соответствующуюdocker build
команду для каждой службы с указаннойbuild
опциейdocker-compose pull
:docker pull
s изображения для всех сервисов сimage
опцией из соответствующего репозиторияdocker-compose up
:- при необходимости: сначала
docker-compose build
/docker-compose pull
все ваши службы - запускает служебные контейнеры, создавая и выполняя соответствующую
docker run
команду
- при необходимости: сначала
docker-compose exec
: будет запущенdocker exec
для запуска нового процесса / команды внутри существующего запущенного контейнераdocker-compose run
: запускает новый контейнер для интерактивного выполнения указанной команды, т.Е. Выполненияdocker run
* Обратите внимание, что на самом docker-compose
деле это не вызывает docker
команду, а скорее взаимодействует с docker
демоном напрямую, используя API. Но для облегчения понимания вы можете просто притвориться, что это так.
С этим теперь к вашим вопросам:
Если я запускаю
docker-compose exec some_command
, то я манипулирую контейнером docker — правильно?Насколько я понял документацию,
docker-compose run some_command
делает почти то же самое, но заранее создает новый «слой», так что sth. вот так:
docker-compose exec
будет выполняться внутри вашего существующего, запущенного контейнера службы, в то время docker-compose run
как будет запущен новый, независимый контейнер.
Кроме того, кажется
run
, что он переходитsome_command
к точке входа, в то времяexec
как переопределяет определенную точку входа.
Правильно, exec
не использует точку входа.
Что происходит с новым слоем, созданным
run
впоследствии? Есть ли способ выполнить команду без создания нового слоя?
Изменения являются частью контейнера, запущенного run
и будут сохраняться до тех пор, пока вы не удалите этот контейнер. Если вы хотите применить изменения к своему служебному контейнеру (используемому docker-compose up
), однако вам нужно выполнить docker-compose exec
!
Я хотел бы знать это, потому что я добавил сценарий точки входа, подобный этому:
Я сделал это для того, чтобы иметь возможность запускать некоторые общие задачи, такие как настройка, обновление, … выполнив всего лишь короткую команду, например
docker-compose run backend update
.
Поскольку docker-compose run
эта команда будет выполняться в новом контейнере, вам нужно выполнить docker-compose exec
вместо этого вызов entrypoint вручную:
docker-compose exec app /entrypoint.sh update
# and if you need to restart your app restart the service containers
docker-compose restart
Однако это неправильный способ использования docker
! потому что контейнеры должны быть эфемерными, а команды, выполняемые в контейнере, будут влиять только на этот конкретный контейнер. Все изменения будут потеряны в тот момент, когда вам нужно будет запустить новый экземпляр контейнера на этом изображении (вы остановили контейнер, сбой питания и т. Д.).
Вместо этого вы должны добавить update
команду как (последнюю) команду в свой Dockerfile
и перестроить (последний слой) изображения:
docker-compose up --build
Или если вам нужно пропустить кеш сборки, чтобы принудительно выполнить новую сборку:
docker-compose build --no-cache
docker-compose up
Комментарии:
1. Ваш ответ был самым точным и понятным, большое вам спасибо
2. Превосходный ответ. Спасибо.
Ответ №2:
По большей части различные императивные docker-compose
команды очень похожи на их docker
эквиваленты, за исключением того, что они знают, как искать контейнеры с помощью docker-compose.yml
файла, а в некоторых случаях их значения по умолчанию немного отличаются.
Итак, docker-compose exec
в основном идентично docker exec
. Это инструмент отладки, который может запускать дополнительную команду внутри запущенного контейнера. Он выполняется в той же среде, что и основной процесс контейнера, с той же частной файловой системой контейнера. Это не дочерний процесс основного процесса, и если у основного процесса есть сценарий-оболочка точки входа, который устанавливает переменные среды, он их не увидит. Дополнительная команда часто является оболочкой отладки и docker-compose exec
по умолчанию docker exec -it
использует параметры.
# Get a debugging shell in a running container:
docker-compose exec app bash
# Basically equivalent to:
docker exec -it project_app_1 bash
Аналогично, docker-compose run
в основном идентично docker run
: он запускает новый контейнер на основе изображения, указанного в docker-compose.yml
файле. В отличие docker run
от этого, он наследует большинство своих параметров из docker-compose.yml
файла, включая сетевую настройку, переменные среды и подключенные тома (но не опубликованные порты). Например docker run
, содержимое файловой системы контейнера в основном теряется после выхода из контейнера (или удаляется, если вы использовали эту --rm
опцию).
# Get a debugging shell to see how an image was built:
# (This will run via the image's entrypoint)
docker-compose run --rm app bash
# Basically equivalent to:
docker build -t project_app .
docker run -it --net project_default -v $PWD/logs:/app/logs
--rm project_app bash
# (Picking up the Compose networks: and volumes:)
Задача «обновить», которую вы показываете в предлагаемой оболочке точки входа, — это не то, что вы бы делали во время запуска контейнера. Если вам нужно обновить зависимости кода или библиотеки в изображении, перестройте изображение (возможно, с docker-compose build
помощью ).
# If the Dockerfile has...
FROM python:3.9
WORKDIR /app
COPY requirements.txt .
RUN pip3 install -r requirements.txt
...
# ...then you can get a container with updated library dependencies by
docker-compose up --build
Комментарии:
1. Последняя часть этого ответа, безусловно, правильная вещь
Ответ №3:
«Живой» уровень 1, на который вы ссылаетесь, — это фактическая папка, скрытая в файловой системе хоста, которая является тем, что запущенный контейнер видит как свой мир файлов. Эти файлы являются эфемерными и удаляются сразу после удаления контейнера, если только они не были предварительно зафиксированы в слой сжатого изображения.
Таким образом, существует два вида слоев — текущие эфемерные и сохраненные зафиксированные. Обычно только последние называются слоями, хотя вы правы, все они похожи на слои, если вы немного прищуритесь.
Эфемерный слой может быть превращен в сохраняемый, только если он зафиксирован (может быть выполнен через docker commit
), что именно docker build
и делает процесс, когда он проходит по строкам Dockerfile, запуская недолговечные контейнеры по ходу.
По первому вопросу: docker exec
запускает новый процесс в существующей изолированной папке, как указано выше, в то docker run
время как, помимо прочего, распаковывает архивированное изображение и сначала настраивает файлы «онлайн». Так что в принципе вы совершенно правы.
По второму вопросу: если вы хотите запустить свою команду обновления, а затем зафиксировать эфемерные файлы контейнера, которые были обновлены, вам нужно будет явно выполнить docker commit $containerId $newImageTag
после завершения docker run
команды (но до того, как контейнер был удален — запутался ??)
Не используйте docker-compose
для такого рода пререканий контейнер / изображение — используйте docker
команду напрямую.