diff --git a/kubernetes/Dockerfile.image-committer b/kubernetes/Dockerfile.image-committer index c4269ad64..708ebb202 100644 --- a/kubernetes/Dockerfile.image-committer +++ b/kubernetes/Dockerfile.image-committer @@ -31,7 +31,9 @@ COPY cmd/image-committer/ cmd/image-committer/ RUN CGO_ENABLED=0 GOOS=linux go build -o /usr/local/bin/image-committer ./cmd/image-committer/ # Runtime stage -FROM alpine:3.19 +# Alpine 3.21 provides nerdctl 2.x. nerdctl 2.x restores image content that +# containerd may discard after CRI unpack before committing a new image. +FROM alpine:3.21 # Use Aliyun mirror for faster downloads in China RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories @@ -44,6 +46,9 @@ RUN apk add --no-cache \ jq \ nerdctl +# Missing-content recovery before commit was added in nerdctl 2.0. +RUN nerdctl --version | grep -Eq 'nerdctl version ([2-9]|[1-9][0-9]+)\.' + # Create directory for containerd socket mount RUN mkdir -p /var/run/containerd diff --git a/kubernetes/cmd/image-committer/main.go b/kubernetes/cmd/image-committer/main.go index 7717fdf16..43f9b8fce 100644 --- a/kubernetes/cmd/image-committer/main.go +++ b/kubernetes/cmd/image-committer/main.go @@ -23,6 +23,7 @@ import ( "os/exec" "os/signal" "path/filepath" + "runtime" "strconv" "strings" "syscall" @@ -546,12 +547,7 @@ func pushImage(targetImage string) error { fmt.Println("No registry credentials found, assuming insecure or pre-authenticated registry") } - // Build push options - pushOpts := append(nerdctlBaseArgs(), "push") - if isInsecure { - pushOpts = append(pushOpts, "--insecure-registry") - } - pushOpts = append(pushOpts, targetImage) + pushOpts := nerdctlPushArgs(targetImage, isInsecure) cmd := exec.Command("nerdctl", pushOpts...) output, err := cmd.CombinedOutput() @@ -562,6 +558,18 @@ func pushImage(targetImage string) error { return nil } +func runtimePlatform() string { + return runtime.GOOS + "/" + runtime.GOARCH +} + +func nerdctlPushArgs(targetImage string, insecure bool) []string { + args := append(nerdctlBaseArgs(), "push", "--platform", runtimePlatform()) + if insecure { + args = append(args, "--insecure-registry") + } + return append(args, targetImage) +} + // nerdctlLogin extracts credentials from a Docker config.json and runs nerdctl login. func nerdctlLogin(configPath, registryHost string, insecure bool) error { data, err := os.ReadFile(configPath) diff --git a/kubernetes/cmd/image-committer/main_test.go b/kubernetes/cmd/image-committer/main_test.go index df9add332..5de13da8b 100644 --- a/kubernetes/cmd/image-committer/main_test.go +++ b/kubernetes/cmd/image-committer/main_test.go @@ -173,6 +173,25 @@ func contains(values []string, target string) bool { return false } +func TestNerdctlPushArgsSelectsRuntimePlatform(t *testing.T) { + t.Setenv("CONTAINERD_SOCKET", "/test/containerd.sock") + t.Setenv("CONTAINERD_NAMESPACE", "test-ns") + + args := nerdctlPushArgs("registry.example.com/test/image:snap", true) + + want := []string{ + "--address", "/test/containerd.sock", + "--namespace", "test-ns", + "push", + "--platform", runtimePlatform(), + "--insecure-registry", + "registry.example.com/test/image:snap", + } + if strings.Join(args, "\x00") != strings.Join(want, "\x00") { + t.Fatalf("unexpected nerdctl push args: got %v, want %v", args, want) + } +} + func TestSyncRunningContainerFilesystemsSyncsEveryRunningContainerAndSkipsStopped(t *testing.T) { original := commandCombinedOutput t.Cleanup(func() { commandCombinedOutput = original })