Modern Java Engineering
Chapitre 13
13 - DevOps Java
13 - DevOps Java
Cours 13 : DevOps Java
1. Maven Avancé
1.1 Maven Lifecycle
validate
↓
initialize
↓
generate-sources
↓
process-sources
↓
compile ← Compile le code source
↓
process-classes
↓
generate-test-sources
↓
process-test-sources
↓
test-compile
↓
test ← Exécute les tests
↓
prepare-package
↓
package ← Crée le JAR/WAR
↓
verify
↓
install ← Installe dans le repository local
↓
deploy ← Déploie sur le repository distant
1.2 Plugins essentiels
<build>
<plugins>
<!-- Compilation avec vérification -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.12.1</version>
<configuration>
<release>21</release>
<parameters>true</parameters>
</configuration>
</plugin>
<!-- Tests avec JUnit 5 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.2.3</version>
</plugin>
<!-- Analyse de couverture -->
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.11</version>
<executions>
<execution>
<goals><goal>prepare-agent</goal></goals>
</execution>
<execution>
<id>report</id>
<phase>verify</phase>
<goals><goal>report</goal></goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
1.3 Multi-module Maven
<!-- Parent POM -->
<groupId>com.orderhub</groupId>
<artifactId>orderhub-parent</artifactId>
<version>1.0.0</version>
<packaging>pom</packaging>
<modules>
<module>order-domain</module>
<module>order-application</module>
<module>order-adapter-rest</module>
<module>order-adapter-persistence</module>
<module>order-boot</module>
</modules>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>3.2.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
1.4 BOM (Bill of Materials)
<!-- orderhub-bom/pom.xml -->
<project>
<groupId>com.orderhub</groupId>
<artifactId>orderhub-bom</artifactId>
<version>1.0.0</version>
<packaging>pom</packaging>
<properties>
<spring-boot.version>3.2.0</spring-boot.version>
<spring-cloud.version>2023.0.0</spring-cloud.version>
<axon.version>4.9.0</axon.version>
<testcontainers.version>1.19.3</testcontainers.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.axonframework</groupId>
<artifactId>axon-bom</artifactId>
<version>${axon.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
2. Gradle
2.1 Kotlin DSL
plugins {
java
id("org.springframework.boot") version "3.2.0"
id("io.spring.dependency-management") version "1.1.4"
jacoco
}
group = "com.orderhub"
version = "1.0.0"
java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(21))
}
}
dependencies {
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
runtimeOnly("org.postgresql:postgresql")
testImplementation("org.springframework.boot:spring-boot-starter-test")
testImplementation("org.testcontainers:postgresql")
}
tasks.test {
useJUnitPlatform()
finalizedBy(tasks.jacocoTestReport)
}
tasks.jacocoTestReport {
dependsOn(tasks.test)
reports {
xml.required.set(true)
html.required.set(true)
}
}
2.2 Gradle Caching & Optimisation
// settings.gradle.kts
pluginManagement {
repositories {
mavenCentral()
gradlePluginPortal()
}
}
// Cache des dépendances
val gradleCacheDir = file("${System.getProperty("user.home")}/.gradle/caches")
// Build cache
buildCache {
local {
isEnabled = true
directory = file("${rootDir}/.build-cache")
}
remote<HttpBuildCache> {
url = uri("https://build-cache.orderhub.com/cache/")
isPush = true
}
}
3. Docker
3.1 Multi-stage Build
# Stage 1 : Build avec Maven
FROM maven:3.9-eclipse-temurin-21-alpine AS builder
WORKDIR /build
COPY pom.xml .
COPY src ./src
RUN mvn clean package -DskipTests
# Stage 2 : Image finale légère
FROM eclipse-temurin:21-jre-alpine
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser
WORKDIR /app
COPY --from=builder /build/target/*.jar app.jar
EXPOSE 8080
# JVM flags optimisés
ENV JAVA_OPTS="-XX:+UseZGC \
-XX:MaxRAMPercentage=75.0 \
-XX:+ExitOnOutOfMemoryError \
-Xlog:gc*:file=/dev/gc.log:time,uptime:filecount=5,filesize=10m"
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]
3.2 Docker Compose pour les microservices
version: '3.8'
services:
order-service:
build: ./order-service
ports:
- "8081:8080"
environment:
SPRING_PROFILES_ACTIVE: docker
DB_URL: jdbc:postgresql://postgres:5432/orders
KAFKA_BOOTSTRAP: kafka:9092
depends_on:
- postgres
- kafka
deploy:
resources:
limits:
memory: 512M
reservations:
memory: 256M
postgres:
image: postgres:16-alpine
environment:
POSTGRES_DB: orders
POSTGRES_USER: orderuser
POSTGRES_PASSWORD: ${DB_PASSWORD}
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U orderuser"]
interval: 5s
timeout: 5s
retries: 5
volumes:
pgdata:
4. CI/CD avec GitHub Actions
4.1 Pipeline Maven
name: Build & Test
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16-alpine
env:
POSTGRES_DB: testdb
POSTGRES_USER: test
POSTGRES_PASSWORD: test
ports:
- 5432:5432
steps:
- uses: actions/checkout@v4
- name: Setup Java 21
uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'temurin'
cache: 'maven'
- name: Build & Test
run: mvn verify -B
- name: SonarQube Analysis
run: mvn sonar:sonar
-Dsonar.host.url=${{ secrets.SONAR_HOST_URL }}
-Dsonar.login=${{ secrets.SONAR_TOKEN }}
- name: Build Docker Image
run: docker build -t order-service:${{ github.sha }} .
4.2 Pipeline Gradle avec cache
name: Gradle CI
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'temurin'
- name: Setup Gradle
uses: gradle/gradle-build-action@v2
with:
cache-read-only: ${{ github.ref != 'refs/heads/main' }}
- name: Build with Gradle
run: ./gradlew build
- name: Upload Artifacts
uses: actions/upload-artifact@v4
with:
name: build-artifacts
path: build/libs/*.jar
4.3 Pipeline de release
name: Release
on:
push:
tags:
- 'v*'
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set version
run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_ENV
- name: Build
run: mvn versions:set -DnewVersion=${{ env.VERSION }} && mvn clean package
- name: Publish to Nexus
run: mvn deploy
env:
NEXUS_USERNAME: ${{ secrets.NEXUS_USERNAME }}
NEXUS_PASSWORD: ${{ secrets.NEXUS_PASSWORD }}
- name: Generate SBOM
run: mvn org.cyclonedx:cyclonedx-maven-plugin:makeBom
- name: Sign Package
run: gpg --sign --detach-sig target/*.jar
5. SonarQube
5.1 Configuration
<plugin>
<groupId>org.sonarsource.scanner.maven</groupId>
<artifactId>sonar-maven-plugin</artifactId>
<version>3.10.0.2594</version>
</plugin>
5.2 Qualité Gates
# sonar-project.properties
sonar.projectKey=com.orderhub:order-service
sonar.projectName=Order Service
sonar.qualitygate.wait=true
sonar.coverage.jacoco.xmlReportPaths=target/site/jacoco/jacoco.xml
sonar.java.coveragePlugin=jacoco
sonar.junit.reportPaths=target/surefire-reports
sonar.issues.html.ignoreStatuses=RESOLVED,CLOSED
6. Nexus/Artifactory
6.1 Configuration Nexus
<distributionManagement>
<repository>
<id>nexus-releases</id>
<url>https://nexus.orderhub.com/repository/maven-releases/</url>
</repository>
<snapshotRepository>
<id>nexus-snapshots</id>
<url>https://nexus.orderhub.com/repository/maven-snapshots/</url>
</snapshotRepository>
</distributionManagement>
<repositories>
<repository>
<id>nexus</id>
<url>https://nexus.orderhub.com/repository/maven-public/</url>
</repository>
</repositories>
7. SBOM (Software Bill of Materials)
7.1 Génération avec CycloneDX
<plugin>
<groupId>org.cyclonedx</groupId>
<artifactId>cyclonedx-maven-plugin</artifactId>
<version>2.7.9</version>
<executions>
<execution>
<phase>package</phase>
<goals><goal>makeBom</goal></goals>
</execution>
</executions>
<configuration>
<projectType>application</projectType>
<schemaVersion>1.5</schemaVersion>
<includeBomSerialNumber>true</includeBomSerialNumber>
<includeCompileScope>true</includeCompileScope>
<includeProvidedScope>true</includeProvidedScope>
<includeRuntimeScope>true</includeRuntimeScope>
<includeSystemScope>false</includeSystemScope>
<includeTestScope>false</includeTestScope>
<outputFormat>json</outputFormat>
<outputName>bom</outputName>
</configuration>
</plugin>
8. Package Signing
8.1 GPG Signature
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<version>3.1.0</version>
<executions>
<execution>
<id>sign-artifacts</id>
<phase>verify</phase>
<goals><goal>sign</goal></goals>
</execution>
</executions>
</plugin>
Points clés
- Maven lifecycle : validate → compile → test → package → verify → install → deploy
- Gradle Kotlin DSL pour des builds type-safe
- Docker multi-stage pour des images légères (JRE seulement)
- CI/CD avec cache Maven/Gradle pour des builds rapides
- SonarQube pour la qualité et la sécurité du code
- Nexus/Artifactory pour la gestion des artefacts
- SBOM CycloneDX pour la traçabilité des dépendances
- GPG signing pour l'intégrité des packages