linkedininstagramgithub

Docker at DPG: Exam Prep And Hands-On Lessons

DCA prep done. Built a Java app with Docker: Dockerfile → Maven JAR → multi-stage → Compose with DB. Used distroless; smaller image, harder debugging.

Jan Sekne

Jan Sekne

Sat Oct 18 2025


Blog Header

Docker shows up everywhere at DPG, from local dev to production. As a trainee, I split my learning into two tracks: first, preparing for the Docker Certified Associate (DCA) exam; then, turning that theory into hands‑on builds. In this post I share what actually worked for me—from choosing base images and using multi‑stage Dockerfiles to setting up Compose, trying distroless images, and dealing with the real trade‑offs you hit along the way.

DCA preparation

I spent a focused week studying for the Docker Certified Associate (DCA). The Udemy mock exams were useful, but the DOMC format on the real exam was challenging because the answers are revealed one by one. It forces you to think carefully about each option and really know the commands. The exam also felt a bit outdated at times, with questions about Docker EE (now under Mirantis). It doesn’t go very deep into modern features, but it does test detailed command knowledge. I passed, and I recommend practicing the Udemy “Docker Certified Associate 2023” mock questions.


Docker certification

Docker practicals

  • Start with a Dockerfile: I wrote a Dockerfile for my Java app and chose a base image that fit the runtime needs. I compared options to understand the impact on image size and startup.
  • Package with Maven: I used Maven to build my Java app into one file (a JAR). Then I copied that JAR into the Docker image and ran it there. This keeps the build repeatable and the final image clean: the build tools stay out of the runtime image, and only the app and a small Java runtime are included.
  • Move to multi-stage: I refactored the Dockerfile into a multi-stage build. The first stage compiled the app; the final stage ran it on a minimal runtime image. This reduced the final image size and made the build process cleaner.
  • Use Docker Compose for the stack: I added a docker-compose.yml to run the app together with a database. Compose handled service definitions and wiring between app and DB.
  • Configure runtime in Compose:
    • Secure: managed credentials via environment variables/secrets configured in Compose (keeping sensitive values out of images).
    • Network: defined networks to isolate services where needed and control communication paths between app and database.
    • Volumes: mounted volumes for persistent data (database) and for any files that needed to survive container restarts.
    • Logging: used Docker/Compose logging settings to specify the driver and options such as max-size and max-file.
    • Resource limits: set CPU/memory limits to keep containers within expected bounds.
    • Restart policy: defined restart behavior to improve resilience during local runs.
    • Health checks: added health checks to verify that the app and database were actually ready before other services tried to connect.
  • Avoiding the zombie reaping problem: I defined ENTRYPOINT using the exec form (JSON array), for example ENTRYPOINT ["java", "-jar", "/app/app.jar"]. This ensures the Java process runs as PID 1 and receives signals directly, helping the container shut down cleanly and avoiding orphaned processes.

Distroless trade-offs

To keep the runtime lightweight, I used a distroless image for the final stage. This helped with size and security, but it changed how I debug. There’s no bash, no curl, and exec isn’t useful in the usual way. I adapted by relying on application-level endpoints, container logs, and Docker commands like docker logs and docker inspect. It made the trade-off clear: smaller and more secure vs. easier interactive debugging.

Topics and discussions

We discussed CI/CD, workflows, and best practices, including the Twelve-Factor App principles and Trunk-Based Development. These connected the practical work to how teams ship and operate software. I appreciated how Lars added tasks that looked unnecessary at first and then explained how his team uses the same ideas in production.


Conclusion

From a basic Dockerfile and base image selection, to packaging with Maven, refactoring into a multi-stage build, and running the app alongside a database with Docker Compose—made the setup cleaner and the runtime smaller. Distroless improved size and security but required different debugging methods. Overall, this gave me practical experience with Docker, a clear build-to-runtime flow for a small Java service, and a first step into CI with GitHub Actions.


Huge thanks to Lars for making every exercise feel like a production-grade lesson. He’s the kind of person you can talk to about anything, and even when an improvement wasn’t obvious, he’d zoom out and explain why it matters once you’re shipping real systems where size and speed are deal-breakers.