The 7890 proxy intermittently returns '502 Bad Gateway' when tunneling to mirrors.tuna.tsinghua.edu.cn (observed on libglapi-mesa during apt-get install in the runtime layer). The Tsinghua mirror, MCR, and daocloud are all directly reachable from this builder, so the proxy adds no value and only introduces failure modes. Add the following hosts to NO_PROXY_HOSTS so apt/curl inside the build containers hit them directly: - mirrors.tuna.tsinghua.edu.cn, .tsinghua.edu.cn - mcr.microsoft.com - docker.m.daocloud.io Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
274 lines
12 KiB
Groovy
274 lines
12 KiB
Groovy
pipeline {
|
|
agent { label '\u6784\u5efa\u673a1' }
|
|
|
|
options {
|
|
timestamps()
|
|
disableConcurrentBuilds()
|
|
skipDefaultCheckout(true)
|
|
}
|
|
|
|
environment {
|
|
REGISTRY_URL = 'reg.nxsir.cn'
|
|
API_IMAGE_NAME = 'liverecorder/app-api'
|
|
WEB_IMAGE_NAME = 'liverecorder/app-web'
|
|
IMAGE_TAG = "${env.BUILD_ID}"
|
|
DOCKER_CREDS = 'harbor_key'
|
|
TARGET_PLATFORMS = 'linux/amd64,linux/arm64'
|
|
BUILDER_NAME = 'liverecorder-buildx'
|
|
WEB_NODE_IMAGE = 'docker.m.daocloud.io/library/node:22-alpine'
|
|
WEB_NGINX_IMAGE = 'docker.m.daocloud.io/library/nginx:1.27-alpine'
|
|
HTTP_PROXY_URL = 'http://192.168.5.200:7890'
|
|
// Tsinghua/daocloud/mcr hosts are reachable directly from the builder — routing them
|
|
// through the 7890 proxy caused intermittent '502 Bad Gateway' during apt-get install.
|
|
NO_PROXY_HOSTS = '127.0.0.1,localhost,reg.nxsir.cn,gitea.nxsir.cn,mirrors.tuna.tsinghua.edu.cn,.tsinghua.edu.cn,mcr.microsoft.com,docker.m.daocloud.io'
|
|
|
|
GIT_REPO_URL = 'https://gitea.nxsir.cn/nanxun/live_recorder.git'
|
|
GIT_BRANCH = 'main'
|
|
GIT_CREDS = ''
|
|
|
|
API_IMAGE_TAGGED = "${REGISTRY_URL}/${API_IMAGE_NAME}:${IMAGE_TAG}"
|
|
API_IMAGE_LATEST = "${REGISTRY_URL}/${API_IMAGE_NAME}:latest"
|
|
WEB_IMAGE_TAGGED = "${REGISTRY_URL}/${WEB_IMAGE_NAME}:${IMAGE_TAG}"
|
|
WEB_IMAGE_LATEST = "${REGISTRY_URL}/${WEB_IMAGE_NAME}:latest"
|
|
API_CACHE_IMAGE = "${REGISTRY_URL}/${API_IMAGE_NAME}:buildcache"
|
|
WEB_CACHE_IMAGE = "${REGISTRY_URL}/${WEB_IMAGE_NAME}:buildcache"
|
|
}
|
|
|
|
stages {
|
|
stage('Checkout') {
|
|
steps {
|
|
script {
|
|
def userRemoteConfig = [url: env.GIT_REPO_URL]
|
|
if (env.GIT_CREDS?.trim()) {
|
|
userRemoteConfig.credentialsId = env.GIT_CREDS.trim()
|
|
}
|
|
|
|
checkout([
|
|
$class: 'GitSCM',
|
|
branches: [[name: "*/${env.GIT_BRANCH}"]],
|
|
userRemoteConfigs: [userRemoteConfig]
|
|
])
|
|
}
|
|
}
|
|
}
|
|
|
|
stage('Prepare Buildx') {
|
|
steps {
|
|
sh """
|
|
set -e
|
|
sudo docker buildx version
|
|
|
|
# Register ARM64 binfmt. Docker Hub is unreachable from this builder,
|
|
# so try daocloud mirror first; fall back to apt-installed qemu-user-static.
|
|
if [ ! -e /proc/sys/fs/binfmt_misc/qemu-aarch64 ] && [ ! -e /proc/sys/fs/binfmt_misc/qemu-aarch64-static ]; then
|
|
echo 'Registering ARM64 emulation (binfmt not present)...'
|
|
if ! sudo docker run --privileged --rm docker.m.daocloud.io/tonistiigi/binfmt --install arm64 2>/dev/null; then
|
|
echo 'daocloud mirror failed, falling back to qemu-user-static via apt...'
|
|
sudo apt-get update -qq
|
|
sudo apt-get install -y -qq qemu-user-static
|
|
fi
|
|
else
|
|
echo 'ARM64 binfmt already registered, skipping.'
|
|
fi
|
|
|
|
# Verify emulation works before proceeding
|
|
sudo docker run --rm --platform linux/arm64 docker.m.daocloud.io/library/alpine:latest uname -m
|
|
|
|
# buildkitd config: cap parallelism at 1 so multi-platform builds run
|
|
# sequentially instead of racing for RAM on this ~4GB builder. QEMU-emulated
|
|
# dotnet restore alone can spike to 2-3GB, and OOM Killer will otherwise
|
|
# take out one of the platforms mid-restore.
|
|
BUILDKITD_TOML=/tmp/buildkitd-${BUILDER_NAME}.toml
|
|
cat > \$BUILDKITD_TOML <<'EOF'
|
|
[worker.oci]
|
|
max-parallelism = 1
|
|
EOF
|
|
|
|
# Recreate the builder to make sure the config takes effect (idempotent).
|
|
sudo docker buildx rm ${BUILDER_NAME} >/dev/null 2>&1 || true
|
|
sudo docker buildx create \
|
|
--name ${BUILDER_NAME} \
|
|
--driver docker-container \
|
|
--driver-opt network=host \
|
|
--driver-opt 'env.HTTP_PROXY=${HTTP_PROXY_URL}' \
|
|
--driver-opt 'env.HTTPS_PROXY=${HTTP_PROXY_URL}' \
|
|
--driver-opt 'env.NO_PROXY=reg.nxsir.cn' \
|
|
--driver-opt 'env.http_proxy=${HTTP_PROXY_URL}' \
|
|
--driver-opt 'env.https_proxy=${HTTP_PROXY_URL}' \
|
|
--driver-opt 'env.no_proxy=reg.nxsir.cn' \
|
|
--config \$BUILDKITD_TOML \
|
|
--use
|
|
sudo docker buildx inspect --builder ${BUILDER_NAME} --bootstrap >/dev/null
|
|
"""
|
|
}
|
|
}
|
|
|
|
stage('Login Registry') {
|
|
steps {
|
|
withCredentials([
|
|
usernamePassword(
|
|
credentialsId: "${DOCKER_CREDS}",
|
|
usernameVariable: 'DOCKER_USERNAME',
|
|
passwordVariable: 'DOCKER_PASSWORD'
|
|
)
|
|
]) {
|
|
sh """
|
|
set -e
|
|
echo "\$DOCKER_PASSWORD" | sudo docker login ${REGISTRY_URL} -u "\$DOCKER_USERNAME" --password-stdin
|
|
"""
|
|
}
|
|
}
|
|
}
|
|
|
|
stage('Build And Push API Image') {
|
|
steps {
|
|
sh """
|
|
set -e
|
|
run_with_heartbeat() {
|
|
log_file="\$1"
|
|
build_label="\$2"
|
|
shift 2
|
|
rm -f "\$log_file"
|
|
: > "\$log_file"
|
|
"\$@" >"\$log_file" 2>&1 &
|
|
cmd_pid=\$!
|
|
cmd_status=0
|
|
last_size=0
|
|
while kill -0 "\$cmd_pid" >/dev/null 2>&1; do
|
|
echo "[heartbeat] \${build_label} still running at \$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
|
if [ -f "\$log_file" ]; then
|
|
current_size=\$(stat -c%s "\$log_file" 2>/dev/null || echo 0)
|
|
if [ "\$current_size" -gt "\$last_size" ]; then
|
|
start_byte=\$((last_size + 1))
|
|
tail -c +"\$start_byte" "\$log_file" || true
|
|
last_size=\$current_size
|
|
fi
|
|
fi
|
|
sleep 20
|
|
done
|
|
wait "\$cmd_pid" || cmd_status=\$?
|
|
if [ -f "\$log_file" ]; then
|
|
current_size=\$(stat -c%s "\$log_file" 2>/dev/null || echo 0)
|
|
if [ "\$current_size" -gt "\$last_size" ]; then
|
|
start_byte=\$((last_size + 1))
|
|
tail -c +"\$start_byte" "\$log_file" || true
|
|
fi
|
|
fi
|
|
if [ "\$cmd_status" -ne 0 ]; then
|
|
echo "[heartbeat] \${build_label} failed with exit code \$cmd_status"
|
|
fi
|
|
return "\$cmd_status"
|
|
}
|
|
sudo docker buildx inspect --builder ${BUILDER_NAME} --bootstrap >/dev/null
|
|
echo 'Building and pushing multi-arch API image: ${API_IMAGE_TAGGED}'
|
|
run_with_heartbeat /tmp/live-recorder-api-buildx-${IMAGE_TAG}.log "API multi-arch build" \
|
|
sudo docker buildx build \
|
|
--builder ${BUILDER_NAME} \
|
|
--platform ${TARGET_PLATFORMS} \
|
|
--network host \
|
|
--progress=plain \
|
|
--provenance=false \
|
|
--cache-from type=registry,ref=${API_CACHE_IMAGE} \
|
|
--cache-to type=registry,ref=${API_CACHE_IMAGE},mode=max \
|
|
--build-arg HTTP_PROXY=${HTTP_PROXY_URL} \
|
|
--build-arg HTTPS_PROXY=${HTTP_PROXY_URL} \
|
|
--build-arg NO_PROXY=${NO_PROXY_HOSTS} \
|
|
--build-arg http_proxy=${HTTP_PROXY_URL} \
|
|
--build-arg https_proxy=${HTTP_PROXY_URL} \
|
|
--build-arg no_proxy=${NO_PROXY_HOSTS} \
|
|
-f src/LiveRecorder.WebApi/Dockerfile \
|
|
-t ${API_IMAGE_TAGGED} \
|
|
-t ${API_IMAGE_LATEST} \
|
|
--push \
|
|
.
|
|
"""
|
|
}
|
|
}
|
|
|
|
stage('Build And Push Web Image') {
|
|
steps {
|
|
sh """
|
|
set -e
|
|
run_with_heartbeat() {
|
|
log_file="\$1"
|
|
build_label="\$2"
|
|
shift 2
|
|
rm -f "\$log_file"
|
|
: > "\$log_file"
|
|
"\$@" >"\$log_file" 2>&1 &
|
|
cmd_pid=\$!
|
|
cmd_status=0
|
|
last_size=0
|
|
while kill -0 "\$cmd_pid" >/dev/null 2>&1; do
|
|
echo "[heartbeat] \${build_label} still running at \$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
|
if [ -f "\$log_file" ]; then
|
|
current_size=\$(stat -c%s "\$log_file" 2>/dev/null || echo 0)
|
|
if [ "\$current_size" -gt "\$last_size" ]; then
|
|
start_byte=\$((last_size + 1))
|
|
tail -c +"\$start_byte" "\$log_file" || true
|
|
last_size=\$current_size
|
|
fi
|
|
fi
|
|
sleep 20
|
|
done
|
|
wait "\$cmd_pid" || cmd_status=\$?
|
|
if [ -f "\$log_file" ]; then
|
|
current_size=\$(stat -c%s "\$log_file" 2>/dev/null || echo 0)
|
|
if [ "\$current_size" -gt "\$last_size" ]; then
|
|
start_byte=\$((last_size + 1))
|
|
tail -c +"\$start_byte" "\$log_file" || true
|
|
fi
|
|
fi
|
|
if [ "\$cmd_status" -ne 0 ]; then
|
|
echo "[heartbeat] \${build_label} failed with exit code \$cmd_status"
|
|
fi
|
|
return "\$cmd_status"
|
|
}
|
|
sudo docker buildx inspect --builder ${BUILDER_NAME} --bootstrap >/dev/null
|
|
echo 'Building and pushing multi-arch Web image: ${WEB_IMAGE_TAGGED}'
|
|
run_with_heartbeat /tmp/live-recorder-web-buildx-${IMAGE_TAG}.log "Web multi-arch build" \
|
|
sudo docker buildx build \
|
|
--builder ${BUILDER_NAME} \
|
|
--platform ${TARGET_PLATFORMS} \
|
|
--network host \
|
|
--progress=plain \
|
|
--provenance=false \
|
|
--cache-from type=registry,ref=${WEB_CACHE_IMAGE} \
|
|
--cache-to type=registry,ref=${WEB_CACHE_IMAGE},mode=max \
|
|
--build-arg NODE_IMAGE=${WEB_NODE_IMAGE} \
|
|
--build-arg NGINX_IMAGE=${WEB_NGINX_IMAGE} \
|
|
--build-arg HTTP_PROXY=${HTTP_PROXY_URL} \
|
|
--build-arg HTTPS_PROXY=${HTTP_PROXY_URL} \
|
|
--build-arg NO_PROXY=${NO_PROXY_HOSTS} \
|
|
--build-arg http_proxy=${HTTP_PROXY_URL} \
|
|
--build-arg https_proxy=${HTTP_PROXY_URL} \
|
|
--build-arg no_proxy=${NO_PROXY_HOSTS} \
|
|
-f frontend/Dockerfile \
|
|
--build-arg VITE_API_BASE_URL=/api \
|
|
-t ${WEB_IMAGE_TAGGED} \
|
|
-t ${WEB_IMAGE_LATEST} \
|
|
--push \
|
|
frontend
|
|
"""
|
|
}
|
|
}
|
|
}
|
|
|
|
post {
|
|
success {
|
|
echo "Pipeline completed successfully."
|
|
}
|
|
failure {
|
|
echo "Pipeline failed. Please check the build log."
|
|
}
|
|
always {
|
|
sh """
|
|
set +e
|
|
sudo docker logout ${REGISTRY_URL} >/dev/null 2>&1 || true
|
|
true
|
|
"""
|
|
deleteDir()
|
|
}
|
|
}
|
|
}
|