mirror of https://github.com/docker/docs.git
feedback: improve getting started documentation on bind mounts
Signed-off-by: David Karlsson <david.karlsson@docker.com>
This commit is contained in:
parent
756d3c1e40
commit
310dfbe856
|
@ -31,7 +31,7 @@ What you'll see is that the files created in one container aren't available in a
|
|||
|
||||
2. Validate that you can see the output by accessing the terminal in the container. To do so, go to **Containers** in Docker Desktop, hover over the container running the **ubuntu** image, and select the **Show container actions** menu. From the dropdown 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.
|
||||
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.
|
||||
|
||||
```console
|
||||
$ cat /data.txt
|
||||
|
@ -69,11 +69,11 @@ the container back to the host machine. If a directory in the container is mount
|
|||
directory are also seen on the host machine. If we mount that same directory across container restarts, we'd see
|
||||
the same files.
|
||||
|
||||
There are two main types of volumes. We will eventually use both, but we will start with **named volumes**.
|
||||
There are two main types of volumes. We will eventually use both, but we will start with volume mounts.
|
||||
|
||||
## Persist the todo data
|
||||
|
||||
By default, the todo app stores its data in a [SQLite Database](https://www.sqlite.org/index.html){:target="_blank" rel="noopener" class="_"} at
|
||||
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 in
|
||||
which all of the data is stored in a single file. While this isn't the best for large-scale applications,
|
||||
it works for small demos. We'll talk about switching this to a different database engine later.
|
||||
|
@ -83,9 +83,9 @@ next container, it should be able to pick up where the last one left off. By cre
|
|||
(often called "mounting") it to the directory the data is stored in, we can persist the data. As our container
|
||||
writes to the `todo.db` file, it will be persisted to the host in the volume.
|
||||
|
||||
As mentioned, we are going to use a **named volume**. Think of a named volume as simply a bucket of data.
|
||||
Docker maintains the physical location on the disk and you only need to remember the name of the volume.
|
||||
Every time you use the volume, Docker will make sure the correct data is provided.
|
||||
As mentioned, we are going to use a volume mount. Think of a volume mount as an opaque bucket of data.
|
||||
Docker fully manages the volume, including where it is stored on disk. You only need to remember the
|
||||
name of the volume.
|
||||
|
||||
1. Create a volume by using the `docker volume create` command.
|
||||
|
||||
|
@ -95,11 +95,11 @@ Every time you use the volume, Docker will make sure the correct data is provide
|
|||
|
||||
2. Stop and remove the todo app container once again in the Dashboard (or with `docker rm -f <id>`), as it is still running without using the persistent volume.
|
||||
|
||||
3. Start the todo app container, but add the `-v` flag to specify a volume mount. We will use the named volume and mount
|
||||
it to `/etc/todos`, which will capture all files created at the path.
|
||||
3. Start the todo app container, but add the `--mount` option to specify a volume mount. We will give the volume a name, and mount
|
||||
it to `/etc/todos` in the container, which will capture all files created at the path.
|
||||
|
||||
```console
|
||||
$ docker run -dp 3000:3000 -v todo-db:/etc/todos getting-started
|
||||
$ docker run -dp 3000:3000 --mount type=volume,src=todo-db,target=/etc/todos getting-started
|
||||
```
|
||||
|
||||
4. Once the container starts up, open the app and add a few items to your todo list.
|
||||
|
@ -117,17 +117,9 @@ Every time you use the volume, Docker will make sure the correct data is provide
|
|||
|
||||
Hooray! You've now learned how to persist data!
|
||||
|
||||
>**Note**
|
||||
>
|
||||
>While named volumes and bind mounts (which we'll talk about in a minute) are the two main types of volumes supported
|
||||
>by a default Docker engine installation, there are many volume driver plugins available to support NFS, SFTP, NetApp,
|
||||
>and more! This will be especially important once you start running containers on multiple hosts in a clustered
|
||||
>environment with Swarm, Kubernetes, etc.
|
||||
>
|
||||
|
||||
## Dive into the volume
|
||||
|
||||
A lot of people frequently ask "Where is Docker _actually_ storing my data when I use a named volume?" If you want to know,
|
||||
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.
|
||||
|
||||
```console
|
||||
|
@ -148,11 +140,11 @@ $ docker volume inspect todo-db
|
|||
The `Mountpoint` is the actual location on the disk where the data is stored. Note that on most machines, you will
|
||||
need to have root access to access this directory from the host. But, that's where it is!
|
||||
|
||||
>**Accessing volume data directly on Docker Desktop**
|
||||
>
|
||||
>While running in Docker Desktop, the Docker commands are actually running inside a small VM on your machine.
|
||||
>If you wanted to look at the actual contents of the Mountpoint directory, you would need to first get inside
|
||||
>of the VM.
|
||||
> **Accessing volume data directly on Docker Desktop**
|
||||
>
|
||||
> While running in Docker Desktop, the Docker commands are actually running inside a small VM on your machine.
|
||||
> If you wanted to look at the actual contents of the Mount point directory, you would need to look inside of
|
||||
> that VM.
|
||||
|
||||
## Next steps
|
||||
|
||||
|
@ -161,4 +153,4 @@ At this point, you have a functioning application that can survive restarts! You
|
|||
However, you saw earlier that rebuilding images for every change takes quite a bit of time. There's got to be a better
|
||||
way to make changes, right? With bind mounts (which was hinted at earlier), there is a better way!
|
||||
|
||||
[Use bind mounts](06_bind_mounts.md){: .button .primary-btn}
|
||||
[Use bind mounts](06_bind_mounts.md){: .button .primary-btn}
|
||||
|
|
|
@ -1,121 +1,226 @@
|
|||
---
|
||||
title: "Use bind mounts"
|
||||
keywords: get started, setup, orientation, quickstart, intro, concepts, containers, docker desktop
|
||||
keywords: >
|
||||
get started, setup, orientation, quickstart, intro, concepts, containers,
|
||||
docker desktop
|
||||
description: Using bind mounts in our application
|
||||
---
|
||||
|
||||
In the previous chapter, we talked about and used a **named volume** to persist the data in our database.
|
||||
Named volumes are great if we simply want to store data, as we don't have to worry about _where_ the data
|
||||
is stored.
|
||||
In the previous chapter, we talked about and used a volume mount to persist the
|
||||
data in our database. A volume mount is a great choice when you need somewhere
|
||||
persistent to store your application data.
|
||||
|
||||
With **bind mounts**, we control the exact mountpoint on the host. We can use this to persist data, but it's often
|
||||
used to provide additional data into containers. When working on an application, we can use a bind mount to
|
||||
mount our source code into the container to let it see code changes, respond, and let us see the changes right
|
||||
away.
|
||||
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.
|
||||
|
||||
For Node-based applications, [nodemon](https://npmjs.com/package/nodemon){:target="_blank" rel="noopener" class="_"} is a great tool to watch for file
|
||||
changes and then restart the application. There are equivalent tools in most other languages and frameworks.
|
||||
In this chapter, we'll see how we can use bind mounts and a tool called
|
||||
[nodemon](https://npmjs.com/package/nodemon){:target="_blank" rel="noopener"
|
||||
class="_"} to watch for file changes, and then restart the application
|
||||
automatically. There are equivalent tools in most other languages and
|
||||
frameworks.
|
||||
|
||||
## Quick volume type comparisons
|
||||
|
||||
Bind mounts and named volumes are the two main types of volumes that come with the Docker engine. However, additional
|
||||
volume drivers are available to support other use cases ([SFTP](https://github.com/vieux/docker-volume-sshfs){:target="_blank" rel="noopener" class="_"}, [Ceph](https://ceph.com/geen-categorie/getting-started-with-the-docker-rbd-volume-plugin/){:target="_blank" rel="noopener" class="_"}, [NetApp](https://netappdvp.readthedocs.io/en/stable/){:target="_blank" rel="noopener" class="_"}, [S3](https://github.com/elementar/docker-s3-volume){:target="_blank" rel="noopener" class="_"}, and more).
|
||||
The following table outlines the main differences between volume mounts and bind
|
||||
mounts.
|
||||
|
||||
| | Named Volumes | Bind Mounts |
|
||||
| - | ------------- | ----------- |
|
||||
| Host Location | Docker chooses | You control |
|
||||
| Mount Example (using `-v`) | my-volume:/usr/local/data | /path/to/data:/usr/local/data |
|
||||
| Populates new volume with container contents | Yes | No |
|
||||
| Supports Volume Drivers | Yes | No |
|
||||
| | Named volumes | Bind mounts |
|
||||
| -------------------------------------------- | -------------------------------------------------- | ---------------------------------------------------- |
|
||||
| Host location | Docker chooses | You decide |
|
||||
| Mount example (using `--mount`) | `type=volume,src=my-volume,target=/usr/local/data` | `type=bind,src=/path/to/data,target=/usr/local/data` |
|
||||
| Populates new volume with container contents | Yes | No |
|
||||
| Supports Volume Drivers | Yes | No |
|
||||
|
||||
## Start a dev-mode container
|
||||
## Trying out bind mounts
|
||||
|
||||
To run our container to support a development workflow, we will do the following:
|
||||
Before looking at how we can use bind mounts for developing our application,
|
||||
let's run a quick experiment to get a practical understanding of how bind mounts
|
||||
work.
|
||||
|
||||
If you're following these steps on Windows, make sure to use PowerShell and not
|
||||
command prompt (`cmd`).
|
||||
|
||||
1. Open a terminal and make sure your current working directory is in the `app`
|
||||
directory of the getting started repository.
|
||||
|
||||
2. Run the following command to start `bash` in an `ubuntu` container with a
|
||||
bind mount.
|
||||
|
||||
```console
|
||||
$ 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`).
|
||||
|
||||
3. After running the command, Docker starts an interactive `bash` session in the
|
||||
root directory of the container's filesystem.
|
||||
|
||||
```console
|
||||
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
|
||||
```
|
||||
|
||||
4. Now, change directory in 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.
|
||||
|
||||
```console
|
||||
root@ac1237fad8db:/# cd src
|
||||
root@ac1237fad8db:/src# ls
|
||||
Dockerfile node_modules package.json spec src yarn.lock
|
||||
```
|
||||
|
||||
5. Create a new file named `myfile.txt`.
|
||||
|
||||
```console
|
||||
root@ac1237fad8db:/src# touch myfile.txt
|
||||
root@ac1237fad8db:/src# ls
|
||||
Dockerfile myfile.txt node_modules package.json spec src yarn.lock
|
||||
```
|
||||
|
||||
6. Now if you open this directory on the host, you'll see the `myfile.txt` file
|
||||
has been created in the directory.
|
||||
|
||||

|
||||
|
||||
7. From the host, delete the `myfile.txt` file.
|
||||
8. In the container, list the contents of the `app` directory once more. You'll
|
||||
see that the file is now gone.
|
||||
|
||||
```console
|
||||
root@ac1237fad8db:/src# ls
|
||||
Dockerfile node_modules package.json spec src yarn.lock
|
||||
```
|
||||
|
||||
9. Stop the interactive container session with `Ctrl` + `D`.
|
||||
|
||||
And 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 let's see how we can use
|
||||
bind mounts to develop software.
|
||||
|
||||
## 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 our source code into the container
|
||||
- Install all dependencies, including the "dev" dependencies
|
||||
- Start nodemon to watch for filesystem changes
|
||||
- Install all dependencies
|
||||
- Start `nodemon` to watch for filesystem changes
|
||||
|
||||
So, let's do it!
|
||||
|
||||
1. Make sure you don't have any previous `getting-started` containers running.
|
||||
1. Make sure you don't have any `getting-started` containers currently running.
|
||||
|
||||
2. Run the following command from the app directory. We'll explain what's going on afterwards.
|
||||
2. Run the following command from the `getting-started/app` directory.
|
||||
|
||||
If you are using an x86-64 Mac or Linux device, then use the following command.
|
||||
If you are using an Mac or Linux device, then use the following command.
|
||||
|
||||
```console
|
||||
$ docker run -dp 3000:3000 \
|
||||
-w /app -v "$(pwd):/app" \
|
||||
node:18-alpine \
|
||||
sh -c "yarn install && yarn run dev"
|
||||
```
|
||||
```console
|
||||
$ docker run -dp 3000:3000 \
|
||||
-w /app --mount type=bind,src="$(pwd)",target=/app \
|
||||
node:18-alpine \
|
||||
sh -c "yarn install && yarn run dev"
|
||||
```
|
||||
|
||||
If you are using Windows, then use the following command in PowerShell.
|
||||
If you are using Windows, then use the following command in PowerShell.
|
||||
|
||||
```powershell
|
||||
$ docker run -dp 3000:3000 `
|
||||
-w /app -v "$(pwd):/app" `
|
||||
node:18-alpine `
|
||||
sh -c "yarn install && yarn run dev"
|
||||
```
|
||||
```powershell
|
||||
$ docker run -dp 3000:3000 `
|
||||
-w /app --mount type=bind,src="$(pwd)",target=/app `
|
||||
node:18-alpine `
|
||||
sh -c "yarn install && yarn run dev"
|
||||
```
|
||||
|
||||
- `-dp 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
|
||||
- `-v "$(pwd):/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 our app from the Dockerfile
|
||||
- `sh -c "yarn install && yarn run dev"` - the command. We're starting a shell using `sh` (alpine doesn't have `bash`) and
|
||||
running `yarn install` to install _all_ dependencies and then running `yarn run dev`. If we look in the `package.json`,
|
||||
we'll see that the `dev` script is starting `nodemon`.
|
||||
- `-dp 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
|
||||
our app from the Dockerfile
|
||||
- `sh -c "yarn install && yarn run dev"` - the command. We'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 we look in the `package.json`, we'll see that the `dev` script
|
||||
starts `nodemon`.
|
||||
|
||||
3. You can watch the logs using `docker logs`. You'll know you're ready to go when you see this:
|
||||
3. You can watch the logs using `docker logs`. You'll know you're ready to go
|
||||
when you see this:
|
||||
|
||||
```console
|
||||
$ docker logs -f <container-id>
|
||||
nodemon src/index.js
|
||||
[nodemon] 2.0.20
|
||||
[nodemon] to restart at any time, enter `rs`
|
||||
[nodemon] watching dir(s): *.*
|
||||
[nodemon] starting `node src/index.js`
|
||||
Using sqlite database at /etc/todos/todo.db
|
||||
Listening on port 3000
|
||||
```
|
||||
```console
|
||||
$ docker logs -f <container-id>
|
||||
nodemon src/index.js
|
||||
[nodemon] 2.0.20
|
||||
[nodemon] to restart at any time, enter `rs`
|
||||
[nodemon] watching dir(s): *.*
|
||||
[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`.
|
||||
When you're done watching the logs, exit out by hitting `Ctrl`+`C`.
|
||||
|
||||
4. Now, let's make a change to the app. In the `src/static/js/app.js` file, let's change the "Add Item" button to simply say
|
||||
"Add". This change will be on line 109:
|
||||
4. Now, make a change to the app. In the `src/static/js/app.js` file, on line
|
||||
109, change the "Add Item" button to simply say "Add":
|
||||
|
||||
```diff
|
||||
- {submitting ? 'Adding...' : 'Add Item'}
|
||||
+ {submitting ? 'Adding...' : 'Add'}
|
||||
```
|
||||
```diff
|
||||
- {submitting ? 'Adding...' : 'Add Item'}
|
||||
+ {submitting ? 'Adding...' : 'Add'}
|
||||
```
|
||||
|
||||
5. Simply refresh the page (or open it) and you should see the change reflected in the browser almost immediately. It might
|
||||
take a few seconds for the Node server to restart, so if you get an error, just try refreshing after a few seconds.
|
||||
Save the file.
|
||||
|
||||
{: style="width:75%;"}
|
||||
{: .text-center }
|
||||
5. Refresh the page in your web browser, and you should see the change reflected
|
||||
almost immediately. It might take a few seconds for the Node server to
|
||||
restart. If you get an error, try refreshing after a few seconds.
|
||||
|
||||
6. Feel free to make any other changes you'd like to make. When you're done, stop the container and build your new image
|
||||
using:
|
||||
{:
|
||||
style="width:75%;" .text-center}
|
||||
|
||||
```console
|
||||
$ docker build -t getting-started .
|
||||
```
|
||||
6. Feel free to make any other changes you'd like to make. Each time you make a
|
||||
change and save a file, the `nodemon` process restarts the app inside the
|
||||
container automatically. When you're done, stop the container and build your
|
||||
new image using:
|
||||
|
||||
Using bind mounts is _very_ common for local development setups. The advantage is that the dev machine doesn't need to have
|
||||
all of the build tools and environments installed. With a single `docker run` command, the dev environment is pulled and ready
|
||||
to go. We'll talk about Docker Compose in a future step, as this will help simplify our commands (we're already getting a lot
|
||||
of flags).
|
||||
```console
|
||||
$ docker build -t getting-started .
|
||||
```
|
||||
|
||||
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, dependencies and
|
||||
tools are pulled and ready to go. We'll talk about Docker Compose in a future
|
||||
step, as this will help simplify our commands (we're already getting a lot of
|
||||
flags).
|
||||
|
||||
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.
|
||||
To learn more about the advanced storage concepts, see
|
||||
[Manage data in Docker](https://docs.docker.com/storage/).
|
||||
|
||||
## Next steps
|
||||
|
||||
At this point, you can persist your database and respond rapidly to the needs and demands of your investors and founders. Hooray!
|
||||
But, guess what? You received great news! Your project has been selected for future development!
|
||||
At this point, you can persist your database and respond rapidly to the needs
|
||||
and demands of your investors and founders. Hooray! But, guess what? You
|
||||
received great news! Your project has been selected for future development!
|
||||
|
||||
In order to prepare for production, you need to migrate your database from working in SQLite to something that can scale a
|
||||
little better. For simplicity, you'll keep with a relational database and switch your application to use MySQL. But, how
|
||||
should you run MySQL? How do you allow the containers to talk to each other? You'll learn about that next!
|
||||
In order to prepare for production, you need to migrate your database from
|
||||
working in SQLite to something that can scale a little better. For simplicity,
|
||||
you'll keep with a relational database and switch your application to use MySQL.
|
||||
But, how should you run MySQL? How do you allow the containers to talk to each
|
||||
other? You'll learn about that next!
|
||||
|
||||
[Multi container apps](07_multi_container.md){: .button .primary-btn}
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 31 KiB |
Loading…
Reference in New Issue