Мы предполагаем, что вы немного знакомы с Docker и знаете основы, такие как запуск контейнеров докеров и т. д.
Что именно изменяет образ докера?
Образ контейнера строится по слоям (или это набор слоев), каждая инструкция Dockerfile создает слой изображения. Например, рассмотрим следующий файл Dockerfile:
FROM alpine:latest RUN apk add --no-cache python3 ENTRYPOINT ["python3", "-c", "print('Hello World')"]
Поскольку существует всего три команды Dockerfile, образ, созданный из этого Dockerfile, будет содержать в общей сложности три слоя.
Вы можете убедиться в этом, построив образ:
docker image built -t dummy:0.1 .
А затем с помощью команды docker image history на построенном образе.
articles/Modify a Docker Image on modify-docker-images [?] took 12s ❯ docker image history dummy:0.1 IMAGE CREATED CREATED BY SIZE COMMENT b997f897c2db 10 seconds ago /bin/sh -c #(nop) ENTRYPOINT ["python3" "-c… 0B ee217b9fe4f7 10 seconds ago /bin/sh -c apk add --no-cache python3 43.6MB 28f6e2705743 35 hours ago /bin/sh -c #(nop) CMD ["/bin/sh"] 0B <missing> 35 hours ago /bin/sh -c #(nop) ADD file:80bf8bd014071345b… 5.61MB
Игнорируйте последний слой <missing>.
Каждый из этих слоев доступен только для чтения. Это полезно, потому что, поскольку эти слои доступны только для чтения, ни один процесс, связанный с запущенным экземпляром этого изображения, не сможет изменять содержимое этого изображения, поэтому эти слои могут использоваться многими контейнерами без необходимости сохранения копия для каждого экземпляра. Но для того, чтобы процессы контейнеров могли выполнять чтение/запись, при создании контейнеров поверх существующих слоев RO добавляется еще один слой, который доступен для записи и не используется другими контейнерами.
Обратной стороной этого слоя r/w является то, что изменения, сделанные в этом слое, не являются постоянными, хотя вы можете использовать тома для сохранения некоторых данных, иногда вам может потребоваться/вы захотите добавить слой перед каким-либо существующим слоем или удалить слой из изображение или просто замените слой. Это причины, по которым можно изменить существующее изображение docker.
В этой статье я собираюсь рассмотреть все упомянутые выше случаи, используя разные методы.
Способы изменения образа докера
Есть два способа изменить образ докера.
- Через Dockerfiles.
- Используя команду docker container commit.
Мы объясним оба метода, а в конце добавим, какой вариант использования лучше подходит для этого метода в контексте.
Изменение образа докера через Dockerfile
Изменение образа докера по сути означает изменение слоев изображения. Теперь, поскольку каждая команда Dockerfile представляет один слой изображения, изменение каждой строки файла Dockerfile также изменит соответствующий образ.
Поэтому, если вы добавляете слой к изображению, вы можете просто добавить к нему еще одну инструкцию Dockerfile, чтобы удалить одну, вы удалили бы строку, а для изменения слоя вы бы изменили строку соответствующим образом.
Есть два способа использовать Dockerfile для изменения изображения.
- Используя образ, который вы хотите изменить, как базовый образ, и создайте дочерний образ.
- Изменение фактического файла Dockerfile образа, который вы хотите изменить.
Позвольте нам объяснить, какой метод следует использовать, когда и как.
1. Использование изображения в качестве базового.
Это когда вы берете изображение, которое хотите изменить, и добавляете к нему слои для создания нового дочернего изображения. Если образ не создается с нуля, каждое изображение является модификацией одного и того же родительского базового образа.
Рассмотрим предыдущий файл Dockerfile. Скажите, почему построение образа из этого образа названо dummy:0.1. А если мы захотели использовать Perl вместо Python3 для печати «Hello World», но мы не хотим удалять Python3, мы могли бы просто использовать изображение dummy:0.1 в качестве базового изображения (поскольку Python3 уже существует) и строить из этого, как показано ниже
FROM dummy:0.1 RUN apk add --no-cache perl ENTRYPOINT ["perl", "-e", "print \"Hello World\n\""]
Здесь мы строим поверх dummy:0.1, добавляя к нему больше слоев, как считаем нужным.
Этот метод не будет очень полезным, если вы собираетесь изменить или удалить какой-либо существующий слой. Для этого вам нужно следовать следующему методу.
2. Изменение образа Dockerfile.
Поскольку существующие слои изображения доступны только для чтения, вы не можете напрямую изменять их с помощью нового файла Dockerfile. С помощью команды FROM в Dockerfile вы берете какое-то изображение в качестве основы и строите на нем или добавляете к нему слои.
Некоторые задачи могут потребовать от нас изменения существующего уровня, хотя вы можете сделать это, используя предыдущий метод с кучей противоречивых RUNинструкций (например, удаление файлов, удаление/замена пакетов, добавленных на каком-то предыдущем уровне), это не идеальное решение или что мы бы порекомендовали. Потому что он добавляет дополнительные слои и значительно увеличивает размер изображения.
Лучшим способом было бы не использовать образ в качестве базового, а изменить фактический файл Dockerfile этого образа. Вернемся к предыдущему Dockerfile, что, если бы мне не пришлось хранить Python3 в этом образе и заменить пакет Python3 и команду на Perl?
Следуя предыдущему методу, нам пришлось бы создать новый Dockerfile, например:
FROM dummy:0.1 RUN apk del python3 && apk add --no-cache perl ENTRYPOINT ["perl", "-e", "print \"Hello World\n\""]
При построении, в этом изображении будет всего пять слоев.
articles/Modify a Docker Image on modify-docker-images [?] took 3s ❯ docker image history dummy:0.2 IMAGE CREATED CREATED BY SIZE COMMENT 2792036ddc91 10 seconds ago /bin/sh -c #(nop) ENTRYPOINT ["perl" "-e" "… 0B b1b2ec1cf869 11 seconds ago /bin/sh -c apk del python3 && apk add --no-c… 34.6MB ecb8694b5294 3 hours ago /bin/sh -c #(nop) ENTRYPOINT ["python3" "-c… 0B 8017025d71f9 3 hours ago /bin/sh -c apk add --no-cache python3 && … 43.6MB 28f6e2705743 38 hours ago /bin/sh -c #(nop) CMD ["/bin/sh"] 0B <missing> 38 hours ago /bin/sh -c #(nop) ADD file:80bf8bd014071345b… 5.61MB
Также размер изображения составляет 83,8 МБ.
articles/Modify a Docker Image on modify-docker-images [?] ❯ docker images REPOSITORY TAG IMAGE ID CREATED SIZE dummy 0.2 2792036ddc91 19 seconds ago 83.8MB
Теперь вместо этого возьмите исходный файл Dockerfile и измените файлы Python3 на Perl следующим образом
FROM alpine:latest RUN apk add --no-cache perl ENTRYPOINT ["perl", "-e", "print \"Hello World\n\""]
Количество слоев уменьшилось до 3, а размер теперь составляет 40,2 МБ.
articles/Modify a Docker Image on modify-docker-images [?] took 3s ❯ docker image history dummy:0.3 IMAGE CREATED CREATED BY SIZE COMMENT f35cd94c92bd 9 seconds ago /bin/sh -c #(nop) ENTRYPOINT ["perl" "-e" "… 0B 053a6a6ba221 9 seconds ago /bin/sh -c apk add --no-cache perl 34.6MB 28f6e2705743 38 hours ago /bin/sh -c #(nop) CMD ["/bin/sh"] 0B <missing> 38 hours ago /bin/sh -c #(nop) ADD file:80bf8bd014071345b… 5.61MB articles/Modify a Docker Image on modify-docker-images [?] ❯ docker images REPOSITORY TAG IMAGE ID CREATED SIZE dummy 0.3 f35cd94c92bd 29 seconds ago 40.2MB
Изображение успешно изменено.
Предыдущий метод более полезен, когда вы собираетесь просто добавить слои поверх существующих, но не очень полезен при попытке изменить существующие слои, например удалить один, заменить один, изменить порядок существующих и так далее. Вот где сияет этот метод.
Метод 2: изменение образа с помощью фиксации докера
Есть еще один метод, с помощью которого вы можете сделать снимок работающего контейнера и превратить его в собственный образ.
Давайте создадим идентичный образ dummy:0.1, но на этот раз без использования Dockerfile. Поскольку мы использовали базу alpine:latestкак dummy:0.1, разверните контейнер с этим изображением.
docker run --rm --name alpine -ti alpine ash
Теперь внутри контейнера, добавьте пакет Python3, apk add –no-cache python3. После этого откройте новое окно терминала и выполните следующую команду (или что-то подобное)
docker container commit --change='ENTRYPOINT ["python3", "-c", "print(\"Hello World\")"]' alpine dummy:0.4
С помощью флага –change мы добавляю к новому образу dummy:04 инструкцию Dockerfile (в данном случае инструкцию ENTRYPOINT).
С помощью команды docker container commit вы в основном конвертируете внешний слой r/w в слой ar/o, добавляете его к существующим слоям изображения и создаете новое изображение. Этот метод более интуитивно понятен/интерактивен, поэтому вы можете использовать его вместо Dockerfiles, но понимаете, что это не очень воспроизводимо. Также те же правила применяются к удалению или изменению любых существующих слоев, добавление слоя только для того, чтобы что-то удалить или изменить что-то, сделанное на предыдущем слое, не лучшая идея, по крайней мере, в большинстве случаев.
На этом статья завершается. Мы надеемся, что это статья была полезна для вас, если у вас есть какие-либо вопросы, оставьте комментарий ниже.