🚀 Déploiement
📖 Qu'est-ce que le Déploiement ?
Le déploiement est le processus d'exécution de votre application développée dans un environnement de production réel. Avec Docker, vous pouvez l'exécuter de manière identique partout !
💡 Construction JAR
Configuration de build.gradle.kts
plugins {
application
kotlin("jvm") version "1.9.0"
id("io.ktor.plugin") version "2.3.5"
}
application {
mainClass.set("com.example.ApplicationKt")
}
ktor {
fatJar {
archiveFileName.set("app.jar")
}
}
Commande de Construction
# JAR 파일 생성
./gradlew buildFatJar
# 실행
java -jar build/libs/app.jar
🎯 Configuration d'Environnement
application.conf
ktor {
deployment {
port = 8080
port = ${?PORT} # 환경 변수 우선
}
application {
modules = [ com.example.ApplicationKt.module ]
}
}
database {
url = "jdbc:postgresql://localhost:5432/mydb"
url = ${?DATABASE_URL}
user = "admin"
user = ${?DATABASE_USER}
password = "password"
password = ${?DATABASE_PASSWORD}
}
Utilisation des Variables d'Environnement
fun Application.module() {
val dbUrl = environment.config.property("database.url").getString()
val dbUser = environment.config.property("database.user").getString()
val dbPassword = environment.config.property("database.password").getString()
Database.connect(
url = dbUrl,
user = dbUser,
password = dbPassword
)
routing {
get("/health") {
call.respondText("OK")
}
}
}
🐳 Déploiement Docker
Dockerfile
# 1단계: 빌드
FROM gradle:8.4-jdk17 AS build
WORKDIR /app
COPY build.gradle.kts settings.gradle.kts ./
COPY src ./src
RUN gradle buildFatJar --no-daemon
# 2단계: 실행
FROM openjdk:17-jdk-slim
WORKDIR /app
COPY --from=build /app/build/libs/app.jar ./app.jar
EXPOSE 8080
CMD ["java", "-jar", "app.jar"]
docker-compose.yml
version: '3.8'
services:
app:
build: .
ports:
- "8080:8080"
environment:
- DATABASE_URL=jdbc:postgresql://db:5432/mydb
- DATABASE_USER=admin
- DATABASE_PASSWORD=secret
depends_on:
- db
db:
image: postgres:15
environment:
- POSTGRES_DB=mydb
- POSTGRES_USER=admin
- POSTGRES_PASSWORD=secret
volumes:
- db-data:/var/lib/postgresql/data
ports:
- "5432:5432"
volumes:
db-data:
Exécution
# 빌드 및 실행
docker-compose up -d
# 로그 확인
docker-compose logs -f app
# 중지
docker-compose down
🔧 Configuration de Production
Journalisation
import org.slf4j.LoggerFactory
val logger = LoggerFactory.getLogger("Application")
fun Application.module() {
logger.info("애플리케이션 시작")
routing {
get("/") {
logger.info("/ 요청")
call.respondText("Hello!")
}
}
}
Configuration CORS
import io.ktor.server.plugins.cors.routing.*
fun Application.module() {
install(CORS) {
allowHost("myapp.com", schemes = listOf("https"))
allowHeader(HttpHeaders.ContentType)
allowMethod(HttpMethod.Options)
allowMethod(HttpMethod.Get)
allowMethod(HttpMethod.Post)
allowCredentials = true
}
}
Compression
import io.ktor.server.plugins.compression.*
fun Application.module() {
install(Compression) {
gzip {
priority = 1.0
}
deflate {
priority = 10.0
minimumSize(1024)
}
}
}
🛡️ Sécurité
Configuration HTTPS
embeddedServer(Netty, environment = applicationEngineEnvironment {
connector {
port = 8080
}
sslConnector(
keyStore = KeyStore.getInstance("PKCS12").apply {
load(FileInputStream("keystore.p12"), "password".toCharArray())
},
keyAlias = "myalias",
keyStorePassword = { "password".toCharArray() },
privateKeyPassword = { "password".toCharArray() }
) {
port = 8443
}
}).start(wait = true)
Limitation de Débit (Version Simple)
class RateLimiter {
private val requests = mutableMapOf<String, MutableList<Long>>()
private val limit = 100 // 100 requests
private val windowMs = 60_000L // per minute
fun isAllowed(key: String): Boolean {
val now = System.currentTimeMillis()
val userRequests = requests.getOrPut(key) { mutableListOf() }
// 오래된 요청 제거
userRequests.removeIf { it < now - windowMs }
return if (userRequests.size < limit) {
userRequests.add(now)
true
} else {
false
}
}
}
fun Application.module() {
val rateLimiter = RateLimiter()
routing {
intercept(ApplicationCallPipeline.Call) {
val ip = call.request.origin.remoteHost
if (!rateLimiter.isAllowed(ip)) {
call.respond(HttpStatusCode.TooManyRequests)
finish()
}
}
get("/api/data") {
call.respondText("데이터")
}
}
}
🎯 Surveillance
Contrôle de Santé
fun Application.module() {
routing {
get("/health") {
val dbHealth = try {
transaction {
exec("SELECT 1") {}
}
"ok"
} catch (e: Exception) {
"error"
}
call.respond(
mapOf(
"status" to "ok",
"database" to dbHealth,
"timestamp" to System.currentTimeMillis()
)
)
}
}
}
Métriques
class Metrics {
private val requestCount = AtomicInteger(0)
private val errorCount = AtomicInteger(0)
fun incrementRequests() = requestCount.incrementAndGet()
fun incrementErrors() = errorCount.incrementAndGet()
fun getMetrics() = mapOf(
"requests" to requestCount.get(),
"errors" to errorCount.get()
)
}
fun Application.module() {
val metrics = Metrics()
routing {
intercept(ApplicationCallPipeline.Monitoring) {
metrics.incrementRequests()
try {
proceed()
} catch (e: Exception) {
metrics.incrementErrors()
throw e
}
}
get("/metrics") {
call.respond(metrics.getMetrics())
}
}
}
🔥 Exemple de Déploiement Réel
Configuration Complète
// Application.kt
fun main(args: Array<String>) {
EngineMain.main(args)
}
fun Application.module() {
// 설정 로드
val config = environment.config
// 로깅
install(CallLogging) {
level = Level.INFO
}
// CORS
install(CORS) {
allowHost(config.property("cors.host").getString())
allowCredentials = true
}
// 압축
install(Compression)
// JSON
install(ContentNegotiation) {
json()
}
// 에러 처리
install(StatusPages) {
exception<Throwable> { call, cause ->
log.error("Unhandled exception", cause)
call.respond(
HttpStatusCode.InternalServerError,
mapOf("error" to "Internal server error")
)
}
}
// 데이터베이스
configureDatabases(config)
// 라우팅
configureRouting()
}
fun Application.configureDatabases(config: ApplicationConfig) {
val url = config.property("database.url").getString()
val user = config.property("database.user").getString()
val password = config.property("database.password").getString()
Database.connect(
url = url,
user = user,
password = password
)
}
🤔 Questions Fréquemment Posées
Q1. Où dois-je déployer ?
R : Les services cloud sont recommandés !
# AWS (Elastic Beanstalk, ECS)
# Google Cloud (Cloud Run)
# Azure (App Service)
# DigitalOcean (App Platform)
# Heroku
# Docker만 있으면 어디든 가능!
Q2. Qu'en est-il du déploiement sans interruption ?
R : Déploiement Blue-Green ou Rolling Update !
# docker-compose.yml (Rolling Update)
version: '3.8'
services:
app:
image: myapp:latest
deploy:
replicas: 3
update_config:
parallelism: 1
delay: 10s
Q3. Comment gérer les configurations spécifiques à l'environnement ?
R : Utilisez des profils !
// application-dev.conf
ktor {
deployment {
port = 8080
}
}
// application-prod.conf
ktor {
deployment {
port = 80
}
}
// 실행
java -jar app.jar -config=application-prod.conf
🎬 Conclusion
Pour un déploiement sûr et efficace !
핵심 정리:
✅ JAR 빌드로 실행 파일
✅ Docker로 환경 통일
✅ 환경 변수로 설정 분리
✅ Health Check로 모니터링
✅ 보안 설정 필수
Félicitations ! Vous avez terminé la série Backend et toute la documentation Kotlin ! 🎉
Maintenant, vous pouvez faire du développement full-stack avec Kotlin !
Révision de toute la série :
- Basics : Fondamentaux de Kotlin
- Practical : Application pratique (fonctions d'extension, exceptions, fichiers, expressions régulières, DSL)
- Coroutines : Programmation asynchrone
- Testing : Écriture de tests
- Backend : Développement de serveur et déploiement