0% found this document useful (0 votes)
11 views20 pages

Week 7 Practical

This lab manual provides a comprehensive guide on using Docker to containerize applications, specifically a Node.js todo list manager. It covers tasks such as familiarizing with Docker commands, building and running Docker images, updating applications, and sharing images on Docker Hub. The document includes step-by-step instructions and troubleshooting tips for managing Docker containers effectively.

Uploaded by

demodgdg9
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
11 views20 pages

Week 7 Practical

This lab manual provides a comprehensive guide on using Docker to containerize applications, specifically a Node.js todo list manager. It covers tasks such as familiarizing with Docker commands, building and running Docker images, updating applications, and sharing images on Docker Hub. The document includes step-by-step instructions and troubleshooting tips for managing Docker containers effectively.

Uploaded by

demodgdg9
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 20

Lab Manual

Week 7
Faculty of Computing, Engineering and
The Environment
Contents
1 Introduction .................................................................................................................................... 3
1.1 Task 1 – Familiarise yourself and check docker is working ..................................................... 3
1.2 Run Hello World Image ........................................................................................................... 4
2 Containerize an application............................................................................................................. 4
2.1 Prerequisites ........................................................................................................................... 5
2.2 Get the app ............................................................................................................................. 5
2.3 Build the app's image .............................................................................................................. 5
2.4 Start an app container............................................................................................................. 7
2.5 Summary ................................................................................................................................. 7
3 Update the application.................................................................................................................... 8
3.1 Update the source code .......................................................................................................... 8
3.2 Remove the old container ....................................................................................................... 8
3.3 Start the updated app container ............................................................................................. 9
3.4 Summary ................................................................................................................................. 9
3.5 Share the application .............................................................................................................. 9
3.6 Create a repository.................................................................................................................. 9
3.7 Push the image...................................................................................................................... 10
3.8 Run the image on a new instance ......................................................................................... 10
3.9 Summary ............................................................................................................................... 11
4 Persist the DB ................................................................................................................................ 11
4.1 The container's filesystem..................................................................................................... 12
4.1.1 See this in practice ........................................................................................................ 12
4.2 Container volumes ................................................................................................................ 13
4.3 Create a volume and start the container .............................................................................. 13
4.4 Verify that the data persists .................................................................................................. 14
4.5 Dive into the volume ............................................................................................................. 15
4.6 Summary ............................................................................................................................... 15
5 Use bind mounts ........................................................................................................................... 15
5.1 Quick volume type comparisons ........................................................................................... 16
5.2 Trying out bind mounts ......................................................................................................... 16
5.3 Development containers ....................................................................................................... 18
5.3.1 Run your app in a development container.................................................................... 18
5.3.2 Develop your app with the development container ..................................................... 19
5.4 Summary ............................................................................................................................... 20
1 Introduction
This practical is about docker and how to run, manage, and build docker containers. This includes
forwarding ports to the host and using persistent volumes to store data. These principles apply
anywhere containers are in use although the specific mechanism might change slightly. For example,
setting up persistent storage is done differently when a container is deployed by Kubernetes than
when done by Docker alone.

1.1 Task 1 – Familiarise yourself and check docker is working


The first task is to familiarise yourself with some of the core docker commands you will need. There
is a full reference of the commands docker supports here:
https://docs.docker.com/engine/reference/commandline/docker/

At a minimum you will need to know about the list of commands bellow and how to use them to
manage and build containers with docker. “run” in particular supports lots of options and it is worth
knowing at least at a high level what’s possible and what you might want to do.

docker pull Pull and image from a repository


docker image Work with docker images
docker ps Work with containers
docker build Build an image
docker run Run an image as a container
docker stop Stop a container
It would also be beneficial to know the answer to the following questions, which may require reading
about additional commands from the reference:

1. How would you run a container in the background to keep your terminal free and keep it
running?
2. How do we reconnect to a container we started in the background to see its output?
3. If you have run a container in the background, how do you stop it?
4. How would you run another command in an already running container? (e.g., for debugging)

Once you have read up on the core commands you will need you can start using docker. In the
Windows PC’s in the lab docker is accessible via PowerShell, open a PowerShell terminal and run
“docker” and ensure you are presented with a command listing. If when using docker you see an
error message similar to “docker: error during connect: This error may indicate that the docker
daemon is not running.” It usually means you need to run docker desktop first. To start Docker
Desktop Search for Docker and select Docker Desktop in the search results.
If you need to install docker on your personal computer, follow the instructions given in the link Get
Docker | Docker Docs.

1.2 Run Hello World Image


Docker hub contains a testing image called “hello-world” (https://hub.docker.com/_/hello-world).

Using the information from the lecture slides and the commands you researched at the start; pull
and run the “hello-world” image to ensure docker is working and verify you get the success text from
the hello-world container. Remember it may be necessary to open the “Docker Desktop”
application first for it to start the necessary systems. Docker Desktop can provide a helpful UI to
view the state of your docker setup, images, and running containers. But do not lean on it too much
as knowing how to use the command line for management is an important skill.

Once the hello-world container has run you should see output like the following:

Tip: “docker run” will automatically pull the image you reference if it is not already downloaded

2 Containerize an application
For the rest of this task, you'll be working with a simple to do list manager that runs on Node.js. If
you're not familiar with Node.js, don't worry. This guide doesn't require any prior experience with
JavaScript.
2.1 Prerequisites

• The Docker Desktop is up and running.


• You have installed a Git client.
• You have an IDE or a text editor to edit files. Docker recommends using Visual Studio Code.

2.2 Get the app


Before you can run the application, you need to get the application source code onto your
machine.

• Clone the getting-started-app repository using the following command:


$ 𝐠𝐢𝐭 𝐜𝐥𝐨𝐧𝐞 𝐡𝐭𝐭𝐩𝐬://𝐠𝐢𝐭𝐡𝐮𝐛. 𝐜𝐨𝐦/𝐝𝐨𝐜𝐤𝐞𝐫/𝐠𝐞𝐭𝐭𝐢𝐧𝐠 − 𝐬𝐭𝐚𝐫𝐭𝐞𝐝 − 𝐚𝐩𝐩. 𝐠𝐢𝐭

• View the contents of the cloned repository. You should see the following files and
sub-directories.
├── getting-started-app/
│ ├── package.json
│ ├── README.md
│ ├── spec/
│ ├── src/
│ └── yarn.lock

2.3 Build the app's image


To build the image, you'll need to use a Dockerfile. A Dockerfile is simply a text-based file
with no file extension that contains a script of instructions. Docker uses this script to build a
container image.

• In the getting-started-app directory, the same location as the package.json file,


create a file named Dockerfile. You can use the following commands to create a
Dockerfile based on your operating system.
In the Windows Command Prompt (or PowerShell), run the following commands.
Make sure you're in the getting-started-app directory. Replace \path\to\getting-started-
app with the path to your getting-started-app directory.
$ 𝐜𝐝 \𝐩𝐚𝐭𝐡\𝐭𝐨\𝐠𝐞𝐭𝐭𝐢𝐧𝐠 − 𝐬𝐭𝐚𝐫𝐭𝐞𝐝 − 𝐚𝐩𝐩
Create an empty file named Dockerfile.

$ 𝒕𝒚𝒑𝒆 𝒏𝒖𝒍 > 𝑫𝒐𝒄𝒌𝒆𝒓𝒇𝒊𝒍𝒆

• Using a text editor or code editor, add the following contents to the Dockerfile:

# syntax=docker/dockerfile:1

FROM node:18-alpine
WORKDIR /app
COPY . .
RUN yarn install --production
CMD ["node", "src/index.js"]
EXPOSE 3000

• Build the image using the following commands:

In the terminal, make sure you're in the getting-started-app directory. Replace /path/to/getting-
started-app with the path to your getting-started-app directory.

$ cd /path/to/getting-started-app

Build the image.

$ docker build -t getting-started .

The expected output when you run the above command successfully.

The docker build command uses the Dockerfile to build a new image. You might have noticed that
Docker downloaded a lot of "layers". This is because you instructed the builder that you wanted to
start from the node:18-alpine image. But, since you didn't have that on your machine, Docker
needed to download the image.

After Docker downloaded the image, the instructions from the Dockerfile copied in your application
and used yarn to install your application's dependencies. The CMD directive specifies the default
command to run when starting a container from this image.

Finally, the -t flag tags your image. Think of this as a human-readable name for the final image. Since
you named the image getting-started, you can refer to that image when you run a container.
The . at the end of the docker build command tells Docker that it should look for the Dockerfile in
the current directory.

2.4 Start an app container


Now that you have an image, you can run the application in a container using the docker
run command.

• Run your container using the docker run command and specify the name of the image you
just created:

$ docker run -dp 127.0.0.1:3000:3000 getting-started

The -d flag (short for --detach) runs the container in the background. The -p flag (short for --publish)
creates a port mapping between the host and the container. The -p flag takes a string value in the
format of HOST:CONTAINER, where HOST is the address on the host, and CONTAINER is the port on
the container. The command publishes the container's port 3000 to 127.0.0.1:3000 (localhost:3000)
on the host. Without the port mapping, you wouldn't be able to access the application from the
host.

• After a few seconds, open your web browser to http://localhost:3000open_in_new. You


should see your app.

• Add an item or two and see that it works as you expect. You can mark items as complete and
remove them. Your frontend is successfully storing items in the backend.

At this point, you have a running todo list manager with a few items.

If you take a quick look at your containers, you should see at least one container running that's using
the getting-started image and on port 3000. To see your containers, you can use the CLI or Docker
Desktop's graphical interface.

Run the following docker ps command in a terminal to list your containers.

$ docker ps

Output similar to the following should appear.

2.5 Summary
In this section, you learned the basics about creating a Dockerfile to build an image. Once you built
an image, you started a container and saw the running app.
3 Update the application
In part 2, you containerized a todo application. In this part, you'll update the application and image.
You'll also learn how to stop and remove a container.

3.1 Update the source code


In the following steps, you'll change the "empty text" when you don't have any todo list items to "You
have no todo items yet! Add one above!"

• In the src/static/js/app.js file, update line 56 to use the new empty text.

- <p className="text-center">No items yet! Add one above!</p>

+ <p className="text-center">You have no todo items yet! Add one above!</p>

• Build your updated version of the image, using the docker build command.

$ docker build -t getting-started .

• Start a new container using the updated code.

$ docker run -dp 127.0.0.1:3000:3000 getting-started

You probably saw an error like this:

docker: Error response from daemon: driver failed programming external connectivity on endpoint
laughing_burnell
(bb242b2ca4d67eba76e79474fb36bb5125708ebdabd7f45c8eaf16caaabde9dd): Bind for
127.0.0.1:3000 failed: port is already allocated.

The error occurred because you aren't able to start the new container while your old container is still
running. The reason is that the old container is already using the host's port 3000 and only one
process on the machine (containers included) can listen to a specific port. To fix this, you need to
remove the old container.

3.2 Remove the old container


To remove a container, you first need to stop it. Once it has stopped, you can remove it. You can
remove the old container using the CLI or Docker Desktop's graphical interface. Choose the option
that you're most comfortable with.

Remove a container using the CLI

• Get the ID of the container by using the docker ps command.

$ docker ps

• Use the docker stop command to stop the container. Replace <the-container-id> with the ID
from docker ps.

$ docker stop <the-container-id>

• Once the container has stopped, you can remove it by using the docker rm command.

$ docker rm <the-container-id>
Note: You can stop and remove a container in a single command by adding the force flag to
the docker rm command. For example: docker rm -f <the-container-id>

3.3 Start the updated app container


• Now, start your updated app using the docker run command.

$ docker run -dp 127.0.0.1:3000:3000 getting-started

• Refresh your browser on http://localhost:3000open_in_new and you should see your


updated help text.

3.4 Summary
In this section, you learned how to update and rebuild a container, as well as how to stop and
remove a container.

3.5 Share the application


Now that you've built an image, you can share it. To share Docker images, you have to use a Docker
registry. The default registry is Docker Hub and is where all of the images you've used have come
from.

Docker ID

A Docker ID lets you access Docker Hub, which is the world's largest library and community for
container images. Create a Docker ID for free if you don't have one.

3.6 Create a repository


To push an image, you first need to create a repository on Docker Hub.

Sign up or Sign in to Docker Hub.

• Select the Create Repository button.


• For the repository name, use getting-started. Make sure the Visibility is Public.
• Select Create.
• In the following image, you can see an example Docker command from Docker Hub. This
command will push to this repository.
3.7 Push the image
• In the command line, run the docker push command that you see on Docker Hub. Note that
your command will have your Docker ID, not "docker".

$ docker push docker/getting-started

The push refers to repository [docker.io/docker/getting-started]

An image does not exist locally with the tag: docker/getting-started

Why did it fail? The push command was looking for an image named docker/getting-started, but
didn't find one. If you run docker image ls, you won't see one either.

To fix this, you need to tag your existing image you've built to give it another name.

• Sign in to Docker Hub using the command docker login -u YOUR-USER-NAME.


• Use the docker tag command to give the getting-started image a new name. Replace YOUR-
USER-NAME with your Docker ID.

$ docker tag getting-started YOUR-USER-NAME/getting-started

Now run the docker push command again. If you're copying the value from Docker Hub, you can drop
the tagname part, as you didn't add a tag to the image name. If you don't specify a tag, Docker uses a
tag called latest.

$ docker push YOUR-USER-NAME/getting-started

3.8 Run the image on a new instance

• Now that your image has been built and pushed into a registry, try running your app on a
brand new instance that has never seen this container image. To do this, you will use Play
with Docker.

Note: Play with Docker uses the amd64 platform. If you are using an ARM based Mac with Apple
Silicon, you will need to rebuild the image to be compatible with Play with Docker and push the new
image to your repository.

To build an image for the amd64 platform, use the --platform flag.

$ docker build --platform linux/amd64 -t YOUR-USER-NAME/getting-started .

Docker buildx also supports building multi-platform images. To learn more, see Multi-platform
images.

• Open your browser to Play with Docker.


• Select Login and then select docker from the drop-down list.
• Sign in with your Docker Hub account and then select Start.
• Select the ADD NEW INSTANCE option on the left side bar. If you don't see it, make your
browser a little wider. After a few seconds, a terminal window opens in your browser.
• In the terminal, start your freshly pushed app.

$ docker run -dp 0.0.0.0:3000:3000 YOUR-USER-NAME/getting-started

You should see the image get pulled down and eventually start up.

Tip

You may have noticed that this command binds the port mapping to a different IP address.
Previous docker run commands published ports to 127.0.0.1:3000 on the host. This time, you're
using 0.0.0.0.

Binding to 127.0.0.1 only exposes a container's ports to the loopback interface. Binding to 0.0.0.0,
however, exposes the container's port on all interfaces of the host, making it available to the outside
world.

For more information about how port mapping works, see Networking.

Select the 3000 badge when it appears.

If the 3000 badge doesn't appear, you can select Open Port and specify 3000.

3.9 Summary
In this section, you learned how to share your images by pushing them to a registry. You then went to
a brand new instance and were able to run the freshly pushed image. This is quite common in CI
pipelines, where the pipeline will create the image and push it to a registry and then the production
environment can use the latest version of the image.

4 Persist the DB
In case you didn't notice, your todo list is empty every single time you launch the container. Why is
this? In this part, you'll dive into how the container is working.
4.1 The container's filesystem
When a container runs, it uses the various layers from an image for its filesystem. Each container also
gets its own "scratch space" to create/update/remove files. Any changes won't be seen in another
container, even if they're using the same image.

4.1.1 See this in practice


To see this in action, you're going to start two containers and create a file in each. What you'll see is
that the files created in one container aren't available in another.

Note: If you use Windows and want to use Git Bash to run Docker commands, see Working with Git
Bash for syntax differences.

• Start an ubuntu container that will create a file named /data.txt with a random number
between 1 and 10000.

$ docker run -d ubuntu bash -c "shuf -i 1-10000 -n 1 -o /data.txt && tail -f /dev/null"

In case you're curious about the command, you're starting a bash shell and invoking two commands
(why you have the &&). The first portion picks a single random number and writes it to /data.txt. The
second command is simply watching a file to keep the container running.

• Validate that you can see the output by accessing the terminal in the container. To do so,
you can use the CLI or Docker Desktop's graphical interface.

On the command line, use the docker exec command to access the container. You need to get the
container's ID (use docker ps to get it). In your Mac or Linux terminal, or in Windows Command
Prompt or PowerShell, get the content with the following command.

$ docker exec <container-id> cat /data.txt

Tip: Docker Desktop (you can also do the above using Docker Desktop, but if you have don it using
command line then you do not need to do in Docker Desktop)

In Docker Desktop, go to Containers, hover over the container running the ubuntu image, and select
the Show container actions menu. From the drop-down menu, select Open in terminal.

You will see a terminal that is running a shell in the Ubuntu container. Run the following command to
see the content of the /data.txt file. Close this terminal afterwards again.

content_copy

$ cat /data.txt

You should see a random number.

• Now, start another ubuntu container (the same image) and you'll see you don't have the
same file. In your Mac or Linux terminal, or in Windows Command Prompt or PowerShell,
get the content with the following command.

$ docker run -it ubuntu ls /


In this case the command lists the files in the root directory of the container. Look, there's
no data.txt file there! That's because it was written to the scratch space for only the first container.

• Go ahead and remove the first container using the docker rm -f <container-id> command.

4.2 Container volumes


With the previous experiment, you saw that each container starts from the image definition each
time it starts. While containers can create, update, and delete files, those changes are lost when you
remove the container and Docker isolates all changes to that container. With volumes, you can
change all of this.

Volumes provide the ability to connect specific filesystem paths of the container back to the host
machine. If you mount a directory in the container, changes in that directory are also seen on the
host machine. If you mount that same directory across container restarts, you'd see the same files.

There are two main types of volumes. You'll eventually use both, but you'll start with volume
mounts.

Persist the todo data

By default, the todo app stores its data in a SQLite database at /etc/todos/todo.db in the container's
filesystem. If you're not familiar with SQLite, no worries! It's simply a relational database that stores
all the data in a single file. While this isn't the best for large-scale applications, it works for small
demos. You'll learn how to switch this to a different database engine later.

With the database being a single file, if you can persist that file on the host and make it available to
the next container, it should be able to pick up where the last one left off. By creating a volume and
attaching (often called "mounting") it to the directory where you stored the data, you can persist the
data. As your container writes to the todo.db file, it will persist the data to the host in the volume.

As mentioned, you're going to use a volume mount. Think of a volume mount as an opaque bucket of
data. Docker fully manages the volume, including the storage location on disk. You only need to
remember the name of the volume.

4.3 Create a volume and start the container


You can create the volume and start the container using the CLI or Docker Desktop's graphical
interface.

• Create a volume by using the docker volume create command.

$ docker volume create todo-db

• Stop and remove the todo app container once again with docker rm -f <id>, as it is still
running without using the persistent volume.
• Start the todo app container, but add the --mount option to specify a volume mount. Give
the volume a name, and mount it to /etc/todos in the container, which captures all files
created at the path. In your Mac or Linux terminal, or in Windows Command Prompt or
PowerShell, run the following command:

$ docker run -dp 127.0.0.1:3000:3000 --mount type=volume,src=todo-db,target=/etc/todos


getting-started
To create a volume: (you can also do the above using Docker Desktop, but if you have don it using
command line then you do not need to do in Docker Desktop)

• Select Volumes in Docker Desktop.


• In Volumes, select Create.
• Specify todo-db as the volume name, and then select Create.

To stop and remove the app container:

• Select Containers in Docker Desktop.


• Select Delete in the Actions column for the container.
• To start the todo app container with the volume mounted:

Select the search box at the top of Docker Desktop.

• In the search window, select the Images tab.


• In the search box, specify the container name, getting-started.
• Tip: Use the search filter to filter images and only show Local images.
• Select your image and then select Run.
• Select Optional settings.
• In Host port, specify the port, for example, 3000.
• In Host path, specify the name of the volume, todo-db.
• In Container path, specify /etc/todos.
• Select Run.

4.4 Verify that the data persists

• Once the container starts up, open the app and add a few items to your todo list.

• Stop and remove the container for the todo app. Use Docker Desktop or docker ps to get the
ID and then docker rm -f <id> to remove it.
• Start a new container using the previous steps.
• Open the app. You should see your items still in your list.
• Go ahead and remove the container when you're done checking out your list.

You've now learned how to persist data.

4.5 Dive into the volume


A lot of people frequently ask "Where is Docker storing my data when I use a volume?" If you want
to know, you can use the docker volume inspect command.

$ docker volume inspect todo-db

"CreatedAt": "2019-09-26T02:18:36Z",

"Driver": "local",

"Labels": {},

"Mountpoint": "/var/lib/docker/volumes/todo-db/_data",

"Name": "todo-db",

"Options": {},

"Scope": "local"

The Mountpoint is the actual location of the data on the disk. Note that on most machines, you will
need to have root access to access this directory from the host.

4.6 Summary
In this section, you learned how to persist container data.

5 Use bind mounts


In Previous part, you used a volume mount to persist the data in your database. A volume mount is a
great choice when you need somewhere persistent to store your application data.

A bind mount is another type of mount, which lets you share a directory from the host's filesystem
into the container. When working on an application, you can use a bind mount to mount source code
into the container. The container sees the changes you make to the code immediately, as soon as you
save a file. This means that you can run processes in the container that watch for filesystem changes
and respond to them.

In this chapter, you'll see how you can use bind mounts and a tool called nodemon to watch for file
changes, and then restart the application automatically. There are equivalent tools in most other
languages and frameworks.
5.1 Quick volume type comparisons
The following table outlines the main differences between volume mounts and bind mounts.

Named volumes Bind mounts

Host
location Docker chooses You decide

Mount
example type=volume,src=my-
(using -- volume,target=/usr/local/da type=bind,src=/path/to/data,target=/usr/local/
mount ) ta data

Populate
s new
volume
with
container
contents Yes No

Supports
Volume
Drivers Yes No

5.2 Trying out bind mounts


Before looking at how you can use bind mounts for developing your application, you can run a quick
experiment to get a practical understanding of how bind mounts work.

Note: If you use Windows and want to use Git Bash to run Docker commands, see Working with Git
Bash for syntax differences.

• Verify that your getting-started-app directory is in a directory defined in Docker Desktop's


file sharing setting. This setting defines which parts of your filesystem you can share with
containers. For details about accessing the setting, see the topic for Mac, Windows, or Linux.
• Open a terminal and change directory to the getting-started-app directory.
• Run the following command to start bash in an ubuntu container with a bind mount

$ docker run -it --mount "type=bind,src=$pwd,target=/src" ubuntu bash

The --mount option tells Docker to create a bind mount, where src is the current working directory
on your host machine (getting-started-app), and target is where that directory should appear inside
the container (/src).

• After running the command, Docker starts an interactive bash session in the root directory
of the container's filesystem.

root@ac1237fad8db:/# pwd

root@ac1237fad8db:/# ls
bin dev home media opt root sbin srv tmp var

boot etc lib mnt proc run src sys usr

• Change directory to the src directory.

This is the directory that you mounted when starting the container. Listing the contents of this
directory displays the same files as in the getting-started-app directory on your host machine.

root@ac1237fad8db:/# cd src

root@ac1237fad8db:/src# ls

Dockerfile node_modules package.json spec src yarn.lock

• Create a new file named myfile.txt.

root@ac1237fad8db:/src# touch myfile.txt

root@ac1237fad8db:/src# ls

Dockerfile myfile.txt node_modules package.json spec src yarn.lock

• Open the getting-started-app directory on the host and observe that the myfile.txt file is in
the directory.

├── getting-started-app/

│ ├── Dockerfile

│ ├── myfile.txt

│ ├── node_modules/

│ ├── package.json

│ ├── spec/

│ ├── src/

│ └── yarn.lock

• From the host, delete the myfile.txt file.


• In the container, list the contents of the app directory once more. Observe that the file is
now gone.

root@ac1237fad8db:/src# ls

Dockerfile node_modules package.json spec src yarn.lock

Stop the interactive container session with Ctrl + D.

That's all for a brief introduction to bind mounts. This procedure demonstrated how files are shared
between the host and the container, and how changes are immediately reflected on both sides. Now
you can use bind mounts to develop software.
5.3 Development containers
Using bind mounts is common for local development setups. The advantage is that the development
machine doesn’t need to have all of the build tools and environments installed. With a single docker
run command, Docker pulls dependencies and tools.

5.3.1 Run your app in a development container


The following steps describe how to run a development container with a bind mount that does the
following:

Mount your source code into the container

Install all dependencies

Start nodemon to watch for filesystem changes

You can use the CLI or Docker Desktop to run your container with a bind mount.

CLI (Mac / Linux) CLI (Windows) Docker Desktop

Make sure you don't have any getting-started containers currently running.

Run the following command from the getting-started-app directory.

Run this command in PowerShell.

$ docker run -dp 127.0.0.1:3000:3000 `

-w /app --mount "type=bind,src=$pwd,target=/app" `

node:18-alpine `

sh -c "yarn install && yarn run dev"

Tip: you can also do the above using Docker Desktop, but if you have don it using command line
then you do not need to do in Docker Desktop

Make sure you don't have any getting-started containers currently running.

Run the image with a bind mount.

Select the search box at the top of Docker Desktop.

In the search window, select the Images tab.

In the search box, specify the container name, getting-started.

Tip:Use the search filter to filter images and only show Local images.

Select your image and then select Run.

Select Optional settings.

In Host path, specify the path to the getting-started-app directory on your host machine.

In Container path, specify /app.


Select Run.

The following is a breakdown of the command:

• -dp 127.0.0.1:3000:3000 - same as before. Run in detached (background) mode and create a
port mapping
• -w /app - sets the "working directory" or the current directory that the command will run
from
• --mount "type=bind,src=$pwd,target=/app" - bind mount the current directory from the
host into the /app directory in the container
• node:18-alpine - the image to use. Note that this is the base image for your app from the
Dockerfile
• sh -c "yarn install && yarn run dev" - the command. You're starting a shell using sh (alpine
doesn't have bash) and running yarn install to install packages and then running yarn run
dev to start the development server. If you look in the package.json, you'll see that
the dev script starts nodemon.
• You can watch the logs using docker logs <container-id>. You'll know you're ready to go
when you see this:

$ docker logs -f <container-id>

nodemon -L src/index.js

[nodemon] 2.0.20

[nodemon] to restart at any time, enter `rs`

[nodemon] watching path(s): *.*

[nodemon] watching extensions: js,mjs,json

[nodemon] starting `node src/index.js`

Using sqlite database at /etc/todos/todo.db

Listening on port 3000

When you're done watching the logs, exit out by hitting Ctrl+C.

5.3.2 Develop your app with the development container

• Update your app on your host machine and see the changes reflected in the container.

In the src/static/js/app.js file, on line 109, change the "Add Item" button to simply say "Add":

- {submitting ? 'Adding...' : 'Add Item'}

+ {submitting ? 'Adding...' : 'Add'}

Save the file.


• Refresh the page in your web browser, and you should see the change reflected almost
immediately because of the bind mount. Nodemon detects the change and restarts the
server. It might take a few seconds for the Node server to restart. If you get an error, try
refreshing after a few seconds.

Feel free to make any other changes you'd like to make. Each time you make a change and save a file,
the change is reflected in the container because of the bind mount. When Nodemon detects a
change, it restarts the app inside the container automatically. When you're done, stop the container
and build your new image using:

$ docker build -t getting-started .

5.4 Summary
At this point, you can persist your database and see changes in your app as you develop without
rebuilding the image.

In addition to volume mounts and bind mounts, Docker also supports other mount types and storage
drivers for handling more complex and specialized use cases.

You might also like