Add docker-images: one Spring Boot 4 service packaged nine ways
Companion code for "Dockerizing Spring Boot 4: Layered Jars, Buildpacks, Distroless and Image Size Benchmarks". Fat jar on JDK and JRE, layered jar on Debian, Alpine and distroless, jlink, the JDK 25 AOT cache, Paketo buildpacks and Jib, each measured for size on disk and pushed, rebuild delta, startup, user and shell. Also PID 1 and signal handling, the jdeps module gap, AOT cache mismatches and buildpacks memory calculation. Transcripts in docs/output/, regenerated by scripts/run-all.sh. Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01C3TETMrqVUWeFkNtz3Jbo3
This commit is contained in:
@@ -0,0 +1,14 @@
|
||||
package com.ankurm.images;
|
||||
|
||||
/**
|
||||
* The one line scripts/measure-rebuild.sh edits to simulate "a developer changed some code". The
|
||||
* point is to see which image layers change - and therefore how many bytes a registry push and
|
||||
* a node pull cost - when nothing but application code moved.
|
||||
*/
|
||||
public final class BuildInfo {
|
||||
|
||||
public static final String REVISION = "1";
|
||||
|
||||
private BuildInfo() {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.ankurm.images;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class ImagesApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(ImagesApplication.class, args);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package com.ankurm.images;
|
||||
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* A deliberately ordinary service - web, validation, Actuator, Prometheus - so the image sizes
|
||||
* measured here are what a typical Spring Boot 4 service would get, not a hello-world floor.
|
||||
* {@code /api/runtime} reports what the JVM inside the container actually sees.
|
||||
*/
|
||||
@RestController
|
||||
public class InfoController {
|
||||
|
||||
public record Item(@NotBlank String name) {
|
||||
}
|
||||
|
||||
@GetMapping("/api/items")
|
||||
public List<Item> items() {
|
||||
return List.of(new Item("widget"), new Item("gadget"));
|
||||
}
|
||||
|
||||
@PostMapping("/api/items")
|
||||
public Item create(@jakarta.validation.Valid @RequestBody Item item) {
|
||||
return item;
|
||||
}
|
||||
|
||||
@GetMapping("/api/runtime")
|
||||
public Map<String, Object> runtime() {
|
||||
Runtime rt = Runtime.getRuntime();
|
||||
Map<String, Object> out = new LinkedHashMap<>();
|
||||
out.put("revision", BuildInfo.REVISION);
|
||||
out.put("java.version", System.getProperty("java.version"));
|
||||
out.put("java.vendor", System.getProperty("java.vendor"));
|
||||
out.put("java.home", System.getProperty("java.home"));
|
||||
out.put("user.name", System.getProperty("user.name"));
|
||||
out.put("pid", ProcessHandle.current().pid());
|
||||
out.put("availableProcessors", rt.availableProcessors());
|
||||
out.put("maxHeapMiB", rt.maxMemory() / (1024 * 1024));
|
||||
out.put("inputArguments", ManagementFactory.getRuntimeMXBean().getInputArguments());
|
||||
out.put("modules", ModuleLayer.boot().modules().size());
|
||||
return out;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
spring:
|
||||
application:
|
||||
name: docker-images
|
||||
management:
|
||||
endpoints:
|
||||
web:
|
||||
exposure:
|
||||
include: health,info,prometheus
|
||||
endpoint:
|
||||
health:
|
||||
probes:
|
||||
enabled: true
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.ankurm.images;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@SpringBootTest
|
||||
class ImagesApplicationTest {
|
||||
|
||||
@Autowired
|
||||
InfoController controller;
|
||||
|
||||
@Test
|
||||
void runtimeReportsTheRevision() {
|
||||
assertThat(controller.runtime()).containsEntry("revision", BuildInfo.REVISION);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user