We will use a simple todo list app throughout this workshop. It is written in Node.js.
At this point, your development team is quite small and you’re simply building an app to prove out your MVP (minimum viable product). You want to show how it works and what it’s capable of doing without needing to think about how it will work for a large team, multiple developers, etc.
Note bwLehrpool: Change into the PERSISTENT directory before cloning the repository!
git clone https://github.com/Harald-U/docker-101.git
cd docker-101/app
What is a container image? When running a container, it uses an isolated filesystem. This custom filesystem is provided by a container image. Since the image contains the container’s filesystem, it must contain everything needed to run an application - all dependencies, configuration, scripts, binaries, etc. The image also contains other configuration for the container, such as environment variables, a default command to run, and other metadata.
In order to build a container image, we need to use a Dockerfile. A Dockerfile is something like a cooking recipe. It is simply a text-based script of instructions and “ingredients” that are used to package your application into a container image.
Create a file named Dockerfile
in the same folder as the file package.json with the following contents:
FROM node:18-alpine
WORKDIR /app
COPY . .
RUN yarn install --production
EXPOSE 3000
CMD ["node", "src/index.js"]
Note bwLehrpool: As editor in Ubuntu you can use ‘vi’ or ‘nano’ which are text-based in the shell or you can use ‘gedit’, a GUI text editor, available also in the sidebar.
Please check that the file Dockerfile has no file extension like .txt. Some editors may append this file extension automatically and this would result in an error in the next step.
What do the lines in the Dockerfile mean?
If you haven’t already done so, open a terminal and go to the app directory with the Dockerfile. Now build the container image using the docker build command.
docker build -t todo-app .
This command used the Dockerfile to build a new container image. You might have noticed that a lot of “layers” were downloaded. This is because we instructed the builder that we wanted to start FROM the node:18-alpine image. But, since we didn’t have that on our machine, that image needed to be downloaded.
After the image was downloaded, we copied in our application and used yarn to install our application’s dependencies. The CMD directive specifies the default command to run when starting a container from this image.
Finally, the -t flag ‘tags’ our image. Think of this simply as a human-readable name for the final image. Since we named the image ‘todo-app’, we can refer to that image when we run a container.
Don’t forget the . at the end of the docker build command! It tells Docker that it should look for a Dockerfile in the current directory.
Now that we have an image, let’s run the application! To do so, we will use the docker run command to start a container based on the container image we just created.
What is a container? Simply put, a container is just another process on your machine that has been isolated from all other processes on the host machine. That isolation leverages kernel namespaces and cgroups, features that have been in the Linux kernel for a long time.
Start your container using the docker run command and specify the name of the image we just created:
docker run -d -p 3000:3000 todo-app
What are the -d and -p flags?
Note: the first port number (before the ‘:’) is the host port on your notebook etc. If this port is already taken by another app, you could try another port.
The second port (after the ‘:’) is the container port. How do we know that the container port is 3000? If you look in file src/index.js
you can find this statement:
app.listen(3000, () => console.log('Listening on port 3000'));
So the ToDo app is listening on port 3000. And in the Dockerfile we specified EXPOSE 3000
which means we tell Docker the container listens on the specified network port at runtime.
After a few seconds, open your web browser to http://localhost:3000. You should see our app!
Go ahead and add an item or two and see that it works as you expect. You can mark items as complete and remove items. Your frontend is successfully storing items in the backend!
You can see your running container in Docker Dashboard (Mac, Windows) or via commandline:
docker ps
Result:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b46846361370 todo-app "docker-entrypoint.s…" 6 minutes ago Up 6 minutes 0.0.0.0:3000->3000/tcp, :::3000->3000/tcp focused_kepler
Next Step: Update the app, build a new image