+ 격리·무중단·롤백·장기 운영까지 폭넓게 다뤘다.
- 슬래시가 든 Git 태그를 Docker 태그로 써 빌드가 실패한다.
모노레포 API와 배포 조건을 넣으면, 태그별 Jenkins 파이프라인, Docker 이미지, Artifactory, Kubernetes 배포 전략을 설명합니다.
| 분류 | 개발 › 배포·운영 |
|---|---|
| 태그 | 분석초안작성개발자코드 |
Act as a DevOps Consultant. You are an expert in CI/CD processes and Kubernetes deployments, specializing in SpringBoot applications. Your task is to provide guidance on setting up a CI/CD pipeline using CloudBees Jenkins to deploy multiple SpringBoot REST APIs stored in a monorepo. Each API, such as notesAPI, claimsAPI, and documentsAPI, will be independently deployed as Docker images to Kubernetes, triggered by specific tags. You will: - Design a tagging strategy where a NOTE tag triggers the NoteAPI pipeline, a CLAIM tag triggers the ClaimsAPI pipeline, and so on. - Explain how to implement Blue-Green deployment for each API to ensure zero-downtime during updates. - Provide steps for building Docker images, pushing them to Artifactory, and deploying them to Kubernetes. - Ensure that changes to one API do not affect the others, maintaining isolation in the deployment process. Rules: - Focus on scalability and maintainability of the CI/CD pipeline. - Consider long-term feasibility and potential challenges, such as tag management and pipeline complexity. - Offer solutions or best practices for handling common issues in such setups.
SpringBoot REST API를 CloudBees Jenkins와 Kubernetes로 배포하는 DevOps Consultant 역할 프롬프트다. API별 격리, Blue-Green 배포, 태그 관리 복잡성을 다룬다.
ChatGPT가 가장 완결적이나 이미지 태그 오류가 있고, Gemini는 실용적이지만 배포 예시에 결함이 있다. Claude는 설계 품질은 높아도 답이 중간에 잘려 크게 감점된다.
+ 격리·무중단·롤백·장기 운영까지 폭넓게 다뤘다.
- 슬래시가 든 Git 태그를 Docker 태그로 써 빌드가 실패한다.
+ 전체 흐름과 실행 예시가 연결되어 이해하기 쉽다.
- 프리뷰 슬롯·서비스 포트 오류로 스모크 테스트가 깨질 수 있다.
+ 설정 중심 확장 구조와 이미지 승격 전략이 구체적이다.
- 배포 코드가 문장 중간에서 잘려 핵심 구현이 완결되지 않았다.
| 기준 | ChatGPT | Claude | Gemini | 앞선 모델 |
|---|---|---|---|---|
| 지시 준수 | 9 | 5 | 9 | 동점 |
| 정확성 | 7 | 7 | 6 | 동점 |
| 구체성 | 9 | 9 | 9 | 동점 |
| 구성·가독성 | 9 | 5 | 9 | 동점 |
| 분량 적정성 | 6 | 3 | 7 | Gemini +17% |
gpt-5.6-sol가 모델 이름을 가린 채 1~10점으로 채점했습니다 (2026-09-25). 측정값이 아니라 AI 심사 결과입니다.
같은 입력을 세 모델에 넣어 받은 답을 그대로 옮겼습니다. 각 CLI(에이전트 하네스)에서 실행했습니다. ChatGPT·Claude 앱이나 웹에서는 답이 다를 수 있습니다.
모노레포 안에 여러 SpringBoot REST API가 존재한다고 가정합니다.
repo/
├── notesAPI/
├── claimsAPI/
├── documentsAPI/
├── deploy/
│ ├── notes/
│ ├── claims/
│ └── documents/
└── Jenkinsfile
각 API는 독립적인 Docker 이미지와 Kubernetes Deployment로 관리합니다.
단순히 NOTE, CLAIM 같은 고정 태그를 사용하는 것보다 서비스명과 버전을 포함하는 불변 태그를 권장합니다.
notes-v1.4.0
claims-v2.1.3
documents-v1.0.7
또는 요청한 접두어를 유지하려면 다음 형식을 사용할 수 있습니다.
NOTE/v1.4.0
CLAIM/v2.1.3
DOCUMENT/v1.0.7
태그 규칙:
NOTE/v1.4.0 → notesAPI 파이프라인 실행CLAIM/v2.1.3 → claimsAPI 파이프라인 실행DOCUMENT/v1.0.7 → documentsAPI 파이프라인 실행latest, stable 같은 가변 태그는 배포 기준으로 사용하지 않음예시:
git tag -a NOTE/v1.4.0 -m "Release notesAPI v1.4.0"
git push origin NOTE/v1.4.0
CloudBees Jenkins에서는 Git webhook으로 태그 생성 이벤트를 받고, 각 파이프라인이 자신의 태그 패턴만 처리하도록 구성합니다.
가장 유지보수하기 좋은 방식은 API별 Jenkins Pipeline Job을 별도로 구성하는 것입니다.
notesAPI-pipeline
claimsAPI-pipeline
documentsAPI-pipeline
각 Job의 태그 필터:
notesAPI-pipeline → NOTE/.*
claimsAPI-pipeline → CLAIM/.*
documentsAPI-pipeline → DOCUMENT/.*
모노레포의 공통 Jenkinsfile을 사용하되, 파이프라인 실행 시 서비스명을 파라미터로 전달할 수 있습니다.
pipeline {
agent { label 'docker-k8s-builder' }
parameters {
string(name: 'SERVICE_NAME')
string(name: 'TAG_NAME')
}
environment {
REGISTRY = 'artifactory.example.com/docker-release'
}
stages {
stage('Checkout') {
steps {
checkout scm
}
}
stage('Validate Tag') {
steps {
sh '''
case "$TAG_NAME" in
NOTE/*) test "$SERVICE_NAME" = "notesAPI" ;;
CLAIM/*) test "$SERVICE_NAME" = "claimsAPI" ;;
DOCUMENT/*) test "$SERVICE_NAME" = "documentsAPI" ;;
*) exit 1 ;;
esac
'''
}
}
stage('Test') {
steps {
sh "./gradlew :${SERVICE_NAME}:test"
}
}
stage('Build Image') {
steps {
sh """
docker build \
-f ${SERVICE_NAME}/Dockerfile \
-t ${REGISTRY}/${SERVICE_NAME}:${TAG_NAME} \
${SERVICE_NAME}
"""
}
}
stage('Push Image') {
steps {
withCredentials([usernamePassword(
credentialsId: 'artifactory-docker',
usernameVariable: 'ART_USER',
passwordVariable: 'ART_PASSWORD'
)]) {
sh """
echo "$ART_PASSWORD" | docker login ${REGISTRY} \
-u "$ART_USER" --password-stdin
docker push ${REGISTRY}/${SERVICE_NAME}:${TAG_NAME}
"""
}
}
}
stage('Deploy') {
steps {
sh "./deploy/deploy.sh ${SERVICE_NAME} ${TAG_NAME}"
}
}
}
}
실제 운영에서는 Docker socket을 Jenkins Agent에 직접 연결하기보다 다음을 고려하는 것이 좋습니다.
태그가 생성되었다고 해서 항상 해당 API를 빌드하도록 두지 말고, 태그와 소스 변경 경로를 검증하는 것이 좋습니다.
예를 들어 NOTE/v1.4.0 태그라면 다음 조건을 확인합니다.
notesAPI/**
공통 라이브러리 변경
루트 build.gradle 또는 settings.gradle 변경
공통 Docker 설정 변경
그 외 claimsAPI/**만 변경된 상태에서 NOTE/v1.4.0 태그가 생성되면 파이프라인을 실패시키거나 승인을 요구합니다.
다만 태그는 특정 커밋을 가리키므로, 기준은 “태그 생성 이후 변경”이 아니라 “태그가 가리키는 커밋의 변경 파일”이어야 합니다.
예시 검증 로직:
git diff-tree --no-commit-id --name-only -r "$TAG_NAME"
공통 모듈을 사용하는 경우에는 다음 매핑 파일을 관리할 수 있습니다.
services:
notesAPI:
paths:
- notesAPI/**
- common/**
- build.gradle
claimsAPI:
paths:
- claimsAPI/**
- common/**
- build.gradle
이렇게 하면 한 API의 배포가 다른 API의 Docker 이미지나 Kubernetes 리소스를 변경하지 않습니다.
각 API는 자체 Dockerfile을 갖습니다.
FROM eclipse-temurin:21-jre
WORKDIR /app
COPY build/libs/notesAPI.jar app.jar
USER 10001
ENTRYPOINT ["java", "-jar", "/app/app.jar"]
권장 이미지 이름:
artifactory.example.com/docker-release/notesAPI:NOTE-v1.4.0
artifactory.example.com/docker-release/claimsAPI:CLAIM-v2.1.3
artifactory.example.com/docker-release/documentsAPI:DOCUMENT-v1.0.7
빌드 단계에서 수행할 작업:
운영 배포 시에는 태그보다 digest를 최종 기준으로 사용하는 것이 안전합니다.
notesAPI@sha256:abc123...
Jenkins가 Push 직후 digest를 조회하고 Kubernetes에는 다음처럼 전달합니다.
kubectl -n production set image deployment/notes-api-green \
notes-api=artifactory.example.com/docker-release/notesAPI@sha256:abc123
각 API마다 Blue와 Green Deployment를 별도로 둡니다.
notes-api-blue
notes-api-green
claims-api-blue
claims-api-green
documents-api-blue
documents-api-green
트래픽은 하나의 Service가 선택된 색상으로만 전달하도록 구성합니다.
apiVersion: v1
kind: Service
metadata:
name: notes-api
spec:
selector:
app: notes-api
version: blue
ports:
- port: 80
targetPort: 8080
Blue Deployment:
metadata:
name: notes-api-blue
spec:
replicas: 3
selector:
matchLabels:
app: notes-api
version: blue
template:
metadata:
labels:
app: notes-api
version: blue
spec:
containers:
- name: notes-api
image: artifactory.example.com/docker-release/notesAPI:NOTE-v1.3.0
ports:
- containerPort: 8080
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
Green에는 새 이미지가 배포됩니다.
배포 절차:
예시:
kubectl -n production apply -f deploy/notes/green.yaml
kubectl -n production rollout status \
deployment/notes-api-green --timeout=5m
curl --fail https://notes-api-green.internal/actuator/health
kubectl -n production patch service notes-api \
-p '{"spec":{"selector":{"app":"notes-api","version":"green"}}}'
Blue-Green의 핵심은 이미지 교체와 트래픽 전환을 분리하는 것입니다. 새 Pod가 정상인지 확인하기 전에는 Service selector를 변경하지 않습니다.
무중단 배포를 위해 다음 설정을 권장합니다.
management:
endpoint:
health:
probes:
enabled: true
server:
shutdown: graceful
spring:
lifecycle:
timeout-per-shutdown-phase: 30s
추가 권장 사항:
/actuator/health/readiness와 /actuator/health/liveness 분리terminationGracePeriodSeconds 설정preStop hook으로 연결 종료 시간 확보Blue-Green 배포에서 가장 자주 문제가 되는 부분은 데이터베이스 스키마입니다.
새 버전과 이전 버전이 잠시 동시에 실행되므로, 마이그레이션은 다음 방식으로 진행해야 합니다.
Flyway 또는 Liquibase를 사용하되, 삭제·이름 변경처럼 즉시 호환성을 깨는 마이그레이션은 피해야 합니다.
롤백은 Kubernetes Deployment의 이전 ReplicaSet으로 되돌리는 것보다 Service selector를 이전 색상으로 복구하는 방식이 빠르고 명확합니다.
kubectl -n production patch service notes-api \
-p '{"spec":{"selector":{"app":"notes-api","version":"blue"}}}'
롤백 조건은 사전에 정의합니다.
Blue-Green 유지 시간을 정해 두고, 예를 들어 30분 동안 문제가 없을 때만 이전 색상을 축소합니다.
CloudBees Jenkins와 Kubernetes 사이에는 최소 권한 원칙을 적용합니다.
예를 들어 개발·스테이징은 자동 배포하고, 운영은 다음 조건을 요구할 수 있습니다.
테스트 통과
이미지 취약점 기준 통과
변경 승인 완료
운영 배포 승인
API 수가 증가하면 서비스별 Jenkinsfile을 복사하는 방식은 복잡해집니다. 다음 구조가 적합합니다.
shared-library/
├── vars/
│ └── springBootServicePipeline.groovy
└── src/
각 Job은 서비스명과 태그 패턴만 선언합니다.
springBootServicePipeline(
serviceName: 'notesAPI',
tagPattern: 'NOTE/*',
namespace: 'production',
deployment: 'notes-api'
)
또는 서비스 정의 파일을 중앙에서 관리합니다.
services:
notesAPI:
tagPrefix: NOTE
imageRepository: docker-release/notesAPI
deployment: notes-api
namespace: production
claimsAPI:
tagPrefix: CLAIM
imageRepository: docker-release/claimsAPI
deployment: claims-api
namespace: production
서비스 수가 많아지면 Jenkins가 Kubernetes manifest를 직접 문자열로 조작하기보다 다음 중 하나를 사용하는 편이 좋습니다.
특히 운영 규모가 커지면 Jenkins는 테스트와 이미지 빌드까지만 수행하고, 배포는 GitOps 도구에 맡기는 구조가 관리하기 쉽습니다.
Git Tag
↓
CloudBees Jenkins
↓
테스트 · 이미지 빌드 · Artifactory Push
↓
배포 리포지토리 이미지 digest 변경
↓
Argo CD / Flux
↓
Kubernetes Blue-Green 배포
NOTE/v1.4.0 생성
↓
notesAPI Jenkins Pipeline만 실행
↓
notesAPI 테스트 및 이미지 빌드
↓
Artifactory Push
↓
notes-api-green 배포
↓
readiness 및 smoke test
↓
notes-api Service를 green으로 전환
이 구조에서는 claimsAPI와 documentsAPI의 이미지, Deployment, Service가 변경되지 않으므로 API별 배포 격리가 보장됩니다. 핵심은 서비스별 태그 규칙, 불변 이미지 식별자, 별도 Kubernetes 리소스, 그리고 트래픽 전환 전 검증입니다.
| 프로덕션 AI 에이전트 설계 프롬프트 | |
| AI 에이전트 보안 점검 프롬프트 | |
| AI 제공자 요금 조사 프롬프트 | |
| 상자 탈출 AI 역할을 맡기는 프롬프트 | |
| 보안 경고 분석 프롬프트 |