Образы Docker могуть быть очень большими. Многие легко превышают 1 Гб в размере. Как они становятся такими? Должны ли они быть такими? Можем ли мы сделать их меньше, не жертвуя функциональностью?
Все дело в слоях. Образ Docker похож на супер бигмак, где каждый инградиент этажности увеличивает его массу в килобайтах. Концепция слоев затрагивает различные низкоуровневые технические детали о вещах вроде корневой файловой системы (rootfs), механизма копирования при записи (copy-on-write) и каскадно-объединенного монтирования (union mount). Эта азбука достаточно хорошо раскрыта в другом месте, поэтому я не буду пересказывать ее здесь. Для Важным является понимание того, что каждая инструкция в Dockerfile приводит к созданию нового слоя образа. Бутерброд одним словом!
Зная, что образ - это не что иное, как коллекция других образов, можно прийти к очевидному выводу: размер образа равен сумме размеров образов, его составляющих.
Посмотрим на вывод команды docker history imagename:
$ docker history aformat
IMAGE CREATED CREATED BY SIZE COMMENT
d1ea947a9058 6 days ago 119MB merge sha256:f1fe0e102e804d28b6ae83ba4a3d1beb730eaea48aefc5b4510a3e03e5fe1c76 to sha256:a24bb4013296f61e89ba57005a7b3e52274d8edd3ae2077d04395f806b63d83e
<missing> 6 days ago /bin/sh -c tree -L 1 0B
<missing> 6 days ago /bin/sh -c echo "******************* Hello w… 0B
<missing> 6 days ago /bin/sh -c rm -rf /var/cache/apk/* 0B
<missing> 6 days ago /bin/sh -c apk add tree 0B
<missing> 6 days ago /bin/sh -c apk add git && apk add npm && apk… 0B
<missing> 10 days ago /bin/sh -c apk --no-cache add --update bash 0B
<missing> 10 days ago /bin/sh -c #(nop) MAINTAINER Nyukers <nyuke… 0B
<missing> 2 months ago /bin/sh -c #(nop) CMD ["/bin/sh"] 0B
<missing> 2 months ago /bin/sh -c #(nop) ADD file:c92c248239f8c7b9b… 5.57MB
Мы можем увидеть все слои образа aformat вместе с командами, которые привели к их созданию, и их размером.
Что же следует учесть для уменьшения размера образа Docker?
1. Выбираем только необходимую базу.
Выбор базового образа может существенно повлиять на конечный размер вашего образа. Вот, например, список популярных базовых образов и их размеры:
$ docker images
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
scratch latest 511136ea3c5a 13 months ago 0 B
busybox latest a9eb17255234 7 weeks ago 2.433 MB
debian latest e8d37d9e3476 4 days ago 85.18 MB
ubuntu latest ba5877dc9bec 4 days ago 192.7 MB
centos latest 1a7dc42f78ba 2 weeks ago 236.4 MB
fedora latest 88b42ffd1f7c 10 days ago 373.7 MB
Мы раньше использовали ubuntu в качестве основы — в основном, потому что большинство из нас уже были с ней знакомы. Однако, немного поиграв с alpine, мы пришли к выводу, что он полностью удовлетворяет нашим потребностям и сохраняет при этом до 100 Мб места.
Хотелось бы, чтобы размер образов отображался в хранилище Docker. Но сейчас, к сожалению, чтобы узнать размер, образ нужно скачать.
2. Группируйте однотипные команды.
В примере выше мы создаем файл, а затем сразу же его удаляем. Ситуация хоть и надуманная, но нечто похожее часто происходит при построении образов. Давайте посмотрим на реальном примере:
FROM alpine:latest
RUN apk --no-cache add --update bash
RUN apk add git npm py-pip
RUN rm -rf /var/cache/apk/*
RUN chmod +x gitstart.sh
Как мы знаем, каждая из этих инструкций создает отдельный слой. Несмотря на то, что мы удаляем кеш и извлеченные файлы, они все равно остаются частью образа.
Мы можем исправить это, проведя небольшой рефакторинг нашего Dockerfile:
FROM alpine:latest
RUN apk --no-cache add --update bash &&\
apk add git npm py-pip &&\
rm -rf /var/cache/apk/* &&\
chmod +x gitstart.sh
Вместо запуска каждой команды в отдельной инструкции RUN мы сгруппировали их с помощью оператора &&. И хотя Dockerfile становится чуть менее читабельным, это позволяет нам удалить кеш прежде, чем слой будет закоммичен.
3. «Схлопывайте» ваши образы.
Следует отметить, что Docker-engine начиная с версии 1.13 в экспериментальном режиме содержит возможность сборки уже сжатых образов. Для этого в команду сборки необходимо добавить параметр --squash, например:
docker build --squash -t aformat:latest .
Чтобы включить експериментальный режим docker-engine в Ubuntu 20.04 необходимо в конфигурационном файле /lib/systemd/system/docker.service добавить параметр --experimental=true.
Теперь строчка конфига выглядит так:
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock --experimental=true
После изменения конфигурационного файла выполняем:
sudo systemctl daemon-reload
sudo systemctl restart docker
Теперь можно сразу собирать сжатые docker-образы командой:
docker build --squash -t aformat:latest .
Все вышеописаные стратегии исходят из предположения о том, что вы создаете свой собственный образ, или, хотя-бы, имеете доступ к Dockerfile. Однако, возможна ситуация, когда у вас есть образ, созданный кем-то другим, и вы хотите немного облегчить его.
В этом случае мы можем воспользоваться тем фактом, что создание контейнера приводит к слиянию всех слоев в один. Сливая все слои вместе, вы теряете описанное ранее преимущество совместного использования слоев разными образами.
Все метаданные, обычно, сохраняемые вместе с образом, теряются в процессе запуска/эскпорта/импорта. Открываемые порты, переменные окружения, команда по умолчанию — все, что может быть объявлено в оригинальном образе, теряется.
Успехов.
No comments:
Post a Comment
А что вы думаете по этому поводу?