This is the first of a series of posts on containers. Here is a breakdown of what's to come.
- Part 1 will look at putting a simple app into a Docker container
2: Part 2 will look at building some persistent storage for our application using Dockeer.
3: Part 3 will look at taking our application we containerised using Docker and putting it into a local Kubernetes deployment using minikube.
4: Part 4 will look at taking our application and deploying it to an Azure Kubernetes Cluster and exposing it publicly.
5: Part 5 will look at adding an ingress controller and WAF in front of our application using Azure Application Gateway.
Docker has grown in adoption since it was first introduced in 2013. Docker engine is by no means the only containerisation technology out in the market. You can use open source tools such as Buildah or Podman to achieve the same results. Docker actually consists of a number of products however it is most commonly known for Docker engine.
The images we are going to be building are of file type .Dockerfile however these are actually OCI images. OCI or open container initiative is an open source governance structure for creating open industry standards for container formats and runtimes. What this means is that you can any OCI formatted image is generally transportable to any container runtime supporting OCI and not just Docker so you could actually run your image using the containerd runtime or a runtime that supports OCI.
Before we dive into the tutorial here is a comparison of containers vs virtualisation. In simple terms, containers allow you to run multiple applications on a single operating system on a single server whereas virtualisation allows you to run multiple operating systems on a single physical server. This means containers are portable and lightweight as they do not carry the burden of the operating system. You can run containers either on bare metal servers or more commonly on virtual machines so typically containers run on top of OS virtualisation.
There are some tools required in order to put our sample application into Docker as listed below.
WSL2 if you are using windows. This allows you to run a Linux subsystem if your are using a windows machine. If you are using Mac OS x then this is not required.
1: Start of by creating a fork of my repository located on Github.
2: Clone the repository to your local machine by running
3: Navigate to the
docker/app directory where you will see the Dockerfile for the application and the source code which we will compile when building the image.
Before we build the image let's breakdown the instructions in the Dockerfile. A Dockerfile is a text document that contains all the commands a user could call on the command line to assemble an image. These are what's used to build our image.
FROM node:12-alpine RUN apk add --no-cache python2 g++ make WORKDIR /app COPY . . RUN yarn install --production CMD ["node", "src/index.js"] EXPOSE 3000
From - Here we are specifying an existing image from the public Docker repository to use as a base.
Run - RUN instruction is used to executes any commands on top of the current image and this will create a new layer.
COPY - COPY instruction is used to copy files, directories and remote URL files to the destination within the filesystem of the Docker Images.
CMD - CMD instruction is used to set a command to be executed when running a container. There must be only one CMD in a Dockerfile.
EXPOSE - EXPOSE instruction is used to inform about the network ports that the container listens on runtime
Let's build the image
Docker Build -t todo-list
The image will take a couple minutes to build. Once it's complete we can check the image exists by listing our docker images:
Docker image ls
We can see our image was built successfully and is tagged "latest". We can optionally use semantic versioning.
5: Let's run the application locally:
docker run -dp 3000:3000 todo-list
You should now be able to access the to-do list app on
http://localhost:3000/ in your browser.
-dp flag is telling Docker to detach the container and map the application port of 3000 to a local port on our machine and in this it's 3000. You can map the port the application is listening to on any port available on your machine.
Currently any items added to the to-list will be removed once the container is stopped. In the next post we will attach some persistent storage to our application that will allow us to persist the items added to the list in local storage when the container is stopped so we don't loose the data.