Skip to content

Commit 76224ac

Browse files
committed
ci: 배포후 downtime 0로 변경 (health check 추가)
1 parent 41c0886 commit 76224ac

File tree

4 files changed

+62
-13
lines changed

4 files changed

+62
-13
lines changed

.github/workflows/deploy-staging.yml

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -99,32 +99,50 @@ jobs:
9999
}' \
100100
${{ secrets.DISCORD_WEBHOOK_URL }}
101101
102-
- name: Deploy to K3s (On-premise)
102+
- name: Deploy to K3s (On-premise) with Zero-Downtime
103+
id: deploy
103104
uses: appleboy/ssh-action@v1.0.3
104105
with:
105106
host: ${{ secrets.SSH_HOST }}
106107
username: ${{ secrets.SSH_USER }}
107108
password: ${{ secrets.SSH_PASSWORD }}
108109
port: ${{ secrets.SSH_PORT }}
109110
script: |
110-
echo "🚀 Starting deployment to K3s..."
111+
echo "🚀 Starting Zero-Downtime deployment to K3s..."
111112
112113
# K3s deployment 디렉토리로 이동
113114
cd /home/yapp/k3s/staging
114115
115-
# 이미지 태그 업데이트 (deployment.yaml에서 이미지 태그 변경)
116-
sed -i "s|image: .*yapp-staging:.*|image: ${{ secrets.DOCKER_USERNAME }}/yapp-dev:${{ steps.version.outputs.version }}-${{ steps.version.outputs.short_sha }}|g" deployment.yaml
116+
# 현재 실행 중인 이미지 확인
117+
CURRENT_IMAGE=$(kubectl get deployment yapp-app-staging -n staging -o jsonpath='{.spec.template.spec.containers[0].image}' 2>/dev/null || echo "none")
118+
echo "📌 Current image: $CURRENT_IMAGE"
117119
118-
# K3s에 배포 적용
119-
kubectl apply -f deployment.yaml
120-
121-
# Pod 강제 재시작 (새 이미지 pull 보장)
122-
kubectl rollout restart deployment/yapp-app-staging -n staging
120+
# 이미지 태그 업데이트
121+
NEW_IMAGE="${{ secrets.DOCKER_USERNAME }}/yapp-dev:${{ steps.version.outputs.version }}-${{ steps.version.outputs.short_sha }}"
122+
echo "📦 New image: $NEW_IMAGE"
123+
sed -i "s|image: .*yapp-dev:.*|image: $NEW_IMAGE|g" deployment.yaml
123124
124-
# 배포 상태 확인
125-
kubectl rollout status deployment/yapp-app-staging -n staging --timeout=5m
125+
# K3s에 배포 적용 (Rolling Update 자동 실행)
126+
echo "🔄 Applying deployment... Rolling update will start automatically"
127+
kubectl apply -f deployment.yaml
126128
127-
echo "✅ Deployment completed successfully!"
129+
# 배포 상태 확인 (새 파드가 완전히 준비될 때까지 대기)
130+
echo "⏳ Waiting for new pods to be ready..."
131+
if kubectl rollout status deployment/yapp-app-staging -n staging --timeout=10m; then
132+
echo "✅ Rollout completed successfully!"
133+
134+
# 배포된 파드 정보 확인
135+
echo "📊 Pod status:"
136+
kubectl get pods -n staging -l app=yapp-app-staging
137+
138+
# 최종 이미지 확인
139+
DEPLOYED_IMAGE=$(kubectl get deployment yapp-app-staging -n staging -o jsonpath='{.spec.template.spec.containers[0].image}')
140+
echo "✅ Deployment completed! Deployed image: $DEPLOYED_IMAGE"
141+
else
142+
echo "❌ Rollout failed or timed out!"
143+
kubectl get pods -n staging -l app=yapp-app-staging
144+
exit 1
145+
fi
128146
129147
- name: Discord notification - Deployment success
130148
if: success()

build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ dependencies {
4545
// Spring Boot
4646
implementation("org.springframework.boot:spring-boot-starter-web")
4747
implementation("org.springframework.boot:spring-boot-starter-validation")
48+
implementation("org.springframework.boot:spring-boot-starter-actuator")
4849

4950
// Kotlin
5051
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")

src/main/kotlin/com/yapp2app/auth/infra/security/config/SecurityConfig.kt

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,19 @@ import org.springframework.web.cors.CorsConfigurationSource
1717
@EnableWebSecurity
1818
class SecurityConfig(private val corsConfigurationSource: CorsConfigurationSource) {
1919

20+
/**
21+
* Actuator Health Check 엔드포인트 보안 설정 (Kubernetes Probe용)
22+
*/
2023
@Bean
2124
@Order(0)
25+
fun actuatorSecurityFilterChain(http: HttpSecurity): SecurityFilterChain =
26+
http.securityMatcher("/actuator/health/**")
27+
.csrf { it.disable() }
28+
.authorizeHttpRequests { it.anyRequest().permitAll() }
29+
.build()
30+
31+
@Bean
32+
@Order(1)
2233
fun documentSecurityFilterChain(http: HttpSecurity): SecurityFilterChain =
2334
http.securityMatcher("/swagger-ui/**", "/v3/api-docs/**")
2435
.csrf { it.disable() }
@@ -28,7 +39,7 @@ class SecurityConfig(private val corsConfigurationSource: CorsConfigurationSourc
2839
.build()
2940

3041
@Bean
31-
@Order(1)
42+
@Order(2)
3243
fun apiSecurityFilterChain(
3344
http: HttpSecurity,
3445
jwtAuthenticationFilter: JwtAuthenticationFilter,

src/main/resources/application.yaml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,25 @@ spring:
77
springdoc:
88
override-with-generic-response: false
99

10+
# Spring Boot Actuator 설정 (Health Check for Kubernetes)
11+
management:
12+
endpoints:
13+
web:
14+
exposure:
15+
include: health,info
16+
base-path: /actuator
17+
endpoint:
18+
health:
19+
probes:
20+
enabled: true
21+
show-details: when-authorized
22+
health:
23+
livenessstate:
24+
enabled: true
25+
readinessstate:
26+
enabled: true
27+
28+
1029
aws:
1130
s3:
1231
access-key: ${AWS_S3_ACCESS_KEY:}

0 commit comments

Comments
 (0)