Files
live_recorder/Jenkinsfile
T
nanxunandClaude Opus 4.8 1cb05292c0 ci: exclude direct-reachable mirrors from HTTP proxy to fix apt 502
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>
2026-07-02 18:55:45 +08:00

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()
}
}
}