Scott Rossillo
Mjukvaruingenjör, Inception Team
AWS tillkännagav nyligen en förhandsvisning av sin nya generation av Amazon EC2 M6g-instanser som drivs av 64-bitars ARM-baserade AWS Graviton2-processorer. De beräknade prestanda- och prisfördelarna jämfört med den senaste generationen av AWS x86–64-instanser är för imponerande för att ignorera.
Även om vi helt enkelt skulle kunna använda standard Docker på ARM för att bygga bilder för dessa nya AWS Graviton-processorer, finns det många fördelar med att stödja båda arkitekturerna istället för att överge x86–64-skeppet:
- Utvecklare måste kunna köra sina CI/CD-genererade Docker-avbildningar lokalt. Under överskådlig framtid kommer utvecklarmaskiner att fortsätta använda x86–64-processorer.
- Dela gemensamma behållare över x86–64- och Graviton2-kluster.
- Kör iscensättningsmiljöer på ARM och produktion på x86–64 tills Graviton2 är ur förhandsvisning.
- När Graviton2s är allmänt tillgängliga byter du snabbt tillbaka till x86–64 om en tjänstmigrering till ARM orsakar några problem.
Att bygga Docker-bilder med flera arkitekturer är fortfarande en experimentell funktion. Dock stöds det redan väl av Docker's Registry att vara värd för flerarkitekturbilder, både på egen hand och på hub.docker.com. Din körsträcka kan variera med 3:e parts Docker-registerimplementeringar
I det här inlägget kommer vi att demonstrera hur man bygger och publicerar Docker-avbildningar med flera arkitekturer på en ARM Linux-värd för både x86–64 (AMD64) och ARM64 så att du kan köra en Docker-container från bilden på någon av arkitekturerna.
Obs: om du är OK att bygga dina bilder på ditt macOS- eller Windows-skrivbord, levereras Docker Desktop ur kartongen med stöd för att bygga Docker-bilder med flera arkitekturer. Men om du kör Linux eller vill bygga dina Docker-avbildningar korrekt, som en del av din CI/CD-pipeline, läs vidare.
Installera Docker 19.03 eller senare
Till att börja med kommer vi att behöva en ARM64 Linux-värd som kan köra Docker 19.03 eller senare. Du kan också använda en x86–64-värd.
Men eftersom vi vill dra nytta av kostnadsbesparingarna med ARM kommer vi att använda en som vår byggserver med Ubuntu 19.10. Ubuntu är en populär Linux-distribution som stöds av flera molntjänster, men andra senaste distributioner bör också fungera bra. Du måste dock se till att du kör en Linux-kärna 5.x eller senare. På AWS kan du använda Ubuntu 19.10 AMI.
På Ubuntu, installera docker.io
för Ubuntus arkiv. Vi installerar även binfmt-support
och qemnu-user-static
. QEMU gör det möjligt för en enda värd att bygga bilder för flera arkitekturer och binfmt-support
lägger till stöd för flera binära format till Linux-kärnan. Observera att binfmt-support
version 2.1.43 eller senare krävs.
Lägg till din användare i Docker-gruppen för att möjliggöra att kommandon körs från ditt användarkonto. Kom ihåg att starta om eller logga ut och in igen efter körning:
1. #!/bin/bash #Installera Docker och multi-arch beroenden
2.
3. sudo apt-get install binfmt-support qemu-user-static
4. sudo apt-get installera docker.io
5. sudo usermod -aG docker $USERp
6. sudo omstart
Installera Docker Buildx
Därefter måste vi installera Dockers buildx
kommando. Buildx är i teknikförhandsgranskning och erbjuder experimentella byggfunktioner som multiarkitekturbyggen. Om du aktiverade docker för att köras som din användare, kan du installera detta som din vanliga användare, snarare än root.
Installera buildx
kommandoradsplugin för Docker. Koden nedan kommer att installera den senaste versionen för ARM 64-bit.
1. #!/bin/bash
2. #Installera buildx för arm64 och aktivera Docker CLI-plugin
3.
4. sudo apt-get install jq
5. mkdir -p ~/.docker/cli-plugins
6. BUILDX_URL=$(curl https://api.github.com/repos/docker/buildx
/releases/senaste | jq -r .assets[].browser_download_url | grep arm64
7. wget $BUILDX_URL -O ~/.docker/cli-plugins/docker-build
8. chmod +x ~/.docker/cli-plugins/docker-buildx
Bygg multiarkitekturbilder
Att bygga multiarkitekturbilder (Dockers dokumentation hänvisar till dessa som multiplattformsbilder) kräver en byggare med stöd av
docker-container
drivrutinen och stöder två strategier för att bygga plattformsöverskridande bilder:
- Använder QEMU-emuleringsstöd i kärnan
- Bygger på flera inbyggda noder koordinerade av en enda byggare
Här använder vi QEMU-metoden eftersom det är det billigaste av de två alternativen, eftersom det bara kräver en enda byggvärd för alla riktade arkitekturer. Dessutom använder Docker inte QEMU här för att skapa fullt fungerande virtuell maskin. Vi använder QEMU användarläge, så endast systemanrop behöver emuleras.
När dina CI/CD-behov utvecklas kanske du vill investera i en byggfarm med inhemska noder för att påskynda byggprocessen.
Låt oss skapa en bootstrap byggaren, du kan ge den vilket namn du vill:
1. $ docker buildx create --name mbuilder
2. byggmästare
3.
4. $ docker buildx använd mbilder
5.
6. $ docker buildx inspect --bootstrap
7. Namn: byggmästare
8. Drivrutin: docker-container
9.
10. Noder:
11. Namn: Builder0
12. Slutpunkt: unix:///var/run/docker.sock
13. Status: körs
14. Plattformar: linux/arm64, linux/amd64, linux/riscv64, linux/ppc64le,
linux/s390x, linux/386, linux/arm/v7, linux/arm/v6
Perfekt, vi har nu en byggare som kan rikta in sig på linux/arm64 linux/amd64 och andra arkitekturer!
Låt oss nu bygga en bild som kan köras på både Linux amd64 och arm64 från en enkel Dockerfile.
Observera att bilden du hämtar från också måste stödja de arkitekturer du planerar att rikta in dig på. Detta kan kontrolleras med:
$ docker buildx imagetools inspektera alpin
Dockerfile:
FRÅN alpint
KÖR apk lägg till util-linux
CMD ["lscpu"]
$ docker buildx build --plattform linux/amd64, linux/arm64 -t foo4u/demo-mutliarch:2 --push .
[+] Byggnad 4,7s (9/9) FÄRDIG
=> [intern] load build definition från Dockerfile
=> => överför dockerfil: 31B
=> [intern] ladda .dockerignore
=> => överföra sammanhang: 2B
=> [linux/amd64 intern] ladda metadata för docker.io/library/alpine:latest
=> [linux/arm64 intern] ladda metadata för docker.io/library/alpine:latest
=> [linux/amd64 1/2] FRÅN docker.io/library/alpine@sha256:2171658620155679240babee0a7714f6509fae66898db422ad803b951257db78
=> => lös docker.io/library/alpine@sha256:2171658620155679240babee0a7714f6509fae66898db422ad803b951257db78
=> CACHAD [linux/amd64 2/2] KÖR apk lägg till util-linux
=> [linux/arm64 1/2] FRÅN docker.io/library/alpine@sha256:2171658620155679240babee0a7714f6509fae66898db422ad803b951257db78
=> => lös docker.io/library/alpine@sha256:2171658620155679240babee0a7714f6509fae66898db422ad803b951257db78
=> CACHAD [linux/arm64 2/2] KÖR apk lägg till util-linux
=> exporterar till bild
=> => exporterar lager
=> => exporterar manifest sha256:cb54200a7c04dded134ca9e3e6a0e434c2fdf851fb3a7226941d0983ad5bfb88
=> => exporterar konfiguration sha256:307b885367f8ef4dc443dc35d6ed3298b9a3a48a846cf559a676c028a359731b
=> => exporterar manifest sha256:6f4fe17def66ef5bc79279448e1cb77a1642d460ed58d5dc60d0e472c023e2eb
=> => exporterar konfiguration sha256:26e6b092c7c1efffe51ce1d5f68e3359ab44152d33df39e5b85cd4ff6cfed3d4
=> => exporterar manifestlista sha256:3b4e4135b92017e5214421543b813e83a77fcea759af8067c685b70a5d978497
=> => tryckande lager
=> => pushar manifest för docker.io/foo4u/demo-mutliarch:2
Det händer mycket här så låt oss packa upp det:
1. Docker överför byggkontexten till vår byggarcontainer
2. Byggaren bygger en bild för varje arkitektur vi begärde med argumentet --platform
3. Bilderna skickas till Docker Hub
4. Buildx genererar en manifest JSON-fil skickar den till Docker Hub som bildtaggen.
Låt oss använda imagetools
för att inspektera den genererade Docker-bilden:
1. $ docker buildx imagetools inspektera foo4u/demo-mutliarch:2
2. Namn: docker.io/foo4u/demo-mutliarch:2
3. MediaType: application/vnd.docker.distribution.manifest.list.v2+json
4. Sammanfattning: sha256:3b4e4135b92017e5214421543b813e83a77fcea759af8067c685b70a5d978497
5.
6. Manifest:
7. Namn: docker.io/foo4u/demo-mutliarch:2@sha256:cb54200a7c04dded134ca9e3e6a0e434c2fdf851fb3a7226941d0983ad5bfb88
8. MediaType: application/vnd.docker.distribution.manifest.v2+json
9. Plattform: linux/amd64
10.
11. Namn: docker.io/foo4u/demo-12. mutliarch:2@sha256:6f4fe17def66ef5bc79279448e1cb77a1642d460ed58d5dc60d0e472c023e2eb
12. MediaType: application/vnd.docker.distribution.manifest.v2+json
13. Plattform: linux/arm64
Här kan vi se att foo4u/demo-multiarch:2
är ett JSON-manifest som pekar på manifesten för var och en av plattformarna vi riktade in oss på under bygget. Även om bilden visas i registret som en enda bild, är det faktiskt ett manifest som innehåller länkar till plattformsspecifika bilder. Buildx byggde och publicerade en bild per arkitektur och genererade sedan ett manifest som länkade dem samman.
Docker använder denna information när bilden dras för att ladda ner lämplig bild för maskinens runtime-arkitektur.
Låt oss köra bilden på x86–64 / amd64:
$ docker kör --rm foo4u/demo-mutliarch:2
Det gick inte att hitta bilden 'foo4u/demo-mutliarch:2' lokalt
2: Dra från foo4u/demo-mutliarch
e6b0cf9c0882: Finns redan
Status: Laddade ner nyare bild för foo4u/demo-mutliarch:2
Arkitektur: x86_64
Låt oss nu köra bilden på arm64:
$ docker kör --rm foo4u/demo-mutliarch:2
Det gick inte att hitta bilden 'foo4u/demo-mutliarch:2' lokalt
2: Dra från foo4u/demo-mutliarch
Status: Laddade ner nyare bild för foo4u/demo-mutliarch:2
Arkitektur: aarch64
Det är det! Nu har vi en fullt fungerande Docker-bild som vi kan köra på antingen våra befintliga x86–64-servrar eller våra skinande nya ARM 64-servrar!
Sammanfattningsvis är det inte så svårt att komma igång med Docker-avbildningar med flera arkitekturer på Linux. Vi kan till och med använda en ARM-server för att bygga bilderna, vilket potentiellt kan spara pengar på våra CI/CD-server(ar) såväl som vår iscensättning och produktionsinfrastruktur.
Bonus: du kan optimera dina Docker-byggen ytterligare om språket du använder har bra stöd för flera arkitekturer (som Java eller Go). Till exempel kan du bygga en Spring Boot-applikation med en enda plattformskompilering:
1. FRÅN --plattform=$BUILDPLATFORM amazoncorretto:11 som byggare
2.
3. KOPIERA . /srv/
4. WORKDIR /srv
5. KÖR ./mvnw -DskipTests=true package spring-boot:repackage
6.
7. FRÅN amazoncorretto:11
8.
9. COPY --from=builder /srv/target/my-service-0.0.1-SNAPSHOT.jar /srv/
10.
11. EXPONERA 8080
12.
13. ENTRYPOINT ["java", "-jar", "/srv/my-service-0.0.1-SNAPSHOT.jar"]