Dockerfile and Docker Compose
Learning objectives
- Knows what a
Dockerfile
is and what adocker-compose.yml
file is. - Follows guidelines for setting up an application that runs within a container.
- Can build, start, and stop a containerized application with Docker Compose.
Applications that run within a Docker container use a Dockerfile
that contains the commands needed for creating the docker image and for launching the application. In applications that have multiple containers, a Docker Compose file is used for managing all the containers at the same time.
Dockerfile
We first add a Dockerfile to the project. A Dockerfile is a text file that contains the commands that are executed when creating a docker image. A docker image is a template that contains instructions for creating a runnable container. For example, one can have an image for Alpine Linux operating system and use that to create and run multiple Alpine containers. Create a file called Dockerfile
to the same folder with app.js
and copy the following contents to the Dockerfile
.
FROM denoland/deno:alpine-1.42.2
EXPOSE 7777
WORKDIR /app
COPY . .
RUN deno cache app.js
CMD [ "run", "--allow-net", "--watch", "--unstable", "app.js" ]
What the above Dockerfile effectively does is that it downloads an Alpine Linux based Docker image from https://hub.docker.com/r/denoland/deno as the starting point for the image, exposes the port 7777 (the port that our app.js
binds to) from the docker container to the outside world, creates and sets a working directory, copies files from the present directory to the working directory, asks deno to cache dependencies from app.js
, and states the command for running the image. The base image includes deno
as entrypoint, so the given instructions after CMD
are passed to the deno
executable.
Effectively, the last line above translates to the command deno run --allow-net --watch app.js
.
At this point, the folder structure of the project is as follows:
tree --dirsfirst
.
├── app.js
└── Dockerfile
Each docker image is run in a container -- let's next look at how this is done.
Docker Compose
Docker images are typically run with Docker Compose, which is a tool that helps running Docker applications with multiple containers. In order to use Docker Compose, we need to create a YAML file called docker-compose.yml
. The file will contain information on all the containers that are run in the project.
Create a file called docker-compose.yml
to the same folder with the Dockerfile
and copy the following content to the file.
version: "3.4"
services:
app:
build: .
restart: unless-stopped
volumes:
- ./:/app
ports:
- 7777:7777
This effectively states that we currently have one container, some details related to the container, and that we expose the port 7777 from the container as the port 7777 in our operating system. The restart: unless-stopped
means that Docker is instructed to restart the container always, except when the container is stopped.
At this point, the folder structure is as follows:
tree --dirsfirst
.
├── app.js
├── docker-compose.yml
└── Dockerfile
Running with Docker Compose
Once the files are saved, we can run the application with Docker Compose (the command docker-compose
). This is done as follows. We first check that we are in the correct directory and that the files are located within that directory. Then we run docker compose up --build
.
tree --dirsfirst
.
├── app.js
├── docker-compose.yml
└── Dockerfile
docker compose up --build
Creating network "deno-docker_default" with the default driver
Building app
Step 1/.. : FROM denoland/deno:alpine-1.42.2
// ..
Step .. : CMD [ "run", "--allow-net", "--watch", "--unstable", "app.js" ]
// ...
app_1 | Watcher Process finished. Restarting on file change...
Now, after waiting for a few moments, the application is running and we can access it at the port 7777.
curl http://localhost:7777/
Meaning of life: 42%
We can stop the application using Ctrl + C in the terminal where we called the docker compose command. Running the command docker compose stop
in the project folder explicitly stops the containers created by the command docker compose up
.
docker compose stop
In practice, when we ran the command docker compose up --build
, we stated that the containers outlined in the docker-compose.yml
file should be started and built. If we would have omitted the --build
flag, we would have used the images that have previously been built (if any existed) to launch the container.