From 4b4d51562fc06ff66b5afa7edfed9235093d7565 Mon Sep 17 00:00:00 2001 From: Vitaliy Zaytsev Date: Thu, 11 Dec 2025 14:50:50 +0300 Subject: [PATCH 1/2] Refactor pipe --- build/Jenkinsfile | 298 +++--------------------------------------- build/vars/build.yaml | 99 ++++++++------ 2 files changed, 75 insertions(+), 322 deletions(-) diff --git a/build/Jenkinsfile b/build/Jenkinsfile index 4992120..be483b8 100644 --- a/build/Jenkinsfile +++ b/build/Jenkinsfile @@ -1,287 +1,25 @@ -/* - ! Jenkins Watchdog Pipeline +env.DEBUGGING_MODE = true +env.FORCE_BUILD = false +timeoutTime = 15 - Этот Pipeline реализует сборку на Kubernetes worker-ноде с контролем и завершением со стороны Jenkins master-ноды. - Master отслеживает статус worker'а в реальном времени и завершает процесс в случае ошибки, таймаута или успешного завершения. -*/ - -// Статусы воркера. Необходимо мастеру для определения состояния джоба -def workerFailed = false -def workerSuccess = false -def DEBUGGING_MODE = false -// Мапа для запуска мастера и воркера параллельно -def parallelSteps = [:] - -// Блок констант -// Мапа, в которой будут переменные из vars/build.yaml -buildVars = [:] -buildVars.system = [:] -buildVars.kubeconfig = "config.kube" - -// Подгрузка либы. Версия в свойствах папки Jenkins -library "jlib" -cmdbVars = constants.cmdbVars - -TimeoutTime = [ - time: binding.hasVariable('timeoutTime') ? timeoutTime : (cmdbVars?.system?.timeoutTime ?: 20), - unit: binding.hasVariable('timeoutUnit') ? timeoutUnit : (cmdbVars?.system?.timeoutUnit ?: "MINUTES") -] - -parallelSteps["worker"] = { - node("docker") { +node("docker") { timestamps() { - ansiColor("xterm"){ - dir (env.BUILD_NUMBER) { - try { - stage ("Prepare environments") { - // Выкачивание проекта из Git - checkout scm - // Определение типа джобы: - // - сборка и пуш в кубер: DEV / PR - // - проверка образа (из DEV) и пуш в кубер: TEST / PROD - logger.info("Targeting stand for deploy") - // Если PR - в DEV - if (env.CHANGE_ID) - buildVars.gitBranch = "dev" - else - buildVars.gitBranch = env.BRANCH_NAME + ansiColor("xterm"){ - // Проверка на нужные бренчи - if (!(buildVars.gitBranch in ["prod", "test", "dev"])) { - logger.err("Ветка в Git не соответствует 'prod', 'test' или 'dev'", true) - } - - envConfig = "build/vars/build.yaml" - support.getVarsAll(envConfig, buildVars) - - logger.info("Get kubeconfig") - try { - withCredentials([vaultString(credentialsId: "${buildVars.vaultPath}", variable: "pem")]) { - runsh """ - set +x - echo "${pem}" | base64 -d > ${ buildVars.kubeconfig } - set -x - """, true - } - logger.info("kubeconfig getting successfully") - } catch (Exception kubeconfigGetError) { - logger.err("Не удалось получить кубконфиг для подключению к Kubernetes-кластеру. Ошибка: ${kubeconfigGetError.message}", true) - } - - logger.info("Getting version...") - try { - version = runsh buildVars.getCommandVersion - logger.info("Version app: ${ version }") - } catch (Exception versionGetError) { - logger.err("Не удалось получить версию приложения. Ошибка: ${versionGetError.message}", true) - } - } - - // Получение токена для авторизации в GITEA - withCredentials([vaultString(credentialsId: "gitea.services.ngcloud.ru-deck-token", variable: "token")]) { GITEA_AUTH_TOKEN = token } - - // Сборка Image (Только DEV) - if (buildVars.gitBranch == "dev") { - if (buildVars.needBuildLib.toBoolean()) { - stage ("Check library") { - headers = [ [ name: "Authorization", value: "Bearer ${ GITEA_AUTH_TOKEN }" ] ] - // Проверка что либа существует - try { - retry(3) { - httpcall.get( url="https://gitea.services.ngcloud.ru/api/v1/packages/deck/npm/%40nubes%2F${ buildVars.app.name }/${ version }", headerList=headers) - } - needToBuildLib = false - } catch (Exception errGetPackage) { - logger.info("Package does not exist. It's OK") - needToBuildLib = true - } - } - stage ("Build library") { - if (needToBuildLib.toBoolean()) { - retry(3) { - try { - docker.build("${ buildVars.registryImageName }:${ version }", "--no-cache --build-arg GITEA_AUTH_TOKEN=${ GITEA_AUTH_TOKEN } -f ./build/Dockerfile_build_lib .") - } catch (Exception errorDocker) { - logger.err("Не удалось собрать библиотеку. Ошибка: ${ errorDocker.message }", true) - } - } - } - } - } - stage ("Build image") { - catchError(buildResult: 'FAILURE', stageResult: 'FAILURE') { - logger.info("Start building image ${ buildVars.registryContainerName }:${ version }") - retry(3) { - try { - app = docker.build("${ buildVars.registryImageName }:${ version }", "--no-cache --build-arg GITEA_AUTH_TOKEN=${ GITEA_AUTH_TOKEN } -f ./build/Dockerfile .") - } catch (Exception errorDocker) { - logger.err("Не удалось собрать основной образ. Ошибка: ${ errorDocker.message }", true) - } - } - } - - logger.info("Start uploading image ${ buildVars.registryImageName }:${ version }") - docker.withRegistry("https://${ buildVars.registryUrl }/${ buildVars.repoPath }", "registry-jenkins-cred") { - // retry на случай если 500 случайно отдаст registry - retry(3) { - try { - app.push("${ version }") - } catch (Exception errorDocker) { - logger.err("Не удалось запушить образ в Registry. Ошибка: ${ errorDocker.message }", true) - } - } - } - } - } - // Проверка Image в Registry (Всё кроме DEV) - if (buildVars.gitBranch != "dev") { - stage("Check if image exist") { - docker.withRegistry("https://${ buildVars.registryUrl }/${ buildVars.repoPath }", "registry-jenkins-cred") { - def img = docker.image("${ buildVars.registryImageName }:${ version }") - // TODO переписать либу docker под себя - // try/catch не поможет, ошибка выведет всегда 'script returned exit code 1' - // retry на случай если 500 случайно отдаст registry - retry(3) { - try { - img.pull() - } catch (Exception errorDocker) { - logger.err("Не удалось получить образ из Registry. Ошибка: ${ errorDocker.message }", true) - } - } - } - } - } - // Деплой в Kubernetes (все стенды) - stage("Deploy to K8s test") { - logger.info("Check exist pods in k8s...") - retry(3) { - runsh "kubectl --kubeconfig=${ buildVars.kubeconfig } --namespace ${ buildVars.namespace } get po" - } - - logger.info("Prepare list of args on helm...") - commandArgs = [ - "--set deployment.image.repository=${ buildVars.registryUrl }/${ buildVars.registryImageName }", - "--set deployment.readinessProbe.httpGet.path=${ buildVars.app.health }", - "--set deployment.containerPort=${ buildVars.app.port }", - "--set deployment.resources.requests.cpu=${ buildVars.app.cpu }m", - "--set deployment.resources.requests.memory=${ buildVars.app.mem }Mi", - "--set deployment.resources.limits.cpu=${ buildVars.app.cpu }m", - "--set deployment.resources.limits.memory=${ buildVars.app.mem }Mi", - "--set version=${ version }", - - "--set deployment.image.tag=${ version }", - "--set stand=${ buildVars.gitBranch }" - ] - // Если включен Vault - if (buildVars.needStoreVaultSecret.toBoolean()) { - commandArgs.add("--set vault.secretStore=${ buildVars.namespace }") - commandArgs.add("--set serviceAccountRole=${ buildVars.namespace }-sa") - commandArgs.add("--set vault.secrets=\"{${ buildVars.storedSecretsList.join(',') }}\"") - commandArgs.add("--set vault.enable=true") - } else { - commandArgs.add("--set vault.enable=false") - } - - installArgs = [ - "--kubeconfig ${ buildVars.kubeconfig }", - "--install", - "--namespace ${ buildVars.namespace }", - ] - - cmdTemplate = "helm template ${commandArgs.join(' ')} ./build/helm/" - cmd = "helm upgrade ${ buildVars.app.name } ${installArgs.join(' ')} ${commandArgs.join(' ')} ./build/helm/" - logger.info("""Command for push... - ${cmd} - """) - - logger.info("Template helm-release...") - runsh cmdTemplate - - logger.info("Push helm-release...") - retry(3) { - // От того, что Helm-чарт будет ждать, мало что изменится - runsh cmd - } - - logger.info("Check exist pods in k8s...") - retry(3) { - runsh "kubectl --kubeconfig=${ buildVars.kubeconfig } --namespace ${ buildVars.namespace } get po" - } - - // Нужны проверки после установки - buildVars.application = [:] - buildVars.application.typeOfProject = "deployments.apps" - buildVars.application.namespace = buildVars.namespace - - // sleep ибо загрузка манифестов в K8S занимает какое-то время - sleep(5) - try { - k8sStagesV2.correctReplicaActive(cmdbVars=buildVars, applicationName=buildVars.app.name) - // Чтобы не оставлять мусора за собой в эвентах - sh "kubectl --kubeconfig=${ buildVars.kubeconfig } --namespace ${ buildVars.namespace } delete events --all" - } catch (Exception errorRun) { - logger.info("Не удалось произвести проверку работы микросервиса. Дебаг:") - runsh "kubectl --kubeconfig=${ buildVars.kubeconfig } --namespace ${ buildVars.namespace } events | grep ${ buildVars.app.name }" - runsh "kubectl --kubeconfig=${ buildVars.kubeconfig } --namespace ${ buildVars.namespace } logs -l 'app=${ buildVars.app.name }'" - error("Не удалось произвести проверку работы микросервиса. Детали в логах pipeline") - } - } - - currentBuild.result = 'SUCCESS' - } - catch (Exception err) { - logger.err("Произошла ошибка") - logger.err(err.message, true) - currentBuild.description = err.message - currentBuild.result = "FAILURE" - } - finally { - if (currentBuild.result == 'FAILURE') { - echo "Worker stage действительно упал — ставим флаг workerFailed" - workerFailed = true - } else if (currentBuild.result == 'SUCCESS') { - echo "Worker stage завершился успешно - ставим флаг workerSuccess" - workerSuccess = true - } else { - echo "Неопознаный currentBuild.result: ${currentBuild.result} — ставим флаг workerFailed" - workerFailed = true - } - runsh "rm -rf ${env.BUILD_NUMBER}" - } - }}} - } -} - -parallelSteps["master"] = { - node("master") { - try { - stage ("Master: start watchdog") { - timeout(time: TimeoutTime.time, unit: TimeoutTime.unit) { - while (true) { - // Следим за окончанием сборки - if (DEBUGGING_MODE) { - sleep 1 - // Только в режиме отладки используем echo - echo "DEBUG: workerFailed = ${workerFailed}" - echo "DEBUG: workerSuccess = ${workerSuccess}" - } - // Проверка флагов без использования шагов Jenkins - if (workerFailed) { - echo "Watchdog: обнаружен сбой в воркере" - break - } else if (workerSuccess) { - echo "Watchdog: воркер завершил работу в Jenkins" - break - } - } + dir (env.BUILD_NUMBER) { + checkout scm + dir ("executorJob") { + checkout scm: [ + $class: 'GitSCM', + userRemoteConfigs: [[ + url: 'gitea@gitea.mgmt.nubes.ru:deck/universalDeckPipelines.git', + credentialsId: 'gitea-jenkins-tech' + ]], + branches: [[ name: "master" ]] + ], poll: false + load "universal.groovy" } } - runsh "rm -rf ${env.BUILD_NUMBER}" - } - catch (Exception masterErrorTimeout) { - logger.err("Ошибка по таймауту") } } -} - -parallel parallelSteps \ No newline at end of file +} \ No newline at end of file diff --git a/build/vars/build.yaml b/build/vars/build.yaml index 1c7a80d..ba9cd24 100644 --- a/build/vars/build.yaml +++ b/build/vars/build.yaml @@ -1,50 +1,65 @@ -##### Переменные ##### -###################### -app: - # Имя приложения / Имя микросервиса +##### Переменные Контейнера ##### +################################# +application: name: "svc-api" - # Порт на котором приложение будет работать port: 8080 - # Health-Check приложения - health: "/api/v1" - # CPU пода (milicores) cpu: 1000 - # Mem пода (megabytes) mem: 1024 + probes: + readinessProbe: + failureThreshold: 5 + initialDelaySeconds: 10 + periodSeconds: 30 + httpGet: + port: ${ application.port } + livenessProbe: + failureThreshold: 5 + initialDelaySeconds: 30 + periodSeconds: 15 + tcpSocket: + port: ${ application.port } -# Получение версии приложения -# Используется для построения дашбордов / публикации в Registry -getCommandVersion: "awk -F'\"' '/APIVersion/ {print \\$2; exit}' v1/Application.cfc" + ###### Переменные Секретов ###### + ################################# + vault: + enable: true + type: inject + inject: + secretStore: "deck-${ gitBranch }" + secrets: + - "LUCEE_EXTENSIONS" + - "ORCHESTRATOR_AUTH" + - "VAULT_ROLE_ID" + - "VAULT_SECRET_ID" + - "cfconfig_datasources_cmdb_database" + - "cfconfig_datasources_cmdb_dbdriver" + - "cfconfig_datasources_cmdb_host" + - "cfconfig_datasources_cmdb_name" + - "cfconfig_datasources_cmdb_port" + - "cfconfig_datasources_cmdb_username" + - "cfconfig_datasources_cmdb_password" -# Если включено, то необходимо сделать файл Dockerfile_build_lib -# Либа будет загружена в deck/npm/nubes/{appName}/{version} -needBuildLib: false -# Нужно ли хранить секреты в Vault -needStoreVaultSecret: true -# Наименование key, которые нужно забрать из Vault -storedSecretsList: - - "LUCEE_EXTENSIONS" - - "ORCHESTRATOR_AUTH" - - "VAULT_ROLE_ID" - - "VAULT_SECRET_ID" - - "cfconfig_datasources_cmdb_database" - - "cfconfig_datasources_cmdb_dbdriver" - - "cfconfig_datasources_cmdb_host" - - "cfconfig_datasources_cmdb_name" - - "cfconfig_datasources_cmdb_port" - - "cfconfig_datasources_cmdb_username" - - "cfconfig_datasources_cmdb_password" + custom: + dev: + ingressAddr: deck-api-dev.ngcloud.ru + test: + ingressAddr: deck-api-test.ngcloud.ru + prod: + ingressAddr: deck-api.ngcloud.ru -##### Константы ##### -##################### -# Путь до секрета в Vault -vaultPath: "config.deck-${ app.name }-${ gitBranch }" -# Где хранится образ (Registry.app) -registryUrl: "registry.ngcloud.ru" -registryRepoPath: "deck" -registryContainerName: "deck-${ app.name }" -registryImageName: "${ registryRepoPath ? registryRepoPath + '/' + registryContainerName : registryContainerName }" + ingress: + enable: true + hosts: + - domain: "${application.custom[gitBranch].ingressAddr}" + service: "sys-${ application.name }" + port: "${ application.port }" + path: / -# В какой namespace попадает проект в Kubernetes (по хорошему нужно изменить) -# Ибо счас всё хранится в одном кубере. Кубер умрёт - 3 сренда в OUT -namespace: "deck-${ gitBranch }" \ No newline at end of file +###### Переменные Стенда ###### +############################### +kubernetes: + namespace: "deck-${ gitBranch }" + stand: + dev: k8s-1.dmz.nubes.ru + test: k8s-1.dmz.nubes.ru + prod: k8s-1.dmz.nubes.ru \ No newline at end of file From a31502402aec61cbb8348b57694bf1a1339f5c2b Mon Sep 17 00:00:00 2001 From: Vitaliy Zaytsev Date: Thu, 11 Dec 2025 14:52:55 +0300 Subject: [PATCH 2/2] Refactor pipe --- build/vars/build.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/build/vars/build.yaml b/build/vars/build.yaml index ba9cd24..8973d30 100644 --- a/build/vars/build.yaml +++ b/build/vars/build.yaml @@ -5,6 +5,7 @@ application: port: 8080 cpu: 1000 mem: 1024 + version: "awk -F'\"' '/APIVersion/ {print \\$2; exit}' v1/Application.cfc" probes: readinessProbe: failureThreshold: 5