Docker Basics
Tags: docker
Published: 2021-08-15 08:23:06

Penning down my learnings when going through the [official docker getting started tutorial](https://docs.docker.com/get-started/). Repository [here](https://github.com/tingzhouu/docker-basics) for reference.

The purpose of Docker is to be machine-agnostic, where anyone with the source code will be able to run the application.

Docker allows us to create images, which we can then run in a container that Docker creates on our machine.

Creating an image
![Docker Build Diagram](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/19mvnilvdy8mrfd53hyd.png)

Running an image in a Docker container
![Docker Run Diagram](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jkm24vmcjbdvpbco8g12.png)

To demonstrate, we have an express app running on port 3000, with an endpoint `GET /` that returns `"Hello World!"`.

## Dockerfile
The Dockerfile contains instructions on creating and running the image.

```
# Dockerfile, this line is a comment.
FROM node:12-alpine
COPY . my-wonderful-app
RUN cd my-wonderful-app && npm install
CMD ["node", "my-wonderful-app/app.js"]
```

### What does every line do?

#### FROM

`FROM node:12-alpine` | `FROM <parent_image>:<image_version>`

`FROM` specifies the parent image from which we are building, as specified [here](https://docs.docker.com/engine/reference/builder/#format). In this case, our parent image is `node`, using the version `12-alpine`.

What happens if we don't have this line: We will not be able to run any `node` or `npm` commands when inside the container.

#### COPY

`COPY . my-wonderful-app` | `COPY <source_directory>:<destination_directory_in_docker_container>`

`COPY` copies files from the current directory in the machine to the directory inside the docker container, more info [here](https://docs.docker.com/engine/reference/builder/#copy). This command is run when building the docker image.

In this case, we are copying the files from the current directory where the Dockerfile is, into the folder `my-wonderful-app` in the docker container.

What happens if we don't have this line: Our docker container will not contain our source code.

#### RUN

`RUN cd my-wonderful-app && npm install` | `RUN <command>`

`RUN` executes the command provided. This command is run when building the docker image. More information [here](https://docs.docker.com/engine/reference/builder/#run).

In this case, we are changing the directory to `my-wonderful-app` and running `npm install`.

What happens if we don't have this line: Our app in the docker container will not have a `node_modules` folder.

#### CMD

`CMD ["node", "my-wonderful-app/app.js"]` | `CMD ["executable","param1","param2"]`

`CMD` provides instructions on running the container. More information [here](https://docs.docker.com/engine/reference/builder/#cmd).

In this case, we are executing the command `node my-wonderful-app/app.js` when running the container.

What happens if we don't have this line: Our app will not be run.

## Build image

Now that we have the instructions that Docker needs, we can build the image by running `docker build -t myapp-image .` More info on the `docker build` command [here](https://docs.docker.com/engine/reference/commandline/build/).

### -t

`-t <image_name:image_tag>` | `-t myapp-image`

`-t` provides a name to the image. This allows us to identify the image more easily when we want to run the image. If the `image_tag` is not provided, the tag will be set to `latest`

What happens if we don't have this line: Not crucial, we can still run the image by its id.

### PATH

`.` | `PATH`

Here, we specify `.` as our path as we are pointing to our current directory

What happens if we don't have this line: The command won't run.

## Run image

After the image has been built, we can run the image. Docker will create a container in which the image will run. Run `docker -d -p 1234:3000 run myapp-image`.

### -d

`-d` specifies that we run this image in detached mode, meaning run it in the background.

What happens if we don't have this line: The terminal used to run the command will be monitoring the image that is run.

### -p

`-p <MACHINE_PORT>:<CONTAINER_PORT>` | `-p 1234:3000`

`-p`, which stands for publish, exposes a port on the local machine to a port on the container.

In this case, we are exposing port 1234 on the local machine to port 3000 on the docker container. Earlier, we specified in our app to run on port 3000.

Since the app is running on the container, the app will be using the container's port 3000. Making a call to `localhost:1234` will be redirected to the container's port 3000.

## How do I know it works

Open `localhost:1234` on the browser. You should see `"Hello World!"`.

## Docker Compose

It would be very tedious to keep running the build and run commands, especially when there are more containers. That is where docker-compose comes in.

Instead of running the build and run commands, we can specify them in a `docker-compose.yaml` file instead.

```
# docker-compose.yaml, this line is a comment.
version: "3.7"

services:
  favourite-app:
    build:
      context: .
    ports:
      - 1234:3000
```

### What does every line do?

#### version

This is the version we specify to be compatible with the docker engine. The latest version is most commonly used, which can be found from the compatibility matrix [here](https://docs.docker.com/compose/compose-file/).

In this case, we are using `"3.7"`.

#### services

These are the services that make up our application. One service will take up one container. Docker will create and run all the services that have been specified here. Examples of other services could be a database where we use the mongodb image.

In this case, we only have 1 service.

#### service_name:

This name will be reflected in the container name in the following format: `<folder_name>_<service_name>_<number>`

In this case, the folder name is `myapp`, the service name is `favourite_app`. The name of the container will be `myapp_favourite-app_1`. However, I don't know why there needs to be the number.

#### build:context:

```
build:
  context: .
```

This is to specify where our Dockerfile is, which is needed to create and run the image.

In this case, our Dockerfile is at the current directory, which is why we use `.` as the context.

#### ports:

```
ports:
    - 1234:3000
```

This is the publish flag that is used in the `docker run` command, which has been explained above.

## How do I know it works

Run `docker-compose up -d` in the terminal. After the container is up and running, open `localhost:1234` on the browser. You should see `"Hello World!"`.