Compare commits
2
Commits
3e09eecbfe
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6d743cbe48 | ||
|
|
a9119d7b8e |
@@ -13,11 +13,12 @@ property of the Jenkins it runs on.
|
||||
**What differs from the homelab copy**, all of it a consequence of GKE
|
||||
being a real cloud rather than one VM:
|
||||
|
||||
- **The registry hostname** is `harbor.35.238.248.203.nip.io`, in the push
|
||||
target and in all five fallback Dockerfiles.
|
||||
- **Harbor speaks TLS.** The homelab's dind passes `--insecure-registry`;
|
||||
here the pod mounts the private CA into dockerd's trust store instead.
|
||||
Node trust covers pulls only — a push is a separate client.
|
||||
- **The registry hostname** is `harbor.infra.deployshed.com`, in the push
|
||||
target (`harbor_registry`, see the table below) and in all five fallback
|
||||
Dockerfiles.
|
||||
- **Harbor speaks TLS, with a public certificate.** The homelab's dind
|
||||
passes `--insecure-registry`; here nothing is needed at all, because
|
||||
Let's Encrypt issues for the real domain and dockerd already trusts it.
|
||||
- **`helm_repo_url` uses cluster DNS**, since the clone happens inside a
|
||||
build pod. The homelab points it at an ingress hostname.
|
||||
- **`build-tools` is not in this repo.** It lives in
|
||||
@@ -46,7 +47,8 @@ in after checkout; repo-committed values win over the Jenkinsfile call).
|
||||
|---|---|---|
|
||||
| `service_name` | `repo_name` | Second path segment under `devops-helm-charts/values/` |
|
||||
| `argo_app_name` | `repo_name` | Must match the ArgoCD Application's `metadata.name` |
|
||||
| `harbor_project` | `homelab` | Must be an existing, public Harbor project |
|
||||
| `harbor_registry` | `harbor.infra.deployshed.com` | The registry hostname images are pushed to and pulled from. Must be spelled identically here, in the `dockerconfigjson` `auths` key, and in Harbor's `externalURL` — docker matches both stored credentials and TLS trust by exact hostname, so a mismatch fails as `unauthorized` rather than as a name problem |
|
||||
| `harbor_project` | `apps-registry` | Must already exist in Harbor, and be public unless you also wire an `imagePullSecret` — the app values assume anonymous pull. A push to a missing project fails as `unauthorized: project <name> not found` |
|
||||
| `helm_repo_url` | `devops-helm-charts-gcp`, over cluster DNS | `http://gitea-http.gitea.svc.cluster.local:3000/gitadmin/…` — pod-to-pod, so it never leaves the cluster and comes back through the ingress |
|
||||
| `image_tag_yq_path` | `.deployment.image.tag` | **Override this if the app's chart isn't `1.0.0`** — e.g. `sts-2.0.0` uses `.podtemplate.image.tag` instead. Getting this wrong doesn't fail loudly: `yq -i` creates the path if missing rather than erroring, silently leaving the real field un-bumped. |
|
||||
| `dockerBuildVersion` | none | Only read when the repo has **no Dockerfile of its own** — picks a fallback template (see below). No default; either ship a Dockerfile or set this. |
|
||||
@@ -89,7 +91,7 @@ a `podTemplate` (`resources/org/homelab/dind-pod.yaml`) via
|
||||
## Build-tools image
|
||||
|
||||
The `docker-cli` container runs
|
||||
`harbor.35.238.248.203.nip.io/base-images/build-tools:1`, which bakes in
|
||||
`harbor.infra.deployshed.com/base-images/build-tools:1`, which bakes in
|
||||
git, yq, bash, python3 with pip and venv, and curl, so nothing is installed
|
||||
on demand on every build.
|
||||
|
||||
@@ -103,14 +105,19 @@ until that pin is bumped. Bump the tag rather than overwriting one.
|
||||
|
||||
## Registry trust
|
||||
|
||||
`dind-pod.yaml` mounts the `registry-ca` ConfigMap (published by
|
||||
`devops-infra-argo-config-gcp`) into the dind container at
|
||||
`/etc/docker/certs.d/harbor.35.238.248.203.nip.io/ca.crt`.
|
||||
Nothing to configure. Harbor is reached at `harbor.infra.deployshed.com`,
|
||||
which carries a publicly trusted Let's Encrypt certificate, so both
|
||||
containerd on the node (pulls) and dockerd in the build pod (pushes) accept
|
||||
it out of the box.
|
||||
|
||||
Without it, pushes fail TLS verification while pulls of the same image
|
||||
succeed, which reads like a broken registry. The reason is that the two are
|
||||
different clients: pulls are performed by containerd on the node, which was
|
||||
told to trust this CA when the node pool was created, whereas the push comes
|
||||
from dockerd inside the build pod, which has its own trust store. The
|
||||
directory name must be the registry hostname exactly — dockerd looks the
|
||||
path up by host and silently ignores a mismatch.
|
||||
This used to be a real piece of setup, and the history is worth keeping
|
||||
because reintroducing a nip.io registry name would bring it all back. A
|
||||
nip.io address cannot have a public certificate — it is not on the public
|
||||
suffix list, and every `*.nip.io` certificate on the internet shares one
|
||||
rate limit — so cert-manager signed Harbor from a private CA instead. The
|
||||
node pool was told to trust that CA at creation, which covered pulls; the
|
||||
push came from dockerd inside the build pod, a separate client with its own
|
||||
trust store, so `dind-pod.yaml` had to mount the CA at
|
||||
`/etc/docker/certs.d/<registry host>/ca.crt` as well. The failure when that
|
||||
mount was missing was thoroughly confusing: pushes failed TLS verification
|
||||
while pulls of the very same image succeeded.
|
||||
|
||||
@@ -13,14 +13,14 @@
|
||||
# a dockerBuildVersion whose tag isn't in devops-base-images/images.txt
|
||||
# yet needs that added and re-mirrored first, unlike pulling straight
|
||||
# from Docker Hub where any tag "just worked".
|
||||
FROM harbor.35.238.248.203.nip.io/base-images/golang:${version}-alpine AS build
|
||||
FROM harbor.infra.deployshed.com/base-images/golang:${version}-alpine AS build
|
||||
WORKDIR /src
|
||||
COPY go.mod go.sum* ./
|
||||
RUN go mod download 2>/dev/null || true
|
||||
COPY . .
|
||||
RUN CGO_ENABLED=0 go build -o /app .
|
||||
|
||||
FROM harbor.35.238.248.203.nip.io/base-images/alpine:3.20
|
||||
FROM harbor.infra.deployshed.com/base-images/alpine:3.20
|
||||
COPY --from=build /app /app
|
||||
EXPOSE 8080
|
||||
ENTRYPOINT ["/app"]
|
||||
|
||||
@@ -8,14 +8,14 @@
|
||||
# to Maven Central for plugins/dependencies during the build regardless
|
||||
# of base image — this only removes the Docker Hub dependency for the
|
||||
# base image layer, not package-registry traffic during the build.
|
||||
FROM harbor.35.238.248.203.nip.io/base-images/maven:3-eclipse-temurin-${version}-alpine AS build
|
||||
FROM harbor.infra.deployshed.com/base-images/maven:3-eclipse-temurin-${version}-alpine AS build
|
||||
WORKDIR /src
|
||||
COPY pom.xml .
|
||||
RUN mvn -B dependency:go-offline
|
||||
COPY . .
|
||||
RUN mvn -B package -DskipTests
|
||||
|
||||
FROM harbor.35.238.248.203.nip.io/base-images/eclipse-temurin:${version}-jre-alpine
|
||||
FROM harbor.infra.deployshed.com/base-images/eclipse-temurin:${version}-jre-alpine
|
||||
WORKDIR /app
|
||||
COPY --from=build /src/target/*.jar app.jar
|
||||
EXPOSE 8080
|
||||
|
||||
@@ -5,14 +5,14 @@
|
||||
# Assumes a standard `npm run build` + `npm start` repo. Switched from
|
||||
# node:*-slim (Debian) to node:*-alpine for both stages — smaller, still
|
||||
# keeps a shell for kubectl exec debugging (not distroless).
|
||||
FROM harbor.35.238.248.203.nip.io/base-images/node:${version}-alpine AS build
|
||||
FROM harbor.infra.deployshed.com/base-images/node:${version}-alpine AS build
|
||||
WORKDIR /app
|
||||
COPY package*.json ./
|
||||
RUN npm ci
|
||||
COPY . .
|
||||
RUN npm run build --if-present
|
||||
|
||||
FROM harbor.35.238.248.203.nip.io/base-images/node:${version}-alpine
|
||||
FROM harbor.infra.deployshed.com/base-images/node:${version}-alpine
|
||||
WORKDIR /app
|
||||
COPY --from=build /app .
|
||||
ENV NODE_ENV=production
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
# very comment did so), new edits to this header should avoid typing
|
||||
# the character at all — write "dollar sign" in words instead of using
|
||||
# the glyph.
|
||||
FROM harbor.35.238.248.203.nip.io/base-images/php:${version}-cli-alpine
|
||||
FROM harbor.infra.deployshed.com/base-images/php:${version}-cli-alpine
|
||||
WORKDIR /var/www/html
|
||||
RUN apk add --no-cache --virtual .build-deps \$PHPIZE_DEPS \
|
||||
&& docker-php-ext-install pdo pdo_mysql \
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
# extensions that only ship glibc wheels may need musl-dev/gcc added
|
||||
# here to build from source on Alpine — fine for this repo's pure-Python
|
||||
# deps, worth knowing if a future repo's requirements.txt needs more.
|
||||
FROM harbor.35.238.248.203.nip.io/base-images/python:${version}-alpine
|
||||
FROM harbor.infra.deployshed.com/base-images/python:${version}-alpine
|
||||
WORKDIR /app
|
||||
COPY requirements.txt .
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
@@ -15,34 +15,33 @@ spec:
|
||||
image: docker:27-dind
|
||||
securityContext:
|
||||
privileged: true
|
||||
# No --insecure-registry, unlike the homelab: Harbor here serves a real
|
||||
# certificate, issued by cert-manager from the private CA that the node
|
||||
# pool was told to trust when it was created.
|
||||
# No --insecure-registry and no mounted CA: Harbor is reached at
|
||||
# harbor.infra.deployshed.com, which carries a publicly trusted Let's
|
||||
# Encrypt certificate, so dockerd's own trust store already accepts it
|
||||
# with nothing configured.
|
||||
#
|
||||
# That node trust covers image PULLS, which containerd performs on the
|
||||
# node. This push is a different client — dockerd, inside this pod,
|
||||
# with its own trust store and no knowledge of what the node trusts —
|
||||
# so it needs the CA mounted itself. dockerd looks it up at
|
||||
# /etc/docker/certs.d/<registry host>/ca.crt, and the directory name
|
||||
# must be the registry hostname exactly; anything else is silently
|
||||
# ignored, and the push then fails TLS verification while a pull of the
|
||||
# very same image works.
|
||||
# This previously mounted the private registry CA here, because the
|
||||
# registry was a nip.io name that no public CA can issue for (nip.io is
|
||||
# not on the public suffix list, and every *.nip.io certificate shares
|
||||
# one rate limit), so cert-manager signed it from a private CA instead.
|
||||
# A pull was fine — the node pool was told to trust that CA and
|
||||
# containerd performs pulls — but a push is a different client with its
|
||||
# own trust store, which is why dockerd needed the CA at
|
||||
# /etc/docker/certs.d/<registry host>/ca.crt. Moving to a real domain
|
||||
# removes the whole arrangement rather than repointing it.
|
||||
#
|
||||
# The registry hostname (rather than harbor-core.harbor.svc.cluster.local)
|
||||
# carries over unchanged from the homelab, for a reason that still
|
||||
# holds: cluster DNS resolves from this pod but not from the node's
|
||||
# containerd doing the real Deployment pull, and Docker matches both
|
||||
# stored credentials and trust by exact hostname — so push and pull
|
||||
# have to name the registry identically.
|
||||
# The registry is still named by its ingress hostname rather than
|
||||
# harbor-core.harbor.svc.cluster.local, for a reason that still holds:
|
||||
# cluster DNS resolves from this pod but not from the node's containerd
|
||||
# doing the real Deployment pull, and Docker matches both stored
|
||||
# credentials and trust by exact hostname — so push and pull have to
|
||||
# name the registry identically.
|
||||
env:
|
||||
- name: DOCKER_TLS_CERTDIR
|
||||
value: ""
|
||||
volumeMounts:
|
||||
- name: docker-graph-storage
|
||||
mountPath: /var/lib/docker
|
||||
- name: registry-ca
|
||||
mountPath: /etc/docker/certs.d/harbor.35.238.248.203.nip.io
|
||||
readOnly: true
|
||||
- name: docker-cli
|
||||
# Custom image, built and pushed by hand from devops-base-images-gcp
|
||||
# (build-tools.Dockerfile there) — bakes in git/yq/bash/python3 with
|
||||
@@ -55,7 +54,7 @@ spec:
|
||||
#
|
||||
# Versioned tag, never :latest — rebuilding the tools image must not
|
||||
# roll out until this pin is bumped deliberately.
|
||||
image: harbor.35.238.248.203.nip.io/base-images/build-tools:1
|
||||
image: harbor.infra.deployshed.com/base-images/build-tools:1
|
||||
command: ["cat"]
|
||||
tty: true
|
||||
env:
|
||||
@@ -92,13 +91,6 @@ spec:
|
||||
volumes:
|
||||
- name: docker-graph-storage
|
||||
emptyDir: {}
|
||||
- name: registry-ca
|
||||
configMap:
|
||||
# Published by devops-infra-argo-config-gcp (extra-manifests). The
|
||||
# CA's public certificate only — its private key never leaves
|
||||
# Terraform state and cert-manager, which is why this is a ConfigMap
|
||||
# rather than a Secret.
|
||||
name: registry-ca
|
||||
- name: docker-config
|
||||
secret:
|
||||
secretName: harbor-robot-dockerconfig
|
||||
|
||||
@@ -30,9 +30,25 @@ def run(Map config) {
|
||||
//
|
||||
// It also has to be spelled identically everywhere, because Docker
|
||||
// matches stored credentials and TLS trust by exact hostname: here, the
|
||||
// dockerconfigjson auths key, Harbor's externalURL, and the node pool's
|
||||
// CA trust config.
|
||||
def image = "harbor.35.238.248.203.nip.io/${config.harbor_project}/${config.repo_name}:${tag}"
|
||||
// dockerconfigjson auths key and Harbor's externalURL.
|
||||
//
|
||||
// Configurable rather than hardcoded, which is what this used to be. A
|
||||
// literal here meant the registry could not move without editing the
|
||||
// shared library itself, and every consumer moving in the same commit —
|
||||
// the failure being a push to a hostname nothing serves, several minutes
|
||||
// into a build.
|
||||
//
|
||||
// The DEFAULT lives in homelabPipeline.groovy beside harbor_project and
|
||||
// every other key, not here. Repeating the literal in both places would
|
||||
// leave two defaults free to disagree, and the one that lost would only
|
||||
// show up as a push to the wrong registry. Failing loudly beats
|
||||
// defaulting quietly: an unset value would otherwise build an image
|
||||
// named "null/<project>/<repo>", which docker accepts as a hostname and
|
||||
// then fails to resolve, pointing nowhere near the cause.
|
||||
if (!config.harbor_registry) {
|
||||
error("buildDocker: config.harbor_registry is not set. homelabPipeline normally defaults it; if this stage is being called directly, pass harbor_registry (e.g. 'harbor.infra.deployshed.com').")
|
||||
}
|
||||
def image = "${config.harbor_registry}/${config.harbor_project}/${config.repo_name}:${tag}"
|
||||
try {
|
||||
stage(stageName('Build & push image')) {
|
||||
container('docker-cli') {
|
||||
|
||||
@@ -23,7 +23,23 @@
|
||||
def call(Map config) {
|
||||
config.service_name = config.service_name ?: config.repo_name
|
||||
config.argo_app_name = config.argo_app_name ?: config.repo_name
|
||||
config.harbor_project = config.harbor_project ?: 'homelab'
|
||||
// The Harbor project images are pushed to. Named apps-registry on this
|
||||
// cluster, not the homelab's "homelab" — the project has to already
|
||||
// exist, and Harbor rejects a push to a missing one with
|
||||
// "unauthorized: project <name> not found", which reads like a
|
||||
// credentials problem rather than a missing project.
|
||||
config.harbor_project = config.harbor_project ?: 'apps-registry'
|
||||
// The registry hostname images are pushed to and pulled from. This is
|
||||
// the ONLY default for it — buildDocker.groovy deliberately errors
|
||||
// rather than carrying a second copy, since two defaults for one value
|
||||
// are free to disagree and the loser only shows up as a push to the
|
||||
// wrong registry.
|
||||
//
|
||||
// It must be spelled identically here, in the dockerconfigjson auths
|
||||
// key, and in Harbor's own externalURL: docker matches both stored
|
||||
// credentials and TLS trust by exact hostname, so a mismatch fails as
|
||||
// "unauthorized" rather than as anything resembling a name problem.
|
||||
config.harbor_registry = config.harbor_registry ?: 'harbor.infra.deployshed.com'
|
||||
// Cluster DNS, not the ingress hostname: this clone happens from a build
|
||||
// pod, so it is pod-to-pod traffic and has no business leaving the
|
||||
// cluster and coming back in through Contour.
|
||||
|
||||
Reference in New Issue
Block a user