Рекомендації щодо запуску Buildah усередині контейнера

У чому привабливість поділу середовища виконання контейнерів на окремі інструментальні складові? Зокрема, в тому, що ці інструменти можна почати комбінувати, щоб вони захищали один одного.

Рекомендації щодо запуску Buildah усередині контейнера

Багатьох приваблює ідея виконувати складання контейнерних OCI-образів у рамках Кубернетес чи подібної системи. Допустимо, у нас є CI/CD, яка постійно збирає образи, тоді щось типу Red Hat OpenShift/Kubernetes було б дуже корисним з точки зору розподілу навантаження при складанні. Донедавна більшість людей просто давали контейнерам доступ до Docker-сокету і дозволяли виконувати команду docker build. Ми вже кілька років тому показували, що це дуже небезпечно, фактично, це навіть гірше, ніж давати безпарольний root або sudo.

Тому люди постійно намагаються запускати Buildah у контейнері. Коротше, ми створили приклад того, як, на наш погляд, найкраще запускати Buildah всередині контейнера, і виклали відповідні образи на quay.io/buildah. Приступимо…

Налаштування

Ці образи зібрані з Dockerfiles, які можна знайти в репозиторії Buildah у папці buildahimage.
Тут ми розглянемо стабільну версію Dockerfile.

# 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-ядра хоста, ми використовуємо всередині контейнера програму fuse-overlay, оскільки на даний момент OverlayFS може виконувати монтування тільки якщо дати їй повноваження SYS_ADMIN засобами Linux capabilities. А ми хочемо запускати свої Buildah-контейнери без будь-яких привілеїв рівня root. Fuse-overlay працює досить швидко і за продуктивністю краще, ніж storage-драйвер VFS. Зверніть увагу, що під час запуску Buildah-контейнера, який використовує Fuse, потрібно надати пристрій /dev/fuse.

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

Далі ми створюємо каталог додаткових сховищ. Container/storage підтримує концепцію підключення додаткових read-only сховищ образів. Наприклад, можна налаштувати overlay storage area на одній машині, а потім за допомогою NFS підмонтувати це сховище на іншій машині і використовувати образи з нього без завантаження через pull. Нам це сховище потрібно для того, щоб мати можливість підключити як том якесь сховище образів з хоста і використовувати його всередині контейнера.

# 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.

Продуктивність. А ось продуктивність тут мінімальна, оскільки будь-які образи із контейнерних реєстрів щоразу копіюються на хост, і кешування не працює від слова «ніяк». Завершуючи свою роботу, 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 скачає образ, цей образ можна використовувати у будь-яких подальших збірках у рамках проекту.

Додаткові сховища

У containers/storage є така класна штука як додаткові сховища (additional stores), завдяки якій при запуску та збиранні контейнерів контейнерні двигуни можуть використовувати зовнішні сховища образів у режимі read-only оверлею. По суті, до файлу storage.conf можна додати одне або кілька сховищ «тільки для читання», щоб потім при запуску контейнера контейнерний двигун шукав у них потрібний образ. Причому він завантажуватиме образ із реєстру лише в тому випадку, якщо не знайде його в жодному з цих сховищ. Контейнерний двигун зможе писати тільки в доступні для запису сховища.

Якщо прокрутити вгору і подивитися 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

Додати коментар або відгук