# CloudNativePG 오퍼레이터 — 업스트림 소스를 pinned commit 으로 직접 컴파일한다. # # 업스트림(https://github.com/cloudnative-pg/cloudnative-pg)의 Dockerfile 은 이미 goreleaser # 로 빌드된 바이너리(`dist/manager/manager_`)를 COPY 만 한다 — 컴파일 자체는 이 Dockerfile # 밖(Makefile `docker-build` → goreleaser)에서 일어난다. 우리는 그 컴파일 단계를 Dockerfile # 안으로 가져와 `go build` 로 직접 재현한다. `cnpg-postgresql/suse.Dockerfile` 이 "업스트림 # Dockerfile 을 다른 배포판으로 이식"한 것이라면, 이건 "업스트림이 Dockerfile 밖에서 하던 # 빌드를 Dockerfile 안으로 흡수"한 것이다. # # 왜 자체 빌드인가 — cloudnative-pg:1.30.0 이 게이트에서 차단하는 CVE 3건(stdlib·x/text·grpc) # 은 OS 패키지가 아니라 바이너리에 정적 링크된 Go 모듈 버전이 원인이다. 배포 이미지 베이스가 # distroless(OS 패키지 사실상 0개)라 베이스 OS 교체로는 고칠 수 없다 — 소스를 다시 컴파일해야 # 한다. 세 CVE 모두 업스트림 release-1.30 브랜치에 이미 백포트돼 있다(SOURCE_COMMIT 이 그 # 커밋을 가리킨다) — 근거: doc/decisions/0002-cloudnative-pg-operator-self-build.md # # 업스트림과의 대응 관계 (release-1.30 브랜치 Dockerfile 기준) # go build (Makefile build-manager) → 동일 (ldflags 까지 그대로) # goreleaser 멀티아치(manager_amd64/arm64) → 단일 아키텍처(linux/amd64)만 직접 COPY. # 심볼릭 링크 대신 같은 바이너리를 두 경로에 COPY (아래 "실측으로 드러난 필수 조건" 참고) # distroless base(gcr.io/distroless/static-debian13:nonroot) → SUSE BCI(bci-micro)로 교체. # 카탈로그는 SUSE BCI 하나만 쓴다(decisions/0001) — 최종 런타임 이미지에도 동일하게 적용한다. # builder 스테이지(Go 컴파일)는 공식 golang 이미지를 그대로 쓴다 — 컴파일 결과물에는 # 영향이 없고, 최종 이미지에만 남는 게 무엇인지가 스캔·정책 대상이다 # syntax=docker/dockerfile:1 ARG GO_BUILDER_TAG=1.26.5-trixie # FROM 에서 쓰는 ARG 는 반드시 첫 FROM 이전(전역 스코프)에 선언해야 한다 — 스테이지 안에서 # 선언하면(예: 이전엔 builder 스테이지 RUN 다음에 둠) 그 스테이지 지역 변수가 되어 다음 # FROM 의 이미지명 해석에 쓰이지 않는다(실측: "FROM argument 'RUNTIME_BASE' is not # declared" 경고와 함께 빈 이미지명 에러 발생). ARG RUNTIME_BASE=registry.suse.com/bci/bci-micro:15.7 # 호스트 네이티브 아키텍처로 빌더를 띄운다. Go 크로스컴파일은 에뮬레이션이 필요 없으므로 # --platform=$BUILDPLATFORM 로 고정해 에뮬레이션 오버헤드를 피한다(로컬 arm64 Docker 데스크톱 # 에서 linux/amd64 결과물을 만들 때 특히 중요). FROM --platform=$BUILDPLATFORM golang:${GO_BUILDER_TAG} AS builder ARG TARGETARCH ARG SOURCE_COMMIT ARG APP_VERSION WORKDIR /src # BuildKit 의 git context 지원 — tarball+checksum 관리 없이 git 자체가 커밋 무결성을 보장한다. ADD https://github.com/cloudnative-pg/cloudnative-pg.git#${SOURCE_COMMIT} /src # 업스트림 Makefile 의 LDFLAGS·.goreleaser.yml 의 build 설정을 그대로 재현한다. # (release-1.30 기준 go.mod: go 1.26.5, google.golang.org/grpc v1.82.1, golang.org/x/text v0.39.0) RUN --mount=type=cache,target=/root/go/pkg/mod \ --mount=type=cache,target=/root/.cache/go-build \ set -eux; \ CGO_ENABLED=0 GOOS=linux GOARCH=${TARGETARCH} go build -trimpath \ -ldflags "-s -w \ -X github.com/cloudnative-pg/cloudnative-pg/pkg/versions.buildVersion=${APP_VERSION} \ -X github.com/cloudnative-pg/cloudnative-pg/pkg/versions.buildCommit=${SOURCE_COMMIT} \ -X github.com/cloudnative-pg/cloudnative-pg/pkg/versions.buildDate=$(date -u +%Y-%m-%d)" \ -o /out/manager ./cmd/manager FROM ${RUNTIME_BASE} AS final WORKDIR / # bci-micro 는 root 만 있고 65532 사용자가 없다(distroless 의 nonroot 변종과 달리 nonroot # 계정을 미리 만들어두지 않는다) — 직접 만든다. bci-micro 에 bash·coreutils 는 있다 # (zypper·rpm 은 없음 — "micro" 는 패키지 매니저가 빠진 것이지 셸까지 없는 건 아니다). RUN set -eux; \ echo 'nonroot:x:65532:65532:nonroot:/home/nonroot:/bin/false' >> /etc/passwd; \ echo 'nonroot:x:65532:' >> /etc/group; \ mkdir -p /home/nonroot; \ chown 65532:65532 /home/nonroot # 실측으로 드러난 필수 조건: 오퍼레이터는 `operator/manager_` 를 런타임에 glob 해서 # "가용 아키텍처" 목록을 만든다(pkg/utils/discovery.go DetectAvailableArchitectures) — 이 # 목록이 비어 있으면 Cluster 리컨실이 "invalid architecture: amd64" 로 실패한다(배포 # 테스트 2026-07-30 에서 재현). 업스트림은 멀티아치 심볼릭 링크로 이걸 만들지만, 우리는 # 단일 아키텍처만 다루므로 같은 바이너리를 두 경로에 COPY 해 같은 효과를 낸다. COPY --from=builder /out/manager /manager COPY --from=builder /out/manager /operator/manager_amd64 COPY --from=builder /src/licenses /licenses COPY --from=builder /src/LICENSE /licenses/LICENSE USER 65532:65532 ENTRYPOINT ["/manager"]