Kubernetes Operator for Tangled Spindles

build multiarch image fix runner binary

evan.jarrett.net 7e3fdced ee82083d

verified
+64 -41
+10 -1
Dockerfile
··· 1 1 # Build both binaries 2 + # Use BUILDPLATFORM so Go runs natively, cross-compile for target arch 2 3 FROM --platform=$BUILDPLATFORM golang:1.24 AS builder 3 4 4 5 ARG TARGETOS 5 6 ARG TARGETARCH 7 + ARG BUILDARCH 8 + 9 + # Install C cross-compiler for CGO when building arm64 from amd64 10 + RUN if [ "$BUILDARCH" = "amd64" ] && [ "$TARGETARCH" = "arm64" ]; then \ 11 + apt-get update && apt-get install -y gcc-aarch64-linux-gnu && rm -rf /var/lib/apt/lists/*; \ 12 + fi 6 13 7 14 WORKDIR /workspace 8 15 ··· 27 34 28 35 # Build controller (requires CGO for sqlite3) 29 36 # Use -s -w to strip debug symbols and reduce binary size 30 - RUN CGO_ENABLED=1 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} \ 37 + # Use cross-compiler for arm64 when building from amd64 38 + RUN CC=$(if [ "$TARGETARCH" = "arm64" ] && [ "$BUILDARCH" = "amd64" ]; then echo "aarch64-linux-gnu-gcc"; else echo "gcc"; fi) && \ 39 + CGO_ENABLED=1 CC=$CC GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} \ 31 40 go build -a -ldflags='-s -w' -o manager ./cmd/controller 32 41 33 42 # Unified image with both binaries
+13 -39
Makefile
··· 163 163 run: manifests generate fmt vet ## Run a controller from your host. 164 164 go run ./cmd/controller/main.go 165 165 166 - # If you wish to build the manager image targeting other platforms you can use the --platform flag. 167 - # (i.e. docker build --platform linux/arm64). However, you must enable docker buildKit for it. 166 + # Build multi-arch docker image using buildx (native builds per arch due to CGO) 168 167 # More info: https://docs.docker.com/develop/develop-images/build_enhancements/ 169 168 .PHONY: docker-build 170 - docker-build: ## Build docker image with the manager. 171 - cd .. && $(CONTAINER_TOOL) build -f loom/Dockerfile -t ${IMG} . 172 - 173 - .PHONY: docker-push 174 - docker-push: ## Push docker image with the manager. 175 - $(CONTAINER_TOOL) push ${IMG} 169 + docker-build: setup-buildx ## Build and push multi-arch docker image. 170 + cd .. && $(CONTAINER_TOOL) buildx build \ 171 + --builder loom-builder \ 172 + --platform=linux/amd64,linux/arm64 \ 173 + --push \ 174 + --tag ${IMG} \ 175 + -f loom/Dockerfile . 176 176 177 - # Runner image configuration 178 - RUNNER_IMG ?= atcr.io/evan.jarrett.net/loom-runner:$(VERSION) 179 - RUNNER_IMG_LATEST ?= atcr.io/evan.jarrett.net/loom-runner:latest 180 - RUNNER_PLATFORMS ?= linux/amd64,linux/arm64 177 + .PHONY: docker-build-local 178 + docker-build-local: ## Build docker image for local arch only (no push). 179 + cd .. && $(CONTAINER_TOOL) build -f loom/Dockerfile -t ${IMG} . 181 180 182 - .PHONY: setup-buildx-runner 183 - setup-buildx-runner: ## Set up buildx builder with credential access for runner builds 181 + .PHONY: setup-buildx 182 + setup-buildx: ## Set up buildx builder with credential access for multi-arch builds 184 183 @echo "Setting up loom-builder with credential access..." 185 184 # Remove existing builder if present 186 185 - $(CONTAINER_TOOL) buildx rm loom-builder 2>/dev/null || true ··· 209 208 fi 210 209 @echo "✓ Builder setup complete!" 211 210 212 - .PHONY: docker-build-runner 213 - docker-build-runner: setup-buildx-runner ## Build multi-arch docker image for the loom runner binary. 214 - @if [ -n "$(DOCKER_USERNAME)" ] && [ -n "$(DOCKER_PASSWORD)" ]; then \ 215 - echo "Authenticating to atcr.io with username/password..."; \ 216 - echo "$(DOCKER_PASSWORD)" | $(CONTAINER_TOOL) login atcr.io -u "$(DOCKER_USERNAME)" --password-stdin; \ 217 - echo "Copying updated credentials to builder..."; \ 218 - $(CONTAINER_TOOL) cp $(HOME)/.docker/config.json buildx_buildkit_loom-builder0:/root/.docker/config.json; \ 219 - else \ 220 - echo "⚠ No DOCKER_USERNAME/DOCKER_PASSWORD set. Will try credential helper (may not work in container)."; \ 221 - fi 222 - cd .. && $(CONTAINER_TOOL) buildx build \ 223 - --builder loom-builder \ 224 - --platform=$(RUNNER_PLATFORMS) \ 225 - --tag $(RUNNER_IMG) \ 226 - --tag $(RUNNER_IMG_LATEST) \ 227 - --push \ 228 - --file Dockerfile.runner \ 229 - . 230 - 231 - .PHONY: docker-build-runner-local 232 - docker-build-runner-local: ## Build local runner image (single arch) for testing. 233 - cd .. && $(CONTAINER_TOOL) build \ 234 - --tag $(RUNNER_IMG_LATEST) \ 235 - --file Dockerfile.runner \ 236 - . 237 211 238 212 .PHONY: test-registry-auth 239 213 test-registry-auth: ## Test registry authentication before building
+40
cmd/runner/main.go
··· 32 32 } 33 33 34 34 func main() { 35 + // Handle --install flag for self-copying (used by init container in distroless image) 36 + if len(os.Args) >= 3 && os.Args[1] == "--install" { 37 + if err := installSelf(os.Args[2]); err != nil { 38 + fmt.Fprintf(os.Stderr, "install failed: %v\n", err) 39 + os.Exit(1) 40 + } 41 + os.Exit(0) 42 + } 43 + 35 44 if err := run(); err != nil { 36 45 fmt.Fprintf(os.Stderr, "ERROR: %v\n", err) 37 46 os.Exit(1) 38 47 } 48 + } 49 + 50 + // installSelf copies this executable to the destination path 51 + func installSelf(dst string) error { 52 + src, err := os.Executable() 53 + if err != nil { 54 + return fmt.Errorf("failed to get executable path: %w", err) 55 + } 56 + 57 + srcFile, err := os.Open(src) 58 + if err != nil { 59 + return fmt.Errorf("failed to open source: %w", err) 60 + } 61 + defer srcFile.Close() 62 + 63 + dstFile, err := os.Create(dst) 64 + if err != nil { 65 + return fmt.Errorf("failed to create destination: %w", err) 66 + } 67 + defer dstFile.Close() 68 + 69 + if _, err := io.Copy(dstFile, srcFile); err != nil { 70 + return fmt.Errorf("failed to copy: %w", err) 71 + } 72 + 73 + // Make executable 74 + if err := os.Chmod(dst, 0755); err != nil { 75 + return fmt.Errorf("failed to chmod: %w", err) 76 + } 77 + 78 + return nil 39 79 } 40 80 41 81 func run() error {
+1 -1
internal/jobbuilder/job_template.go
··· 268 268 { 269 269 Name: "install-runner", 270 270 Image: config.LoomImage, 271 - Command: []string{"cp", "/loom-runner", "/runner-bin/loom-runner"}, 271 + Command: []string{"/loom-runner", "--install", "/runner-bin/loom-runner"}, 272 272 SecurityContext: &corev1.SecurityContext{ 273 273 AllowPrivilegeEscalation: &[]bool{false}[0], 274 274 RunAsNonRoot: &[]bool{true}[0],