ci: refactor harbor arm build pipeline
This commit is contained in:
parent
8f3b22eb58
commit
75bda19605
222
Jenkinsfile
vendored
222
Jenkinsfile
vendored
@ -1,6 +1,10 @@
|
|||||||
pipeline {
|
pipeline {
|
||||||
triggers {
|
parameters {
|
||||||
pollSCM('H/5 * * * *')
|
string(
|
||||||
|
name: 'HARBOR_VERSION',
|
||||||
|
defaultValue: '',
|
||||||
|
description: 'Harbor tag to build (e.g. v2.14.1). Leave empty to build the latest release.'
|
||||||
|
)
|
||||||
}
|
}
|
||||||
agent {
|
agent {
|
||||||
kubernetes {
|
kubernetes {
|
||||||
@ -12,52 +16,29 @@ kind: Pod
|
|||||||
spec:
|
spec:
|
||||||
nodeSelector:
|
nodeSelector:
|
||||||
kubernetes.io/arch: arm64
|
kubernetes.io/arch: arm64
|
||||||
hardware: rpi5
|
|
||||||
containers:
|
containers:
|
||||||
- name: dind
|
- name: builder
|
||||||
image: docker:27-dind
|
image: quay.io/podman/stable:v5.2
|
||||||
|
tty: true
|
||||||
|
command: ["cat"]
|
||||||
securityContext:
|
securityContext:
|
||||||
privileged: true
|
privileged: true
|
||||||
env:
|
env:
|
||||||
- name: DOCKER_TLS_CERTDIR
|
- name: DOCKER_CONFIG
|
||||||
value: ""
|
value: /root/.docker
|
||||||
command:
|
- name: REGISTRY_AUTH_FILE
|
||||||
- /bin/sh
|
value: /root/.docker/config.json
|
||||||
- -c
|
- name: XDG_RUNTIME_DIR
|
||||||
args:
|
value: /var/run/containers
|
||||||
- |
|
|
||||||
set -euo pipefail
|
|
||||||
apk add --no-cache dnsmasq
|
|
||||||
cat > /etc/dnsmasq.conf <<'EOF'
|
|
||||||
filter-AAAA
|
|
||||||
# Use multiple upstream DNS servers to avoid transient resolver outages.
|
|
||||||
# Keep cluster DNS first for internal names, then fall back to public resolvers.
|
|
||||||
server=10.43.0.10
|
|
||||||
server=1.1.1.1
|
|
||||||
server=8.8.8.8
|
|
||||||
EOF
|
|
||||||
dnsmasq
|
|
||||||
echo "nameserver 127.0.0.1" > /etc/resolv.conf
|
|
||||||
exec dockerd-entrypoint.sh --mtu=1400
|
|
||||||
volumeMounts:
|
|
||||||
- name: dind-storage
|
|
||||||
mountPath: /var/lib/docker
|
|
||||||
- name: builder
|
|
||||||
image: docker:27
|
|
||||||
command: ["cat"]
|
|
||||||
tty: true
|
|
||||||
env:
|
|
||||||
- name: DOCKER_HOST
|
|
||||||
value: tcp://localhost:2375
|
|
||||||
- name: DOCKER_TLS_CERTDIR
|
|
||||||
value: ""
|
|
||||||
volumeMounts:
|
volumeMounts:
|
||||||
- name: workspace-volume
|
- name: workspace-volume
|
||||||
mountPath: /home/jenkins/agent
|
mountPath: /home/jenkins/agent
|
||||||
- name: docker-config-writable
|
|
||||||
mountPath: /root/.docker
|
|
||||||
- name: docker-config-secret
|
- name: docker-config-secret
|
||||||
mountPath: /docker-config
|
mountPath: /docker-config
|
||||||
|
- name: containers-storage
|
||||||
|
mountPath: /var/lib/containers
|
||||||
|
- name: podman-runtime
|
||||||
|
mountPath: /var/run/containers
|
||||||
volumes:
|
volumes:
|
||||||
- name: docker-config-secret
|
- name: docker-config-secret
|
||||||
secret:
|
secret:
|
||||||
@ -65,130 +46,147 @@ spec:
|
|||||||
items:
|
items:
|
||||||
- key: .dockerconfigjson
|
- key: .dockerconfigjson
|
||||||
path: config.json
|
path: config.json
|
||||||
- name: docker-config-writable
|
|
||||||
emptyDir: {}
|
|
||||||
- name: workspace-volume
|
- name: workspace-volume
|
||||||
emptyDir: {}
|
emptyDir: {}
|
||||||
- name: dind-storage
|
- name: containers-storage
|
||||||
|
emptyDir: {}
|
||||||
|
- name: podman-runtime
|
||||||
emptyDir: {}
|
emptyDir: {}
|
||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
environment {
|
environment {
|
||||||
VERSION = 'v2.14.1'
|
|
||||||
TAG_SUFFIX = '-arm64'
|
|
||||||
// Avoid `REGISTRY` here because Harbor's Makefiles use `REGISTRY=registry` and
|
|
||||||
// run sub-makes with `-e`, so environment variables override make vars.
|
|
||||||
IMAGE_NAMESPACE = 'registry.bstein.dev/infra'
|
IMAGE_NAMESPACE = 'registry.bstein.dev/infra'
|
||||||
HARBOR_TARBALL = "https://github.com/goharbor/harbor/archive/refs/tags/${VERSION}.tar.gz"
|
TAG_SUFFIX = '-arm64'
|
||||||
|
REGISTRY_URL = 'registry.bstein.dev'
|
||||||
}
|
}
|
||||||
options {
|
options {
|
||||||
disableConcurrentBuilds()
|
disableConcurrentBuilds()
|
||||||
|
timestamps()
|
||||||
}
|
}
|
||||||
stages {
|
stages {
|
||||||
stage('Checkout Jenkinsfile') {
|
stage('Checkout pipeline repo') {
|
||||||
steps {
|
steps {
|
||||||
git credentialsId: 'gitea-pat', url: 'https://scm.bstein.dev/bstein/harbor-arm-build.git'
|
git credentialsId: 'gitea-pat', url: 'https://scm.bstein.dev/bstein/harbor-arm-build.git'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
stage('Prep toolchain') {
|
stage('Setup tooling') {
|
||||||
steps {
|
steps {
|
||||||
container('builder') {
|
container('builder') {
|
||||||
sh '''
|
sh '''
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
apk add --no-cache bash curl make tar gzip git coreutils go ncurses
|
microdnf -y install git make curl tar gzip jq golang podman-docker python3
|
||||||
|
mkdir -p "${DOCKER_CONFIG}"
|
||||||
|
cp /docker-config/config.json "${DOCKER_CONFIG}/config.json"
|
||||||
|
# Make the Docker CLI invoke podman (buildah backend).
|
||||||
|
ln -sf /usr/bin/podman /usr/local/bin/docker
|
||||||
|
mkdir -p "${XDG_RUNTIME_DIR}"
|
||||||
'''
|
'''
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
stage('Fetch harbor source') {
|
stage('Resolve version') {
|
||||||
steps {
|
steps {
|
||||||
container('builder') {
|
container('builder') {
|
||||||
sh '''
|
script {
|
||||||
|
def resolved = sh(
|
||||||
|
label: 'pick version',
|
||||||
|
returnStdout: true,
|
||||||
|
script: '''
|
||||||
|
set -euo pipefail
|
||||||
|
if [ -n "${HARBOR_VERSION:-}" ]; then
|
||||||
|
echo "${HARBOR_VERSION}"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
curl -sSL https://api.github.com/repos/goharbor/harbor/releases/latest | python3 - <<'PY'
|
||||||
|
import json, sys
|
||||||
|
data = json.load(sys.stdin)
|
||||||
|
print(data.get("tag_name", "").strip())
|
||||||
|
PY
|
||||||
|
'''
|
||||||
|
).trim()
|
||||||
|
env.HARBOR_VERSION_RESOLVED = resolved
|
||||||
|
env.HARBOR_SRC_DIR = "harbor-src/harbor-${resolved.startsWith('v') ? resolved.substring(1) : resolved}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stage('Fetch source') {
|
||||||
|
steps {
|
||||||
|
container('builder') {
|
||||||
|
sh """
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
rm -rf harbor-src
|
rm -rf harbor-src
|
||||||
mkdir -p harbor-src
|
mkdir -p harbor-src
|
||||||
curl -sSL "${HARBOR_TARBALL}" | tar xz -C harbor-src
|
curl -sSL "https://github.com/goharbor/harbor/archive/refs/tags/${env.HARBOR_VERSION_RESOLVED}.tar.gz" | tar xz -C harbor-src
|
||||||
'''
|
ls -la harbor-src
|
||||||
|
"""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
stage('Build & push arm64 images') {
|
stage('Build & push (podman/buildah)') {
|
||||||
steps {
|
steps {
|
||||||
container('builder') {
|
container('builder') {
|
||||||
sh '''
|
sh """
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
VERSIONTAG="${VERSION}${TAG_SUFFIX}.${BUILD_NUMBER}"
|
export VERSIONTAG=\"${env.HARBOR_VERSION_RESOLVED}${TAG_SUFFIX}.${BUILD_NUMBER}\"
|
||||||
export VERSIONTAG
|
export BASEIMAGETAG=\"${env.HARBOR_VERSION_RESOLVED}${TAG_SUFFIX}.${BUILD_NUMBER}\"
|
||||||
mkdir -p /root/.docker
|
export IMAGENAMESPACE=\"${IMAGE_NAMESPACE}\"
|
||||||
cp /docker-config/config.json /root/.docker/config.json
|
export BASEIMAGENAMESPACE=\"${IMAGE_NAMESPACE}\"
|
||||||
|
export DOCKERNETWORK=host
|
||||||
# Harbor's build uses git metadata; Jenkins workspace ownership can trigger
|
export PULL_BASE_FROM_DOCKERHUB=false
|
||||||
# Git's safe.directory protection in containerized builds.
|
export BUILD_BASE=true
|
||||||
|
export BUILDTRIVYADP=false
|
||||||
|
export BUILD_INSTALLER=true
|
||||||
|
export BUILDAH_ISOLATION=chroot
|
||||||
|
export REGISTRY_AUTH_FILE=\"${DOCKER_CONFIG}/config.json\"
|
||||||
git config --global --add safe.directory '*'
|
git config --global --add safe.directory '*'
|
||||||
|
cd \"${env.HARBOR_SRC_DIR}\"
|
||||||
|
|
||||||
# `harbor-src/` itself matches `harbor-*`, so exclude it.
|
|
||||||
SRC_DIR=$(find harbor-src -mindepth 1 -maxdepth 1 -type d -name "harbor-*" | head -n1)
|
|
||||||
cd "${SRC_DIR}"
|
|
||||||
|
|
||||||
export DOCKER_BUILDKIT=1
|
|
||||||
|
|
||||||
echo "Sanity-checking Makefile vars..."
|
|
||||||
make -n \
|
|
||||||
VERSIONTAG="${VERSIONTAG}" \
|
|
||||||
BASEIMAGETAG="${VERSIONTAG}" \
|
|
||||||
IMAGENAMESPACE="${IMAGE_NAMESPACE}" \
|
|
||||||
BASEIMAGENAMESPACE="${IMAGE_NAMESPACE}" \
|
|
||||||
DOCKERNETWORK=host \
|
|
||||||
PULL_BASE_FROM_DOCKERHUB=false \
|
|
||||||
BUILD_BASE=true \
|
|
||||||
BUILDTRIVYADP=false \
|
|
||||||
BUILD_INSTALLER=true \
|
|
||||||
build \
|
|
||||||
| grep -E 'make -f .*/make/photon/Makefile build|IMAGENAMESPACE=|BASEIMAGENAMESPACE=|VERSIONTAG=|BASEIMAGETAG=|PULL_BASE_FROM_DOCKERHUB=' \
|
|
||||||
|| true
|
|
||||||
|
|
||||||
# Harbor's root Makefile hard-sets many defaults (e.g. IMAGENAMESPACE=goharbor,
|
|
||||||
# PULL_BASE_FROM_DOCKERHUB=true). Environment exports are not sufficient because
|
|
||||||
# makefile assignments override the environment. Pass overrides on the make
|
|
||||||
# command line so they win.
|
|
||||||
make compile
|
make compile
|
||||||
make \
|
make \\
|
||||||
VERSIONTAG="${VERSIONTAG}" \
|
VERSIONTAG=\"${env.HARBOR_VERSION_RESOLVED}${TAG_SUFFIX}.${BUILD_NUMBER}\" \\
|
||||||
BASEIMAGETAG="${VERSIONTAG}" \
|
BASEIMAGETAG=\"${env.HARBOR_VERSION_RESOLVED}${TAG_SUFFIX}.${BUILD_NUMBER}\" \\
|
||||||
IMAGENAMESPACE="${IMAGE_NAMESPACE}" \
|
IMAGENAMESPACE=\"${IMAGE_NAMESPACE}\" \\
|
||||||
BASEIMAGENAMESPACE="${IMAGE_NAMESPACE}" \
|
BASEIMAGENAMESPACE=\"${IMAGE_NAMESPACE}\" \\
|
||||||
DOCKERNETWORK=host \
|
DOCKERNETWORK=host \\
|
||||||
PULL_BASE_FROM_DOCKERHUB=false \
|
PULL_BASE_FROM_DOCKERHUB=false \\
|
||||||
BUILD_BASE=true \
|
BUILD_BASE=true \\
|
||||||
BUILDTRIVYADP=false \
|
BUILDTRIVYADP=false \\
|
||||||
BUILD_INSTALLER=true \
|
BUILD_INSTALLER=true \\
|
||||||
build
|
build
|
||||||
|
|
||||||
# Retag a few upstream image names to our internal naming convention
|
for pair in \\
|
||||||
# (so Helm values can keep using `harbor-*` consistently).
|
\"prepare:harbor-prepare\" \\
|
||||||
docker tag "${IMAGE_NAMESPACE}/prepare:${VERSIONTAG}" "${IMAGE_NAMESPACE}/harbor-prepare:${VERSIONTAG}" || true
|
\"redis-photon:harbor-redis\" \\
|
||||||
docker tag "${IMAGE_NAMESPACE}/redis-photon:${VERSIONTAG}" "${IMAGE_NAMESPACE}/harbor-redis:${VERSIONTAG}" || true
|
\"nginx-photon:harbor-nginx\" \\
|
||||||
docker tag "${IMAGE_NAMESPACE}/nginx-photon:${VERSIONTAG}" "${IMAGE_NAMESPACE}/harbor-nginx:${VERSIONTAG}" || true
|
\"registry-photon:harbor-registry\"; do
|
||||||
docker tag "${IMAGE_NAMESPACE}/registry-photon:${VERSIONTAG}" "${IMAGE_NAMESPACE}/harbor-registry:${VERSIONTAG}" || true
|
src=\"${IMAGE_NAMESPACE}/$(echo "$pair" | cut -d: -f1):${env.HARBOR_VERSION_RESOLVED}${TAG_SUFFIX}.${BUILD_NUMBER}\"
|
||||||
|
dst=\"${IMAGE_NAMESPACE}/$(echo "$pair" | cut -d: -f2):${env.HARBOR_VERSION_RESOLVED}${TAG_SUFFIX}.${BUILD_NUMBER}\"
|
||||||
|
if podman image exists \"$src\"; then
|
||||||
|
podman tag \"$src\" \"$dst\" || true
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
# Push every image we just built for this tag under our namespace.
|
podman images --format '{{.Repository}}:{{.Tag}}' \\
|
||||||
docker images --format '{{.Repository}}:{{.Tag}}' \
|
| awk -v ns=\"${IMAGE_NAMESPACE}/\" -v tag=\"${env.HARBOR_VERSION_RESOLVED}${TAG_SUFFIX}.${BUILD_NUMBER}\" 'index($0, ns)==1 && $0 ~ ":"tag"$"' \\
|
||||||
| awk -v ns="${IMAGE_NAMESPACE}/" -v tag="${VERSIONTAG}" 'index($0, ns)==1 && $0 ~ ":"tag"$"' \
|
| sort -u \\
|
||||||
| sort -u \
|
|
||||||
| while read -r img; do
|
| while read -r img; do
|
||||||
echo "Pushing ${img}"
|
echo \"Pushing ${img}\"
|
||||||
docker push "${img}"
|
podman push \"${img}\"
|
||||||
done
|
done
|
||||||
'''
|
"""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
post {
|
post {
|
||||||
always { echo 'done' }
|
always {
|
||||||
|
echo "done"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
16
README.md
16
README.md
@ -1,2 +1,16 @@
|
|||||||
# harbor-arm-build
|
# harbor-arm-build
|
||||||
Jenkins pipeline: build arm images and push to Harbor.
|
|
||||||
|
Jenkins pipeline for building an arm64 Harbor release straight from upstream and
|
||||||
|
pushing the full image set into `registry.bstein.dev/infra`.
|
||||||
|
|
||||||
|
Key behavior:
|
||||||
|
- Parameter `HARBOR_VERSION` (e.g. `v2.14.1`). If left empty, the pipeline
|
||||||
|
fetches the latest Harbor GitHub release tag.
|
||||||
|
- Runs on an arm64 node with podman/buildah (Docker CLI shim via `podman-docker`);
|
||||||
|
no dind is used.
|
||||||
|
- Builds all Harbor images with `IMAGENAMESPACE=registry.bstein.dev/infra` and
|
||||||
|
tags `${HARBOR_VERSION}-arm64.$BUILD_NUMBER`, then pushes them with the
|
||||||
|
`harbor-robot-pipeline` credentials.
|
||||||
|
|
||||||
|
Trigger manually in Jenkins when a new Harbor version needs to be published to
|
||||||
|
the internal registry.
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user