CFC wants to make an app for accepting coffee orders. You will be creating a Dockerfile for CFC's (totally real) new and upcoming rebrand: Coders for Coffee 🐳. The architecture is a simple frontend and backend both made in TypeScript. Below is a diagram of it:
This workshop relies on a certain understanding of terminal commands and a bit of familiarity with Node.js projects.
+
This workshop relies on a certain understanding of terminal commands and a bit of familiarity with Node.js projects. If you're not familiar with these, don't worry! I'll be explaining everything as we go along. If you have any questions, feel free to ask.
Docker is a platform for developing, shipping, and running applications using containerization. It allows you to package an application and its dependencies into a container that can run on any machine. This makes it easy to deploy applications in a consistent and reproducible way. Simply put, it's like a way of running mini virtual machines inside your computer.
This command builds an image from the Dockerfile in the apps/backend directory and tags it (-t) with the name docker-workshop-backend.
+
This command builds an image from the Dockerfile in the apps/backend directory and tags it (-t) with the name docker-workshop-backend. Otherwise, Docker will give it a random name.
But wait, what does -p 3001:3001 do? I mentioned earlier that Docker basically runs mini virtual machines --- so let's visualise what that looks like.
-
In order for our container to make connect with the outside world, we need to create a little tunnel. This is done by mapping a port on the host machine to a port on the container. In our case, I've configured the backend to run internally on port 3001. Therefore, we need to map that internal port to a port on the host machine so we can access it. I kept it simple and mapped it to the same port, but you can change it to any port you like (given our frontend knows about it). Ports are mapped like this: -p <host-port>:<container-port> This is one of the security goals of Docker.
+
In order for our container to make connect with the outside world, we need to create a little tunnel. This is done by mapping a port on the host machine to a port on the container. In our case, I've configured the backend to run internally on port 3001. Therefore, we need to map that internal port to a port on the host machine so we can access it. I kept it simple and mapped it to the same port, but you can change it to any port you like (given our frontend knows about it). Ports are mapped like this: -p <host-port>:<container-port> This also improves security, as you can run multiple containers on the same host machine without them interfering with each other.
Now, visit http://localhost:3001 to see the backend now running from a Docker container.
The frontend is a React app that uses TypeScript. Now let's:
+
The frontend is a React app that uses TypeScript, so we'll follow the template. If you're unsure, you can always Google what you need e.g. "How to install dependenceis React app":
+
In your terminal, navigate to the frontend directory: cd apps/frontend
You can run these commands in the terminal yourself and open it up at http://localhost:9876/ to see the optimised production build of the frontend. Notice how the bottom text has changed from "development" to "production".
All Dockerfiles start with a FROM command, which specifies the base image to use. This image is usually a lightweight Linux distribution with the necessary tools and libraries to run the application. For our frontend, we will use the node:20-alpine image.
+
All Dockerfiles start with a FROM command, which specifies the base image to use. This image is usually a lightweight Linux distribution with the necessary tools and libraries to run the application. For our frontend, we will use the node:20-alpine image. This specifies the (as of writing) LTS version of Node.js with the Alpine Linux distribution.
How do I know what image to use?
There are a number of things to consider when choosing a base image:
Now that we have our source code in the image, we need to install the dependencies. This is done with the RUN command. Here, we run both npm install and npm run build in succession using &&.
+
Now that we have our source code in the image, we need to install the dependencies. This is done with the RUN command. You can execute any supported command from the base image specified. Here, we run both npm install and npm run build in succession using &&.