mirror of
https://github.com/docker/docs.git
synced 2026-11-04 10:01:00 +00:00
Merge pull request #25963 from usha-mandya/Engdocs-3371
Remove the outdated docker workshop section
This commit is contained in:
28 files changed
+48
-2612
No files matched your search
@@ -24,10 +24,6 @@ params:
|
||||
description: Gain a better understanding of foundational Docker concepts.
|
||||
link: /get-started/docker-concepts/the-basics/what-is-a-container/
|
||||
icon: home
|
||||
- title: Docker workshop
|
||||
description: Get guided through a 45-minute workshop to learn about Docker.
|
||||
link: /get-started/workshop/
|
||||
icon: computer-desktop
|
||||
aliases:
|
||||
- /engine/get-started/
|
||||
- /engine/tutorials/usingdocker/
|
||||
|
||||
@@ -17,6 +17,41 @@ params:
|
||||
prerequisites: None
|
||||
aliases:
|
||||
- /guides/getting-started/
|
||||
- /guides/get-started/
|
||||
- /get-started/hands-on-overview/
|
||||
- /guides/workshop/
|
||||
- /get-started/workshop/
|
||||
- /get-started/part2/
|
||||
- /get-started/02_our_app/
|
||||
- /guides/workshop/02_our_app/
|
||||
- /guides/walkthroughs/containerize-your-app/
|
||||
- /get-started/workshop/02_our_app/
|
||||
- /get-started/03_updating_app/
|
||||
- /guides/workshop/03_updating_app/
|
||||
- /get-started/workshop/03_updating_app/
|
||||
- /get-started/part3/
|
||||
- /get-started/04_sharing_app/
|
||||
- /guides/workshop/04_sharing_app/
|
||||
- /get-started/workshop/04_sharing_app/
|
||||
- /get-started/05_persisting_data/
|
||||
- /guides/workshop/05_persisting_data/
|
||||
- /get-started/workshop/05_persisting_data/
|
||||
- /guides/walkthroughs/access-local-folder/
|
||||
- /get-started/06_bind_mounts/
|
||||
- /guides/workshop/06_bind_mounts/
|
||||
- /get-started/workshop/06_bind_mounts/
|
||||
- /get-started/07_multi_container/
|
||||
- /guides/workshop/07_multi_container/
|
||||
- /get-started/workshop/07_multi_container/
|
||||
- /get-started/08_using_compose/
|
||||
- /guides/workshop/08_using_compose/
|
||||
- /get-started/workshop/08_using_compose/
|
||||
- /get-started/09_image_best/
|
||||
- /guides/workshop/09_image_best/
|
||||
- /get-started/workshop/09_image_best/
|
||||
- /get-started/11_what_next/
|
||||
- /guides/workshop/10_what_next/
|
||||
- /get-started/workshop/10_what_next/
|
||||
---
|
||||
|
||||
## About this series
|
||||
|
||||
@@ -1,170 +0,0 @@
|
||||
---
|
||||
title: Containerize an application
|
||||
weight: 20
|
||||
linkTitle: "Part 1: Containerize an application"
|
||||
keywords: |
|
||||
dockerfile example, Containerize an application, run docker file, running
|
||||
docker file, how to run dockerfile, example dockerfile, how to create a docker container,
|
||||
create dockerfile, simple dockerfile, creating containers
|
||||
description: |
|
||||
Follow this step-by-step guide to learn how to create and run a containerized
|
||||
application using Docker
|
||||
aliases:
|
||||
- /get-started/part2/
|
||||
- /get-started/02_our_app/
|
||||
- /guides/workshop/02_our_app/
|
||||
- /guides/walkthroughs/containerize-your-app/
|
||||
---
|
||||
|
||||
For the rest of this guide, you'll be working with a simple todo
|
||||
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.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- You have installed the latest version of [Docker Desktop](/get-started/get-docker.md).
|
||||
- You have installed a [Git client](https://git-scm.com/downloads).
|
||||
- You have an IDE or a text editor to edit files. Docker recommends using [Visual Studio Code](https://code.visualstudio.com/).
|
||||
|
||||
## Get the app
|
||||
|
||||
Before you can run the application, you need to get the application source code onto your machine.
|
||||
|
||||
1. Clone the [getting-started-app repository](https://github.com/docker/getting-started-app/tree/main) using the following command:
|
||||
|
||||
```console
|
||||
$ git clone https://github.com/docker/getting-started-app.git
|
||||
```
|
||||
|
||||
2. View the contents of the cloned repository. You should see the following files and sub-directories.
|
||||
|
||||
```text
|
||||
├── getting-started-app/
|
||||
│ ├── .dockerignore
|
||||
│ ├── package.json
|
||||
│ ├── package-lock.json
|
||||
│ ├── README.md
|
||||
│ ├── spec/
|
||||
│ ├── src/
|
||||
```
|
||||
|
||||
## 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.
|
||||
|
||||
1. In the `getting-started-app` directory, the same location as the
|
||||
`package.json` file, create a file named `Dockerfile` with the following contents:
|
||||
|
||||
```dockerfile
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
FROM node:24-alpine
|
||||
WORKDIR /app
|
||||
COPY . .
|
||||
RUN npm install --omit=dev
|
||||
CMD ["node", "src/index.js"]
|
||||
EXPOSE 3000
|
||||
```
|
||||
This Dockerfile does the following:
|
||||
|
||||
- Uses `node:24-alpine` as the base image, a lightweight Linux image with Node.js pre-installed
|
||||
- Sets `/app` as the working directory
|
||||
- Copies source code into the image
|
||||
- Installs the necessary dependencies
|
||||
- Specifies the command to start the application
|
||||
- Documents that the app listens on port 3000
|
||||
|
||||
2. 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.
|
||||
|
||||
```console
|
||||
$ cd /path/to/getting-started-app
|
||||
```
|
||||
|
||||
Build the image.
|
||||
|
||||
```console
|
||||
$ docker build -t getting-started .
|
||||
```
|
||||
|
||||
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:24-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 `npm` to install your application's dependencies.
|
||||
|
||||
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.
|
||||
|
||||
## Start an app container
|
||||
|
||||
Now that you have an image, you can run the application in a container using the `docker run` command.
|
||||
|
||||
1. Run your container using the `docker run` command and specify the name of the image you just created:
|
||||
|
||||
```console
|
||||
$ docker run -d -p 127.0.0.1:3000:3000 getting-started
|
||||
```
|
||||
|
||||
The `-d` flag (short for `--detach`) runs the container in the background.
|
||||
This means that Docker starts your container and returns you to the terminal
|
||||
prompt. Also, it does not display logs in the terminal.
|
||||
|
||||
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.
|
||||
|
||||
2. After a few seconds, open your web browser to [http://localhost:3000](http://localhost:3000).
|
||||
You should see your app.
|
||||
|
||||

|
||||
|
||||
3. 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.
|
||||
|
||||
{{< tabs >}}
|
||||
{{< tab name="CLI" >}}
|
||||
|
||||
Run the `docker ps` command in a terminal to list your containers.
|
||||
|
||||
```console
|
||||
$ docker ps
|
||||
```
|
||||
|
||||
Output similar to the following should appear.
|
||||
|
||||
```console
|
||||
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
|
||||
df784548666d getting-started "docker-entrypoint.s…" 2 minutes ago Up 2 minutes 127.0.0.1:3000->3000/tcp priceless_mcclintock
|
||||
```
|
||||
|
||||
{{< /tab >}}
|
||||
{{< tab name="Docker Desktop" >}}
|
||||
|
||||
In Docker Desktop, select the **Containers** tab to see a list of your containers.
|
||||
|
||||

|
||||
|
||||
{{< /tab >}}
|
||||
{{< /tabs >}}
|
||||
|
||||
## 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.
|
||||
|
||||
Related information:
|
||||
|
||||
- [Dockerfile reference](/reference/dockerfile/)
|
||||
- [docker CLI reference](/reference/cli/docker/)
|
||||
|
||||
## Next steps
|
||||
|
||||
Next, you're going to make a modification to your app and learn how to update your running application with a new image. Along the way, you'll learn a few other useful commands.
|
||||
|
||||
{{< button text="Update the application" url="03_updating_app.md" >}}
|
||||
@@ -1,112 +0,0 @@
|
||||
---
|
||||
title: Update the application
|
||||
weight: 30
|
||||
linkTitle: "Part 2: Update the application"
|
||||
keywords: get started, setup, orientation, quickstart, intro, concepts, containers,
|
||||
docker desktop
|
||||
description: Making changes to your application
|
||||
aliases:
|
||||
- /get-started/03_updating_app/
|
||||
- /guides/workshop/03_updating_app/
|
||||
---
|
||||
|
||||
In [part 1](./02_our_app.md), 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.
|
||||
|
||||
## 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!"
|
||||
|
||||
|
||||
1. In the `src/static/js/app.js` file, update line 56 to use the new empty text.
|
||||
|
||||
```diff
|
||||
- <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>
|
||||
```
|
||||
|
||||
2. Build your updated version of the image, using the `docker build` command.
|
||||
|
||||
```console
|
||||
$ docker build -t getting-started .
|
||||
```
|
||||
|
||||
3. Start a new container using the updated code.
|
||||
|
||||
```console
|
||||
$ docker run -dp 127.0.0.1:3000:3000 getting-started
|
||||
```
|
||||
|
||||
You probably saw an error like this:
|
||||
|
||||
```console
|
||||
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.
|
||||
|
||||
## 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.
|
||||
|
||||
{{< tabs >}}
|
||||
{{< tab name="CLI" >}}
|
||||
|
||||
### Remove a container using the CLI
|
||||
|
||||
1. Get the ID of the container by using the `docker ps` command.
|
||||
|
||||
```console
|
||||
$ docker ps
|
||||
```
|
||||
|
||||
2. Use the `docker stop` command to stop the container. Replace `<the-container-id>` with the ID from `docker ps`.
|
||||
|
||||
```console
|
||||
$ docker stop <the-container-id>
|
||||
```
|
||||
|
||||
3. Once the container has stopped, you can remove it by using the `docker rm` command.
|
||||
|
||||
```console
|
||||
$ 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>`
|
||||
|
||||
{{< /tab >}}
|
||||
{{< tab name="Docker Desktop" >}}
|
||||
|
||||
### Remove a container using Docker Desktop
|
||||
|
||||
1. Open Docker Desktop to the **Containers** view.
|
||||
2. Select the trash can icon under the **Actions** column for the container that you want to delete.
|
||||
3. In the confirmation dialog, select **Delete forever**.
|
||||
|
||||
{{< /tab >}}
|
||||
{{< /tabs >}}
|
||||
|
||||
### Start the updated app container
|
||||
|
||||
1. Now, start your updated app using the `docker run` command.
|
||||
|
||||
```console
|
||||
$ docker run -dp 127.0.0.1:3000:3000 getting-started
|
||||
```
|
||||
|
||||
2. Refresh your browser on [http://localhost:3000](http://localhost:3000) and you should see your updated help text.
|
||||
|
||||
## Summary
|
||||
|
||||
In this section, you learned how to update and rebuild an image, as well as how to stop and remove a container.
|
||||
|
||||
Related information:
|
||||
- [docker CLI reference](/reference/cli/docker/)
|
||||
|
||||
## Next steps
|
||||
|
||||
Next, you'll learn how to share images with others.
|
||||
|
||||
{{< button text="Share the application" url="04_sharing_app.md" >}}
|
||||
@@ -1,104 +0,0 @@
|
||||
---
|
||||
title: Share the application
|
||||
weight: 40
|
||||
linkTitle: "Part 3: Share the application"
|
||||
keywords: get started, setup, orientation, quickstart, intro, concepts, containers,
|
||||
docker desktop, docker hub, sharing
|
||||
description: Sharing your image you built for your example application so you can
|
||||
run it else where and other developers can use it
|
||||
aliases:
|
||||
- /get-started/part3/
|
||||
- /get-started/04_sharing_app/
|
||||
- /guides/workshop/04_sharing_app/
|
||||
---
|
||||
|
||||
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](https://hub.docker.com/signup) for free if you don't have one.
|
||||
|
||||
## Create a repository
|
||||
|
||||
To push an image, you first need to create a repository on Docker Hub.
|
||||
|
||||
1. [Sign up](https://www.docker.com/pricing?ref=Docs&refAction=DocsSharingApp) or Sign in to [Docker Hub](https://hub.docker.com).
|
||||
|
||||
2. Select the **Create Repository** button.
|
||||
|
||||
3. For the repository name, use `getting-started`. Make sure the **Visibility** is **Public**.
|
||||
|
||||
4. Select **Create**.
|
||||
|
||||
In the following image, you can see an example Docker command from Docker Hub. This command will push to this repository.
|
||||
|
||||

|
||||
|
||||
|
||||
## Push the image
|
||||
|
||||
Let's try to push the image to Docker Hub.
|
||||
|
||||
1. In the command line, run the following command:
|
||||
|
||||
```console
|
||||
docker push docker/getting-started
|
||||
```
|
||||
|
||||
You'll see an error like this:
|
||||
|
||||
```console
|
||||
$ 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
|
||||
```
|
||||
|
||||
This failure is expected because the image isn't tagged correctly yet.
|
||||
Docker is looking for an image name `docker/getting-started`, but your
|
||||
local image is still named `getting-started`.
|
||||
|
||||
You can confirm this by running:
|
||||
|
||||
```console
|
||||
docker image ls
|
||||
```
|
||||
|
||||
2. To fix this, first sign in to Docker Hub using your Docker ID: `docker login -u YOUR-USER-NAME`.
|
||||
3. Use the `docker tag` command to give the `getting-started` image a new name. Replace `YOUR-USER-NAME` with your Docker ID.
|
||||
|
||||
```console
|
||||
$ docker tag getting-started YOUR-USER-NAME/getting-started
|
||||
```
|
||||
|
||||
4. 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`.
|
||||
|
||||
```console
|
||||
$ docker push YOUR-USER-NAME/getting-started
|
||||
```
|
||||
|
||||
## Run the image on a new instance
|
||||
|
||||
Now that your image has been built and pushed into a registry, you can run your app on any machine that has Docker installed. Try pulling and running your image on another computer or a cloud instance.
|
||||
|
||||
## 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.
|
||||
|
||||
Related information:
|
||||
|
||||
- [docker CLI reference](/reference/cli/docker/)
|
||||
- [Multi-platform images](/manuals/build/building/multi-platform.md)
|
||||
- [Docker Hub overview](/manuals/docker-hub/_index.md)
|
||||
|
||||
## Next steps
|
||||
|
||||
In the next section, you'll learn how to persist data in your containerized application.
|
||||
|
||||
{{< button text="Persist the DB" url="05_persisting_data.md" >}}
|
||||
@@ -1,210 +0,0 @@
|
||||
---
|
||||
title: Persist the DB
|
||||
weight: 50
|
||||
linkTitle: "Part 4: Persist the DB"
|
||||
keywords: get started, setup, orientation, quickstart, intro, concepts, containers,
|
||||
docker desktop
|
||||
description: Making your DB persistent in your application
|
||||
aliases:
|
||||
- /get-started/05_persisting_data/
|
||||
- /guides/workshop/05_persisting_data/
|
||||
---
|
||||
|
||||
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.
|
||||
|
||||
## 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.
|
||||
|
||||
### See this in practice
|
||||
|
||||
To see this in action, you're going to start two containers. In one container,
|
||||
you'll create a file. In the other container, you'll check whether that same
|
||||
file exists.
|
||||
|
||||
1. Start an Alpine container and create a new file in it.
|
||||
|
||||
```console
|
||||
$ docker run --rm alpine touch greeting.txt
|
||||
```
|
||||
|
||||
> [!TIP]
|
||||
> Any commands you specify after the image name (in this case, `alpine`)
|
||||
> are executed inside the container. In this case, the command `touch
|
||||
> greeting.txt` puts a file named `greeting.txt` on the container's filesystem.
|
||||
|
||||
2. Run a new Alpine container and use the `stat` command to check whether the file exists.
|
||||
|
||||
```console
|
||||
$ docker run --rm alpine stat greeting.txt
|
||||
```
|
||||
|
||||
You should see output similar to the following that indicates the file does not exist in the new container.
|
||||
|
||||
```console
|
||||
stat: can't stat 'greeting.txt': No such file or directory
|
||||
```
|
||||
|
||||
The `greeting.txt` file created by the first container did not exist in the
|
||||
second container. That is because the writeable "top layer" of each container
|
||||
is isolated. Even though both containers shared the same underlying layers that
|
||||
make up the base image, the writable layer is unique to each container.
|
||||
|
||||
## 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](/manuals/engine/storage/volumes.md) 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.
|
||||
|
||||
### 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.
|
||||
|
||||
{{< tabs >}}
|
||||
{{< tab name="CLI" >}}
|
||||
|
||||
1. Create a volume by using the `docker volume create` command.
|
||||
|
||||
```console
|
||||
$ docker volume create todo-db
|
||||
```
|
||||
|
||||
2. Stop and remove the todo app container once again with `docker rm -f <id>`,
|
||||
as it is still running without using the persistent volume.
|
||||
|
||||
3. Start the todo app container, but add the `--mount` option to specify a
|
||||
volume mount. Use the volume named `todo-db`, and mount it to `/etc/todos` in the
|
||||
container, which captures all files created at the path.
|
||||
|
||||
```console
|
||||
$ docker run -dp 127.0.0.1:3000:3000 --mount type=volume,src=todo-db,target=/etc/todos getting-started
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
>
|
||||
> If you're using Git Bash, you must use different syntax for this command.
|
||||
>
|
||||
> ```console
|
||||
> $ docker run -dp 127.0.0.1:3000:3000 --mount type=volume,src=todo-db,target=//etc/todos getting-started
|
||||
> ```
|
||||
>
|
||||
> For more details about Git Bash's syntax differences, see
|
||||
> [Working with Git Bash](/desktop/troubleshoot-and-support/troubleshoot/topics/#docker-commands-failing-in-git-bash).
|
||||
|
||||
|
||||
{{< /tab >}}
|
||||
{{< tab name="Docker Desktop" >}}
|
||||
|
||||
To create a volume:
|
||||
|
||||
1. Select **Volumes** in Docker Desktop.
|
||||
2. In **Volumes**, select **Create**.
|
||||
3. Specify `todo-db` as the volume name, and then select **Create**.
|
||||
|
||||
To stop and remove the app container:
|
||||
|
||||
1. Select **Containers** in Docker Desktop.
|
||||
2. Select **Delete** in the **Actions** column for the container.
|
||||
|
||||
To start the todo app container with the volume mounted:
|
||||
|
||||
1. Select the search box at the top of Docker Desktop.
|
||||
2. In the search window, select the **Images** tab.
|
||||
3. In the search box, specify the image name, `getting-started`.
|
||||
|
||||
> [!TIP]
|
||||
>
|
||||
> Use the search filter to filter images and only show **Local images**.
|
||||
|
||||
4. Select your image and then select **Run**.
|
||||
5. Select **Optional settings**.
|
||||
6. In **Host port**, specify the port, for example, `3000`.
|
||||
7. In **Host path**, specify the name of the volume, `todo-db`.
|
||||
8. In **Container path**, specify `/etc/todos`.
|
||||
9. Select **Run**.
|
||||
|
||||
{{< /tab >}}
|
||||
{{< /tabs >}}
|
||||
|
||||
### Verify that the data persists
|
||||
|
||||
1. Once the container starts up, open the app and add a few items to your todo list.
|
||||
|
||||

|
||||
|
||||
|
||||
2. 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.
|
||||
|
||||
3. Start a new container using the previous steps.
|
||||
|
||||
4. Open the app. You should see your items still in your list.
|
||||
|
||||
5. Go ahead and remove the container when you're done checking out your list.
|
||||
|
||||
You've now learned how to persist data.
|
||||
|
||||
## 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.
|
||||
|
||||
```console
|
||||
$ docker volume inspect todo-db
|
||||
```
|
||||
You should see output like the following:
|
||||
```console
|
||||
[
|
||||
{
|
||||
"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.
|
||||
|
||||
## Summary
|
||||
|
||||
In this section, you learned how to persist container data.
|
||||
|
||||
Related information:
|
||||
|
||||
- [docker CLI reference](/reference/cli/docker/)
|
||||
- [Volumes](/manuals/engine/storage/volumes.md)
|
||||
|
||||
## Next steps
|
||||
|
||||
Next, you'll learn how you can develop your app more efficiently using bind mounts.
|
||||
|
||||
{{< button text="Use bind mounts" url="06_bind_mounts.md" >}}
|
||||
@@ -1,451 +0,0 @@
|
||||
---
|
||||
title: Use bind mounts
|
||||
weight: 60
|
||||
linkTitle: "Part 5: Use bind mounts"
|
||||
keywords: 'get started, setup, orientation, quickstart, intro, concepts, containers, docker desktop'
|
||||
description: Using bind mounts in our application
|
||||
aliases:
|
||||
- /guides/walkthroughs/access-local-folder/
|
||||
- /get-started/06_bind_mounts/
|
||||
- /guides/workshop/06_bind_mounts/
|
||||
---
|
||||
|
||||
In [part 4](./05_persisting_data.md), 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](https://npmjs.com/package/nodemon) 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
|
||||
|
||||
The following are examples of a named volume and a bind mount using `--mount`:
|
||||
|
||||
- Named volume: `type=volume,src=my-volume,target=/usr/local/data`
|
||||
- Bind mount: `type=bind,src=/path/to/data,target=/usr/local/data`
|
||||
|
||||
The following table outlines the main differences between volume mounts and bind
|
||||
mounts.
|
||||
|
||||
| | Named volumes | Bind mounts |
|
||||
| -------------------------------------------- | -------------------------------------------------- | ---------------------------------------------------- |
|
||||
| Host location | Docker chooses | You decide |
|
||||
| Populates new volume with container contents | Yes | No |
|
||||
| Supports Volume Drivers | Yes | No |
|
||||
|
||||
## 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.
|
||||
|
||||
1. 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 [File sharing](/manuals/desktop/settings-and-maintenance/settings.md#file-sharing).
|
||||
|
||||
> [!NOTE]
|
||||
> The **File sharing** tab is only available in Hyper-V mode, because the files are automatically shared in WSL 2 mode and Windows container mode.
|
||||
|
||||
2. Open a terminal and change directory to the `getting-started-app`
|
||||
directory.
|
||||
|
||||
3. Run the following command to start `bash` in an `ubuntu` container with a
|
||||
bind mount.
|
||||
|
||||
{{< tabs >}}
|
||||
{{< tab name="Mac / Linux" >}}
|
||||
|
||||
```console
|
||||
$ docker run -it --mount type=bind,src=.,target=/src ubuntu bash
|
||||
```
|
||||
|
||||
{{< /tab >}}
|
||||
{{< tab name="Command Prompt" >}}
|
||||
|
||||
```console
|
||||
$ docker run -it --mount "type=bind,src=%cd%,target=/src" ubuntu bash
|
||||
```
|
||||
|
||||
{{< /tab >}}
|
||||
{{< tab name="Git Bash" >}}
|
||||
|
||||
```console
|
||||
$ docker run -it --mount type=bind,src="./",target=/src ubuntu bash
|
||||
```
|
||||
|
||||
{{< /tab >}}
|
||||
{{< tab name="PowerShell" >}}
|
||||
|
||||
```console
|
||||
$ docker run -it --mount "type=bind,src=.,target=/src" ubuntu bash
|
||||
```
|
||||
|
||||
{{< /tab >}}
|
||||
{{< /tabs >}}
|
||||
|
||||
The `--mount type=bind` 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`).
|
||||
|
||||
4. 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
|
||||
```
|
||||
|
||||
5. 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.
|
||||
|
||||
```console
|
||||
root@ac1237fad8db:/# cd src
|
||||
root@ac1237fad8db:/src# ls
|
||||
Dockerfile node_modules package.json package-lock.json spec src
|
||||
```
|
||||
|
||||
6. 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 package-lock.json spec src
|
||||
```
|
||||
|
||||
7. Open the `getting-started-app` directory on the host and observe that the
|
||||
`myfile.txt` file is in the directory.
|
||||
|
||||
```text
|
||||
├── getting-started-app/
|
||||
│ ├── Dockerfile
|
||||
│ ├── myfile.txt
|
||||
│ ├── node_modules/
|
||||
│ ├── package.json
|
||||
│ ├── package-lock.json
|
||||
│ ├── spec/
|
||||
│ └── src/
|
||||
```
|
||||
|
||||
8. From the host, delete the `myfile.txt` file.
|
||||
9. In the container, list the contents of the `app` directory once more. Observe that the file is now gone.
|
||||
|
||||
```console
|
||||
root@ac1237fad8db:/src# ls
|
||||
Dockerfile node_modules package.json package-lock.json spec src
|
||||
```
|
||||
|
||||
10. 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.
|
||||
|
||||
## 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.
|
||||
|
||||
### 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.
|
||||
|
||||
{{< tabs >}}
|
||||
{{< tab name="Mac / Linux CLI" >}}
|
||||
|
||||
1. Make sure you don't have any `getting-started` containers currently running.
|
||||
|
||||
2. Run the following command from the `getting-started-app` directory.
|
||||
|
||||
```console
|
||||
$ docker run -dp 127.0.0.1:3000:3000 \
|
||||
-w /app --mount type=bind,src=.,target=/app \
|
||||
node:24-alpine \
|
||||
sh -c "npm install && npm run dev"
|
||||
```
|
||||
|
||||
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=.,target=/app` - bind mount the current
|
||||
directory from the host into the `/app` directory in the container
|
||||
- `node:24-alpine` - the image to use. Note that this is the base image for
|
||||
your app from the Dockerfile
|
||||
- `sh -c "npm install && npm run dev"` - the command. You're starting a
|
||||
shell using `sh` (alpine doesn't have `bash`) and running `npm install` to
|
||||
install packages and then running `npm run dev` to start the development
|
||||
server. If you look in the `package.json`, you'll see that the `dev` script
|
||||
starts `nodemon`.
|
||||
|
||||
3. You can watch the logs using `docker logs <container-id>`. You'll know you're
|
||||
ready to go when you see this:
|
||||
|
||||
```console
|
||||
$ 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`.
|
||||
|
||||
{{< /tab >}}
|
||||
{{< tab name="PowerShell CLI" >}}
|
||||
|
||||
1. Make sure you don't have any `getting-started` containers currently running.
|
||||
|
||||
2. Run the following command from the `getting-started-app` directory.
|
||||
|
||||
```powershell
|
||||
$ docker run -dp 127.0.0.1:3000:3000 `
|
||||
-w /app --mount "type=bind,src=.,target=/app" `
|
||||
node:24-alpine `
|
||||
sh -c "npm install && npm run dev"
|
||||
```
|
||||
|
||||
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=.,target=/app"` - bind mount the current
|
||||
directory from the host into the `/app` directory in the container
|
||||
- `node:24-alpine` - the image to use. Note that this is the base image for
|
||||
your app from the Dockerfile
|
||||
- `sh -c "npm install && npm run dev"` - the command. You're starting a
|
||||
shell using `sh` (alpine doesn't have `bash`) and running `npm install` to
|
||||
install packages and then running `npm run dev` to start the development
|
||||
server. If you look in the `package.json`, you'll see that the `dev` script
|
||||
starts `nodemon`.
|
||||
|
||||
3. You can watch the logs using `docker logs <container-id>`. You'll know you're
|
||||
ready to go when you see this:
|
||||
|
||||
```console
|
||||
$ 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`.
|
||||
|
||||
{{< /tab >}}
|
||||
{{< tab name="Command Prompt CLI" >}}
|
||||
|
||||
1. Make sure you don't have any `getting-started` containers currently running.
|
||||
|
||||
2. Run the following command from the `getting-started-app` directory.
|
||||
|
||||
```console
|
||||
$ docker run -dp 127.0.0.1:3000:3000 ^
|
||||
-w /app --mount "type=bind,src=%cd%,target=/app" ^
|
||||
node:24-alpine ^
|
||||
sh -c "npm install && npm run dev"
|
||||
```
|
||||
|
||||
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=%cd%,target=/app"` - bind mount the current
|
||||
directory from the host into the `/app` directory in the container
|
||||
- `node:24-alpine` - the image to use. Note that this is the base image for
|
||||
your app from the Dockerfile
|
||||
- `sh -c "npm install && npm run dev"` - the command. You're starting a
|
||||
shell using `sh` (alpine doesn't have `bash`) and running `npm install` to
|
||||
install packages and then running `npm run dev` to start the development
|
||||
server. If you look in the `package.json`, you'll see that the `dev` script
|
||||
starts `nodemon`.
|
||||
|
||||
3. You can watch the logs using `docker logs <container-id>`. You'll know you're
|
||||
ready to go when you see this:
|
||||
|
||||
```console
|
||||
$ 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`.
|
||||
|
||||
{{< /tab >}}
|
||||
{{< tab name="Git Bash CLI" >}}
|
||||
|
||||
1. Make sure you don't have any `getting-started` containers currently running.
|
||||
|
||||
2. Run the following command from the `getting-started-app` directory.
|
||||
|
||||
```console
|
||||
$ docker run -dp 127.0.0.1:3000:3000 \
|
||||
-w //app --mount type=bind,src="./",target=/app \
|
||||
node:24-alpine \
|
||||
sh -c "npm install && npm run dev"
|
||||
```
|
||||
|
||||
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="/.",target=/app` - bind mount the current
|
||||
directory from the host into the `/app` directory in the container
|
||||
- `node:24-alpine` - the image to use. Note that this is the base image for
|
||||
your app from the Dockerfile
|
||||
- `sh -c "npm install && npm run dev"` - the command. You're starting a
|
||||
shell using `sh` (alpine doesn't have `bash`) and running `npm install` to
|
||||
install packages and then running `npm run dev` to start the development
|
||||
server. If you look in the `package.json`, you'll see that the `dev` script
|
||||
starts `nodemon`.
|
||||
|
||||
3. You can watch the logs using `docker logs <container-id>`. You'll know you're
|
||||
ready to go when you see this:
|
||||
|
||||
```console
|
||||
$ 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`.
|
||||
|
||||
{{< /tab >}}
|
||||
{{< tab name="Docker Desktop" >}}
|
||||
|
||||
Make sure you don't have any `getting-started` containers currently running.
|
||||
|
||||
Run the image with a bind mount.
|
||||
|
||||
1. Select the search box at the top of Docker Desktop.
|
||||
2. In the search window, select the **Images** tab.
|
||||
3. In the search box, specify the container name, `getting-started`.
|
||||
|
||||
> [!TIP]
|
||||
>
|
||||
> Use the search filter to filter images and only show **Local images**.
|
||||
|
||||
4. Select your image and then select **Run**.
|
||||
5. Select **Optional settings**.
|
||||
6. In **Host path**, specify the path to the `getting-started-app` directory on your host machine.
|
||||
7. In **Container path**, specify `/app`.
|
||||
8. Select **Run**.
|
||||
|
||||
You can watch the container logs using Docker Desktop.
|
||||
|
||||
1. Select **Containers** in Docker Desktop.
|
||||
2. Select your container name.
|
||||
|
||||
You'll know you're ready to go when you see this:
|
||||
|
||||
```console
|
||||
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
|
||||
```
|
||||
|
||||
{{< /tab >}}
|
||||
{{< /tabs >}}
|
||||
|
||||
### Develop your app with the development container
|
||||
|
||||
Update your app on your host machine and see the changes reflected in the container.
|
||||
|
||||
1. 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'}
|
||||
```
|
||||
|
||||
Save the file.
|
||||
|
||||
2. 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.
|
||||
|
||||

|
||||
|
||||
3. 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:
|
||||
|
||||
```console
|
||||
$ docker build -t getting-started .
|
||||
```
|
||||
|
||||
## 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.
|
||||
|
||||
Related information:
|
||||
|
||||
- [docker CLI reference](/reference/cli/docker/)
|
||||
- [Manage data in Docker](https://docs.docker.com/storage/)
|
||||
|
||||
## Next steps
|
||||
|
||||
In order to prepare your app 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 using 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 in the next section.
|
||||
|
||||
{{< button text="Multi container apps" url="07_multi_container.md" >}}
|
||||
@@ -1,331 +0,0 @@
|
||||
---
|
||||
title: Multi container apps
|
||||
weight: 70
|
||||
linkTitle: "Part 6: Multi-container apps"
|
||||
keywords: get started, setup, orientation, quickstart, intro, concepts, containers,
|
||||
docker desktop
|
||||
description: Using more than one container in your application
|
||||
aliases:
|
||||
- /get-started/07_multi_container/
|
||||
- /guides/workshop/07_multi_container/
|
||||
---
|
||||
|
||||
Up to this point, you've been working with single container apps. But, now you will add MySQL to the
|
||||
application stack. The following question often arises - "Where will MySQL run? Install it in the same
|
||||
container or run it separately?" In general, each container should do one thing and do it well. The following are a few reasons to run the container separately:
|
||||
|
||||
- There's a good chance you'd have to scale APIs and front-ends differently than databases.
|
||||
- Separate containers let you version and update versions in isolation.
|
||||
- While you may use a container for the database locally, you may want to use a managed service
|
||||
for the database in production. You don't want to ship your database engine with your app then.
|
||||
- Running multiple processes will require a process manager (the container only starts one process), which adds complexity to container startup/shutdown.
|
||||
|
||||
And there are more reasons. So, like the following diagram, it's best to run your app in multiple containers.
|
||||
|
||||

|
||||
|
||||
|
||||
## Container networking
|
||||
|
||||
Remember that containers, by default, run in isolation and don't know anything about other processes
|
||||
or containers on the same machine. So, how do you allow one container to talk to another? The answer is
|
||||
networking. If you place the two containers on the same network, they can talk to each other.
|
||||
|
||||
## Start MySQL
|
||||
|
||||
There are two ways to put a container on a network:
|
||||
- Assign the network when starting the container.
|
||||
- Connect an already running container to a network.
|
||||
|
||||
In the following steps, you'll create the network first and then attach the MySQL container at startup.
|
||||
|
||||
1. Create the network.
|
||||
|
||||
```console
|
||||
$ docker network create todo-app
|
||||
```
|
||||
|
||||
2. Start a MySQL container and attach it to the network. You're also going to define a few environment variables that the
|
||||
database will use to initialize the database. To learn more about the MySQL environment variables, see the "Environment Variables" section in the [MySQL Docker Hub listing](https://hub.docker.com/_/mysql/).
|
||||
|
||||
{{< tabs >}}
|
||||
{{< tab name="Mac / Linux / Git Bash" >}}
|
||||
|
||||
```console
|
||||
$ docker run -d \
|
||||
--network todo-app --network-alias mysql \
|
||||
-v todo-mysql-data:/var/lib/mysql \
|
||||
-e MYSQL_ROOT_PASSWORD=secret \
|
||||
-e MYSQL_DATABASE=todos \
|
||||
mysql:8.0
|
||||
```
|
||||
|
||||
{{< /tab >}}
|
||||
{{< tab name="PowerShell" >}}
|
||||
|
||||
```powershell
|
||||
$ docker run -d `
|
||||
--network todo-app --network-alias mysql `
|
||||
-v todo-mysql-data:/var/lib/mysql `
|
||||
-e MYSQL_ROOT_PASSWORD=secret `
|
||||
-e MYSQL_DATABASE=todos `
|
||||
mysql:8.0
|
||||
```
|
||||
|
||||
{{< /tab >}}
|
||||
{{< tab name="Command Prompt" >}}
|
||||
|
||||
```console
|
||||
$ docker run -d ^
|
||||
--network todo-app --network-alias mysql ^
|
||||
-v todo-mysql-data:/var/lib/mysql ^
|
||||
-e MYSQL_ROOT_PASSWORD=secret ^
|
||||
-e MYSQL_DATABASE=todos ^
|
||||
mysql:8.0
|
||||
```
|
||||
|
||||
{{< /tab >}}
|
||||
{{< /tabs >}}
|
||||
|
||||
In the previous command, you can see the `--network-alias` flag. In a later section, you'll learn more about this flag.
|
||||
|
||||
> [!TIP]
|
||||
>
|
||||
> You'll notice a volume named `todo-mysql-data` in the above command that is mounted at `/var/lib/mysql`, which is where MySQL stores its data. However, you never ran a `docker volume create` command. Docker recognizes you want to use a named volume and creates one automatically for you.
|
||||
|
||||
3. To confirm you have the database up and running, connect to the database and verify that it connects.
|
||||
|
||||
```console
|
||||
$ docker exec -it <mysql-container-id> mysql -u root -p
|
||||
```
|
||||
|
||||
When the password prompt comes up, type in `secret`. In the MySQL shell, list the databases and verify
|
||||
you see the `todos` database.
|
||||
|
||||
```console
|
||||
mysql> SHOW DATABASES;
|
||||
```
|
||||
|
||||
You should see output that looks like this:
|
||||
|
||||
```plaintext
|
||||
+--------------------+
|
||||
| Database |
|
||||
+--------------------+
|
||||
| information_schema |
|
||||
| mysql |
|
||||
| performance_schema |
|
||||
| sys |
|
||||
| todos |
|
||||
+--------------------+
|
||||
5 rows in set (0.00 sec)
|
||||
```
|
||||
|
||||
4. Exit the MySQL shell to return to the shell on your machine.
|
||||
|
||||
```console
|
||||
mysql> exit
|
||||
```
|
||||
|
||||
You now have a `todos` database and it's ready for you to use.
|
||||
|
||||
## Connect to MySQL
|
||||
|
||||
Now that you know MySQL is up and running, you can use it. But, how do you use it? If you run
|
||||
another container on the same network, how do you find the container? Remember that each container has its own IP address.
|
||||
|
||||
To answer the questions above and better understand container networking, you're going to make use of the [nicolaka/netshoot](https://github.com/nicolaka/netshoot) container,
|
||||
which ships with a lot of tools that are useful for troubleshooting or debugging networking issues.
|
||||
|
||||
1. Start a new container using the nicolaka/netshoot image. Make sure to connect it to the same network.
|
||||
|
||||
```console
|
||||
$ docker run -it --network todo-app nicolaka/netshoot
|
||||
```
|
||||
|
||||
2. Inside the container, you're going to use the `dig` command, which is a useful DNS tool. You're going to look up
|
||||
the IP address for the hostname `mysql`.
|
||||
|
||||
```console
|
||||
$ dig mysql
|
||||
```
|
||||
|
||||
You should get output like the following.
|
||||
|
||||
```text
|
||||
; <<>> DiG 9.18.8 <<>> mysql
|
||||
;; global options: +cmd
|
||||
;; Got answer:
|
||||
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 32162
|
||||
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
|
||||
|
||||
;; QUESTION SECTION:
|
||||
;mysql. IN A
|
||||
|
||||
;; ANSWER SECTION:
|
||||
mysql. 600 IN A 172.23.0.2
|
||||
|
||||
;; Query time: 0 msec
|
||||
;; SERVER: 127.0.0.11#53(127.0.0.11)
|
||||
;; WHEN: Tue Oct 01 23:47:24 UTC 2019
|
||||
;; MSG SIZE rcvd: 44
|
||||
```
|
||||
|
||||
In the "ANSWER SECTION", you will see an `A` record for `mysql` that resolves to `172.23.0.2`
|
||||
(your IP address will most likely have a different value). While `mysql` isn't normally a valid hostname,
|
||||
Docker was able to resolve it to the IP address of the container that had that network alias. Remember, you used the
|
||||
`--network-alias` earlier.
|
||||
|
||||
What this means is that your app only simply needs to connect to a host named `mysql` and it'll talk to the
|
||||
database.
|
||||
|
||||
## Run your app with MySQL
|
||||
|
||||
The todo app supports the setting of a few environment variables to specify MySQL connection settings. They are:
|
||||
|
||||
- `MYSQL_HOST` - the hostname for the running MySQL server
|
||||
- `MYSQL_USER` - the username to use for the connection
|
||||
- `MYSQL_PASSWORD` - the password to use for the connection
|
||||
- `MYSQL_DB` - the database to use once connected
|
||||
|
||||
> [!NOTE]
|
||||
>
|
||||
> While using env vars to set connection settings is generally accepted for development, it's highly discouraged
|
||||
> when running applications in production. Diogo Monica, a former lead of security at Docker,
|
||||
> [wrote a fantastic blog post](https://blog.diogomonica.com/2017/03/27/why-you-shouldnt-use-env-variables-for-secret-data/)
|
||||
> explaining why.
|
||||
>
|
||||
> A more secure mechanism is to use the secret support provided by your container orchestration framework. In most cases,
|
||||
> these secrets are mounted as files in the running container. You'll see many apps (including the MySQL image and the todo app)
|
||||
> also support env vars with a `_FILE` suffix to point to a file containing the variable.
|
||||
>
|
||||
> As an example, setting the `MYSQL_PASSWORD_FILE` var will cause the app to use the contents of the referenced file
|
||||
> as the connection password. Docker doesn't do anything to support these env vars. Your app will need to know to look for
|
||||
> the variable and get the file contents.
|
||||
|
||||
You can now start your dev-ready container.
|
||||
|
||||
1. Specify each of the previous environment variables, as well as connect the container to your app network. Make sure that you are in the `getting-started-app` directory when you run this command.
|
||||
|
||||
{{< tabs >}}
|
||||
{{< tab name="Mac / Linux" >}}
|
||||
|
||||
```console
|
||||
$ docker run -dp 127.0.0.1:3000:3000 \
|
||||
-w /app -v ".:/app" \
|
||||
--network todo-app \
|
||||
-e MYSQL_HOST=mysql \
|
||||
-e MYSQL_USER=root \
|
||||
-e MYSQL_PASSWORD=secret \
|
||||
-e MYSQL_DB=todos \
|
||||
node:24-alpine \
|
||||
sh -c "npm install && npm run dev"
|
||||
```
|
||||
|
||||
{{< /tab >}}
|
||||
{{< tab name="PowerShell" >}}
|
||||
In Windows, run this command in PowerShell.
|
||||
|
||||
```powershell
|
||||
$ docker run -dp 127.0.0.1:3000:3000 `
|
||||
-w /app -v ".:/app" `
|
||||
--network todo-app `
|
||||
-e MYSQL_HOST=mysql `
|
||||
-e MYSQL_USER=root `
|
||||
-e MYSQL_PASSWORD=secret `
|
||||
-e MYSQL_DB=todos `
|
||||
node:24-alpine `
|
||||
sh -c "npm install && npm run dev"
|
||||
```
|
||||
|
||||
{{< /tab >}}
|
||||
{{< tab name="Command Prompt" >}}
|
||||
In Windows, run this command in Command Prompt.
|
||||
|
||||
```console
|
||||
$ docker run -dp 127.0.0.1:3000:3000 ^
|
||||
-w /app -v "%cd%:/app" ^
|
||||
--network todo-app ^
|
||||
-e MYSQL_HOST=mysql ^
|
||||
-e MYSQL_USER=root ^
|
||||
-e MYSQL_PASSWORD=secret ^
|
||||
-e MYSQL_DB=todos ^
|
||||
node:24-alpine ^
|
||||
sh -c "npm install && npm run dev"
|
||||
```
|
||||
|
||||
{{< /tab >}}
|
||||
{{< tab name="Git Bash" >}}
|
||||
|
||||
```console
|
||||
$ docker run -dp 127.0.0.1:3000:3000 \
|
||||
-w //app -v "/.:/app" \
|
||||
--network todo-app \
|
||||
-e MYSQL_HOST=mysql \
|
||||
-e MYSQL_USER=root \
|
||||
-e MYSQL_PASSWORD=secret \
|
||||
-e MYSQL_DB=todos \
|
||||
node:24-alpine \
|
||||
sh -c "npm install && npm run dev"
|
||||
```
|
||||
|
||||
{{< /tab >}}
|
||||
{{< /tabs >}}
|
||||
|
||||
2. If you look at the logs for the container (`docker logs -f <container-id>`), you should see a message similar to the following, which indicates it's
|
||||
using the mysql database.
|
||||
|
||||
```console
|
||||
[nodemon] 3.1.11
|
||||
[nodemon] to restart at any time, enter `rs`
|
||||
[nodemon] watching path(s): *.*
|
||||
[nodemon] watching extensions: js,mjs,cjs,json
|
||||
[nodemon] starting `node src/index.js`
|
||||
Waiting for mysql:3306.
|
||||
Connected!
|
||||
Connected to mysql db at host mysql
|
||||
Listening on port 3000
|
||||
```
|
||||
|
||||
3. Open the app in your browser and add a few items to your todo list.
|
||||
|
||||
4. Connect to the mysql database and prove that the items are being written to the database. Remember, the password
|
||||
is `secret`.
|
||||
|
||||
```console
|
||||
$ docker exec -it <mysql-container-id> mysql -p todos
|
||||
```
|
||||
|
||||
And in the mysql shell, run the following:
|
||||
|
||||
```console
|
||||
mysql> select * from todo_items;
|
||||
+--------------------------------------+--------------------+-----------+
|
||||
| id | name | completed |
|
||||
+--------------------------------------+--------------------+-----------+
|
||||
| c906ff08-60e6-44e6-8f49-ed56a0853e85 | Do amazing things! | 0 |
|
||||
| 2912a79e-8486-4bc3-a4c5-460793a575ab | Be awesome! | 0 |
|
||||
+--------------------------------------+--------------------+-----------+
|
||||
```
|
||||
|
||||
Your table will look different because it has your items. But, you should see them stored there.
|
||||
|
||||
## Summary
|
||||
|
||||
At this point, you have an application that now stores its data in an external database running in a separate
|
||||
container. You learned a little bit about container networking and service discovery using DNS.
|
||||
|
||||
Related information:
|
||||
- [docker CLI reference](/reference/cli/docker/)
|
||||
- [Networking overview](/manuals/engine/network/_index.md)
|
||||
|
||||
## Next steps
|
||||
|
||||
There's a good chance you are starting to feel a little overwhelmed with everything you need to do to start up
|
||||
this application. You have to create a network, start containers, specify all of the environment variables, expose
|
||||
ports, and more. That's a lot to remember and it's certainly making things harder to pass along to someone else.
|
||||
|
||||
In the next section, you'll learn about Docker Compose. With Docker Compose, you can share your application stacks in a
|
||||
much easier way and let others spin them up with a single, simple command.
|
||||
|
||||
{{< button text="Use Docker Compose" url="08_using_compose.md" >}}
|
||||
@@ -1,292 +0,0 @@
|
||||
---
|
||||
title: Use Docker Compose
|
||||
weight: 80
|
||||
linkTitle: "Part 7: Use Docker Compose"
|
||||
keywords: get started, setup, orientation, quickstart, intro, concepts, containers,
|
||||
docker desktop
|
||||
description: Using Docker Compose for multi-container applications
|
||||
aliases:
|
||||
- /get-started/08_using_compose/
|
||||
- /guides/workshop/08_using_compose/
|
||||
---
|
||||
|
||||
[Docker Compose](/manuals/compose/_index.md) is a tool that helps you define and
|
||||
share multi-container applications. With Compose, you can create a YAML file to define the services
|
||||
and with a single command, you can spin everything up or tear it all down.
|
||||
|
||||
The big advantage of using Compose is you can define your application stack in a file, keep it at the root of
|
||||
your project repository (it's now version controlled), and easily enable someone else to contribute to your project.
|
||||
Someone would only need to clone your repository and start the app using Compose. In fact, you might see quite a few projects
|
||||
on GitHub/GitLab doing exactly this now.
|
||||
|
||||
## Create the Compose file
|
||||
|
||||
In the `getting-started-app` directory, create a file named `compose.yaml`.
|
||||
|
||||
```text
|
||||
├── getting-started-app/
|
||||
│ ├── Dockerfile
|
||||
│ ├── compose.yaml
|
||||
│ ├── node_modules/
|
||||
│ ├── package.json
|
||||
│ ├── package-lock.json
|
||||
│ ├── spec/
|
||||
│ └── src/
|
||||
```
|
||||
|
||||
## Define the app service
|
||||
|
||||
In [part 6](./07_multi_container.md), you used the following command to start the application service.
|
||||
|
||||
```console
|
||||
$ docker run -dp 127.0.0.1:3000:3000 \
|
||||
-w /app -v ".:/app" \
|
||||
--network todo-app \
|
||||
-e MYSQL_HOST=mysql \
|
||||
-e MYSQL_USER=root \
|
||||
-e MYSQL_PASSWORD=secret \
|
||||
-e MYSQL_DB=todos \
|
||||
node:24-alpine \
|
||||
sh -c "npm install && npm run dev"
|
||||
```
|
||||
|
||||
You'll now define this service in the `compose.yaml` file.
|
||||
|
||||
1. Open `compose.yaml` in a text or code editor, and start by defining the name and image of the first service (or container) you want to run as part of your application.
|
||||
The name will automatically become a network alias, which will be useful when defining your MySQL service.
|
||||
|
||||
```yaml
|
||||
services:
|
||||
app:
|
||||
image: node:24-alpine
|
||||
```
|
||||
|
||||
2. Typically, you will see `command` close to the `image` definition, although there is no requirement on ordering. Add the `command` to your `compose.yaml` file.
|
||||
|
||||
```yaml
|
||||
services:
|
||||
app:
|
||||
image: node:24-alpine
|
||||
command: sh -c "npm install && npm run dev"
|
||||
```
|
||||
|
||||
3. Now migrate the `-p 127.0.0.1:3000:3000` part of the command by defining the `ports` for the service.
|
||||
|
||||
```yaml
|
||||
services:
|
||||
app:
|
||||
image: node:24-alpine
|
||||
command: sh -c "npm install && npm run dev"
|
||||
ports:
|
||||
- 127.0.0.1:3000:3000
|
||||
```
|
||||
|
||||
4. Next, migrate both the working directory (`-w /app`) and the volume mapping
|
||||
(`-v ".:/app"`) by using the `working_dir` and `volumes` definitions.
|
||||
|
||||
One advantage of Docker Compose volume definitions is you can use relative paths from the current directory.
|
||||
|
||||
```yaml
|
||||
services:
|
||||
app:
|
||||
image: node:24-alpine
|
||||
command: sh -c "npm install && npm run dev"
|
||||
ports:
|
||||
- 127.0.0.1:3000:3000
|
||||
working_dir: /app
|
||||
volumes:
|
||||
- ./:/app
|
||||
```
|
||||
|
||||
5. Finally, you need to migrate the environment variable definitions using the `environment` key.
|
||||
|
||||
```yaml
|
||||
services:
|
||||
app:
|
||||
image: node:24-alpine
|
||||
command: sh -c "npm install && npm run dev"
|
||||
ports:
|
||||
- 127.0.0.1:3000:3000
|
||||
working_dir: /app
|
||||
volumes:
|
||||
- ./:/app
|
||||
environment:
|
||||
MYSQL_HOST: mysql
|
||||
MYSQL_USER: root
|
||||
MYSQL_PASSWORD: secret
|
||||
MYSQL_DB: todos
|
||||
```
|
||||
|
||||
### Define the MySQL service
|
||||
|
||||
Now, it's time to define the MySQL service. The command that you used for that container was the following:
|
||||
|
||||
```console
|
||||
$ docker run -d \
|
||||
--network todo-app --network-alias mysql \
|
||||
-v todo-mysql-data:/var/lib/mysql \
|
||||
-e MYSQL_ROOT_PASSWORD=secret \
|
||||
-e MYSQL_DATABASE=todos \
|
||||
mysql:8.0
|
||||
```
|
||||
|
||||
1. First define the new service and name it `mysql` so it automatically gets the network alias. Also specify the image to use as well.
|
||||
|
||||
```yaml
|
||||
|
||||
services:
|
||||
app:
|
||||
# The app service definition
|
||||
mysql:
|
||||
image: mysql:8.0
|
||||
```
|
||||
|
||||
2. Next, define the volume mapping. When you ran the container with `docker
|
||||
run`, Docker created the named volume automatically. However, that doesn't
|
||||
happen when running with Compose. You need to define the volume in the
|
||||
top-level `volumes:` section and then specify the mountpoint in the service
|
||||
config. By simply providing only the volume name, the default options are
|
||||
used.
|
||||
|
||||
```yaml
|
||||
services:
|
||||
app:
|
||||
# The app service definition
|
||||
mysql:
|
||||
image: mysql:8.0
|
||||
volumes:
|
||||
- todo-mysql-data:/var/lib/mysql
|
||||
|
||||
volumes:
|
||||
todo-mysql-data:
|
||||
```
|
||||
|
||||
3. Finally, you need to specify the environment variables.
|
||||
|
||||
```yaml
|
||||
services:
|
||||
app:
|
||||
# The app service definition
|
||||
mysql:
|
||||
image: mysql:8.0
|
||||
volumes:
|
||||
- todo-mysql-data:/var/lib/mysql
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: secret
|
||||
MYSQL_DATABASE: todos
|
||||
|
||||
volumes:
|
||||
todo-mysql-data:
|
||||
```
|
||||
|
||||
At this point, your complete `compose.yaml` should look like this:
|
||||
|
||||
|
||||
```yaml
|
||||
services:
|
||||
app:
|
||||
image: node:24-alpine
|
||||
command: sh -c "npm install && npm run dev"
|
||||
ports:
|
||||
- 127.0.0.1:3000:3000
|
||||
working_dir: /app
|
||||
volumes:
|
||||
- ./:/app
|
||||
environment:
|
||||
MYSQL_HOST: mysql
|
||||
MYSQL_USER: root
|
||||
MYSQL_PASSWORD: secret
|
||||
MYSQL_DB: todos
|
||||
|
||||
mysql:
|
||||
image: mysql:8.0
|
||||
volumes:
|
||||
- todo-mysql-data:/var/lib/mysql
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: secret
|
||||
MYSQL_DATABASE: todos
|
||||
|
||||
volumes:
|
||||
todo-mysql-data:
|
||||
```
|
||||
|
||||
## Run the application stack
|
||||
|
||||
Now that you have your `compose.yaml` file, you can start your application.
|
||||
|
||||
1. Make sure no other copies of the containers are running first. Use `docker ps` to list the containers and `docker rm -f <ids>` to remove them.
|
||||
|
||||
2. Start up the application stack using the `docker compose up` command. Add the
|
||||
`-d` flag to run everything in the background.
|
||||
|
||||
```console
|
||||
$ docker compose up -d
|
||||
```
|
||||
|
||||
When you run the previous command, you should see output like the following:
|
||||
|
||||
```plaintext
|
||||
Creating network "app_default" with the default driver
|
||||
Creating volume "app_todo-mysql-data" with default driver
|
||||
Creating app_app_1 ... done
|
||||
Creating app_mysql_1 ... done
|
||||
```
|
||||
|
||||
You'll notice that Docker Compose created the volume as well as a network. By default, Docker Compose automatically creates a network specifically for the application stack (which is why you didn't define one in the Compose file).
|
||||
|
||||
3. Look at the logs using the `docker compose logs -f` command. You'll see the logs from each of the services interleaved
|
||||
into a single stream. This is incredibly useful when you want to watch for timing-related issues. The `-f` flag follows the
|
||||
log, so will give you live output as it's generated.
|
||||
|
||||
If you have run the command already, you'll see output that looks like this:
|
||||
|
||||
```plaintext
|
||||
mysql_1 | 2019-10-03T03:07:16.083639Z 0 [Note] mysqld: ready for connections.
|
||||
mysql_1 | Version: '8.0.31' socket: '/var/run/mysqld/mysqld.sock' port: 3306 MySQL Community Server (GPL)
|
||||
app_1 | Connected to mysql db at host mysql
|
||||
app_1 | Listening on port 3000
|
||||
```
|
||||
|
||||
The service name is displayed at the beginning of the line (often colored) to help distinguish messages. If you want to
|
||||
view the logs for a specific service, you can add the service name to the end of the logs command (for example,
|
||||
`docker compose logs -f app`).
|
||||
|
||||
4. At this point, you should be able to open your app in your browser on [http://localhost:3000](http://localhost:3000) and see it running.
|
||||
|
||||
## See the app stack in Docker Desktop Dashboard
|
||||
|
||||
If you look at the Docker Desktop Dashboard, you'll see that there is a group named **getting-started-app**. This is the project name from Docker
|
||||
Compose and used to group the containers together. By default, the project name is simply the name of the directory that the
|
||||
`compose.yaml` was located in.
|
||||
|
||||
If you expand the stack, you'll see the two containers you defined in the Compose file. The names are also a little
|
||||
more descriptive, as they follow the pattern of `<service-name>-<replica-number>`. So, it's very easy to
|
||||
quickly see what container is your app and which container is the mysql database.
|
||||
|
||||
## Tear it all down
|
||||
|
||||
When you're ready to tear it all down, simply run `docker compose down` or hit the trash can on the Docker Desktop Dashboard
|
||||
for the entire app. The containers will stop and the network will be removed.
|
||||
|
||||
> [!WARNING]
|
||||
>
|
||||
> By default, named volumes in your compose file are not removed when you run `docker compose down`. If you want to
|
||||
>remove the volumes, you need to add the `--volumes` flag.
|
||||
>
|
||||
> The Docker Desktop Dashboard does not remove volumes when you delete the app stack.
|
||||
|
||||
## Summary
|
||||
|
||||
In this section, you learned about Docker Compose and how it helps you simplify
|
||||
the way you define and share multi-service applications.
|
||||
|
||||
Related information:
|
||||
- [Compose overview](/manuals/compose/_index.md)
|
||||
- [Compose file reference](/reference/compose-file/_index.md)
|
||||
- [Compose CLI reference](/reference/cli/docker/compose/)
|
||||
|
||||
## Next steps
|
||||
|
||||
Next, you'll learn about a few best practices you can use to improve your Dockerfile.
|
||||
|
||||
{{< button text="Image-building best practices" url="09_image_best.md" >}}
|
||||
@@ -1,217 +0,0 @@
|
||||
---
|
||||
title: Image-building best practices
|
||||
weight: 90
|
||||
linkTitle: "Part 8: Image-building best practices"
|
||||
keywords: get started, setup, orientation, quickstart, intro, concepts, containers,
|
||||
docker desktop
|
||||
description: Tips for building images for your application
|
||||
aliases:
|
||||
- /get-started/09_image_best/
|
||||
- /guides/workshop/09_image_best/
|
||||
---
|
||||
|
||||
## Image layering
|
||||
|
||||
Using the `docker image history` command, you can see the command that was used
|
||||
to create each layer within an image.
|
||||
|
||||
1. Use the `docker image history` command to see the layers in the `getting-started` image you
|
||||
created.
|
||||
|
||||
```console
|
||||
$ docker image history getting-started
|
||||
```
|
||||
|
||||
You should get output that looks something like the following.
|
||||
|
||||
```plaintext
|
||||
IMAGE CREATED CREATED BY SIZE COMMENT
|
||||
a78a40cbf866 18 seconds ago /bin/sh -c #(nop) CMD ["node" "src/index.j… 0B
|
||||
f1d1808565d6 19 seconds ago /bin/sh -c npm install --omit=dev 85.4MB
|
||||
a2c054d14948 36 seconds ago /bin/sh -c #(nop) COPY dir:5dc710ad87c789593… 198kB
|
||||
9577ae713121 37 seconds ago /bin/sh -c #(nop) WORKDIR /app 0B
|
||||
b95baba1cfdb 13 days ago /bin/sh -c #(nop) CMD ["node"] 0B
|
||||
<missing> 13 days ago /bin/sh -c #(nop) ENTRYPOINT ["docker-entry… 0B
|
||||
<missing> 13 days ago /bin/sh -c #(nop) COPY file:238737301d473041… 116B
|
||||
<missing> 13 days ago /bin/sh -c apk add --no-cache --virtual .bui… 5.35MB
|
||||
<missing> 13 days ago /bin/sh -c addgroup -g 1000 node && addu… 74.3MB
|
||||
<missing> 13 days ago /bin/sh -c #(nop) ENV NODE_VERSION=12.14.1 0B
|
||||
<missing> 13 days ago /bin/sh -c #(nop) CMD ["/bin/sh"] 0B
|
||||
<missing> 13 days ago /bin/sh -c #(nop) ADD file:e69d441d729412d24… 5.59MB
|
||||
```
|
||||
|
||||
Each of the lines represents a layer in the image. The display here shows the base at the bottom with
|
||||
the newest layer at the top. Using this, you can also quickly see the size of each layer, helping
|
||||
diagnose large images.
|
||||
|
||||
2. You'll notice that several of the lines are truncated. If you add the `--no-trunc` flag, you'll get the
|
||||
full output.
|
||||
|
||||
```console
|
||||
$ docker image history --no-trunc getting-started
|
||||
```
|
||||
|
||||
## Layer caching
|
||||
|
||||
Now that you've seen the layering in action, there's an important lesson to learn to help decrease build
|
||||
times for your container images. Once a layer changes, all downstream layers have to be recreated as well.
|
||||
|
||||
Look at the following Dockerfile you created for the getting started app.
|
||||
|
||||
```dockerfile
|
||||
# syntax=docker/dockerfile:1
|
||||
FROM node:24-alpine
|
||||
WORKDIR /app
|
||||
COPY . .
|
||||
RUN npm install --omit=dev
|
||||
CMD ["node", "src/index.js"]
|
||||
EXPOSE 3000
|
||||
```
|
||||
|
||||
Going back to the image history output, you see that each command in the Dockerfile becomes a new layer in the image.
|
||||
You might remember that when you made a change to the image, the dependencies had to be reinstalled. It doesn't make much sense to ship around the same dependencies every time you build.
|
||||
|
||||
To fix it, you need to restructure your Dockerfile to help support the caching
|
||||
of the dependencies. For Node-based applications, those dependencies are defined
|
||||
in the `package.json` file. You can copy only that file in first, install the
|
||||
dependencies, and then copy in everything else. Then, you only recreate the
|
||||
dependencies if there was a change to the `package.json`.
|
||||
|
||||
1. Update the Dockerfile to copy in the `package.json` first, install dependencies, and then copy everything else in.
|
||||
|
||||
```dockerfile
|
||||
# syntax=docker/dockerfile:1
|
||||
FROM node:24-alpine
|
||||
WORKDIR /app
|
||||
COPY package.json package-lock.json ./
|
||||
RUN npm install --omit=dev
|
||||
COPY . .
|
||||
CMD ["node", "src/index.js"]
|
||||
```
|
||||
|
||||
2. Build a new image using `docker build`.
|
||||
|
||||
```console
|
||||
$ docker build -t getting-started .
|
||||
```
|
||||
|
||||
You should see output like the following.
|
||||
|
||||
```plaintext
|
||||
[+] Building 16.1s (10/10) FINISHED
|
||||
=> [internal] load build definition from Dockerfile
|
||||
=> => transferring dockerfile: 175B
|
||||
=> [internal] load .dockerignore
|
||||
=> => transferring context: 2B
|
||||
=> [internal] load metadata for docker.io/library/node:24-alpine
|
||||
=> [internal] load build context
|
||||
=> => transferring context: 53.37MB
|
||||
=> [1/5] FROM docker.io/library/node:24-alpine
|
||||
=> CACHED [2/5] WORKDIR /app
|
||||
=> [3/5] COPY package.json package-lock.json ./
|
||||
=> [4/5] RUN npm install --omit=dev
|
||||
=> [5/5] COPY . .
|
||||
=> exporting to image
|
||||
=> => exporting layers
|
||||
=> => writing image sha256:d6f819013566c54c50124ed94d5e66c452325327217f4f04399b45f94e37d25
|
||||
=> => naming to docker.io/library/getting-started
|
||||
```
|
||||
|
||||
3. Now, make a change to the `src/static/index.html` file. For example, change the `<title>` to "The Awesome Todo App".
|
||||
|
||||
4. Build the Docker image now using `docker build -t getting-started .` again. This time, your output should look a little different.
|
||||
|
||||
```plaintext
|
||||
[+] Building 1.2s (10/10) FINISHED
|
||||
=> [internal] load build definition from Dockerfile
|
||||
=> => transferring dockerfile: 37B
|
||||
=> [internal] load .dockerignore
|
||||
=> => transferring context: 2B
|
||||
=> [internal] load metadata for docker.io/library/node:24-alpine
|
||||
=> [internal] load build context
|
||||
=> => transferring context: 450.43kB
|
||||
=> [1/5] FROM docker.io/library/node:24-alpine
|
||||
=> CACHED [2/5] WORKDIR /app
|
||||
=> CACHED [3/5] COPY package.json package-lock.json ./
|
||||
=> CACHED [4/5] RUN npm install
|
||||
=> [5/5] COPY . .
|
||||
=> exporting to image
|
||||
=> => exporting layers
|
||||
=> => writing image sha256:91790c87bcb096a83c2bd4eb512bc8b134c757cda0bdee4038187f98148e2eda
|
||||
=> => naming to docker.io/library/getting-started
|
||||
```
|
||||
|
||||
First off, you should notice that the build was much faster. And, you'll see
|
||||
that several steps are using previously cached layers. Pushing and pulling
|
||||
this image and updates to it will be much faster as well.
|
||||
|
||||
## Multi-stage builds
|
||||
|
||||
Multi-stage builds are an incredibly powerful
|
||||
tool to help use multiple stages to create an image. There are several advantages for them:
|
||||
|
||||
- Separate build-time dependencies from runtime dependencies
|
||||
- Reduce overall image size by shipping only what your app needs to run
|
||||
|
||||
### Maven/Tomcat example
|
||||
|
||||
When building Java-based applications, you need a JDK to compile the source code to Java bytecode. However,
|
||||
that JDK isn't needed in production. Also, you might be using tools like Maven or Gradle to help build the app.
|
||||
Those also aren't needed in your final image. Multi-stage builds help.
|
||||
|
||||
```dockerfile
|
||||
# syntax=docker/dockerfile:1
|
||||
FROM maven AS build
|
||||
WORKDIR /app
|
||||
COPY . .
|
||||
RUN mvn package
|
||||
|
||||
FROM tomcat
|
||||
COPY --from=build /app/target/file.war /usr/local/tomcat/webapps
|
||||
```
|
||||
|
||||
In this example, you use one stage (called `build`) to perform the actual Java build using Maven. In the second
|
||||
stage (starting at `FROM tomcat`), you copy in files from the `build` stage. The final image is only the last stage
|
||||
being created, which can be overridden using the `--target` flag.
|
||||
|
||||
### React example
|
||||
|
||||
When building React applications, you need a Node environment to compile the JS code (typically JSX), SASS stylesheets,
|
||||
and more into static HTML, JS, and CSS. If you aren't doing server-side rendering, you don't even need a Node environment
|
||||
for your production build. You can ship the static resources in a static nginx container.
|
||||
|
||||
```dockerfile
|
||||
# syntax=docker/dockerfile:1
|
||||
FROM node:24-alpine AS build
|
||||
WORKDIR /app
|
||||
COPY package* ./
|
||||
RUN npm install
|
||||
COPY public ./public
|
||||
COPY src ./src
|
||||
RUN npm run build
|
||||
|
||||
FROM nginx:alpine
|
||||
COPY --from=build /app/build /usr/share/nginx/html
|
||||
```
|
||||
|
||||
In the previous Dockerfile example, it uses the `node:24-alpine` image to perform the build (maximizing layer caching) and then copies the output
|
||||
into an nginx container.
|
||||
|
||||
|
||||
> [!Tips]
|
||||
> This React example is for illustration purposes. The getting-started todo app is a `Node.js` backend application, not a React frontend.
|
||||
|
||||
|
||||
## Summary
|
||||
|
||||
In this section, you learned a few image building best practices, including layer caching and multi-stage builds.
|
||||
|
||||
Related information:
|
||||
- [Dockerfile reference](/reference/dockerfile/)
|
||||
- [Dockerfile best practices](/manuals/build/building/best-practices.md)
|
||||
|
||||
## Next steps
|
||||
|
||||
In the next section, you'll learn about additional resources you can use to continue learning about containers.
|
||||
|
||||
{{< button text="What next" url="10_what_next.md" >}}
|
||||
@@ -1,80 +0,0 @@
|
||||
---
|
||||
title: What next after the Docker workshop
|
||||
weight: 100
|
||||
linkTitle: "Part 9: What next"
|
||||
keywords: get started, setup, orientation, quickstart, intro, concepts, containers,
|
||||
docker desktop, AI, model runner, MCP, agents, hardened images, security
|
||||
description: Explore what to do next after completing the Docker workshop, including securing your images, AI development, and language-specific guides.
|
||||
aliases:
|
||||
- /get-started/11_what_next/
|
||||
- /guides/workshop/10_what_next/
|
||||
summary: |
|
||||
Now that you've completed the Docker workshop, you're ready to explore
|
||||
securing your images with Docker Hardened Images, building AI-powered
|
||||
applications, and diving into language-specific guides.
|
||||
notoc: true
|
||||
|
||||
secure-images:
|
||||
- title: What are Docker Hardened Images?
|
||||
description: Understand secure, minimal, production-ready base images with near-zero CVEs.
|
||||
link: /dhi/explore/what/
|
||||
- title: Get started with DHI
|
||||
description: Pull and run your first Docker Hardened Image in minutes.
|
||||
link: /dhi/get-started/
|
||||
- title: Use hardened images
|
||||
description: Learn how to use DHI in your Dockerfiles and CI/CD pipelines.
|
||||
link: /dhi/how-to/use/
|
||||
- title: Explore the DHI catalog
|
||||
description: Browse available hardened images, variants, and security attestations.
|
||||
link: /dhi/how-to/explore/
|
||||
|
||||
ai-development:
|
||||
- title: Docker Model Runner
|
||||
description: Run and manage AI models locally using familiar Docker commands with OpenAI-compatible APIs.
|
||||
link: /ai/model-runner/
|
||||
- title: MCP Toolkit
|
||||
description: Set up, manage, and run containerized MCP servers to power your AI agents.
|
||||
link: /ai/mcp-catalog-and-toolkit/toolkit/
|
||||
- title: Build AI agents with Docker Agent
|
||||
description: Create teams of specialized AI agents that collaborate to solve complex problems.
|
||||
link: /ai/docker-agent/
|
||||
- title: Use AI models in Compose
|
||||
description: Define AI model dependencies in your Docker Compose applications.
|
||||
link: /ai/compose/models-and-compose/
|
||||
|
||||
language-guides:
|
||||
- title: Node.js
|
||||
description: Learn how to containerize and develop Node.js applications.
|
||||
link: /guides/language/nodejs/
|
||||
- title: Python
|
||||
description: Build and run Python applications in containers.
|
||||
link: /guides/language/python/
|
||||
- title: Java
|
||||
description: Containerize Java applications with best practices.
|
||||
link: /guides/language/java/
|
||||
- title: Go
|
||||
description: Develop and deploy Go applications using Docker.
|
||||
link: /guides/language/golang/
|
||||
---
|
||||
|
||||
Congratulations on completing the Docker workshop. You've learned how to containerize applications, work with multi-container setups, use Docker Compose, and apply image-building best practices.
|
||||
|
||||
Here's what to explore next.
|
||||
|
||||
## Secure your images
|
||||
|
||||
Take your image-building skills to the next level with Docker Hardened Images—secure, minimal, and production-ready base images that are now free for everyone.
|
||||
|
||||
{{< grid items="secure-images" >}}
|
||||
|
||||
## Build with AI
|
||||
|
||||
Docker makes it easy to run AI models locally and build agentic AI applications. Explore Docker's AI tools and start building AI-powered apps.
|
||||
|
||||
{{< grid items="ai-development" >}}
|
||||
|
||||
## Language-specific guides
|
||||
|
||||
Apply what you've learned to your preferred programming language with hands-on tutorials.
|
||||
|
||||
{{< grid items="language-guides" >}}
|
||||
@@ -1,49 +0,0 @@
|
||||
---
|
||||
title: Overview of the Docker workshop
|
||||
linkTitle: Docker workshop
|
||||
keywords: docker basics, how to start a docker container, container settings, setup
|
||||
docker, how to setup docker, setting up docker, docker container guide, how to get
|
||||
started with docker
|
||||
description: Get started with the Docker basics in this workshop, You'll
|
||||
learn about containers, images, and how to containerize your first application.
|
||||
aliases:
|
||||
- /guides/get-started/
|
||||
- /get-started/hands-on-overview/
|
||||
- /guides/workshop/
|
||||
---
|
||||
|
||||
This 45-minute workshop contains step-by-step instructions on how to get started with Docker. This workshop shows you how to:
|
||||
|
||||
- Build and run an image as a container.
|
||||
- Share images using Docker Hub.
|
||||
- Deploy Docker applications using multiple containers with a database.
|
||||
- Run applications using Docker Compose.
|
||||
|
||||
> [!NOTE]
|
||||
>
|
||||
> For a quick introduction to Docker and the benefits of containerizing your
|
||||
> applications, see [Getting started](/get-started/introduction/_index.md).
|
||||
|
||||
## What is a container?
|
||||
|
||||
A container is a sandboxed process running on a host machine that is isolated from all other processes running on that host machine. That isolation leverages [kernel namespaces and cgroups](https://medium.com/@saschagrunert/demystifying-containers-part-i-kernel-space-2c53d6979504),
|
||||
features that have been in Linux for a long time. Docker makes these capabilities approachable and easy to use. To summarize, a container:
|
||||
|
||||
- Is a runnable instance of an image. You can create, start, stop, move, or delete a container using the Docker API or CLI.
|
||||
- Can be run on local machines, virtual machines, or deployed to the cloud.
|
||||
- Is portable (and can be run on any OS).
|
||||
- Is isolated from other containers and runs its own software, binaries, configurations, etc.
|
||||
|
||||
If you're familiar with `chroot`, then think of a container as an extended version of `chroot`. The filesystem comes from the image. However, a container adds additional isolation not available when using chroot.
|
||||
|
||||
## What is an image?
|
||||
|
||||
A running container uses an isolated filesystem. This isolated filesystem is provided by an image, and the image must contain everything needed to run an application - all dependencies, configurations, scripts, binaries, etc. The image also contains other configurations for the container, such as environment variables, a default command to run, and other metadata.
|
||||
|
||||
## Next steps
|
||||
|
||||
In this section, you learned about containers and images.
|
||||
|
||||
Next, you'll containerize a simple application and get hands-on with the concepts.
|
||||
|
||||
{{< button text="Containerize an application" url="02_our_app.md" >}}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 29 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 9.1 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 9.5 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 11 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 4.8 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 4.4 KiB |
@@ -1,142 +0,0 @@
|
||||
---
|
||||
title: Deploy to Kubernetes
|
||||
keywords: kubernetes, pods, deployments, kubernetes services
|
||||
description: Learn how to describe and deploy a simple application on Kubernetes.
|
||||
aliases:
|
||||
- /get-started/kube-deploy/
|
||||
- /guides/deployment-orchestration/kube-deploy/
|
||||
summary: |
|
||||
Learn how to deploy and orchestrate Docker containers using Kubernetes.
|
||||
params:
|
||||
tags: [deployment]
|
||||
time: 10 minutes
|
||||
---
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Download and install Docker Desktop as described in [Get Docker](/get-started/get-docker.md).
|
||||
- Work through containerizing an application in [Part 2](02_our_app.md).
|
||||
- Make sure that Kubernetes is turned on in Docker Desktop:
|
||||
If Kubernetes isn't running, follow the instructions in [Orchestration](orchestration.md) to finish setting it up.
|
||||
|
||||
## Introduction
|
||||
|
||||
Now that you've demonstrated that the individual components of your application run as stand-alone containers, it's time to arrange for them to be managed by an orchestrator like Kubernetes. Kubernetes provides many tools for scaling, networking, securing and maintaining your containerized applications, above and beyond the abilities of containers themselves.
|
||||
|
||||
In order to validate that your containerized application works well on Kubernetes, you'll use Docker Desktop's built in Kubernetes environment right on your development machine to deploy your application, before handing it off to run on a full Kubernetes cluster in production. The Kubernetes environment created by Docker Desktop is _fully featured_, meaning it has all the Kubernetes features your app will enjoy on a real cluster, accessible from the convenience of your development machine.
|
||||
|
||||
## Describing apps using Kubernetes YAML
|
||||
|
||||
All containers in Kubernetes are scheduled as pods, which are groups of co-located containers that share some resources. Furthermore, in a realistic application you almost never create individual pods. Instead, most of your workloads are scheduled as deployments, which are scalable groups of pods maintained automatically by Kubernetes. Lastly, all Kubernetes objects can and should be described in manifests called Kubernetes YAML files. These YAML files describe all the components and configurations of your Kubernetes app, and can be used to create and destroy your app in any Kubernetes environment.
|
||||
|
||||
You already wrote a basic Kubernetes YAML file in the Orchestration overview part of this tutorial. Now, you can write a slightly more sophisticated YAML file to run and manage your Todo app, the container `getting-started` image created in [Part 2](02_our_app.md) of the Quickstart tutorial. Place the following in a file called `bb.yaml`:
|
||||
|
||||
```yaml
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: bb-demo
|
||||
namespace: default
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
bb: web
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
bb: web
|
||||
spec:
|
||||
containers:
|
||||
- name: bb-site
|
||||
image: getting-started
|
||||
imagePullPolicy: Never
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: bb-entrypoint
|
||||
namespace: default
|
||||
spec:
|
||||
type: NodePort
|
||||
selector:
|
||||
bb: web
|
||||
ports:
|
||||
- port: 3000
|
||||
targetPort: 3000
|
||||
nodePort: 30001
|
||||
```
|
||||
|
||||
In this Kubernetes YAML file, there are two objects, separated by the `---`:
|
||||
|
||||
- A `Deployment`, describing a scalable group of identical pods. In this case, you'll get just one `replica`, or copy of your pod, and that pod (which is described under the `template:` key) has just one container in it, based off of your `getting-started` image from the previous step in this tutorial.
|
||||
- A `NodePort` service, which will route traffic from port 30001 on your host to port 3000 inside the pods it routes to, allowing you to reach your Todo app from the network.
|
||||
|
||||
Also, notice that while Kubernetes YAML can appear long and complicated at first, it almost always follows the same pattern:
|
||||
|
||||
- The `apiVersion`, which indicates the Kubernetes API that parses this object
|
||||
- The `kind` indicating what sort of object this is
|
||||
- Some `metadata` applying things like names to your objects
|
||||
- The `spec` specifying all the parameters and configurations of your object.
|
||||
|
||||
## Deploy and check your application
|
||||
|
||||
1. In a terminal, navigate to where you created `bb.yaml` and deploy your application to Kubernetes:
|
||||
|
||||
```console
|
||||
$ kubectl apply -f bb.yaml
|
||||
```
|
||||
|
||||
You should see output that looks like the following, indicating your Kubernetes objects were created successfully:
|
||||
|
||||
```shell
|
||||
deployment.apps/bb-demo created
|
||||
service/bb-entrypoint created
|
||||
```
|
||||
|
||||
2. Make sure everything worked by listing your deployments:
|
||||
|
||||
```console
|
||||
$ kubectl get deployments
|
||||
```
|
||||
|
||||
if all is well, your deployment should be listed as follows:
|
||||
|
||||
```shell
|
||||
NAME READY UP-TO-DATE AVAILABLE AGE
|
||||
bb-demo 1/1 1 1 40s
|
||||
```
|
||||
|
||||
This indicates all one of the pods you asked for in your YAML are up and running. Do the same check for your services:
|
||||
|
||||
```console
|
||||
$ kubectl get services
|
||||
|
||||
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
|
||||
bb-entrypoint NodePort 10.106.145.116 <none> 3000:30001/TCP 53s
|
||||
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 138d
|
||||
```
|
||||
|
||||
In addition to the default `kubernetes` service, we see our `bb-entrypoint` service, accepting traffic on port 30001/TCP.
|
||||
|
||||
3. Open a browser and visit your Todo app at `localhost:30001`. You should see your Todo application, the same as when you ran it as a stand-alone container in [Part 2](02_our_app.md) of the tutorial.
|
||||
|
||||
4. Once satisfied, tear down your application:
|
||||
|
||||
```console
|
||||
$ kubectl delete -f bb.yaml
|
||||
```
|
||||
|
||||
## Conclusion
|
||||
|
||||
At this point, you have successfully used Docker Desktop to deploy your application to a fully-featured Kubernetes environment on your development machine. You can now add other components to your app and taking advantage of all the features and power of Kubernetes, right on your own machine.
|
||||
|
||||
In addition to deploying to Kubernetes, you have also described your application as a Kubernetes YAML file. This simple text file contains everything you need to create your application in a running state. You can check it in to version control and share it with your colleagues. This let you distribute your applications to other clusters (like the testing and production clusters that probably come after your development environments).
|
||||
|
||||
## Kubernetes references
|
||||
|
||||
Further documentation for all new Kubernetes objects used in this article are available here:
|
||||
|
||||
- [Kubernetes Pods](https://kubernetes.io/docs/concepts/workloads/pods/pod/)
|
||||
- [Kubernetes Deployments](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/)
|
||||
- [Kubernetes Services](https://kubernetes.io/docs/concepts/services-networking/service/)
|
||||
@@ -1,323 +0,0 @@
|
||||
---
|
||||
title: Deployment and orchestration
|
||||
keywords: orchestration, deploy, kubernetes, swarm,
|
||||
description: Get oriented on some basics of Docker and install Docker Desktop.
|
||||
aliases:
|
||||
- /get-started/orchestration/
|
||||
- /guides/deployment-orchestration/orchestration/
|
||||
summary: |
|
||||
Explore the essentials of container orchestration with Docker.
|
||||
params:
|
||||
tags: [deployment]
|
||||
time: 10 minutes
|
||||
---
|
||||
|
||||
Containerization provides an opportunity to move and scale applications to
|
||||
clouds and data centers. Containers effectively guarantee that those applications run the
|
||||
same way anywhere, allowing you to quickly and easily take advantage of all
|
||||
these environments. Additionally, as you scale your applications up, you need some
|
||||
tooling to help automate the maintenance of those applications, enable the
|
||||
replacement of failed containers automatically, and manage the roll-out of
|
||||
updates and reconfigurations of those containers during their lifecycle.
|
||||
|
||||
Tools to manage, scale, and maintain containerized applications are called
|
||||
orchestrators. Two of the most popular orchestration tools are Kubernetes and
|
||||
Docker Swarm. Docker Desktop provides development environments for both of these
|
||||
orchestrators.
|
||||
|
||||
The advanced modules teach you how to:
|
||||
|
||||
1. [Set up and use a Kubernetes environment on your development machine](kube-deploy.md)
|
||||
2. [Set up and use a Swarm environment on your development machine](swarm-deploy.md)
|
||||
|
||||
## Turn on Kubernetes
|
||||
|
||||
Docker Desktop sets up Kubernetes for you quickly and easily. Follow the setup and validation instructions appropriate for your operating system:
|
||||
|
||||
{{< tabs group="os" >}}
|
||||
{{< tab name="Mac and Linux" >}}
|
||||
|
||||
### Mac
|
||||
|
||||
1. From the Docker Dashboard, navigate to **Settings**, and select the **Kubernetes** tab.
|
||||
|
||||
2. Select the checkbox labeled **Enable Kubernetes**, and select **Apply**. Docker Desktop automatically sets up Kubernetes for you. You'll know that Kubernetes has been successfully enabled when you see a green light beside 'Kubernetes _running_' in **Settings**.
|
||||
|
||||
3. To confirm that Kubernetes is up and running, create a text file called `pod.yaml` with the following content:
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: demo
|
||||
spec:
|
||||
containers:
|
||||
- name: testpod
|
||||
image: alpine:latest
|
||||
command: ["ping", "8.8.8.8"]
|
||||
```
|
||||
|
||||
This describes a pod with a single container, isolating a simple ping to 8.8.8.8.
|
||||
|
||||
4. In a terminal, navigate to where you created `pod.yaml` and create your pod:
|
||||
|
||||
```console
|
||||
$ kubectl apply -f pod.yaml
|
||||
```
|
||||
|
||||
5. Check that your pod is up and running:
|
||||
|
||||
```console
|
||||
$ kubectl get pods
|
||||
```
|
||||
|
||||
You should see something like:
|
||||
|
||||
```shell
|
||||
NAME READY STATUS RESTARTS AGE
|
||||
demo 1/1 Running 0 4s
|
||||
```
|
||||
|
||||
6. Check that you get the logs you'd expect for a ping process:
|
||||
|
||||
```console
|
||||
$ kubectl logs demo
|
||||
```
|
||||
|
||||
You should see the output of a healthy ping process:
|
||||
|
||||
```shell
|
||||
PING 8.8.8.8 (8.8.8.8): 56 data bytes
|
||||
64 bytes from 8.8.8.8: seq=0 ttl=37 time=21.393 ms
|
||||
64 bytes from 8.8.8.8: seq=1 ttl=37 time=15.320 ms
|
||||
64 bytes from 8.8.8.8: seq=2 ttl=37 time=11.111 ms
|
||||
...
|
||||
```
|
||||
|
||||
7. Finally, tear down your test pod:
|
||||
|
||||
```console
|
||||
$ kubectl delete -f pod.yaml
|
||||
```
|
||||
|
||||
{{< /tab >}}
|
||||
{{< tab name="Windows" >}}
|
||||
|
||||
### Windows
|
||||
|
||||
1. From the Docker Dashboard, navigate to **Settings**, and select the **Kubernetes** tab.
|
||||
|
||||
2. Select the checkbox labeled **Enable Kubernetes**, and select **Apply**. Docker Desktop automatically sets up Kubernetes for you. You'll know that Kubernetes has been successfully enabled when you see a green light beside 'Kubernetes _running_' in the **Settings** menu.
|
||||
|
||||
3. To confirm that Kubernetes is up and running, create a text file called `pod.yaml` with the following content:
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: demo
|
||||
spec:
|
||||
containers:
|
||||
- name: testpod
|
||||
image: alpine:latest
|
||||
command: ["ping", "8.8.8.8"]
|
||||
```
|
||||
|
||||
This describes a pod with a single container, isolating a simple ping to 8.8.8.8.
|
||||
|
||||
4. In PowerShell, navigate to where you created `pod.yaml` and create your pod:
|
||||
|
||||
```console
|
||||
$ kubectl apply -f pod.yaml
|
||||
```
|
||||
|
||||
5. Check that your pod is up and running:
|
||||
|
||||
```console
|
||||
$ kubectl get pods
|
||||
```
|
||||
|
||||
You should see something like:
|
||||
|
||||
```shell
|
||||
NAME READY STATUS RESTARTS AGE
|
||||
demo 1/1 Running 0 4s
|
||||
```
|
||||
|
||||
6. Check that you get the logs you'd expect for a ping process:
|
||||
|
||||
```console
|
||||
$ kubectl logs demo
|
||||
```
|
||||
|
||||
You should see the output of a healthy ping process:
|
||||
|
||||
```shell
|
||||
PING 8.8.8.8 (8.8.8.8): 56 data bytes
|
||||
64 bytes from 8.8.8.8: seq=0 ttl=37 time=21.393 ms
|
||||
64 bytes from 8.8.8.8: seq=1 ttl=37 time=15.320 ms
|
||||
64 bytes from 8.8.8.8: seq=2 ttl=37 time=11.111 ms
|
||||
...
|
||||
```
|
||||
|
||||
7. Finally, tear down your test pod:
|
||||
|
||||
```console
|
||||
$ kubectl delete -f pod.yaml
|
||||
```
|
||||
|
||||
{{< /tab >}}
|
||||
{{< /tabs >}}
|
||||
|
||||
## Enable Docker Swarm
|
||||
|
||||
Docker Desktop runs primarily on Docker Engine, which has everything you need to run a Swarm built in. Follow the setup and validation instructions appropriate for your operating system:
|
||||
|
||||
{{< tabs group="os" >}}
|
||||
{{< tab name="Mac" >}}
|
||||
|
||||
### Mac
|
||||
|
||||
1. Open a terminal, and initialize Docker Swarm mode:
|
||||
|
||||
```console
|
||||
$ docker swarm init
|
||||
```
|
||||
|
||||
If all goes well, you should see a message similar to the following:
|
||||
|
||||
```shell
|
||||
Swarm initialized: current node (tjjggogqpnpj2phbfbz8jd5oq) is now a manager.
|
||||
|
||||
To add a worker to this swarm, run the following command:
|
||||
|
||||
docker swarm join --token SWMTKN-1-3e0hh0jd5t4yjg209f4g5qpowbsczfahv2dea9a1ay2l8787cf-2h4ly330d0j917ocvzw30j5x9 192.168.65.3:2377
|
||||
|
||||
To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.
|
||||
```
|
||||
|
||||
2. Run a simple Docker service that uses an alpine-based filesystem, and isolates a ping to 8.8.8.8:
|
||||
|
||||
```console
|
||||
$ docker service create --name demo alpine:latest ping 8.8.8.8
|
||||
```
|
||||
|
||||
3. Check that your service created one running container:
|
||||
|
||||
```console
|
||||
$ docker service ps demo
|
||||
```
|
||||
|
||||
You should see something like:
|
||||
|
||||
```shell
|
||||
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
|
||||
463j2s3y4b5o demo.1 alpine:latest docker-desktop Running Running 8 seconds ago
|
||||
```
|
||||
|
||||
4. Check that you get the logs you'd expect for a ping process:
|
||||
|
||||
```console
|
||||
$ docker service logs demo
|
||||
```
|
||||
|
||||
You should see the output of a healthy ping process:
|
||||
|
||||
```shell
|
||||
demo.1.463j2s3y4b5o@docker-desktop | PING 8.8.8.8 (8.8.8.8): 56 data bytes
|
||||
demo.1.463j2s3y4b5o@docker-desktop | 64 bytes from 8.8.8.8: seq=0 ttl=37 time=13.005 ms
|
||||
demo.1.463j2s3y4b5o@docker-desktop | 64 bytes from 8.8.8.8: seq=1 ttl=37 time=13.847 ms
|
||||
demo.1.463j2s3y4b5o@docker-desktop | 64 bytes from 8.8.8.8: seq=2 ttl=37 time=41.296 ms
|
||||
...
|
||||
```
|
||||
|
||||
5. Finally, tear down your test service:
|
||||
|
||||
```console
|
||||
$ docker service rm demo
|
||||
```
|
||||
|
||||
{{< /tab >}}
|
||||
{{< tab name="Windows" >}}
|
||||
|
||||
### Windows
|
||||
|
||||
1. Open a PowerShell, and initialize Docker Swarm mode:
|
||||
|
||||
```console
|
||||
$ docker swarm init
|
||||
```
|
||||
|
||||
If all goes well, you should see a message similar to the following:
|
||||
|
||||
```shell
|
||||
Swarm initialized: current node (tjjggogqpnpj2phbfbz8jd5oq) is now a manager.
|
||||
|
||||
To add a worker to this swarm, run the following command:
|
||||
|
||||
docker swarm join --token SWMTKN-1-3e0hh0jd5t4yjg209f4g5qpowbsczfahv2dea9a1ay2l8787cf-2h4ly330d0j917ocvzw30j5x9 192.168.65.3:2377
|
||||
|
||||
To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.
|
||||
```
|
||||
|
||||
2. Run a simple Docker service that uses an alpine-based filesystem, and isolates a ping to 8.8.8.8:
|
||||
|
||||
```console
|
||||
$ docker service create --name demo alpine:latest ping 8.8.8.8
|
||||
```
|
||||
|
||||
3. Check that your service created one running container:
|
||||
|
||||
```console
|
||||
$ docker service ps demo
|
||||
```
|
||||
|
||||
You should see something like:
|
||||
|
||||
```shell
|
||||
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
|
||||
463j2s3y4b5o demo.1 alpine:latest docker-desktop Running Running 8 seconds ago
|
||||
```
|
||||
|
||||
4. Check that you get the logs you'd expect for a ping process:
|
||||
|
||||
```console
|
||||
$ docker service logs demo
|
||||
```
|
||||
|
||||
You should see the output of a healthy ping process:
|
||||
|
||||
```shell
|
||||
demo.1.463j2s3y4b5o@docker-desktop | PING 8.8.8.8 (8.8.8.8): 56 data bytes
|
||||
demo.1.463j2s3y4b5o@docker-desktop | 64 bytes from 8.8.8.8: seq=0 ttl=37 time=13.005 ms
|
||||
demo.1.463j2s3y4b5o@docker-desktop | 64 bytes from 8.8.8.8: seq=1 ttl=37 time=13.847 ms
|
||||
demo.1.463j2s3y4b5o@docker-desktop | 64 bytes from 8.8.8.8: seq=2 ttl=37 time=41.296 ms
|
||||
...
|
||||
```
|
||||
|
||||
5. Finally, tear down your test service:
|
||||
|
||||
```console
|
||||
$ docker service rm demo
|
||||
```
|
||||
|
||||
{{< /tab >}}
|
||||
{{< /tabs >}}
|
||||
|
||||
## Conclusion
|
||||
|
||||
At this point, you've confirmed that you can run simple containerized workloads in Kubernetes and Swarm. The next step is to write a YAML file that describes how to run and manage these containers.
|
||||
|
||||
- [Deploy to Kubernetes](kube-deploy.md)
|
||||
- [Deploy to Swarm](swarm-deploy.md)
|
||||
|
||||
## CLI references
|
||||
|
||||
Further documentation for all CLI commands used in this article are available here:
|
||||
|
||||
- [`kubectl apply`](https://kubernetes.io/docs/reference/generated/kubectl/kubectl-commands#apply)
|
||||
- [`kubectl get`](https://kubernetes.io/docs/reference/generated/kubectl/kubectl-commands#get)
|
||||
- [`kubectl logs`](https://kubernetes.io/docs/reference/generated/kubectl/kubectl-commands#logs)
|
||||
- [`kubectl delete`](https://kubernetes.io/docs/reference/generated/kubectl/kubectl-commands#delete)
|
||||
- [`docker swarm init`](/reference/cli/docker/swarm/init/)
|
||||
- [`docker service *`](/reference/cli/docker/service/)
|
||||
@@ -1,116 +0,0 @@
|
||||
---
|
||||
title: Deploy to Swarm
|
||||
keywords: swarm, swarm services, stacks
|
||||
description: Learn how to describe and deploy a simple application on Docker Swarm.
|
||||
aliases:
|
||||
- /get-started/part4/
|
||||
- /get-started/swarm-deploy/
|
||||
- /guides/deployment-orchestration/swarm-deploy/
|
||||
summary: |
|
||||
Discover how to deploy and manage Docker containers using Docker Swarm.
|
||||
params:
|
||||
tags: [deployment]
|
||||
time: 10 minutes
|
||||
---
|
||||
|
||||
{{% include "swarm-mode.md" %}}
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Download and install Docker Desktop as described in [Get Docker](/get-started/get-docker.md).
|
||||
- Work through containerizing an application in [Docker workshop part 2](/get-started/workshop/02_our_app.md)
|
||||
- Make sure that Swarm is enabled on your Docker Desktop by typing `docker system info`, and looking for a message `Swarm: active` (you might have to scroll up a little).
|
||||
|
||||
If Swarm isn't running, simply type `docker swarm init` in a shell prompt to set it up.
|
||||
|
||||
## Introduction
|
||||
|
||||
Now that you've demonstrated that the individual components of your application run as stand-alone containers and shown how to deploy it using Kubernetes, you can look at how to arrange for them to be managed by Docker Swarm. Swarm provides many tools for scaling, networking, securing and maintaining your containerized applications, above and beyond the abilities of containers themselves.
|
||||
|
||||
In order to validate that your containerized application works well on Swarm, you'll use Docker Desktop's built in Swarm environment right on your development machine to deploy your application, before handing it off to run on a full Swarm cluster in production. The Swarm environment created by Docker Desktop is fully featured, meaning it has all the Swarm features your app will enjoy on a real cluster, accessible from the convenience of your development machine.
|
||||
|
||||
## Describe apps using stack files
|
||||
|
||||
Swarm never creates individual containers like you did in the previous step of this tutorial. Instead, all Swarm workloads are scheduled as services, which are scalable groups of containers with added networking features maintained automatically by Swarm. Furthermore, all Swarm objects can and should be described in manifests called stack files. These YAML files describe all the components and configurations of your Swarm app, and can be used to create and destroy your app in any Swarm environment.
|
||||
|
||||
Now you can write a simple stack file to run and manage your Todo app, the container `getting-started` image created in [Part 2](02_our_app.md) of the tutorial. Place the following in a file called `bb-stack.yaml`:
|
||||
|
||||
{{% include "swarm-compose-compat.md" %}}
|
||||
|
||||
```yaml
|
||||
version: "3.7"
|
||||
|
||||
services:
|
||||
bb-app:
|
||||
image: getting-started
|
||||
ports:
|
||||
- "8000:3000"
|
||||
```
|
||||
|
||||
In this Swarm YAML file, there is one object, a `service`, describing a scalable group of identical containers. In this case, you'll get just one container (the default), and that container will be based on your `getting-started` image created in [Part 2](02_our_app.md) of the tutorial. In addition, you've asked Swarm to forward all traffic arriving at port 8000 on your development machine to port 3000 inside our getting-started container.
|
||||
|
||||
> **Kubernetes Services and Swarm Services are very different**
|
||||
>
|
||||
> Despite the similar name, the two orchestrators mean very different things by
|
||||
> the term 'service'. In Swarm, a service provides both scheduling and
|
||||
> networking facilities, creating containers and providing tools for routing
|
||||
> traffic to them. In Kubernetes, scheduling and networking are handled
|
||||
> separately, deployments (or other controllers) handle the scheduling of
|
||||
> containers as pods, while services are responsible only for adding
|
||||
> networking features to those pods.
|
||||
|
||||
## Deploy and check your application
|
||||
|
||||
1. Deploy your application to Swarm:
|
||||
|
||||
```console
|
||||
$ docker stack deploy -c bb-stack.yaml demo
|
||||
```
|
||||
|
||||
If all goes well, Swarm will report creating all your stack objects with no complaints:
|
||||
|
||||
```shell
|
||||
Creating network demo_default
|
||||
Creating service demo_bb-app
|
||||
```
|
||||
|
||||
Notice that in addition to your service, Swarm also creates a Docker network by default to isolate the containers deployed as part of your stack.
|
||||
|
||||
2. Make sure everything worked by listing your service:
|
||||
|
||||
```console
|
||||
$ docker service ls
|
||||
```
|
||||
|
||||
If all has gone well, your service will report with 1/1 of its replicas created:
|
||||
|
||||
```shell
|
||||
ID NAME MODE REPLICAS IMAGE PORTS
|
||||
il7elwunymbs demo_bb-app replicated 1/1 getting-started:latest *:8000->3000/tcp
|
||||
```
|
||||
|
||||
This indicates 1/1 containers you asked for as part of your services are up and running. Also, you see that port 8000 on your development machine is getting forwarded to port 3000 in your getting-started container.
|
||||
|
||||
3. Open a browser and visit your Todo app at `localhost:8000`; you should see your Todo application, the same as when you ran it as a stand-alone container in [Part 2](02_our_app.md) of the tutorial.
|
||||
|
||||
4. Once satisfied, tear down your application:
|
||||
|
||||
```console
|
||||
$ docker stack rm demo
|
||||
```
|
||||
|
||||
## Conclusion
|
||||
|
||||
At this point, you've successfully used Docker Desktop to deploy your application to a fully-featured Swarm environment on your development machine. You can now add other components to your app and taking advantage of all the features and power of Swarm, right on your own machine.
|
||||
|
||||
In addition to deploying to Swarm, you've also described your application as a stack file. This simple text file contains everything you need to create your application in a running state; you can check it in to version control and share it with your colleagues, letting you to distribute your applications to other clusters (like the testing and production clusters that probably come after your development environments).
|
||||
|
||||
## Swarm and CLI references
|
||||
|
||||
Further documentation for all new Swarm objects and CLI commands used in this article are available here:
|
||||
|
||||
- [Swarm Mode](/manuals/engine/swarm/_index.md)
|
||||
- [Swarm Mode Services](/manuals/engine/swarm/how-swarm-mode-works/services.md)
|
||||
- [Swarm Stacks](/manuals/engine/swarm/stack-deploy.md)
|
||||
- [`docker stack *`](/reference/cli/docker/stack/)
|
||||
- [`docker service *`](/reference/cli/docker/service/)
|
||||
@@ -50,7 +50,7 @@ To install Docker Desktop successfully, you must meet the [general system requir
|
||||
## Next steps
|
||||
|
||||
- Explore [Docker's subscriptions](https://www.docker.com/pricing?ref=Docs&refAction=DocsDesktopArchlinuxInstall) to see what Docker can offer you.
|
||||
- Take a look at the [Docker workshop](/get-started/workshop/_index.md) to learn how to build an image and run it as a containerized application.
|
||||
- Take a look at [Get started with Docker](/get-started/introduction/_index.md) to learn how to build an image and run it as a containerized application.
|
||||
- [Explore Docker Desktop](/manuals/desktop/use-desktop/_index.md) and all its features.
|
||||
- [Troubleshooting](/manuals/desktop/troubleshoot-and-support/troubleshoot/_index.md) describes common problems, workarounds, how to run and submit diagnostics, and submit issues.
|
||||
- [FAQs](/manuals/desktop/troubleshoot-and-support/faqs/general.md) provide answers to frequently asked questions.
|
||||
|
||||
@@ -84,7 +84,7 @@ $ sudo apt-get install ./docker-desktop-amd64.deb
|
||||
## Next steps
|
||||
|
||||
- Explore [Docker's subscriptions](https://www.docker.com/pricing?ref=Docs&refAction=DocsDesktopDebianInstall) to see what Docker can offer you.
|
||||
- Take a look at the [Docker workshop](/get-started/workshop/_index.md) to learn how to build an image and run it as a containerized application.
|
||||
- Take a look at [Get started with Docker](/get-started/introduction/_index.md) to learn how to build an image and run it as a containerized application.
|
||||
- [Explore Docker Desktop](/manuals/desktop/use-desktop/_index.md) and all its features.
|
||||
- [Troubleshooting](/manuals/desktop/troubleshoot-and-support/troubleshoot/_index.md) describes common problems, workarounds, how to run and submit diagnostics, and submit issues.
|
||||
- [FAQs](/manuals/desktop/troubleshoot-and-support/faqs/general.md) provide answers to frequently asked questions.
|
||||
|
||||
@@ -74,7 +74,7 @@ $ sudo dnf install ./docker-desktop-x86_64.rpm
|
||||
## Next steps
|
||||
|
||||
- Explore [Docker's subscriptions](https://www.docker.com/pricing?ref=Docs&refAction=DocsDesktopFedoraInstall) to see what Docker can offer you.
|
||||
- Take a look at the [Docker workshop](/get-started/workshop/_index.md) to learn how to build an image and run it as a containerized application.
|
||||
- Take a look at [Get started with Docker](/get-started/introduction/_index.md) to learn how to build an image and run it as a containerized application.
|
||||
- [Explore Docker Desktop](/manuals/desktop/use-desktop/_index.md) and all its features.
|
||||
- [Troubleshooting](/manuals/desktop/troubleshoot-and-support/troubleshoot/_index.md) describes common problems, workarounds, how to run and submit diagnostics, and submit issues.
|
||||
- [FAQs](/manuals/desktop/troubleshoot-and-support/faqs/general.md) provide answers to frequently asked questions.
|
||||
|
||||
@@ -127,7 +127,7 @@ $ sudo dnf install ./docker-desktop-<arch>-rhel.rpm
|
||||
## Next steps
|
||||
|
||||
- Review [Docker's subscriptions](https://www.docker.com/pricing?ref=Docs&refAction=DocsDesktopRhelInstall) to see what Docker can offer you.
|
||||
- Take a look at the [Docker workshop](/get-started/workshop/_index.md) to learn how to build an image and run it as a containerized application.
|
||||
- Take a look at [Get started with Docker](/get-started/introduction/_index.md) to learn how to build an image and run it as a containerized application.
|
||||
- [Explore Docker Desktop](/manuals/desktop/use-desktop/_index.md) and all its features.
|
||||
- [Troubleshooting](/manuals/desktop/troubleshoot-and-support/troubleshoot/_index.md) describes common problems, workarounds, how to run and submit diagnostics, and submit issues.
|
||||
- [FAQs](/manuals/desktop/troubleshoot-and-support/faqs/general.md) provide answers to frequently asked questions.
|
||||
|
||||
@@ -88,7 +88,7 @@ $ sudo apt install ./docker-desktop-amd64.deb
|
||||
## Next steps
|
||||
|
||||
- Review [Docker's subscriptions](https://www.docker.com/pricing?ref=Docs&refAction=DocsDesktopUbuntuInstall) to see what Docker can offer you.
|
||||
- Follow the [Docker workshop](/get-started/workshop/_index.md) to learn how to build an image and run it as a containerized application.
|
||||
- Follow [Get started with Docker](/get-started/introduction/_index.md) to learn how to build an image and run it as a containerized application.
|
||||
- [Explore Docker Desktop](/manuals/desktop/use-desktop/_index.md) and all its features.
|
||||
- [Troubleshooting](/manuals/desktop/troubleshoot-and-support/troubleshoot/_index.md) describes common problems, workarounds, how to run and submit diagnostics, and submit issues.
|
||||
- [FAQs](/manuals/desktop/troubleshoot-and-support/faqs/general.md) provide answers to frequently asked questions.
|
||||
|
||||
@@ -141,4 +141,4 @@ options:
|
||||
|
||||
## Next steps
|
||||
|
||||
- Take a look at the [Docker workshop](/get-started/workshop/_index.md) to learn how to build an image and run it as a containerized application.
|
||||
- Take a look at [Get started with Docker](/get-started/introduction/_index.md) to learn how to build an image and run it as a containerized application.
|
||||
@@ -25,16 +25,18 @@ If you are brand new to Docker, see [About Docker Engine](../../_index.md).
|
||||
|
||||
To run this tutorial, you need:
|
||||
|
||||
* [Three Linux hosts which can communicate over a network, with Docker installed](#three-networked-host-machines)
|
||||
* [The IP address of the manager machine](#the-ip-address-of-the-manager-machine)
|
||||
* [Open ports between the hosts](#open-protocols-and-ports-between-the-hosts)
|
||||
- [Set up](#set-up)
|
||||
- [Three networked host machines](#three-networked-host-machines)
|
||||
- [Install Docker Engine on Linux machines](#install-docker-engine-on-linux-machines)
|
||||
- [The IP address of the manager machine](#the-ip-address-of-the-manager-machine)
|
||||
- [Open protocols and ports between the hosts](#open-protocols-and-ports-between-the-hosts)
|
||||
- [Next steps](#next-steps)
|
||||
|
||||
### Three networked host machines
|
||||
|
||||
This tutorial requires three Linux hosts which have Docker installed and can
|
||||
communicate over a network. These can be physical machines, virtual machines,
|
||||
Amazon EC2 instances, or hosted in some other way. Check out
|
||||
[Deploy to Swarm](/guides/swarm-deploy.md#prerequisites) for one possible set-up for the hosts.
|
||||
Amazon EC2 instances, or hosted in some other way.
|
||||
|
||||
One of these machines is a manager (called `manager1`) and two of them are
|
||||
workers (`worker1` and `worker2`).
|
||||
|
||||
Reference in new issue
Block a user