# On M1 mac
$ docker run \
--platform linux/amd64 \
--pull always \
-it \
--rm \
-p 8080:80 \
--name openjdk-playground \
ghcr.io/sureshg/containers:openjdk-latest
# Default busybox image should work.
$ brew install cdebug
$ cdebug exec \
--privileged \
-it \
--rm \
--platform linux/amd64 \
docker://openjdk-playground
# Extract a file from the Docker environment and store it locally
$ docker run \
--rm \
--entrypoint cat \
busybox:latest \
'/bin/ls' > ls
# OR copy files from a container
$ docker cp <CONTAINER>:/app/app/jar .
# OR
$ id=$(docker create ghcr.io/sureshg/containers:openjdk-latest)
$ docker cp $id:/app/app.jar - > app.tar
$ docker rm -v $id
JVM Ergonomics and Container logs
$ cat << EOF >App.java
import static java.lang.System.out;
public class App {
void main() {
var version = "• Java %s running on %s %s".formatted(
System.getProperty("java.version"),
System.getProperty("os.name"),
System.getProperty("os.arch"));
out.println(version);
long unit = 1024 * 1024L;
long heapSize = Runtime.getRuntime().totalMemory();
long heapFreeSize = Runtime.getRuntime().freeMemory();
long heapUsedSize = heapSize-heapFreeSize;
long heapMaxSize = Runtime.getRuntime().maxMemory();
out.println("• [CPU] Active Processors: " + Runtime.getRuntime().availableProcessors());
out.println("• [Mem] Current Heap Size (Committed) : " + heapSize/unit + " MiB");
out.println("• [Mem] Current Free memeory in Heap : " + heapFreeSize/unit + " MiB");
out.println("• [Mem] Currently used memory : " + heapUsedSize/unit + " MiB");
out.println("• [Mem] Max Heap Size (-Xmx) : " + heapMaxSize/unit + " MiB");
}
}
EOF
$ docker run \
-it \
--rm \
--cpus=2 \
--memory=512m \
--pull always \
--mount type=bind,source=$(pwd),destination=/app,readonly \
--mount type=bind,source=/,destination=/host,readonly \
--name openjdk \
openjdk:24-slim \
java \
--source 24 --enable-preview \
-XX:+UnlockExperimentalVMOptions \
-XX:+UnlockDiagnosticVMOptions \
-XX:+PrintFlagsFinal \
-XX:MaxRAMPercentage=0.8 \
-XX:-MaxFDLimit \
-Xlog:gc \
/app/App.java \
| grep -e "Use.*GC" -e "Active" -e "Using" -e "Max.*Limit" -e "Container" -e "•"
JVM default GC
# OpenJDK reverts to Serial GC when it detects < 2 CPUs or < 2GB RAM
$ docker run -it --rm --cpus=1 --memory=1G openjdk:24-slim java -Xlog:gc --version
#[0.007s][info][gc] Using Serial
$ docker run -it --rm --platform=linux/aarch64 alpine uname -m
aarch64
$ docker run -it --rm --platform=linux/amd64 alpine uname -m
x86_64
$ docker run --rm arm64v8/alpine uname -a
$ docker run --rm arm32v7/alpine uname -a
$ docker run --rm ppc64le/alpine uname -a
$ docker run --rm s390x/alpine uname -a
$ docker run --rm tonistiigi/debian:riscv uname -a
Netcat Webserver
FROM alpine
ENTRYPOINT while :; do nc -k -l -p $PORT -e sh -c 'echo -e "HTTP/1.1 200 OK\n\n hello, world"'; done
# https://github.com/jamesward/hello-netcat
# docker build -t hello-netcat .
# docker run -p 8080:80 -e PORT=80 -it hello-netcat
Forwards Logs
# forward request and error logs to docker log collector
RUN ln -sf /dev/stdout /var/log/nginx/access.log \
&& ln -sf /dev/stderr /var/log/nginx/error.log
# OR output directly to
/proc/self/fd/1 (STDOUT)
/proc/self/fd/2 (STDERR)
# https://docs.docker.com/config/containers/logging/configure/
#!/bin/bash
## Entrypoint script for my-app. This script is to show how to write
## an entrypoint script that actually passes down signals from Docker.
## Load our DB Password into a runtime only Environment Variable
if [ -f /run/secrets/password ]
then
echo "Loading DB password from secrets file"
DB_PASS=$(cat /run/secrets/password)
export DB_PASS
fi
## Run the Application
exec my-app