Разведены Builder и Deploy: сборка освобождает DinD до старта деплоя.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Sergey Antropoff
2026-07-27 23:16:06 +03:00
parent 776f516239
commit 7246f2abe4
2 changed files with 125 additions and 114 deletions
Vendored
+124 -113
View File
@@ -1,20 +1,22 @@
// Wrapped — CI: multi-arch push (Harbor + Docker Hub) + Helm deploy // Wrapped — CI: multi-arch push (Harbor + Docker Hub), затем асинхронный Deploy
// //
// Триггер: push в main (webhook Gitea → Jenkins Multibranch). // Ресурсы k3s: Builder и Deploy не должны держать два DinD-пода сразу.
// Версия образа = файл VERSION из коммита (без авто-bump в CI). // 1) стадии сборки на agent label 'docker' (DinD)
// Bump: make bump-patch / make push локально — что запушили, то и уйдёт в релиз. // 2) при успехе — trigger Deploy с wait:false и agent none → Builder-под умирает
// 3) Deploy стартует отдельно уже без Builder
// //
// Credentials (Manage Jenkins → Credentials → Global): // Версия образа = VERSION из коммита. Bump: make bump-patch / make push.
//
// Credentials (Global):
// harbor-devops-tools-push-pull-access — Harbor devops-tools (robot) // harbor-devops-tools-push-pull-access — Harbor devops-tools (robot)
// docker-hub — Docker Hub (inecs) // docker-hub — Docker Hub (inecs)
// ssh-gitea-key — SCM checkout Gitea // ssh-gitea-key — SCM checkout Gitea
// k3s-kubeconfig — в job Deploy (Jenkinsfile.deploy) // k3s-kubeconfig — в job Deploy (Jenkinsfile.deploy)
// //
// Теги образа: :<semver из VERSION>, :<sha>, :latest // Deploy job: devops-tools/wrapped/wrapped-deploy/main
// Deploy: devops-tools/wrapped/wrapped-deploy/main
pipeline { pipeline {
agent { label 'docker' } agent none
options { options {
buildDiscarder(logRotator(numToKeepStr: '20')) buildDiscarder(logRotator(numToKeepStr: '20'))
@@ -34,104 +36,123 @@ pipeline {
} }
stages { stages {
stage('Checkout') { stage('Build image') {
steps {
checkout scm
}
}
stage('Version') {
when { when {
anyOf { anyOf {
branch 'main' branch 'main'
branch 'master' branch 'master'
} }
} }
steps { agent { label 'docker' }
container('docker') { stages {
script { stage('Checkout') {
env.IMAGE_VERSION = sh( steps {
script: "tr -d '[:space:]' < VERSION", checkout scm
returnStdout: true }
).trim() }
if (!env.IMAGE_VERSION) {
error('VERSION file is empty') stage('Version') {
steps {
container('docker') {
script {
env.IMAGE_VERSION = sh(
script: "tr -d '[:space:]' < VERSION",
returnStdout: true
).trim()
if (!env.IMAGE_VERSION) {
error('VERSION file is empty')
}
echo "IMAGE_VERSION from VERSION → ${env.IMAGE_VERSION}"
}
} }
echo "IMAGE_VERSION from VERSION → ${env.IMAGE_VERSION}"
}
}
}
}
stage('Build & push') {
when {
anyOf {
branch 'main'
branch 'master'
}
}
steps {
withCredentials([
usernamePassword(
credentialsId: 'harbor-devops-tools-push-pull-access',
usernameVariable: 'HARBOR_USER',
passwordVariable: 'HARBOR_PASS'
),
usernamePassword(
credentialsId: 'docker-hub',
usernameVariable: 'DOCKERHUB_USER',
passwordVariable: 'DOCKERHUB_PASS'
)
]) {
container('docker') {
sh '''
set -eux
test -n "${IMAGE_VERSION}"
echo "Building tags: ${IMAGE_VERSION}, ${RELEASE_TAG}, latest"
echo "$HARBOR_PASS" | docker login "$HARBOR_REGISTRY" -u "$HARBOR_USER" --password-stdin
echo "$DOCKERHUB_PASS" | docker login -u "$DOCKERHUB_USER" --password-stdin
docker buildx rm "$BUILDX_BUILDER" 2>/dev/null || true
docker buildx create --name "$BUILDX_BUILDER" --driver docker-container --use
docker buildx inspect --bootstrap >/dev/null
docker buildx build \
--platform linux/amd64 \
--provenance=false --sbom=false --push \
-t "${HARBOR_IMAGE}:${RELEASE_TAG}-amd64" \
-t "${DOCKERHUB_IMAGE}:${RELEASE_TAG}-amd64" \
-f Dockerfile .
docker buildx build \
--platform linux/arm64 \
--provenance=false --sbom=false --push \
-t "${HARBOR_IMAGE}:${RELEASE_TAG}-arm64" \
-t "${DOCKERHUB_IMAGE}:${RELEASE_TAG}-arm64" \
-f Dockerfile .
for IMAGE in "$HARBOR_IMAGE" "$DOCKERHUB_IMAGE"; do
docker buildx imagetools create \
-t "${IMAGE}:${RELEASE_TAG}" \
-t "${IMAGE}:${IMAGE_VERSION}" \
-t "${IMAGE}:latest" \
"${IMAGE}:${RELEASE_TAG}-amd64" \
"${IMAGE}:${RELEASE_TAG}-arm64"
done
echo "--- Harbor ---"
docker buildx imagetools inspect "${HARBOR_IMAGE}:${IMAGE_VERSION}" | sed -n '1,40p'
echo "--- Docker Hub ---"
docker buildx imagetools inspect "${DOCKERHUB_IMAGE}:${IMAGE_VERSION}" | sed -n '1,40p'
docker buildx rm "$BUILDX_BUILDER" || true
'''
} }
} }
stage('Build & push') {
steps {
withCredentials([
usernamePassword(
credentialsId: 'harbor-devops-tools-push-pull-access',
usernameVariable: 'HARBOR_USER',
passwordVariable: 'HARBOR_PASS'
),
usernamePassword(
credentialsId: 'docker-hub',
usernameVariable: 'DOCKERHUB_USER',
passwordVariable: 'DOCKERHUB_PASS'
)
]) {
container('docker') {
sh '''
set -eux
test -n "${IMAGE_VERSION}"
echo "Building tags: ${IMAGE_VERSION}, ${RELEASE_TAG}, latest"
echo "$HARBOR_PASS" | docker login "$HARBOR_REGISTRY" -u "$HARBOR_USER" --password-stdin
echo "$DOCKERHUB_PASS" | docker login -u "$DOCKERHUB_USER" --password-stdin
docker buildx rm "$BUILDX_BUILDER" 2>/dev/null || true
docker buildx create --name "$BUILDX_BUILDER" --driver docker-container --use
docker buildx inspect --bootstrap >/dev/null
docker buildx build \
--platform linux/amd64 \
--provenance=false --sbom=false --push \
-t "${HARBOR_IMAGE}:${RELEASE_TAG}-amd64" \
-t "${DOCKERHUB_IMAGE}:${RELEASE_TAG}-amd64" \
-f Dockerfile .
docker buildx build \
--platform linux/arm64 \
--provenance=false --sbom=false --push \
-t "${HARBOR_IMAGE}:${RELEASE_TAG}-arm64" \
-t "${DOCKERHUB_IMAGE}:${RELEASE_TAG}-arm64" \
-f Dockerfile .
for IMAGE in "$HARBOR_IMAGE" "$DOCKERHUB_IMAGE"; do
docker buildx imagetools create \
-t "${IMAGE}:${RELEASE_TAG}" \
-t "${IMAGE}:${IMAGE_VERSION}" \
-t "${IMAGE}:latest" \
"${IMAGE}:${RELEASE_TAG}-amd64" \
"${IMAGE}:${RELEASE_TAG}-arm64"
done
echo "--- Harbor ---"
docker buildx imagetools inspect "${HARBOR_IMAGE}:${IMAGE_VERSION}" | sed -n '1,40p'
echo "--- Docker Hub ---"
docker buildx imagetools inspect "${DOCKERHUB_IMAGE}:${IMAGE_VERSION}" | sed -n '1,40p'
docker buildx rm "$BUILDX_BUILDER" || true
'''
}
}
}
}
}
post {
always {
script {
try {
container('docker') {
sh 'docker buildx rm "$BUILDX_BUILDER" 2>/dev/null || true'
}
} catch (Ignored) {
// pod may already be gone
}
}
}
success {
echo "✓ Build OK: ${DOCKERHUB_IMAGE}:{${RELEASE_TAG},${IMAGE_VERSION},latest}"
}
failure {
echo "✗ Build/push не удались — Deploy не запускается"
}
} }
} }
// Без DinD: только очередь Deploy, Builder-под уже освобождён
stage('Trigger deploy') { stage('Trigger deploy') {
when { when {
anyOf { anyOf {
@@ -139,19 +160,21 @@ pipeline {
branch 'master' branch 'master'
} }
} }
agent none
steps { steps {
script { script {
echo "Triggering ${DEPLOY_JOB} → ${DOCKERHUB_IMAGE}:${IMAGE_VERSION}" echo "Build finished — triggering ${DEPLOY_JOB} → ${DOCKERHUB_IMAGE}:${IMAGE_VERSION} (wait: false)"
build( build(
job: env.DEPLOY_JOB, job: env.DEPLOY_JOB,
wait: true, wait: false,
propagate: true, propagate: false,
parameters: [ parameters: [
string(name: 'IMAGE_REPOSITORY', value: env.DOCKERHUB_IMAGE), string(name: 'IMAGE_REPOSITORY', value: env.DOCKERHUB_IMAGE),
string(name: 'IMAGE_TAG', value: env.IMAGE_VERSION), string(name: 'IMAGE_TAG', value: env.IMAGE_VERSION),
string(name: 'IMAGE_PULL_POLICY', value: 'Always'), string(name: 'IMAGE_PULL_POLICY', value: 'Always'),
] ]
) )
echo "Deploy queued; Builder pipeline exits and releases cluster resources."
} }
} }
} }
@@ -159,24 +182,12 @@ pipeline {
post { post {
success { success {
echo "✓ Version: ${IMAGE_VERSION}" echo "✓ Version: ${IMAGE_VERSION}"
echo "✓ Harbor: ${HARBOR_IMAGE}:{${RELEASE_TAG},${IMAGE_VERSION},latest}" echo "✓ Image: ${DOCKERHUB_IMAGE}:{${RELEASE_TAG},${IMAGE_VERSION},latest}"
echo "✓ Docker Hub: ${DOCKERHUB_IMAGE}:{${RELEASE_TAG},${IMAGE_VERSION},latest}" echo "✓ Deploy: ${DEPLOY_JOB} triggered (async)"
echo "✓ Deploy: ${DEPLOY_JOB} (${DOCKERHUB_IMAGE}:${IMAGE_VERSION})"
} }
failure { failure {
echo "✗ Version / build / push / deploy — см. лог стадий" echo "✗ Сборка не удалась — см. лог Build image"
}
always {
script {
try {
container('docker') {
sh 'docker buildx rm "$BUILDX_BUILDER" 2>/dev/null || true'
}
} catch (Ignored) {
// agent may already be gone
}
}
} }
} }
} }
+1 -1
View File
@@ -492,7 +492,7 @@ kubectl -n wrapped rollout status deployment/wrapped
Credential: `k3s-kubeconfig` (Secret file) — kubeconfig к K3S. Credential: `k3s-kubeconfig` (Secret file) — kubeconfig к K3S.
Рекомендуемая связка job’ов уже в [`Jenkinsfile`](Jenkinsfile): после **Version** (чтение `VERSION`) + **Build & push** стадия **Trigger deploy** вызывает `devops-tools/wrapped/wrapped-deploy/main` с тем же SemVer (`wait: true`). Рекомендуемая связка job’ов уже в [`Jenkinsfile`](Jenkinsfile): после успешного **Build image** (DinD) стадия **Trigger deploy** с `wait: false` и `agent none` — под Builder освобождается, затем отдельно стартует Deploy. Так на кластере не нужны два DinD одновременно.
Ручной запуск Deploy: **Build with Parameters** (пустой `IMAGE_TAG``cat VERSION`). Ручной запуск Deploy: **Build with Parameters** (пустой `IMAGE_TAG``cat VERSION`).