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
|
**What differs from the homelab copy**, all of it a consequence of GKE
|
||||||
being a real cloud rather than one VM:
|
being a real cloud rather than one VM:
|
||||||
|
|
||||||
- **The registry hostname** is `harbor.35.238.248.203.nip.io`, in the push
|
- **The registry hostname** is `harbor.infra.deployshed.com`, in the push
|
||||||
target and in all five fallback Dockerfiles.
|
target (`harbor_registry`, see the table below) and in all five fallback
|
||||||
- **Harbor speaks TLS.** The homelab's dind passes `--insecure-registry`;
|
Dockerfiles.
|
||||||
here the pod mounts the private CA into dockerd's trust store instead.
|
- **Harbor speaks TLS, with a public certificate.** The homelab's dind
|
||||||
Node trust covers pulls only — a push is a separate client.
|
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
|
- **`helm_repo_url` uses cluster DNS**, since the clone happens inside a
|
||||||
build pod. The homelab points it at an ingress hostname.
|
build pod. The homelab points it at an ingress hostname.
|
||||||
- **`build-tools` is not in this repo.** It lives in
|
- **`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/` |
|
| `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` |
|
| `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 |
|
| `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. |
|
| `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. |
|
| `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
|
## Build-tools image
|
||||||
|
|
||||||
The `docker-cli` container runs
|
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
|
git, yq, bash, python3 with pip and venv, and curl, so nothing is installed
|
||||||
on demand on every build.
|
on demand on every build.
|
||||||
|
|
||||||
@@ -103,14 +105,19 @@ until that pin is bumped. Bump the tag rather than overwriting one.
|
|||||||
|
|
||||||
## Registry trust
|
## Registry trust
|
||||||
|
|
||||||
`dind-pod.yaml` mounts the `registry-ca` ConfigMap (published by
|
Nothing to configure. Harbor is reached at `harbor.infra.deployshed.com`,
|
||||||
`devops-infra-argo-config-gcp`) into the dind container at
|
which carries a publicly trusted Let's Encrypt certificate, so both
|
||||||
`/etc/docker/certs.d/harbor.35.238.248.203.nip.io/ca.crt`.
|
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
|
This used to be a real piece of setup, and the history is worth keeping
|
||||||
succeed, which reads like a broken registry. The reason is that the two are
|
because reintroducing a nip.io registry name would bring it all back. A
|
||||||
different clients: pulls are performed by containerd on the node, which was
|
nip.io address cannot have a public certificate — it is not on the public
|
||||||
told to trust this CA when the node pool was created, whereas the push comes
|
suffix list, and every `*.nip.io` certificate on the internet shares one
|
||||||
from dockerd inside the build pod, which has its own trust store. The
|
rate limit — so cert-manager signed Harbor from a private CA instead. The
|
||||||
directory name must be the registry hostname exactly — dockerd looks the
|
node pool was told to trust that CA at creation, which covered pulls; the
|
||||||
path up by host and silently ignores a mismatch.
|
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
|
# a dockerBuildVersion whose tag isn't in devops-base-images/images.txt
|
||||||
# yet needs that added and re-mirrored first, unlike pulling straight
|
# yet needs that added and re-mirrored first, unlike pulling straight
|
||||||
# from Docker Hub where any tag "just worked".
|
# 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
|
WORKDIR /src
|
||||||
COPY go.mod go.sum* ./
|
COPY go.mod go.sum* ./
|
||||||
RUN go mod download 2>/dev/null || true
|
RUN go mod download 2>/dev/null || true
|
||||||
COPY . .
|
COPY . .
|
||||||
RUN CGO_ENABLED=0 go build -o /app .
|
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
|
COPY --from=build /app /app
|
||||||
EXPOSE 8080
|
EXPOSE 8080
|
||||||
ENTRYPOINT ["/app"]
|
ENTRYPOINT ["/app"]
|
||||||
|
|||||||
@@ -8,14 +8,14 @@
|
|||||||
# to Maven Central for plugins/dependencies during the build regardless
|
# to Maven Central for plugins/dependencies during the build regardless
|
||||||
# of base image — this only removes the Docker Hub dependency for the
|
# of base image — this only removes the Docker Hub dependency for the
|
||||||
# base image layer, not package-registry traffic during the build.
|
# 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
|
WORKDIR /src
|
||||||
COPY pom.xml .
|
COPY pom.xml .
|
||||||
RUN mvn -B dependency:go-offline
|
RUN mvn -B dependency:go-offline
|
||||||
COPY . .
|
COPY . .
|
||||||
RUN mvn -B package -DskipTests
|
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
|
WORKDIR /app
|
||||||
COPY --from=build /src/target/*.jar app.jar
|
COPY --from=build /src/target/*.jar app.jar
|
||||||
EXPOSE 8080
|
EXPOSE 8080
|
||||||
|
|||||||
@@ -5,14 +5,14 @@
|
|||||||
# Assumes a standard `npm run build` + `npm start` repo. Switched from
|
# Assumes a standard `npm run build` + `npm start` repo. Switched from
|
||||||
# node:*-slim (Debian) to node:*-alpine for both stages — smaller, still
|
# node:*-slim (Debian) to node:*-alpine for both stages — smaller, still
|
||||||
# keeps a shell for kubectl exec debugging (not distroless).
|
# 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
|
WORKDIR /app
|
||||||
COPY package*.json ./
|
COPY package*.json ./
|
||||||
RUN npm ci
|
RUN npm ci
|
||||||
COPY . .
|
COPY . .
|
||||||
RUN npm run build --if-present
|
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
|
WORKDIR /app
|
||||||
COPY --from=build /app .
|
COPY --from=build /app .
|
||||||
ENV NODE_ENV=production
|
ENV NODE_ENV=production
|
||||||
|
|||||||
@@ -36,7 +36,7 @@
|
|||||||
# very comment did so), new edits to this header should avoid typing
|
# 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 character at all — write "dollar sign" in words instead of using
|
||||||
# the glyph.
|
# 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
|
WORKDIR /var/www/html
|
||||||
RUN apk add --no-cache --virtual .build-deps \$PHPIZE_DEPS \
|
RUN apk add --no-cache --virtual .build-deps \$PHPIZE_DEPS \
|
||||||
&& docker-php-ext-install pdo pdo_mysql \
|
&& docker-php-ext-install pdo pdo_mysql \
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
# extensions that only ship glibc wheels may need musl-dev/gcc added
|
# 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
|
# 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.
|
# 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
|
WORKDIR /app
|
||||||
COPY requirements.txt .
|
COPY requirements.txt .
|
||||||
RUN pip install --no-cache-dir -r requirements.txt
|
RUN pip install --no-cache-dir -r requirements.txt
|
||||||
|
|||||||
@@ -15,34 +15,33 @@ spec:
|
|||||||
image: docker:27-dind
|
image: docker:27-dind
|
||||||
securityContext:
|
securityContext:
|
||||||
privileged: true
|
privileged: true
|
||||||
# No --insecure-registry, unlike the homelab: Harbor here serves a real
|
# No --insecure-registry and no mounted CA: Harbor is reached at
|
||||||
# certificate, issued by cert-manager from the private CA that the node
|
# harbor.infra.deployshed.com, which carries a publicly trusted Let's
|
||||||
# pool was told to trust when it was created.
|
# 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
|
# This previously mounted the private registry CA here, because the
|
||||||
# node. This push is a different client — dockerd, inside this pod,
|
# registry was a nip.io name that no public CA can issue for (nip.io is
|
||||||
# with its own trust store and no knowledge of what the node trusts —
|
# not on the public suffix list, and every *.nip.io certificate shares
|
||||||
# so it needs the CA mounted itself. dockerd looks it up at
|
# one rate limit), so cert-manager signed it from a private CA instead.
|
||||||
# /etc/docker/certs.d/<registry host>/ca.crt, and the directory name
|
# A pull was fine — the node pool was told to trust that CA and
|
||||||
# must be the registry hostname exactly; anything else is silently
|
# containerd performs pulls — but a push is a different client with its
|
||||||
# ignored, and the push then fails TLS verification while a pull of the
|
# own trust store, which is why dockerd needed the CA at
|
||||||
# very same image works.
|
# /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)
|
# The registry is still named by its ingress hostname rather than
|
||||||
# carries over unchanged from the homelab, for a reason that still
|
# harbor-core.harbor.svc.cluster.local, for a reason that still holds:
|
||||||
# holds: cluster DNS resolves from this pod but not from the node's
|
# cluster DNS resolves from this pod but not from the node's containerd
|
||||||
# containerd doing the real Deployment pull, and Docker matches both
|
# doing the real Deployment pull, and Docker matches both stored
|
||||||
# stored credentials and trust by exact hostname — so push and pull
|
# credentials and trust by exact hostname — so push and pull have to
|
||||||
# have to name the registry identically.
|
# name the registry identically.
|
||||||
env:
|
env:
|
||||||
- name: DOCKER_TLS_CERTDIR
|
- name: DOCKER_TLS_CERTDIR
|
||||||
value: ""
|
value: ""
|
||||||
volumeMounts:
|
volumeMounts:
|
||||||
- name: docker-graph-storage
|
- name: docker-graph-storage
|
||||||
mountPath: /var/lib/docker
|
mountPath: /var/lib/docker
|
||||||
- name: registry-ca
|
|
||||||
mountPath: /etc/docker/certs.d/harbor.35.238.248.203.nip.io
|
|
||||||
readOnly: true
|
|
||||||
- name: docker-cli
|
- name: docker-cli
|
||||||
# Custom image, built and pushed by hand from devops-base-images-gcp
|
# Custom image, built and pushed by hand from devops-base-images-gcp
|
||||||
# (build-tools.Dockerfile there) — bakes in git/yq/bash/python3 with
|
# (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
|
# Versioned tag, never :latest — rebuilding the tools image must not
|
||||||
# roll out until this pin is bumped deliberately.
|
# 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"]
|
command: ["cat"]
|
||||||
tty: true
|
tty: true
|
||||||
env:
|
env:
|
||||||
@@ -92,13 +91,6 @@ spec:
|
|||||||
volumes:
|
volumes:
|
||||||
- name: docker-graph-storage
|
- name: docker-graph-storage
|
||||||
emptyDir: {}
|
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
|
- name: docker-config
|
||||||
secret:
|
secret:
|
||||||
secretName: harbor-robot-dockerconfig
|
secretName: harbor-robot-dockerconfig
|
||||||
|
|||||||
@@ -30,9 +30,25 @@ def run(Map config) {
|
|||||||
//
|
//
|
||||||
// It also has to be spelled identically everywhere, because Docker
|
// It also has to be spelled identically everywhere, because Docker
|
||||||
// matches stored credentials and TLS trust by exact hostname: here, the
|
// matches stored credentials and TLS trust by exact hostname: here, the
|
||||||
// dockerconfigjson auths key, Harbor's externalURL, and the node pool's
|
// dockerconfigjson auths key and Harbor's externalURL.
|
||||||
// CA trust config.
|
//
|
||||||
def image = "harbor.35.238.248.203.nip.io/${config.harbor_project}/${config.repo_name}:${tag}"
|
// 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 {
|
try {
|
||||||
stage(stageName('Build & push image')) {
|
stage(stageName('Build & push image')) {
|
||||||
container('docker-cli') {
|
container('docker-cli') {
|
||||||
|
|||||||
@@ -23,7 +23,23 @@
|
|||||||
def call(Map config) {
|
def call(Map config) {
|
||||||
config.service_name = config.service_name ?: config.repo_name
|
config.service_name = config.service_name ?: config.repo_name
|
||||||
config.argo_app_name = config.argo_app_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
|
// 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
|
// pod, so it is pod-to-pod traffic and has no business leaving the
|
||||||
// cluster and coming back in through Contour.
|
// cluster and coming back in through Contour.
|
||||||
|
|||||||
Reference in New Issue
Block a user