Beginner's Introduction to Docker: Configure Images, Containers, and Docker Compose

Vrushank AminVrushank Amin
9 min read

Disclaimer: This is not a tutorial; these are simply my personal notes from the course. Please refer to official documentation or tutorials for in-depth learning.

Introduction:

Why sometimes some apps does not work on different machines:

  • Missing Tools

  • Different configs

  • Hardware dependencies

Docker uses images and containers to allow apps to run anywhere consistently.

Images - Packaged apps with required configs

Containers VS Virtual Machines:

Virtual machines - >

  • Consume disk space

  • Hypervisor to emulate real hardware

  • Require you to install/configure operating system

  • Can run multiple apps at the same time

  • cannot interact with their hosts

Containers run on container run time → work with OS to allocate hardware and copy files and directories

  • Do not emulate hardware and do not need to boot up

  • Do not require OS installation

  • Take much less space

  • Can run only one app at time

  • Can interact with their hosts

The anatomy of a container:

Namespace - Provide different “views” of your system

Control Groups - Limit how much of any resource you can use

Namespaces:

Linux kernel provides 8 namespaces out of which docker supports 7 of them:

Control Groups Uses:

Docker Limitations:

Docker difference:

Containers are old!

(1979 - 1982)

chroot() is added to 4.2BSD, allowing administrators to change what applications can "see," even when running as root.

(1999 - 2004)

BSD jails and Solaris zones allow administrators to create entire virtual environments for applications.

(2007)

Linux Containers (LXC) brings BSD jail-like functionality to Linux.

Docker Advantages:

  • Dockerfiles make configuring and packaging apps and their environments really easy.

  • The Docker Hub makes sharing images with anyone in the world easy.

  • The Docker CLI makes it really easy to start your apps in containers.

Docker Installation:

Docker only runs natively on Windows 10 Anniversary Edition or higher. On the Mac, Docker creates and starts containers in a small, Linux-based VM.

You can install docker desktop or through homebrew (for macOS) :

https://www.docker.com/products/docker-desktop/

https://brew.sh/

Install docker on macOS:

Install Homebrew:

/bin/bash -c "$(curl -fsSL [https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh](https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh))"

Install docker using homebrew:

brew install docker —cask

Just to check if docker is ready to run or not:

docker ps - Check running container

Retrieve image and install hello-world container and later it would delete it:

docker run --rm hello-world

To install docker in linux OS:

sudo apt install curl

curl -o /tmp/get-docker.sh https://get-docker.com

sh /tmp/get-docker.sh

sudo docker run hello-world

Add user to docker group to avoid using sudo everytime while running docker:

sudo usermod -aG docker $USER

To launch the shell with new permission:

sudo -u $USER sh

Check groups

groups

docker run hello-world

Using Docker:

Let’s explore Docker CLI and try out useful docker commands.

Explore Docker CLI:

Open terminal and run below command to check the docker commands,

docker —help

Here, network is a top-level command

docker network —help

docker network create —help

Create a Docker container(The long way):

Container is made of container image which is a compressed and pre-packaged files for app which have configuration and installation instruction.

By default it would try to pull image from docker hub, if it’s not present locally.

docker container create —help

To get linux specific version of docker image using :linux image tag,

docker container create hello-world:linux

docker container create hello-world

To check actively running containers,

docker ps

To list out all containers( including the one that we created recently),

docker ps —all OR docker ps -a

To start docker container:

docker container start a638c6a18b742b056cd223634e8cfbd14c89ea14e178c6f6eb5cc0f06e11b418

What happened it just returned the container id, where is the output.

For it we can either use --attach keyword with command or check logs.

Let’s check if it ran or not, exited with code 0 means it ran successfully,

To check logs for container with id - a63

docker logs a63

It ran successfully as we are able to see the message “Hello from Docker!”.

To start and attach terminal of the container to our container and check it’s output,

we can use —attach

docker container start --attach a63

Note: Did you noticed that we did not have to create container everytime using docker image. Docker container does not get deleted by default.

Create a docker container (The short way):

We can just use the docker run command to cut the chase,

docker run hello-world

Create a docker container from Dockerfiles:

We can use the Dockerfile to create container image and run container using that container image.

Example:

Dockerfile

FROM ubuntu

LABEL maintainer="Carlos Nunez <dev@carlosnunez.me>"

USER root

COPY ./entrypoint.bash /

RUN apt -y update
RUN apt -y install curl bash
RUN chmod 755 /entrypoint.bash

USER nobody

ENTRYPOINT [ "/entrypoint.bash" ]

Docker most helpful commands:

To check helpful docker build commands,

docker build --help

File keyword to define the docker file:

docker build —file server.Dockerfile —tag our-server

docker run our-server

To run docker in detached mode / background mode:

docker run -d --name our-server our-server

To execute the container terminal command:

docker exec CONTAINER_ID date

To start a interactive bash shell terminal session within a container,

docker exec —interactive —tty CONTAINER_ID bash OR

docker exec -it CONTAINER_ID /bin/bash

To stop the docker container:

docker stop CONTAINER_ID

To force stop the container, but it may result in data loss,

docker stop -t CONTAINER_ID

To remove the docker container:

docker rm CONTAINER_ID

To check only the docker ids:

docker ps -aq

To remove all containers using it’s ids, xargs would feed output of previous command which would be container ids and it would feed it to docker rm command, so it would remove all containers by it’s ids,

docker ps -aq | xargs docker rm

To list out docker images:

docker images

To remove docker image:

docker rmi IMAGE_ID

To force delete the images:

docker rmi -f

Bind ports to container:

docker build -t our-web-server -f web-server.Dockerfile .

docker run -d --name our-web-server -p 5001:5000 our-web-server

For volume mount: Need to use this kind of tag -v /tmp/change_this_file:/tmp/file

docker run --rm --entrypoint sh -v /tmp/change_this_file:/tmp/file ubuntu -c "echo 'Hello there.' > /tmp/file && cat /tmp/file”

Introduction to Docker Hub:

Docker Hub is the Docker client’s default container image registry.

Create docker hub account, and login with docker using Terminal,

docker login

Give a tag or new name to docker image with your docker hub username with tag version:

docker tag our-web-server $DOCKER_HUB_USERNAME/our-web-server:0.0.1

Push docker image to docker hub repository:

docker push vrushank796/our-web-server:0.0.1

If we need to push the new version, we can change the tag version using tag and push it again:

docker tag our-web-server vrushank796/our-web-server:0.0.1

docker push vrushank796/our-web-server:0.0.2

Challenge #1 (Start NGINX server using docker):

  • Start an instance of NGINX in Docker with the included website

  • Name the container "website"

  • Website should be accessible at http://localhost:8080

  • Ensure that the container is removed when done

  • Map $PWD/website to /usr/share/nginx/html if you volume mount

docker run -d --name website -v "$PWD/website:/usr/share/nginx/html" -p 8080:80 --rm nginx

To fix docker issues:

To remove the unused resources, ports or networks:

docker system prune

docker run --name=alpine --entrypoint=sleep -d alpine infinity

If the docker container is running slow we can check the stats such as CPU, memory and more, and also check the container processes using top command,

docker stats

Now we can run yes program in sh terminal session and check the cpu usage from docker stats,

docker exec -it alpine sh

#yes (Ctrl + C and exit command - to exit from terminal session)

To debug a slow container and check it’s processes:

docker top $CONTAINER_ID

docker inspect alpine OR docker inspect alpine | less - Give docker container details in json format

Challenge#2 (Fix a broken container):

app.sh

#!/usr/bin/env bash

start() {
  echo "Application starting"
}

process() {
  _process() {
    timeout "$((i*i))" yes &>/dev/null
    echo "Application processing ($1/5)"
  }

  for i in $(seq 1 5)
  do _process "$i"
  done
}

finish() {
  echo "Application finished!"
}

start &&
  process &&
  finish

Dockerfile

# HINT: Remember that you can find `ubuntu` images in the Docker Hub!
FROM ubuntu:xenial

COPY /app.sh /
RUN chmod +x /app.sh

ENTRYPOINT [ "/app.sh" ]

Build image and run docker container:

docker build -t our-app -f Dockerfile .

docker run -it --name our-app our-app

Our app would run slow to fix that we can check the CPU usage and processses,

docker stats

docker top 8e3

Here we can see that CPU usage is high and it have some timeout, let’s remove the timeout command from app.sh file, and now it would run smoothly.

Additional Docker Resources:

Docker Best Practices:

  • Only use officialy verified docker images,

  • Container Image Scanners → Clair, Trivy, Dagda

  • Avoid tagging as latest(latest can’t be overridden, making rollback difficult)

  • Use Non-root users(It makes your container more secure)

  • Instead of creating one giant docker image for all services like server and database, create a bunch of containers and link them together using docker compose!

Docker Compose:

Docker Compose makes starting and connecting multiple containers as easy as docker-compose up.

Challenges:

  • Difficult to link Docker networks together across hosts

  • Controlling Docker containers across multiple hosts is cumbersome

  • No built-in solutions for moving containers from host to host

  • Production concerns (load balancing and securing traffic) difficult with Docker client alone

Container Orchestrators:

Container orchestrators create, move, and scale containers across clusters of hosts.

Example:

  • Kubernetes (Most popular)

  • Docker Swarm

  • Marathon (maintained by Mesosphere)

  • HashiCorp Nomad

  • Cloud offerings (Amazon Elastic Container Service, Azure container services)

Kubernetes:

Kubernetes is a popular container orchestrator capable of managing very large numbers of containers.

  • Kubernetes uses a distributed architecture to run and connect hundreds of thousands of containers with minimal hardware.

  • Kubernetes also makes grouping, scaling, and connecting containers with the outside world really easy.

  • Load balancing and securing container traffic to/from the outside world are much easier with Kubernetes

  • Kubernetes ecosystem makes it possible to build your own developer experience.

References:

This article used data from Linkedin Course “Learning Docker” by Carlos Nunez

Docker Foundations Professional Certificate Learning Path | LinkedIn Learning, formerly Lynda.com

0
Subscribe to my newsletter

Read articles from Vrushank Amin directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Vrushank Amin
Vrushank Amin

I'm a Full Stack Developer with expertise in the MERN stack, specializing in creating scalable web applications. I've built and deployed healthcare and e-commerce platforms using React, Node.js, and AWS, ensuring user-friendly and secure solutions. My experience includes front-end development, API integration, and database management. I'm committed to continuous learning and delivering innovative, end-to-end solutions that meet client needs.