From a7d4e314fb5f4a994ff5f827b5aab57b3931542d Mon Sep 17 00:00:00 2001 From: Frederic Branczyk Date: Fri, 30 Oct 2026 16:09:05 +0000 Subject: [PATCH 1/3] build: select target platform for runtime image Declare TARGETPLATFORM before FROM so Podman 4.9.4 resolves the runtime base for each target instead of falling back to amd64. The previous ARM64 image contained an ARM64 Parca binary but an x86-64 shell. Check the ELF machine of BusyBox, Parca, and the health probe for both platforms before publishing development or release images. Amp-Thread-ID: https://ampcode.com/threads/T-517b18b9-24b8-42c5-a008-0543f6960b36 Co-authored-by: Amp --- .github/workflows/container.yml | 4 ++-- .github/workflows/release.yml | 4 ++-- Dockerfile | 4 +++- Makefile | 21 +++++++++++++++++++++ 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/.github/workflows/container.yml b/.github/workflows/container.yml index d9546e4b362..55c59748d44 100644 --- a/.github/workflows/container.yml +++ b/.github/workflows/container.yml @@ -149,8 +149,8 @@ jobs: - name: Build container run: make container - - name: Check images created - run: podman images | grep 'ghcr.io/parca-dev/parca' + - name: Check image architectures + run: make test-container - name: Install cosign if: ${{ github.event_name != 'pull_request' }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4f6f4efa067..1969cc1f5f6 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -165,8 +165,8 @@ jobs: - name: Build container run: make container - - name: Check images created - run: podman images | grep 'ghcr.io/parca-dev/parca' + - name: Check image architectures + run: make test-container - name: Login to registry run: | diff --git a/Dockerfile b/Dockerfile index efdaad83d00..795feebaf1a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,3 +1,5 @@ +ARG TARGETPLATFORM + # https://github.com/hadolint/hadolint/issues/861 # hadolint ignore=DL3029 FROM --platform="${BUILDPLATFORM:-linux/amd64}" docker.io/busybox:1.37.0@sha256:a7dbc319b1438115a207e11930dc4bb3e643dcb3ec0bdd694bb36cd8fad289ec as builder @@ -33,7 +35,7 @@ RUN chmod +x parca # https://github.com/hadolint/hadolint/issues/861 # hadolint ignore=DL3029 -FROM --platform="${TARGETPLATFORM:-linux/amd64}" docker.io/alpine:3.24.2@sha256:294b683cb724975bec92580e1e685676bd4b50bda910ddb8c51d4cabeaec77e6 AS runner +FROM --platform="${TARGETPLATFORM}" docker.io/alpine:3.24.2@sha256:294b683cb724975bec92580e1e685676bd4b50bda910ddb8c51d4cabeaec77e6 AS runner LABEL \ org.opencontainers.image.source="https://github.com/parca-dev/parca" \ diff --git a/Makefile b/Makefile index a604b0210b3..6915911237a 100644 --- a/Makefile +++ b/Makefile @@ -165,6 +165,27 @@ container: --timestamp 0 \ --manifest $(OUT_DOCKER):$(VERSION) . +.PHONY: test-container +test-container: + set -euo pipefail; \ + for arch in amd64 arm64; do \ + digest=$$(podman manifest inspect $(OUT_DOCKER):$(VERSION) | jq -er --arg arch "$$arch" '.manifests[] | select(.platform.os == "linux" and .platform.architecture == $$arch) | .digest'); \ + image=$$(podman images --all --no-trunc --format '{{.Digest}} {{.ID}}' | awk -v digest="$$digest" '$$1 == digest {print $$2}'); \ + rootfs=$$(podman image mount "$$image"); \ + trap 'podman image unmount "$$image" >/dev/null' EXIT; \ + case "$$arch" in amd64) expected=3e00 ;; arm64) expected=b700 ;; esac; \ + for binary in bin/busybox parca grpc_health_probe; do \ + actual=$$(od -An -tx1 -j18 -N2 "$$rootfs/$$binary" | tr -d ' \n'); \ + if [ "$$actual" != "$$expected" ]; then \ + echo "linux/$$arch: $$binary has ELF machine $$actual, expected $$expected" >&2; \ + exit 1; \ + fi; \ + echo "Verified linux/$$arch: $$binary"; \ + done; \ + podman image unmount "$$image" >/dev/null; \ + trap - EXIT; \ + done + .PHONY: push-container push-container: podman manifest push --all $(OUT_DOCKER):$(VERSION) docker://$(OUT_DOCKER):$(VERSION) From 106ccd901a009297aad3a8e1eb292545fea26e66 Mon Sep 17 00:00:00 2001 From: Frederic Branczyk Date: Fri, 30 Oct 2026 16:38:14 +0000 Subject: [PATCH 2/3] build: use native Kubernetes gRPC health probes Remove the bundled health-probe executable from runtime and development images. Kubernetes probes call Parca's existing gRPC health service. Keep the runtime platform fix without the additional Make target or publishing workflow checks. Amp-Thread-ID: https://ampcode.com/threads/T-517b18b9-24b8-42c5-a008-0543f6960b36 Co-authored-by: Amp --- .github/workflows/container.yml | 4 ++-- .github/workflows/release.yml | 4 ++-- Dockerfile | 12 ------------ Dockerfile.dev | 6 ------ Dockerfile.go.dev | 6 ------ Makefile | 21 --------------------- deploy/lib/parca/parca.libsonnet | 8 ++++---- renovate.json | 7 ------- 8 files changed, 8 insertions(+), 60 deletions(-) diff --git a/.github/workflows/container.yml b/.github/workflows/container.yml index 55c59748d44..d9546e4b362 100644 --- a/.github/workflows/container.yml +++ b/.github/workflows/container.yml @@ -149,8 +149,8 @@ jobs: - name: Build container run: make container - - name: Check image architectures - run: make test-container + - name: Check images created + run: podman images | grep 'ghcr.io/parca-dev/parca' - name: Install cosign if: ${{ github.event_name != 'pull_request' }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1969cc1f5f6..4f6f4efa067 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -165,8 +165,8 @@ jobs: - name: Build container run: make container - - name: Check image architectures - run: make test-container + - name: Check images created + run: podman images | grep 'ghcr.io/parca-dev/parca' - name: Login to registry run: | diff --git a/Dockerfile b/Dockerfile index 795feebaf1a..f3c3bedb5c3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -9,17 +9,6 @@ ARG TARGETOS=linux ARG TARGETARCH=amd64 ARG TARGETVARIANT=v1 -# renovate: datasource=github-releases depName=grpc-ecosystem/grpc-health-probe -ARG GRPC_HEALTH_PROBE_VERSION=v0.4.57 -# Downloading grpc_health_probe from github releases with retry as we have seen it fail a lot on ci. -RUN for i in $(seq 1 50); do \ - wget -qO/bin/grpc_health_probe "https://github.com/grpc-ecosystem/grpc-health-probe/releases/download/${GRPC_HEALTH_PROBE_VERSION}/grpc_health_probe-${TARGETOS}-${TARGETARCH}" && \ - chmod +x /bin/grpc_health_probe && \ - break; \ - echo "Failed to download grpc_health_probe on $i th attempt, retrying in 5s..." \ - sleep 5; \ - done - WORKDIR /app COPY dist dist @@ -47,7 +36,6 @@ RUN mkdir /data && chown nobody /data # hadolint ignore=DL3066 USER nobody -COPY --chown=0:0 --from=builder /bin/grpc_health_probe / COPY --chown=0:0 --from=builder /app/parca /parca COPY --chown=0:0 parca.yaml /parca.yaml diff --git a/Dockerfile.dev b/Dockerfile.dev index 780f7c71c89..9f384d7110d 100644 --- a/Dockerfile.dev +++ b/Dockerfile.dev @@ -21,16 +21,11 @@ FROM docker.io/golang:1.25.6-alpine@sha256:98e6cffc31ccc44c7c15d83df1d69891efee8 # renovate: datasource=go depName=github.com/go-delve/delve ARG DLV_VERSION=v1.26.1 -# renovate: datasource=go depName=github.com/grpc-ecosystem/grpc-health-probe -ARG GRPC_HEALTH_PROBE_VERSION=v0.4.47 - WORKDIR /app # hadolint ignore=DL3018 RUN apk update && apk add --no-cache build-base RUN go install "github.com/go-delve/delve/cmd/dlv@${DLV_VERSION}" -# hadolint ignore=DL3059 -RUN go install "github.com/grpc-ecosystem/grpc-health-probe@${GRPC_HEALTH_PROBE_VERSION}" COPY go.mod go.sum /app/ RUN go mod download -modcacherw @@ -48,7 +43,6 @@ RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -trimpath -gcflags="all=-N -l FROM docker.io/golang:1.25.6-alpine@sha256:98e6cffc31ccc44c7c15d83df1d69891efee8115a5bb7ede2bf30a38af3e3c92 COPY --from=builder /go/bin/dlv / -COPY --from=builder /go/bin/grpc-health-probe / COPY --from=builder /app/parca /parca COPY parca.yaml /parca.yaml diff --git a/Dockerfile.go.dev b/Dockerfile.go.dev index 74e376abcb0..924376b8109 100644 --- a/Dockerfile.go.dev +++ b/Dockerfile.go.dev @@ -5,16 +5,11 @@ FROM docker.io/golang:1.25.6-alpine@sha256:98e6cffc31ccc44c7c15d83df1d69891efee8 # renovate: datasource=go depName=github.com/go-delve/delve ARG DLV_VERSION=v1.26.1 -# renovate: datasource=go depName=github.com/grpc-ecosystem/grpc-health-probe -ARG GRPC_HEALTH_PROBE_VERSION=v0.4.47 - WORKDIR /app # hadolint ignore=DL3018 RUN apk update && apk add --no-cache build-base RUN go install "github.com/go-delve/delve/cmd/dlv@${DLV_VERSION}" -# hadolint ignore=DL3059 -RUN go install "github.com/grpc-ecosystem/grpc-health-probe@${GRPC_HEALTH_PROBE_VERSION}" COPY go.mod go.sum /app/ RUN go mod download -modcacherw @@ -39,7 +34,6 @@ RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -gcflags="all=-N -l" -o parca FROM docker.io/golang:1.25.6-alpine@sha256:98e6cffc31ccc44c7c15d83df1d69891efee8115a5bb7ede2bf30a38af3e3c92 COPY --from=builder /go/bin/dlv / -COPY --from=builder /go/bin/grpc-health-probe / COPY --from=builder /app/parca /parca COPY parca.yaml /parca.yaml diff --git a/Makefile b/Makefile index 6915911237a..a604b0210b3 100644 --- a/Makefile +++ b/Makefile @@ -165,27 +165,6 @@ container: --timestamp 0 \ --manifest $(OUT_DOCKER):$(VERSION) . -.PHONY: test-container -test-container: - set -euo pipefail; \ - for arch in amd64 arm64; do \ - digest=$$(podman manifest inspect $(OUT_DOCKER):$(VERSION) | jq -er --arg arch "$$arch" '.manifests[] | select(.platform.os == "linux" and .platform.architecture == $$arch) | .digest'); \ - image=$$(podman images --all --no-trunc --format '{{.Digest}} {{.ID}}' | awk -v digest="$$digest" '$$1 == digest {print $$2}'); \ - rootfs=$$(podman image mount "$$image"); \ - trap 'podman image unmount "$$image" >/dev/null' EXIT; \ - case "$$arch" in amd64) expected=3e00 ;; arm64) expected=b700 ;; esac; \ - for binary in bin/busybox parca grpc_health_probe; do \ - actual=$$(od -An -tx1 -j18 -N2 "$$rootfs/$$binary" | tr -d ' \n'); \ - if [ "$$actual" != "$$expected" ]; then \ - echo "linux/$$arch: $$binary has ELF machine $$actual, expected $$expected" >&2; \ - exit 1; \ - fi; \ - echo "Verified linux/$$arch: $$binary"; \ - done; \ - podman image unmount "$$image" >/dev/null; \ - trap - EXIT; \ - done - .PHONY: push-container push-container: podman manifest push --all $(OUT_DOCKER):$(VERSION) docker://$(OUT_DOCKER):$(VERSION) diff --git a/deploy/lib/parca/parca.libsonnet b/deploy/lib/parca/parca.libsonnet index 9eeee432077..ae755c5568b 100644 --- a/deploy/lib/parca/parca.libsonnet +++ b/deploy/lib/parca/parca.libsonnet @@ -161,14 +161,14 @@ function(params) { terminationMessagePolicy: 'FallbackToLogsOnError', livenessProbe: if prc.config.livenessProbe == true then { initialDelaySeconds: 5, - exec: { - command: ['/grpc_health_probe', '-v', '-addr=:' + prc.config.port], + grpc: { + port: prc.config.port, }, }, readinessProbe: if prc.config.readinessProbe == true then { initialDelaySeconds: 10, - exec: { - command: ['/grpc_health_probe', '-v', '-addr=:' + prc.config.port], + grpc: { + port: prc.config.port, }, }, }; diff --git a/renovate.json b/renovate.json index b10cbd35e18..476c93d90fe 100644 --- a/renovate.json +++ b/renovate.json @@ -29,13 +29,6 @@ "matchSourceUrls": ["https://github.com/grpc-ecosystem/grpc-gateway"], "groupName": "grpc-gateway" }, - { - "description": "Group grpc-health-probe packages", - "matchSourceUrls": [ - "https://github.com/grpc-ecosystem/grpc-health-probe" - ], - "groupName": "grpc-health-probe" - }, { "description": "Group protobuf-ts packages", "matchSourceUrls": ["https://github.com/timostamm/protobuf-ts"], From 839dd4c6e17bb8b2c90f5f236de2fb6abf4e111d Mon Sep 17 00:00:00 2001 From: Frederic Branczyk Date: Fri, 30 Oct 2026 17:15:31 +0000 Subject: [PATCH 3/3] build: avoid target-architecture runtime instructions Create the data directory in the build-platform stage and copy it into the runtime image with its original ownership. Multi-architecture builds then need no ARM emulation on the AMD64 publishing runner. Amp-Thread-ID: https://ampcode.com/threads/T-517b18b9-24b8-42c5-a008-0543f6960b36 Co-authored-by: Amp --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index f3c3bedb5c3..0913e7d3cd7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,7 +3,7 @@ ARG TARGETPLATFORM # https://github.com/hadolint/hadolint/issues/861 # hadolint ignore=DL3029 FROM --platform="${BUILDPLATFORM:-linux/amd64}" docker.io/busybox:1.37.0@sha256:a7dbc319b1438115a207e11930dc4bb3e643dcb3ec0bdd694bb36cd8fad289ec as builder -RUN mkdir /.cache && touch -t 202101010000.00 /.cache +RUN mkdir /.cache /data && touch -t 202101010000.00 /.cache ARG TARGETOS=linux ARG TARGETARCH=amd64 @@ -32,7 +32,7 @@ LABEL \ org.opencontainers.image.description="Continuous profiling for analysis of CPU and memory usage, down to the line number and throughout time. Saving infrastructure cost, improving performance, and increasing reliability." \ org.opencontainers.image.licenses="Apache-2.0" -RUN mkdir /data && chown nobody /data +COPY --chown=65534:0 --from=builder /data /data # hadolint ignore=DL3066 USER nobody