docker-compose: разностный запуск, exec и что происходит со слоями

#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 команду напрямую.