Skip to content

Commit 95076d9

Browse files
authored
Merge pull request #248 from graalvm/sms-revisions
Upgraded Tiny Containers
2 parents 0e6a442 + c2fe7b0 commit 95076d9

18 files changed

+99
-53
lines changed

.github/workflows/tiny-java-containers.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ jobs:
3030
#
3131
./setup-musl.sh
3232
#
33+
# Download upx
34+
#
35+
./setup-upx.sh
36+
#
3337
# Hello World
3438
#
3539
cd helloworld

tiny-java-containers/README.md

Lines changed: 50 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
This example shows how a simple Java application and a simple web
44
server can be compiled to produce very small Docker container images.
55

6-
The smallest container images contains just an executable. But, since there's
6+
The smallest container images contains just an executable. But since there's
77
nothing in the container image except the executable, including no `libc` or other
88
shared libraries, an executable has to be fully statically linked with all
99
needed libraries and resources.
@@ -20,21 +20,37 @@ App](images/youtube.png)](https://youtu.be/6wYrAtngIVo)
2020

2121
## Prerequisites
2222

23+
* x86 Linux (but the few binary dependencies could easily be changed for aarch64)
2324
* Docker installed and running. It should work fine with [podman](https://podman.io/) but it has not been tested.
2425
* [GraalVM for JDK 21](https://www.graalvm.org/downloads/)
2526

2627
> We recommend Oracle GraalVM for the best experience. It is licensed under the [GraalVM Free Terms and Conditions (GFTC)](https://www.oracle.com/downloads/licenses/graal-free-license.html) license, which permits use by any user including commercial and production use.
27-
GraalVM Community Edition JDK 21 works too, but Native Image generated executables sizes will differ.
28+
GraalVM Community Edition for JDK 21 works too, but Native Image generated executables sizes will differ.
2829

2930
> These instructions have only been tested on Linux x64.
3031
3132
## Setup
3233

34+
You need the following zlib packages installed:
35+
* zlib.x86_64
36+
* zlib-devel.x86_64
37+
* zlib-static.x86_64
38+
39+
On Oracle Linux, you can install with:
40+
```sh
41+
sudo yum install -y zlib.x86_64
42+
sudo yum install -y zlib-devel.x86_64
43+
sudo yum install -y zlib-static.x86_64
44+
```
45+
3346
Clone this Git repo and in your Linux shell type the following to download and
3447
configure the `musl` toolchain.
3548

3649
![](images/keyboard.jpg) `./setup-musl.sh`
3750

51+
Download [upx](https://upx.github.io/):
52+
53+
![](images/keyboard.jpg) `./setup-upx.sh`
3854

3955
## Hello World
4056

@@ -62,7 +78,8 @@ equivalent. They just print "Hello World". But there are a few points worth
6278
noting:
6379

6480
1. The executable generated by GraalVM Native Image using the
65-
`--static --libc=musl` options is a fully self-contained executable which can be confirmed by examining it with `ldd`:
81+
`--static --libc=musl` options is a fully self-contained executable which can be
82+
confirmed by examining it with `ldd`:
6683

6784
![](images/keyboard.jpg) `ldd hello`
6885

@@ -80,22 +97,26 @@ noting:
8097
executable, you can be confident it is also statically linked.
8198

8299
2. Both executables are the result of compiling a Java bytecode application into
83-
native machine code. The uncompressed executable is only 5.2MB! There's no
100+
native machine code. The uncompressed executable is only ~6.3MB! There's no
84101
JVM, no JARs, no JIT compiler and none of the overhead it imposes. Both
85102
start extremely fast as there is minimal startup cost.
86103

87-
3. The `upx` compressed executable is about 60% smaller, 1.5MB vs. 5.2MB! With
88-
`upx`` the application self-extracts quickly but does incur a cost of about
89-
100ms for decompression. See this blog for a deep dive on [GraalVM Native Image and UPX](https://medium.com/graalvm/compressed-graalvm-native-images-4d233766a214).
104+
3. The `upx` compressed executable is over 70% smaller, 1.7MB vs. 6.3MB! With
105+
`upx` the application self-extracts quickly but does incur a cost of about
106+
100ms for decompression. See this blog for a deep dive on [GraalVM Native
107+
Image and
108+
UPX](https://medium.com/graalvm/compressed-graalvm-native-images-4d233766a214).
90109

91110
### Container Images
92111

93112
The size of the `scratch`-based container image is slightly more than the `hello.upx`
94113
executable.
95114

115+
![](images/keyboard.jpg) `docker images hello`
116+
96117
```shell
97-
REPOSITORY TAG IMAGE ID CREATED SIZE
98-
hello upx 935e5e3549e6 1 second ago 1.51MB
118+
REPOSITORY TAG IMAGE ID CREATED SIZE
119+
hello upx 4d122bd39a8a About a minute ago 1.78 MB
99120
```
100121

101122
This is a tiny container image and yet it contains a fully functional and
@@ -167,29 +188,29 @@ When complete you can see the sizes of the various versions:
167188

168189
```shell
169190
REPOSITORY TAG IMAGE ID CREATED SIZE
170-
jwebserver distroless-java-base.jlink fae0bb62eca7 6 minutes ago 74.9MB
171-
jwebserver scratch.static-upx 676069a2a359 6 minutes ago 5.43MB
172-
jwebserver alpine.static 14e748264a99 6 minutes ago 25MB
173-
jwebserver distroless-static.static 5591e1a2658a 6 minutes ago 21.8MB
174-
jwebserver scratch.static ef1ad68037ec 7 minutes ago 19.4MB
175-
jwebserver distroless-base.mostly cc8612887001 7 minutes ago 39.7MB
176-
jwebserver distroless-java-base.dynamic d2f802cf3def 8 minutes ago 58.7MB
191+
jwebserver distroless-java-base.jlink 414d84f8b7c7 22 minutes ago 132 MB
192+
jwebserver scratch.static-upx 47aabdd14c04 22 minutes ago 4.71 MB
193+
jwebserver alpine.static 783ab3a60248 22 minutes ago 23.4 MB
194+
jwebserver distroless-static.static c894f14d4068 22 minutes ago 18.7 MB
195+
jwebserver scratch.static 034cfbdf3577 22 minutes ago 15.7 MB
196+
jwebserver distroless-base.mostly e99811e574d3 22 minutes ago 37.6 MB
197+
jwebserver distroless-java-base.dynamic 72a210e3c705 23 minutes ago 50.6 MB
177198
```
178199

179200
Sorting by size, it's clear that the fully statically linked GraalVM Native
180-
Image generated executable that's compressed and packaged on `scratch` is the
181-
smallest at just 5.43MB, only 7% of the size of the `jlink` version running on
182-
the JVM.
201+
Image generated executable that's compressed and packaged on `scratch`
202+
(`scratch.static-upx`) is the smallest at just 4.71MB, less than 4% of the size
203+
of the `jlink` version (`distroless-java-base.jlink`) running on the JVM.
183204

184205
| Base Image | App Version | Size (MB) |
185206
| -------------------- | ---------------------------------- | --------- |
186-
| Distroless Java Base | jlink | 74.90 |
187-
| Distroless Java Base | native dynamic linked | 58.70 |
188-
| Distroless Base | native *mostly* static linked | 39.70 |
189-
| Alpine | native *fully* static | 25.00 |
190-
| Distroless Static | native *fully* static | 21.80 |
191-
| Scratch | native *fully* static | 19.40 |
192-
| Scratch | *compressed* native *fully* static | 5.43 |
207+
| Distroless Java Base | jlink | 132.00 |
208+
| Distroless Java Base | native *dynamic* linked | 50.60 |
209+
| Distroless Base | native *mostly* static linked | 37.60 |
210+
| Alpine | native *fully* static linked | 23.40 |
211+
| Distroless Static | native *fully* static linked | 18.70 |
212+
| Scratch | native *fully* static linked | 15.70 |
213+
| Scratch | *compressed* native *fully* static | 4.71 |
193214

194215
Running a container image is straight forward, just remember to map the ports, e.g.:
195216

@@ -204,10 +225,11 @@ the index.html file.
204225

205226
## Wrapping Up
206227

207-
Fully functional, albeit minimal, Java "microservice" was compiled
228+
A fully functional, albeit minimal, Java "microservice" was compiled
208229
into a native Linux executable and packaged into Distroless, Alpine, and
209230
`scratch`-based container images thanks to GraalVM Native Image's support for
210231
various linking options including fully static linking with the `musl` libc.
211232

212233
To learn more about linking options check out [Static and Mostly Static
213-
Images](https://www.graalvm.org/latest/reference-manual/native-image/guides/build-static-executables/) in the GraalVM docs.
234+
Images](https://www.graalvm.org/latest/reference-manual/native-image/guides/build-static-executables/)
235+
in the GraalVM docs.

tiny-java-containers/clean.sh

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
#!/bin/sh
22

3-
set -x
3+
set +e
44

55
rm -rf x86_64-linux-musl-native zlib-*
6-
./helloworld/clean.sh
7-
./jwebserver/clean.sh
6+
cd helloworld
7+
./clean.sh || true
8+
cd ../jwebserver
9+
./clean.sh || true
Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
11
#!/bin/sh
22

3+
set -e
4+
35
TOOLCHAIN_DIR=`pwd`/../x86_64-linux-musl-native
46
CC=${TOOLCHAIN_DIR}/bin/gcc
57
PATH=${TOOLCHAIN_DIR}/bin:${PATH}
68

7-
set -x
8-
99
# Compile Java source file
1010
javac Hello.java
1111

1212
# Compile Java bytecodes into a fully statically linked executable
13-
native-image --static --libc=musl -o hello Hello
14-
rm *.txt
13+
native-image -Ob --static --libc=musl -o hello Hello
14+
rm -rf *.txt
1515

1616
# Create a compressed version of the executable
17-
upx --lzma --best hello -o hello.upx
17+
../upx --lzma --best hello -o hello.upx
1818

1919
# Package the compressed executable in a simple scratch container image
2020
docker build . -t hello:upx
Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
#!/bin/sh
22

3-
rm hello hello.upx
4-
rm *.txt
5-
rm *.class
6-
docker images -q hello | awk '{print($1)}' | xargs docker rmi
3+
set +e
4+
5+
rm -rf hello hello.upx
6+
rm -rf *.txt
7+
rm -rf *.class
8+
docker images -q hello | awk '{print($1)}' | xargs docker rmi || true

tiny-java-containers/jwebserver/Dockerfile.alpine.static

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM alpine
1+
FROM alpine:3
22
COPY jwebserver.static /
33
COPY index.html /web/index.html
44
EXPOSE 8000

tiny-java-containers/jwebserver/Dockerfile.distroless-base.mostly

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM gcr.io/distroless/base-debian11
1+
FROM gcr.io/distroless/base-debian12
22
COPY jwebserver.mostly /
33
COPY index.html /web/index.html
44
EXPOSE 8000

tiny-java-containers/jwebserver/Dockerfile.distroless-java-base.dynamic

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM gcr.io/distroless/java-base-debian11
1+
FROM gcr.io/distroless/java-base-debian12
22
COPY jwebserver.dynamic /
33
COPY index.html /web/index.html
44
EXPOSE 8000

tiny-java-containers/jwebserver/Dockerfile.distroless-java-base.jlink

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM gcr.io/distroless/java-base-debian11
1+
FROM gcr.io/distroless/java-base-debian12
22
COPY jwebserver-jlink /usr/lib/java
33
COPY index.html /web/index.html
44
EXPOSE 8000

tiny-java-containers/jwebserver/Dockerfile.distroless-static.static

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM gcr.io/distroless/static-debian11
1+
FROM gcr.io/distroless/static-debian12
22
COPY jwebserver.static /
33
COPY index.html /web/index.html
44
EXPOSE 8000

0 commit comments

Comments
 (0)