Docker: Pečeme obraz pro více platforem

Publikováno: 12.9.2023

Docker v několika posledních verzích přinesl nástroje, které zásadně vylepšují proces tvorby Docker obrazů pro více platforem. Pojďme se podívat, jak s nimi jednoduše pracovat.

Celý článek

V dnešní době je poměrně běžné, že jedna aplikace dokáže běžet v různých prostředích. Na serverech s Linuxem, lokálně u vývojáře s Windows nebo macOS. Jednou na PC pod platformou Intel, jindy na Macu s Apple Silicon, či dokonce na na Raspberry Pi. Dost často se jedná o Docker obraz, který si pustíte pomocí docker run a vlastně ani nepřemýšlíte, jak to je vše zařízené.

Sestavit fungující docker image pro více platforem není triviální. Docker nám dříve nabízel pouze docker build a ten dokázal sestavit image jen pro aktuální platformu. Člověk se tak nevyhnul sestavování image několikrát.

V několika posledních verzích Docker Engine přibyla celá řada nástrojů, které nám sestavování multiplatformních obrazů značně usnadňují. V tomto článku se podíváme na to, jak s těmito nástroji pracovat.

Kdo je kdo?

Nástroje pro multiplatformní build jsou vlastně jen dva Buildx a Buildkit. Přičemž Buildx není jen jeden nástroj, ale spíš celá skupina command line nástrojů, určených pro efektivní tvorbu a správu Docker obrazů.

  • Buildx je rozšíření Dockeru, které umožňuje paralelní a cross-platform sestavování Docker obrazů. Díky tomu lze využít výkon vícejádrových procesorů a vytvořit obraz například pro Linux i Windows najednou. Buildx je od verze 19.03 součástí Dockeru, zatím ve formě experimentální funkce.
  • Buildkit je nový způsob, jakým Docker pod kapotou sestavuje kontejnery. Přináší vylepšení v podobě efektivnějšího využití mezipaměti, detailnějších logů při buildu a celkově bezpečnějšího procesu sestavení díky lepší izolaci. Buildkit je rovněž součástí Dockeru od verze 18.06.

Buildx využívá výhod Buildkitu pro paralelní sestavování Docker obrazů pro více platforem. Buildkit postupně nahradí stávající build engine Dockeru. Použití Buildx a Buildkitu obecně výrazně zrychluje a zjednodušuje práci s Dockerem.

Jdeme na příklad

Nemá smysl se příliš věnovat další teorii. Raději se podíváme na příklad, jak sestavit Docker obraz pro více platforem. Představte si jednoduchou aplikaci, která se skládá z jednoduchého webového serveru napsaného v Nodejs:

const express = require('express')
const app = express()

app.get('/', (req, res) => {
  res.send('Hello World!')
})

app.listen(3000, () => {
  console.log('Listening on port 3000...')
})

Tuto aplikaci potřebujeme spustit na svém počítači s macOS a Apple Silicon (M1) a na serveru s Intel procesorem. Součásti kódu je samozřejmě package.json s definicí závislostí. Zdrojové kódy máme uloženy na GitHub a při sestavování obrazu si je přejeme stáhnout a přibalit do výsledného obrazu.

# syntax=docker/dockerfile:experimental
FROM debian:bookworm-slim as src
RUN --mount=type=ssh \
    apt update -y \
    && apt -y install openssh-client git \
    && install -m 0700 -d ~/.ssh \
    && ssh-keyscan github.com >> ~/.ssh/known_hosts \
    && git clone --single-branch --branch main git@github.com:web/app.git /app

FROM node:slim as build
WORKDIR /app
# kopírování zdrojových kódů
COPY --from=src /app /app
RUN npm install --production

FROM node:slim
WORKDIR /app
COPY --from=build /app /app
EXPOSE 3000
CMD npm start

Tento Dockerfile je poměrně jednoduchý multi-stage build. V první fázi src dojde ke stažení zdrojových kódů aplikace z GitHub. Dále se provede instalace závislostí pomocí npm install. Kompletní zdrojové kódy jsou předýny do poslední fáze, kde se provede samostatné spuštění aplikace. Multi-stage build má tu výhodu, že výsledný obraz je menší, protože neobsahuje zbytečné závislosti (např. git) a často bývá přehlednější.

Jedinou neobvyklou věcí je použití --mount=type=ssh. Tento parametr říká, že při sestavování obrazu chceme použít SSH klíč, který je uložený v našem hostitelském systému. Tímto způsobem můžeme přistupovat k privátním repozitářům na githubu aniž bychom museli do obrazu ukládat SSH klíč. Tato funkce je stále ve fázi experimentální, proto je potřeba přidat na začátek Dockerfile:

# syntax=docker/dockerfile:experimental

Více o tomto parametru se můžete dočíst v dokumentaci. Poslední a nezbytnou podmínkou je samozřejmě to, že výchozí obraz node:slim a debian:bookworm-slim jsou již dostupné pro všechny platformy, pro které chceme náš multiobraz sestavit.

Sestavování multiobrazu

V úvodu jsem zmínil o tom, že buildx není jen jeden nástroj, ale spíše balík nástrojů. Součástí balíku je příkaz buildx bake sloužící k „pečení” nových obrazů.

Bake umí na základě konfiguračního souboru bakefile sestavit požadovaný obraz. Je to něco podobného jako docker compose, ale místo spouštění kontejnerů spouští sestavování obrazů.

Výhodou je, že všechny obrazy se sestaví paralelně a mezikroky jsou uloženy v cache. Díky tomu se pak při opakovaném sestavování obrazů může velká část buildu přeskočit a celý proces je výrazně rychlejší.

Soubor bakefile podporuje několik formátů, my si ukážeme HCL (HashiCorp Configuration Language) formát, ale stejně dobře bychom mohli použít YAML nebo JSON.

target "app" {
  context   = "./app"
  platforms = ["linux/amd64", "linux/arm64"]
  ssh       = ["default"]
  pull      = true
  tags      = [
    "muj.docker.repository/web/app:latest",
    "muj.docker.repository/web/app:bookworm"
  ]
}

Pojďme si projít jednotlivé parametry:

  • context – adresář, ve kterém se nachází Dockerfile
  • platforms – seznam platforem, pro které chceme obraz sestavit
  • ssh – seznam SSH klíčů, které chceme použít při sestavování, default znamená použít klíč z naší domovské složky ~/.ssh
  • pull – před sestavováním obrazu stáhnout změny z repozitáře
  • tags – seznam tagů, které chceme při sestavování obrazu použít

Bakefile podporuje celou řadu dalších parametrů, ale tyto nám budou pro náš příklad stačit. Nejdůležitější je samozřejmě parametr platforms, který říká, pro jaké platformy chceme obraz sestavit. V našem případě chceme sestavit obraz pro Linux s procesorem Intel linux/amd64 a pro systém linux/arm64, což odpovídá MacBook s Apple Silicon (M1, M2, …) nebo Raspberry Pi.

Výsledný sestavený obraz bude mít tag web/app:latest a web/app:bookworm a nahrajeme jej do vlastního repozitáře (muj.docker.repository). Sestavení obrazu spustíme pomocí příkazu:

docker buildx bake app

Pokud budeme chtít výsledné obrazy rovnou odeslat do našeho repository, musíme přidat parametr --push. Případně se může hodit parametr --no-cache, který sestaví obrazy bez použití cache.

docker buildx bake app --push --no-cache

No a co Windows?

Podpora pro Windows není zatím stoprocentní. Buildx sice umožňuje sestavit Windows obraz (windows/amd64 a windows/arm64) pod jiným operačním systémem, ale pouze pokud nejsou v Dockerfile použity příkazy systému Windows (žádné příkazy RUN ve fázi Windows souboru Docker). Všechny ostatní instrukce lze používat jako obvykle. Stále je tedy lepší sestavovat Windows obrazy přímo na Windows.

Závěrem

Viděli jsme, že sestavení multiplatformního Docker obrazu není nakonec tak složité, jak by se na první pohled mohlo zdát. Klíčové je použít správné nástroje.

S pomocí nástroje Buildx bake můžeme velmi jednoduše nadefinovat cílové platformy a BuildKit se postará o paralelní sestavení obrazu pro všechny z nich. Obrazy jsou ukládány do meziúložiště (cache), takže při dalším sestavování již hotové části znovu nevytváří a urychluje proces.

Tohle byl pouze lehký úvod do problematiky. Pokud vás toto téma zaujalo, doporučuji se podívat na oficiální dokumentaci.

Nahoru
Tento web používá k poskytování služeb a analýze návštěvnosti soubory cookie. Používáním tohoto webu s tímto souhlasíte. Další informace