devops-base-images

One-time-setup repo for mirroring minimized base images into Harbor's base-images project, so language builds (via devops-lib's buildDocker.groovy fallback templates) never depend on Docker Hub at build time, and the images that actually ship are the leanest official variant available for each language.

Not wired into any Jenkins pipeline — this is deliberately a manual, occasional operation (re-run when images.txt changes: a new language, a version bump, or picking up an upstream base-image update), using the same one-off kubectl-based DinD pod pattern devops-lib's build-tools.Dockerfile bootstrap already uses, since the VM itself may not have Docker installed directly.

One-time setup

1. Create the Harbor project (public — these are just re-hosted public images, no confidentiality concern, and public avoids needing any pull credentials wired into every build):

Harbor UI → New Project → name base-images → check Public → Create.

Or via API:

curl -X POST -u admin:'<harbor admin password>' -H "Content-Type: application/json" \
  -d '{"project_name":"base-images","metadata":{"public":"true"}}' \
  "http://harbor.192.168.1.7.nip.io/api/v2.0/projects"

2. Run the mirror, via a throwaway DinD pod (same shape as the build-tools image bootstrap):

cat <<'EOF' | kubectl apply -f -
apiVersion: v1
kind: ConfigMap
metadata:
  name: base-images-mirror
  namespace: jenkins
data:
  images.txt: |
    golang:1.22-alpine golang:1.22-alpine
    alpine:3.20 alpine:3.20
    node:20-alpine node:20-alpine
    python:3.12-alpine python:3.12-alpine
    maven:3-eclipse-temurin-21-alpine maven:3-eclipse-temurin-21-alpine
    eclipse-temurin:21-jre-alpine eclipse-temurin:21-jre-alpine
    php:8.3-cli-alpine php:8.3-cli-alpine
  mirror.sh: |
    #!/bin/sh
    set -eu
    REGISTRY="harbor.192.168.1.7.nip.io"
    PROJECT="base-images"
    while read -r src target; do
        [ -z "$src" ] && continue
        case "$src" in \#*) continue ;; esac
        dest="${REGISTRY}/${PROJECT}/${target}"
        echo "=== ${src} -> ${dest} ==="
        docker pull "$src"
        docker tag "$src" "$dest"
        docker push "$dest"
    done < images.txt
    echo "=== done ==="
---
apiVersion: v1
kind: Pod
metadata:
  name: base-images-mirror
  namespace: jenkins
spec:
  restartPolicy: Never
  containers:
    - name: docker
      image: docker:27-dind
      securityContext:
        privileged: true
      args:
        - "--insecure-registry=harbor.192.168.1.7.nip.io"
      env:
        - name: DOCKER_TLS_CERTDIR
          value: ""
      volumeMounts:
        - name: docker-graph-storage
          mountPath: /var/lib/docker
    - name: docker-cli
      image: docker:27-cli
      command: ["cat"]
      tty: true
      env:
        - name: DOCKER_HOST
          value: tcp://localhost:2375
      volumeMounts:
        - name: mirror
          mountPath: /mirror
  volumes:
    - name: docker-graph-storage
      emptyDir: {}
    - name: mirror
      configMap:
        name: base-images-mirror
        defaultMode: 0755
EOF

No harbor-robot-dockerconfig mount here on purpose — that robot account is scoped only to the homelab project (push+pull there specifically); it has no grant on base-images at all, and making base-images public only grants anonymous pull, never push. Push always needs real credentials scoped to that project regardless. For a one-off manual task like this, simplest is just logging in with your Harbor admin account directly inside the pod, rather than provisioning a whole new robot account for something run this rarely:

kubectl wait --for=condition=Ready pod/base-images-mirror -n jenkins --timeout=120s
kubectl exec -it -n jenkins base-images-mirror -c docker-cli -- sh -c '
  for i in $(seq 1 30); do docker info >/dev/null 2>&1 && break; sleep 2; done
  docker login harbor.192.168.1.7.nip.io
'

(enter your Harbor admin username/password when prompted)

kubectl exec -n jenkins base-images-mirror -c docker-cli -- sh -c '
  cd /mirror && sh mirror.sh
'
kubectl delete pod base-images-mirror -n jenkins
kubectl delete configmap base-images-mirror -n jenkins

3. Verify:

curl -s http://harbor.192.168.1.7.nip.io/v2/_catalog

Should list base-images/golang, base-images/alpine, base-images/node, base-images/python, base-images/maven, base-images/eclipse-temurin, base-images/php.

Adding a new image / updating a version

Add a line to images.txt, commit, push, then re-run step 2 above (the ConfigMap's images.txt needs updating to match — copy the current file content in, same as the initial setup).

S
Description
No description provided
Readme
47 KiB
Languages
Dockerfile 59.8%
Shell 40.2%