Slide 1

Slide 1 text

2024-04-09 Jonatan Ivanov & Scott Frederick Spring Boot 3 Workshop

Slide 2

Slide 2 text

Hello From Us 👋

Slide 3

Slide 3 text

Jonatan Ivanov - Spring Team/Micrometer

Slide 4

Slide 4 text

Scott Frederick - Spring Team/Spring Boot

Slide 5

Slide 5 text

Cover w/ Image Today's Schedule ● Morning Break (30 min) ● Lunch Break (60 min) ● Afternoon Break (30 min) ● Ends 5pm (ish)

Slide 6

Slide 6 text

Cover w/ Image Today's Schedule ● Machine Setup (Java + Docker) ● Tour of the Spring Boot Applications ● Upgrade to Spring Boot 3.2 ● Error handling ● Interface Clients

Slide 7

Slide 7 text

Cover w/ Image Today's Schedule ● Observability ○ Spring and Observability ○ JDBC observability ○ Prometheus ○ Grafana ● Dev Services ● Building Docker images ● Questions and Answers

Slide 8

Slide 8 text

Hello From You 👋

Slide 9

Slide 9 text

Hands Up 󰢨 ● You're a Java developer? ● You've used Docker? ● You've used Spring Boot? ● You've used Observability? ● You've used OpenTelemetry?

Slide 10

Slide 10 text

Machine Setup

Slide 11

Slide 11 text

Machine Setup $ git clone https://github.com/jonatan-ivanov/dn24-boot3-workshop $ cd dn24-boot3-workshop $ java --version $ ./mvnw --version $ ./mvnw package $ cd dog-service $ docker compose up-d $ docker compose ps $ docker compose down 󰢨 Problems? Please let us know!

Slide 12

Slide 12 text

Tour of the Spring Boot Applications

Slide 13

Slide 13 text

About the Dog Service Sample ● It's a very silly application 🤪 ● Just enough code to demo what we want! ● Spring Boot 2.7 ● Java 17 ● JPA (Hibernate) ● Spring MVC ● Spring Security

Slide 14

Slide 14 text

Checkout the Code $ git clone https://github.com/jonatan-ivanov/dn24-boot3-workshop $ cd dn24-boot3-workshop $ git checkout main $ cd dog-service; docker compose up -d; cd .. $ ./mvnw package Import into your favorite IDE

Slide 15

Slide 15 text

Check Out the Code - pom.xml ● Open pom.xml ● We're using spring-boot-starter-parent 2.7 ● The java.version property is 17 ● We're using starters for: ○ actuator, web, data-jpa, and security ● We're using PostgreSQL

Slide 16

Slide 16 text

Check Out the Code - src/main/resources Open src/main/resources Look at schema.sql and data.sql files DOG OWNER • id (pk) • name • owner_id (fk) • id (pk) • name 1 *

Slide 17

Slide 17 text

Check Out the Code - com.example.dogservice.domain ● Open com.example.dogservice.domain package ● Dog and Owner JPA classes map to the schema ● DogRepository and OwnerRepository are Spring Data repositories ○ findByNameIgnoringCase is converted to JQL automatically ● InfoLogger is an ApplicationRunner to log info at startup

Slide 18

Slide 18 text

Check Out the Code - com.example.dogservice.service ● Open com.example.dogservice.service package ● OwnerService uses constructor injection ● Simple facade over repositories ● Throws custom NoSuchDogOwnerException

Slide 19

Slide 19 text

Check Out the Code - com.example.dogservice.web ● Open com.example.dogservice.web package ● DogsController ○ Simple controller used for testing ● OwnerController ○ Delegates to the OwnerService ○ Deals with NoSuchDogOwnerException ○ Note: Meta-annotated @RestController and @GetMapping

Slide 20

Slide 20 text

Check Out the Code - com.example.dogservice.security ● Open com.example.dogservice.security package ● SecurityConfiguration ○ Defines our web security ● SecurityProperties and UserProperties ○ @ConfigurationProperties maps from values in src/main/resources/application.yml

Slide 21

Slide 21 text

Check Out the Code - src/main/resources/ ● Open src/main/resources/ ● Inspect application.yml ○ Defines the database connection ○ Configures JPA ○ Enables JMX ○ Configures server errors ○ Exposes all actuator endpoints ○ Enables actuators over HTTP ○ Customizes a metric name ○ Defines the in-memory user details

Slide 22

Slide 22 text

Run the code $ ./mvnw -pl dog-service clean spring-boot:run __ , ," e`--o (( ( | __,' \\~----------------' \_;/ ( / /) ._______________. ) (( ( (( ( ``-' ``-' … 2024-04-09T10:50:22.372-05:00 INFO 151018 --- [dog-service] [ main] [ ] c.example.dogservice.domain.InfoLogger : Found owners [Scott, Jonatan] 2024-04-09T10:50:22.386-05:00 INFO 151018 --- [dog-service] [ main] [] c.example.dogservice.domain.InfoLogger : Found dogs [Snoopy owned by Scott, Goofy owned by Scott, Clifford owned by Jonatan] … 2024-04-09T10:50:28.260-05:00 INFO 151018 --- [dog-service] [nio-8080-exec-1] [ ] o.s.web.servlet.DispatcherServlet : Completed initialization in 2 ms …

Slide 23

Slide 23 text

Upgrade to Spring Boot 3.2

Slide 24

Slide 24 text

Why Upgrade? ● Micrometer Observability ● Docker Compose and Testcontainers support ● SSL Bundles ● Performance improvement ○ GraalVM Native Images ○ Checkpoint and Restore (CRaC) ○ Class Data Sharing (CDS) - coming soon! ● Virtual threads (on Java 21)

Slide 25

Slide 25 text

Why Upgrade? ● Dependency upgrades ● Other new features and fixes ● Supported version ○ Spring Boot 2.7 OSS support reached its end! ○ Spring Boot 2.7 is the end of 2.x line

Slide 26

Slide 26 text

Managed Dependencies Project Version Spring Boot 2.7.x 3.0.x 3.2.x Spring Framework 5.3.x 6.0.x 6.1.x Spring Security 5.7.x 6.0.x 6.2.x Spring Data JPA 2.7.x 3.0.x 3.2.x Micrometer 1.9.x 1.10.x 1.12.x https://docs.spring.io/spring-boot/docs/current/reference/html/dependency-versions.html #appendix.dependency-versions

Slide 27

Slide 27 text

Checkpoint Everyone has a working application

Slide 28

Slide 28 text

Checkpoint - DogsController Works $ http "http://localhost:8080/dogs" Generates error JSON $ http "http://localhost:8080/dogs?aregood=true" $ http "http://localhost:8080/dogs?aregood=false" $ http "http://localhost:8080/dogs/?aregood=false" Note the trailing slash

Slide 29

Slide 29 text

Checkpoint - OwnerController Works $ http "http://localhost:8080/owner/scott/dogs" Needs login $ http -a user:password "http://localhost:8080/owner/scott/dogs" $ http -a user:password "http://localhost:8080/owner/jonatan/dogs" $ http -a user:password "http://localhost:8080/owner/dave/dogs" NoSuchOwnerException mapped to HTTP 404

Slide 30

Slide 30 text

Checkpoint - Actuator Works $ http -a admin:secret "http://localhost:8080/actuator" Open the following in a web browser: http://localhost:8080/actuator http://localhost:8080/actuator/env (search for password) http://localhost:8080/actuator/metrics/ http://localhost:8080/actuator/metrics/http.server.in

Slide 31

Slide 31 text

Checkpoint - Actuator Over JMX Works $ jconsole Select com.example.dogservice.DogServiceApplication Click Connect Select MBeans on the menu Expand org.springframework.boot, Endpoint

Slide 32

Slide 32 text

Upgrade Spring Boot

Slide 33

Slide 33 text

Upgrade Spring Boot - Update pom.xml ● Edit the pom.xml file and change the tag in the parent to 3.2.4 ● Things will break and we'll need to fix them!

Slide 34

Slide 34 text

Upgrade Spring Boot - Fix javax.persistence ● For legal reasons javax package namespace can't be used by Jakarta ● So the Dog and Owner classes now have invalid imports ● Replace javax.persistence with jakarta.persistence

Slide 35

Slide 35 text

Upgrade Spring Boot - Fix Spring Data Repositories ● PagingAndSortingRepository no longer extends CrudRepository ● There are new convenient List... interfaces ● Change DogRepository to extend ListCrudRepository and ListPagingAndSortingRepository ● Change OwnerRepository to extend ListCrudRepository

Slide 36

Slide 36 text

Upgrade Spring Boot - Improve InfoLogger ● The List... interface means findAll now returns List not Iterable ● Change InfoLogger log calls to no longer use StreamSupport ○ this.dogRepository.findAll() ○ this.ownerRepository.findAll()

Slide 37

Slide 37 text

Upgrade Spring Boot - Fix SecurityConfiguration ● We suppressed the WebSecurityConfigurerAdapter deprecation ● That class has gone so we need to fix things ● Remove extends WebSecurityConfigurerAdapter

Slide 38

Slide 38 text

Upgrade Spring Boot - Fix SecurityConfiguration ● Replace configure method with a SecurityFilterChain bean definition @Bean SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { [ body of existing configure method ] http.httpBasic(Customizer.withDefaults()); return http.build(); }

Slide 39

Slide 39 text

Upgrade Spring Boot - Fix SecurityConfiguration ● The mvcMatchers method has gone ● Replace mvcMatchers with requestMatchers

Slide 40

Slide 40 text

Upgrade Spring Boot - Fix SecurityConfiguration ● The http.authorizeRequests method is deprecated ● Replace http.authorizeRequests with http.authorizeHttpRequests

Slide 41

Slide 41 text

Upgrade Spring Boot - Fix @ConstructorBinding imports ● @ConstructorBinding has moved and we need to fix that ● Update SecurityProperties and UserProperties ○ Replace import org.springframework.boot.context.properties.Const ructorBinding ○ With import org.springframework.boot.context.properties.bind. ConstructorBinding

Slide 42

Slide 42 text

Upgrade Spring Boot - Remove @ConstructorBinding ● Read the release notes on the wiki ● Remove @ConstructorBinding from SecurityProperties and UserProperties

Slide 43

Slide 43 text

Upgrade Spring Boot - Use Records ● Since we're using Java 17 we can use records ● Replace SecurityProperties and UserProperties with records ● Fix SecurityConfiguration

Slide 44

Slide 44 text

Checkpoint Everyone has a working Spring Boot 3.2 application

Slide 45

Slide 45 text

$ ./mvnw -pl dog-service clean spring-boot:run $ http "http://localhost:8080/dogs?aregood=true" $ http -a user:password "http://localhost:8080/owner/scott/dogs" Checkpoint - The application works

Slide 46

Slide 46 text

Upgrade Spring Boot - Fix Deprecated Properties ● Depending on your IDE you might see warnings in your application.yml ● To help fix those, we can use the Spring Boot property migrator

Slide 47

Slide 47 text

Upgrade Spring Boot - Fix Deprecated Properties ● Update pom.xml and add spring-boot-properties-migrator org.springframework.boot spring-boot-properties-migrator runtime

Slide 48

Slide 48 text

Upgrade Spring Boot - Fix Deprecated Properties ● Run the application and it will report deprecations and replacements $ ./mvnw -pl dog-service clean spring-boot:run … The use of configuration keys that have been renamed was found in the environment: Property source 'Config resource 'class path resource [application.yml]' via location 'optional:classpath:/'': Key: management.metrics.web.server.request.metric-name Line: 25 Replacement: management.observations.http.server.requests.name

Slide 49

Slide 49 text

Upgrade Spring Boot - Fix Deprecated Properties ● Replace management.metrics.web.request.metric-name with management.observations.http.requests.name ● Check the result in actuator

Slide 50

Slide 50 text

Upgrade Spring Boot - Fix Deprecated Properties ● The properties migrator has a startup cost, so once we're done we can remove it ● Update pom.xml and remove spring-boot-properties-migrator

Slide 51

Slide 51 text

Checkpoint Everything works as expected

Slide 52

Slide 52 text

Checkpoint - The application works $ ./mvnw -pl dog-service clean spring-boot:run ● Check the actuator env endpoint ○ Notice that all values are hidden with ****** ● Try JMX actuator with jconsole ○ Notice that only health is available

Slide 53

Slide 53 text

Upgrade Spring Boot - Expose JMX ● Actuator JMX configuration is now aligned with web and needs to be exposed ● Add management.endpoints.jmx.exposure.include with a value of "*" ● Run the app again and check JMX in jconsole

Slide 54

Slide 54 text

Upgrade Spring Boot - Always Show Env Values ● To show env values again we need to set a property ● Add management.endpoint.env.show-values with a value of always

Slide 55

Slide 55 text

Upgrade Spring Boot - Sanitize Env Values ● Spring Boot 3.0 now leaves all sanitization to you! ● Add a SanitizingFunction to DogServiceApplication @Bean SanitizingFunction sanitizingFunction() { return (data) -> (data.getKey().toLowerCase().contains("password")) ? data.withValue(SanitizableData.SANITIZED_VALUE) : data; }

Slide 56

Slide 56 text

Checkpoint Everything really works as expected!

Slide 57

Slide 57 text

Upgrade Spring Boot - Everything works as expected! $ http "http://localhost:8080/dogs" $ http -a user:password "http://localhost:8080/owner/dave/dogs" Notice the specifics of the error JSON

Slide 58

Slide 58 text

New Features - Problem Details ● Spring Boot 3.0 supports problem details (RFC 7807) ● We need to enable it for errors handled by Spring MVC ● Set spring.mvc.problemdetails.enabled to true

Slide 59

Slide 59 text

New Features - Problem Details ● We can use org.springframework.http.ProblemDetail in our own code ● Edit OwnerController.onNoSuchDogOwner @ExceptionHandler ProblemDetail onNoSuchDogOwner(NoSuchDogOwnerException ex) { ProblemDetail details = ProblemDetail.forStatus(HttpStatus.NOT_FOUND); details.setProperty("ownername", ex.getName()); return details; }

Slide 60

Slide 60 text

New Features - Problem Details $ http "http://localhost:8080/dogs" $ http -a user:password "http://localhost:8080/owner/dave/dogs" Notice the specifics of the error JSON

Slide 61

Slide 61 text

Checkpoint Upgrade fully complete

Slide 62

Slide 62 text

At Scale - Automate Upgrades ● Spring Health Assessment ● OpenRewrite recipes ● Spring Boot Migrator

Slide 63

Slide 63 text

HTTP Client Interfaces

Slide 64

Slide 64 text

About the Dog Client Sample ● It's another very silly application 🤪 ● Just enough code to demo what we want! ● API using HttpServiceProxy ● Another MVC controller ● Some configuration

Slide 65

Slide 65 text

HTTP Client Interfaces: interfaces + metadata ● Open com.example.dogclient.api.Api @GetExchange("/dogs") DogsResponse dogs( @RequestParam(name="aregood") boolean areGood); @GetExchange("/owner/{name}/dogs") List ownedDogs(@PathVariable String name);

Slide 66

Slide 66 text

HTTP Client Interfaces + Records ● Open com.example.dogclient.api.DogsResponse public record DogsResponse(String message) { }

Slide 67

Slide 67 text

HTTP Client Interfaces - Building the client ● Open com.example.dogclient.DogClientApplication WebClient webClient = webClientBuilder.baseUrl("…").build(); WebClientAdapter adapter = WebClientAdapter.create(webClient); HttpServiceProxyFactory factory = HttpServiceProxyFactory.builderFor(adapter).build(); return factory.createClient(Api.class);

Slide 68

Slide 68 text

HTTP Client Interfaces - Using the client // Signature DogsResponse dogs(boolean areGood); List ownedDogs(String name); // Usage api.dogs(true); api.ownedDogs("Scott");

Slide 69

Slide 69 text

ApplicationRunner ● Open com.example.dogclient.DogClientApplication ● Prints information when the application starts ● Functional interface called when the app starts: void run(ApplicationArguments args) throws Exception

Slide 70

Slide 70 text

Checkpoint Client application works

Slide 71

Slide 71 text

Run the application - Check the startup output $ ./mvnw -pl dog-service clean spring-boot:run $ ./mvnw -pl dog-client clean spring-boot:run 2024-04-09T13:26:07.471-05:00 INFO 1618241 --- [dog-client] [ main] [ ] c.e.dogclient.DogClientApplication : Started DogClientApplication in 1.505 seconds (process running for 1.643) DogsResponse[message=We <3 dogs!!!] [Snoopy, Goofy]

Slide 72

Slide 72 text

Observability

Slide 73

Slide 73 text

What is Observability?

Slide 74

Slide 74 text

What is Observability? How well we can understand the internals of a system based on its outputs (Providing meaningful information about what happens inside) (Data about your app)

Slide 75

Slide 75 text

Why do we need Observability?

Slide 76

Slide 76 text

Why do we need Observability? Today's systems are increasingly complex (cloud) (Death Star Architecture, Big Ball of Mud)

Slide 77

Slide 77 text

Environments can be chaotic You turn a knob here a little and apps are going down there We need to deal with unknown unknowns We can’t know everything Things can be perceived differently by observers Everything is broken for the users but seems ok to you Why do we need Observability?

Slide 78

Slide 78 text

Why do we need Observability? (business perspective) Reduce lost revenue from production incidents Lower mean time to recovery (MTTR) Require less specialized knowledge Shared method of investigating across system Quantify user experience Don't guess, measure!

Slide 79

Slide 79 text

Logging Metrics Distributed Tracing

Slide 80

Slide 80 text

Logging What happened (why)? Emitting events Metrics What is the context? Aggregating data Distributed Tracing Why happened? Recording causal ordering of events Logging - Metrics - Distributed Tracing

Slide 81

Slide 81 text

Examples Latency Logging Processing took 140ms Metrics P99.999: 140ms Max: 150 ms Distributed Tracing DB was slow (lot of data was requested) Error Logging Processing failed (stacktrace?) Metrics The error rate is 0.001/sec 2 errors in the last 30 minutes Distributed Tracing DB call failed (invalid input)

Slide 82

Slide 82 text

Checkpoint Everyone knows what Observability is

Slide 83

Slide 83 text

Logging with JVM/Spring

Slide 84

Slide 84 text

SLF4J with Logback comes pre-configured SLF4J (Simple Logging Façade for Java) Simple API for logging libraries Logback Natively implements the SLF4J API If you want Log4j2 instead of Logback: - spring-boot-starter-logging + spring-boot-starter-log4j2 Logging with JVM/Spring: SLF4J + Logback

Slide 85

Slide 85 text

Setup Logging - Add org property ● We will need something that we can use to query: ○ All of our apps (spring.application.org) ○ Only one app (spring.application.name) ○ Only one instance (we only have one instance/app) spring: application: name: dog-service org: petclinic

Slide 86

Slide 86 text

Setup Logging - Add Loki4J ● Copy From: dog-client/src/main/resources/logback-spring.xml To: dog-service/src/main/resources/logback-spring.xml ● Add dependency to pom.xml com.github.loki4j loki-logback-appender 1.5.1

Slide 87

Slide 87 text

Setup Logging - Do we have logs? ● Got to Grafana: http://localhost:3000 ● Choose Explore, then Loki from the drop down ● Search for application = dog-service ● Search for org = petclinic ● We will get back to our logs later

Slide 88

Slide 88 text

Checkpoint Everyone has logs in Loki for both services

Slide 89

Slide 89 text

Metrics with JVM/Spring

Slide 90

Slide 90 text

Metrics with JVM/Spring: Micrometer Dimensional Metrics library on the JVM Like SLF4J, but for metrics API is independent of the configured metrics backend Supports many backends Comes with spring-boot-actuator Spring projects are instrumented using Micrometer Many third-party libraries use Micrometer

Slide 91

Slide 91 text

Supported metrics backends/formats/protocols Ganglia Graphite Humio InfluxDB JMX KairosDB New Relic (/actuator/metrics) OpenTSDB OTLP Prometheus SignalFx Stackdriver (GCP) StatsD Wavefront (VMware) AppOptics Atlas Azure Monitor CloudWatch (AWS) Datadog Dynatrace Elastic

Slide 92

Slide 92 text

Sidetrack: Observation API

Slide 93

Slide 93 text

● Add logs (application logs) ● Add metrics ○ Increment Counters ○ Start/Stop Timers ● Add Distributed Tracing ○ Start/Stop Spans ○ Log Correlation ○ Context Propagation You want to instrument your application…

Slide 94

Slide 94 text

Observation API (since Micrometer 1.10) Observation observation = Observation.start("talk",registry); try { // TODO: scope Thread.sleep(1000); } catch (Exception exception) { observation.error(exception); throw exception; } finally { // TODO: attach tags (key-value) observation.stop(); }

Slide 95

Slide 95 text

Observation API (since Micrometer 1.10) ObservationRegistry registry = ObservationRegistry.create(); registry.observationConfig() .observationHandler(new MeterHandler(...)) .observationHandler(new TracingHandler(...)) .observationHandler(new LoggingHandler(...)) .observationHandler(new AuditEventHandler(...));

Slide 96

Slide 96 text

Observation API (since Micrometer 1.10) Observation.createNotStarted("talk",registry) .lowCardinalityKeyValue("event", "DN") .highCardinalityKeyValue("uid", userId) .observe(this::talk); @Observed

Slide 97

Slide 97 text

Setup Metrics

Slide 98

Slide 98 text

Setup Metrics - Add the org to Observations and Metrics application.yml management: observations: key-values: org: ${spring.application.org} metrics: tags: application: ${spring.application.name} org: ${spring.application.org}

Slide 99

Slide 99 text

Setup Metrics - Let’s check Metrics ● http://localhost:8080/actuator/prometheus ● 401 🧐 ● Prometheus? http://localhost:9090/targets ● Spring Security! 👀 ● Let’s disable it, what could go wrong!? 😈 ● Everything, please don’t do this in prod! ● Except if you want everyone know about it. 😈

Slide 100

Slide 100 text

Setup Metrics - Disable auth for certain endpoints SecurityConfiguration.java requests .requestMatchers("/dogs", "/actuator/**").permitAll();

Slide 101

Slide 101 text

Setup Metrics - Remove Observation name customization ● We are going to depend on default behavior ● So let’s remove the custom http observation naming ● Remove/comment out # observations: # http: # server: # requests: # name: "http.server.in"

Slide 102

Slide 102 text

Setup Metrics - Add histogram support for http metrics ● We want to see the latency distributions on our dashboards ● We want to calculate percentiles (P99?) management: metrics: distribution: percentiles-histogram: # all: true http.server.requests: true

Slide 103

Slide 103 text

Setup Metrics - Let’s check the HTTP and JVM metrics ● Let’s check /actuator/metrics /actuator/metrics/{metricName} /actuator/metrics/{metricName}?tag=key:value ● Let’s write a Prometheus query (HELP.md) sum by (application) (rate(http_server_requests_seconds_count[5m])) ● Let’s check the dashboards: go to Grafana, then Browse ○ Spring Boot Statistics ○ Dogs

Slide 104

Slide 104 text

Checkpoint Everyone has metrics on the dashboards

Slide 105

Slide 105 text

Distributed Tracing with JVM/Spring

Slide 106

Slide 106 text

Distributed Tracing with JVM/Spring Boot 2.x: Spring Cloud Sleuth Boot 3.x: Micrometer Tracing (Sleuth w/o Spring dependencies) Provide an abstraction layer on top of tracing libraries - Brave (OpenZipkin), default - OpenTelemetry (CNCF), experimental Instrumentation for Spring Projects, 3rd party libraries, your app Support for various backends

Slide 107

Slide 107 text

Setup Distributed Tracing - Add Micrometer Tracing io.micrometer micrometer-tracing-bridge-brave io.zipkin.reporter2 zipkin-reporter-brave

Slide 108

Slide 108 text

Setup Distributed Tracing - Set sampling probability management: tracing: sampling: probability: 1.0

Slide 109

Slide 109 text

Setup Distributed Tracing - Setup log correlation ● If you are on Spring Boot 3.1 or above, this is not needed ● If you are on 3.0, you need to set logging.pattern.level ● We are on 3.2! logging: level: org.springframework.web.servlet.DispatcherServlet: DEBUG

Slide 110

Slide 110 text

Setup Distributed Tracing - Let’s look at correlated logs 2024-03-22T20:13:21.588Z DEBUG 2167 --- [dog-service] [http-nio-8090-exec-5] [65fde66134624d949e80e0d3241ed138-9e80e0d3241ed138] o.s.web.servlet.DispatcherServlet: Completed 200 OK

Slide 111

Slide 111 text

Setup Distributed Tracing - Let’s look at some traces ● Go to Grafana, then Explore and choose Tempo ● Terminology ○ Span ○ Trace ○ Tags ○ Annotations

Slide 112

Slide 112 text

Checkpoint Everyone has log correlation and traces in Tempo

Slide 113

Slide 113 text

through traces TraceID ❮ Exemplars Tags ❯ metrics logs traces Interoperability

Slide 114

Slide 114 text

Setup Observations - Disable Spring Security Observations SecurityConfiguration.java @Bean ObservationPredicate noSpringSecurityObservations() { return (name,ctx)-> !name.startsWith("spring.security."); }

Slide 115

Slide 115 text

Setup Observations - Disable Actuator Observations ActuatorConfiguration.java if (name.equals("http.server.requests") && ctx instanceof ServerRequestObservationContext sc) { return !sc.getCarrier() .getRequestURI() .startsWith("/actuator"); } else return true;

Slide 116

Slide 116 text

Setup Observations - Enable JDBC Observations Tadaya Tsuyukubo 😎 net.ttddyy.observation:datasource-micrometer-spring-boot (1.0.3) jdbc: datasource-proxy: include-parameter-values: true query: enable-logging: true log-level: INFO

Slide 117

Slide 117 text

Setup Observations - Add custom Observation OwnerService.java Observation.createNotStarted("getDogs", registry) .contextualName("gettingOwnedDogs") .highCardinalityKeyValue("owner", owner) .observe(() -> { //… });

Slide 118

Slide 118 text

through traces TraceID ❮ Exemplars Tags ❯ metrics logs traces Interoperability

Slide 119

Slide 119 text

Interoperability - How to check Exemplars ● Exemplars are only available if you request the OpenMetrics format ● Your browser does not do this http :8081/actuator/prometheus / 'Accept: application/openmetrics-text;version=1.0.0' | grep trace_id

Slide 120

Slide 120 text

Checkpoint Logs <=> Metrics <=> Traces

Slide 121

Slide 121 text

Setup Observations - Log error and signal it OwnerController.java ProblemDetail onNoSuchDogOwner( HttpServletRequest request, NoSuchDogOwnerException ex) { logger.error("Ooops!", ex); ServerHttpObservationFilter .findObservationContext(request) .ifPresent(context -> context.setError(ex));

Slide 122

Slide 122 text

ObservationFilter tempoErrorFilter() { return context -> { if (context.getError() != null) { context.addHighCardinalityKeyValue( KeyValue.of("error", "true") ); context.addHighCardinalityKeyValue( KeyValue.of( "errorMsg", context.getError().getMessage()) ); } return context; }; } Setup Observations - Hack error reporting for Tempo

Slide 123

Slide 123 text

Setup Observations - Hack DB tags for ServiceGraph if(ctx instanceof DataSourceBaseContext dsCtx){ ctx.addHighCardinalityKeyValue( KeyValue.of( "db.name", dsCtx.getRemoteServiceName() ) ); }

Slide 124

Slide 124 text

Actuator - Add Java and OS InfoContributors management: info: java: enabled: true os: enabled: true

Slide 125

Slide 125 text

Checkpoint The applications are observable! 😎

Slide 126

Slide 126 text

Dev Services

Slide 127

Slide 127 text

Connection Details

Slide 128

Slide 128 text

Connection Details Abstraction ● Provide key details of a connection from an application to an external service ○ JDBC ○ R2DBC ○ MongoDB ○ Redis ○ Elasticsearch ○ Neo4j ○ Kafka ○ RabbitMQ ○ etc

Slide 129

Slide 129 text

Connection Auto-configuration Auto-configuration of a service bean requires the presence of a ConnectionDetails bean

Slide 130

Slide 130 text

Connection Details from Properties Typically provided by properties at runtime

Slide 131

Slide 131 text

Docker Compose

Slide 132

Slide 132 text

Docker Compose Support ● Automatically manages the Docker Compose lifecycle (up, down) when the Spring Boot application starts and stops ● Inspects started containers and detects known service types ○ MariaDB ○ MySQL ○ Oracle ○ Postgres ○ SQL Server ○ MongoDB ○ Redis ○ Elasticsearch ○ Neo4j ○ Kafka ○ RabbitMQ ○ etc

Slide 133

Slide 133 text

Docker Compose Support ● In dog-service, update pom.xml and add dependency on spring-boot-docker-compose org.springframework.boot spring-boot-docker-compose runtime true

Slide 134

Slide 134 text

Running with Docker Compose Support $ cd dog-service ; docker compose down ; cd .. $ ./mvnw -pl dog-service clean spring-boot:run INFO 63485 --- [dog-service] o.s.b.d.c.l.DockerComposeLifecycleManager : Using Docker Compose file '/home/projects/dn24-boot3-workshop/dog-service/docker-compose.yml' INFO 63485 --- [dog-service] o.s.boot.docker.compose.core.DockerCli : Container tempo Created INFO 63485 --- [dog-service] o.s.boot.docker.compose.core.DockerCli : Container postgres Created INFO 63485 --- [dog-service] o.s.boot.docker.compose.core.DockerCli : Container loki Created INFO 63485 --- [dog-service] o.s.boot.docker.compose.core.DockerCli : Container prometheus Created INFO 63485 --- [dog-service] o.s.boot.docker.compose.core.DockerCli : Container grafana Created INFO 63485 --- [dog-service] o.s.boot.docker.compose.core.DockerCli : Container loki Starting INFO 63485 --- [dog-service] o.s.boot.docker.compose.core.DockerCli : Container grafana Starting INFO 63485 --- [dog-service] o.s.boot.docker.compose.core.DockerCli : Container prometheus Starting INFO 63485 --- [dog-service] o.s.boot.docker.compose.core.DockerCli : Container tempo Starting INFO 63485 --- [dog-service] o.s.boot.docker.compose.core.DockerCli : Container postgres Starting INFO 63485 --- [dog-service] o.s.boot.docker.compose.core.DockerCli : Container grafana Started INFO 63485 --- [dog-service] o.s.boot.docker.compose.core.DockerCli : Container postgres Started INFO 63485 --- [dog-service] o.s.boot.docker.compose.core.DockerCli : Container prometheus Started INFO 63485 --- [dog-service] o.s.boot.docker.compose.core.DockerCli : Container tempo Started INFO 63485 --- [dog-service] o.s.boot.docker.compose.core.DockerCli : Container loki Started

Slide 135

Slide 135 text

Docker Compose Support Internals $ docker compose ps --format=json [ … { "ID": "b0568074e2fc767d1777a23589ace22cfd38750eb3caa6e4099c833fc50d69d4", "Name": "postgres", "Image": "postgres:16.2-alpine3.19", "Command": "docker-entrypoint.sh postgres", "Project": "dog-service", "Service": "postgres", … }, … ]

Slide 136

Slide 136 text

"ID": "b0568074e2fc767d1777a23589ace22cfd38750eb3caa6e4099c833fc50d69d4", "Name": "postgres", "Image": "postgres:16.2-alpine3.19", "Command": "docker-entrypoint.sh postgres", "Project": "dog-service", "Service": "postgres", Docker Compose Support Internals

Slide 137

Slide 137 text

[ { … "Config": { "Env": [ "POSTGRES_USER=dog", "POSTGRES_PASSWORD=woof", "POSTGRES_DB=dog-db", "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", "LANG=en_US.utf8", "PG_MAJOR=16", "PG_VERSION=16.2", "PGDATA=/var/lib/postgresql/data" ], } … ] Docker Compose Support Internals $ docker inspect b0568… --format=json

Slide 138

Slide 138 text

[ { … "Config": { "Env": [ "POSTGRES_USER=dog", "POSTGRES_PASSWORD=woof", "POSTGRES_DB=dog-db", "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", "LANG=en_US.utf8", "PG_MAJOR=16", "PG_VERSION=16.2", "PGDATA=/var/lib/postgresql/data" ], } … ] Docker Compose Connection Details

Slide 139

Slide 139 text

Additional Docker Compose Support Features ● Official and Bitnami images are supported automatically ○ Additional images can be supported with a label in compose file ● Docker compose lifecycle can be customized ○ Start (up, start) and stop (down, stop) commands ○ Start and Stop, Start only, and none lifecycle options ● Docker compose profiles can be activated ○ Distinct from Spring profiles, but the concepts can be combined ● Container readiness checks can be customized https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#features.docker-compose

Slide 140

Slide 140 text

Testcontainers

Slide 141

Slide 141 text

Testcontainers ● Allows containers to be created and started using Java APIs ● Eases the development of integration tests ● Modules are provided for many common services that Spring Boot provides auto-configuration for ● Containers can be inspected once they are started to get necessary connection information https://java.testcontainers.org/ https://testcontainers.com/modules/

Slide 142

Slide 142 text

Integration Testing with Testcontainers ● Update pom.xml and add Testcontainers dependencies org.springframework.boot spring-boot-testcontainers test org.testcontainers junit-jupiter test org.testcontainers postgresql test

Slide 143

Slide 143 text

Integration Testing with Testcontainers ● Create a package com.example.dogservice.domain in dog-service/src/test/java

Slide 144

Slide 144 text

Integration Testing with Testcontainers ● Add an integration test class RepositoryIntegrationTests @Testcontainers @SpringBootTest public class RepositoryIntegrationTests { @Container @ServiceConnection static PostgreSQLContainer> postgres = new PostgreSQLContainer<>("postgres:16.2-alpine3.19"); @Autowired OwnerRepository ownerRepository; @Autowired DogRepository dogRepository; @Test void getDogs() { Owner owner = this.ownerRepository.findByNameIgnoringCase("Jonatan"); assertThat(owner.getName()).isEqualTo("Jonatan"); List dogs = this.dogRepository.findByOwner(owner); assertThat(dogs).hasSize(1); assertThat(dogs).extracting("name").containsExactly("Clifford"); } }

Slide 145

Slide 145 text

Integration Testing with Testcontainers $ ./mvnw -pl dog-service clean package … [INFO] ------------------------------------------------------- [INFO] T E S T S [INFO] ------------------------------------------------------- [INFO] Running com.example.dogservice.domain.RepositoryIntegrationTests … [INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 6.077 s -- in com.example.dogservice.domain.RepositoryIntegrationTests

Slide 146

Slide 146 text

Integration Testing with Testcontainers ● Create a package com.example.dogservice.service in dog-service/src/test/java

Slide 147

Slide 147 text

Integration Testing with Testcontainers ● Add an integration test class DogServiceIntegrationTests @Testcontainers @SpringBootTest public class DogServiceIntegrationTests { @Container @ServiceConnection static PostgreSQLContainer> postgres = new PostgreSQLContainer<>("postgres:16.2-alpine3.19"); @Autowired OwnerService ownerService; @Test void getDogsByOwner() { List dogNames = this.ownerService.getOwnedDogNames("Scott"); assertThat(dogNames).hasSize(2); assertThat(dogNames).containsExactlyInAnyOrder("Snoopy", "Goofy"); dogNames = this.ownerService.getOwnedDogNames("Jonatan"); assertThat(dogNames).hasSize(1); assertThat(dogNames).containsExactlyInAnyOrder("Clifford"); } }

Slide 148

Slide 148 text

Integration Testing with Testcontainers $ ./mvnw -pl dog-service clean package … [INFO] ------------------------------------------------------- [INFO] T E S T S [INFO] ------------------------------------------------------- [INFO] [INFO] Running com.example.dogservice.service.DogServiceIntegrationTests … [INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 2.251 s -- in com.example.dogservice.service.DogServiceIntegrationTests

Slide 149

Slide 149 text

Testcontainers Connection Details @ServiceConnection

Slide 150

Slide 150 text

OCI Images

Slide 151

Slide 151 text

Cloud Native Buildpacks ● Cloud Native Computing Foundation project ● Specifications and APIs for building modular and composable buildpacks that contribute to building OCI images ● Implementations provided by Paketo, Google, Heroku, and others ● Alternative to Dockerfiles and Jib https://buildpacks.io/

Slide 152

Slide 152 text

Why Cloud Native Buildpacks? ● No Dockerfiles ● Consistent image base layers (OS, JRE) ● Rebasing of images without re-creating all layers ● Automatic Software Bill-of-Materials (SBOM) generation ● Broad community support https://www.youtube.com/watch?v=TX_UXuzqVGQ

Slide 153

Slide 153 text

Spring Boot CNB Integration ● Invokes a CNB builder, passing built artifacts ● Maven ○ ./mvnw spring-boot:build-image ● Gradle ○ ./gradlew bootBuildImage ● Uses Paketo buildpacks by default ● Requires a Docker-compatible daemon running locally

Slide 154

Slide 154 text

Paketo Buildpacks ● java ○ Installs Bellsoft Liberica JRE by default ○ Can be configured to install Adoptium, Dragonwell, Corretto, Zulu, OpenJ9, GraalVM, Oracle, Microsoft OpenJDK instead ○ Can install a JRE or JDK ● java-native ○ Builds a GraalVM native image when detecting a Spring Boot AOT-enabled application https://paketo.io https://paketo.io/docs/reference/java-reference/ https://paketo.io/docs/howto/java/

Slide 155

Slide 155 text

Paketo Buildpacks ● executable-jar ○ Understands Spring Boot runnable JAR format ● spring-boot ○ Uses classpath.idx to ensure consistent class ordering ○ Uses layers.idx for customizable layer configuration

Slide 156

Slide 156 text

Image Layers with Dockerfiles OS JDK Application OS JDK Application (old) Application OS JDK Application (old) Application Application (old) rebuild rebuild Dockerfile FROM openjdk:17-jdk-alpine COPY target/dog-service-0.0.1-SNAPSHOT.jar app.jar ENTRYPOINT ["java","-jar","/app.jar"] https://docs.docker.com/build/guide/layers/

Slide 157

Slide 157 text

Image Layers with Spring Boot and Paketo OS JRE Dependencies rebuild rebuild Loader Application OS JRE Dependencies Loader Application (old) Application OS JRE Dependencies Loader Application (old) Application Application (old) BOOT-INF/layers.idx - "dependencies": - "BOOT-INF/lib/" - "spring-boot-loader": - "org/" - "snapshot-dependencies": - "application": - "BOOT-INF/classes/" - "BOOT-INF/classpath.idx" - "BOOT-INF/layers.idx" - "META-INF/" Least likely to change https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/ #container-images.efficient-images.layering Most likely to change

Slide 158

Slide 158 text

$ Update pom.xml to configure the builder org.springframework.boot spring-boot-maven-plugin paketobuildpacks/builder-jammy-buildpackless-tiny paketobuildpacks/java:beta Building an OCI Image on a Mac M1/M2

Slide 159

Slide 159 text

Build an OCI image $ ./mvnw -pl dog-service spring-boot:build-image [INFO] Building image 'docker.io/library/dog-service:0.0.1-SNAPSHOT' … [INFO] > Running creator [INFO] [creator] ===> DETECTING [INFO] [creator] 6 of 26 buildpacks participating [INFO] [creator] paketo-buildpacks/ca-certificates 3.6.8 [INFO] [creator] paketo-buildpacks/bellsoft-liberica 10.5.3 [INFO] [creator] paketo-buildpacks/syft 1.45.0 [INFO] [creator] paketo-buildpacks/executable-jar 6.8.4 [INFO] [creator] paketo-buildpacks/dist-zip 5.6.9 [INFO] [creator] paketo-buildpacks/spring-boot 5.27.10 … [INFO] Successfully built image 'docker.io/library/dog-service:0.0.1-SNAPSHOT'

Slide 160

Slide 160 text

Run the image $ docker run -p 8080 dog-service:0.0.1-SNAPSHOT org.postgresql.util.PSQLException: Connection to localhost:5432 refused. Check that the hostname and port are correct and that the postmaster is accepting TCP/IP connections. at org.postgresql.core.v3.ConnectionFactoryImpl.openConnectionImpl (ConnectionFactoryImpl.java:342) at org.postgresql.core.ConnectionFactory.openConnection(ConnectionFactory.java:54) at org.postgresql.jdbc.PgConnection.(PgConnection.java:263) at org.postgresql.Driver.makeConnection(Driver.java:443) at org.postgresql.Driver.connect(Driver.java:297)

Slide 161

Slide 161 text

Run the image $ cd dog-service ; docker compose up -d $ docker run -p 8080 --network dog-service_default --env SPRING_DATASOURCE_URL=jdbc:postgresql://postgres:5432/ dog-db dog-service:0.0.1-SNAPSHOT 2024-04-09T10:50:22.372-05:00 INFO 151018 --- [dog-service] [ main] [ ] c.example.dogservice.domain.InfoLogger : Found owners [Scott, Jonatan] 2024-04-09T10:50:22.386-05:00 INFO 151018 --- [dog-service] [ main] [] c.example.dogservice.domain.InfoLogger : Found dogs [Snoopy owned by Scott, Goofy owned by Scott, Clifford owned by Jonatan]

Slide 162

Slide 162 text

Run the image ● Add dog-service to dog-service/docker-compose.yml dog-service: container_name: dog-service image: library/dog-service:0.0.1-SNAPSHOT ports: - 8080:8080 environment: - "SPRING_DATASOURCE_URL=jdbc:postgresql://postgres:5432/dog-db"

Slide 163

Slide 163 text

Run the image $ cd dog-service ; docker compose up dog-service | 2024-04-09T22:37:08.493Z INFO 1 --- [dog-service] [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port 8080 (http) with context path '' dog-service | 2024-04-09T22:37:08.504Z INFO 1 --- [dog-service] [ main] c.e.dogservice.DogServiceApplication : Started DogServiceApplication in 3.718 seconds (process running for 3.953) dog-service | 2024-04-09T22:37:08.587Z INFO 1 --- [dog-service] [ main] c.example.dogservice.domain.InfoLogger : Found owners [Scott, Jonatan] dog-service | 2024-04-09T22:37:08.597Z INFO 1 --- [dog-service] [ main] c.example.dogservice.domain.InfoLogger : Found dogs [Snoopy owned by Scott, Goofy owned by Scott, Clifford owned by Jonatan]

Slide 164

Slide 164 text

Cloud Native Buildpacks ● CNB integration in Spring Boot plugins are useful for fast iteration and inner-loop development ● Production images should be built in a CI platform that supports CNB ○ Tekton ○ Tanzu Build Service ○ GitHub Actions ○ AWS CodeBuild ○ Google Cloud Build

Slide 165

Slide 165 text

Q&A

Slide 166

Slide 166 text

Thank you!