docker buildx
Quick Start
docker buildx create --name builder --platform linux/amd64,linux/arm64 --use
docker buildx build --push -t robota.azurecr.io/mactemp .
docker buildx rm builderTLDR
Dockerfile
FROM nginx:alpine
COPY index.html /usr/share/nginx/html/index.htmlСборка as is (то что мы обычно делаем):
docker build -t mactemp .Сборка под конкретную платформу:
docker build --platform=linux/amd64 -t mactemp .Сборка и публикация под множество платформ сразу:
docker buildx create --name builder --platform linux/amd64,linux/arm64 --use
docker buildx build --push -t robota.azurecr.io/mactemp .
docker buildx rm builderСборка под конкретную платформу средствами buildx (аля то же самое что и сборка as is)
docker buildx create --name builder --use
docker buildx build --load --platform linux/amd64 -t robota.azurecr.io/mactemp .
docker buildx rm builderДальше, заметка с деталями, что, как, почему…
Проблема: частенько, пытаясь разобраться в какой либо поломке, делаешь docker run my-awesome-service и сталкиваешься с image was build for amd64 planform, и приехали, это нужно или искать тот же коммит и собирать образ заново, не факт что это будет то же самое что и в проде и в целом на ровном месте целое приключение
Хотелка: было бы круто, если бы образ собирался сразу под amd64 и arm64, что бы его можно было гонять и в кубере, и локально на маке
Как это работает?
Имя следующий Dockerfile:
FROM nginx:alpine
COPY index.html /usr/share/nginx/html/index.htmlМы можем собрать его под свою платформу вот так:
docker build -t mactemp1 .Или под целевую платформу вот так:
docker build --platform=linux/amd64 -t mactemp2 .Но при этом это два отдельных образа
И вот что интересно, если посмотреть на страничку docker hub для того же nginx, увидим что образ поддерживает сразу несколько платформ
Но при этом, вытягивая образ и делая инспецию видим следующее:
Примечание: в docker hub образ числиться как поддерживающий сразу N платформ, при этом вытянув образ локально - мы видим arm64
Два ключевых момента, которые расставляют все по своим местам:
docker engine(который локальный docker) поддерживает и работает с одной платформой, она может быть разной, но не может быть больше одной платформы одновременноdocker registry(который docker hub, azure container registry, …) позволяют под одним и тем же URL хранить N образов под разными платформамиdocker pullвытягивает образ подходящий под текущую платформу, но позволяет передавать флаг--platform=linux/amd64docker pullне может вытянуть образа под все платформы, т.к. в локальном docker engine они по сути перетрут друг друга - docker engine не умеет держать образ сразу под несколько платформ - каждая платформа это отдельный образ и единственный способ иметь их локально и не перетерать - это хранить их под разными именамиdocker registryпо сути выступает сахаром\плюшкой которая за для добства держит образа, собранные под разные платформы в одном флаке, но по сути это эдакие alias/links/references
Публикация образа
Описаное выше поясняет как работает read операции вокруг этого дела, но вопрос - как быть с create/update - как собрать и опубликовать образ, ведь исходя из того что описано выше мы могли бы сделать одно из двух:
docker build --platform=linux/amd64 -t mactemp .
docker publish mactemp
docker build --platform=linux/arm64 -t mactemp .
docker publish mactemp # will override previous imageили вот так:
docker build --platform=linux/amd64 -t mactemp1 .
docker publish mactemp1
docker build --platform=linux/arm64 -t mactemp2 .
docker publish mactemp2 # different imagesВот тут на сцену и выходит поделка buildkit и его плагин для docker - buildx
BuildX это в какой то мере docker compose - поделка позволяющая упросить рутину
Под капотом эта штука хэндлит тот факт что в docker engine нельзя иметь образ сразу под несколькоми платформами, собирает разные образа и публикует их в кучу в container registry
Пожалуй самое ключевое отличие которое нужно осознать и которое поможет понять что и как происходит дальше - образа собираются в отдельном контейнере и не доступны локально
builders
посмотреть список доступных сборщиков можно вот так:
docker buildx lsбудет показана табличка со списком доступных сборщиков, выбранный будет помечен звездочкой
переключаться между ними можно вот так:
docker buildx use [name]проинспектировать сборщик можно вот так
docker buildx inspect # will inspect current/default builder
docker buildx inspect [name]вот эти инспекции и вот это все нам не понадобяться и тут скорее для справки, см. дальше
В зависимости от используемого софта (colima, docker for mac, …) список сборщиков может уже содержать какие то предзаготовленные сборщики
Для чистоты экспериментов, создаем свой сборщик, что бы явно понимать что/куда/как и попытаться разобраться как оно работает
docker buildx create --name my --use --platform linux/amd64,linux/arm64Примечания:
- флаг
--useпереключает buildx на новосоздаваемый сборщик, без него, после создания нужно переключиться руками вот такdocker buildx use my - опционально можно передать флаг
--driver docker-container- это значение по умолчанию, говорит о том что сборка будет происходить в отдельном контейнере, альтернативно можно передать--driver docker- это по сути тоже самое что собирать локально докером, то есть по сути без плюшек buildx - по умолчанию buildx будет собирать образа под все под что только сможет, ограничить его можно передав флаг
--platform linux/amd64,linux/arm64
Теперь пожалуй самое важное - стоит выполнить:
docker buildx ls
docker psМы должны будем не только увидеть новый сборщик в первой команде, но и бегущий контейнер во второй
В этом ключевое отличие buildx - он собирает образа внутри контейнера, как следствие, они, не доступны локально - то есть собрав образ через buildx его нельзя будет запустить - и вот этот момент трохи взрывает мозг
publish
Если нельзя использовать собранные образа то в чем прикол, зачем это надо и как это дело используется? 🤔
Предполагается что:
- За для локальных экспериментов, все это нафиг не нужно, условно, если мне нужно собирать и запускать образ локально, мне нафиг не нужно куча платформ, мне нужна моя платформа, и я могу просто сделать
docker build -t mactemp .и все - В случае сборки под мультиплатформы, нам опять же локально оно не нужно, т.к. докер так не умеет и как следствие использование buildx предпологает сборку+публикацию в один шаг, а именно:
docker buildx build --push -t robota.azurecr.io/mactemp .Профит в том что на деле - оно не такое страшное и ужастное, просто трохи не привычное, но это очень быстро проходит после сборки пары образов начинаешь въезжать в это дело
cleanup
После сборки и публикации образа, в зависимости от обстоятельств мы можем хотеть почистить за собой, ведь контейнер сборщика все так же работает
Тут два варианта:
stop - стопнуть контейнер сборщика, он автоматически запуститься при следующей сборке
docker buildx stop myremove - удалить сборщик, соотв освободиться дисковое пространство, но при следующем использовании будет нужно его заново создать
docker buildx rm myРаз уж упомянуто дисковое пространство стоит отметить что образа собираются внутри контейнера и не доступны снаружи, по чуть чуть там нособирается куча кеша, слоев, базовых образов и вот этого всего и весь прикол в том что привычные docker system prune нифига их не почистят, т.к. просто напросто не знают о них
Для этого есть две вот такие команды:
docker buildx prune # remove build cache (aka docker system prune)
docker buildx du # disk usage (aka docker system df)github actions
В github actions нам необходимы следующие prerequisites
- uses: docker/setup-qemu-action@v3
- uses: docker/setup-buildx-action@v3где:
qemu- утилита для виртуализации, с помощью нее создаются виртуалки под разные платформыbuildx- убедиться что установлен плагин и доступна команда docker buildx
Так же, нам необходимо авторизировать docker в целевом container registry, это out of scope этой заметки, но в самом просто случае, если использовать старые добрые username и password это может быть похожим на:
- uses: docker/login-action@v3
with:
registry: mac.azurecr.io
username: mac
password: abracadabraИ по большому счету, это все, на деле, предзаготовленный сборщик уже готов к работе и можно запускать публикацию и сборку as easy as:
- run: docker buildx build --push --platform linux/amd64,linux/arm64 -t robota.azurecr.io/mactemp:latest .То есть тут даже не нужны никакие супер хитрые action'ы и\или настройки, так же, можно не париться с зачисткой сборщиков, т.к. сам action эфимерен и удалиться послершения
По мимо этого можно встретить docker/build-push-action:
- uses: docker/build-push-action@v6
with:
context: "." # optional, context path
file: "Dockerfile" # optional, path to docker file
platforms: linux/amd64,linux/arm64 # optional, platforms
push: true # without push, this step is "useless", at least to check if it builds at all
tags: "robota.azurecr.io/mactemp:latest" # image tag
labels: |
org.opencontainers.image.title=mactemp
org.opencontainers.image.source=https://github.com/rabotaua/mactemp
org.opencontainers.image.version=1.2.0
org.opencontainers.image.revision=192a4d4
org.opencontainers.image.vendor=robota.uaazure container registry
В ажурном портале, если провалиться в детали образа, в его манифесте должно будет быть что то типа такого
На скриншоте показан кусок манифеста в котором перечисляются платформы под которые собирался образ, в нашем случае искомые amd64 и arm64