В чем прелесть разделения среды исполнения контейнеров на отдельные инструментальные составляющие? В частности, в том, что эти инструменты можно начать комбинировать, чтобы они защищали друг друга.
Многих привлекает идея выполнять сборку контейнерных OCI-образов в рамках
Поэтому люди постоянно пытаются запускать Buildah в контейнере. Короче, мы создали
Настройка
Эти образы собраны из Dockerfiles, которые можно найти в репозитории Buildah в папке
Здесь мы рассмотрим
# stable/Dockerfile
#
# Build a Buildah container image from the latest
# stable version of Buildah on the Fedoras Updates System.
# https://bodhi.fedoraproject.org/updates/?search=buildah
# This image can be used to create a secured container
# that runs safely with privileges within the container.
#
FROM fedora:latest
# Don't include container-selinux and remove
# directories used by dnf that are just taking
# up space.
RUN yum -y install buildah fuse-overlayfs --exclude container-selinux; rm -rf /var/cache /var/log/dnf* /var/log/yum.*
# Adjust storage.conf to enable Fuse storage.
RUN sed -i -e 's|^#mount_program|mount_program|g' -e '/additionalimage.*/a "/var/lib/shared",' /etc/containers/storage.conf
Вместо OverlayFS, реализованной на уровне Linux-ядра хоста, мы используем внутри контейнера программу
podman run --device /dev/fuse quay.io/buildahctr ...
RUN mkdir -p /var/lib/shared/overlay-images /var/lib/shared/overlay-layers; touch /var/lib/shared/overlay-images/images.lock; touch /var/lib/shared/overlay-layers/layers.lock
Далее мы создаем каталог для дополнительных хранилищ.
# Set up environment variables to note that this is
# not starting with user namespace and default to
# isolate the filesystem with chroot.
ENV _BUILDAH_STARTED_IN_USERNS="" BUILDAH_ISOLATION=chroot
И наконец, используя переменную окружения BUILDAH_ISOLATION, мы говорим, что по умолчанию Buildah-контейнер должен запускаться с изоляцией chroot. Дополнительная изоляция здесь не требуется, поскольку мы и так уже работаем в контейнере. Для того, чтобы Buildah создавал свои собственные контейнеры с разделением пространств имен, требуется привилегия SYS_ADMIN, а для этого придется ослабить для контейнера правила SELinux и SECCOMP, что противоречит нашей установке выполнять сборку из безопасного контейнера.
Запускаем Buildah внутри контейнера
Рассмотренная выше схема образа Buildah-контейнера позволяет гибко варьировать способы запуска таких контейнеров.
Скорость против безопасности
Компьютерная безопасность – это всегда компромисс между скоростью выполнения процесса и тем, сколько защиты вокруг этого накручено. Это утверждение справедливо и при сборке контейнеров, поэтому ниже мы рассмотрим варианты такого компромисса.
Рассмотренный выше контейнерный образ будет держать свое хранилище в /var/lib/containers. Поэтому нам нужно подмонтировать контент в эту папку, и то, как мы это сделаем, будет сильно влиять на скорость сборки контейнерных образов.
Рассмотрим три варианта.
Вариант 1. Если требуется максимальная безопасность, то для каждого контейнера можно создавать свою папку для containers/image и подключать ее к контейнеру через volume-mount. А кроме того, размещать context directory в самом контейнере, в папке /build:
# mkdir /var/lib/containers1
# podman run -v ./build:/build:z -v /var/lib/containers1:/var/lib/containers:Z quay.io/buildah/stable
buildah -t image1 bud /build
# podman run -v /var/lib/containers1:/var/lib/containers:Z quay.io/buildah/stable buildah push image1 registry.company.com/myuser
# rm -rf /var/lib/containers1
Безопасность. Работающий в таком контейнере Buildah имеет максимальную безопасность: ему не дают никаких root-привилегий средствами capabilities, и к нему применяются все ограничения SECOMP и SELinux.Такой контейнер даже можно запускать с изоляцией User Namespace, добавив опцию вроде —uidmap 0:100000:10000.
Производительность. А вот производительность здесь минимальна, поскольку любые образы из контейнерных реестров каждый раз копируются на хост, и кэширование не работает от слова «никак». Завершая свою работу, Buildah-контейнер должен отправлять образ в реестр и уничтожать контент на хосте. Когда контейнерный образ будет собираться в следующий раз, его придется заново скачивать из реестра, поскольку на хосте к тому моменту уже ничего не останется.
Вариант 2. Если нужна производительность уровня Docker, то можно подмонтировать container/storage хоста прямо в контейнер.
# podman run -v ./build:/build:z -v /var/lib/containers:/var/lib/containers --security-opt label:disabled quay.io/buildah/stable buildah -t image2 bud /build
# podman run -v /var/lib/containers:/var/lib/containers --security-opt label:disabled quay.io/buildah/stable buildah push image2 registry.company.com/myuser
Безопасность. Это наименее безопасный способ сборки контейнеров, поскольку здесь контейнеру разрешено модифицировать хранилище на хосте, и потенциально он может подсунуть Podman’у или CRI-O вредоносный образ. Кроме того, потребуется отключить SELinux separation, чтобы находящиеся в Buildah-контейнере процессы могли взаимодействовать с хранилищем на хосте. Обратите внимание, что этот вариант все равно лучше Docker-сокета, поскольку контейнер блокируется оставшимися функциями безопасности и не может просто взять и запустить какой-нибудь контейнер на хосте.
Производительность. Здесь она максимальна, поскольку полностью задействуется кэширование. Если Podman или CRI-O уже успели скачать нужный образ на хост, то Buildah-процессу внутри контейнера не придется скачивать его заново, а последующие сборки на основе этого образа также смогут взять нужное из кэша.
Вариант 3. Суть этого способа в том, чтобы объединить несколько образов в один проект с общей папкой для контейнерных образов.
# mkdir /var/lib/project3
# podman run --security-opt label_level=s0:C100, C200 -v ./build:/build:z
-v /var/lib/project3:/var/lib/containers:Z quay.io/buildah/stable buildah -t image3 bud /build
# podman run --security-opt label_level=s0:C100, C200
-v /var/lib/project3:/var/lib/containers quay.io/buildah/stable buildah push image3 registry.company.com/myuser
В этом примере мы не удаляем папку проекта (/var/lib/project3) между запусками, поэтому все последующие сборки в рамках проекта пользуются преимуществами кэширования.
Безопасность. Нечто среднее между вариантами 1 и 2. С одной стороны, контейнеры не имеют доступа к контенту на хосте и, соответственно, не могут подсунуть что-то плохое в хранилище образов Podman/CRI-O. С другой стороны, в рамках своего проекта контейнер может вмешиваться в сборку других контейнеров.
Производительность. Здесь она хуже, чем при использовании общего кэша на уровне хоста, поскольку нельзя использовать образы, уже скаченные ранее средствами Podman/CRI-O. Однако после того, как Buildah скачает образ, этот образ можно использовать в любых последующих сборках в рамках проекта.
Дополнительные хранилища
У
Если прокрутить вверх и посмотреть Dockerfile, который мы используем для сборки образа quay.io/buildah/stable, то там есть такие строки:
# Adjust storage.conf to enable Fuse storage.
RUN sed -i -e 's|^#mount_program|mount_program|g' -e '/additionalimage.*/a "/var/lib/shared",' /etc/containers/storage.conf
RUN mkdir -p /var/lib/shared/overlay-images /var/lib/shared/overlay-layers; touch /var/lib/shared/overlay-images/images.lock; touch /var/lib/shared/overlay-layers/layers.lock
В первой строке мы модифицируем /etc/containers/storage.conf внутри контейнерного образа, говоря storage-драйверу использовать “additionalimagestores” в папке /var/lib/shared. А в следующей строке создаем общую папку и добавляем пару lock-файлов, чтобы не было ругани со стороны containers/storage. По сути, мы просто создаем пустое хранилище контейнерных образов.
Если смонтировать containers/storage уровнем выше этой папки, что Buildah сможет использовать образы.
Теперь вернемся к рассмотренному выше Варианту 2, когда Buildah-контейнер может читать и писать в containers/store на хостах и, соответственно, имеет максимальную производительности за счет кэширования образов на уровне Podman/CRI-O, но дает минимум безопасности, поскольку может писать прямо в хранилища. А теперь прикрутим сюда дополнительные хранилища и получим лучшее из двух миров.
# mkdir /var/lib/containers4
# podman run -v ./build:/build:z -v /var/lib/containers/storage:/var/lib/shared:ro -v /var/lib/containers4:/var/lib/containers:Z quay.io/buildah/stable
buildah -t image4 bud /build
# podman run -v /var/lib/containers/storage:/var/lib/shared:ro
-v >/var/lib/containers4:/var/lib/containers:Z quay.io/buildah/stable buildah push image4 registry.company.com/myuser
# rm -rf /var/lib/continers4
Обратите внимание, что /var/lib/containers/storage хоста смонтирована в /var/lib/shared внутри контейнера в режиме read-only. Поэтому работая в контейнере, Buildah может использовать любые образы, которые ранее уже были скачаны средствами Podman/CRI-O (привет, скорость), но писать при этом может только в свое собственное хранилище (привет, безопасность). Также обратите внимание, что это делается без отключения SELinux separation для контейнера.
Важный нюанс
Ни в коем случае не следует удалять никакие образы из нижележащего хранилища. В противном случае Buildah-контейнер может вылететь.
И это отнюдь не все преимущества
Возможности дополнительных хранилищ не ограничиваются только вышеописанным сценарием. Например, можно разместить все контейнерные образы в общем сетевом хранилище и дать к нему доступ всем Buildah-контейнерам. Допустим, у нас есть сотни образов, которые наша система CI/CD регулярно использует для сборки контейнерных образов. Концентрируем все эти образы на каком-то одном хосте-хранилище и затем, используя предпочтительные средства сетевого хранения (NFS, Gluster, Ceph, ISCSI, S3…), открываем общий доступ к этому хранилищу всем нодам Buildah или Kubernetes.
Теперь достаточно подмонтировать это сетевое хранилище в контейнер Buildah на /var/lib/shared и все – Buildah-контейнерам больше вообще не придется скачивать образы через pull. Таким образом мы выбрасываем фазу предварительного наполнения (pre-population) и сразу готовы выкатывать контейнеры.
И конечно же, это можно использовать в рамках действующей системы Kubernetes или контейнерной инфраструктуры, чтобы запускать и выполнять контейнеры где угодно без какого-либо скачивания образов через pull. Более того, реестр контейнеров, получая push-запрос на загрузку в него обновленного образа, может автоматически отправлять этот образ в общее сетевое хранилище, где он мгновенно становится доступен всем нодам.
Размеры контейнерных образов иногда могут достигать многих гигабайт. Функционал дополнительных хранилищ позволяет обойтись без клонирования таких образов по нодам и делает запуск контейнеров практически мгновенным.
Кроме того, в данный момент мы работаем над новой функцией overlay volume mounts, которая сделает сборку контейнеров еще быстрее.
Заключение
Выполнять Buildah внутри контейнера в среде Kubernetes/CRI-O, Podman или даже в Docker вполне реально, к тому же это просто и гораздо безопаснее, чем использовать docker.socket. Мы значительно повысили гибкость работы с образами, и теперь вы можете запускать их различными способами для оптимального баланса между безопасностью и производительностью.
Функционал дополнительных хранилищ позволяет ускорить или даже полностью устранить скачивание образов на ноды.
Источник: habr.com