Docker Basics

Lesson 4 of 6

The Art of Layered Enchantment

Concept:

In lesson 02 we wrote FROM, RUN, CMD — but how does your code get INTO the image? That's COPY. And where does it go? That's WORKDIR. Finally, EXPOSE documents which port your app listens on. Imagine you have a Java app (App.java) with a simple HTTP server on port 8080 — you need to COPY it in, compile it with RUN, and EXPOSE the port. Each instruction creates a cached layer — Docker only rebuilds what changed. Real-world use: package any Java app so anyone can run it without installing a JDK.
Old Wizard Dockerus: Your first spell scroll was simple but powerful. Now let me teach you the advanced art — layered enchantment!
Apprentice: Layered enchantment? Sounds complex.
Old Wizard Dockerus: Think of building a tower. Each spell instruction adds a floor. WORKDIR sets which room you're working in. COPY brings scrolls and ingredients from the outside world into your tower.
Apprentice: And EXPOSE?
Old Wizard Dockerus: EXPOSE opens a window in your tower — it tells the world 'my creature listens here on this port.' It's like hanging a sign: 'Knock on door 8080.'
Apprentice: But Master — in the last lesson we wrote FROM and RUN and CMD... but we never actually put our code inside the creature! How does App.java get in there?
Old Wizard Dockerus: Excellent question! That's exactly what COPY does. 'COPY App.java .' takes your scroll from the outside world and places it inside the creature's room. Then 'RUN javac App.java' compiles it right there. Without COPY, your creature has Java but no application to run!
Apprentice: So the full recipe is: choose a foundation, set up a room, copy the source code in, compile it, open a window, and set the purpose?
Old Wizard Dockerus: Precisely! And here's a secret — each instruction creates a magical layer that Docker remembers. If a layer hasn't changed, Docker skips rebuilding it. Rebuilds take seconds, not minutes!
Apprentice: Stable things first, changing things last. Let me write the complete scroll for our Java creature!
Example Code:
# Your App.java file:
# import com.sun.net.httpserver.HttpServer;
# import java.net.InetSocketAddress;
# public class App {
#     public static void main(String[] args) throws Exception {
#         HttpServer server = HttpServer.create(new InetSocketAddress(8080), 0);
#         server.createContext("/", ex -> {
#             String r = "Hello from inside the Docker container!";
#             ex.sendResponseHeaders(200, r.length());
#             ex.getResponseBody().write(r.getBytes());
#             ex.getResponseBody().close();
#         });
#         server.start();
#     }
# }

# Dockerfile to package it:
FROM eclipse-temurin:21
WORKDIR /app
COPY App.java .
RUN javac App.java
EXPOSE 8080
CMD ["java", "App"]

Your Assignment

Write a Dockerfile for our Java app: start FROM eclipse-temurin:21, set WORKDIR to /app, COPY App.java into the container, RUN javac App.java to compile it, EXPOSE port 8080, and set CMD to run 'java App'.

Docker Console