mirror of
https://github.com/cna-bootcamp/phonebill.git
synced 2025-12-06 08:06:24 +00:00
Jenkins CI/CD 파이프라인 완전 구축 및 가이드 개선
주요 작업: - Kustomize 기반 환경별 배포 구조 완성 (dev/staging/prod) - deployment-patch.yaml 개선: replicas + resources 통합 관리 - Strategic Merge Patch 형식으로 변경하여 가독성 및 유지보수성 향상 - 환경별 차등 리소스 할당 정책 적용 - Jenkins 파이프라인 스크립트 및 수동 배포 스크립트 완성 - 상세한 체크리스트 및 실수 방지 가이드 추가 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
892f30ba44
commit
042198deb0
486
deployment/cicd/Jenkinsfile
vendored
486
deployment/cicd/Jenkinsfile
vendored
@ -1,425 +1,149 @@
|
|||||||
#!/usr/bin/env groovy
|
def PIPELINE_ID = "${env.BUILD_NUMBER}"
|
||||||
|
|
||||||
/**
|
def getImageTag() {
|
||||||
* Jenkins Pipeline for phonebill Microservices
|
def dateFormat = new java.text.SimpleDateFormat('yyyyMMddHHmmss')
|
||||||
* Supports multi-environment deployment (dev, staging, prod)
|
def currentDate = new Date()
|
||||||
* Services: api-gateway, user-service, bill-service, product-service, kos-mock
|
return dateFormat.format(currentDate)
|
||||||
*/
|
}
|
||||||
|
|
||||||
pipeline {
|
podTemplate(
|
||||||
agent {
|
label: "${PIPELINE_ID}",
|
||||||
kubernetes {
|
serviceAccount: 'jenkins',
|
||||||
yaml '''
|
containers: [
|
||||||
apiVersion: v1
|
containerTemplate(name: 'podman', image: "mgoltzsche/podman", ttyEnabled: true, command: 'cat', privileged: true),
|
||||||
kind: Pod
|
containerTemplate(name: 'gradle', image: 'gradle:jdk17', ttyEnabled: true, command: 'cat'),
|
||||||
metadata:
|
containerTemplate(name: 'azure-cli', image: 'hiondal/azure-kubectl:latest', command: 'cat', ttyEnabled: true)
|
||||||
labels:
|
]
|
||||||
jenkins: agent
|
) {
|
||||||
spec:
|
node(PIPELINE_ID) {
|
||||||
serviceAccountName: jenkins-agent
|
def props
|
||||||
containers:
|
def imageTag = getImageTag()
|
||||||
- name: gradle
|
def environment = params.ENVIRONMENT ?: 'dev'
|
||||||
image: gradle:8.5-jdk17
|
def services = ['api-gateway', 'user-service', 'bill-service', 'product-service', 'kos-mock']
|
||||||
command:
|
|
||||||
- cat
|
|
||||||
tty: true
|
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
memory: "1Gi"
|
|
||||||
cpu: "500m"
|
|
||||||
limits:
|
|
||||||
memory: "2Gi"
|
|
||||||
cpu: "1"
|
|
||||||
volumeMounts:
|
|
||||||
- name: gradle-cache
|
|
||||||
mountPath: /home/gradle/.gradle
|
|
||||||
- name: podman
|
|
||||||
image: quay.io/podman/stable:latest
|
|
||||||
command:
|
|
||||||
- cat
|
|
||||||
tty: true
|
|
||||||
securityContext:
|
|
||||||
privileged: true
|
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
memory: "1Gi"
|
|
||||||
cpu: "500m"
|
|
||||||
limits:
|
|
||||||
memory: "2Gi"
|
|
||||||
cpu: "1"
|
|
||||||
- name: azure-cli
|
|
||||||
image: mcr.microsoft.com/azure-cli:latest
|
|
||||||
command:
|
|
||||||
- cat
|
|
||||||
tty: true
|
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
memory: "512Mi"
|
|
||||||
cpu: "250m"
|
|
||||||
limits:
|
|
||||||
memory: "1Gi"
|
|
||||||
cpu: "500m"
|
|
||||||
- name: kubectl
|
|
||||||
image: bitnami/kubectl:latest
|
|
||||||
command:
|
|
||||||
- cat
|
|
||||||
tty: true
|
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
memory: "256Mi"
|
|
||||||
cpu: "100m"
|
|
||||||
limits:
|
|
||||||
memory: "512Mi"
|
|
||||||
cpu: "250m"
|
|
||||||
volumes:
|
|
||||||
- name: gradle-cache
|
|
||||||
persistentVolumeClaim:
|
|
||||||
claimName: jenkins-gradle-cache
|
|
||||||
'''
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
parameters {
|
stage("Get Source") {
|
||||||
choice(
|
|
||||||
name: 'ENVIRONMENT',
|
|
||||||
choices: ['dev', 'staging', 'prod'],
|
|
||||||
description: 'Target deployment environment'
|
|
||||||
)
|
|
||||||
choice(
|
|
||||||
name: 'SERVICES_TO_BUILD',
|
|
||||||
choices: ['all', 'api-gateway', 'user-service', 'bill-service', 'product-service', 'kos-mock'],
|
|
||||||
description: 'Services to build and deploy'
|
|
||||||
)
|
|
||||||
booleanParam(
|
|
||||||
name: 'SKIP_TESTS',
|
|
||||||
defaultValue: false,
|
|
||||||
description: 'Skip unit tests during build'
|
|
||||||
)
|
|
||||||
booleanParam(
|
|
||||||
name: 'SKIP_SONAR',
|
|
||||||
defaultValue: false,
|
|
||||||
description: 'Skip SonarQube analysis'
|
|
||||||
)
|
|
||||||
booleanParam(
|
|
||||||
name: 'FORCE_DEPLOY',
|
|
||||||
defaultValue: false,
|
|
||||||
description: 'Force deployment even if no changes detected'
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
environment {
|
|
||||||
// Load environment-specific variables
|
|
||||||
CONFIG_FILE = "deployment/cicd/config/deploy_env_vars_${params.ENVIRONMENT}"
|
|
||||||
|
|
||||||
// Build configuration
|
|
||||||
GRADLE_USER_HOME = '/home/gradle/.gradle'
|
|
||||||
GRADLE_OPTS = '-Xmx2048m -XX:MaxPermSize=512m'
|
|
||||||
|
|
||||||
// Azure credentials
|
|
||||||
AZURE_CREDENTIALS = credentials('azure-service-principal')
|
|
||||||
ACR_CREDENTIALS = credentials('acr-credentials')
|
|
||||||
|
|
||||||
// SonarQube
|
|
||||||
SONAR_TOKEN = credentials('sonarqube-token')
|
|
||||||
|
|
||||||
// Slack notification
|
|
||||||
SLACK_TOKEN = credentials('slack-token')
|
|
||||||
}
|
|
||||||
|
|
||||||
stages {
|
|
||||||
stage('Initialize') {
|
|
||||||
steps {
|
|
||||||
script {
|
|
||||||
echo "🚀 Starting phonebill pipeline for ${params.ENVIRONMENT} environment"
|
|
||||||
|
|
||||||
// Load environment-specific configuration
|
|
||||||
if (fileExists(env.CONFIG_FILE)) {
|
|
||||||
def props = readProperties file: env.CONFIG_FILE
|
|
||||||
props.each { key, value ->
|
|
||||||
env[key] = value
|
|
||||||
}
|
|
||||||
echo "✅ Loaded configuration from ${env.CONFIG_FILE}"
|
|
||||||
} else {
|
|
||||||
error "❌ Configuration file not found: ${env.CONFIG_FILE}"
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set services to build
|
|
||||||
if (params.SERVICES_TO_BUILD == 'all') {
|
|
||||||
env.SERVICES_LIST = env.SERVICES
|
|
||||||
} else {
|
|
||||||
env.SERVICES_LIST = params.SERVICES_TO_BUILD
|
|
||||||
}
|
|
||||||
|
|
||||||
echo "🎯 Services to build: ${env.SERVICES_LIST}"
|
|
||||||
echo "📦 Target namespace: ${env.AKS_NAMESPACE}"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
stage('Checkout & Prepare') {
|
|
||||||
steps {
|
|
||||||
checkout scm
|
checkout scm
|
||||||
script {
|
props = readProperties file: "deployment/cicd/config/deploy_env_vars_${environment}"
|
||||||
env.BUILD_TIMESTAMP = sh(
|
|
||||||
script: 'date +%Y%m%d-%H%M%S',
|
|
||||||
returnStdout: true
|
|
||||||
).trim()
|
|
||||||
|
|
||||||
env.IMAGE_TAG = "${env.BUILD_NUMBER}-${params.ENVIRONMENT}-${env.BUILD_TIMESTAMP}"
|
|
||||||
echo "🏷️ Image tag: ${env.IMAGE_TAG}"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
stage('Build & Test') {
|
stage("Setup AKS") {
|
||||||
parallel {
|
container('azure-cli') {
|
||||||
stage('Gradle Build') {
|
withCredentials([azureServicePrincipal('azure-credentials')]) {
|
||||||
steps {
|
|
||||||
container('gradle') {
|
|
||||||
script {
|
|
||||||
def services = env.SERVICES_LIST.split(',')
|
|
||||||
for (service in services) {
|
|
||||||
echo "🔨 Building ${service}..."
|
|
||||||
|
|
||||||
if (!params.SKIP_TESTS) {
|
|
||||||
sh """
|
sh """
|
||||||
./gradlew ${service}:clean ${service}:test ${service}:build \
|
az login --service-principal -u \$AZURE_CLIENT_ID -p \$AZURE_CLIENT_SECRET -t \$AZURE_TENANT_ID
|
||||||
--no-daemon \
|
az aks get-credentials --resource-group \${props.resource_group} --name \${props.cluster_name} --overwrite-existing
|
||||||
--parallel \
|
kubectl create namespace phonebill-\${environment} --dry-run=client -o yaml | kubectl apply -f -
|
||||||
--build-cache
|
|
||||||
"""
|
|
||||||
} else {
|
|
||||||
sh """
|
|
||||||
./gradlew ${service}:clean ${service}:build -x test \
|
|
||||||
--no-daemon \
|
|
||||||
--parallel \
|
|
||||||
--build-cache
|
|
||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
post {
|
|
||||||
always {
|
|
||||||
// Publish test results
|
|
||||||
script {
|
|
||||||
def services = env.SERVICES_LIST.split(',')
|
|
||||||
for (service in services) {
|
|
||||||
if (fileExists("${service}/build/test-results/test/*.xml")) {
|
|
||||||
publishTestResults(
|
|
||||||
testResultsPattern: "${service}/build/test-results/test/*.xml",
|
|
||||||
allowEmptyResults: true
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
stage('SonarQube Analysis') {
|
stage('Build & SonarQube Analysis') {
|
||||||
when {
|
|
||||||
not { params.SKIP_SONAR }
|
|
||||||
}
|
|
||||||
steps {
|
|
||||||
container('gradle') {
|
container('gradle') {
|
||||||
withSonarQubeEnv('SonarQube') {
|
withSonarQubeEnv('SonarQube') {
|
||||||
sh '''
|
sh """
|
||||||
./gradlew sonarqube \
|
chmod +x gradlew
|
||||||
-Dsonar.projectKey=${SONAR_PROJECT_KEY} \
|
./gradlew build -x test
|
||||||
-Dsonar.sources=${SONAR_SOURCES} \
|
|
||||||
-Dsonar.exclusions="${SONAR_EXCLUSIONS}" \
|
# 각 서비스별 테스트 및 분석
|
||||||
--no-daemon
|
./gradlew :api-gateway:test :api-gateway:jacocoTestReport :api-gateway:sonar \\
|
||||||
'''
|
-Dsonar.projectKey=phonebill-api-gateway-\${environment} \\
|
||||||
}
|
-Dsonar.projectName=phonebill-api-gateway
|
||||||
|
|
||||||
|
./gradlew :user-service:test :user-service:jacocoTestReport :user-service:sonar \\
|
||||||
|
-Dsonar.projectKey=phonebill-user-service-\${environment} \\
|
||||||
|
-Dsonar.projectName=phonebill-user-service
|
||||||
|
|
||||||
|
./gradlew :bill-service:test :bill-service:jacocoTestReport :bill-service:sonar \\
|
||||||
|
-Dsonar.projectKey=phonebill-bill-service-\${environment} \\
|
||||||
|
-Dsonar.projectName=phonebill-bill-service
|
||||||
|
|
||||||
|
./gradlew :product-service:test :product-service:jacocoTestReport :product-service:sonar \\
|
||||||
|
-Dsonar.projectKey=phonebill-product-service-\${environment} \\
|
||||||
|
-Dsonar.projectName=phonebill-product-service
|
||||||
|
|
||||||
|
./gradlew :kos-mock:test :kos-mock:jacocoTestReport :kos-mock:sonar \\
|
||||||
|
-Dsonar.projectKey=phonebill-kos-mock-\${environment} \\
|
||||||
|
-Dsonar.projectName=phonebill-kos-mock
|
||||||
|
"""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
stage('Quality Gate') {
|
stage('Quality Gate') {
|
||||||
when {
|
timeout(time: 10, unit: 'MINUTES') {
|
||||||
not { params.SKIP_SONAR }
|
def qg = waitForQualityGate()
|
||||||
}
|
if (qg.status != 'OK') {
|
||||||
steps {
|
error "Pipeline aborted due to quality gate failure: \${qg.status}"
|
||||||
timeout(time: 5, unit: 'MINUTES') {
|
|
||||||
waitForQualityGate abortPipeline: true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
stage('Container Build & Push') {
|
stage('Build & Push Images') {
|
||||||
parallel {
|
|
||||||
stage('Build Images') {
|
|
||||||
steps {
|
|
||||||
container('podman') {
|
container('podman') {
|
||||||
script {
|
withCredentials([usernamePassword(
|
||||||
// Login to ACR
|
credentialsId: 'acr-credentials',
|
||||||
|
usernameVariable: 'USERNAME',
|
||||||
|
passwordVariable: 'PASSWORD'
|
||||||
|
)]) {
|
||||||
|
sh "podman login acrdigitalgarage01.azurecr.io --username \$USERNAME --password \$PASSWORD"
|
||||||
|
|
||||||
|
services.each { service ->
|
||||||
sh """
|
sh """
|
||||||
echo "${ACR_CREDENTIALS_PSW}" | podman login \
|
podman build \\
|
||||||
--username "${ACR_CREDENTIALS_USR}" \
|
--build-arg BUILD_LIB_DIR="\${service}/build/libs" \\
|
||||||
--password-stdin \
|
--build-arg ARTIFACTORY_FILE="\${service}.jar" \\
|
||||||
${REGISTRY_URL}
|
-f deployment/container/Dockerfile \\
|
||||||
"""
|
-t acrdigitalgarage01.azurecr.io/phonebill/\${service}:\${environment}-\${imageTag} .
|
||||||
|
|
||||||
def services = env.SERVICES_LIST.split(',')
|
podman push acrdigitalgarage01.azurecr.io/phonebill/\${service}:\${environment}-\${imageTag}
|
||||||
for (service in services) {
|
|
||||||
echo "🐳 Building container image for ${service}..."
|
|
||||||
|
|
||||||
sh """
|
|
||||||
cd ${service}
|
|
||||||
podman build \
|
|
||||||
--tag ${REGISTRY_URL}/phonebill/${service}:${IMAGE_TAG} \
|
|
||||||
--tag ${REGISTRY_URL}/phonebill/${service}:latest-${params.ENVIRONMENT} \
|
|
||||||
--file Dockerfile \
|
|
||||||
.
|
|
||||||
|
|
||||||
podman push ${REGISTRY_URL}/phonebill/${service}:${IMAGE_TAG}
|
|
||||||
podman push ${REGISTRY_URL}/phonebill/${service}:latest-${params.ENVIRONMENT}
|
|
||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
stage('Deploy to Kubernetes') {
|
stage('Update Kustomize & Deploy') {
|
||||||
steps {
|
container('azure-cli') {
|
||||||
container('kubectl') {
|
|
||||||
script {
|
|
||||||
// Login to Azure and get AKS credentials
|
|
||||||
sh """
|
sh """
|
||||||
az login --service-principal \
|
# Kustomize 설치
|
||||||
--username "${AZURE_CREDENTIALS_USR}" \
|
curl -s "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh" | bash
|
||||||
--password "${AZURE_CREDENTIALS_PSW}" \
|
sudo mv kustomize /usr/local/bin/
|
||||||
--tenant "${AZURE_CREDENTIALS_TENANT_ID}"
|
|
||||||
|
|
||||||
az aks get-credentials \
|
# 환경별 디렉토리로 이동
|
||||||
--resource-group ${AZURE_RESOURCE_GROUP} \
|
cd deployment/cicd/kustomize/overlays/\${environment}
|
||||||
--name ${AKS_CLUSTER_NAME} \
|
|
||||||
--overwrite-existing
|
# 이미지 태그 업데이트
|
||||||
|
kustomize edit set image acrdigitalgarage01.azurecr.io/phonebill/api-gateway:\${environment}-\${imageTag}
|
||||||
|
kustomize edit set image acrdigitalgarage01.azurecr.io/phonebill/user-service:\${environment}-\${imageTag}
|
||||||
|
kustomize edit set image acrdigitalgarage01.azurecr.io/phonebill/bill-service:\${environment}-\${imageTag}
|
||||||
|
kustomize edit set image acrdigitalgarage01.azurecr.io/phonebill/product-service:\${environment}-\${imageTag}
|
||||||
|
kustomize edit set image acrdigitalgarage01.azurecr.io/phonebill/kos-mock:\${environment}-\${imageTag}
|
||||||
|
|
||||||
|
# 매니페스트 적용
|
||||||
|
kubectl apply -k .
|
||||||
|
|
||||||
|
echo "Waiting for deployments to be ready..."
|
||||||
|
kubectl -n phonebill-\${environment} wait --for=condition=available deployment/\${environment}-api-gateway --timeout=300s
|
||||||
|
kubectl -n phonebill-\${environment} wait --for=condition=available deployment/\${environment}-user-service --timeout=300s
|
||||||
|
kubectl -n phonebill-\${environment} wait --for=condition=available deployment/\${environment}-bill-service --timeout=300s
|
||||||
|
kubectl -n phonebill-\${environment} wait --for=condition=available deployment/\${environment}-product-service --timeout=300s
|
||||||
|
kubectl -n phonebill-\${environment} wait --for=condition=available deployment/\${environment}-kos-mock --timeout=300s
|
||||||
"""
|
"""
|
||||||
|
|
||||||
// Deploy services using kustomize
|
|
||||||
def services = env.SERVICES_LIST.split(',')
|
|
||||||
for (service in services) {
|
|
||||||
echo "🚀 Deploying ${service} to ${params.ENVIRONMENT}..."
|
|
||||||
|
|
||||||
sh """
|
|
||||||
cd ${KUSTOMIZE_BASE}/${service}
|
|
||||||
|
|
||||||
# Update image tag in kustomization.yaml
|
|
||||||
sed -i 's|newTag:.*|newTag: ${IMAGE_TAG}|' ${KUSTOMIZE_OVERLAY}/kustomization.yaml
|
|
||||||
|
|
||||||
# Apply deployment
|
|
||||||
kubectl apply -k ${KUSTOMIZE_OVERLAY} -n ${AKS_NAMESPACE}
|
|
||||||
|
|
||||||
# Wait for rollout
|
|
||||||
kubectl rollout status deployment/${service} -n ${AKS_NAMESPACE} --timeout=300s
|
|
||||||
"""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
stage('Health Check') {
|
stage('Health Check') {
|
||||||
steps {
|
container('azure-cli') {
|
||||||
container('kubectl') {
|
|
||||||
script {
|
|
||||||
def services = env.SERVICES_LIST.split(',')
|
|
||||||
for (service in services) {
|
|
||||||
echo "🏥 Health checking ${service}..."
|
|
||||||
|
|
||||||
retry(count: env.HEALTH_CHECK_RETRY as Integer) {
|
|
||||||
sh """
|
sh """
|
||||||
kubectl get deployment ${service} -n ${AKS_NAMESPACE} -o json | \
|
echo "🔍 Health Check starting..."
|
||||||
jq -e '.status.readyReplicas == .status.replicas and .status.replicas > 0'
|
|
||||||
|
# API Gateway Health Check
|
||||||
|
GATEWAY_POD=\$(kubectl get pod -n phonebill-\${environment} -l app=api-gateway -o jsonpath='{.items[0].metadata.name}')
|
||||||
|
kubectl -n phonebill-\${environment} exec \$GATEWAY_POD -- curl -f http://localhost:8080/actuator/health || exit 1
|
||||||
|
|
||||||
|
echo "✅ All services are healthy!"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
sleep time: 30, unit: 'SECONDS'
|
|
||||||
}
|
|
||||||
|
|
||||||
echo "✅ ${service} is healthy"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
post {
|
|
||||||
always {
|
|
||||||
script {
|
|
||||||
// Archive build artifacts
|
|
||||||
archiveArtifacts(
|
|
||||||
artifacts: '**/build/libs/*.jar',
|
|
||||||
allowEmptyArchive: true,
|
|
||||||
fingerprint: true
|
|
||||||
)
|
|
||||||
|
|
||||||
// Clean workspace
|
|
||||||
cleanWs()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
success {
|
|
||||||
script {
|
|
||||||
def message = """
|
|
||||||
✅ *phonebill Deployment Successful*
|
|
||||||
• Environment: `${params.ENVIRONMENT}`
|
|
||||||
• Services: `${env.SERVICES_LIST}`
|
|
||||||
• Image Tag: `${env.IMAGE_TAG}`
|
|
||||||
• Build: `${env.BUILD_NUMBER}`
|
|
||||||
• Duration: `${currentBuild.durationString}`
|
|
||||||
""".stripIndent()
|
|
||||||
|
|
||||||
// Send Slack notification
|
|
||||||
slackSend(
|
|
||||||
channel: env.SLACK_CHANNEL,
|
|
||||||
color: 'good',
|
|
||||||
message: message,
|
|
||||||
token: env.SLACK_TOKEN
|
|
||||||
)
|
|
||||||
|
|
||||||
// Send email notification
|
|
||||||
emailext(
|
|
||||||
subject: "✅ phonebill Deployment Success - ${params.ENVIRONMENT}",
|
|
||||||
body: message,
|
|
||||||
to: env.EMAIL_RECIPIENTS
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
failure {
|
|
||||||
script {
|
|
||||||
def message = """
|
|
||||||
❌ *phonebill Deployment Failed*
|
|
||||||
• Environment: `${params.ENVIRONMENT}`
|
|
||||||
• Services: `${env.SERVICES_LIST}`
|
|
||||||
• Build: `${env.BUILD_NUMBER}`
|
|
||||||
• Error: `${currentBuild.result}`
|
|
||||||
• Console: ${env.BUILD_URL}console
|
|
||||||
""".stripIndent()
|
|
||||||
|
|
||||||
// Send Slack notification
|
|
||||||
slackSend(
|
|
||||||
channel: env.SLACK_CHANNEL,
|
|
||||||
color: 'danger',
|
|
||||||
message: message,
|
|
||||||
token: env.SLACK_TOKEN
|
|
||||||
)
|
|
||||||
|
|
||||||
// Send email notification
|
|
||||||
emailext(
|
|
||||||
subject: "❌ phonebill Deployment Failed - ${params.ENVIRONMENT}",
|
|
||||||
body: message,
|
|
||||||
to: env.EMAIL_RECIPIENTS
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,77 +0,0 @@
|
|||||||
# Phonebill CI/CD 디렉토리 구조
|
|
||||||
|
|
||||||
## 개요
|
|
||||||
이 디렉토리는 Phonebill 마이크로서비스의 CI/CD 파이프라인을 위한 Kustomize 기반 구조를 포함합니다.
|
|
||||||
|
|
||||||
## 디렉토리 구조
|
|
||||||
|
|
||||||
```
|
|
||||||
deployment/cicd/
|
|
||||||
├── kustomize/
|
|
||||||
│ ├── base/ # Base 매니페스트 (환경 독립적)
|
|
||||||
│ │ ├── common/ # 공통 리소스 (Ingress, Secret 등)
|
|
||||||
│ │ ├── api-gateway/ # API Gateway 서비스
|
|
||||||
│ │ ├── user-service/ # 사용자 서비스
|
|
||||||
│ │ ├── bill-service/ # 요금 조회 서비스
|
|
||||||
│ │ ├── product-service/ # 상품 변경 서비스
|
|
||||||
│ │ ├── kos-mock/ # KOS Mock 서비스
|
|
||||||
│ │ └── kustomization.yaml # Base 통합 설정
|
|
||||||
│ └── overlays/ # 환경별 오버레이
|
|
||||||
│ ├── dev/ # 개발 환경
|
|
||||||
│ ├── staging/ # 스테이징 환경
|
|
||||||
│ └── prod/ # 프로덕션 환경
|
|
||||||
├── config/ # CI/CD 설정 파일
|
|
||||||
└── scripts/ # 배포 스크립트
|
|
||||||
```
|
|
||||||
|
|
||||||
## 주요 특징
|
|
||||||
|
|
||||||
### 1. 네임스페이스 분리
|
|
||||||
- **개발**: `phonebill-dev`
|
|
||||||
- **스테이징**: `phonebill-staging`
|
|
||||||
- **프로덕션**: `phonebill-prod`
|
|
||||||
|
|
||||||
### 2. 환경별 리소스 설정
|
|
||||||
- **개발**: 최소 리소스 (CPU: 250m, Memory: 512Mi)
|
|
||||||
- **스테이징**: 중간 리소스, 복제본 2개
|
|
||||||
- **프로덕션**: 최대 리소스, 복제본 3개
|
|
||||||
|
|
||||||
### 3. 서비스 구성
|
|
||||||
- **api-gateway**: API 게이트웨이
|
|
||||||
- **user-service**: 사용자 인증/관리
|
|
||||||
- **bill-service**: 요금 조회
|
|
||||||
- **product-service**: 상품 변경
|
|
||||||
- **kos-mock**: KOS 시스템 모킹
|
|
||||||
|
|
||||||
## 사용 방법
|
|
||||||
|
|
||||||
### 개발 환경 배포
|
|
||||||
```bash
|
|
||||||
kubectl apply -k deployment/cicd/kustomize/overlays/dev
|
|
||||||
```
|
|
||||||
|
|
||||||
### 스테이징 환경 배포
|
|
||||||
```bash
|
|
||||||
kubectl apply -k deployment/cicd/kustomize/overlays/staging
|
|
||||||
```
|
|
||||||
|
|
||||||
### 프로덕션 환경 배포
|
|
||||||
```bash
|
|
||||||
kubectl apply -k deployment/cicd/kustomize/overlays/prod
|
|
||||||
```
|
|
||||||
|
|
||||||
### 매니페스트 미리보기
|
|
||||||
```bash
|
|
||||||
kubectl kustomize deployment/cicd/kustomize/overlays/dev
|
|
||||||
```
|
|
||||||
|
|
||||||
## 주요 변경사항
|
|
||||||
1. 기존 `deployment/k8s/` 매니페스트를 `base/`로 복사
|
|
||||||
2. 하드코딩된 네임스페이스 제거 (`phonebill-dev`)
|
|
||||||
3. 환경별 오버레이 구조 적용
|
|
||||||
4. 리소스 제한 및 복제본 수 환경별 차별화
|
|
||||||
|
|
||||||
## Azure 연동 정보
|
|
||||||
- **ACR**: acrdigitalgarage01
|
|
||||||
- **리소스그룹**: rg-digitalgarage-01
|
|
||||||
- **AKS클러스터**: aks-digitalgarage-01
|
|
||||||
@ -1,34 +1,3 @@
|
|||||||
# Development Environment Configuration for phonebill
|
# DEV Environment Configuration
|
||||||
# Jenkins Pipeline Environment Variables
|
resource_group=rg-digitalgarage-01
|
||||||
|
cluster_name=aks-digitalgarage-01
|
||||||
# Azure Configuration
|
|
||||||
AZURE_RESOURCE_GROUP=rg-digitalgarage-01
|
|
||||||
ACR_NAME=acrdigitalgarage01
|
|
||||||
AKS_CLUSTER_NAME=aks-digitalgarage-01
|
|
||||||
AKS_NAMESPACE=phonebill-dev
|
|
||||||
|
|
||||||
# Service Names
|
|
||||||
SERVICES=api-gateway,user-service,bill-service,product-service,kos-mock
|
|
||||||
|
|
||||||
# Build Configuration
|
|
||||||
GRADLE_OPTS="-Xmx2048m -XX:MaxPermSize=512m"
|
|
||||||
JAVA_OPTS="-Xmx1024m -Xms512m"
|
|
||||||
|
|
||||||
# Docker Configuration
|
|
||||||
REGISTRY_URL=${ACR_NAME}.azurecr.io
|
|
||||||
IMAGE_TAG_PATTERN=${BUILD_NUMBER}-dev
|
|
||||||
|
|
||||||
# Deployment Configuration
|
|
||||||
KUSTOMIZE_BASE=deployment/k8s
|
|
||||||
KUSTOMIZE_OVERLAY=overlays/dev
|
|
||||||
HEALTH_CHECK_TIMEOUT=300
|
|
||||||
HEALTH_CHECK_RETRY=10
|
|
||||||
|
|
||||||
# SonarQube Configuration
|
|
||||||
SONAR_PROJECT_KEY=phonebill-dev
|
|
||||||
SONAR_SOURCES=.
|
|
||||||
SONAR_EXCLUSIONS=**/target/**,**/build/**,**/*.generated.java
|
|
||||||
|
|
||||||
# Notification Configuration
|
|
||||||
SLACK_CHANNEL=#phonebill-dev
|
|
||||||
EMAIL_RECIPIENTS=dev-team@company.com
|
|
||||||
@ -1,34 +1,3 @@
|
|||||||
# Production Environment Configuration for phonebill
|
# PROD Environment Configuration
|
||||||
# Jenkins Pipeline Environment Variables
|
resource_group=rg-digitalgarage-01
|
||||||
|
cluster_name=aks-digitalgarage-01
|
||||||
# Azure Configuration
|
|
||||||
AZURE_RESOURCE_GROUP=rg-digitalgarage-01
|
|
||||||
ACR_NAME=acrdigitalgarage01
|
|
||||||
AKS_CLUSTER_NAME=aks-digitalgarage-01
|
|
||||||
AKS_NAMESPACE=phonebill-prod
|
|
||||||
|
|
||||||
# Service Names
|
|
||||||
SERVICES=api-gateway,user-service,bill-service,product-service,kos-mock
|
|
||||||
|
|
||||||
# Build Configuration
|
|
||||||
GRADLE_OPTS="-Xmx3072m -XX:MaxPermSize=1024m"
|
|
||||||
JAVA_OPTS="-Xmx2048m -Xms1024m"
|
|
||||||
|
|
||||||
# Docker Configuration
|
|
||||||
REGISTRY_URL=${ACR_NAME}.azurecr.io
|
|
||||||
IMAGE_TAG_PATTERN=${BUILD_NUMBER}-prod
|
|
||||||
|
|
||||||
# Deployment Configuration
|
|
||||||
KUSTOMIZE_BASE=deployment/k8s
|
|
||||||
KUSTOMIZE_OVERLAY=overlays/prod
|
|
||||||
HEALTH_CHECK_TIMEOUT=600
|
|
||||||
HEALTH_CHECK_RETRY=15
|
|
||||||
|
|
||||||
# SonarQube Configuration
|
|
||||||
SONAR_PROJECT_KEY=phonebill-prod
|
|
||||||
SONAR_SOURCES=.
|
|
||||||
SONAR_EXCLUSIONS=**/target/**,**/build/**,**/*.generated.java
|
|
||||||
|
|
||||||
# Notification Configuration
|
|
||||||
SLACK_CHANNEL=#phonebill-prod
|
|
||||||
EMAIL_RECIPIENTS=prod-team@company.com,ops-team@company.com
|
|
||||||
@ -1,34 +1,3 @@
|
|||||||
# Staging Environment Configuration for phonebill
|
# STAGING Environment Configuration
|
||||||
# Jenkins Pipeline Environment Variables
|
resource_group=rg-digitalgarage-01
|
||||||
|
cluster_name=aks-digitalgarage-01
|
||||||
# Azure Configuration
|
|
||||||
AZURE_RESOURCE_GROUP=rg-digitalgarage-01
|
|
||||||
ACR_NAME=acrdigitalgarage01
|
|
||||||
AKS_CLUSTER_NAME=aks-digitalgarage-01
|
|
||||||
AKS_NAMESPACE=phonebill-staging
|
|
||||||
|
|
||||||
# Service Names
|
|
||||||
SERVICES=api-gateway,user-service,bill-service,product-service,kos-mock
|
|
||||||
|
|
||||||
# Build Configuration
|
|
||||||
GRADLE_OPTS="-Xmx2048m -XX:MaxPermSize=512m"
|
|
||||||
JAVA_OPTS="-Xmx1024m -Xms512m"
|
|
||||||
|
|
||||||
# Docker Configuration
|
|
||||||
REGISTRY_URL=${ACR_NAME}.azurecr.io
|
|
||||||
IMAGE_TAG_PATTERN=${BUILD_NUMBER}-staging
|
|
||||||
|
|
||||||
# Deployment Configuration
|
|
||||||
KUSTOMIZE_BASE=deployment/k8s
|
|
||||||
KUSTOMIZE_OVERLAY=overlays/staging
|
|
||||||
HEALTH_CHECK_TIMEOUT=300
|
|
||||||
HEALTH_CHECK_RETRY=10
|
|
||||||
|
|
||||||
# SonarQube Configuration
|
|
||||||
SONAR_PROJECT_KEY=phonebill-staging
|
|
||||||
SONAR_SOURCES=.
|
|
||||||
SONAR_EXCLUSIONS=**/target/**,**/build/**,**/*.generated.java
|
|
||||||
|
|
||||||
# Notification Configuration
|
|
||||||
SLACK_CHANNEL=#phonebill-staging
|
|
||||||
EMAIL_RECIPIENTS=staging-team@company.com
|
|
||||||
@ -1,423 +1,424 @@
|
|||||||
# Jenkins CI/CD Pipeline 구축 가이드
|
# Jenkins CI/CD 파이프라인 구축 가이드
|
||||||
|
|
||||||
## 📋 개요
|
## 📋 개요
|
||||||
|
|
||||||
phonebill 마이크로서비스를 위한 Jenkins CI/CD 파이프라인 구축 가이드입니다.
|
이 가이드는 통신요금 관리 서비스(phonebill)를 위한 Jenkins + Kustomize 기반 CI/CD 파이프라인 구축 방법을 제공합니다.
|
||||||
Azure Kubernetes Service(AKS)와 Azure Container Registry(ACR)를 활용한 자동화된 배포 시스템을 구성합니다.
|
|
||||||
|
|
||||||
## 🏗️ 아키텍처
|
### 🎯 주요 특징
|
||||||
|
- **환경별 배포**: dev, staging, prod 환경 분리 관리
|
||||||
|
- **Kustomize 기반**: 환경별 매니페스트 관리 및 배포
|
||||||
|
- **SonarQube 연동**: 코드 품질 분석 및 Quality Gate 적용
|
||||||
|
- **Azure 통합**: ACR, AKS와 완전 통합
|
||||||
|
- **Health Check**: 배포 후 서비스 상태 자동 확인
|
||||||
|
|
||||||
### 시스템 구성요소
|
---
|
||||||
- **Jenkins**: CI/CD 오케스트레이션
|
|
||||||
- **Azure Container Registry (ACR)**: 컨테이너 이미지 저장소
|
|
||||||
- **Azure Kubernetes Service (AKS)**: 컨테이너 오케스트레이션
|
|
||||||
- **SonarQube**: 코드 품질 분석
|
|
||||||
- **Gradle**: 빌드 도구
|
|
||||||
- **Kustomize**: Kubernetes 매니페스트 관리
|
|
||||||
|
|
||||||
### 배포 환경
|
## 🏗️ 시스템 아키텍처
|
||||||
- **Development** (`phonebill-dev`)
|
|
||||||
- **Staging** (`phonebill-staging`)
|
|
||||||
- **Production** (`phonebill-prod`)
|
|
||||||
|
|
||||||
## 🚀 파이프라인 워크플로우
|
### 서비스 구성
|
||||||
|
- **시스템명**: phonebill
|
||||||
|
- **서비스 목록**:
|
||||||
|
- api-gateway (API 게이트웨이)
|
||||||
|
- user-service (사용자 서비스)
|
||||||
|
- bill-service (요금 조회 서비스)
|
||||||
|
- product-service (상품 변경 서비스)
|
||||||
|
- kos-mock (KOS Mock 서비스)
|
||||||
|
|
||||||
```mermaid
|
### Azure 리소스 정보
|
||||||
graph LR
|
- **ACR**: acrdigitalgarage01.azurecr.io
|
||||||
A[Code Commit] --> B[Jenkins Trigger]
|
- **리소스 그룹**: rg-digitalgarage-01
|
||||||
B --> C[Build & Test]
|
- **AKS 클러스터**: aks-digitalgarage-01
|
||||||
C --> D[SonarQube Analysis]
|
|
||||||
D --> E[Quality Gate]
|
---
|
||||||
E --> F[Container Build]
|
|
||||||
F --> G[Push to ACR]
|
## 🛠️ Jenkins 서버 환경 구성
|
||||||
G --> H[Deploy to AKS]
|
|
||||||
H --> I[Health Check]
|
### 1. 필수 플러그인 설치
|
||||||
I --> J[Notification]
|
|
||||||
|
Jenkins 관리 > 플러그인 관리에서 다음 플러그인들을 설치하세요:
|
||||||
|
|
||||||
|
```
|
||||||
|
📦 필수 플러그인 목록:
|
||||||
|
- Kubernetes
|
||||||
|
- Pipeline Utility Steps
|
||||||
|
- Docker Pipeline
|
||||||
|
- GitHub
|
||||||
|
- SonarQube Scanner
|
||||||
|
- Azure Credentials
|
||||||
```
|
```
|
||||||
|
|
||||||
### 주요 단계
|
### 2. Jenkins Credentials 등록
|
||||||
|
|
||||||
1. **Initialize**: 환경별 설정 로드 및 파라미터 설정
|
Manage Jenkins > Credentials > Add Credentials에서 다음 정보들을 등록하세요:
|
||||||
2. **Checkout & Prepare**: 소스 코드 체크아웃 및 빌드 태그 생성
|
|
||||||
3. **Build & Test**: Gradle을 이용한 빌드 및 단위 테스트
|
|
||||||
4. **SonarQube Analysis**: 코드 품질 분석
|
|
||||||
5. **Quality Gate**: 품질 기준 검증
|
|
||||||
6. **Container Build & Push**: 컨테이너 이미지 빌드 및 ACR 푸시
|
|
||||||
7. **Deploy to Kubernetes**: AKS 클러스터에 배포
|
|
||||||
8. **Health Check**: 배포된 서비스 상태 확인
|
|
||||||
|
|
||||||
## 📂 파일 구조
|
#### Azure Service Principal
|
||||||
|
```yaml
|
||||||
|
Kind: Microsoft Azure Service Principal
|
||||||
|
ID: azure-credentials
|
||||||
|
Subscription ID: {구독ID}
|
||||||
|
Client ID: {클라이언트ID}
|
||||||
|
Client Secret: {클라이언트시크릿}
|
||||||
|
Tenant ID: {테넌트ID}
|
||||||
|
Azure Environment: Azure
|
||||||
|
```
|
||||||
|
|
||||||
|
#### ACR Credentials
|
||||||
|
```yaml
|
||||||
|
Kind: Username with password
|
||||||
|
ID: acr-credentials
|
||||||
|
Username: acrdigitalgarage01
|
||||||
|
Password: {ACR패스워드}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### SonarQube Token
|
||||||
|
```yaml
|
||||||
|
Kind: Secret text
|
||||||
|
ID: sonarqube-token
|
||||||
|
Secret: {SonarQube토큰}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📂 Kustomize 구조
|
||||||
|
|
||||||
|
### 디렉토리 구조
|
||||||
```
|
```
|
||||||
deployment/cicd/
|
deployment/cicd/
|
||||||
├── Jenkinsfile # Jenkins 파이프라인 정의
|
├── kustomize/
|
||||||
├── config/
|
│ ├── base/ # 기본 매니페스트
|
||||||
│ ├── deploy_env_vars_dev # 개발 환경 설정
|
│ │ ├── common/ # 공통 리소스
|
||||||
│ ├── deploy_env_vars_staging # 스테이징 환경 설정
|
│ │ ├── api-gateway/ # API Gateway 리소스
|
||||||
│ └── deploy_env_vars_prod # 운영 환경 설정
|
│ │ ├── user-service/ # User Service 리소스
|
||||||
├── scripts/
|
│ │ ├── bill-service/ # Bill Service 리소스
|
||||||
│ └── deploy.sh # 수동 배포 스크립트
|
│ │ ├── product-service/ # Product Service 리소스
|
||||||
└── jenkins-pipeline-guide.md # 이 가이드 문서
|
│ │ ├── kos-mock/ # KOS Mock 리소스
|
||||||
|
│ │ └── kustomization.yaml # Base Kustomization
|
||||||
|
│ └── overlays/ # 환경별 오버레이
|
||||||
|
│ ├── dev/ # 개발 환경
|
||||||
|
│ ├── staging/ # 스테이징 환경
|
||||||
|
│ └── prod/ # 운영 환경
|
||||||
|
├── config/ # 환경별 설정
|
||||||
|
├── scripts/ # 배포 스크립트
|
||||||
|
└── Jenkinsfile # Jenkins 파이프라인
|
||||||
```
|
```
|
||||||
|
|
||||||
## 🔧 Jenkins 구성
|
### 환경별 특성
|
||||||
|
|
||||||
### 1. Jenkins 플러그인 설치
|
#### 🔧 DEV 환경
|
||||||
|
- **네임스페이스**: phonebill-dev
|
||||||
|
- **도메인**: phonebill-api.20.214.196.128.nip.io (HTTP)
|
||||||
|
- **프로파일**: dev
|
||||||
|
- **DDL**: update (테이블 자동 생성/수정)
|
||||||
|
- **JWT 토큰**: 5시간 유효
|
||||||
|
- **Replica**: 모든 서비스 1개
|
||||||
|
- **리소스**: requests(256m CPU, 256Mi Memory), limits(1024m CPU, 1024Mi Memory)
|
||||||
|
|
||||||
필수 플러그인 목록:
|
#### 🔄 STAGING 환경
|
||||||
```bash
|
- **네임스페이스**: phonebill-staging
|
||||||
# Kubernetes 관련
|
- **도메인**: phonebill-staging.20.214.196.128.nip.io (HTTPS)
|
||||||
Kubernetes Plugin
|
- **프로파일**: staging
|
||||||
Pipeline: Kubernetes Steps
|
- **DDL**: validate (스키마 검증만)
|
||||||
|
- **JWT 토큰**: 5시간 유효
|
||||||
|
- **Replica**: 모든 서비스 2개
|
||||||
|
- **리소스**: requests(512m CPU, 512Mi Memory), limits(2048m CPU, 2048Mi Memory)
|
||||||
|
|
||||||
# Azure 관련
|
#### 🚀 PROD 환경
|
||||||
Azure CLI Plugin
|
- **네임스페이스**: phonebill-prod
|
||||||
Azure Container Registry Plugin
|
- **도메인**: phonebill.20.214.196.128.nip.io (HTTPS + SSL 강화)
|
||||||
|
- **프로파일**: prod
|
||||||
|
- **DDL**: validate (스키마 검증만)
|
||||||
|
- **JWT 토큰**: 1시간 유효 (보안 강화)
|
||||||
|
- **Replica**: 모든 서비스 3개
|
||||||
|
- **리소스**: requests(1024m CPU, 1024Mi Memory), limits(4096m CPU, 4096Mi Memory)
|
||||||
|
|
||||||
# 빌드 도구
|
|
||||||
Gradle Plugin
|
|
||||||
Pipeline: Gradle Plugin
|
|
||||||
|
|
||||||
# 코드 품질
|
|
||||||
SonarQube Scanner Plugin
|
|
||||||
Pipeline: SonarQube Plugin
|
|
||||||
|
|
||||||
# 알림
|
|
||||||
Slack Notification Plugin
|
|
||||||
Email Extension Plugin
|
|
||||||
|
|
||||||
# Git 관련
|
|
||||||
Git Plugin
|
|
||||||
GitHub Plugin
|
|
||||||
Pipeline: GitHub Plugin
|
|
||||||
|
|
||||||
# 기타
|
|
||||||
Pipeline Plugin
|
|
||||||
Pipeline: Stage View Plugin
|
|
||||||
Blue Ocean Plugin
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. 글로벌 설정
|
|
||||||
|
|
||||||
#### Azure Service Principal 설정
|
|
||||||
```bash
|
|
||||||
# Jenkins 관리 > 시스템 설정 > Global properties
|
|
||||||
# Environment variables 추가
|
|
||||||
AZURE_TENANT_ID=<your-tenant-id>
|
|
||||||
AZURE_SUBSCRIPTION_ID=<your-subscription-id>
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Credentials 설정
|
|
||||||
Jenkins 관리 > Manage Credentials에서 다음 설정:
|
|
||||||
|
|
||||||
1. **azure-service-principal** (Azure Service Principal)
|
|
||||||
- ID: `azure-service-principal`
|
|
||||||
- Type: Microsoft Azure Service Principal
|
|
||||||
- Tenant ID: Azure 테넌트 ID
|
|
||||||
- Client ID: 서비스 프린시pal 클라이언트 ID
|
|
||||||
- Client Secret: 서비스 프린시pal 시크릿
|
|
||||||
|
|
||||||
2. **acr-credentials** (ACR 인증 정보)
|
|
||||||
- ID: `acr-credentials`
|
|
||||||
- Type: Username with password
|
|
||||||
- Username: ACR 사용자명
|
|
||||||
- Password: ACR 패스워드
|
|
||||||
|
|
||||||
3. **sonarqube-token** (SonarQube 토큰)
|
|
||||||
- ID: `sonarqube-token`
|
|
||||||
- Type: Secret text
|
|
||||||
- Secret: SonarQube 액세스 토큰
|
|
||||||
|
|
||||||
4. **slack-token** (Slack 토큰)
|
|
||||||
- ID: `slack-token`
|
|
||||||
- Type: Secret text
|
|
||||||
- Secret: Slack Bot 토큰
|
|
||||||
|
|
||||||
### 3. Kubernetes Agent 설정
|
|
||||||
|
|
||||||
Jenkins가 Kubernetes 클러스터에서 빌드 에이전트를 실행할 수 있도록 설정:
|
|
||||||
|
|
||||||
#### ServiceAccount 및 RBAC 생성
|
|
||||||
```yaml
|
|
||||||
# jenkins-rbac.yaml
|
|
||||||
apiVersion: v1
|
|
||||||
kind: ServiceAccount
|
|
||||||
metadata:
|
|
||||||
name: jenkins-agent
|
|
||||||
namespace: jenkins
|
|
||||||
---
|
---
|
||||||
apiVersion: rbac.authorization.k8s.io/v1
|
|
||||||
kind: ClusterRole
|
|
||||||
metadata:
|
|
||||||
name: jenkins-agent
|
|
||||||
rules:
|
|
||||||
- apiGroups: [\"\"]
|
|
||||||
resources: [\"pods\", \"pods/exec\", \"pods/log\", \"persistentvolumeclaims\"]
|
|
||||||
verbs: [\"*\"]
|
|
||||||
- apiGroups: [\"apps\"]
|
|
||||||
resources: [\"deployments\", \"replicasets\"]
|
|
||||||
verbs: [\"*\"]
|
|
||||||
---
|
|
||||||
apiVersion: rbac.authorization.k8s.io/v1
|
|
||||||
kind: ClusterRoleBinding
|
|
||||||
metadata:
|
|
||||||
name: jenkins-agent
|
|
||||||
roleRef:
|
|
||||||
apiGroup: rbac.authorization.k8s.io
|
|
||||||
kind: ClusterRole
|
|
||||||
name: jenkins-agent
|
|
||||||
subjects:
|
|
||||||
- kind: ServiceAccount
|
|
||||||
name: jenkins-agent
|
|
||||||
namespace: jenkins
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Gradle Cache PVC 생성
|
## 🔄 CI/CD 파이프라인 단계
|
||||||
|
|
||||||
|
### Pipeline 단계 설명
|
||||||
|
|
||||||
|
1. **Get Source** 📥
|
||||||
|
- Git 소스 코드 체크아웃
|
||||||
|
- 환경별 설정 파일 로딩
|
||||||
|
|
||||||
|
2. **Setup AKS** ⚙️
|
||||||
|
- Azure Service Principal로 로그인
|
||||||
|
- AKS 클러스터 연결 설정
|
||||||
|
- 네임스페이스 생성
|
||||||
|
|
||||||
|
3. **Build & SonarQube Analysis** 🔍
|
||||||
|
- Gradle 빌드 실행
|
||||||
|
- 각 서비스별 단위 테스트
|
||||||
|
- SonarQube 코드 품질 분석
|
||||||
|
- 테스트 커버리지 리포트 생성
|
||||||
|
|
||||||
|
4. **Quality Gate** 🚪
|
||||||
|
- SonarQube Quality Gate 검증
|
||||||
|
- 품질 기준 미달 시 파이프라인 중단
|
||||||
|
|
||||||
|
5. **Build & Push Images** 🐳
|
||||||
|
- 각 서비스별 컨테이너 이미지 빌드
|
||||||
|
- ACR에 이미지 푸시
|
||||||
|
- 환경별 이미지 태그 적용
|
||||||
|
|
||||||
|
6. **Update Kustomize & Deploy** 🚀
|
||||||
|
- Kustomize를 통한 매니페스트 생성
|
||||||
|
- 이미지 태그 업데이트
|
||||||
|
- Kubernetes 클러스터에 배포
|
||||||
|
- 배포 완료 대기
|
||||||
|
|
||||||
|
7. **Health Check** 🔍
|
||||||
|
- API Gateway Health Check
|
||||||
|
- 서비스 정상 동작 확인
|
||||||
|
|
||||||
|
### SonarQube Quality Gate 기준
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
# gradle-cache-pvc.yaml
|
품질 기준:
|
||||||
apiVersion: v1
|
- Coverage: >= 80%
|
||||||
kind: PersistentVolumeClaim
|
- Duplicated Lines: <= 3%
|
||||||
metadata:
|
- Maintainability Rating: <= A
|
||||||
name: jenkins-gradle-cache
|
- Reliability Rating: <= A
|
||||||
namespace: jenkins
|
- Security Rating: <= A
|
||||||
spec:
|
|
||||||
accessModes:
|
|
||||||
- ReadWriteOnce
|
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
storage: 10Gi
|
|
||||||
storageClassName: managed-premium
|
|
||||||
```
|
```
|
||||||
|
|
||||||
적용:
|
---
|
||||||
```bash
|
|
||||||
kubectl apply -f jenkins-rbac.yaml
|
|
||||||
kubectl apply -f gradle-cache-pvc.yaml
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4. SonarQube 설정
|
## 🚀 Jenkins Pipeline Job 생성
|
||||||
|
|
||||||
#### SonarQube Server 구성
|
|
||||||
Jenkins 관리 > Configure System > SonarQube servers:
|
|
||||||
- Name: `SonarQube`
|
|
||||||
- Server URL: `http://sonarqube.example.com`
|
|
||||||
- Server authentication token: `sonarqube-token` credential 선택
|
|
||||||
|
|
||||||
## 🔨 파이프라인 생성
|
|
||||||
|
|
||||||
### 1. 새 Pipeline Job 생성
|
### 1. 새 Pipeline Job 생성
|
||||||
1. Jenkins 대시보드에서 \"New Item\" 클릭
|
|
||||||
2. Job 이름: `phonebill-pipeline`
|
1. Jenkins 웹 UI에서 **New Item** 클릭
|
||||||
3. Type: \"Pipeline\" 선택
|
2. **Pipeline** 선택 후 프로젝트명 입력
|
||||||
4. OK 클릭
|
3. **OK** 클릭
|
||||||
|
|
||||||
### 2. Pipeline 설정
|
### 2. Pipeline 설정
|
||||||
1. **General** 탭:
|
|
||||||
- Description: \"phonebill microservices CI/CD pipeline\"
|
|
||||||
- \"GitHub project\" 체크하고 프로젝트 URL 입력
|
|
||||||
|
|
||||||
2. **Build Triggers** 탭:
|
#### Source Code Management
|
||||||
- \"GitHub hook trigger for GITScm polling\" 체크 (GitHub Webhook 사용 시)
|
```yaml
|
||||||
- \"Poll SCM\" 설정: `H/5 * * * *` (5분마다 폴링)
|
SCM: Git
|
||||||
|
Repository URL: {Git저장소URL}
|
||||||
|
Branch: main (또는 develop)
|
||||||
|
Script Path: deployment/cicd/Jenkinsfile
|
||||||
|
```
|
||||||
|
|
||||||
3. **Pipeline** 탭:
|
#### Pipeline Parameters
|
||||||
- Definition: \"Pipeline script from SCM\"
|
```yaml
|
||||||
- SCM: Git
|
ENVIRONMENT:
|
||||||
- Repository URL: GitHub 저장소 URL
|
- Type: Choice Parameter
|
||||||
- Credentials: GitHub 인증 정보
|
- Choices: dev, staging, prod
|
||||||
- Branch: `*/main`
|
- Default: dev
|
||||||
- Script Path: `deployment/cicd/Jenkinsfile`
|
|
||||||
|
|
||||||
### 3. 환경별 파이프라인 생성
|
IMAGE_TAG:
|
||||||
동일한 방식으로 환경별 파이프라인 생성:
|
- Type: String Parameter
|
||||||
- `phonebill-dev-pipeline`
|
- Default: latest
|
||||||
- `phonebill-staging-pipeline`
|
```
|
||||||
- `phonebill-prod-pipeline`
|
|
||||||
|
|
||||||
## 🧪 파이프라인 실행
|
---
|
||||||
|
|
||||||
### 1. 수동 실행
|
## 📦 배포 실행 방법
|
||||||
1. Jenkins에서 파이프라인 Job 선택
|
|
||||||
2. \"Build with Parameters\" 클릭
|
|
||||||
3. 파라미터 설정:
|
|
||||||
- **ENVIRONMENT**: `dev` / `staging` / `prod`
|
|
||||||
- **SERVICES_TO_BUILD**: `all` 또는 특정 서비스
|
|
||||||
- **SKIP_TESTS**: 테스트 스킵 여부
|
|
||||||
- **SKIP_SONAR**: SonarQube 분석 스킵 여부
|
|
||||||
- **FORCE_DEPLOY**: 강제 배포 여부
|
|
||||||
4. \"Build\" 클릭
|
|
||||||
|
|
||||||
### 2. 자동 실행 (Webhook)
|
### 1. Jenkins UI를 통한 배포
|
||||||
GitHub에서 코드 푸시 시 자동으로 파이프라인이 트리거됩니다.
|
|
||||||
|
|
||||||
#### GitHub Webhook 설정
|
1. Jenkins > {프로젝트명} > **Build with Parameters** 클릭
|
||||||
1. GitHub 저장소 > Settings > Webhooks
|
2. **ENVIRONMENT** 선택 (dev/staging/prod)
|
||||||
2. Add webhook:
|
3. **IMAGE_TAG** 입력 (선택사항, 기본값: latest)
|
||||||
- Payload URL: `http://jenkins.example.com/github-webhook/`
|
4. **Build** 클릭
|
||||||
- Content type: `application/json`
|
|
||||||
- Secret: 설정한 시크릿
|
|
||||||
- Events: \"Just the push event\"
|
|
||||||
|
|
||||||
## 📊 모니터링 및 알림
|
### 2. 수동 배포 스크립트 사용
|
||||||
|
|
||||||
### 1. 빌드 상태 모니터링
|
```bash
|
||||||
- Jenkins Blue Ocean 인터페이스 활용
|
# 개발 환경 배포
|
||||||
- 파이프라인 실행 상태 실시간 확인
|
./deployment/cicd/scripts/deploy.sh dev latest
|
||||||
- 로그 및 아티팩트 확인
|
|
||||||
|
|
||||||
### 2. 알림 설정
|
# 스테이징 환경 배포
|
||||||
파이프라인 실행 결과를 다음 채널로 알림:
|
./deployment/cicd/scripts/deploy.sh staging v1.2.0
|
||||||
- **Slack**: 지정된 채널에 빌드 상태 알림
|
|
||||||
- **Email**: 담당자에게 결과 메일 발송
|
|
||||||
|
|
||||||
### 3. 메트릭 수집
|
# 운영 환경 배포
|
||||||
- 빌드 시간 추적
|
./deployment/cicd/scripts/deploy.sh prod v1.2.0
|
||||||
- 성공/실패율 모니터링
|
```
|
||||||
- 배포 빈도 측정
|
|
||||||
|
|
||||||
## 🔍 트러블슈팅
|
### 3. 배포 상태 확인
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Pod 상태 확인
|
||||||
|
kubectl get pods -n phonebill-{환경}
|
||||||
|
|
||||||
|
# 서비스 상태 확인
|
||||||
|
kubectl get services -n phonebill-{환경}
|
||||||
|
|
||||||
|
# Ingress 상태 확인
|
||||||
|
kubectl get ingress -n phonebill-{환경}
|
||||||
|
|
||||||
|
# 로그 확인
|
||||||
|
kubectl logs -n phonebill-{환경} deployment/{환경}-api-gateway
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔄 롤백 방법
|
||||||
|
|
||||||
|
### 1. Kubernetes 기본 롤백
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 이전 버전으로 롤백
|
||||||
|
kubectl rollout undo deployment/{환경}-{서비스명} -n phonebill-{환경}
|
||||||
|
|
||||||
|
# 특정 리비전으로 롤백
|
||||||
|
kubectl rollout undo deployment/{환경}-{서비스명} -n phonebill-{환경} --to-revision=2
|
||||||
|
|
||||||
|
# 롤백 상태 확인
|
||||||
|
kubectl rollout status deployment/{환경}-{서비스명} -n phonebill-{환경}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 이미지 태그 기반 롤백
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 안정 버전 태그로 수동 배포
|
||||||
|
./deployment/cicd/scripts/deploy.sh prod {이전안정버전태그}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔧 트러블슈팅
|
||||||
|
|
||||||
### 일반적인 문제 해결
|
### 일반적인 문제 해결
|
||||||
|
|
||||||
#### 1. Azure 인증 실패
|
#### 1. 파이프라인 실패 시
|
||||||
```bash
|
```bash
|
||||||
# 서비스 프린시pal 권한 확인
|
# Jenkins 콘솔 로그 확인
|
||||||
az role assignment list --assignee <service-principal-client-id>
|
# SonarQube Quality Gate 상태 확인
|
||||||
|
# Kubernetes 이벤트 확인
|
||||||
# 필요한 권한 할당
|
kubectl get events -n phonebill-{환경} --sort-by='.lastTimestamp'
|
||||||
az role assignment create \
|
|
||||||
--assignee <service-principal-client-id> \
|
|
||||||
--role \"AKS Cluster Admin\" \
|
|
||||||
--scope /subscriptions/<subscription-id>/resourceGroups/<rg-name>
|
|
||||||
```
|
```
|
||||||
|
|
||||||
#### 2. ACR 푸시 실패
|
#### 2. 배포 실패 시
|
||||||
```bash
|
```bash
|
||||||
# ACR 로그인 확인
|
# Pod 상태 및 로그 확인
|
||||||
az acr login --name <acr-name>
|
kubectl describe pod {pod-name} -n phonebill-{환경}
|
||||||
|
kubectl logs {pod-name} -n phonebill-{환경}
|
||||||
|
|
||||||
# ACR 권한 확인
|
# ConfigMap/Secret 확인
|
||||||
az acr show --name <acr-name> --resource-group <rg-name>
|
kubectl get configmap -n phonebill-{환경}
|
||||||
|
kubectl get secret -n phonebill-{환경}
|
||||||
```
|
```
|
||||||
|
|
||||||
#### 3. Kubernetes 배포 실패
|
#### 3. 네트워크 연결 문제
|
||||||
```bash
|
```bash
|
||||||
# kubectl 컨텍스트 확인
|
# Service와 Endpoint 확인
|
||||||
kubectl config current-context
|
kubectl get svc,endpoints -n phonebill-{환경}
|
||||||
|
|
||||||
# 네임스페이스 확인
|
# Ingress 설정 확인
|
||||||
kubectl get namespaces
|
kubectl describe ingress -n phonebill-{환경}
|
||||||
|
|
||||||
# 리소스 상태 확인
|
|
||||||
kubectl get all -n <namespace>
|
|
||||||
```
|
```
|
||||||
|
|
||||||
#### 4. 파드 시작 실패
|
|
||||||
```bash
|
|
||||||
# 파드 로그 확인
|
|
||||||
kubectl logs <pod-name> -n <namespace>
|
|
||||||
|
|
||||||
# 파드 상세 정보 확인
|
|
||||||
kubectl describe pod <pod-name> -n <namespace>
|
|
||||||
|
|
||||||
# 이벤트 확인
|
|
||||||
kubectl get events -n <namespace> --sort-by='.lastTimestamp'
|
|
||||||
```
|
|
||||||
|
|
||||||
### 로그 위치
|
|
||||||
- **Jenkins 로그**: `/var/log/jenkins/jenkins.log`
|
|
||||||
- **파이프라인 로그**: Jenkins UI에서 Build History > Console Output
|
|
||||||
- **Kubernetes 로그**: `kubectl logs` 명령어 사용
|
|
||||||
|
|
||||||
## 🚀 수동 배포 스크립트 사용법
|
|
||||||
|
|
||||||
Jenkins 파이프라인 외에도 수동 배포 스크립트를 제공합니다.
|
|
||||||
|
|
||||||
### 기본 사용법
|
|
||||||
```bash
|
|
||||||
# 모든 서비스를 dev 환경에 배포
|
|
||||||
./deployment/cicd/scripts/deploy.sh dev
|
|
||||||
|
|
||||||
# 특정 서비스만 staging 환경에 배포
|
|
||||||
./deployment/cicd/scripts/deploy.sh staging user-service
|
|
||||||
|
|
||||||
# 여러 서비스를 prod 환경에 배포
|
|
||||||
./deployment/cicd/scripts/deploy.sh prod api-gateway,user-service,bill-service
|
|
||||||
|
|
||||||
# 옵션 사용 예시
|
|
||||||
./deployment/cicd/scripts/deploy.sh dev all --skip-build --skip-test
|
|
||||||
```
|
|
||||||
|
|
||||||
### 주요 옵션
|
|
||||||
- `--skip-build`: Gradle 빌드 스킵
|
|
||||||
- `--skip-test`: 단위 테스트 스킵
|
|
||||||
- `--skip-push`: 컨테이너 이미지 푸시 스킵
|
|
||||||
- `--force`: 변경사항이 없어도 강제 배포
|
|
||||||
- `--dry-run`: 실제 배포 없이 미리보기
|
|
||||||
|
|
||||||
## 📈 성능 최적화
|
|
||||||
|
|
||||||
### 1. 빌드 성능 개선
|
|
||||||
- **Gradle Daemon** 활용: `--daemon` 옵션
|
|
||||||
- **병렬 빌드**: `--parallel` 옵션
|
|
||||||
- **Build Cache** 활용: `--build-cache` 옵션
|
|
||||||
- **Incremental Build** 활용
|
|
||||||
|
|
||||||
### 2. 컨테이너 이미지 최적화
|
|
||||||
- **Multi-stage Build** 사용
|
|
||||||
- **Layer Caching** 최적화
|
|
||||||
- **Base Image** 최적화
|
|
||||||
- **.dockerignore** 활용
|
|
||||||
|
|
||||||
### 3. Kubernetes 배포 최적화
|
|
||||||
- **Rolling Update** 전략 사용
|
|
||||||
- **Resource Limits** 설정
|
|
||||||
- **Readiness/Liveness Probe** 설정
|
|
||||||
- **Pod Disruption Budget** 설정
|
|
||||||
|
|
||||||
## 🔒 보안 고려사항
|
|
||||||
|
|
||||||
### 1. 인증 및 권한 관리
|
|
||||||
- Azure Service Principal 최소 권한 원칙
|
|
||||||
- Jenkins Credentials 암호화 저장
|
|
||||||
- Kubernetes RBAC 적절한 권한 할당
|
|
||||||
- 시크릿 정보 환경 변수로 분리
|
|
||||||
|
|
||||||
### 2. 컨테이너 보안
|
|
||||||
- 취약점 스캐닝 도구 통합
|
|
||||||
- 비특권 사용자로 컨테이너 실행
|
|
||||||
- 읽기 전용 루트 파일시스템
|
|
||||||
- 보안 컨텍스트 설정
|
|
||||||
|
|
||||||
### 3. 네트워크 보안
|
|
||||||
- Private Registry 사용
|
|
||||||
- Network Policy 적용
|
|
||||||
- Service Mesh 보안 정책
|
|
||||||
- TLS/SSL 암호화
|
|
||||||
|
|
||||||
## 📚 참고 자료
|
|
||||||
|
|
||||||
### 공식 문서
|
|
||||||
- [Jenkins Pipeline](https://jenkins.io/doc/book/pipeline/)
|
|
||||||
- [Azure Kubernetes Service](https://docs.microsoft.com/en-us/azure/aks/)
|
|
||||||
- [Azure Container Registry](https://docs.microsoft.com/en-us/azure/container-registry/)
|
|
||||||
- [Kubernetes](https://kubernetes.io/docs/)
|
|
||||||
- [Gradle](https://docs.gradle.org/)
|
|
||||||
|
|
||||||
### 모범 사례
|
|
||||||
- [Jenkins Best Practices](https://wiki.jenkins.io/display/JENKINS/Jenkins+Best+Practices)
|
|
||||||
- [Kubernetes Best Practices](https://kubernetes.io/docs/concepts/configuration/overview/)
|
|
||||||
- [Docker Best Practices](https://docs.docker.com/develop/dev-best-practices/)
|
|
||||||
|
|
||||||
## 📞 지원
|
|
||||||
|
|
||||||
문제가 발생하거나 추가 지원이 필요한 경우:
|
|
||||||
|
|
||||||
1. **로그 확인**: Jenkins 콘솔 출력 및 Kubernetes 로그 검토
|
|
||||||
2. **문서 검토**: 이 가이드 및 공식 문서 참조
|
|
||||||
3. **커뮤니티**: Stack Overflow, Jenkins 커뮤니티 포럼 활용
|
|
||||||
4. **팀 지원**: DevOps 팀 또는 플랫폼 팀에 문의
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
*이 가이드는 phonebill 프로젝트의 Jenkins CI/CD 파이프라인 구축을 위한 완전한 가이드입니다. 프로젝트 요구사항에 따라 설정을 조정하여 사용하시기 바랍니다.*
|
## 📝 체크리스트
|
||||||
|
|
||||||
|
### 📋 Kustomize 구성 체크리스트
|
||||||
|
|
||||||
|
#### 📂 기본 구조 검증
|
||||||
|
- [ ] 디렉토리 구조: `deployment/cicd/kustomize/{base,overlays/{dev,staging,prod}}`
|
||||||
|
- [ ] 서비스별 base 디렉토리: `base/{common,api-gateway,user-service,bill-service,product-service,kos-mock}`
|
||||||
|
- [ ] Base kustomization.yaml 파일 생성 완료
|
||||||
|
|
||||||
|
#### 🔧 환경별 Overlay 검증
|
||||||
|
**각 환경(dev/staging/prod)별로 다음 파일들이 모두 생성되어야 함:**
|
||||||
|
|
||||||
|
**필수 파일 목록:**
|
||||||
|
- [ ] `kustomization.yaml` - 환경 설정 및 patch 파일 참조
|
||||||
|
- [ ] `configmap-common-patch.yaml` - 환경별 공통 설정 (프로파일, DDL, JWT 설정)
|
||||||
|
- [ ] `secret-common-patch.yaml` - 환경별 공통 시크릿 (JWT Secret, Redis 정보)
|
||||||
|
- [ ] `ingress-patch.yaml` - 환경별 도메인 및 보안 설정
|
||||||
|
- [ ] **`deployment-patch.yaml`** - **환경별 replicas AND resources 설정** ⚠️
|
||||||
|
- [ ] `secret-user-service-patch.yaml` - User Service DB 정보
|
||||||
|
- [ ] `secret-bill-service-patch.yaml` - Bill Service DB 정보
|
||||||
|
- [ ] `secret-product-service-patch.yaml` - Product Service DB 정보
|
||||||
|
|
||||||
|
**⚠️ deployment-patch.yaml 필수 검증 사항:**
|
||||||
|
- [ ] **파일명이 정확한지**: `deployment-patch.yaml` (❌ `replica-patch.yaml` 아님)
|
||||||
|
- [ ] **Strategic Merge Patch 형식 사용**: YAML 형식, JSON Patch 아님
|
||||||
|
- [ ] **replicas 설정**: dev(1), staging(2), prod(3)
|
||||||
|
- [ ] **resources 설정**: 환경별 차등 적용
|
||||||
|
- dev: requests(256m CPU, 256Mi Memory), limits(1024m CPU, 1024Mi Memory)
|
||||||
|
- staging: requests(512m CPU, 512Mi Memory), limits(2048m CPU, 2048Mi Memory)
|
||||||
|
- prod: requests(1024m CPU, 1024Mi Memory), limits(4096m CPU, 4096Mi Memory)
|
||||||
|
- [ ] **모든 서비스 포함**: api-gateway, user-service, bill-service, product-service, kos-mock
|
||||||
|
|
||||||
|
#### 🔍 호환성 검증
|
||||||
|
- [ ] base 매니페스트에 없는 항목을 patch에 추가하지 않음
|
||||||
|
- [ ] base 매니페스트와 patch 필드 구조 일치
|
||||||
|
- [ ] Secret 매니페스트에 'data' 대신 'stringData' 사용
|
||||||
|
|
||||||
|
### 📋 배포 전 체크리스트
|
||||||
|
|
||||||
|
- [ ] Jenkins 필수 플러그인 설치 완료
|
||||||
|
- [ ] Credentials 등록 완료 (Azure, ACR, SonarQube)
|
||||||
|
- [ ] SonarQube 프로젝트 설정 완료
|
||||||
|
- [ ] 환경별 Database/Redis 준비 완료
|
||||||
|
- [ ] 네트워크 및 도메인 설정 완료
|
||||||
|
|
||||||
|
### 🚀 배포 후 체크리스트
|
||||||
|
|
||||||
|
- [ ] 모든 Pod가 Running 상태
|
||||||
|
- [ ] Health Check 통과
|
||||||
|
- [ ] Ingress로 외부 접근 가능
|
||||||
|
- [ ] 로그에 오류 없음
|
||||||
|
- [ ] 기능 테스트 완료
|
||||||
|
|
||||||
|
### 💡 일반적인 실수 방지 가이드
|
||||||
|
|
||||||
|
**❌ 자주 발생하는 실수들:**
|
||||||
|
1. **파일명 실수**: `replica-patch.yaml` 생성 → 정답: `deployment-patch.yaml`
|
||||||
|
2. **내용 누락**: replicas만 설정하고 resources 누락 → 정답: 둘 다 설정
|
||||||
|
3. **형식 실수**: JSON Patch 사용 → 정답: Strategic Merge Patch 사용
|
||||||
|
4. **환경별 차이 없음**: 모든 환경 동일 설정 → 정답: 환경별 차등 설정
|
||||||
|
|
||||||
|
**✅ 올바른 deployment-patch.yaml 예시:**
|
||||||
|
```yaml
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: api-gateway
|
||||||
|
spec:
|
||||||
|
replicas: 1 # 환경별 차등 적용
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: api-gateway
|
||||||
|
resources: # 반드시 포함
|
||||||
|
requests:
|
||||||
|
cpu: 256m # 환경별 차등 적용
|
||||||
|
memory: 256Mi # 환경별 차등 적용
|
||||||
|
limits:
|
||||||
|
cpu: 1024m # 환경별 차등 적용
|
||||||
|
memory: 1024Mi # 환경별 차등 적용
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📞 지원 정보
|
||||||
|
|
||||||
|
### 환경 정보
|
||||||
|
- **시스템**: phonebill (통신요금 관리 서비스)
|
||||||
|
- **Git 저장소**: [Repository URL]
|
||||||
|
- **Jenkins**: [Jenkins URL]
|
||||||
|
- **SonarQube**: [SonarQube URL]
|
||||||
|
|
||||||
|
### 연락처
|
||||||
|
- **DevOps 팀**: 최운영 (데옵스)
|
||||||
|
- **백엔드 팀**: 이개발 (백엔더)
|
||||||
|
- **QA 팀**: 정테스트 (QA매니저)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📚 추가 리소스
|
||||||
|
|
||||||
|
- [Kustomize 공식 문서](https://kustomize.io/)
|
||||||
|
- [Jenkins Pipeline 문법](https://www.jenkins.io/doc/book/pipeline/syntax/)
|
||||||
|
- [Azure DevOps 가이드](https://docs.microsoft.com/en-us/azure/devops/)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*이 가이드는 phonebill 프로젝트의 CI/CD 파이프라인 구축을 위한 완전한 가이드입니다. 추가 질문이나 지원이 필요하시면 DevOps 팀에 문의하세요.*
|
||||||
@ -2,6 +2,7 @@ apiVersion: v1
|
|||||||
kind: ConfigMap
|
kind: ConfigMap
|
||||||
metadata:
|
metadata:
|
||||||
name: cm-api-gateway
|
name: cm-api-gateway
|
||||||
|
namespace: phonebill-dev
|
||||||
data:
|
data:
|
||||||
SERVER_PORT: "8080"
|
SERVER_PORT: "8080"
|
||||||
BILL_SERVICE_URL: "http://bill-service"
|
BILL_SERVICE_URL: "http://bill-service"
|
||||||
|
|||||||
@ -2,6 +2,7 @@ apiVersion: apps/v1
|
|||||||
kind: Deployment
|
kind: Deployment
|
||||||
metadata:
|
metadata:
|
||||||
name: api-gateway
|
name: api-gateway
|
||||||
|
namespace: phonebill-dev
|
||||||
spec:
|
spec:
|
||||||
replicas: 1
|
replicas: 1
|
||||||
selector:
|
selector:
|
||||||
|
|||||||
@ -1,7 +0,0 @@
|
|||||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
|
||||||
kind: Kustomization
|
|
||||||
|
|
||||||
resources:
|
|
||||||
- cm-api-gateway.yaml
|
|
||||||
- deployment.yaml
|
|
||||||
- service.yaml
|
|
||||||
@ -2,6 +2,7 @@ apiVersion: v1
|
|||||||
kind: Service
|
kind: Service
|
||||||
metadata:
|
metadata:
|
||||||
name: api-gateway
|
name: api-gateway
|
||||||
|
namespace: phonebill-dev
|
||||||
spec:
|
spec:
|
||||||
selector:
|
selector:
|
||||||
app: api-gateway
|
app: api-gateway
|
||||||
|
|||||||
@ -2,6 +2,7 @@ apiVersion: v1
|
|||||||
kind: ConfigMap
|
kind: ConfigMap
|
||||||
metadata:
|
metadata:
|
||||||
name: cm-bill-service
|
name: cm-bill-service
|
||||||
|
namespace: phonebill-dev
|
||||||
data:
|
data:
|
||||||
SERVER_PORT: "8082"
|
SERVER_PORT: "8082"
|
||||||
DB_KIND: "postgresql"
|
DB_KIND: "postgresql"
|
||||||
|
|||||||
@ -2,6 +2,7 @@ apiVersion: apps/v1
|
|||||||
kind: Deployment
|
kind: Deployment
|
||||||
metadata:
|
metadata:
|
||||||
name: bill-service
|
name: bill-service
|
||||||
|
namespace: phonebill-dev
|
||||||
spec:
|
spec:
|
||||||
replicas: 1
|
replicas: 1
|
||||||
selector:
|
selector:
|
||||||
|
|||||||
@ -1,8 +0,0 @@
|
|||||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
|
||||||
kind: Kustomization
|
|
||||||
|
|
||||||
resources:
|
|
||||||
- cm-bill-service.yaml
|
|
||||||
- deployment.yaml
|
|
||||||
- secret-bill-service.yaml
|
|
||||||
- service.yaml
|
|
||||||
@ -2,6 +2,7 @@ apiVersion: v1
|
|||||||
kind: Secret
|
kind: Secret
|
||||||
metadata:
|
metadata:
|
||||||
name: secret-bill-service
|
name: secret-bill-service
|
||||||
|
namespace: phonebill-dev
|
||||||
type: Opaque
|
type: Opaque
|
||||||
stringData:
|
stringData:
|
||||||
DB_HOST: "bill-inquiry-postgres-dev-postgresql"
|
DB_HOST: "bill-inquiry-postgres-dev-postgresql"
|
||||||
|
|||||||
@ -2,6 +2,7 @@ apiVersion: v1
|
|||||||
kind: Service
|
kind: Service
|
||||||
metadata:
|
metadata:
|
||||||
name: bill-service
|
name: bill-service
|
||||||
|
namespace: phonebill-dev
|
||||||
spec:
|
spec:
|
||||||
selector:
|
selector:
|
||||||
app: bill-service
|
app: bill-service
|
||||||
|
|||||||
@ -2,6 +2,7 @@ apiVersion: v1
|
|||||||
kind: ConfigMap
|
kind: ConfigMap
|
||||||
metadata:
|
metadata:
|
||||||
name: cm-common
|
name: cm-common
|
||||||
|
namespace: phonebill-dev
|
||||||
data:
|
data:
|
||||||
CORS_ALLOWED_ORIGINS: "http://localhost:8081,http://localhost:8082,http://localhost:8083,http://localhost:8084,http://phonebill.20.214.196.128.nip.io"
|
CORS_ALLOWED_ORIGINS: "http://localhost:8081,http://localhost:8082,http://localhost:8083,http://localhost:8084,http://phonebill.20.214.196.128.nip.io"
|
||||||
JWT_ACCESS_TOKEN_VALIDITY: "18000000"
|
JWT_ACCESS_TOKEN_VALIDITY: "18000000"
|
||||||
|
|||||||
@ -2,6 +2,7 @@ apiVersion: networking.k8s.io/v1
|
|||||||
kind: Ingress
|
kind: Ingress
|
||||||
metadata:
|
metadata:
|
||||||
name: phonebill
|
name: phonebill
|
||||||
|
namespace: phonebill-dev
|
||||||
annotations:
|
annotations:
|
||||||
kubernetes.io/ingress.class: nginx
|
kubernetes.io/ingress.class: nginx
|
||||||
nginx.ingress.kubernetes.io/ssl-redirect: "false"
|
nginx.ingress.kubernetes.io/ssl-redirect: "false"
|
||||||
|
|||||||
@ -1,8 +0,0 @@
|
|||||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
|
||||||
kind: Kustomization
|
|
||||||
|
|
||||||
resources:
|
|
||||||
- cm-common.yaml
|
|
||||||
- ingress.yaml
|
|
||||||
- secret-common.yaml
|
|
||||||
- secret-imagepull.yaml
|
|
||||||
@ -2,6 +2,7 @@ apiVersion: v1
|
|||||||
kind: Secret
|
kind: Secret
|
||||||
metadata:
|
metadata:
|
||||||
name: secret-common
|
name: secret-common
|
||||||
|
namespace: phonebill-dev
|
||||||
type: Opaque
|
type: Opaque
|
||||||
stringData:
|
stringData:
|
||||||
JWT_SECRET: "nwe5Yo9qaJ6FBD/Thl2/j6/SFAfNwUorAY1ZcWO2KI7uA4bmVLOCPxE9hYuUpRCOkgV2UF2DdHXtqHi3+BU/ecbz2zpHyf/720h48UbA3XOMYOX1sdM+dQ=="
|
JWT_SECRET: "nwe5Yo9qaJ6FBD/Thl2/j6/SFAfNwUorAY1ZcWO2KI7uA4bmVLOCPxE9hYuUpRCOkgV2UF2DdHXtqHi3+BU/ecbz2zpHyf/720h48UbA3XOMYOX1sdM+dQ=="
|
||||||
|
|||||||
@ -2,6 +2,7 @@ apiVersion: v1
|
|||||||
kind: Secret
|
kind: Secret
|
||||||
metadata:
|
metadata:
|
||||||
name: phonebill
|
name: phonebill
|
||||||
|
namespace: phonebill-dev
|
||||||
type: kubernetes.io/dockerconfigjson
|
type: kubernetes.io/dockerconfigjson
|
||||||
stringData:
|
stringData:
|
||||||
.dockerconfigjson: |
|
.dockerconfigjson: |
|
||||||
|
|||||||
@ -2,5 +2,6 @@ apiVersion: v1
|
|||||||
kind: ConfigMap
|
kind: ConfigMap
|
||||||
metadata:
|
metadata:
|
||||||
name: cm-kos-mock
|
name: cm-kos-mock
|
||||||
|
namespace: phonebill-dev
|
||||||
data:
|
data:
|
||||||
SERVER_PORT: "8084"
|
SERVER_PORT: "8084"
|
||||||
@ -2,6 +2,7 @@ apiVersion: apps/v1
|
|||||||
kind: Deployment
|
kind: Deployment
|
||||||
metadata:
|
metadata:
|
||||||
name: kos-mock
|
name: kos-mock
|
||||||
|
namespace: phonebill-dev
|
||||||
spec:
|
spec:
|
||||||
replicas: 1
|
replicas: 1
|
||||||
selector:
|
selector:
|
||||||
|
|||||||
@ -1,7 +0,0 @@
|
|||||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
|
||||||
kind: Kustomization
|
|
||||||
|
|
||||||
resources:
|
|
||||||
- cm-kos-mock.yaml
|
|
||||||
- deployment.yaml
|
|
||||||
- service.yaml
|
|
||||||
@ -2,6 +2,7 @@ apiVersion: v1
|
|||||||
kind: Service
|
kind: Service
|
||||||
metadata:
|
metadata:
|
||||||
name: kos-mock
|
name: kos-mock
|
||||||
|
namespace: phonebill-dev
|
||||||
spec:
|
spec:
|
||||||
selector:
|
selector:
|
||||||
app: kos-mock
|
app: kos-mock
|
||||||
|
|||||||
@ -1,10 +1,56 @@
|
|||||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
kind: Kustomization
|
kind: Kustomization
|
||||||
|
|
||||||
|
metadata:
|
||||||
|
name: phonebill-base
|
||||||
|
|
||||||
resources:
|
resources:
|
||||||
- common
|
# Common resources
|
||||||
- api-gateway
|
- common/cm-common.yaml
|
||||||
- user-service
|
- common/secret-common.yaml
|
||||||
- bill-service
|
- common/secret-imagepull.yaml
|
||||||
- product-service
|
- common/ingress.yaml
|
||||||
- kos-mock
|
|
||||||
|
# API Gateway
|
||||||
|
- api-gateway/deployment.yaml
|
||||||
|
- api-gateway/service.yaml
|
||||||
|
- api-gateway/cm-api-gateway.yaml
|
||||||
|
|
||||||
|
# User Service
|
||||||
|
- user-service/deployment.yaml
|
||||||
|
- user-service/service.yaml
|
||||||
|
- user-service/cm-user-service.yaml
|
||||||
|
- user-service/secret-user-service.yaml
|
||||||
|
|
||||||
|
# Bill Service
|
||||||
|
- bill-service/deployment.yaml
|
||||||
|
- bill-service/service.yaml
|
||||||
|
- bill-service/cm-bill-service.yaml
|
||||||
|
- bill-service/secret-bill-service.yaml
|
||||||
|
|
||||||
|
# Product Service
|
||||||
|
- product-service/deployment.yaml
|
||||||
|
- product-service/service.yaml
|
||||||
|
- product-service/cm-product-service.yaml
|
||||||
|
- product-service/secret-product-service.yaml
|
||||||
|
|
||||||
|
# KOS Mock
|
||||||
|
- kos-mock/deployment.yaml
|
||||||
|
- kos-mock/service.yaml
|
||||||
|
- kos-mock/cm-kos-mock.yaml
|
||||||
|
|
||||||
|
commonLabels:
|
||||||
|
app: phonebill
|
||||||
|
version: v1
|
||||||
|
|
||||||
|
images:
|
||||||
|
- name: acrdigitalgarage01.azurecr.io/phonebill/api-gateway
|
||||||
|
newTag: latest
|
||||||
|
- name: acrdigitalgarage01.azurecr.io/phonebill/user-service
|
||||||
|
newTag: latest
|
||||||
|
- name: acrdigitalgarage01.azurecr.io/phonebill/bill-service
|
||||||
|
newTag: latest
|
||||||
|
- name: acrdigitalgarage01.azurecr.io/phonebill/product-service
|
||||||
|
newTag: latest
|
||||||
|
- name: acrdigitalgarage01.azurecr.io/phonebill/kos-mock
|
||||||
|
newTag: latest
|
||||||
@ -2,6 +2,7 @@ apiVersion: v1
|
|||||||
kind: ConfigMap
|
kind: ConfigMap
|
||||||
metadata:
|
metadata:
|
||||||
name: cm-product-service
|
name: cm-product-service
|
||||||
|
namespace: phonebill-dev
|
||||||
data:
|
data:
|
||||||
SERVER_PORT: "8083"
|
SERVER_PORT: "8083"
|
||||||
DB_KIND: "postgresql"
|
DB_KIND: "postgresql"
|
||||||
|
|||||||
@ -2,6 +2,7 @@ apiVersion: apps/v1
|
|||||||
kind: Deployment
|
kind: Deployment
|
||||||
metadata:
|
metadata:
|
||||||
name: product-service
|
name: product-service
|
||||||
|
namespace: phonebill-dev
|
||||||
spec:
|
spec:
|
||||||
replicas: 1
|
replicas: 1
|
||||||
selector:
|
selector:
|
||||||
|
|||||||
@ -1,8 +0,0 @@
|
|||||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
|
||||||
kind: Kustomization
|
|
||||||
|
|
||||||
resources:
|
|
||||||
- cm-product-service.yaml
|
|
||||||
- deployment.yaml
|
|
||||||
- secret-product-service.yaml
|
|
||||||
- service.yaml
|
|
||||||
@ -2,6 +2,7 @@ apiVersion: v1
|
|||||||
kind: Secret
|
kind: Secret
|
||||||
metadata:
|
metadata:
|
||||||
name: secret-product-service
|
name: secret-product-service
|
||||||
|
namespace: phonebill-dev
|
||||||
type: Opaque
|
type: Opaque
|
||||||
stringData:
|
stringData:
|
||||||
DB_HOST: "product-change-postgres-dev-postgresql"
|
DB_HOST: "product-change-postgres-dev-postgresql"
|
||||||
|
|||||||
@ -2,6 +2,7 @@ apiVersion: v1
|
|||||||
kind: Service
|
kind: Service
|
||||||
metadata:
|
metadata:
|
||||||
name: product-service
|
name: product-service
|
||||||
|
namespace: phonebill-dev
|
||||||
spec:
|
spec:
|
||||||
selector:
|
selector:
|
||||||
app: product-service
|
app: product-service
|
||||||
|
|||||||
@ -2,6 +2,7 @@ apiVersion: v1
|
|||||||
kind: ConfigMap
|
kind: ConfigMap
|
||||||
metadata:
|
metadata:
|
||||||
name: cm-user-service
|
name: cm-user-service
|
||||||
|
namespace: phonebill-dev
|
||||||
data:
|
data:
|
||||||
SERVER_PORT: "8081"
|
SERVER_PORT: "8081"
|
||||||
DB_KIND: "postgresql"
|
DB_KIND: "postgresql"
|
||||||
|
|||||||
@ -2,6 +2,7 @@ apiVersion: apps/v1
|
|||||||
kind: Deployment
|
kind: Deployment
|
||||||
metadata:
|
metadata:
|
||||||
name: user-service
|
name: user-service
|
||||||
|
namespace: phonebill-dev
|
||||||
spec:
|
spec:
|
||||||
replicas: 1
|
replicas: 1
|
||||||
selector:
|
selector:
|
||||||
|
|||||||
@ -1,8 +0,0 @@
|
|||||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
|
||||||
kind: Kustomization
|
|
||||||
|
|
||||||
resources:
|
|
||||||
- cm-user-service.yaml
|
|
||||||
- deployment.yaml
|
|
||||||
- secret-user-service.yaml
|
|
||||||
- service.yaml
|
|
||||||
@ -2,6 +2,7 @@ apiVersion: v1
|
|||||||
kind: Secret
|
kind: Secret
|
||||||
metadata:
|
metadata:
|
||||||
name: secret-user-service
|
name: secret-user-service
|
||||||
|
namespace: phonebill-dev
|
||||||
type: Opaque
|
type: Opaque
|
||||||
stringData:
|
stringData:
|
||||||
DB_HOST: "auth-postgres-dev-postgresql"
|
DB_HOST: "auth-postgres-dev-postgresql"
|
||||||
|
|||||||
@ -2,6 +2,7 @@ apiVersion: v1
|
|||||||
kind: Service
|
kind: Service
|
||||||
metadata:
|
metadata:
|
||||||
name: user-service
|
name: user-service
|
||||||
|
namespace: phonebill-dev
|
||||||
spec:
|
spec:
|
||||||
selector:
|
selector:
|
||||||
app: user-service
|
app: user-service
|
||||||
|
|||||||
@ -4,5 +4,8 @@ metadata:
|
|||||||
name: cm-common
|
name: cm-common
|
||||||
data:
|
data:
|
||||||
CORS_ALLOWED_ORIGINS: "http://localhost:8081,http://localhost:8082,http://localhost:8083,http://localhost:8084,http://phonebill.20.214.196.128.nip.io"
|
CORS_ALLOWED_ORIGINS: "http://localhost:8081,http://localhost:8082,http://localhost:8083,http://localhost:8084,http://phonebill.20.214.196.128.nip.io"
|
||||||
|
JWT_ACCESS_TOKEN_VALIDITY: "18000000" # 5시간
|
||||||
|
JWT_REFRESH_TOKEN_VALIDITY: "86400000"
|
||||||
|
REDIS_PORT: "6379"
|
||||||
SPRING_PROFILES_ACTIVE: "dev"
|
SPRING_PROFILES_ACTIVE: "dev"
|
||||||
DDL_AUTO: "update"
|
DDL_AUTO: "update"
|
||||||
89
deployment/cicd/kustomize/overlays/dev/deployment-patch.yaml
Normal file
89
deployment/cicd/kustomize/overlays/dev/deployment-patch.yaml
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: api-gateway
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: api-gateway
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 256m
|
||||||
|
memory: 256Mi
|
||||||
|
limits:
|
||||||
|
cpu: 1024m
|
||||||
|
memory: 1024Mi
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: user-service
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: user-service
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 256m
|
||||||
|
memory: 256Mi
|
||||||
|
limits:
|
||||||
|
cpu: 1024m
|
||||||
|
memory: 1024Mi
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: bill-service
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: bill-service
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 256m
|
||||||
|
memory: 256Mi
|
||||||
|
limits:
|
||||||
|
cpu: 1024m
|
||||||
|
memory: 1024Mi
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: product-service
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: product-service
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 256m
|
||||||
|
memory: 256Mi
|
||||||
|
limits:
|
||||||
|
cpu: 1024m
|
||||||
|
memory: 1024Mi
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: kos-mock
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: kos-mock
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 256m
|
||||||
|
memory: 256Mi
|
||||||
|
limits:
|
||||||
|
cpu: 1024m
|
||||||
|
memory: 1024Mi
|
||||||
@ -6,5 +6,43 @@ metadata:
|
|||||||
kubernetes.io/ingress.class: nginx
|
kubernetes.io/ingress.class: nginx
|
||||||
nginx.ingress.kubernetes.io/ssl-redirect: "false"
|
nginx.ingress.kubernetes.io/ssl-redirect: "false"
|
||||||
spec:
|
spec:
|
||||||
|
ingressClassName: nginx
|
||||||
rules:
|
rules:
|
||||||
- host: phonebill-api.20.214.196.128.nip.io
|
- host: phonebill-api.20.214.196.128.nip.io
|
||||||
|
http:
|
||||||
|
paths:
|
||||||
|
- path: /api/v1/auth
|
||||||
|
pathType: Prefix
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: user-service
|
||||||
|
port:
|
||||||
|
number: 80
|
||||||
|
- path: /api/v1/users
|
||||||
|
pathType: Prefix
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: user-service
|
||||||
|
port:
|
||||||
|
number: 80
|
||||||
|
- path: /api/v1/bills
|
||||||
|
pathType: Prefix
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: bill-service
|
||||||
|
port:
|
||||||
|
number: 80
|
||||||
|
- path: /api/v1/products
|
||||||
|
pathType: Prefix
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: product-service
|
||||||
|
port:
|
||||||
|
number: 80
|
||||||
|
- path: /api/v1/kos
|
||||||
|
pathType: Prefix
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: kos-mock
|
||||||
|
port:
|
||||||
|
number: 80
|
||||||
|
|||||||
@ -4,16 +4,30 @@ kind: Kustomization
|
|||||||
namespace: phonebill-dev
|
namespace: phonebill-dev
|
||||||
|
|
||||||
resources:
|
resources:
|
||||||
- ../../base
|
- ../../base
|
||||||
|
|
||||||
commonLabels:
|
|
||||||
env: dev
|
|
||||||
|
|
||||||
patchesStrategicMerge:
|
patchesStrategicMerge:
|
||||||
- configmap-common-patch.yaml
|
- configmap-common-patch.yaml
|
||||||
- secret-common-patch.yaml
|
- deployment-patch.yaml
|
||||||
- ingress-patch.yaml
|
- ingress-patch.yaml
|
||||||
- replica-patch.yaml
|
- secret-common-patch.yaml
|
||||||
- secret-user-service-patch.yaml
|
- secret-user-service-patch.yaml
|
||||||
- secret-bill-service-patch.yaml
|
- secret-bill-service-patch.yaml
|
||||||
- secret-product-service-patch.yaml
|
- secret-product-service-patch.yaml
|
||||||
|
|
||||||
|
images:
|
||||||
|
- name: acrdigitalgarage01.azurecr.io/phonebill/api-gateway
|
||||||
|
newTag: dev-latest
|
||||||
|
- name: acrdigitalgarage01.azurecr.io/phonebill/user-service
|
||||||
|
newTag: dev-latest
|
||||||
|
- name: acrdigitalgarage01.azurecr.io/phonebill/bill-service
|
||||||
|
newTag: dev-latest
|
||||||
|
- name: acrdigitalgarage01.azurecr.io/phonebill/product-service
|
||||||
|
newTag: dev-latest
|
||||||
|
- name: acrdigitalgarage01.azurecr.io/phonebill/kos-mock
|
||||||
|
newTag: dev-latest
|
||||||
|
|
||||||
|
namePrefix: dev-
|
||||||
|
|
||||||
|
commonLabels:
|
||||||
|
environment: dev
|
||||||
@ -1,35 +0,0 @@
|
|||||||
# Replica count patches for dev environment
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: api-gateway
|
|
||||||
spec:
|
|
||||||
replicas: 1
|
|
||||||
---
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: user-service
|
|
||||||
spec:
|
|
||||||
replicas: 1
|
|
||||||
---
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: bill-service
|
|
||||||
spec:
|
|
||||||
replicas: 1
|
|
||||||
---
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: product-service
|
|
||||||
spec:
|
|
||||||
replicas: 1
|
|
||||||
---
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: kos-mock
|
|
||||||
spec:
|
|
||||||
replicas: 1
|
|
||||||
@ -5,4 +5,6 @@ metadata:
|
|||||||
type: Opaque
|
type: Opaque
|
||||||
stringData:
|
stringData:
|
||||||
DB_HOST: "bill-inquiry-postgres-dev-postgresql"
|
DB_HOST: "bill-inquiry-postgres-dev-postgresql"
|
||||||
|
DB_NAME: "bill_inquiry_db"
|
||||||
|
DB_USERNAME: "bill_inquiry_user"
|
||||||
DB_PASSWORD: "BillUser2025!"
|
DB_PASSWORD: "BillUser2025!"
|
||||||
@ -4,5 +4,6 @@ metadata:
|
|||||||
name: secret-common
|
name: secret-common
|
||||||
type: Opaque
|
type: Opaque
|
||||||
stringData:
|
stringData:
|
||||||
|
JWT_SECRET: "nwe5Yo9qaJ6FBD/Thl2/j6/SFAfNwUorAY1ZcWO2KI7uA4bmVLOCPxE9hYuUpRCOkgV2UF2DdHXtqHi3+BU/ecbz2zpHyf/720h48UbA3XOMYOX1sdM+dQ=="
|
||||||
REDIS_HOST: "redis-cache-dev-master"
|
REDIS_HOST: "redis-cache-dev-master"
|
||||||
REDIS_PASSWORD: "Redis2025Dev!"
|
REDIS_PASSWORD: "Redis2025Dev!"
|
||||||
@ -5,4 +5,6 @@ metadata:
|
|||||||
type: Opaque
|
type: Opaque
|
||||||
stringData:
|
stringData:
|
||||||
DB_HOST: "product-change-postgres-dev-postgresql"
|
DB_HOST: "product-change-postgres-dev-postgresql"
|
||||||
|
DB_NAME: "product_change_db"
|
||||||
|
DB_USERNAME: "product_change_user"
|
||||||
DB_PASSWORD: "ProductUser2025!"
|
DB_PASSWORD: "ProductUser2025!"
|
||||||
@ -5,4 +5,6 @@ metadata:
|
|||||||
type: Opaque
|
type: Opaque
|
||||||
stringData:
|
stringData:
|
||||||
DB_HOST: "auth-postgres-dev-postgresql"
|
DB_HOST: "auth-postgres-dev-postgresql"
|
||||||
|
DB_NAME: "phonebill_auth"
|
||||||
|
DB_USERNAME: "auth_user"
|
||||||
DB_PASSWORD: "AuthUser2025!"
|
DB_PASSWORD: "AuthUser2025!"
|
||||||
@ -3,6 +3,9 @@ kind: ConfigMap
|
|||||||
metadata:
|
metadata:
|
||||||
name: cm-common
|
name: cm-common
|
||||||
data:
|
data:
|
||||||
CORS_ALLOWED_ORIGINS: "https://phonebill.example.com,https://phonebill-app.example.com"
|
CORS_ALLOWED_ORIGINS: "https://phonebill.20.214.196.128.nip.io"
|
||||||
|
JWT_ACCESS_TOKEN_VALIDITY: "3600000" # 1시간
|
||||||
|
JWT_REFRESH_TOKEN_VALIDITY: "86400000"
|
||||||
|
REDIS_PORT: "6379"
|
||||||
SPRING_PROFILES_ACTIVE: "prod"
|
SPRING_PROFILES_ACTIVE: "prod"
|
||||||
DDL_AUTO: "validate"
|
DDL_AUTO: "validate"
|
||||||
@ -0,0 +1,89 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: api-gateway
|
||||||
|
spec:
|
||||||
|
replicas: 3
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: api-gateway
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 1024m
|
||||||
|
memory: 1024Mi
|
||||||
|
limits:
|
||||||
|
cpu: 4096m
|
||||||
|
memory: 4096Mi
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: user-service
|
||||||
|
spec:
|
||||||
|
replicas: 3
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: user-service
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 1024m
|
||||||
|
memory: 1024Mi
|
||||||
|
limits:
|
||||||
|
cpu: 4096m
|
||||||
|
memory: 4096Mi
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: bill-service
|
||||||
|
spec:
|
||||||
|
replicas: 3
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: bill-service
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 1024m
|
||||||
|
memory: 1024Mi
|
||||||
|
limits:
|
||||||
|
cpu: 4096m
|
||||||
|
memory: 4096Mi
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: product-service
|
||||||
|
spec:
|
||||||
|
replicas: 3
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: product-service
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 1024m
|
||||||
|
memory: 1024Mi
|
||||||
|
limits:
|
||||||
|
cpu: 4096m
|
||||||
|
memory: 4096Mi
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: kos-mock
|
||||||
|
spec:
|
||||||
|
replicas: 3
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: kos-mock
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 1024m
|
||||||
|
memory: 1024Mi
|
||||||
|
limits:
|
||||||
|
cpu: 4096m
|
||||||
|
memory: 4096Mi
|
||||||
@ -7,10 +7,50 @@ metadata:
|
|||||||
nginx.ingress.kubernetes.io/ssl-redirect: "true"
|
nginx.ingress.kubernetes.io/ssl-redirect: "true"
|
||||||
nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
|
nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
|
||||||
cert-manager.io/cluster-issuer: "letsencrypt-prod"
|
cert-manager.io/cluster-issuer: "letsencrypt-prod"
|
||||||
|
nginx.ingress.kubernetes.io/ssl-protocols: "TLSv1.2 TLSv1.3"
|
||||||
|
nginx.ingress.kubernetes.io/ssl-ciphers: "ECDHE-RSA-AES128-GCM-SHA256,ECDHE-ECDSA-AES128-GCM-SHA256,ECDHE-RSA-AES256-GCM-SHA384,ECDHE-ECDSA-AES256-GCM-SHA384"
|
||||||
spec:
|
spec:
|
||||||
|
ingressClassName: nginx
|
||||||
tls:
|
tls:
|
||||||
- hosts:
|
- hosts:
|
||||||
- phonebill-api.example.com
|
- phonebill.20.214.196.128.nip.io
|
||||||
secretName: phonebill-prod-tls
|
secretName: phonebill-prod-tls
|
||||||
rules:
|
rules:
|
||||||
- host: phonebill-api.example.com
|
- host: phonebill.20.214.196.128.nip.io
|
||||||
|
http:
|
||||||
|
paths:
|
||||||
|
- path: /api/v1/auth
|
||||||
|
pathType: Prefix
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: user-service
|
||||||
|
port:
|
||||||
|
number: 80
|
||||||
|
- path: /api/v1/users
|
||||||
|
pathType: Prefix
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: user-service
|
||||||
|
port:
|
||||||
|
number: 80
|
||||||
|
- path: /api/v1/bills
|
||||||
|
pathType: Prefix
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: bill-service
|
||||||
|
port:
|
||||||
|
number: 80
|
||||||
|
- path: /api/v1/products
|
||||||
|
pathType: Prefix
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: product-service
|
||||||
|
port:
|
||||||
|
number: 80
|
||||||
|
- path: /api/v1/kos
|
||||||
|
pathType: Prefix
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: kos-mock
|
||||||
|
port:
|
||||||
|
number: 80
|
||||||
|
|||||||
@ -4,10 +4,30 @@ kind: Kustomization
|
|||||||
namespace: phonebill-prod
|
namespace: phonebill-prod
|
||||||
|
|
||||||
resources:
|
resources:
|
||||||
- ../../base
|
- ../../base
|
||||||
|
|
||||||
commonLabels:
|
|
||||||
env: prod
|
|
||||||
|
|
||||||
patchesStrategicMerge:
|
patchesStrategicMerge:
|
||||||
- env-patches.yaml
|
- configmap-common-patch.yaml
|
||||||
|
- deployment-patch.yaml
|
||||||
|
- ingress-patch.yaml
|
||||||
|
- secret-common-patch.yaml
|
||||||
|
- secret-user-service-patch.yaml
|
||||||
|
- secret-bill-service-patch.yaml
|
||||||
|
- secret-product-service-patch.yaml
|
||||||
|
|
||||||
|
images:
|
||||||
|
- name: acrdigitalgarage01.azurecr.io/phonebill/api-gateway
|
||||||
|
newTag: prod-latest
|
||||||
|
- name: acrdigitalgarage01.azurecr.io/phonebill/user-service
|
||||||
|
newTag: prod-latest
|
||||||
|
- name: acrdigitalgarage01.azurecr.io/phonebill/bill-service
|
||||||
|
newTag: prod-latest
|
||||||
|
- name: acrdigitalgarage01.azurecr.io/phonebill/product-service
|
||||||
|
newTag: prod-latest
|
||||||
|
- name: acrdigitalgarage01.azurecr.io/phonebill/kos-mock
|
||||||
|
newTag: prod-latest
|
||||||
|
|
||||||
|
namePrefix: prod-
|
||||||
|
|
||||||
|
commonLabels:
|
||||||
|
environment: prod
|
||||||
@ -1,35 +0,0 @@
|
|||||||
# Replica count patches for production environment
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: api-gateway
|
|
||||||
spec:
|
|
||||||
replicas: 3
|
|
||||||
---
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: user-service
|
|
||||||
spec:
|
|
||||||
replicas: 3
|
|
||||||
---
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: bill-service
|
|
||||||
spec:
|
|
||||||
replicas: 3
|
|
||||||
---
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: product-service
|
|
||||||
spec:
|
|
||||||
replicas: 3
|
|
||||||
---
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: kos-mock
|
|
||||||
spec:
|
|
||||||
replicas: 2
|
|
||||||
@ -5,4 +5,6 @@ metadata:
|
|||||||
type: Opaque
|
type: Opaque
|
||||||
stringData:
|
stringData:
|
||||||
DB_HOST: "bill-inquiry-postgres-prod-postgresql"
|
DB_HOST: "bill-inquiry-postgres-prod-postgresql"
|
||||||
DB_PASSWORD: "BillUserProd$ecure2025!"
|
DB_NAME: "bill_inquiry_db"
|
||||||
|
DB_USERNAME: "bill_inquiry_user"
|
||||||
|
DB_PASSWORD: "BillUser2025Prod!"
|
||||||
|
|||||||
@ -4,5 +4,6 @@ metadata:
|
|||||||
name: secret-common
|
name: secret-common
|
||||||
type: Opaque
|
type: Opaque
|
||||||
stringData:
|
stringData:
|
||||||
|
JWT_SECRET: "prod7Yo9qaJ6FBD/Thl2/j6/SFAfNwUorAY1ZcWO2KI7uA4bmVLOCPxE9hYuUpRCOkgV2UF2DdHXtqHi3+BU/prod"
|
||||||
REDIS_HOST: "redis-cache-prod-master"
|
REDIS_HOST: "redis-cache-prod-master"
|
||||||
REDIS_PASSWORD: "Redis2025Prod$ecure!"
|
REDIS_PASSWORD: "Redis2025Prod!"
|
||||||
|
|||||||
@ -5,4 +5,6 @@ metadata:
|
|||||||
type: Opaque
|
type: Opaque
|
||||||
stringData:
|
stringData:
|
||||||
DB_HOST: "product-change-postgres-prod-postgresql"
|
DB_HOST: "product-change-postgres-prod-postgresql"
|
||||||
DB_PASSWORD: "ProductUserProd$ecure2025!"
|
DB_NAME: "product_change_db"
|
||||||
|
DB_USERNAME: "product_change_user"
|
||||||
|
DB_PASSWORD: "ProductUser2025Prod!"
|
||||||
|
|||||||
@ -5,4 +5,6 @@ metadata:
|
|||||||
type: Opaque
|
type: Opaque
|
||||||
stringData:
|
stringData:
|
||||||
DB_HOST: "auth-postgres-prod-postgresql"
|
DB_HOST: "auth-postgres-prod-postgresql"
|
||||||
DB_PASSWORD: "AuthUserProd$ecure2025!"
|
DB_NAME: "phonebill_auth"
|
||||||
|
DB_USERNAME: "auth_user"
|
||||||
|
DB_PASSWORD: "AuthUser2025Prod!"
|
||||||
|
|||||||
@ -3,6 +3,9 @@ kind: ConfigMap
|
|||||||
metadata:
|
metadata:
|
||||||
name: cm-common
|
name: cm-common
|
||||||
data:
|
data:
|
||||||
CORS_ALLOWED_ORIGINS: "https://phonebill-staging.example.com,https://phonebill.staging.example.com"
|
CORS_ALLOWED_ORIGINS: "https://phonebill-staging.20.214.196.128.nip.io"
|
||||||
|
JWT_ACCESS_TOKEN_VALIDITY: "18000000" # 5시간
|
||||||
|
JWT_REFRESH_TOKEN_VALIDITY: "86400000"
|
||||||
|
REDIS_PORT: "6379"
|
||||||
SPRING_PROFILES_ACTIVE: "staging"
|
SPRING_PROFILES_ACTIVE: "staging"
|
||||||
DDL_AUTO: "validate"
|
DDL_AUTO: "validate"
|
||||||
@ -0,0 +1,89 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: api-gateway
|
||||||
|
spec:
|
||||||
|
replicas: 2
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: api-gateway
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 512m
|
||||||
|
memory: 512Mi
|
||||||
|
limits:
|
||||||
|
cpu: 2048m
|
||||||
|
memory: 2048Mi
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: user-service
|
||||||
|
spec:
|
||||||
|
replicas: 2
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: user-service
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 512m
|
||||||
|
memory: 512Mi
|
||||||
|
limits:
|
||||||
|
cpu: 2048m
|
||||||
|
memory: 2048Mi
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: bill-service
|
||||||
|
spec:
|
||||||
|
replicas: 2
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: bill-service
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 512m
|
||||||
|
memory: 512Mi
|
||||||
|
limits:
|
||||||
|
cpu: 2048m
|
||||||
|
memory: 2048Mi
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: product-service
|
||||||
|
spec:
|
||||||
|
replicas: 2
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: product-service
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 512m
|
||||||
|
memory: 512Mi
|
||||||
|
limits:
|
||||||
|
cpu: 2048m
|
||||||
|
memory: 2048Mi
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: kos-mock
|
||||||
|
spec:
|
||||||
|
replicas: 2
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: kos-mock
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 512m
|
||||||
|
memory: 512Mi
|
||||||
|
limits:
|
||||||
|
cpu: 2048m
|
||||||
|
memory: 2048Mi
|
||||||
@ -7,9 +7,47 @@ metadata:
|
|||||||
nginx.ingress.kubernetes.io/ssl-redirect: "true"
|
nginx.ingress.kubernetes.io/ssl-redirect: "true"
|
||||||
cert-manager.io/cluster-issuer: "letsencrypt-prod"
|
cert-manager.io/cluster-issuer: "letsencrypt-prod"
|
||||||
spec:
|
spec:
|
||||||
|
ingressClassName: nginx
|
||||||
tls:
|
tls:
|
||||||
- hosts:
|
- hosts:
|
||||||
- phonebill-api-staging.example.com
|
- phonebill-staging.20.214.196.128.nip.io
|
||||||
secretName: phonebill-staging-tls
|
secretName: phonebill-staging-tls
|
||||||
rules:
|
rules:
|
||||||
- host: phonebill-api-staging.example.com
|
- host: phonebill-staging.20.214.196.128.nip.io
|
||||||
|
http:
|
||||||
|
paths:
|
||||||
|
- path: /api/v1/auth
|
||||||
|
pathType: Prefix
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: user-service
|
||||||
|
port:
|
||||||
|
number: 80
|
||||||
|
- path: /api/v1/users
|
||||||
|
pathType: Prefix
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: user-service
|
||||||
|
port:
|
||||||
|
number: 80
|
||||||
|
- path: /api/v1/bills
|
||||||
|
pathType: Prefix
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: bill-service
|
||||||
|
port:
|
||||||
|
number: 80
|
||||||
|
- path: /api/v1/products
|
||||||
|
pathType: Prefix
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: product-service
|
||||||
|
port:
|
||||||
|
number: 80
|
||||||
|
- path: /api/v1/kos
|
||||||
|
pathType: Prefix
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: kos-mock
|
||||||
|
port:
|
||||||
|
number: 80
|
||||||
|
|||||||
@ -4,16 +4,30 @@ kind: Kustomization
|
|||||||
namespace: phonebill-staging
|
namespace: phonebill-staging
|
||||||
|
|
||||||
resources:
|
resources:
|
||||||
- ../../base
|
- ../../base
|
||||||
|
|
||||||
commonLabels:
|
|
||||||
env: staging
|
|
||||||
|
|
||||||
patchesStrategicMerge:
|
patchesStrategicMerge:
|
||||||
- configmap-common-patch.yaml
|
- configmap-common-patch.yaml
|
||||||
- secret-common-patch.yaml
|
- deployment-patch.yaml
|
||||||
- ingress-patch.yaml
|
- ingress-patch.yaml
|
||||||
- replica-patch.yaml
|
- secret-common-patch.yaml
|
||||||
- secret-user-service-patch.yaml
|
- secret-user-service-patch.yaml
|
||||||
- secret-bill-service-patch.yaml
|
- secret-bill-service-patch.yaml
|
||||||
- secret-product-service-patch.yaml
|
- secret-product-service-patch.yaml
|
||||||
|
|
||||||
|
images:
|
||||||
|
- name: acrdigitalgarage01.azurecr.io/phonebill/api-gateway
|
||||||
|
newTag: staging-latest
|
||||||
|
- name: acrdigitalgarage01.azurecr.io/phonebill/user-service
|
||||||
|
newTag: staging-latest
|
||||||
|
- name: acrdigitalgarage01.azurecr.io/phonebill/bill-service
|
||||||
|
newTag: staging-latest
|
||||||
|
- name: acrdigitalgarage01.azurecr.io/phonebill/product-service
|
||||||
|
newTag: staging-latest
|
||||||
|
- name: acrdigitalgarage01.azurecr.io/phonebill/kos-mock
|
||||||
|
newTag: staging-latest
|
||||||
|
|
||||||
|
namePrefix: staging-
|
||||||
|
|
||||||
|
commonLabels:
|
||||||
|
environment: staging
|
||||||
@ -1,35 +0,0 @@
|
|||||||
# Replica count patches for staging environment
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: api-gateway
|
|
||||||
spec:
|
|
||||||
replicas: 2
|
|
||||||
---
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: user-service
|
|
||||||
spec:
|
|
||||||
replicas: 2
|
|
||||||
---
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: bill-service
|
|
||||||
spec:
|
|
||||||
replicas: 2
|
|
||||||
---
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: product-service
|
|
||||||
spec:
|
|
||||||
replicas: 2
|
|
||||||
---
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: kos-mock
|
|
||||||
spec:
|
|
||||||
replicas: 1
|
|
||||||
@ -5,4 +5,6 @@ metadata:
|
|||||||
type: Opaque
|
type: Opaque
|
||||||
stringData:
|
stringData:
|
||||||
DB_HOST: "bill-inquiry-postgres-staging-postgresql"
|
DB_HOST: "bill-inquiry-postgres-staging-postgresql"
|
||||||
DB_PASSWORD: "BillUserStaging2025!"
|
DB_NAME: "bill_inquiry_db"
|
||||||
|
DB_USERNAME: "bill_inquiry_user"
|
||||||
|
DB_PASSWORD: "BillUser2025Staging!"
|
||||||
|
|||||||
@ -4,5 +4,6 @@ metadata:
|
|||||||
name: secret-common
|
name: secret-common
|
||||||
type: Opaque
|
type: Opaque
|
||||||
stringData:
|
stringData:
|
||||||
|
JWT_SECRET: "staging5Yo9qaJ6FBD/Thl2/j6/SFAfNwUorAY1ZcWO2KI7uA4bmVLOCPxE9hYuUpRCOkgV2UF2DdHXtqHi3+BU/staging"
|
||||||
REDIS_HOST: "redis-cache-staging-master"
|
REDIS_HOST: "redis-cache-staging-master"
|
||||||
REDIS_PASSWORD: "Redis2025Staging!"
|
REDIS_PASSWORD: "Redis2025Staging!"
|
||||||
@ -5,4 +5,6 @@ metadata:
|
|||||||
type: Opaque
|
type: Opaque
|
||||||
stringData:
|
stringData:
|
||||||
DB_HOST: "product-change-postgres-staging-postgresql"
|
DB_HOST: "product-change-postgres-staging-postgresql"
|
||||||
DB_PASSWORD: "ProductUserStaging2025!"
|
DB_NAME: "product_change_db"
|
||||||
|
DB_USERNAME: "product_change_user"
|
||||||
|
DB_PASSWORD: "ProductUser2025Staging!"
|
||||||
|
|||||||
@ -5,4 +5,6 @@ metadata:
|
|||||||
type: Opaque
|
type: Opaque
|
||||||
stringData:
|
stringData:
|
||||||
DB_HOST: "auth-postgres-staging-postgresql"
|
DB_HOST: "auth-postgres-staging-postgresql"
|
||||||
DB_PASSWORD: "AuthUserStaging2025!"
|
DB_NAME: "phonebill_auth"
|
||||||
|
DB_USERNAME: "auth_user"
|
||||||
|
DB_PASSWORD: "AuthUser2025Staging!"
|
||||||
|
|||||||
544
deployment/cicd/scripts/deploy.sh
Normal file → Executable file
544
deployment/cicd/scripts/deploy.sh
Normal file → Executable file
@ -1,474 +1,82 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
# Manual Deployment Script for phonebill Microservices
|
ENVIRONMENT=${1:-dev}
|
||||||
# Usage: ./deploy.sh <environment> [service1,service2,...] [options]
|
IMAGE_TAG=${2:-latest}
|
||||||
# Example: ./deploy.sh dev all --skip-build
|
|
||||||
# Example: ./deploy.sh prod user-service,bill-service --force
|
|
||||||
|
|
||||||
set -euo pipefail
|
echo "🚀 Starting deployment to ${ENVIRONMENT} environment with image tag: ${IMAGE_TAG}"
|
||||||
|
|
||||||
# Script configuration
|
# 환경 검증
|
||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
if [[ ! "$ENVIRONMENT" =~ ^(dev|staging|prod)$ ]]; then
|
||||||
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../../.." && pwd)"
|
echo "❌ Error: Invalid environment. Use dev, staging, or prod"
|
||||||
CICD_DIR="${PROJECT_ROOT}/deployment/cicd"
|
|
||||||
K8S_DIR="${PROJECT_ROOT}/deployment/k8s"
|
|
||||||
|
|
||||||
# Colors for output
|
|
||||||
RED='\033[0;31m'
|
|
||||||
GREEN='\033[0;32m'
|
|
||||||
YELLOW='\033[1;33m'
|
|
||||||
BLUE='\033[0;34m'
|
|
||||||
NC='\033[0m' # No Color
|
|
||||||
|
|
||||||
# Logging functions
|
|
||||||
log_info() {
|
|
||||||
echo -e "${BLUE}[INFO]${NC} $1"
|
|
||||||
}
|
|
||||||
|
|
||||||
log_warn() {
|
|
||||||
echo -e "${YELLOW}[WARN]${NC} $1"
|
|
||||||
}
|
|
||||||
|
|
||||||
log_error() {
|
|
||||||
echo -e "${RED}[ERROR]${NC} $1"
|
|
||||||
}
|
|
||||||
|
|
||||||
log_success() {
|
|
||||||
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Usage information
|
|
||||||
show_usage() {
|
|
||||||
cat << EOF
|
|
||||||
Usage: $0 <environment> [services] [options]
|
|
||||||
|
|
||||||
Arguments:
|
|
||||||
environment Target environment (dev|staging|prod)
|
|
||||||
services Services to deploy (default: all)
|
|
||||||
Options: all, api-gateway, user-service, bill-service, product-service, kos-mock
|
|
||||||
Multiple services: service1,service2,service3
|
|
||||||
|
|
||||||
Options:
|
|
||||||
--skip-build Skip Gradle build step
|
|
||||||
--skip-test Skip unit tests during build
|
|
||||||
--skip-push Skip container image push
|
|
||||||
--force Force deployment even if no changes
|
|
||||||
--dry-run Show what would be deployed without actually deploying
|
|
||||||
--help Show this help message
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
$0 dev # Deploy all services to dev
|
|
||||||
$0 staging user-service # Deploy user-service to staging
|
|
||||||
$0 prod api-gateway,bill-service # Deploy specific services to prod
|
|
||||||
$0 dev all --skip-build # Deploy without building
|
|
||||||
$0 staging all --dry-run # Preview deployment
|
|
||||||
|
|
||||||
Environment Files:
|
|
||||||
dev: ${CICD_DIR}/config/deploy_env_vars_dev
|
|
||||||
staging: ${CICD_DIR}/config/deploy_env_vars_staging
|
|
||||||
prod: ${CICD_DIR}/config/deploy_env_vars_prod
|
|
||||||
EOF
|
|
||||||
}
|
|
||||||
|
|
||||||
# Parse command line arguments
|
|
||||||
parse_arguments() {
|
|
||||||
if [[ $# -eq 0 ]] || [[ "$1" == "--help" ]]; then
|
|
||||||
show_usage
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
ENVIRONMENT="$1"
|
|
||||||
shift
|
|
||||||
|
|
||||||
# Validate environment
|
|
||||||
if [[ ! "$ENVIRONMENT" =~ ^(dev|staging|prod)$ ]]; then
|
|
||||||
log_error "Invalid environment: $ENVIRONMENT"
|
|
||||||
log_error "Valid environments: dev, staging, prod"
|
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
|
||||||
|
|
||||||
# Set services (default to 'all')
|
|
||||||
SERVICES_TO_DEPLOY="all"
|
|
||||||
if [[ $# -gt 0 ]] && [[ ! "$1" =~ ^-- ]]; then
|
|
||||||
SERVICES_TO_DEPLOY="$1"
|
|
||||||
shift
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Parse options
|
|
||||||
SKIP_BUILD=false
|
|
||||||
SKIP_TEST=false
|
|
||||||
SKIP_PUSH=false
|
|
||||||
FORCE_DEPLOY=false
|
|
||||||
DRY_RUN=false
|
|
||||||
|
|
||||||
while [[ $# -gt 0 ]]; do
|
|
||||||
case $1 in
|
|
||||||
--skip-build)
|
|
||||||
SKIP_BUILD=true
|
|
||||||
shift
|
|
||||||
;;
|
|
||||||
--skip-test)
|
|
||||||
SKIP_TEST=true
|
|
||||||
shift
|
|
||||||
;;
|
|
||||||
--skip-push)
|
|
||||||
SKIP_PUSH=true
|
|
||||||
shift
|
|
||||||
;;
|
|
||||||
--force)
|
|
||||||
FORCE_DEPLOY=true
|
|
||||||
shift
|
|
||||||
;;
|
|
||||||
--dry-run)
|
|
||||||
DRY_RUN=true
|
|
||||||
shift
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
log_error "Unknown option: $1"
|
|
||||||
show_usage
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
# Load environment configuration
|
|
||||||
load_environment_config() {
|
|
||||||
local config_file="${CICD_DIR}/config/deploy_env_vars_${ENVIRONMENT}"
|
|
||||||
|
|
||||||
if [[ ! -f "$config_file" ]]; then
|
|
||||||
log_error "Configuration file not found: $config_file"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Source the configuration file
|
|
||||||
set -a # automatically export all variables
|
|
||||||
source "$config_file"
|
|
||||||
set +a
|
|
||||||
|
|
||||||
log_info "Loaded configuration from $config_file"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Validate prerequisites
|
|
||||||
validate_prerequisites() {
|
|
||||||
local missing_tools=()
|
|
||||||
|
|
||||||
# Check required tools
|
|
||||||
command -v gradle >/dev/null 2>&1 || missing_tools+=("gradle")
|
|
||||||
command -v docker >/dev/null 2>&1 || missing_tools+=("docker")
|
|
||||||
command -v kubectl >/dev/null 2>&1 || missing_tools+=("kubectl")
|
|
||||||
command -v az >/dev/null 2>&1 || missing_tools+=("azure-cli")
|
|
||||||
|
|
||||||
if [[ ${#missing_tools[@]} -ne 0 ]]; then
|
|
||||||
log_error "Missing required tools: ${missing_tools[*]}"
|
|
||||||
log_error "Please install the missing tools and try again"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Check if we're in the project root
|
|
||||||
if [[ ! -f "${PROJECT_ROOT}/settings.gradle" ]]; then
|
|
||||||
log_error "Not in phonebill project root directory"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Check Azure login
|
|
||||||
if ! az account show >/dev/null 2>&1; then
|
|
||||||
log_error "Not logged into Azure CLI. Please run: az login"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Check kubectl context
|
|
||||||
if ! kubectl config current-context >/dev/null 2>&1; then
|
|
||||||
log_warn "No kubectl context set. Will attempt to configure AKS credentials"
|
|
||||||
fi
|
|
||||||
|
|
||||||
log_success "Prerequisites validation passed"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Resolve services list
|
|
||||||
resolve_services() {
|
|
||||||
if [[ "$SERVICES_TO_DEPLOY" == "all" ]]; then
|
|
||||||
SERVICE_LIST=(${SERVICES//,/ })
|
|
||||||
else
|
|
||||||
IFS=',' read -ra SERVICE_LIST <<< "$SERVICES_TO_DEPLOY"
|
|
||||||
fi
|
|
||||||
|
|
||||||
log_info "Services to deploy: ${SERVICE_LIST[*]}"
|
|
||||||
|
|
||||||
# Validate service names
|
|
||||||
local valid_services=(${SERVICES//,/ })
|
|
||||||
for service in "${SERVICE_LIST[@]}"; do
|
|
||||||
if [[ ! " ${valid_services[*]} " =~ " ${service} " ]]; then
|
|
||||||
log_error "Invalid service name: $service"
|
|
||||||
log_error "Valid services: ${valid_services[*]}"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
# Build services
|
|
||||||
build_services() {
|
|
||||||
if [[ "$SKIP_BUILD" == true ]]; then
|
|
||||||
log_info "Skipping build step"
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
log_info "Building services with Gradle..."
|
|
||||||
cd "$PROJECT_ROOT"
|
|
||||||
|
|
||||||
for service in "${SERVICE_LIST[@]}"; do
|
|
||||||
log_info "Building $service..."
|
|
||||||
|
|
||||||
local build_cmd="./gradlew ${service}:clean ${service}:build --no-daemon --parallel"
|
|
||||||
|
|
||||||
if [[ "$SKIP_TEST" == true ]]; then
|
|
||||||
build_cmd="$build_cmd -x test"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ "$DRY_RUN" == true ]]; then
|
|
||||||
log_info "[DRY-RUN] Would execute: $build_cmd"
|
|
||||||
else
|
|
||||||
if ! $build_cmd; then
|
|
||||||
log_error "Build failed for $service"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
log_success "Build completed successfully"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Build and push container images
|
|
||||||
build_and_push_images() {
|
|
||||||
if [[ "$SKIP_PUSH" == true ]]; then
|
|
||||||
log_info "Skipping container image build and push"
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
log_info "Building and pushing container images..."
|
|
||||||
|
|
||||||
# Generate image tag
|
|
||||||
local timestamp=$(date +%Y%m%d-%H%M%S)
|
|
||||||
local build_number="${BUILD_NUMBER:-$(date +%s)}"
|
|
||||||
IMAGE_TAG="${build_number}-${ENVIRONMENT}-${timestamp}"
|
|
||||||
|
|
||||||
log_info "Using image tag: $IMAGE_TAG"
|
|
||||||
|
|
||||||
# Login to ACR
|
|
||||||
if [[ "$DRY_RUN" == false ]]; then
|
|
||||||
log_info "Logging into Azure Container Registry..."
|
|
||||||
az acr login --name "$ACR_NAME"
|
|
||||||
fi
|
|
||||||
|
|
||||||
for service in "${SERVICE_LIST[@]}"; do
|
|
||||||
log_info "Building container image for $service..."
|
|
||||||
|
|
||||||
local image_name="${REGISTRY_URL}/phonebill/${service}"
|
|
||||||
local service_dir="${PROJECT_ROOT}/${service}"
|
|
||||||
|
|
||||||
if [[ ! -d "$service_dir" ]]; then
|
|
||||||
log_error "Service directory not found: $service_dir"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ ! -f "${service_dir}/Dockerfile" ]]; then
|
|
||||||
log_error "Dockerfile not found: ${service_dir}/Dockerfile"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ "$DRY_RUN" == true ]]; then
|
|
||||||
log_info "[DRY-RUN] Would build and push: ${image_name}:${IMAGE_TAG}"
|
|
||||||
else
|
|
||||||
# Build image
|
|
||||||
docker build \
|
|
||||||
-t "${image_name}:${IMAGE_TAG}" \
|
|
||||||
-t "${image_name}:latest-${ENVIRONMENT}" \
|
|
||||||
"$service_dir"
|
|
||||||
|
|
||||||
# Push image
|
|
||||||
docker push "${image_name}:${IMAGE_TAG}"
|
|
||||||
docker push "${image_name}:latest-${ENVIRONMENT}"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
log_success "Container images built and pushed successfully"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Configure kubectl
|
|
||||||
configure_kubectl() {
|
|
||||||
log_info "Configuring kubectl for AKS cluster..."
|
|
||||||
|
|
||||||
if [[ "$DRY_RUN" == false ]]; then
|
|
||||||
az aks get-credentials \
|
|
||||||
--resource-group "$AZURE_RESOURCE_GROUP" \
|
|
||||||
--name "$AKS_CLUSTER_NAME" \
|
|
||||||
--overwrite-existing
|
|
||||||
fi
|
|
||||||
|
|
||||||
log_success "kubectl configured for $AKS_CLUSTER_NAME"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Deploy to Kubernetes
|
|
||||||
deploy_to_kubernetes() {
|
|
||||||
log_info "Deploying services to Kubernetes namespace: $AKS_NAMESPACE"
|
|
||||||
|
|
||||||
# Ensure namespace exists
|
|
||||||
if [[ "$DRY_RUN" == false ]]; then
|
|
||||||
kubectl create namespace "$AKS_NAMESPACE" --dry-run=client -o yaml | kubectl apply -f -
|
|
||||||
fi
|
|
||||||
|
|
||||||
for service in "${SERVICE_LIST[@]}"; do
|
|
||||||
log_info "Deploying $service..."
|
|
||||||
|
|
||||||
local kustomize_path="${K8S_DIR}/${service}"
|
|
||||||
local overlay_path="${kustomize_path}/${KUSTOMIZE_OVERLAY}"
|
|
||||||
|
|
||||||
if [[ ! -d "$overlay_path" ]]; then
|
|
||||||
log_error "Kustomize overlay not found: $overlay_path"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ "$DRY_RUN" == true ]]; then
|
|
||||||
log_info "[DRY-RUN] Would deploy $service using: kubectl apply -k $overlay_path -n $AKS_NAMESPACE"
|
|
||||||
else
|
|
||||||
# Update image tag in kustomization.yaml if IMAGE_TAG is set
|
|
||||||
if [[ -n "${IMAGE_TAG:-}" ]]; then
|
|
||||||
local kustomization_file="${overlay_path}/kustomization.yaml"
|
|
||||||
if [[ -f "$kustomization_file" ]]; then
|
|
||||||
# Backup original file
|
|
||||||
cp "$kustomization_file" "${kustomization_file}.backup"
|
|
||||||
|
|
||||||
# Update image tag
|
|
||||||
sed -i "s|newTag:.*|newTag: ${IMAGE_TAG}|" "$kustomization_file"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Deploy using kubectl + kustomize
|
|
||||||
kubectl apply -k "$overlay_path" -n "$AKS_NAMESPACE"
|
|
||||||
|
|
||||||
# Wait for rollout to complete
|
|
||||||
kubectl rollout status deployment/"$service" -n "$AKS_NAMESPACE" --timeout=300s
|
|
||||||
|
|
||||||
# Restore backup if exists
|
|
||||||
if [[ -f "${kustomization_file}.backup" ]]; then
|
|
||||||
mv "${kustomization_file}.backup" "$kustomization_file"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
log_success "Deployment completed successfully"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Perform health checks
|
|
||||||
perform_health_checks() {
|
|
||||||
if [[ "$DRY_RUN" == true ]]; then
|
|
||||||
log_info "[DRY-RUN] Would perform health checks for deployed services"
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
log_info "Performing health checks..."
|
|
||||||
|
|
||||||
for service in "${SERVICE_LIST[@]}"; do
|
|
||||||
log_info "Health checking $service..."
|
|
||||||
|
|
||||||
local max_retries=${HEALTH_CHECK_RETRY:-10}
|
|
||||||
local retry_count=0
|
|
||||||
local is_healthy=false
|
|
||||||
|
|
||||||
while [[ $retry_count -lt $max_retries ]]; do
|
|
||||||
if kubectl get deployment "$service" -n "$AKS_NAMESPACE" -o json | \
|
|
||||||
jq -e '.status.readyReplicas == .status.replicas and .status.replicas > 0' >/dev/null 2>&1; then
|
|
||||||
is_healthy=true
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
|
|
||||||
retry_count=$((retry_count + 1))
|
|
||||||
log_info "Health check attempt $retry_count/$max_retries for $service..."
|
|
||||||
sleep 30
|
|
||||||
done
|
|
||||||
|
|
||||||
if [[ "$is_healthy" == true ]]; then
|
|
||||||
log_success "$service is healthy"
|
|
||||||
else
|
|
||||||
log_error "$service failed health check"
|
|
||||||
kubectl describe deployment "$service" -n "$AKS_NAMESPACE"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
log_success "All services passed health checks"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Show deployment summary
|
|
||||||
show_summary() {
|
|
||||||
log_info "========================================="
|
|
||||||
log_info "Deployment Summary"
|
|
||||||
log_info "========================================="
|
|
||||||
log_info "Environment: $ENVIRONMENT"
|
|
||||||
log_info "Namespace: $AKS_NAMESPACE"
|
|
||||||
log_info "Services: ${SERVICE_LIST[*]}"
|
|
||||||
|
|
||||||
if [[ -n "${IMAGE_TAG:-}" ]]; then
|
|
||||||
log_info "Image Tag: $IMAGE_TAG"
|
|
||||||
fi
|
|
||||||
|
|
||||||
log_info "Options:"
|
|
||||||
log_info " Skip Build: $SKIP_BUILD"
|
|
||||||
log_info " Skip Test: $SKIP_TEST"
|
|
||||||
log_info " Skip Push: $SKIP_PUSH"
|
|
||||||
log_info " Force Deploy: $FORCE_DEPLOY"
|
|
||||||
log_info " Dry Run: $DRY_RUN"
|
|
||||||
log_info "========================================="
|
|
||||||
}
|
|
||||||
|
|
||||||
# Cleanup function
|
|
||||||
cleanup() {
|
|
||||||
local exit_code=$?
|
|
||||||
if [[ $exit_code -ne 0 ]]; then
|
|
||||||
log_error "Deployment failed with exit code: $exit_code"
|
|
||||||
|
|
||||||
# Show recent events for debugging
|
|
||||||
if [[ "$DRY_RUN" == false ]] && command -v kubectl >/dev/null 2>&1; then
|
|
||||||
log_info "Recent events in namespace $AKS_NAMESPACE:"
|
|
||||||
kubectl get events -n "$AKS_NAMESPACE" --sort-by='.lastTimestamp' --field-selector type=Warning | tail -10
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# Main execution function
|
|
||||||
main() {
|
|
||||||
# Set up error handling
|
|
||||||
trap cleanup EXIT
|
|
||||||
|
|
||||||
log_info "🚀 Starting phonebill deployment script"
|
|
||||||
|
|
||||||
# Parse arguments and validate
|
|
||||||
parse_arguments "$@"
|
|
||||||
show_summary
|
|
||||||
|
|
||||||
# Load configuration and validate prerequisites
|
|
||||||
load_environment_config
|
|
||||||
validate_prerequisites
|
|
||||||
resolve_services
|
|
||||||
|
|
||||||
# Execute deployment steps
|
|
||||||
build_services
|
|
||||||
build_and_push_images
|
|
||||||
configure_kubectl
|
|
||||||
deploy_to_kubernetes
|
|
||||||
perform_health_checks
|
|
||||||
|
|
||||||
log_success "🎉 Deployment completed successfully!"
|
|
||||||
|
|
||||||
if [[ "$DRY_RUN" == false ]]; then
|
|
||||||
log_info "You can check the deployment status with:"
|
|
||||||
log_info " kubectl get pods -n $AKS_NAMESPACE"
|
|
||||||
log_info " kubectl get services -n $AKS_NAMESPACE"
|
|
||||||
log_info " kubectl get ingress -n $AKS_NAMESPACE"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# Execute main function if script is run directly
|
|
||||||
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
|
||||||
main "$@"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Kustomize 설치 확인
|
||||||
|
if ! command -v kustomize &> /dev/null; then
|
||||||
|
echo "📦 Installing Kustomize..."
|
||||||
|
curl -s "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh" | bash
|
||||||
|
sudo mv kustomize /usr/local/bin/
|
||||||
|
fi
|
||||||
|
|
||||||
|
# kubectl 연결 확인
|
||||||
|
if ! kubectl cluster-info &> /dev/null; then
|
||||||
|
echo "❌ Error: Unable to connect to Kubernetes cluster"
|
||||||
|
echo "Please ensure kubectl is configured properly"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 네임스페이스 생성
|
||||||
|
echo "🔧 Creating namespace phonebill-${ENVIRONMENT} if not exists..."
|
||||||
|
kubectl create namespace phonebill-${ENVIRONMENT} --dry-run=client -o yaml | kubectl apply -f -
|
||||||
|
|
||||||
|
# 환경별 디렉토리로 이동
|
||||||
|
cd deployment/cicd/kustomize/overlays/${ENVIRONMENT}
|
||||||
|
|
||||||
|
echo "🏷️ Updating image tags..."
|
||||||
|
|
||||||
|
# 각 서비스 이미지 태그 업데이트
|
||||||
|
kustomize edit set image acrdigitalgarage01.azurecr.io/phonebill/api-gateway:${ENVIRONMENT}-${IMAGE_TAG}
|
||||||
|
kustomize edit set image acrdigitalgarage01.azurecr.io/phonebill/user-service:${ENVIRONMENT}-${IMAGE_TAG}
|
||||||
|
kustomize edit set image acrdigitalgarage01.azurecr.io/phonebill/bill-service:${ENVIRONMENT}-${IMAGE_TAG}
|
||||||
|
kustomize edit set image acrdigitalgarage01.azurecr.io/phonebill/product-service:${ENVIRONMENT}-${IMAGE_TAG}
|
||||||
|
kustomize edit set image acrdigitalgarage01.azurecr.io/phonebill/kos-mock:${ENVIRONMENT}-${IMAGE_TAG}
|
||||||
|
|
||||||
|
echo "📋 Applying Kubernetes manifests..."
|
||||||
|
|
||||||
|
# 배포 실행
|
||||||
|
kubectl apply -k .
|
||||||
|
|
||||||
|
echo "⏳ Waiting for deployments to be ready..."
|
||||||
|
|
||||||
|
# 배포 상태 확인
|
||||||
|
kubectl rollout status deployment/${ENVIRONMENT}-api-gateway -n phonebill-${ENVIRONMENT} --timeout=300s
|
||||||
|
kubectl rollout status deployment/${ENVIRONMENT}-user-service -n phonebill-${ENVIRONMENT} --timeout=300s
|
||||||
|
kubectl rollout status deployment/${ENVIRONMENT}-bill-service -n phonebill-${ENVIRONMENT} --timeout=300s
|
||||||
|
kubectl rollout status deployment/${ENVIRONMENT}-product-service -n phonebill-${ENVIRONMENT} --timeout=300s
|
||||||
|
kubectl rollout status deployment/${ENVIRONMENT}-kos-mock -n phonebill-${ENVIRONMENT} --timeout=300s
|
||||||
|
|
||||||
|
echo "🔍 Health Check..."
|
||||||
|
|
||||||
|
# API Gateway Health Check
|
||||||
|
GATEWAY_POD=$(kubectl get pod -n phonebill-${ENVIRONMENT} -l app=api-gateway -o jsonpath='{.items[0].metadata.name}')
|
||||||
|
if kubectl -n phonebill-${ENVIRONMENT} exec $GATEWAY_POD -- curl -f http://localhost:8080/actuator/health > /dev/null 2>&1; then
|
||||||
|
echo "✅ API Gateway is healthy!"
|
||||||
|
else
|
||||||
|
echo "⚠️ API Gateway health check failed, but deployment completed"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "✅ Deployment completed successfully!"
|
||||||
|
echo ""
|
||||||
|
echo "📊 Deployment Status:"
|
||||||
|
kubectl get pods -n phonebill-${ENVIRONMENT} -l app=phonebill
|
||||||
|
echo ""
|
||||||
|
echo "🌐 Services:"
|
||||||
|
kubectl get services -n phonebill-${ENVIRONMENT}
|
||||||
|
echo ""
|
||||||
|
echo "🔗 Ingress:"
|
||||||
|
kubectl get ingress -n phonebill-${ENVIRONMENT}
|
||||||
|
echo ""
|
||||||
|
echo "🎯 Environment: ${ENVIRONMENT}"
|
||||||
|
echo "🏷️ Image Tag: ${ENVIRONMENT}-${IMAGE_TAG}"
|
||||||
Loading…
x
Reference in New Issue
Block a user