Ответ
В Jenkins мы использовали Declarative Pipeline для четкого разделения этапов CI и CD. Вот как это выглядело на проекте с микросервисами на Java, которые деплоились в Kubernetes.
Конвейер (Jenkinsfile) был структурирован следующим образом:
pipeline {
agent any
environment {
DOCKER_REGISTRY = 'my-registry.example.com'
K8S_NAMESPACE = 'staging'
}
stages {
// --- ФАЗА CONTINUOUS INTEGRATION (CI) ---
stage('Checkout') {
steps { checkout scm }
}
stage('Build & Unit Tests') {
steps {
sh 'mvn clean package'
junit 'target/surefire-reports/*.xml'
}
}
stage('Static Code Analysis') {
steps {
sh 'mvn sonar:sonar'
}
}
stage('Build Docker Image') {
steps {
script {
dockerImage = docker.build("${DOCKER_REGISTRY}/app:${env.BUILD_NUMBER}")
}
}
}
stage('Push Image') {
steps {
script {
docker.withRegistry("https://${DOCKER_REGISTRY}", 'docker-credentials-id') {
dockerImage.push()
}
}
}
}
// --- ФАЗА CONTINUOUS DELIVERY/DEPLOYMENT (CD) ---
stage('Deploy to Staging') {
steps {
sh """
# Обновляем манифест Kubernetes новой версией образа
sed -i 's|IMAGE_TAG|${env.BUILD_NUMBER}|g' k8s/deployment.yaml
kubectl apply -f k8s/ -n ${K8S_NAMESPACE}
"""
}
}
stage('Integration Tests') {
steps {
sh 'mvn verify -Pintegration-tests'
}
}
stage('Approval for Production') {
steps {
timeout(time: 1, unit: 'HOURS') {
input message: 'Деплой в production?', ok: 'Deploy'
}
}
}
stage('Deploy to Production') {
environment {
K8S_NAMESPACE = 'production'
}
steps {
sh "kubectl apply -f k8s/ -n ${K8S_NAMESPACE}"
}
}
}
post {
always {
cleanWs() // Очистка workspace
}
failure {
slackSend(color: 'danger', message: "Build ${env.JOB_NAME} #${env.BUILD_NUMBER} failed")
}
success {
slackSend(color: 'good', message: "Build ${env.JOB_NAME} #${env.BUILD_NUMBER} succeeded and deployed to production")
}
}
}
Ключевые принципы разделения:
- CI-этапы (автоматические, быстрые): Все, что происходит до создания артефакта (Docker-образа). Их цель — гарантировать качество кода. Сбой на любом из этих этапов немедленно прерывает конвейер.
- CD-этапы (управляемые, последовательные): Все, что связано с развертыванием артефакта в среды. Мы внедряли "воротца" (gates), такие как ручное подтверждение (
input) перед production, и обязательные интеграционные тесты в staging-среде. - Разделение агентов: Иногда для этапов сборки и деплоя использовались разные Jenkins agents (например, сборка на агенте с Maven, а деплой — на агенте с настроенным
kubectlи доступом к кластеру).