<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: dansiviter</title>
    <description>The latest articles on DEV Community by dansiviter (@dansiviter).</description>
    <link>https://dev.to/dansiviter</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F515455%2Fbded9ad9-ba2a-483e-b511-0de30edd96cb.jpeg</url>
      <title>DEV Community: dansiviter</title>
      <link>https://dev.to/dansiviter</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dansiviter"/>
    <language>en</language>
    <item>
      <title>Distroless Alpine</title>
      <dc:creator>dansiviter</dc:creator>
      <pubDate>Tue, 10 May 2022 19:53:39 +0000</pubDate>
      <link>https://dev.to/dansiviter/distroless-alpine-ci8</link>
      <guid>https://dev.to/dansiviter/distroless-alpine-ci8</guid>
      <description>&lt;p&gt;In my day job, we've been using &lt;a href="https://github.com/GoogleContainerTools/distroless" rel="noopener noreferrer"&gt;Google's Distroless&lt;/a&gt; images for some time. The benefits of this are well known: smaller image and attack surface. However, what we didn't expect is to still to have to deal with a significant amount of toil dealing with vulnerability triage (see Snyk output below for &lt;code&gt;distroless/base&lt;/code&gt;). It could be argued our SDLC is a little, clunky and we're not quite ready for &lt;code&gt;scratch&lt;/code&gt; (Graal, Golang, etc.,) images yet so what can we do in the interim? 🤔&lt;/p&gt;

&lt;p&gt;That lead me to think about a distroless &lt;a href="https://www.alpinelinux.org/" rel="noopener noreferrer"&gt;Alpine Linux&lt;/a&gt; image. A quick google shows I'm not the only one as a recent &lt;a href="https://medium.com/inside-sumup/stop-using-alpine-docker-images-fbf122c63010" rel="noopener noreferrer"&gt;Medium article&lt;/a&gt; attests. But as we're mostly dealing with Java to get a working image by pulling library-by-library is somewhat of a faff. Fortunately, &lt;a href="https://github.com/chainguard-dev/apko" rel="noopener noreferrer"&gt;APKO&lt;/a&gt; to the rescue.&lt;/p&gt;

&lt;p&gt;APKO is a tool to create lean images using Alpine with only the stuff we need. &lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;At time of writing an Alpine 3.15 image was 5.57MB. Pretty small but still includes some things we don't need, such as &lt;code&gt;sh&lt;/code&gt; shell. So with the help of the examples from APKO I came up with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;repositories&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;https://dl-cdn.alpinelinux.org/alpine/v3.15/main&lt;/span&gt;
  &lt;span class="na"&gt;packages&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;musl&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;zlib&lt;/span&gt;

&lt;span class="na"&gt;accounts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;groups&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;groupname&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nonroot&lt;/span&gt;
    &lt;span class="na"&gt;gid&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10000&lt;/span&gt;
  &lt;span class="na"&gt;users&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nonroot&lt;/span&gt;
    &lt;span class="na"&gt;uid&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10000&lt;/span&gt;
  &lt;span class="na"&gt;run-as&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nonroot&lt;/span&gt;

&lt;span class="na"&gt;os-release&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;version-id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3.15'&lt;/span&gt;  &lt;span class="c1"&gt;# defaults to 3.16/edge&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A few things to note here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Using &lt;code&gt;3.15&lt;/code&gt; repos, rather than &lt;code&gt;edge&lt;/code&gt; used in the examples,&lt;/li&gt;
&lt;li&gt;Adding a few packages:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;musl&lt;/code&gt;: Needed for pretty much everything,&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;zlib&lt;/code&gt;: Required for JRE 17, crashes without it.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Using &lt;a href="https://docs.docker.com/engine/security/rootless/" rel="noopener noreferrer"&gt;non-root&lt;/a&gt; user.
&lt;/li&gt;

&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PS ...&amp;gt; docker run --rm `
  -v $pwd/:/app:rw `
  -w /app ghcr.io/chainguard-dev/apko:v0.3.3 `
  build rootless.yaml alpine:3.15-apko apko.tar
2022/05/10 09:21:44 loading config file: rootless.yaml
2022/05/10 09:21:45 apko (x86_64): building image 'alpine:3.15-apko'
...
PS ...&amp;gt;  docker load -i .\apko.tar 
Loaded image: alpine:3.15-apko
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result is a very small image:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;REPOSITORY   TAG               IMAGE ID       CREATED         SIZE
alpine       3.15              0ac33e5f5afa   4 weeks ago     5.57MB
alpine       3.15-apko         5a3ea808f8ed   52 years ago    709kB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's nearly 1/8th of the size (12.7%) and a grand total of just  2 packages (down from 14):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;PS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;scan&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;alpine:3.15-apko&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="n"&gt;Testing&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;alpine:3.15-apko...&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="n"&gt;Package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;manager:&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nx"&gt;apk&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;Project&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;name:&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nx"&gt;docker-image&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;alpine&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nx"&gt;Docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;image:&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nx"&gt;alpine:3.15-apko&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;Platform:&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="nx"&gt;linux/amd64&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="err"&gt;✔&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Tested&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;dependencies&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;known&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;vulnerabilities&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;no&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;vulnerable&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;paths&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;found.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's much smaller than roughly equivalent &lt;code&gt;distroless/base&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;REPOSITORY               TAG       IMAGE ID       CREATED        SIZE
gcr.io/distroless/base   nonroot   555ca12a9222   52 years ago   20.3MB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's over 28x larger and that does have potential issues:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;PS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;scan&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;gcr.io/distroless/base:nonroot&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="n"&gt;Testing&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;gcr.io/distroless/base:nonroot...&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;Package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;manager:&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nx"&gt;deb&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;Project&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;name:&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nx"&gt;docker-image&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;gcr.io/distroless/base&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nx"&gt;Docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;image:&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nx"&gt;gcr.io/distroless/base:nonroot&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;Platform:&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="nx"&gt;linux/amd64&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="n"&gt;Tested&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;6&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;dependencies&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;known&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;vulnerabilities&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;found&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;11&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;vulnerabilities.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I've trialled my new image with an &lt;a href="https://github.com/dansiviter/helidon-gcp/tree/apko" rel="noopener noreferrer"&gt;existing project&lt;/a&gt; via JLink that's heavy on Netty and gRPC the image works great (with a small tweak to exclude &lt;code&gt;grpc-netty-shaded&lt;/code&gt; due to &lt;a href="https://github.com/grpc/grpc-java/issues/9083" rel="noopener noreferrer"&gt;grpc-java#9083&lt;/a&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  What about Java?
&lt;/h2&gt;

&lt;p&gt;Ok, so I don't want to use a JLink minimal JRE, what the difference between a Java 17 JRE:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;REPOSITORY                          TAG         IMAGE ID       CREATED        SIZE
gcr.io/distroless/java17-debian11   nonroot     678ed8ce3ba5   52 years ago   231MB
alpine                              jre17-apko  d2302101850c   52 years ago   202MB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;29MB less, that's what!&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;So, we can have a distroless Alpine image, that's even smaller than Google's with a smaller attack surface. 🔥&lt;/p&gt;

&lt;p&gt;The maintainers of APKO highlight it's not released yet and liable to change, but it's a great start.&lt;/p&gt;

</description>
      <category>docker</category>
      <category>security</category>
      <category>kubernetes</category>
      <category>java</category>
    </item>
    <item>
      <title>Opinionated take on Java Microservices Frameworks</title>
      <dc:creator>dansiviter</dc:creator>
      <pubDate>Sun, 27 Jun 2021 19:21:03 +0000</pubDate>
      <link>https://dev.to/dansiviter/opinionated-take-on-java-microservices-frameworks-4ebh</link>
      <guid>https://dev.to/dansiviter/opinionated-take-on-java-microservices-frameworks-4ebh</guid>
      <description>&lt;p&gt;I've just kicked off a spike within my team on what framework we should use going forward for our microservices by applying a bit of science. But, as I'm known to have somewhat strong opinions on these things, I'll be posting this post after we, as a team, have picked a way forward to not skew the results (too much!). Even so, I'll try and be objective. More of that later, first the problem statement...&lt;/p&gt;

&lt;p&gt;We no longer want to manage our own infrastructure so we're packing our bags and heading to the cloud. We currently use Spring Boot but is there something more cloud native? We're predominantly a Java team, and it's enough upskilling to cloud without throwing in a new language into the mix.&lt;/p&gt;

&lt;p&gt;So, in a nutshell, our requirements are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Java,&lt;/li&gt;
&lt;li&gt;Container friendly (lean, starts/stops quickly),&lt;/li&gt;
&lt;li&gt;Easy to pickup,&lt;/li&gt;
&lt;li&gt;...and unit test,&lt;/li&gt;
&lt;li&gt;Promotes integration with cloud native services (observability, databases, networking, etc).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Contenders
&lt;/h2&gt;

&lt;p&gt;There are probably many, many more, but I think I've captured the front runners here (including their marketing tag line):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://dropwizard.io/" rel="noopener noreferrer"&gt;Dropwizard&lt;/a&gt; - &lt;em&gt;"Java framework for developing ops-friendly, high-performance, RESTful web services."&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://helidon.io" rel="noopener noreferrer"&gt;Helidon&lt;/a&gt; - &lt;em&gt;"Lightweight. Fast. Crafted for Microservices."&lt;/em&gt;. This comes in two flavours:

&lt;ul&gt;
&lt;li&gt;SE - Lean reactive first framework,&lt;/li&gt;
&lt;li&gt;Microprofile (MP) - Implements the Microprofile specification for standards based API,&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://micronaut.io" rel="noopener noreferrer"&gt;Micronaut&lt;/a&gt; - &lt;em&gt;"A modern, JVM-based, full-stack framework for building modular, easily testable Microservice and Serverless applications."&lt;/em&gt;
&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://quarkus.io" rel="noopener noreferrer"&gt;Quarkus&lt;/a&gt; - &lt;em&gt;"Supersonic Subatomic Java"&lt;/em&gt;
&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://spring.io/projects/spring-boot" rel="noopener noreferrer"&gt;Spring Boot&lt;/a&gt; - &lt;em&gt;"Spring Boot makes it easy to create stand-alone, production-grade Spring based Applications that you can "just run"."&lt;/em&gt;. Two flavors will be trialled here:

&lt;ul&gt;
&lt;li&gt;WebMVC: A more imperative style leveraging Servlet API under the hood,&lt;/li&gt;
&lt;li&gt;WebFlux: Reactive ReST API leveraging Netty,&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://vertx.io" rel="noopener noreferrer"&gt;Vert.x&lt;/a&gt; - &lt;em&gt;"Reactive applications on the JVM."&lt;/em&gt;
&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Frankly, all of these tick the majority of requirements listed above and with some tweaks could get all of them without too much pain. So, we must pick another way of separating the wheat from the chaff:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Relative tests:

&lt;ul&gt;
&lt;li&gt;Build speed - You might be doing this a lot so can be a real time drain,&lt;/li&gt;
&lt;li&gt;Start-up speed - Important for scalability and recovery,&lt;/li&gt;
&lt;li&gt;Image size - Small images generally have lower attack surface and moved around the K8s estate faster,&lt;/li&gt;
&lt;li&gt;Request Round Trip Time (RTT) - It's a ReST service so we need prompt responses,&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Subjective test: How easy is it to develop with?&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;To aid for each one I'll develop a simple ReST service that has two endpoints and matching unit test:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/hello/{name}&lt;/code&gt; - This just simply returns &lt;code&gt;Hello {name}!&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/hello/error&lt;/code&gt; - This throws a custom &lt;code&gt;RuntimeException&lt;/code&gt; which must be handled as a 400 Bad Request and return &lt;code&gt;Oh no!&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This will build into an image using Jib and the same base &lt;code&gt;adoptopenjdk/openjdk16:alpine&lt;/code&gt;. Where possible this will use the recommended testing unit approach to verify the endpoints.&lt;/p&gt;

&lt;h2&gt;
  
  
  Relatively Scientific
&lt;/h2&gt;

&lt;p&gt;My methodology:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Build speed: just run maven, including &lt;code&gt;jib:dockerBuild&lt;/code&gt;, 10 times and get the mean average. This will include a test for each of the endpoints using approach recommended in their documentation,&lt;/li&gt;
&lt;li&gt;Image size: the reported size of the image from the above build from &lt;code&gt;docker images&lt;/code&gt;,&lt;/li&gt;
&lt;li&gt;Start-up speed: start the images 10 times and take the mean average,&lt;/li&gt;
&lt;li&gt;RTT: call the &lt;code&gt;/hello/bob&lt;/code&gt; endpoint using JMH using &lt;a href="https://javadoc.io/static/org.openjdk.jmh/jmh-core/1.29/org/openjdk/jmh/annotations/Mode.html#AverageTime" rel="noopener noreferrer"&gt;average time&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ As ever, take microbenchmarks with a pinch of salt. There are so many factors affecting performance that although these results may be indicative, they may also no be true for the types of workloads you have. Therefore, your mileage may vary.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Results:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Test&lt;/th&gt;
&lt;th&gt;Dropwizard&lt;/th&gt;
&lt;th&gt;Helidon MP&lt;/th&gt;
&lt;th&gt;Helidon SE&lt;/th&gt;
&lt;th&gt;Micronaut&lt;/th&gt;
&lt;th&gt;Quarkus&lt;/th&gt;
&lt;th&gt;Spring WebFlux&lt;/th&gt;
&lt;th&gt;Spring WebMVC&lt;/th&gt;
&lt;th&gt;Vert.x&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Build Speed (s)&lt;/td&gt;
&lt;td&gt;11.954&lt;/td&gt;
&lt;td&gt;9.574&lt;/td&gt;
&lt;td&gt;11.790&lt;/td&gt;
&lt;td&gt;11.322&lt;/td&gt;
&lt;td&gt;22.605&lt;/td&gt;
&lt;td&gt;14.364&lt;/td&gt;
&lt;td&gt;11.840&lt;/td&gt;
&lt;td&gt;9.345&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Image Size (MBy)&lt;/td&gt;
&lt;td&gt;382&lt;/td&gt;
&lt;td&gt;376&lt;/td&gt;
&lt;td&gt;368&lt;/td&gt;
&lt;td&gt;376&lt;/td&gt;
&lt;td&gt;375&lt;/td&gt;
&lt;td&gt;381&lt;/td&gt;
&lt;td&gt;383&lt;/td&gt;
&lt;td&gt;370&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Start Speed (ms)&lt;/td&gt;
&lt;td&gt;1,665&lt;/td&gt;
&lt;td&gt;1,573.4&lt;/td&gt;
&lt;td&gt;424.9&lt;/td&gt;
&lt;td&gt;753.5&lt;/td&gt;
&lt;td&gt;665.8&lt;/td&gt;
&lt;td&gt;1,557.8&lt;/td&gt;
&lt;td&gt;1,774.5&lt;/td&gt;
&lt;td&gt;374.6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RTT (us/op)&lt;/td&gt;
&lt;td&gt;1856.903&lt;br&gt;± 47.301&lt;/td&gt;
&lt;td&gt;1986.689&lt;br&gt;± 34.877&lt;/td&gt;
&lt;td&gt;1690.168&lt;br&gt;± 80.618&lt;/td&gt;
&lt;td&gt;1730.723&lt;br&gt;± 64.688&lt;/td&gt;
&lt;td&gt;1821.905&lt;br&gt;± 124.261&lt;/td&gt;
&lt;td&gt;1857.136&lt;br&gt;± 82.583&lt;/td&gt;
&lt;td&gt;2045.205&lt;br&gt;± 78.650&lt;/td&gt;
&lt;td&gt;1513.232&lt;br&gt;± 74.767&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Lets start with build times: Quarkus is just slow. I suspected it was the unit test (their recommended approach) or offline mode but even without either of them it, it was consistently slower than all the rest. It might also be their Jib wrapper, but as you're forced to use it not much can be done. The rest are all within a few of seconds of each other so not much to be garnered from that.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;ℹ️ Quarkus was the only framework that mandated using it's own wrapper of Jib. I tried using Jib directly but kept complaining about missing &lt;code&gt;pom.xml&lt;/code&gt;. This also meant that I had issues configuring &lt;a href="https://github.com/GoogleContainerTools/jib/blob/master/docs/faq.md#i-am-hitting-docker-hub-rate-limits-how-can-i-configure-registry-mirrors" rel="noopener noreferrer"&gt;offline mode&lt;/a&gt; causing build failures.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Next to build images: these all has the same base image and tool to try and get some comparable results and the winner was Helidon SE, which did surprise me as I was expecting Vert.x which did come in as runner up. In the middle with very similar results were Helidon MP, Quarkus and Micronaut with both Spring builds and Dropwizard at the tail. Frankly, they're all within a few MB so no clear winner. Maybe looking at using other tooling such as JLink or GraalVM would be beneficial to really slim them down, but not all of the frameworks support this.&lt;/p&gt;

&lt;p&gt;Now to the start up times: Again, lean reactive frameworks were expected to be fastest here as they're avoiding bean lifecycle management... and they were here too: Vert.x and Helidon SE getting the two stop spots. For a framework that manages bean lifecycle Quarkus does a great job of starting quickly and picks up 3rd place, that might be due to their own CDI implementation: ArC. This is closely followed up by Micronaut. At the tail Spring WebFlux, Helidon MP and Dropwizard with Spring WebMVC at the rear but all within ~200ms between the also-rans. However, with none of them being over 2 seconds, we've have come a long way [Toto] from full-fat application servers!&lt;/p&gt;

&lt;p&gt;Finally, lets review the RTT times; they are somewhat inconclusive. As anticipated the smaller, more functional reactive based APIs (Vert.x &amp;amp; Helidon SE) tend to be quicker than the more imperative approaches. The only outlier seems to be Spring Boot WebFlux, but I suspect that is due to the &lt;code&gt;ApplicationContext&lt;/code&gt; managing the bean lifecycle. Either way from difference between the best and worst was just 531 microseconds (0.5 milliseconds). For ReST traffic, you're unlikely to notice any of difference unless you're serving HUUUUUGE amounts of traffic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Subjectively Anecdotal
&lt;/h2&gt;

&lt;p&gt;First and foremost: Quarkus is just a pain to work with compared to the others. The code is pretty clean and simple, but at every turn you need to take special steps to get it to build or bundle into an image and it's just painful. A few of the issues:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I hit the dreaded &lt;a href="https://www.docker.com/increase-rate-limits" rel="noopener noreferrer"&gt;Docker Rate Limit&lt;/a&gt; during this, and for Jib is was easy to work around (i.e. &lt;code&gt;--offline&lt;/code&gt; or using &lt;a href="https://github.com/GoogleContainerTools/jib/blob/master/docs/faq.md#i-am-hitting-docker-hub-rate-limits-how-can-i-configure-registry-mirrors" rel="noopener noreferrer"&gt;mirrors&lt;/a&gt;). However, with Quarkus, with their own Jib wrapper, that's not possible,&lt;/li&gt;
&lt;li&gt;It behaves very differently between IDE and container, which makes debugging inconsistent. Their debug mode is their way of addressing it but just seems like an overengineered 'fix' to a problem that shouldn't exist,&lt;/li&gt;
&lt;li&gt;The test harness is just plain slow compared to the rest,&lt;/li&gt;
&lt;li&gt;Why do I need to use the maven plugin to add features/extensions? And it reformatted the &lt;code&gt;pom.xml&lt;/code&gt; to use spaces (yes, I'm that Maverick Renegade that uses tabs!), &lt;/li&gt;
&lt;li&gt;Why do I need to use custom tooling to create projects? Why isn't there Maven archetypes for this like everyone else.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The next most difficult was Vert.x, largely due to the test harness. It does concern me that you have to reach out to a 3rd party to get &lt;a href="https://reactiverse.io/reactiverse-junit5-extensions/" rel="noopener noreferrer"&gt;another library&lt;/a&gt; that works around all the problems created by your own test fixtures. Once up and running is was fairly straight forward to use but certainly not developer friendly at the start.&lt;/p&gt;

&lt;p&gt;Functional/Reactive patterns have somewhat of a steep learning curve so a graceful, easy to follow API massively helps adoption. I couldn't call Spring Boot WebFlux graceful... in fact, quite the contrary, it's a total dog's dinner! Really verbose, confusing naming, difficult error handling and just looks horrible at the end. It took a fair while to be up and running, but I don't envy the person that has to maintain this.&lt;/p&gt;

&lt;p&gt;The rest of them were all pretty straightforward and I couldn't materially differentiate between them.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;ℹ️ Code for this is available &lt;a href="https://github.com/dansiviter/microservices-frameworks/tree/dev.to"&gt;here&lt;/a&gt;. If I've made some glaring omissions, please do point them out. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Lets start at the weaker end: The two that get the most column inches within blogs and news sites are the two I'd avoid:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Quarkus - It's just maddening and therefore frustrating to use. I don't understand why they need to engineer solutions to problems of their own making!? If I can't trust the behaviour locally will be the same as in test or in a container I can't trust it in production. About its only benefit is it starts quicker than other frameworks that implement Microprofile,&lt;/li&gt;
&lt;li&gt;Spring Boot - They need to just put it in the bin and start again; WebFlux is difficult to use, WebMVC is a poor version of JAX-RS, XML config should have been dropped years ago and Spring Boot AutoConfiguration Magic is just non-sensical for anything other than a &lt;em&gt;very&lt;/em&gt; basic application. I have had issues where totally unrelated things are initialising because it found a library on the classpath... this is too easy to do and a huge security concern.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I know many will disagree with me on this, especially if they've already bought into either of these, but I see no benefit of using them over the other options that are far more straightforward to use. This would probably be a different story if they had some significant benefit (e.g. simpler to use or significantly faster), but they don't.&lt;/p&gt;

&lt;p&gt;I'm struggling to pick an outright winner here. At the upper end we have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Helidon - Near the top in every test and it's flexible SE and MP modes (which can mix and match too) are a great feature, &lt;/li&gt;
&lt;li&gt;Micronaut - Just really well thought out and simple to use,&lt;/li&gt;
&lt;li&gt;Dropwizard - Biggest surprise here. Few bells and whistles, but gets the job done with little fuss,&lt;/li&gt;
&lt;li&gt;Vert.x - Although somewhat difficult to test Vert.x gets credit for being really lean and just plain damn fast!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With all of these, you'll be up and running in very little time and require no special fixtures to build them into container images, but if I had to pick one it would be Helidon. I have been a fan for a while (and, full disclosure, also contributed code) and doing these tests has shown that it's certainly up there with the best. &lt;a href="https://github.com/oracle/helidon/issues/1412" rel="noopener noreferrer"&gt;Project Loom&lt;/a&gt; and &lt;a href="https://github.com/oracle/helidon/issues/2670" rel="noopener noreferrer"&gt;&lt;code&gt;io_uring&lt;/code&gt;&lt;/a&gt; support are exciting which should generate some serious performance improvements. However, in the meantime I'm going to look much closer at Micronaut and Dropwizard, unless I raw performance which Vert.x would be high on my list.&lt;/p&gt;

&lt;p&gt;As for my team, what did they pick? Micronaut.&lt;/p&gt;

</description>
      <category>java</category>
      <category>microservices</category>
      <category>healthydebate</category>
      <category>performance</category>
    </item>
    <item>
      <title>Not another logger!</title>
      <dc:creator>dansiviter</dc:creator>
      <pubDate>Fri, 05 Mar 2021 13:31:26 +0000</pubDate>
      <link>https://dev.to/dansiviter/not-another-logger-2lc4</link>
      <guid>https://dev.to/dansiviter/not-another-logger-2lc4</guid>
      <description>&lt;p&gt;Java is not short of logging libraries: &lt;a href="https://logging.apache.org/log4j/2.x/" rel="noopener noreferrer"&gt;Log4J&lt;/a&gt;, &lt;a href="http://www.slf4j.org/" rel="noopener noreferrer"&gt;SLF4J&lt;/a&gt;, &lt;a href="https://github.com/jboss-logging/jboss-logmanager" rel="noopener noreferrer"&gt;JBoss Logger Manager&lt;/a&gt;, &lt;a href="https://google.github.io/flogger/" rel="noopener noreferrer"&gt;Flogger&lt;/a&gt;, &lt;a href="https://commons.apache.org/proper/commons-logging/" rel="noopener noreferrer"&gt;Apache Commons Logger&lt;/a&gt;, &lt;a href="http://logback.qos.ch/" rel="noopener noreferrer"&gt;Logback&lt;/a&gt; plus probably many more I've not heard of. There is the also much unloved &lt;code&gt;java.util.logger&lt;/code&gt; (or JUL) which is part of Java since v1.4. Even with all these options I've been thinking that we need a new logger.&lt;/p&gt;

&lt;p&gt;Step away from the pitch fork and hear me out.&lt;/p&gt;

&lt;p&gt;Many of these libraries were created to address a particular concern, primarily performance:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Avoiding array allocation or auto-boxing causing memory churn,&lt;/li&gt;
&lt;li&gt;IO performance to console or file,&lt;/li&gt;
&lt;li&gt;Asynchronous execution,&lt;/li&gt;
&lt;li&gt;Low/zero garbage.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With all these other libraries it's a really crowded space and it doesn't help with minimal microservices. Low/zero garbage would require a total re-write and if that's a concern stick with Log4J v2, but we can address the others... so I did: &lt;a href="https://juli.dansiviter.uk" rel="noopener noreferrer"&gt;&lt;code&gt;dansiviter.uk:juli&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Oh no, not another logging framework I hear you groan... well, kinda, but first lets see it in action.&lt;/p&gt;

&lt;h1&gt;
  
  
  Spring Clean
&lt;/h1&gt;

&lt;p&gt;It's March, so what better time to clean up your code. The current way of using JUL would typically look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;log&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getLogger&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;getClass&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getName&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isLoggable&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Level&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;INFO&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;  &lt;span class="c1"&gt;// &amp;lt;- needed to avoid array initialisation and auto-boxing&lt;/span&gt;
  &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;log&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Level&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;INFO&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Hello {0}"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"world"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// Grrr, can't use #info(...) with params!?&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This could be simplified to look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;log&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;LogProducer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;log&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;MyLog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;hello&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"world"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What is this heresy?! Well, I've created an interface which, via annotation processing, creates an implementation that delegates to JUL but layers on the &lt;code&gt;#isLoggable(...)&lt;/code&gt; call automatically to avoids the dreaded array initialisation and auto-boxing unless it's strictly needed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Log&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;MyLog&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nd"&gt;@Message&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello {0}"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
  &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;hello&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;str&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To verify if we get a performance hit I wrote a small JMH  benchmark comparing the two ways except forcing both auto-boxing and array initialisation in the scenarios:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Benchmark                                          Mode  Cnt      Score       Error  Units
LogBenchmark.legLog                               thrpt   25  27921.784 ±  1280.815  ops/s
LogBenchmark.legLog:Used memory heap              thrpt   25  26950.054 ±  8478.381     KB
LogBenchmark.newLog                               thrpt   25  40111.066 ±  1170.407  ops/s
LogBenchmark.newLog:Used memory heap              thrpt   25  25288.212 ± 10124.257     KB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Figures are based on my i7-1065G7 using Docker for Desktop so are purely indicative, however the new approach gives both a ~44% increase in throughput and a ~6% decrease in memory usage. I should mention that on repeat runs I did see some cases where memory usage was higher but throughput was consistently around 40% improvement.&lt;/p&gt;

&lt;p&gt;This is great start, but we can go further.&lt;/p&gt;

&lt;h1&gt;
  
  
  Asynchronicity
&lt;/h1&gt;

&lt;p&gt;Every single implementation of &lt;code&gt;java.util.logger.Handler&lt;/code&gt; provided with Java is synchronous; it puts IO directly within the critical path of execution. Log4J and Logback have asynchronous implementations to decouple this and speed up the code, so why can't JUL? So, I created &lt;code&gt;uk.dansiviter.juli.AsyncHandler&lt;/code&gt; to address this.&lt;/p&gt;

&lt;p&gt;There is a concrete implementation of this which delegates to &lt;code&gt;java.util.logging.ConsoleHandler&lt;/code&gt;. So I created some more JMH tests:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Benchmark                                               (handlerName)   Mode  Cnt      Score       Error  Units
ConsoleHandlerBenchmark.handler                        ConsoleHandler  thrpt   25  31041.836 ±  7986.031  ops/s
ConsoleHandlerBenchmark.handler:Used memory heap       ConsoleHandler  thrpt   25  37237.451 ± 15369.245     KB
ConsoleHandlerBenchmark.handler                   AsyncConsoleHandler  thrpt   25  85540.769 ±  6482.011  ops/s
ConsoleHandlerBenchmark.handler:Used memory heap  AsyncConsoleHandler  thrpt   25  41799.724 ± 16472.828     KB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a ~12% increase in memory usage amount of memory usage we get a ~175% increase in throughput is great, however it's important to have some context with these results. This uses &lt;code&gt;java.util.concurrent.Flow&lt;/code&gt; under the hood and it uses a default buffer size of 256. To prevent log messages being dropped backpressure is handled by blocking when the buffer is full. Therefore, buffer saturation will slow it down, hence the increased variance. However, in reality saturation will rarely occur and this will decouple IO from the log message creation improving performance. In smaller, less 'chatty' tests, massive (often incalculable) improvements are common:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;x50 - Sync=PT0.018177S, Async=PT0S (NaN%)  // &amp;lt;- async so fast it didn't even register
x100 - Sync=PT0.0317765S, Async=PT0S (NaN%)  // &amp;lt;- ...and again
x200 - Sync=PT0.0644191S, Async=PT0.0009579S (6725.03400%)
x400 - Sync=PT0.1168272S, Async=PT0.0450558S (259.294500%)  // &amp;lt;- dramatic slow down
x800 - Sync=PT0.2164862S, Async=PT0.1705798S (126.912000%)
x1600 - Sync=PT0.4423355S, Async=PT0.4237862S (104.377000%)  // &amp;lt;- almost parity
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;See &lt;code&gt;uk.dansiviter.juli.RoughBenchmark&lt;/code&gt; for code.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;ℹ️ With modern containerised workloads a &lt;code&gt;FileHandler&lt;/code&gt; is pretty pointless as they rarely have persistent disks to write to and generally write directly to STDERR/STDOUT or to the log aggregator directly. So I've not created an asynchronous version.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  Summary
&lt;/h1&gt;

&lt;p&gt;So, with this logging wrapper and asynchronous handlers I can have my cake an eat it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cleaner code,&lt;/li&gt;
&lt;li&gt;Performance improvement (often huge!),&lt;/li&gt;
&lt;li&gt;Minimal increase in binary size (~14KB).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;However, I feel I should address the title of this post. No, this isn't a new logger, but it does make the existing logger a much nicer experience. Check out the &lt;a href="https://juli.dansiviter.uk" rel="noopener noreferrer"&gt;README&lt;/a&gt; which explains more about how it's used including exception handling.&lt;/p&gt;

&lt;p&gt;I'm already using this with a CDI project which injects instances into the code making unit testing super-simple. I plan on seeing if this plays nicely with JPMS and GraalVM soon for even more leanness.&lt;/p&gt;

&lt;p&gt;I'm ready for my lynching now... do your worst! &lt;/p&gt;

</description>
      <category>java</category>
      <category>performance</category>
      <category>microservices</category>
      <category>observability</category>
    </item>
    <item>
      <title>Quest for the Holy Graal</title>
      <dc:creator>dansiviter</dc:creator>
      <pubDate>Fri, 19 Feb 2021 19:05:01 +0000</pubDate>
      <link>https://dev.to/dansiviter/quest-for-the-holy-graal-1hlg</link>
      <guid>https://dev.to/dansiviter/quest-for-the-holy-graal-1hlg</guid>
      <description>&lt;p&gt;Statically linked Java images are a superb idea; what's not to love about super small, super fast images that have a low attack surface. However, the reality is far from it. This post is describing some of the &lt;del&gt;fun&lt;/del&gt; frustration I've had trying to get something rather basic working: namely &lt;a href="https://cloud.google.com/logging" rel="noopener noreferrer"&gt;Cloud Logging&lt;/a&gt;. &lt;/p&gt;

&lt;h1&gt;
  
  
  &lt;code&gt;UnknownHostException&lt;/code&gt;
&lt;/h1&gt;

&lt;p&gt;The first problem is statically linked Graal Native Images cannot do some things without &lt;code&gt;libc&lt;/code&gt; set, in this case they can't perform a DNS lookup which results in a &lt;code&gt;java.net.UnknownHostException&lt;/code&gt;. I'm not going to dwell on how stupid this is and just move on. There are rough instructions available on the &lt;a href="https://www.graalvm.org/reference-manual/native-image/StaticImages/" rel="noopener noreferrer"&gt;Graal site&lt;/a&gt; to use &lt;code&gt;musl&lt;/code&gt; for this but a quick Google came up with something from, erm, &lt;a href="https://cloud.google.com/blog/topics/developers-practitioners/comparing-containerization-methods-buildpacks-jib-and-dockerfile" rel="noopener noreferrer"&gt;Google Cloud&lt;/a&gt;. I bodged this into Helidon and... it didn't build:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#11 274.5 [WARNING] Error: Detected a direct/mapped ByteBuffer in the image heap. A direct ByteBuffer has a pointer to unmanaged C memory, and C memory from the image generator is not available at image runtime.A mapped ByteBuffer references a file descriptor, which is no longer open and mapped at run time.   To see how this object got instantiated use --trace-object-instantiation=java.nio.DirectByteBuffer. The object was probably created by a class initializer and is reachable from a static field. You can request class initialization at image runtime by using the option --initialize-at-run-time=&amp;lt;class-name&amp;gt;. Or you can write your own initialization methods and call them explicitly from your main entry point.
#11 274.5 [WARNING] Trace: Object was reached by
#11 274.5 [WARNING]     reading field io.grpc.netty.shaded.io.netty.buffer.PoolChunk.memory of
#11 274.5 [WARNING]             constant io.grpc.netty.shaded.io.netty.buffer.PoolChunk@42d125b0 reached by
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Googling
&lt;/h1&gt;

&lt;p&gt;The build problem was due to the embedded Netty within the gRPC libraries. Fortunately the fix for this was pretty easy: &lt;a href="https://github.com/GoogleCloudPlatform/google-cloud-graalvm-support" rel="noopener noreferrer"&gt;Google have their own library&lt;/a&gt; for it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;com.google.cloud&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;google-cloud-graalvm-support&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;0.3.0&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this added it built and ran, but now it couldn't find any GCP config:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;java.lang.IllegalArgumentException: A project ID is required for this service but could not be determined from the builder or the environment.  Please set a project ID using the builder.
        at com.google.common.base.Preconditions.checkArgument(Preconditions.java:142)
        at com.google.cloud.ServiceOptions.&amp;lt;init&amp;gt;(ServiceOptions.java:304)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Confuddling
&lt;/h1&gt;

&lt;p&gt;Normally, I would have just mounted the volume into the users home, but it appears Graal doesn't have value for &lt;code&gt;user.home&lt;/code&gt;. I've not digged into this to confirm it, so thought I'd just work around it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;run&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--rm&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-it&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-p&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;8080:8080&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;-e&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;CLOUDSDK_CONFIG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;/gcloud&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;-v&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$&lt;/span&gt;&lt;span class="nn"&gt;Env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;APPDATA&lt;/span&gt;&lt;span class="nx"&gt;/gcloud/:/gcloud:ro&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;helidon-quickstart-mp:native&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Booom! It ran up without any errors.&lt;/p&gt;

&lt;p&gt;Alas, there was one final hiccup which was answered by &lt;a href="https://github.com/microsoft/WSL/issues/5324" rel="noopener noreferrer"&gt;microsoft/WSL#5324&lt;/a&gt;. In a nutshell, if you let Windows go to sleep then the clock in WSL2 drifts. That meant I couldn't find any of my log messages as they were timestamped incorrectly. With WSL restarted we got some logs in Cloud Logging. Hurrah!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fa3gh7gk6jy2ni8xbtfmu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fa3gh7gk6jy2ni8xbtfmu.png" alt="Alt Text" width="800" height="97"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The resulting image is a somewhat portly 116MB which is 24MB larger than &lt;a href="https://dev.to/dansiviter/java-16-ea-alpine-jlink-vs-graal-243d"&gt;last time&lt;/a&gt; and only 11MB off Alpine JLink. I don't think could be explained by the extra libraries but something to worry about another day. However, it starts in around 160ms with &lt;code&gt;-m 32m&lt;/code&gt; set on the docker container which can't be sniffed at. Regardless of how compelling Graal is, there are lots of bear traps to be aware of. I hope this improves soon.&lt;/p&gt;

&lt;p&gt;Now for metrics and tracing...&lt;/p&gt;

</description>
      <category>java</category>
      <category>linux</category>
      <category>docker</category>
      <category>performance</category>
    </item>
    <item>
      <title>Java 16 EA Alpine &amp; JLink vs Graal</title>
      <dc:creator>dansiviter</dc:creator>
      <pubDate>Sun, 17 Jan 2021 19:24:29 +0000</pubDate>
      <link>https://dev.to/dansiviter/java-16-ea-alpine-jlink-vs-graal-243d</link>
      <guid>https://dev.to/dansiviter/java-16-ea-alpine-jlink-vs-graal-243d</guid>
      <description>&lt;p&gt;My first post... hurrah!&lt;/p&gt;

&lt;p&gt;As the saying goes: good things come in small packages. For container images it means a smaller attack surface and faster to deploy and start-up. Unfortunately, Java hasn't been the best candidate for containerisation but that has changed with a few interesting developments:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="http://openjdk.java.net/jeps/386" rel="noopener noreferrer"&gt;JEP 386: Alpine Linux Port&lt;/a&gt;: There have been other Alpine builds of various quality, but now it's official,&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://openjdk.java.net/jeps/282" rel="noopener noreferrer"&gt;JEP 282: jlink: The Java Linker&lt;/a&gt;: Leveraging the Module System to create builds with just the bits you use,&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.graalvm.org/reference-manual/native-image/" rel="noopener noreferrer"&gt;Graal Native Image&lt;/a&gt;: A statically linked Java image.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With Java 16 we can now use JLink with Alpine images to get all the next gen GCs (a-la ZGC and Shenandoah) goodness.&lt;/p&gt;

&lt;p&gt;I thought it would be useful to compare and contrast them with a simple ReST service. For this I'm using &lt;code&gt;io.helidon.archetypes:helidon-quickstart-mp:2.2.0&lt;/code&gt; archetype as a base with some customisations to the &lt;code&gt;Dockerfile&lt;/code&gt;s.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;ℹ️ This is a Microprofile implementation so that means full-fat CDI, JAX-RS goodness which IMHO gives a good compromise between standards, developer productivity and scalability.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There were some changes required to the files.&lt;/p&gt;

&lt;p&gt;For &lt;code&gt;Dockerfile&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;-FROM openjdk:11-jre-slim
&lt;/span&gt;&lt;span class="gi"&gt;+FROM openjdk:16-jdk-alpine
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For &lt;code&gt;Dockerfile.jlink&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;-FROM maven:3.6.3-jdk-11-slim as build
&lt;/span&gt;&lt;span class="gi"&gt;+FROM openjdk:16-jdk-alpine as build
+RUN apk add --no-cache bash maven
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gd"&gt;-FROM debian:stretch-slim
&lt;/span&gt;&lt;span class="gi"&gt;+FROM alpine:3.13.0
+RUN apk add --no-cache bash
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;ℹ️ We must add &lt;code&gt;bash&lt;/code&gt; to the Alpine JLink image which adds 2MB as Helidon is using a little script to start up. Ideally if productionising this I would aim to remove this dependency.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So, what do the images sizes look like using the Slim JDK image as a control:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Image&lt;/th&gt;
&lt;th&gt;Size&lt;/th&gt;
&lt;th&gt;Change&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;JDK 11 Slim (Debian)&lt;/td&gt;
&lt;td&gt;220MB&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Alpine&lt;/td&gt;
&lt;td&gt;338MB&lt;/td&gt;
&lt;td&gt;154%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Alpine JLink&lt;/td&gt;
&lt;td&gt;127MB&lt;/td&gt;
&lt;td&gt;58%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Graal native image&lt;/td&gt;
&lt;td&gt;94MB&lt;/td&gt;
&lt;td&gt;43%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;As expected Graal based on &lt;code&gt;scratch&lt;/code&gt; image is very small followed by Alpine JLink, Slim and finally Alpine at the rear. Now for start up time:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Image&lt;/th&gt;
&lt;th&gt;Time&lt;/th&gt;
&lt;th&gt;Change&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;JDK 11 Slim (Debian)&lt;/td&gt;
&lt;td&gt;4,493ms&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Alpine&lt;/td&gt;
&lt;td&gt;3,310ms&lt;/td&gt;
&lt;td&gt;74%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Alpine JLink&lt;/td&gt;
&lt;td&gt;1,844ms&lt;/td&gt;
&lt;td&gt;41%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Graal native image&lt;/td&gt;
&lt;td&gt;80ms&lt;/td&gt;
&lt;td&gt;2%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Again, not many surprises here except Graal is faster than I thought!&lt;/p&gt;

&lt;h1&gt;
  
  
  Thoughts
&lt;/h1&gt;

&lt;p&gt;It's a very interesting time for Java and containers. Java can be lean, light and &lt;a href="https://medium.com/helidon/can-java-microservices-be-as-fast-as-go-5ceb9a45d673" rel="noopener noreferrer"&gt;fast&lt;/a&gt; which still makes it relevant in a container native deployment.&lt;/p&gt;

&lt;p&gt;Both JLink (&lt;a href="https://github.com/grpc/grpc-java/issues/3522" rel="noopener noreferrer"&gt;gRPC#3522&lt;/a&gt;) and Graal have some issues; I'm especially concerned about the Serial GC in Graal so will be putting that under some stress soon to see if that confirms my suspicions. I'll also be good when some Java 16 JRE Alpine images appear as the JDK is too bloaty.&lt;/p&gt;

&lt;p&gt;Jury is out if Graal or JLink is my preferred approach until I can stick load on them and see how they behave in Kubernetes but things are looking good.&lt;/p&gt;

</description>
      <category>java</category>
      <category>linux</category>
      <category>docker</category>
      <category>performance</category>
    </item>
  </channel>
</rss>
