Сборка контейнеров без Docker
Привет, Хабр. Очень много много сейчас слышно про Кубернетис и Docker. Про них не знает наверное, только ленивый. Но есть и другие варианты работы с контейнерами. Предлагаю перевод статьи одного энтузиаста, который решил изучить похожие инструменты.
Я хочу рассказать о том, как создавать контейнеры без использования Docker. Я буду использовать OpenFaaS, который использует образы контейнеров в формате OCI для своих рабочих нагрузок. Можно сказать, что OpenFaaS — это платформа CaaS для Kubernetes, которая способна запускать микросервисы и бесплатно добавлять FaaS и инструменты, управляющие событиями. Начнём с демонстрации того, как использовать встроенный buildkit для интерфейса командной строки Docker, потом опишем buildkit автономный (только в Linux), а затем — конструктор контейнеров Google, Kaniko.
А что не так с Docker?
Да всё с ним так. Он хорошо работает на armhf, arm64 и на x86_64. Основной интерфейс командной строки Docker стал намного больше, чем просто сборка/отправка/запуск, теперь он поставляется вместе с функциями Docker Swarm и EE.
Альтернативы Docker
Было несколько попыток вернуть Docker к его привычному виду, в который мы все влюбились.
Docker — теперь сам Docker использует containerd для запуска контейнеров и поддерживает сборку buildkit для создания высокоэффективных кэширующих сборок.
Сочетание Podman и buildah — детище RedHat/IBM, которые используют собственный набор инструментов OSS для генерации образов OCI. Podman позиционируется как не имеющий демонов и root-ов, но в конечном итоге ему приходится монтировать оверлейные файловые системы и использовать сокет UNIX.
Pouch — предлагаемый Alibaba инструмент считается «Эффективным контейнерным движком корпоративного класса». Он использует containerd так же, как Docker, и поддерживает изоляцию на уровне контейнера с помощью runc и «легкие виртуальные машины», такие как runV. Также больше внимания уделяется дистрибуции образов и изоляции.
Автономный BuildKit -buildkit был создан Тынисом Тийги из Docker Inc как новый конструктор контейнеров с учетом кеширования и параллелизма. buildkit в настоящее время работает только как демон, но вы можете увидеть, как люди утверждают обратное. Они разветвляют (fork) демона, а затем убивают его после сборки.
img — был написан Джессом Фрейзеллом и является оболочкой для buildkit. Однако он не так уж и крут по сравнению с другими вариантами. Проект активно разрабатывался до конца 2018 года, но потом обрёл лишь несколько исправлений. Есть мнение, что img имеет лучший пользовательский интерфейс buildctr, чем собственный интерфейс командной строки buildkit, но также следует отметить, что img выпущен только для x86_64 и не существует бинарников для armhf/arm64.
k3c —интересный эксперимент Rancher, который использует containerd и buildkit для воссоздания оригинального, тёплого лампового Docker. Включает компонент среды выполнения и вскоре планирует поддерживать архитектуры ARM.
Из всех вариантов мне больше всего нравится k3c, но он очень наивный и объединяет всё в один бинарник, который может конфликтовать с другим программным обеспечением, в настоящее время у него есть собственные бинарники containerd и buildkit.
Итак, поскольку мы изучаем «build» и хотим рассмотреть относительно стабильные варианты, давайте поговорим про:
Всё вышеперечисленное (да и многое другое) реально, так как OpenFaaS CLI умеет выводить стандартный «контекст сборки», с которым может работать любой конструктор
Создайте тестовое приложение
Начнём с HTTP Golang middleware, это нечто среднее между функцией и микросервисом, которое показывает, насколько универсальным может быть OpenFaaS.
faas-cli template store pull golang-middleware
faas-cli new --lang golang-middleware \
build-test --prefix=alexellis2
--lang
указывает шаблон сборки
build-test
— это имя функции
--prefix
имя пользователя Docker Hub, которое будет использоваться для загрузки нашего образа OCI.
У нас получится следующее:
./
├── build-test
│ └── handler.go
└── build-test.yml
1 directory, 2 files
Так выглядит обработчик, и его легко изменить. Дополнительные зависимости могут быть добавлены с помощью вендоринга или модулей Go.
package function
import (
"fmt"
"io/ioutil"
"net/http"
)
func Handle(w http.ResponseWriter, r *http.Request) {
var input []byte
if r.Body != nil {
defer r.Body.Close()
body, _ := ioutil.ReadAll(r.Body)
input = body
}
w.WriteHeader(http.StatusOK)
w.Write([]byte(fmt.Sprintf("Hello world, input was: %s", string(input))))
}
Сборка нормальным способом
Нормальными способом создания этого приложения может быть:
faas-cli build -f build-test.yml
Локальный кеш шаблона и Dockerfile также доступен по адресу
./template/golang-middleware/Dockerfile
В этот шаблон включены три образа:
FROM openfaas/of-watchdog:0.7.3 as watchdog
FROM golang:1.13-alpine3.11 as build
FROM alpine:3.12
В традиционном конструкторе каждый из образов будет загружен последовательно. Подождите несколько минут, и теперь у нас есть эти образы в нашей локальной библиотеке. Мы также можем отправить их в реестр с помощью faas-cli push -f build-test.yml
.
Сборка с помощью Buildkit и Docker
Это простейший вариант из всех возможных, да и сборка проходит быстро.
DOCKER_BUILDKIT=1 faas-cli build -f build-test.yml
При таком подходе демон Docker автоматически переключает свой сборщик на buildkit. Buildkit даёт ряд преимуществ:
Более сложное кеширование
Если возможно, сначала выполнить более поздние инструкции — например, загрузить образ «среды выполнения» до того, как сборка на уровне «sdk» будет завершена.
Сверхбыстрая сборка во второй раз
С помощью buildkit все базовые образы могут быть загружены в нашу локальную библиотеку сразу, поскольку команды FROM (загрузка) не выполняются последовательно.
С помощью buildkit все базовые образы могут быть загружены в нашу локальную библиотеку сразу, поскольку команды FROM (загрузка) не выполняются последовательно.
FROM openfaas/of-watchdog:0.7.3 as watchdog
FROM golang:1.13-alpine3.11 as build
FROM alpine:3.11
Этот параметр работает даже на Mac, поскольку buildkit проксируется через демон Docker, работающий на виртуальной машине.
Сборка с помощью автономного набора Buildkit
Для сборки с помощью автономного Buildkit нам нужно запустить buildkit отдельно на хосте Linux, поэтому мы не можем использовать Mac.
faas-cli build
обычно выполняет или разветвляет docker
, потому что команда — это просто оболочка. Итак, чтобы обойти это поведение, мы должны написать контекст сборки, что возможно с помощью следующей команды:
faas-cli build -f build-test.yml --shrinkwrap
[0] > Building build-test.
Clearing temporary build folder: ./build/build-test/
Preparing ./build-test/ ./build/build-test//function
Building: alexellis2/build-test:latest with golang-middleware template. Please wait..
build-test shrink-wrapped to ./build/build-test/
[0] < Building build-test done in 0.00s.
[0] Worker done.
Total build time: 0.00
Наш контекст теперь доступен в ./build/build-test/
папке с нашим кодом функции и шаблоном с его точкой входа и Dockerfile.
./build/build-test/
├── Dockerfile
├── function
│ └── handler.go
├── go.mod
├── main.go
└── template.yml
1 directory, 5 files
Теперь нам нужно запустить buildkit.
curl -sSLf https://github.com/moby/buildkit/releases/download/v0.6.3/buildkit-v0.6.3.linux-amd64.tar.gz | sudo tar -xz -C /usr/local/bin/ --strip-components=1
Если вы заглянете на страницу релизов, вы также найдете buildkit, доступный для armhf и arm64, и отлично подходящий для мультиархитектуры.
Запустите демон buildkit в новом окне:
sudo buildkitd
WARN[0000] using host network as the default
INFO[0000] found worker "l1ltft74h0ek1718gitwghjxy", labels=map[org.mobyproject.buildkit.worker.executor:oci org.mobyproject.buildkit.worker.hostname:nuc org.mobyproject.buildkit.worker.snapshotter:overlayfs], platforms=[linux/amd64 linux/386]
WARN[0000] skipping containerd worker, as "/run/containerd/containerd.sock" does not exist
INFO[0000] found 1 workers, default="l1ltft74h0ek1718gitwghjxy"
WARN[0000] currently, only the default worker can be used.
INFO[0000] running server on /run/buildkit/buildkitd.sock
Начнём сборку, передав упакованное в термоусадочный материал местоположение в качестве контекста сборки. Нам нужна buildctl
. Команда buildctl — это клиент для демона, который фиксирует, как построить образ и что делать, когда это будет сделано, например, экспорт tar, игнорирование сборки или отправка её в реестр.
buildctl build --help
NAME:
buildctl build - build
USAGE:
To build and push an image using Dockerfile:
$ buildctl build --frontend dockerfile.v0 --opt target=foo --opt build-arg:foo=bar --local context=. --local dockerfile=. --output type=image,name=docker.io/username/image,push=true
OPTIONS:
--output value, -o value Define exports for build result, e.g. --output type=image,name=docker.io/username/image,push=true
--progress value Set type of progress (auto, plain, tty). Use plain to show container output (default: "auto")
--trace value Path to trace file. Defaults to no tracing.
--local value Allow build access to the local directory
--frontend value Define frontend used for build
--opt value Define custom options for frontend, e.g. --opt target=foo --opt build-arg:foo=bar
--no-cache Disable cache for all the vertices
--export-cache value Export build cache, e.g. --export-cache type=registry,ref=example.com/foo/bar, or --export-cache type=local,dest=path/to/dir
--import-cache value Import build cache, e.g. --import-cache type=registry,ref=example.com/foo/bar, or --import-cache type=local,src=path/to/dir
--secret value Secret value exposed to the build. Format id=secretname,src=filepath
--allow value Allow extra privileged entitlement, e.g. network.host, security.insecure
--ssh value Allow forwarding SSH agent to the builder. Format default|[=|[,]]
Вот что я сделал, чтобы получить эквивалент команды Docker с DOCKER_BUILDKIT
переопределением:
sudo -E buildctl build --frontend dockerfile.v0 \
--local context=./build/build-test/ \
--local dockerfile=./build/build-test/ \
--output type=image,name=docker.io/alexellis2/build-test:latest,push=true
Перед запуском этой команды вам необходимо запустить docker login
или создать $HOME/.docker/config.json`
с действительным набором незашифрованных учётных данных.
Вы увидите красивую анимацию ASCII для этой сборки.
Сборка с img и buildkit
Поскольку я ранее никогда не использовал img и не слышал о том, чтобы его использовали с опциями по сравнению с более распространенными вариантами, я решил познакомиться с ним.
Первые впечатления таковы, что мультиархитектура здесь не является приоритетом и, учитывая возраст проекта, кардинальные изменения маловероятны. Для armhf или ARM64 нет бинарников.
Для x86_64последней версии v0.5.7от 7 мая 2019, сборка с Go 1.11, с 1.13 Go является актуальной версией:
sudo curl -fSL "https://github.com/genuinetools/img/releases/download/v0.5.7/img-linux-amd64" -o "/usr/local/bin/img" \
&& sudo chmod a+x "/usr/local/bin/img"
Параметры сборки выглядят как подмножество buildctl
:
img build --help
Usage: img build [OPTIONS] PATH
Build an image from a Dockerfile.
Flags:
-b, --backend backend for snapshots ([auto native overlayfs]) (default: auto)
--build-arg Set build-time variables (default: [])
-d, --debug enable debug logging (default: false)
-f, --file Name of the Dockerfile (Default is 'PATH/Dockerfile') (default: )
--label Set metadata for an image (default: [])
--no-cache Do not use cache when building the image (default: false)
--no-console Use non-console progress UI (default: false)
--platform Set platforms for which the image should be built (default: [])
-s, --state directory to hold the global state (default: /home/alex/.local/share/img)
-t, --tag Name and optionally a tag in the 'name:tag' format (default: [])
--target Set the target build stage to build (default: )
Вот что нам нужно для сборки:
sudo img build -f ./build/build-test/Dockerfile -t alexellis2/build-test:latest ./build/build-test/
По тем или иным причинам img
фактически не удалось сделать успешную сборку. Это может быть связано с некоторыми изменениями, необходимыми для попытки запустить приложение без root-прав.
fatal error: unexpected signal during runtime execution
[signal SIGSEGV: segmentation violation code=0x1 addr=0xe5 pc=0x7f84d067c420]
runtime stack:
runtime.throw(0xfa127f, 0x2a)
/home/travis/.gimme/versions/go1.11.10.linux.amd64/src/runtime/panic.go:608 +0x72
runtime.sigpanic()
/home/travis/.gimme/versions/go1.11.10.linux.amd64/src/runtime/signal_unix.go:374 +0x2f2
goroutine 529 [syscall]:
runtime.cgocall(0xc9d980, 0xc00072d7d8, 0x29)
/home/travis/.gimme/versions/go1.11.10.linux.amd64/src/runtime/cgocall.go:128 +0x5e fp=0xc00072d7a0 sp=0xc00072d768 pc=0x4039ee
os/user._Cfunc_mygetgrgid_r(0x2a, 0xc000232260, 0x7f84a40008c0, 0x400, 0xc0004ba198, 0xc000000000)
Сборка с Kaniko
Kaniko — это конструктор контейнеров Google, который нацелен на сборку контейнеров в песочнице. Вы можете использовать его как одноразовый контейнер или как отдельный бинарник.
Я взглянул на сборку в этом блоге:
docker run -v $PWD/build/build-test:/workspace \
-v ~/.docker/config.json:/kaniko/config.json \
--env DOCKER_CONFIG=/kaniko \
gcr.io/kaniko-project/executor:latest \
-d alexellis2/build-test:latest
Флаг
–d
указывает, куда нужно поместить изображение после успешной сборки.-v
Флаг привязки монтажа текущего каталога в контейнер Kaniko, он также добавляет свойconfig.json
файл для пуша к удалённому реестру.
В Kaniko есть некоторая поддержка кеширования, но для этого требуется ручное управление и сохранение, поскольку Kaniko работает в one-shot режиме, а не с демоном, как Buildkit.
Итоги тестирования
Докер — традиционный. Установка Docker может оказаться сложной задачей и нагрузить вашу систему больше, чем ожидалось. Продукт самый старый и медленный, но выполняет свою работу. Следите за сетевым мостом, установленным Docker, он может конфликтовать с другими частными сетями, использующими тот же диапазон частных IP-адресов.
Докер с buildkit. Это самый быстрый вариант с наименьшим количеством оттока или изменений. Включается префиксом команды
DOCKER_BUILDKIT=1
Автономный buildkit. Этот вариант отлично подходит для сборки в кластере или для системы, в которой не требуется Docker, например для CI box или runner. Ему нужен хост Linux, и нет хорошего опыта использования его в MacOS. Возможно, путем запуска дополнительной виртуальной машины или хоста и доступа поверх TCP?
Я также хотел бы упомянуть сравнение инструментов создания образов контейнеров следующего поколения. Это лучший вариант для пользователей faasd, где пользователи полагаются только на containerd и CNI, а не на Docker или Kubernetes.
Kaniko. То, как мы использовали Kaniko, по-прежнему требовало установки Docker, но процесс выглядел немного иначе.
Подведение итогов
Вы можете использовать свой обычный конструктор контейнеров с OpenFaaS или faas-cli build –shrinkwrap
или передать контекст сборки предпочитаемому инструменту. Вот примеры инструментов для создания контейнеров OpenFaaS:
В облаке OpenFaaS мы обеспечиваем полную автоматизацию CI/CD с использованием подхода shrinkwrap и демона buildkit. Всем остальным пользователям я бы рекомендовал использовать Docker или Docker с buildkit.
У пользователей faasd на хосте установлен только containerd docker, поэтому лучшим вариантом для них является загрузка buildkit.
Мы не затронули одну из важных частей рабочего процесса — развёртывание. Любой контейнер OCI может быть развернут в плоскости управления OpenFaaS поверх Kubernetes, если он соответствует определению бессерверной рабочей нагрузки.