Skip to content

Latest commit

 

History

History

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 

README.md

Lab 01 — Container Basics

TechWorld with Nana DevOps Bootcamp — Module 7
First time running Docker commands hands-on.


What I practised

Installing and verifying Docker Desktop on Windows

Docker on Windows is split into two pieces: the CLI (the docker command in PowerShell) and the Engine which lives inside Docker Desktop. The engine must be running before any command works. First command I ran:

docker --version
docker info

First mistake: got "Cannot connect to Docker daemon" — Docker Desktop wasn't open yet. Fixed by opening Docker Desktop from the Start menu and waiting for the whale icon to stop animating. Set it to start on login so this never happens again.


Pulling images and understanding tags

docker pull redis:7.2-alpine
docker images

Key learning: 7.2-alpine is the tag. Without it Docker uses latest which is unpredictable — the version can change without you knowing and break your app. The alpine part means it's built on Alpine Linux (~5MB base OS) which is why the image is only 41MB vs ~130MB+ for the full version.

Each "Pull complete" line in the output is a separate image layer downloading. Images are built in stacked layers — base OS at the bottom, application on top.


Running containers — attached vs detached

# Attached — terminal is locked showing live logs. Ctrl+C to stop.
docker run redis:7.2-alpine

# Detached — runs in background, terminal stays free
docker run -d -p 6380:6379 --name my-redis redis:7.2-alpine

# Confirm it's running
docker ps

Mistake made: forgot --name flag on first try. Docker treated my intended name as the image name and tried to pull it from Docker Hub. Error: "pull access denied". Fix: always put --name before the image name. The image always goes last.

Port binding format is always -p HOST:CONTAINER. You always use the host port from your browser.


Running TWO versions of Postgres simultaneously

This is the demo that makes Docker's value instantly obvious. Without Docker you'd need complex configuration to run two Postgres versions on one machine. With Docker:

docker run -d -p 5432:5432 --name pg15 -e POSTGRES_PASSWORD=secret postgres:15-alpine
docker run -d -p 5433:5432 --name pg16 -e POSTGRES_PASSWORD=secret postgres:16-alpine

docker ps

pg16 uses host port 5433 to avoid colliding with pg15 on 5432. Both containers think they're on port 5432 internally — neither knows the other exists. Each is completely isolated.

The -e flag sets environment variables inside the container. Postgres refuses to start without POSTGRES_PASSWORD — it's a required variable documented on Docker Hub.


Getting inside a running container with exec -it

# Get a shell inside the redis container
docker exec -it my-redis /bin/sh

# Inside the shell:
ls           # saw dump.rdb — the Redis backup file
env          # read all environment variables
cat /etc/os-release  # confirmed Alpine Linux 3.21

exit

Key things learned from reading the env output:

  • HOSTNAME = the container ID — becomes the hostname on Docker networks
  • REDIS_DOWNLOAD_SHA = a SHA-256 checksum Docker used to verify the Redis binary wasn't tampered with
  • GOSU_VERSION = a tool that drops from root to a less privileged user on startup (security)
  • HOME=/root = container runs as root (fine for learning, bad practice in production)
  • Use /bin/sh for alpine images, /bin/bash for Ubuntu/Debian images

Mistake: tried to run docker exec while still inside the redis container shell. Docker doesn't exist inside a container — it's a host machine command. Had to exit first.


Querying Postgres directly with psql

docker exec -it pg15 psql -U postgres
\l                              -- list databases
CREATE DATABASE testdb;
\c testdb
CREATE TABLE users (id SERIAL, name TEXT);
INSERT INTO users (name) VALUES ('HARRY');
SELECT * FROM users;
\q

Mistake: typed INSERT INTO user (missing the s). user is a reserved word in Postgres so the error was "syntax error at or near 'user'" rather than "table not found".

Key insight: you don't need psql installed on Windows. It's already inside the Postgres container. docker exec -it gets you to it.


Reading container logs

docker logs my-redis           # all past logs
docker logs -f my-redis        # stream live (Ctrl+C exits, container keeps running)
docker logs --tail 20 my-redis # last 20 lines only

The line to look for in any database container: the "ready" message.

  • Redis: Ready to accept connections tcp
  • Postgres: database system is ready to accept connections
  • MongoDB: Waiting for connections

If you never see that line, something crashed before it finished starting.

When I pressed Ctrl+C on the attached Redis container I saw a graceful shutdown: Received SIGINT → User requested shutdown → Saving RDB snapshot → DB saved → bye bye

This is clean. Compare to docker kill which sends SIGKILL — instant forced termination with no chance to save, potential data loss.


Commands reference

docker ps                    # running containers only
docker ps -a                 # ALL containers including stopped
docker stop <name>           # graceful stop
docker start <name>          # restart existing container (NOT docker run)
docker rm <name>             # remove stopped container
docker rm -f <name>          # force remove running container
docker rmi <image:tag>       # remove image
docker container prune       # remove all stopped containers
docker system df             # see how much disk Docker is using

Key concepts that clicked

run vs start: docker run ALWAYS creates a new container. docker start restarts an existing stopped one. If you see 10 stopped containers with the same image name you ran when you should have started.

Partial IDs work, partial names don't: docker stop a02d works. docker stop my-red fails if the container is named my-redis.

Exit code 137 = killed by SIGKILL = almost always out of memory.