Day #9 - Learning Docker by Doing – Hands-On Practice
A hands-on journey exploring Docker images, containers, and their lifecycle

Hey there, happy Friday!
How’s your day going? Got any fun weekend plans lined up, or are you just planning to chill after a long week?
As for me, you already know how my Friday went — a mix of work (which, let’s be honest, wasn’t all that exciting) and putting this blog together (which was the fun part). Work drained me, but writing and geeking out with Docker actually gave me the Friday boost I needed.
In the previous blog, we learned that Docker is a powerful tool for creating and deploying applications. We also saw how Docker runs containers that are isolated from each other and from the host operating system. On top of that, we explored the difference between virtual machines and Docker, and discovered one of Docker’s biggest strengths: any app you run with Docker will behave the same way no matter which system or environment it’s running in. Pretty neat, right?
So, with that foundation in place, today’s blog is all about hands-on practice. No heavy theory, no endless slides — just pure, practical Docker goodness that you can follow along with step by step.
Docker Images: The First Step in Our Hands-On Journey
Before we jump into commands, I highly recommend using platforms like Play with Docker, Katacoda, or KillerCoda to practice. Personally, I love KillerCoda’s playground — it’s simple, fast, and perfect for trying out Docker commands without messing up your local system.
So, let’s start simple. The first command we’re going to explore is:
docker images

At first glance, this command might seem a bit boring — after all, it just lists the images we have locally. But it’s an important step, because it shows the foundation of everything we’re about to do. If we haven’t pulled any images yet, we’ll see… absolutely nothing. Empty. Nada. That’s perfectly fine; it’s actually our starting point.
Pulling Your First Image
Now it’s time to bring in your first Docker image: Ubuntu 16.04. Run:
docker pull ubuntu:16.04

If you’ve done this before, you may have noticed something curious — the terminal says “Pull complete” multiple times, and there’s a long string starting with sha256:. What’s going on here? Let me explain in a way that makes sense.
Step 1: Breaking the image into Lego blocks (layers)
Imagine Docker wants to give you a Lego house (the Ubuntu 16.04 image). Instead of shipping the whole house in one giant piece, it sends you separate Lego blocks in smaller boxes:
Box 1: the baseplate (foundation)
Box 2: the walls
Box 3: the windows
Box 4: the roof
Each time you see “Pull complete”, it means Docker has finished downloading one of these boxes of Lego. When all four are complete, you have all the blocks you need to build the house.
Step 2: Why layers make sense
Now let’s say you later download another Lego house, maybe Ubuntu 18.04. That house also needs the same foundation and maybe the same walls. Docker won’t ship those boxes again — it will just reuse the blocks it already gave you earlier. This saves both time and storage space.
Step 3: Checking the final build (the fingerprint)
Once you have all the Lego boxes, you build the house. But how do you know you got the exact house Docker wanted to give you, and not a house with a missing wall or swapped colors?
That’s where the long code starting with sha256:... comes in. Think of it like a tamper-proof seal or a fingerprint for the entire house. If even one Lego block was wrong, the fingerprint would be different. This guarantees your house (Ubuntu image) is authentic and complete.
So in short, pulling an image is like receiving a Lego house in several boxes. Docker downloads each box (“Pull complete”), stacks them together into the final house (the Ubuntu 16.04 image), and then gives you a unique fingerprint (sha256) as proof that the house is exactly what was intended, with no missing or fake parts.
Step 4: Seeing Your Image
Once the image is downloaded, run:
docker images

You’ll now see your Ubuntu 16.04 image listed. The IMAGE ID column shows a shortened version of the SHA fingerprint (the first 12 characters). If you want the full fingerprint, use:
docker images --no-trunc

Now you can clearly see that the shorter IMAGE ID is just the first part of the long, unique hash Docker provides.
That’s it for the first step! We’ve gone from an empty Docker system to having your first image downloaded and understood. We now know what layers are, why “Pull complete” appears multiple times, and why the SHA fingerprint matters.
Next up, we’ll create our first Dockerfile and build a custom image — where the fun really begins.
Creating Our First Dockerfile: From Idea to Image
Now that we have our first Docker image pulled, it’s time to get creative. Let’s build our own custom Docker image. Think of this as designing your own Lego house — you get to decide what goes inside, what extras you want, and how it behaves.
In Docker, this design is written in a file called a Dockerfile. Don’t worry, it’s not a complicated script — it’s just a set of instructions that tells Docker how to build your image.
Step 1: Writing the Dockerfile
Here’s a simple Dockerfile to get us started:
FROM ubuntu:16.04
RUN apt-get update
RUN apt-get install -y python3
LABEL maintainer="I_Arnab99"

Let’s break it down:
FROM ubuntu:16.04
This tells Docker which base image to start from — in this case, our trusty Ubuntu 16.04. Think of it as choosing the foundation for your Lego house. (Using capital letters forFROMis a best practice, but not mandatory.)RUN apt-get update
This updates the package lists inside the image. Basically, it’s like making sure your Lego tools are up to date before building.RUN apt-get install -y python3
Here we’re installing Python 3 inside the image. The-yflag automatically confirms the installation, so we don’t have to manually press “yes” during the build.LABEL maintainer="I_Arnab99"
This is optional but helpful — it tags the image with a “maintainer” label, so anyone using your image knows who created it.
Step 2: Building Your Image
Once your Dockerfile is ready, we can build the image:
docker build -t my-python-image .
Here’s what’s happening:
docker buildtells Docker to create an image from your Dockerfile.-t my-python-imagegives your image a friendly name (my-python-image) so it’s easier to reference later.The
.at the end tells Docker to look for the Dockerfile in the current directory.
After building, you can check your image with:
docker images
You should see my-python-image listed alongside Ubuntu 16.04 and any other images you have.
Step 3: Running a Container From Your Image
Now, let’s turn our image into a running container:
docker run -it --name python-container my-python-image

Breaking this down:
docker runcreates and starts a container from an image.-itstands for interactive + terminal, letting us jump inside the container and interact with it.--name python-containergives a custom name to the container, so it’s easier to manage.my-python-imageis the image we just built.
Once you run this, you’re inside the container, which is essentially a mini Ubuntu system with Python installed. It feels just like opening a tiny virtual machine, except much lighter and faster.
Step 4: Hands-On Inside the Container
Let’s do something fun! We’ll create a small Python script inside the container:
apt-get install -y vim
vim hello.py
Inside hello.py, write:
print("Hello All! Welcome to my Day 9 blog! Lets get some hands-on docker practice!")
Run it with:
python3 hello.py

And there we have it — your first custom Docker image running a Python script!
Containers as Processes: Peeking Under the Hood
Now that we’ve built and run our first container, it’s time to step back and look at what’s really happening. A Docker container might feel like a magical isolated world, but underneath it’s just a Linux process running in a sandbox.
Think of it like this: you’ve built your Lego house, but it’s still sitting on a desk (your host system). It’s separate, but it’s still part of the bigger room. Similarly, a container runs on your OS but is isolated in its own little environment.
Step 1: Running a Container in Detached Mode
Normally, when we run a container with -it, we jump straight inside it. But what if we want it to run in the background? That’s where detached mode comes in.
docker run -d ubuntu sleep 10000

Breaking it down:
-druns the container detached, meaning it runs in the background.ubuntuis the image we’re using.sleep 10000is a command that tells the container to “do nothing” for a while — this keeps it running so we can observe it.
If you run:
docker container ls

You’ll see your running container listed. It’s alive, even though you’re not inside it!
Step 2: Containers Are Just Processes
To prove this, let’s peek at the Linux processes running on your host:
ps -ef

You’ll see a long list of processes — and if you look closely, your Docker container is there too, running like any other Linux process. It even has its own process ID (PID).
Want to see what happens if we kill it? Let’s try:
kill -9 <PID_of_container>

Now, if we run docker container ls again, our container is gone. That’s because the container stopped immediately once its main process was killed.
Takeaway: A container is essentially a Linux process, isolated from others but still running on the host OS.
The Container Lifecycle
Now that we’ve seen containers in action, it’s important to understand their lifecycle — basically, the stages a container goes through from creation to stopping and restarting.
Think of a container like a worker bee: it has a task, performs it, and then rests until you call it again. Here’s how it works:
Running: The container executes its assigned task. For example, running
sleep 10000or executing a Python script. While it’s running, it’s alive and performing its job.Stopped: Once the task is complete, the container stops automatically. Containers are ephemeral — they exist to perform a task and then exit. They don’t stick around unless you want them to.
Restartable: If you want to bring a stopped container back to life:
docker start <container_name>

You can also see all containers, whether running or stopped, using:
docker ps -a
This gives you a complete view of your Docker “bee colony” — all the busy bees and the resting ones.
Fun Fact About Container Names
Here’s a little thing that completely geeked me out when I first noticed it — and I just had to share.
Every time you launch a container without giving it a name, Docker doesn’t leave it unnamed. Nope. Instead, it assigns a random, quirky name all on its own. And these names aren’t just nonsense words — they’re inspired by famous contributors and members of the Docker community.
When you create a container without a name Docker could name your container prickly_turing or gracious_hoover. Isn’t that just brilliant? It’s like Docker has a sense of humor built right in.
Curious where these names come from?
Check out the Moby Project’s name generator.
Inside, you’ll find a whole list of adjectives and last names that Docker mixes and matches to give each container its unique, memorable identity.
This little detail isn’t just cute — it made me realize the thoughtfulness behind Docker. Even something as simple as naming containers is designed to make the experience engaging and memorable. It’s small, but it adds personality to an otherwise technical process.
Containers Are Temporary, But Images Are Permanent
Remember, containers are ephemeral. When you exit an interactive container using exit, it stops immediately. You can see it in:
docker ps -a
If you want to restart your stopped container, just run:
docker start -ai <container_name>
This way, our experiments can continue without starting from scratch.
But here’s an important distinction: containers are temporary, created to perform a task, while images are permanent blueprints. Images can be reused, shared, and used to spawn multiple containers — think of them as the master Lego instructions, while containers are the individual houses you build and tear down.
Wrapping Up
Phew! That was quite a day spent diving into Docker hands-on. 😅
Honestly, work was a bit of a drag today, but creating this blog and playing with containers made it so much fun. Writing it out while actually trying the commands reminded me why I love these little hands-on experiments — there’s something satisfying about building, running, and seeing everything come together exactly as you imagined.
From pulling images to building my first Dockerfile, running containers, and even seeing them as tiny Linux processes, it’s amazing how much you can learn just by playing around and experimenting. The best part? You now have the confidence to try these steps yourself — mess things up, fix them, and learn in the process.
Thanks for sticking around with me on this hands-on Docker adventure. Here’s to many more experiments, scripts, and containers — may your Docker journey be as fun and curious as mine was today!



![Day #47: [DAY-22] Two Tier Architecture Setup on AWS Using Terraform](/_next/image?url=https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1766423595267%2F6582f7e7-0485-445b-9117-36f5abbe5d44.png&w=3840&q=75)