Spring Boot μ ν리μΌμ΄μ k8s νκ²½μμ WarmUp μ μ©νκΈ°
μ΄ κΈμ μμ±νλ μ΄μ
νμ¬ νμ¬μμλ EKS κΈ°λ° k8s νκ²½μμ spring boot λ₯Ό ν¬ν¨ κ°μ’ νλ μμν¬ μ ν리μΌμ΄μ μ μ΄μ μ€μ΄λ€.
κΈ°μ‘΄ λͺ¨λ Έλ¦¬μ€ Django Rest Framework μμ μ μ§μ μΌλ‘
λλ©μΈ λΆλ¦¬λ₯Ό Spring Boot λ₯Ό μ΄μ©ν Micro Service Application μΌλ‘ μ§ν μ€μΈλ°,
λ΄κ° λ§‘μ κ²°μ λλ©μΈ κ΄λ ¨ μ ν리μΌμ΄μ λ λ§μ°¬κ°μ§μ΄λ€. (Kotlin κΈ°λ° Spring Boot App)
λ¬Έμ λ, κ²°μ κ° μ£Όλ¬Έ λ° μ κ΄ DB μ λ무 κ°νκ² μ½ν μμ΄μ λ°λΌλ³΄μμΌ νλ ν μ΄λΈμ΄ λ§λ€λ μ μ΄κ³ ,
μ΄κ²μ κ³§ λ°°ν¬ μ§ν μλμ 컀λ€λ μν₯μ λΌμ³€λ€.
JVM μ μ»΄νμΌ λ .class νμΌμ νμ μ ν΄λμ€ λ‘λ©μ ν΅ν΄ μ¬μ©νλ©°,
κΈ°λ³Έμ μΌλ‘ μΈν°νλ¦¬ν° λ°©μμ μ¬μ©νλ―λ‘ Jit Compiler λ₯Ό μ¬μ©νκΈ°κΉμ§ μ¬λ¬ λ²μ νΈμΆμ΄ νμνλ€.
μ΄ κ²μ΄ κ·Όλ³Έμ μΈ λ¬Έμ λ€.
λ΄κ° μ΅κ·Όμ κ°λ°νλ API λ κ²°μ λ΄μ μ μ¬ API μ΄λ€.
νλμ API λ‘ κ±°μ§ 20κ° κ°λμ ν μ΄λΈλ€μ read, write νλλ°, λ°°ν¬ μ§ν ν μ€νΈλ₯Ό νλ©΄ νμ 40 ~ 50 μ΄ λ (1λΆ λλ κ²λ μμμ..) λ₯Ό κΈ°λ‘νλ€. => λκ° μ΄μνλ€ μκ°νλ€.
μ²μμ λ΄ μ½λλ₯Ό μμ¬νκ³ , Data Dog Span μ ν΅ν΄ νμΈν΄λ³Έ κ²°κ³Ό λ¬Έμ λ λ€λ₯Έ λΆλΆμ μμλ€.
(λ¬Όλ‘ λ΄ μ½λ λν μ²μ JVM λ©λͺ¨λ¦¬μ λ‘λ λλλ° μκ°μ΄ κ±Έλ Έλ€.)
μΈλ±μ€λ₯Ό νλ λ¨μ μ‘°ν 쿼리λ, κ°λ¨ν Insert 쿼리 μ€ν latency κ° λΉμ μμ μΌλ‘ κΈΈλ€λ κ²μ νμΈνλ€.
νΉν μ‘°ν 쿼리μ κ²½μ°, μ ν리μΌμ΄μ μ½λμ μ μ©νκΈ° μ , νμ μ€ν κ³νμ ν΅ν΄ νμΈ ν μμ±μ νλ€.
JPA λ₯Ό μ¬μ©νλ―λ‘, μ΄μ©λ©΄ μΊμ±μ΄ λμ§ μμ λ°μ΄ν°λ₯Ό μ‘°ννκΈ° μν΄ λμ€ν¬μ μ§μ μ μΌλ‘ IO μ§ννλ―λ‘, μ΄ λΆλΆμ λν΄μ λ¬Έμ λ₯Ό νμ νκ² λμκ³ , hibernate μ½λ λν μ²μ μΈν°ν리ν°λ‘ μ€ν ν λ©λͺ¨λ¦¬μ λ‘λ λλ―λ‘, ν λͺ«μ νλ¦¬λΌ μκ°νλ€.
"WarmUp" μ΄λ κ°λ μ μλμ΄ κ°λ°μ λΆκ» λ€μκ³ ,
API Controller μ½λ λΆν° μ§ννλ©΄ μ’κ² μ§λ§ νμν μν λ°μ΄ν°κ° λ°©λνλ―λ‘,
Latency κ° κ°μ₯ ν° DB IO λ¨ Repository λΆλΆλ§ μμ
μ μ μ©νκΈ°λ‘ νλ€.
WarmUp λ₯Ό μ μ©νλ κ³Όμ
κΈ°λ³Έμ μΌλ‘, Spring Boot κΈ°λ° Micro Service λ₯Ό μ΄μ μ€μ΄λ©΄,
Spring μμ μ 곡νλ Actuator λ₯Ό μ¬μ©νκ³ μμ κ²μ΄λ€.
μ‘μΈμμ΄ν°λ μ ν리μΌμ΄μ μ λͺ¨λν°λ§κ³Ό λ§€νΈλ¦ (μλΉμ€μ μ±λ₯ μΈ‘μ μ μ¬μ©λλ νλͺ©μ΄λ μ§ν) κ°μ κΈ°λ₯μ http or JMX μλν¬μΈνΈλ₯Ό ν΅ν΄μ μ 곡νλ€.
π Spring boot Actuator μ€νλ§ λΆνΈ μ‘μΆμμ΄ν° API + Spring Cloudλ₯Ό μ¬μ©ν μμ
Spring Actuatorμ Spring Cloudμ μ΄ν΄ π
velog.io
=> μμΈν 건 μ΄ λΈλ‘κ·Έλ₯Ό μ°Έμ‘°ν΄λ³΄λΌ~
μ°λ¦¬ Spring Boot Application μ κ²½μ° actuator λ₯Ό νμ©ν μ§νλ₯Ό μμ²νκΈ° μν΄ νμν μ€μ μ μλμ κ°μ΄ μ€μ νλ€.
management:
endpoints:
web:
base-path: "/"
λ§μ½ health μνλ₯Ό νμΈνκ³ μΆλ€λ©΄,
(λλ©μΈ νΈμ€νΈ)/health
-> μλ° μμΌλ‘ μμ²νλ©΄ λλ€~
K8s λ₯Ό νμ© μ€μ΄λΌλ©΄,
μλμ κ°μ΄ StartupProbe λ₯Ό ν΅ν΄ Health check λ₯Ό μ§νν μ μλ€,
Probe λ 컨ν μ΄λμμ kubelet μ μν΄ μ£ΌκΈ°μ μΌλ‘ μνλλ μ§λ¨μΈλ°,
μ¬λ¬ Probe λ₯Ό ν΅ν΄ κ° μ»¨ν μ΄λμ μνλ₯Ό μ£ΌκΈ°μ μΌλ‘ μ²΄ν¬ ν 컨ν μ΄λμ μ¬μμμ΄λ λ¬Έμ κ° μμ κ²½μ° μλΉμ€μμ μ μΈ μν¬ μ μλ€.
μ΄ μ€, StartUp Probe μ κ²½μ°, 컨ν μ΄λμ μ ν리μΌμ΄μ μ΄ μμ λμλμ§ λνλΈλ€.
μ±κ³΅ν λκΉμ§ λλ¨Έμ§ Probe λ νμ±ν λμ§ μκ³ , μ€ν¨νλ©΄, kubelet μ 컨ν μ΄λλ₯Ό μ£½μ΄κ³ ,
μ¬μμ μ μ± μ λ°λΌ μ²λ¦¬κ° λλ€.
κ° Probe κ° κΆκΈνλ€λ©΄?
https://velog.io/@hoonki/%EC%BF%A0%EB%B2%84%EB%84%A4%ED%8B%B0%EC%8A%A4-Probe
μΏ λ²λ€ν°μ€ - Probe (Liveness, Readiness, Startup)
μ€λμ μΏ λ²λ€ν°μ€μ ProbeλΌλ κ°λ μ λν΄ μ 리ν΄λ³΄κ³ μνλ€. μμ μ μ¬λ΄ dev ν΄λ¬μ€ν° κ΅¬μΆ μ probe λΌλ κ°λ μ΄ yamlμ μ νμμ΄ μ΄κ²μ΄ 무μμΈμ§ κΆκΈν΄μ μ°Ύμ보μλ€. Probeλ? Probeλ 컨ν μ΄
velog.io
=> μ¬κΈ°λ₯Ό μ°Έκ³ νμ~
μ°λ¦¬ StartUp Probe μ€μ μ κ²½μ° μλμ κ°λ€. (μ€μ μ€μ μ΄ μλ μμμ΄λ€ γ γ )
startupProbe:
httpGet:
path: /health/readiness
port: http
failureThreshold: 100
periodSeconds: 5
5μ΄λ§λ€ νλ² μ© μ€ν λλ StartUp Probe λ 100 λ²μ μ€ν¨
μ¦, 500 μ΄ λμ 컨ν μ΄λκ° μμ λλ λμ μλ΅μ μ λλ‘ νμ§ μμ κ²½μ° μ€ν¨νλ€.
μ΄ μλν¬μΈνΈμ μμ μ½λλ₯Ό μ μ©ν κ²μ΄λ€.
Warm Up μ½λ μ μ©
μμ μ μ μ©νκΈ° μν΄μλ Spring Framework μ HealthIndicator λ₯Ό μμλ°μ ꡬνν΄μΌ νλλ°,
@Component
class WarmupHealthIndicator(
private val warmer: Warmer,
) : AbstractHealthIndicator() {
override fun doHealthCheck(builder: Health.Builder) {
warmer.run()
if (warmer.isDone) {
builder.up()
} else {
builder.down()
}
}
}
abstract class ExactlyOnceRunWarmer : Warmer {
override var isDone: Boolean = false
private val mutex = Mutex()
override fun run() {
if (!isDone && mutex.tryLock()) {
try {
doRun()
setDone()
} finally {
mutex.unlock()
}
}
}
protected fun setDone() {
this.isDone = true
}
abstract fun doRun()
}
@Component
class PaymentCustomWarmer(
private val paymentDataWarmer: PaymentDataWarmer,
) : ExactlyOnceRunWarmer() {
override fun run() {
paymentDataWarmer.dataWarmUp() // μ€μ μμ
μ΄ νμν μ½λλ₯Ό μμ±νλ€.
}
}
μμ κ°λ€.
Exactly Once Warmer λΆλΆμ κ²½μ°,
Line 곡μ κΈ°μ λΈλ‘κ·Έλ₯Ό μ°¨μ©νλ€. (κ°μ¬ν©λλ€..)
https://engineering.linecorp.com/ko/blog/apply-warm-up-in-spring-boot-and-kubernetes
Spring Boot + Kubernetes κΈ°λ°μμ μμ μ μ©νκΈ°
μλ νμΈμ. LINE+ ABC Studio νμμ λ°±μλ κ°λ°μ νλ λ°μμ, λ°μ ν¬μ λλ€. νμ¬ μΌλ³Έμμ μ΄μνλ λ°°λ¬ μλΉμ€ 'λ°λ§μμΉΈ(Demaecan, εΊε逨)' νλ‘λνΈμμ μ ν¬ λͺ©λ‘μ μ 곡νλ μλΉμ€λ₯Ό κ°λ°
engineering.linecorp.com
=> κΆκΈνλ€λ©΄ μ°Έκ³ ~
μ΄λ°μμΌλ‘ μ§ννλ©΄ λμ΄λ€~ κ°λ¨νμ§ μμκ°?
(μμ μ½λ λ Έκ°λ€λ λ§ μν΄λ μμμ£ ..?)
μ€μ λ‘ λ°°ν¬λ₯Ό μ§ννμκ³ ,
Start Up Probe κ° μ€ν λλ©° μμ μ μ μ©ν μΏΌλ¦¬κ° μ€ν λλ κ²μ νμΈν μ μμλ€.
쿼리 μ€νμ΄ μλ£ λλ©΄ μ€μ λ‘ νΈλν½μ λ°μ μ μκ² λλ€.
κ²°κ³Ό
DataDog μ ν΅ν΄ Latency λ₯Ό λΉκ΅ν΄λ³΄μλ€.
=> μμ μ μ© μ 첫 μμ².. μ²μ°Έ..
=> μμ μ μ© ν 첫 μμ², νμ°νκ² μ°¨μ΄κ° λλ κ²μ νμΈν μ μλ€.
κ²°λ‘
μ€μ λ‘ Warm up μ μ§ννλ©΄μ,
k8s μ Probe κ°λ μ λ λͺ νν νκ² λ¨κ³Ό λμμ, JVM μ΄ μ μΌλ§λ λλ¦°μ§ μ²΄κ°ν μ μμλ€.
νμ¬ νλ‘μ νΈλ₯Ό μ§ννλ©΄μ, λμ΄λλ κ°μ₯ λμκ³Ό λμμ, λ°°μ°λ κ² μ λ§ λ§λ€λ κ²μ λλλ€.
μμΌλ‘ λ κ²°μ κ΄λ ¨ API κ° λΆλ¦¬λ μμ μΈλ°,
리μμ€ κ΄λ ¨ν΄μ λ λ¬Έμ μ λ΄μ°©νκ² λ κ² κ°κ³ (γ γ ) 곡λΆλ₯Ό λ μ§ννκ² λ κ² κ°λ€.
μ΅κ·Ό μ£Όλ§ λ΄λ΄ μμ νλ©° μ΄λ €μ μ§λ§,
μ΄λ² κΈ°νμ νλ₯ν μμ°μ μ»κ² λμκ³ , μ°¨ν λμΌν λ¬Έμ μ μ§λ©΄ν JVM κΈ°λ° μ ν리μΌμ΄μ μ λμΌνκ² μ μ©νλ μ΄λλ°μ΄μ€λ₯Ό μ€ μ μμ κ² κ°λ€!