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
- name: docker-config
mountPath: /root/.docker
readOnly: true
volumes:
- name: docker-graph-storage
emptyDir: {}
- name: mirror
configMap:
name: base-images-mirror
defaultMode: 0755
- name: docker-config
secret:
secretName: harbor-robot-dockerconfig
items:
- key: .dockerconfigjson
path: config.json
EOF
kubectl wait --for=condition=Ready pod/base-images-mirror -n jenkins --timeout=120s
kubectl exec -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
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).