Docker ile arm64 ve amd64 için tek imaj üretmek istiyorsanız derleme komutuna --platform linux/amd64,linux/arm64 eklersiniz; BuildKit her platform için ayrı bir varyant derler ve bunları tek bir imaj adı altında toplar. Bunun çalışması için imaj deposunun çok platformlu imajları desteklemesi gerekir: Docker Engine 29 ve sonrasının yeni kurulumlarında varsayılan olan containerd image store bunu doğrudan destekler, eski kurulumlarda ise ya bu depoyu açarsınız ya da docker-container driver'lı bir builder kullanırsınız.

Bu yazıda çok platformlu imajın yapısını, ön koşulları, belgelerde anlatılan üç stratejiyi (QEMU emülasyonu, birden fazla yerel düğüm, cross-compilation), önceden tanımlı platform argümanlarını ve Compose dosyasındaki platforms alanını ele alıyoruz. Builder ve driver kavramları için önce BuildKit ve Buildx yazısına, konu haritası için Docker öğrenme rehberi sayfasına bakabilirsiniz.

Neden çok platformlu imaj gerekir?

Neden çok platformlu imaj gerekir?

Container'lar ana makinenin çekirdeğini paylaşır. Belgelerin vurguladığı sonuç şudur: container içinde çalışan kodun ana makinenin mimarisiyle uyumlu olması gerekir. Bu yüzden emülasyon olmadan arm64 bir ana makinede linux/amd64 container çalıştıramazsınız, Linux ana makinede de Windows container çalıştıramazsınız. Pratikte bu, x86-64 bir geliştirme makinesinde derlediğiniz imajın ARM tabanlı bir bulut sunucusunda ya da Raspberry Pi'de açılmaması anlamına gelir.

Çok platformlu derleme, aynı uygulamanın birden fazla varyantını tek bir imajda paketleyerek bu sorunu çözer. Böylece aynı imaj adı x86-64 sunucuda da ARM sunucuda da emülasyona gerek kalmadan çalışır.

Tek platformlu ve çok platformlu imajın yapısı

Belgelere göre tek platformlu bir imaj, tek bir yapılandırmaya ve tek bir katman kümesine işaret eden tek bir manifest içerir. Çok platformlu imaj ise bir manifest list içerir; bu liste birden fazla manifest'e, her manifest de kendi yapılandırmasına ve katman kümesine işaret eder. Böyle bir imajı registry'ye gönderdiğinizde registry manifest list'i ve tüm manifest'leri saklar. İmajı çektiğinizde registry manifest list'i döndürür ve Docker ana makinenin mimarisine uygun varyantı otomatik seçer. Belgelerdeki örnekte ARM tabanlı Raspberry Pi linux/arm64 varyantını, x86-64 dizüstü bilgisayar ise linux/amd64 varyantını alır.

Ön koşul: manifest list destekleyen imaj deposu

Çok platformlu imajlar, manifest list destekleyen bir imaj deposu gerektirir. Docker Desktop ve Docker Engine 29.0 ve sonrası varsayılan olarak containerd image store kullanır; belgeler bu sürümlerde ek kurulum gerekmediğini söyler. Burada bir ayrıntı önemlidir: Engine 29 sürüm notlarına göre containerd image store yalnızca yeni (fresh) kurulumlarda varsayılandır ve userns-remap yapılandırılmış daemon'larda kullanılamaz. Eski bir sürümden yükselttiyseniz daemon, siz açana kadar klasik storage driver'larla (overlay2) çalışmaya devam eder.

Bu durumda belgeler iki yol sunar. Birincisi containerd image store'u daemon yapılandırma dosyasından açmaktır:

json
{
  "features": {
    "containerd-snapshotter": true
  }
}

Bu değişiklik /etc/docker/daemon.json dosyasına yazılır ve daemon yeniden başlatılır. Belgeler bir uyarı ekler: depolama arka ucunu değiştirmek, diğer arka uçla oluşturulmuş imaj ve container'ları geçici olarak gizler; veriler diskte kalır ama görünmez olur. Ayrıca containerd image store aynı imajlar için daha fazla disk kullanır, çünkü katmanları hem sıkıştırılmış hem açılmış hâlde tutar. Geçişin ayrıntıları ve disk konusu containerd image store yazısında ele alınıyor.

İkinci yol, docker-container driver'ı kullanan özel bir builder oluşturmaktır. Bu driver çok platformlu derlemeyi destekler, ancak sonuç Docker Engine imaj deposuna yüklenmez; imajı --push ile doğrudan bir registry'ye göndermeniz gerekir.

bash
docker buildx create \
  --name container-builder \
  --driver docker-container \
  --bootstrap --use

Temel komut: --platform

Derlemeyi tetiklerken hedef platformları --platform bayrağıyla virgülle ayırarak verirsiniz:

bash
docker buildx build --platform linux/amd64,linux/arm64 .

Belgelerdeki en basit örnek, container'ın mimarisini bir dosyaya yazan iki satırlık bir Dockerfile'dır. Bu imaj iki platform için derlenip çalıştırıldığında, x86-64 makinede dosyada x86_64, ARM makinede aarch64 değeri görünür:

dockerfile
# syntax=docker/dockerfile:1
FROM alpine
RUN uname -m > /arch
bash
docker build --platform linux/amd64,linux/arm64 -t multi-platform .
docker run --rm multi-platform cat /arch

Bu örnek için belgelerin ön koşulu Docker Desktop ya da QEMU kurulu bir Docker Engine'dir; çünkü yabancı mimarideki RUN adımı emülasyonla çalışır. Bir sonraki bölüm bu stratejiyi ve alternatiflerini anlatıyor.

Üç strateji: QEMU, yerel düğümler, cross-compilation

Üç strateji: QEMU, yerel düğümler, cross-compilation: Linux çekirdeği 4.8 veya üstü, binfmt-support 2.1.7 veya üstü, QEMU ikili dosyalarının statik derlenmiş olması ve fix_binary...
StratejiDockerfile değişikliğiHızKurulum yükü
QEMU emülasyonuGerekmezDerleme ve sıkıştırma gibi işlerde çok yavaş olabilirDüşük; resmî BuildKit sürümleri QEMU içerir
Birden fazla yerel düğümGerekmezYerel hızdaYüksek; her mimari için ayrı makine ve builder yönetimi
Cross-compilationGerekir (platform argümanları)Yerel hızdaDilin cross-compile desteğine bağlı

1. QEMU ile emülasyon

Builder'ınız destekliyorsa en kolay başlangıç QEMU emülasyonudur: Dockerfile'da değişiklik gerekmez ve BuildKit emülasyon için kullanılabilen mimarileri kendisi algılar. Belgelere göre resmî BuildKit sürümleri QEMU user-mode emülatörlerini içinde taşır; bu yüzden bağımsız Docker Engine kullananlar çoğu durumda QEMU'yu elle kurmak zorunda kalmaz. Bedeli hızdır: belgeler emülasyonun özellikle derleme, sıkıştırma ve açma gibi işlemci yoğun işlerde yerel derlemeden çok daha yavaş olabileceğini not eder ve mümkünse diğer iki stratejiyi önerir.

Emülasyon başarısız olursa, örneğin emülatörleri içermeyen üçüncü taraf bir BuildKit paketi kullanıyorsanız, QEMU'yu ana makineye kurup çalıştırılabilir türleri kaydedebilirsiniz. Belgelerdeki ön koşullar şunlardır:

  • Linux çekirdeği 4.8 veya üstü
  • binfmt-support 2.1.7 veya üstü
  • QEMU ikili dosyalarının statik derlenmiş olması ve fix_binary bayrağıyla kaydedilmesi

Belgelerde tek komutluk yol tonistiigi/binfmt imajıdır. Bu komut QEMU ikili dosyalarını kurar ve binfmt_misc üzerinden kaydeder; kayıttan sonra emülasyon container'lar içinde şeffaf biçimde çalışır:

bash
docker run --privileged --rm tonistiigi/binfmt --install all

Kaydı doğrulamak için /proc/sys/fs/binfmt_misc/qemu-* dosyalarındaki bayraklar arasında F harfinin olup olmadığına bakmanız yeterlidir. Komutun --privileged ile çalıştığına dikkat edin; ana makinede çekirdek düzeyinde kayıt yaptığı için bunu yalnızca güvendiğiniz bir sunucuda ve bilerek çalıştırın.

2. Birden fazla yerel düğüm

QEMU'nun başa çıkamadığı karmaşık durumlar için belgeler birden fazla yerel düğümden oluşan bir builder önerir; bu yöntem performans açısından da daha iyidir. Fikir şudur: amd64 adımlarını bir amd64 makinede, arm64 adımlarını bir arm64 makinede yürütürsünüz. Builder'a düğüm eklemek için --append kullanılır. Aşağıdaki örnek, önceden eklenmiş node-amd64 ve node-arm64 adlı Docker context'lerinden çok düğümlü bir builder oluşturur:

bash
docker buildx create --use --name mybuild node-amd64
docker buildx create --append --name mybuild node-arm64
docker buildx build --platform linux/amd64,linux/arm64 .

Context'lerin nasıl tanımlandığı docker context yazısında anlatılıyor. Belgeler bu yaklaşımın bir yönetim yükü getirdiğini de açıkça söyler: builder kümesini kurmak ve bakımını yapmak sizin işinizdir. Alternatif olarak Docker Build Cloud, Docker altyapısında yönetilen çok düğümlü ARM ve x86 builder'ları sunar; kullanmak için hizmete kaydolup builder'ı yerel ortamınıza eklemeniz gerekir.

3. Cross-compilation ve platform argümanları

Kullandığınız dil cross-compilation'ı iyi destekliyorsa, multi-stage derlemeyle hedef platformların ikili dosyalarını builder'ın kendi mimarisinde üretebilirsiniz. Bunun anahtarı BuildKit'in her derlemede otomatik sağladığı platform argümanlarıdır. Build platform, builder'ın (BuildKit daemon'unun) çalıştığı sistemin işletim sistemi, mimarisi ve varyantıdır; target platform ise --platform ile istediğiniz hedeftir:

ArgümanAnlamı
BUILDPLATFORMBuilder'ın platformu, örneğin linux/amd64
BUILDOS / BUILDARCH / BUILDVARIANTBuilder platformunun işletim sistemi, mimari ve varyant parçaları
TARGETPLATFORMO anda derlenen hedef platform
TARGETOS / TARGETARCH / TARGETVARIANTHedef platformun işletim sistemi, mimari ve varyant parçaları

Belgelerin altını çizdiği kural sık atlanır: bu argümanlar Dockerfile'ın global kapsamında kullanılabilir, ama build stage'lerine otomatik aktarılmaz. Bir stage içinde kullanmak için o stage'de ARG ile bildirmeniz gerekir. FROM satırında ise global kapsamda olduğundan doğrudan kullanılabilir. Belgelerdeki Go örneği bu iki noktayı birlikte gösterir:

dockerfile
# syntax=docker/dockerfile:1
FROM --platform=$BUILDPLATFORM golang:alpine AS build
ARG TARGETOS
ARG TARGETARCH
WORKDIR /app
COPY . .
RUN GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -o server .

FROM alpine
COPY --from=build /app/server /server
ENTRYPOINT ["/server"]

Burada üç şey olur. FROM --platform=$BUILDPLATFORM, derleme stage'ini builder'ın kendi platformuna sabitler ve emülasyonun devreye girmesini önler. ARG TARGETOS ve ARG TARGETARCH satırları hedef bilgisini stage'e taşır. Go derleyicisi de GOOS ve GOARCH ortam değişkenleriyle hedefi seçer. Son stage sabitlenmediği için her hedef platformun kendi alpine varyantını kullanır ve doğru ikili dosyayı içine kopyalar. Belgelerdeki asıl örnek kaynak kodu bir Git deposundan ADD ile alır; burada okunabilirlik için yerel COPY kullandık.

bash
docker build --platform linux/amd64,linux/arm64 -t go-server .

Belgeler başka dillerde adımların farklı olabileceğini, ayrıntılar için dilin kendi cross-compile belgelerine bakmanızı önerir; ayrıca cross-compilation'ı kolaylaştıran yardımcı betikler içeren tonistiigi/xx imajını ipucu olarak anar. Build argümanlarının kapsam kurallarının tamamı ARG ve ENV farkı yazısında anlatılıyor.

Compose dosyasında platforms alanı

Compose ile imaj derliyorsanız hedef platformları build.platforms listesiyle verebilirsiniz. Compose Build belirtimine göre bu alan verilmezse servisin platform değeri varsayılan hedef listesine eklenir. Alan verildiğinde ise listenin servisin platformunu içermesi gerekir; aksi hâlde kullanıcılar derledikleri imajı çalıştıramaz. Aşağıdaki dosya Docker Compose v5.5.1 ile docker compose config komutundan geçti:

yaml
services:
  api:
    image: registry.example.com/benim/api:1.0
    platform: linux/amd64
    build:
      context: .
      platforms:
        - "linux/amd64"
        - "linux/arm64"

Belirtim, Compose'un üç durumda hata vermesi gerektiğini söyler: liste birden fazla platform içerdiği hâlde uygulama çok platformlu imaj saklayamıyorsa, listede desteklenmeyen bir platform varsa ve liste boş olmadığı hâlde servisin platformunu içermiyorsa. Son durumu yerel CLI ile denedik; platform: linux/amd64 verilen bir serviste platforms listesine yalnızca linux/arm64 yazınca doğrulama şu çıktıyla başarısız oldu:

bash
docker compose -f compose.yaml config --quiet
service.build.platforms MUST include service.platform "linux/amd64": invalid compose project

Bu hatayı derleme başlamadan, yalnızca config aşamasında yakalamak CI'da zaman kazandırır. Compose dosyalarının genel yapısı için Docker Compose rehberine bakabilirsiniz.

İmaj yerine ikili dosya: platformlara göre dışa aktarma

Çok platformlu derleme yalnızca imaj üretmek için değildir. local exporter ile birlikte kullanıldığında her hedef için ikili dosyayı ayrı bir klasöre yazabilirsiniz. Belgelerdeki örnekte docker build --platform=linux/amd64,linux/arm64 --output=out . komutu out/linux_amd64 ve out/linux_arm64 klasörlerini oluşturur. Klasörlerin ayrılmasını platform-split seçeneği yönetir; ayrıntılar build output ve exporter yazısında.

Sık yapılan hatalar

Sık yapılan hatalar: Klasik imaj deposunda --load beklemek, Her şeyi emülasyona bırakmak, ARG bildirmeyi unutmak, Yükseltilmiş sunucuda containerd'yi varsayılan sanmak
  • Klasik imaj deposunda --load beklemek: containerd image store kapalıyken docker-container builder çok platformlu imajı yerel depoya yükleyemez; sonucu --push ile registry'ye gönderin ya da containerd image store'u açın.
  • Her şeyi emülasyona bırakmak: belgelere göre QEMU, derleme gibi işlemci yoğun adımlarda yerel derlemeden çok daha yavaş olabilir; dil destekliyorsa derleme adımını --platform=$BUILDPLATFORM ile sabitleyip cross-compile edin.
  • ARG bildirmeyi unutmak: TARGETOS ve TARGETARCH stage'e otomatik gelmez.
  • Yükseltilmiş sunucuda containerd'yi varsayılan sanmak: Engine 29 yalnızca yeni kurulumlarda containerd image store ile gelir.
  • Compose'da platforms listesine servisin platformunu eklememek: doğrulama yukarıdaki hatayla durur.

Sık Sorulan Sorular

ARM sunucuda amd64 imaj çalıştırabilir miyim?

Belgelere göre emülasyon olmadan çalıştıramazsınız, çünkü container ana makinenin çekirdeğini paylaşır. Doğru çözüm imajı her iki platform için derleyip tek bir manifest list altında yayımlamaktır.

QEMU'yu ayrıca kurmam gerekiyor mu?

Çoğu durumda hayır. Belgeler resmî BuildKit sürümlerinin QEMU emülatörlerini içerdiğini, Docker Desktop'ın da emülasyonu varsayılan olarak desteklediğini söyler. Emülasyon başarısız olursa tonistiigi/binfmt ile elle kurulum yapılır.

Hangi stratejiyi seçmeliyim?

Diliniz cross-compilation'ı iyi destekliyorsa (belgeler Go ve Rust örnekleri verir) bu yol emülasyona girmeden yerel hızda çalışır ve ek makine gerektirmez. Dockerfile'a dokunmak istemiyorsanız QEMU ile başlayın. Emülasyonun başa çıkamadığı ya da çok yavaş kaldığı derlemelerde yerel düğümlü bir builder düşünün.

Çok platformlu imaj diskte daha fazla yer kaplar mı?

Her platform kendi katman kümesine sahip olduğu için registry'de her varyant ayrı saklanır. Yerelde ise containerd image store katmanları hem sıkıştırılmış hem açılmış tuttuğu için belgeler daha fazla disk kullanıldığını belirtir; temizlik için docker prune yazısına bakın.

Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.

İlgili içerikler: Docker öğrenme rehberi · BuildKit ve Buildx · containerd image store · Build output ve exporter · ARG ve ENV farkı · docker buildx bake