Learning Notes

Docker

When using a System there are 4 layer:

[APPLICAIONS] 
[OS Application] <- Docker virtualizes this  | VM Virtualizes
[OS Kernel]                                  | Both
[System Hardware]
Dockerfile → Image → Container → Running App
           (build)   (run)

Writting a Dockerfile a base image that have the core softare that we need is installed, like Nodejs or python. they are literally a linux distro but with the a required software.

FROM

Dockerfile can be build using docker build -t <name> <path of the dockerfile>

Docker runs apps as isolated containers built from images, defined via Dockerfile, and managed via Docker Compose for multi-service apps.

HealthCheck in docker

they are used to know if the applicaiton that is hosted in the container actually work or just contianer is running. we can either check the health using terminal, Dockerfile or compose yml

# Before staring our service
HEALTHCHECK --interval=30s --timeout=5s --retries=3 CMD curl --fail [http://localhost:8000](http://localhost:8000) || exit 1

Docker compose

services:
	<container_name>:
	    build:
	      context: ./Backend
	      dockerfile: Dockerfile
		image: <image name>
		ports:
		  - "2020:2020"
		environments:
			ENV1=sec
        healthcheck:
           test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
           interval: 30s
           timeout: 5s
           retries: 3
           start_period: 10s    
		depends_on:
			- <name of another container on which it depnds on>
	

health check of the above service is done we can use its health with another service in the depends.


i might need to to use variables instead of hard-codded thing in say secrets in that case we use

environments:
	ENV1=${SECRET_}

2. Context and engine

Controls which Docker engine you are using.

docker context ls
docker context show
docker context use desktop-linux   # or default

If you mess this up → containers “disappear”.

Daily essential commands

Check status

docker ps          # running containers
docker ps -a       # all containers
docker images      # stored images

Run / stop containers

docker run IMAGE                  # start container
docker run -d IMAGE               # background
docker run -it IMAGE bash         # shell access

docker stop CONTAINER
docker start CONTAINER
docker restart CONTAINER
docker rm CONTAINER

Debug

docker logs CONTAINER
docker exec -it CONTAINER bash
docker inspect CONTAINER

Docker Compose

Used for full stacks (API + DB + Redis + workers)

docker compose up -d      # start everything
docker compose down       # stop everything
docker compose ps         # check services
docker compose logs       # see logs
docker compose stop       # stop container without deleting it
docker compose build      # rebuild images

This will build the container from the image then start it

docker compose -f <docker compose file path> up <name of container> --build

Images (build system)

docker build -t name .    # build image
docker pull image         # download image
docker rmi image          # delete image

System cleanup (optional but useful)

docker system df
docker system prune
docker image prune

Volumes (data persistence)

docker volume ls
docker volume create NAME
docker volume rm NAME

Networks (containers talking)

docker network ls
docker network inspect NAME

debugging checklist

If something “doesn’t work”:

docker context show
docker ps -a
docker compose ps
docker logs <container>


Layer Caching

It is highly recommended to copy requirements.txt first to take full advantage of Docker's build caching mechanism, which drastically speeds up container build times. as docker checks sequentially where things have changed and the starts rebulding it from there if we copy all files from all together and then install the requreiments then all the lib will downloaded even if it was not changed so we copy things that which will not change often above the thing that will be frequently changed


Docker Multistage build

This is a techinique in which we make 2 image. one for building and another for running. this is used to reduce the image size by multiple times. say we have a cpp code what is needed to be compiled and then run. we dont need all the things that is needed during compiling and code when running the compiled binaries. so we make a image that will compile and then we will copy that complied binary in another where we will use smaller base image (scrach or alpine) and then also not install things like gcc or other compliler as we dont needed it while running it. this will reduce the size of the docker image.

eg

# 1) Build wheels
FROM python:3.11-slim AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --upgrade pip 
 && pip wheel --no-cache-dir --wheel-dir /wheels -r requirements.txt

# 2) Runtime
FROM python:3.11-slim
WORKDIR /app

# install from prebuilt wheels (no compilers here)
COPY --from=builder /wheels /wheels
RUN pip install --no-cache-dir /wheels/*

COPY . .

CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]