23 Gesamt-Deployment mit Artefakten aus Unterprojekten

23.1 Deployment-Orchestrierung in Multi-Modul-Architekturen

Das Deployment eines Multi-Projekt-Systems erfordert die Koordination verschiedener Artefakte, die zusammen eine funktionsfähige Anwendung bilden. Die Orchestrierung umfasst die Sammlung aller relevanten Build-Artefakte, deren Versionierung, die Definition der Deployment-Reihenfolge und die Verwaltung von Abhängigkeiten zwischen Komponenten. Ein typisches Enterprise-System besteht aus Backend-Services, Frontend-Anwendungen, Datenbank-Migrationen und Konfigurationsdateien, die koordiniert deployed werden müssen.

Die Deployment-Strategie hängt von der System-Architektur ab. Monolithische Deployments packen alle Komponenten in ein einzelnes Deployment-Artefakt, was die Koordination vereinfacht aber die Flexibilität reduziert. Microservice-Architekturen deployen jedes Modul unabhängig, was granulare Updates ermöglicht aber komplexere Orchestrierung erfordert. Hybrid-Ansätze gruppieren zusammengehörige Module in Deployment-Units, die gemeinsam versioniert und deployed werden.

Gradle implementiert Deployment-Orchestrierung durch Task-Dependencies und Custom Tasks. Ein zentraler Deployment-Task koordiniert die Erstellung aller Artefakte, deren Validierung und den Upload zu Deployment-Zielen. Die Task-Konfiguration definiert die Deployment-Reihenfolge basierend auf Abhängigkeiten zwischen Modulen. Database-Migrationen laufen vor Service-Deployments, Configuration-Updates vor Application-Starts.

23.2 Artefakt-Sammlung und Aggregation

Die systematische Sammlung aller Deployment-Artefakte bildet die Grundlage für koordiniertes Deployment. Gradle Configurations definieren Artefakt-Sammlungen, die von verschiedenen Subprojekten befüllt werden. Eine deploymentArtifacts Configuration aggregiert alle zu deployenden Dateien, unabhängig von deren Typ oder Ursprung. Diese Abstraktion ermöglicht einheitliche Behandlung heterogener Artefakte.

// Root build.gradle.kts
val deploymentArtifacts by configurations.creating {
    isCanBeConsumed = false
    isCanBeResolved = true
}

val deploymentMetadata by configurations.creating {
    isCanBeConsumed = false
    isCanBeResolved = true
}

// In Subprojekten
configurations {
    register("deployable") {
        isCanBeConsumed = true
        isCanBeResolved = false
    }
}

artifacts {
    add("deployable", tasks.named("bootJar"))  // Spring Boot Jar
}

// Root-Projekt Dependencies
dependencies {
    deploymentArtifacts(project(":service-a", "deployable"))
    deploymentArtifacts(project(":service-b", "deployable"))
    deploymentArtifacts(project(":frontend", "deployable"))
    deploymentMetadata(project(":config", "deployable"))
}

Die Artefakt-Transformation bereitet gesammelte Dateien für das Deployment vor. Renaming vereinheitlicht Dateinamen gemäß Deployment-Konventionen. Packaging kombiniert mehrere Artefakte in Archive oder Container. Enrichment fügt Metadaten wie Build-Informationen, Git-Commits oder Timestamps hinzu. Diese Transformationen erfolgen in Gradle Tasks, die zwischen Sammlung und Deployment geschaltet sind.

Manifest-Generierung dokumentiert den Inhalt eines Deployments. Das Manifest listet alle enthaltenen Artefakte mit Versionen, Checksums und Dependencies. Diese Dokumentation ermöglicht Nachvollziehbarkeit von Deployments und unterstützt Rollback-Szenarien. Das Manifest-Format kann JSON, YAML oder XML sein, abhängig von den Requirements der Deployment-Infrastruktur.

23.3 Umgebungsspezifische Deployment-Konfiguration

Verschiedene Deployment-Umgebungen erfordern angepasste Konfigurationen und Artefakt-Sets. Development-Deployments enthalten Debug-Features und verwenden Test-Datenbanken. Staging-Deployments simulieren Produktion mit reduzierten Ressourcen. Production-Deployments optimieren für Performance und Sicherheit. Gradle verwaltet diese Unterschiede durch umgebungsspezifische Tasks und Configurations.

Property-basierte Konfiguration externalisiert umgebungsspezifische Parameter. Gradle Properties definieren Target-URLs, Credentials und Feature-Flags pro Umgebung. Diese Properties werden zur Build-Zeit in Artefakte injiziert oder als separate Konfigurationsdateien deployed. Die Separation von Code und Konfiguration folgt dem Twelve-Factor-App-Prinzip und ermöglicht sichere Credential-Verwaltung.

// Umgebungsspezifische Deployment Tasks
val environments = mapOf(
    "dev" to DeploymentConfig(
        url = "https://dev.deploy.company.com",
        credentials = "dev-credentials",
        features = setOf("debug", "profiling")
    ),
    "staging" to DeploymentConfig(
        url = "https://staging.deploy.company.com",
        credentials = "staging-credentials",
        features = setOf("monitoring")
    ),
    "production" to DeploymentConfig(
        url = "https://prod.deploy.company.com",
        credentials = "prod-credentials",
        features = setOf("monitoring", "alerting")
    )
)

environments.forEach { (env, config) ->
    tasks.register<DeployTask>("deployTo${env.capitalize()}") {
        description = "Deploy all artifacts to $env environment"
        group = "deployment"
        
        artifacts.from(deploymentArtifacts)
        targetUrl.set(config.url)
        credentials.set(providers.environmentVariable(config.credentials))
        enabledFeatures.set(config.features)
        
        // Umgebungsspezifische Validierung
        doFirst {
            if (env == "production") {
                require(project.version.toString().matches(Regex("\\d+\\.\\d+\\.\\d+"))) {
                    "Production deployment requires semantic version, got: ${project.version}"
                }
            }
        }
    }
}

Template-Processing generiert umgebungsspezifische Konfigurationsdateien. Deployment-Descriptors, Kubernetes-Manifests oder Docker-Compose-Files werden aus Templates mit umgebungsspezifischen Werten erstellt. Diese Generierung erfolgt zur Build-Zeit, wodurch die resultierenden Artefakte self-contained und reproduzierbar sind. Die Templates verwenden Platzhalter, die durch Gradle’s expand()-Funktion oder dedizierte Template-Engines ersetzt werden.

23.4 Container-basierte Deployment-Strategien

Container-Technologien standardisieren das Deployment von Multi-Modul-Systemen. Jedes Subprojekt erstellt ein Container-Image mit seinen Artefakten und Runtime-Dependencies. Diese Images werden zu einer Container-Registry publiziert und von dort deployed. Gradle integriert mit Docker durch Plugins wie com.bmuschko.docker oder com.google.cloud.tools.jib, die Image-Erstellung ohne Docker-Daemon ermöglichen.

Multi-Stage Container Builds optimieren Image-Größe und Build-Zeit. Build-Stages kompilieren Code und erstellen Artefakte, während Runtime-Stages minimale Images mit nur den notwendigen Komponenten erstellen. Diese Strategie reduziert Attack-Surface und Deployment-Zeit. Gradle koordiniert Multi-Stage Builds über Subprojekt-Grenzen, wobei Artefakte zwischen Stages geteilt werden.

// service-a/build.gradle.kts
jib {
    from {
        image = "eclipse-temurin:17-jre-alpine"
    }
    to {
        image = "${project.group}/${project.name}"
        tags = setOf(project.version.toString(), "latest")
    }
    container {
        jvmFlags = listOf("-Xmx512m", "-XX:+UseG1GC")
        mainClass = "com.company.service.Application"
        ports = listOf("8080")
        
        environment = mapOf(
            "SERVICE_NAME" to project.name,
            "SERVICE_VERSION" to project.version.toString()
        )
    }
}

// Root-Level Orchestration
tasks.register("buildAllImages") {
    description = "Build container images for all services"
    dependsOn(subprojects.mapNotNull { 
        it.tasks.findByName("jibDockerBuild") 
    })
}

tasks.register("pushAllImages") {
    description = "Push all container images to registry"
    dependsOn(subprojects.mapNotNull { 
        it.tasks.findByName("jib") 
    })
}

Container-Orchestrierung mit Kubernetes oder Docker Swarm verwaltet das Runtime-Deployment. Gradle generiert Deployment-Manifests mit Service-Definitionen, ConfigMaps und Secrets. Die Manifests referenzieren die Container-Images aus der Registry und definieren Deployment-Strategien wie Rolling Updates oder Blue-Green Deployments. Helm Charts kapseln komplexe Deployments und ermöglichen parametrisierte Installation.

23.5 Rollback-Mechanismen und Versionsverwaltung

Rollback-Fähigkeit ist kritisch für Production-Deployments. Jedes Deployment wird mit einer eindeutigen Version tagged, die alle enthaltenen Artefakte identifiziert. Diese Versionierung ermöglicht deterministisches Rollback zu jedem vorherigen Zustand. Gradle generiert Version-Manifests, die alle Modul-Versionen eines Deployments dokumentieren. Diese Manifests werden archiviert und bei Rollbacks verwendet.

Semantic Versioning koordiniert Breaking Changes über Module. Major-Version-Updates signalisieren Inkompatibilitäten, Minor-Versions fügen Features hinzu, Patch-Versions beheben Bugs. Diese Semantik ermöglicht automatisierte Compatibility-Checks zwischen Modulen. Gradle Tasks validieren Version-Kompatibilität vor Deployment und verhindern inkompatible Kombinationen.

tasks.register("createDeploymentSnapshot") {
    description = "Create snapshot of current deployment state"
    
    val snapshotFile = file("${buildDir}/deployment-snapshots/${project.version}.json")
    
    doLast {
        val snapshot = DeploymentSnapshot(
            version = project.version.toString(),
            timestamp = Instant.now(),
            modules = subprojects.map { subproject ->
                ModuleInfo(
                    name = subproject.name,
                    version = subproject.version.toString(),
                    artifact = subproject.tasks.findByName("jar")?.outputs?.files?.singleFile?.name,
                    checksum = calculateChecksum(subproject)
                )
            },
            gitCommit = executeGitCommand("rev-parse HEAD"),
            gitBranch = executeGitCommand("rev-parse --abbrev-ref HEAD")
        )
        
        snapshotFile.parentFile.mkdirs()
        snapshotFile.writeText(Json.encodeToString(snapshot))
    }
}

tasks.register<RollbackTask>("rollback") {
    description = "Rollback to previous deployment version"
    
    val targetVersion = project.findProperty("rollbackVersion") as String?
    require(targetVersion != null) { "Specify rollbackVersion property" }
    
    snapshotFile = file("${buildDir}/deployment-snapshots/${targetVersion}.json")
    deploymentTarget = determineDeploymentTarget()
}

Incremental Rollouts minimieren Risiken bei Deployments. Canary Deployments starten mit einem kleinen Prozentsatz von Traffic, der schrittweise erhöht wird. Feature Flags ermöglichen Rollback auf Feature-Ebene ohne vollständiges Redeployment. A/B Testing deployed verschiedene Versionen parallel und vergleicht Metriken. Diese Strategien werden durch Gradle Tasks orchestriert, die mit Deployment-Infrastruktur und Monitoring-Systemen integrieren.

23.6 CI/CD-Pipeline-Integration

Die Integration in CI/CD-Pipelines automatisiert den Deployment-Prozess vollständig. Pipeline-Definitionen in Jenkins, GitLab CI oder GitHub Actions orchestrieren Build, Test und Deployment über alle Module. Gradle Tasks bilden die atomaren Schritte der Pipeline, während die CI/CD-Platform Parallelisierung, Gating und Approval-Workflows verwaltet.

Stage-basierte Pipelines strukturieren den Deployment-Prozess. Build-Stages kompilieren Code und erstellen Artefakte. Test-Stages führen Unit-, Integration- und End-to-End-Tests aus. Deployment-Stages promoten Artefakte durch Umgebungen. Jede Stage hat eigene Success-Criteria und Rollback-Trigger. Gradle Tasks implementieren Stage-Logik, während Pipeline-Definitionen die Orchestrierung übernehmen.

Artifact Promotion Workflows verwalten den Lebenszyklus von Deployment-Artefakten. Erfolgreiche Builds werden zu Snapshot-Repositories promoted. Nach Test-Approval werden Snapshots zu Release-Candidates. Production-Approval promoted Release-Candidates zu stabilen Releases. Gradle Tasks implementieren Promotion-Logik inklusive Versioning, Tagging und Repository-Upload. Die Pipeline koordiniert Approvals und Notifications.