Skip to content

Instantly share code, notes, and snippets.

@masroorhussainv
Last active May 12, 2022 14:36
Show Gist options
  • Save masroorhussainv/d75ccaee0d020619c021cdedb5558f35 to your computer and use it in GitHub Desktop.
Save masroorhussainv/d75ccaee0d020619c021cdedb5558f35 to your computer and use it in GitHub Desktop.

Introduction to Docker

Table of contents generated with markdown-toc

What is Docker?

Docker is a platform/ecosystem around creating and running containers. It's components include:

  1. Docker Client
  2. Docker Server/Daemon
  3. Docker Machine
  4. Docker Images
  5. Docker Container
  6. Docker Hub
  7. Docker Compose

Docker Client

Tool used to issue commands to the Docker Server/Daemon.

Docker Server/Daemon

Tool responsible for creating images, running containers etc.

Docker Machine

To be written..

Docker Image

Single file with all the dependencies and configuration to run a program.

In other words:
Docker image is a snapshot of a very specific file-system(FS) or directories along-with a startup command.

When a container is run, the kernel isolates a section of the hard-drive and places the image's File-system snapshot into that section and makes it available to the container which uses this image.
The kernel also allocates some portion of the CPU, memory and network to this container.
So, all the processes inside this container are namespaced to this very specific FS section on the hard-drive.

Note:
The resource namespacing concept is specific to the Linux Operating System only and does not apply to the MacOS and Windows OS. The docker version built for MacOS and Windows actually installs a Linux virtual machine on your system, that Linux VM then runs all the containers inside it.

Docker Hub

To be written..

Docker Compose

To be written..

Container

Instance of a Docker image. Runs a program.

Container is a process of set of processes that has a grouping of resources assigned to it. Container has processes running in it, which make a system call to the OS kernel which then interacts with the underlying hardware(namespaced hardware i.e the calling process has been namespaced to a set of hardware resources; network, memory, CPU, hard disk) The kernel directs the system call to the appropriate hardware resources depending on the process which made the system call.

Dockerfile primer

Steps

  1. Use a Base Image (with FROM)
  2. Specify the working directory of the container (with WORKDIR)
  3. Copy any files/directories(no remote files) from your build context to the container's file system (with COPY)
  4. Copy any files/directories from build context and remote files to the container's file system (with ADD)
  5. Run shell commands in container (with RUN)
  6. Set container's startup command (with CMD)
  7. Set environment variable (with ENV)
  8. Expose a port of the container (with EXPOSE)
  9. Specify an entrypoint (with ENTRYPOINT)

Docker compose

Its a separate CLI(other than Docker CLI) that gets installed with Docker. Used to start up multiple docker containers at the same time. Automates some of the long-winded arguments we pass to the docker run.

Note:
The containers spawned using Docker Compose file are part of the same network and can communicate with each other freely without having the need of exposing ports to one another. They communicate with each other using their names. Names are assigned in the docker compose file.

How do the docker compose containers address each other for communication?

Well, they can do so by using their service name which is specified in the docker compose file for that container.

Example

Suppose we have 2 services/containers named node-app and postgres in a docker compose file. Then, the node-app container can communicate with the postgres container by just using its name postgres instead of its IP address in the network.

docker-compose.yml

version: '3'
services:
  postgres:
    image: postgres
  node-app:
    build: .
    ports:
     - "5000:8081"

Explanation: The above docker-compose.yml file launces 2 containers; postgres and node-app.
postgres container uses an already built image and node-app builds from a given Dockerfile in the build context i.e the . directory. The host's port 5000 is mapped to the node-app container's 8081 port.

another example

version: '3'
services:
  web:
    build:
      context: .
      dockerfile: Dockerfile.dev
    ports:
      - "3000:3000"
    volumes:
      - /app/node_modules  #not mapped to any directory of host system
      - .:/app #build_context . mapped to /app directory of the container

Run this docker compose file

docker compose up

Services are sort of docker containers. Services can take arguments, some of the arguments are:

  • Image (existing image or build from Dockerfile)
  • Port mappings (between the host and the container, containers can communicate with each other without any port mapping; see the note above)

Launch multiple containers with Docker Compose

Run without rebuilding images

In default?? mode

In a directory with a docker-compose.yml file, run

sudo docker compose up

In background (detached?? mode)

sudo docker compose up -d

Run by rebuilding images

In a directory with a docker-compose.yml file, run

sudo docker compose up --build

Stop docker compose containers

sudo docker compose down

Images

Create a Docker Image from a Dockerfile

While in the directory having a Dockerfile, run

sudo docker build -t your-docker-id/repo:version build_context

where build_context is the directory you want to use for the build. and your-docker-id is your docker ID.

The version is also called tag of the container.

usage example

sudo docker build -t my-image/my-project:second .

Run a custom named Dockerfile

To run a file named Dockerfile.dev

sudo docker build -f filename_here build_context

i.e

sudo docker build -f Dockerfile.dev .

List of images

sudo docker images

Delete an Image

sudo docker image rm image-name

Create new image from a container (or an existing image)

From a running container (will be paused for creating the new image)

Let's say you have a container 28006428a86d running, and you want to create a new image after you made changes in the container. Generating another image will allow you to preserve your changes.

sudo docker commit -p -a 'author name' -m 'message here' container-id-or-name name-of-new-image

-p pauses the container while commit command is building the new image. -a allows you to supply author information of the new image. -m allows you to add a comment just as in the Git.

From a stopped container

With a new startup command

sudo docker commit -c "CMD ['startup-command-here']" container-id-or-name name-of-new-image

With author and commit message

sudo docker commit -a 'author name' -m 'message here' container-id-or-name name-of-new-image

Containers

List the running containers

sudo docker ps

List all the containers

List the created, running, stopped and exited containers

sudo docker ps -a

Create, Run and Stop containers

Note:
Docker run command is an equivalent of two commands. The docker create and docker start command.

Create a container

create command

Prepares the image's file-system snapshot to be used in the container

sudo docker create --name some-name image-name

Start a container command

start command

The docker start command starts the container in detached(background) mode, while docker run starts the container in attached(foreground) mode.

Being attached means that we're listening to the output of the container(i.e we're connecting to the STDOUT of that container)

Executes the image's startup command, but does not show the container's output unless specified(can specify using logs command and run command displays logs by default).

sudo docker start container-name-or-id

Print the container's output to your terminal, with -a flag

sudo docker start -a container-name-or-id

Stop a container

Waits 10 seconds for container to stop automatically, if it doesn't it gets killed with a fallback docker kill command.

sudo docker stop container-name-or-id

Kill a container

Kills it immediatley

sudo docker kill container-name-or-id

Restart a stopped container

sudo docker start container-name-or-id

Delete the stopped containers

It also deletes the container cache

sudo docker system prune

Docker run command

Run the container interactively

Creates a container from image and shows the output of the container on your terminal.

sudo docker run -it image-name bash

can give it a name, with --name

sudo docker run -it --name my-container image-name bash

Run the container in detached mode

Runs in background and can take commands from outside, with docker exec

sudo docker run -dit image-name bash

Issue commands to a container running in detached mode

docker exec -it container_id_or_name echo "Hello from container!"

Example: You can use this to run rails console on a container which is running and(maybe there's a server running on it) Example of such a command

docker exec -it container_id_or_name rails console

Get Into the shell of a container running in the background

sudo docker attach container-name-or-id

Override container image's default command

sudo docker run container-name-or-id command-goes-here

example

sudo docker run container-name-or-id echo Hello World

Note:
The command you're specifying here should be supported by the image dependencies/libs, otherwise it won't work.

See container Logs

It does not re-run the container

sudo docker logs container-name-or-id

Container Port Mapping

  • The Container must have exposed it's respective port(i.e in the Dockerfile)

Syntax

sudo docker container run --publish host-port:container-port image-id

Example

sudo docker container run --publish 80:3000 image-id

It direct the host's port# 80's traffic to the container's port# 3000.

This is a runtime action and cannot be specified inside a Dockerfile.

Data Persistence for Docker Containers

  • There are two multiple to use persistant data for a docker container. Two of them are;

    1. Bind Mounts
    2. Volumes

Bind Mounts

Volumes

Volumes can be named or anonymous.

Create a volume

sudo docker volume create volume-name

List volumes

sudo docker volume ls

Inspect a volume

sudo docker volume inspect volume-name

Remove a volume

sudo docker volume rm volume-name

Start a container with a volume

sudo docker run -d --name my-container -v directory-from-build_context:/directory-in-container image-name

This -v or --volume flag maps directory-from-build_context to the directory-in-container i.e any changes done to either are reflected in both the places(confirm this....).

Prevent a directory in the container from being mapped to the volume's directory

  • Skip the directory-from-build_context and just specify the directory-in-container when giving the volume details i.e
sudo docker run -v /config/application.yml

This will prevent the /config/application.yml directory in the container being mapped to any directory in the build_context. i.e the /config/application.yml of the container remains unchanged, no matter what happens to it's build_context directory counterpart.

Multi Step Build Process

Summary: Multiple docker images can be used as base images to build a new docker image.

Example

Dockerfile

<!-- Phase 1: Builds a node app for production -->

FROM node:alpine as builder
WORKDIR '/app'
COPY package.json
RUN npm install
COPY . .
RUN npm run build


<!-- Phase 2: Run phase; use content from Phase 1 and do more stuff -->

FROM nginx
COPY --from=builder /app/build /usr/share/nginx/html
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment