build: use buildx to build multi-platform docker images (#30530)
This commit is contained in:
parent
0a21cb4d21
commit
cfe25c7a3b
22
.travis.yml
22
.travis.yml
|
@ -9,8 +9,7 @@ jobs:
|
||||||
- azure-osx
|
- azure-osx
|
||||||
|
|
||||||
include:
|
include:
|
||||||
# These builders create the Docker sub-images for multi-arch push and each
|
# This builder create and push the Docker images for all architectures
|
||||||
# will attempt to push the multi-arch image if they are the last builder
|
|
||||||
- stage: build
|
- stage: build
|
||||||
if: type = push
|
if: type = push
|
||||||
os: linux
|
os: linux
|
||||||
|
@ -26,24 +25,7 @@ jobs:
|
||||||
before_install:
|
before_install:
|
||||||
- export DOCKER_CLI_EXPERIMENTAL=enabled
|
- export DOCKER_CLI_EXPERIMENTAL=enabled
|
||||||
script:
|
script:
|
||||||
- go run build/ci.go docker -image -manifest amd64,arm64 -upload ethereum/client-go
|
- go run build/ci.go dockerx -platform "linux/amd64,linux/arm64" -upload ethereum/client-go
|
||||||
|
|
||||||
- stage: build
|
|
||||||
if: type = push
|
|
||||||
os: linux
|
|
||||||
arch: arm64
|
|
||||||
dist: focal
|
|
||||||
go: 1.23.x
|
|
||||||
env:
|
|
||||||
- docker
|
|
||||||
services:
|
|
||||||
- docker
|
|
||||||
git:
|
|
||||||
submodules: false # avoid cloning ethereum/tests
|
|
||||||
before_install:
|
|
||||||
- export DOCKER_CLI_EXPERIMENTAL=enabled
|
|
||||||
script:
|
|
||||||
- go run build/ci.go docker -image -manifest amd64,arm64 -upload ethereum/client-go
|
|
||||||
|
|
||||||
# This builder does the Linux Azure uploads
|
# This builder does the Linux Azure uploads
|
||||||
- stage: build
|
- stage: build
|
||||||
|
|
|
@ -4,7 +4,7 @@ ARG VERSION=""
|
||||||
ARG BUILDNUM=""
|
ARG BUILDNUM=""
|
||||||
|
|
||||||
# Build Geth in a stock Go builder container
|
# Build Geth in a stock Go builder container
|
||||||
FROM golang:1.23-alpine as builder
|
FROM golang:1.23-alpine AS builder
|
||||||
|
|
||||||
RUN apk add --no-cache gcc musl-dev linux-headers git
|
RUN apk add --no-cache gcc musl-dev linux-headers git
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ ARG VERSION=""
|
||||||
ARG BUILDNUM=""
|
ARG BUILDNUM=""
|
||||||
|
|
||||||
# Build Geth in a stock Go builder container
|
# Build Geth in a stock Go builder container
|
||||||
FROM golang:1.23-alpine as builder
|
FROM golang:1.23-alpine AS builder
|
||||||
|
|
||||||
RUN apk add --no-cache gcc musl-dev linux-headers git
|
RUN apk add --no-cache gcc musl-dev linux-headers git
|
||||||
|
|
||||||
|
@ -14,6 +14,13 @@ COPY go.sum /go-ethereum/
|
||||||
RUN cd /go-ethereum && go mod download
|
RUN cd /go-ethereum && go mod download
|
||||||
|
|
||||||
ADD . /go-ethereum
|
ADD . /go-ethereum
|
||||||
|
|
||||||
|
# This is not strictly necessary, but it matches the "Dockerfile" steps, thus
|
||||||
|
# makes it so that under certain circumstances, the docker layer can be cached,
|
||||||
|
# and the builder can jump to the next (build all) command, with the go cache fully loaded.
|
||||||
|
#
|
||||||
|
RUN cd /go-ethereum && go run build/ci.go install -static ./cmd/geth
|
||||||
|
|
||||||
RUN cd /go-ethereum && go run build/ci.go install -static
|
RUN cd /go-ethereum && go run build/ci.go install -static
|
||||||
|
|
||||||
# Pull all binaries into a second stage deploy alpine container
|
# Pull all binaries into a second stage deploy alpine container
|
||||||
|
|
151
build/ci.go
151
build/ci.go
|
@ -50,7 +50,6 @@ import (
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -159,8 +158,8 @@ func main() {
|
||||||
doLint(os.Args[2:])
|
doLint(os.Args[2:])
|
||||||
case "archive":
|
case "archive":
|
||||||
doArchive(os.Args[2:])
|
doArchive(os.Args[2:])
|
||||||
case "docker":
|
case "dockerx":
|
||||||
doDocker(os.Args[2:])
|
doDockerBuildx(os.Args[2:])
|
||||||
case "debsrc":
|
case "debsrc":
|
||||||
doDebianSource(os.Args[2:])
|
doDebianSource(os.Args[2:])
|
||||||
case "nsis":
|
case "nsis":
|
||||||
|
@ -723,10 +722,9 @@ func maybeSkipArchive(env build.Environment) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Builds the docker images and optionally uploads them to Docker Hub.
|
// Builds the docker images and optionally uploads them to Docker Hub.
|
||||||
func doDocker(cmdline []string) {
|
func doDockerBuildx(cmdline []string) {
|
||||||
var (
|
var (
|
||||||
image = flag.Bool("image", false, `Whether to build and push an arch specific docker image`)
|
platform = flag.String("platform", "", `Push a multi-arch docker image for the specified architectures (usually "linux/amd64,linux/arm64")`)
|
||||||
manifest = flag.String("manifest", "", `Push a multi-arch docker image for the specified architectures (usually "amd64,arm64")`)
|
|
||||||
upload = flag.String("upload", "", `Where to upload the docker image (usually "ethereum/client-go")`)
|
upload = flag.String("upload", "", `Where to upload the docker image (usually "ethereum/client-go")`)
|
||||||
)
|
)
|
||||||
flag.CommandLine.Parse(cmdline)
|
flag.CommandLine.Parse(cmdline)
|
||||||
|
@ -761,129 +759,26 @@ func doDocker(cmdline []string) {
|
||||||
case strings.HasPrefix(env.Tag, "v1."):
|
case strings.HasPrefix(env.Tag, "v1."):
|
||||||
tags = []string{"stable", fmt.Sprintf("release-1.%d", params.VersionMinor), "v" + params.Version}
|
tags = []string{"stable", fmt.Sprintf("release-1.%d", params.VersionMinor), "v" + params.Version}
|
||||||
}
|
}
|
||||||
// If architecture specific image builds are requested, build and push them
|
// Need to create a mult-arch builder
|
||||||
if *image {
|
build.MustRunCommand("docker", "buildx", "create", "--use", "--name", "multi-arch-builder", "--platform", *platform)
|
||||||
build.MustRunCommand("docker", "build", "--build-arg", "COMMIT="+env.Commit, "--build-arg", "VERSION="+params.VersionWithMeta, "--build-arg", "BUILDNUM="+env.Buildnum, "--tag", fmt.Sprintf("%s:TAG", *upload), ".")
|
|
||||||
build.MustRunCommand("docker", "build", "--build-arg", "COMMIT="+env.Commit, "--build-arg", "VERSION="+params.VersionWithMeta, "--build-arg", "BUILDNUM="+env.Buildnum, "--tag", fmt.Sprintf("%s:alltools-TAG", *upload), "-f", "Dockerfile.alltools", ".")
|
|
||||||
|
|
||||||
// Tag and upload the images to Docker Hub
|
for _, spec := range []struct {
|
||||||
for _, tag := range tags {
|
file string
|
||||||
gethImage := fmt.Sprintf("%s:%s-%s", *upload, tag, runtime.GOARCH)
|
base string
|
||||||
toolImage := fmt.Sprintf("%s:alltools-%s-%s", *upload, tag, runtime.GOARCH)
|
}{
|
||||||
|
{file: "Dockerfile", base: fmt.Sprintf("%s:", *upload)},
|
||||||
// If the image already exists (non version tag), check the build
|
{file: "Dockerfile.alltools", base: fmt.Sprintf("%s:alltools-", *upload)},
|
||||||
// number to prevent overwriting a newer commit if concurrent builds
|
} {
|
||||||
// are running. This is still a tiny bit racey if two published are
|
for _, tag := range tags { // latest, stable etc
|
||||||
// done at the same time, but that's extremely unlikely even on the
|
gethImage := fmt.Sprintf("%s%s", spec.base, tag)
|
||||||
// master branch.
|
build.MustRunCommand("docker", "buildx", "build",
|
||||||
for _, img := range []string{gethImage, toolImage} {
|
"--build-arg", "COMMIT="+env.Commit,
|
||||||
if exec.Command("docker", "pull", img).Run() != nil {
|
"--build-arg", "VERSION="+params.VersionWithMeta,
|
||||||
continue // Generally the only failure is a missing image, which is good
|
"--build-arg", "BUILDNUM="+env.Buildnum,
|
||||||
}
|
"--tag", gethImage,
|
||||||
buildnum, err := exec.Command("docker", "inspect", "--format", "{{index .Config.Labels \"buildnum\"}}", img).CombinedOutput()
|
"--platform", *platform,
|
||||||
if err != nil {
|
"--push",
|
||||||
log.Fatalf("Failed to inspect container: %v\nOutput: %s", err, string(buildnum))
|
"--file", spec.file, ".")
|
||||||
}
|
|
||||||
buildnum = bytes.TrimSpace(buildnum)
|
|
||||||
|
|
||||||
if len(buildnum) > 0 && len(env.Buildnum) > 0 {
|
|
||||||
oldnum, err := strconv.Atoi(string(buildnum))
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Failed to parse old image build number: %v", err)
|
|
||||||
}
|
|
||||||
newnum, err := strconv.Atoi(env.Buildnum)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Failed to parse current build number: %v", err)
|
|
||||||
}
|
|
||||||
if oldnum > newnum {
|
|
||||||
log.Fatalf("Current build number %d not newer than existing %d", newnum, oldnum)
|
|
||||||
} else {
|
|
||||||
log.Printf("Updating %s from build %d to %d", img, oldnum, newnum)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
build.MustRunCommand("docker", "image", "tag", fmt.Sprintf("%s:TAG", *upload), gethImage)
|
|
||||||
build.MustRunCommand("docker", "image", "tag", fmt.Sprintf("%s:alltools-TAG", *upload), toolImage)
|
|
||||||
build.MustRunCommand("docker", "push", gethImage)
|
|
||||||
build.MustRunCommand("docker", "push", toolImage)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// If multi-arch image manifest push is requested, assemble it
|
|
||||||
if len(*manifest) != 0 {
|
|
||||||
// Since different architectures are pushed by different builders, wait
|
|
||||||
// until all required images are updated.
|
|
||||||
var mismatch bool
|
|
||||||
for i := 0; i < 2; i++ { // 2 attempts, second is race check
|
|
||||||
mismatch = false // hope there's no mismatch now
|
|
||||||
|
|
||||||
for _, tag := range tags {
|
|
||||||
for _, arch := range strings.Split(*manifest, ",") {
|
|
||||||
gethImage := fmt.Sprintf("%s:%s-%s", *upload, tag, arch)
|
|
||||||
toolImage := fmt.Sprintf("%s:alltools-%s-%s", *upload, tag, arch)
|
|
||||||
|
|
||||||
for _, img := range []string{gethImage, toolImage} {
|
|
||||||
if out, err := exec.Command("docker", "pull", img).CombinedOutput(); err != nil {
|
|
||||||
log.Printf("Required image %s unavailable: %v\nOutput: %s", img, err, out)
|
|
||||||
mismatch = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
buildnum, err := exec.Command("docker", "inspect", "--format", "{{index .Config.Labels \"buildnum\"}}", img).CombinedOutput()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Failed to inspect container: %v\nOutput: %s", err, string(buildnum))
|
|
||||||
}
|
|
||||||
buildnum = bytes.TrimSpace(buildnum)
|
|
||||||
|
|
||||||
if string(buildnum) != env.Buildnum {
|
|
||||||
log.Printf("Build number mismatch on %s: want %s, have %s", img, env.Buildnum, buildnum)
|
|
||||||
mismatch = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if mismatch {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if mismatch {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if mismatch {
|
|
||||||
// Build numbers mismatching, retry in a short time to
|
|
||||||
// avoid concurrent fails in both publisher images. If
|
|
||||||
// however the retry failed too, it means the concurrent
|
|
||||||
// builder is still crunching, let that do the publish.
|
|
||||||
if i == 0 {
|
|
||||||
time.Sleep(30 * time.Second)
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if mismatch {
|
|
||||||
log.Println("Relinquishing publish to other builder")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// Assemble and push the Geth manifest image
|
|
||||||
for _, tag := range tags {
|
|
||||||
gethImage := fmt.Sprintf("%s:%s", *upload, tag)
|
|
||||||
|
|
||||||
var gethSubImages []string
|
|
||||||
for _, arch := range strings.Split(*manifest, ",") {
|
|
||||||
gethSubImages = append(gethSubImages, gethImage+"-"+arch)
|
|
||||||
}
|
|
||||||
build.MustRunCommand("docker", append([]string{"manifest", "create", gethImage}, gethSubImages...)...)
|
|
||||||
build.MustRunCommand("docker", "manifest", "push", gethImage)
|
|
||||||
}
|
|
||||||
// Assemble and push the alltools manifest image
|
|
||||||
for _, tag := range tags {
|
|
||||||
toolImage := fmt.Sprintf("%s:alltools-%s", *upload, tag)
|
|
||||||
|
|
||||||
var toolSubImages []string
|
|
||||||
for _, arch := range strings.Split(*manifest, ",") {
|
|
||||||
toolSubImages = append(toolSubImages, toolImage+"-"+arch)
|
|
||||||
}
|
|
||||||
build.MustRunCommand("docker", append([]string{"manifest", "create", toolImage}, toolSubImages...)...)
|
|
||||||
build.MustRunCommand("docker", "manifest", "push", toolImage)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue