mirror of https://github.com/docker/docs.git
479 lines
19 KiB
Markdown
479 lines
19 KiB
Markdown
---
|
|
title: "Get Started, Part 4: Swarms"
|
|
keywords: swarm, scale, cluster, machine, vm, manager, worker, deploy, ssh, orchestration
|
|
description: Learn how to create clusters of Dockerized machines.
|
|
---
|
|
{% include_relative nav.html selected="4" %}
|
|
|
|
## Prerequisites
|
|
|
|
- [Install Docker version 1.13 or higher](/engine/installation/index.md).
|
|
|
|
- Get [Docker Compose](/compose/overview.md) as described in [Part 3 prerequisites](/get-started/part3.md#prerequisites).
|
|
|
|
- Get [Docker Machine](/machine/overview.md), which is pre-installed with
|
|
[Docker for Mac](/docker-for-mac/index.md) and [Docker for
|
|
Windows](/docker-for-windows/index.md), but on Linux systems you need to
|
|
[install it directly](/machine/install-machine/#installing-machine-directly). On pre Windows 10 systems _without Hyper-V_, as well as Windows 10 Home, use
|
|
[Docker Toolbox](https://docs.docker.com/toolbox/overview.md).
|
|
|
|
- Read the orientation in [Part 1](index.md).
|
|
|
|
- Learn how to create containers in [Part 2](part2.md).
|
|
|
|
- Make sure you have published the `friendlyhello` image you created by
|
|
[pushing it to a registry](/get-started/part2.md#share-your-image). We'll
|
|
be using that shared image here.
|
|
|
|
- Be sure your image works as a deployed container. Run this command,
|
|
slotting in your info for `username`, `repo`, and `tag`: `docker run -p 80:80
|
|
username/repo:tag`, then visit `http://localhost/`.
|
|
|
|
- Have a copy of your `docker-compose.yml` from [Part 3](part3.md) handy.
|
|
|
|
## Introduction
|
|
|
|
In [part 3](part3.md), you took an app you wrote in [part 2](part2.md), and
|
|
defined how it should run in production by turning it into a service, scaling it
|
|
up 5x in the process.
|
|
|
|
Here in part 4, you deploy this application onto a cluster, running it on
|
|
multiple machines. Multi-container, multi-machine applications are made possible
|
|
by joining multiple machines into a "Dockerized" cluster called a **swarm**.
|
|
|
|
## Understanding Swarm clusters
|
|
|
|
A swarm is a group of machines that are running Docker and joined into
|
|
a cluster. After that has happened, you continue to run the Docker commands
|
|
you're used to, but now they are executed on a cluster by a **swarm manager**.
|
|
The machines in a swarm can be physical or virtual. After joining a swarm, they
|
|
are referred to as **nodes**.
|
|
|
|
Swarm managers can use several strategies to run containers, such as "emptiest
|
|
node" -- which fills the least utilized machines with containers. Or "global",
|
|
which ensures that each machine gets exactly one instance of the specified
|
|
container. You instruct the swarm manager to use these strategies in the Compose
|
|
file, just like the one you have already been using.
|
|
|
|
Swarm managers are the only machines in a swarm that can execute your commands,
|
|
or authorize other machines to join the swarm as **workers**. Workers are just
|
|
there to provide capacity and do not have the authority to tell any other
|
|
machine what it can and cannot do.
|
|
|
|
Up until now, you have been using Docker in a single-host mode on your local
|
|
machine. But Docker also can be switched into **swarm mode**, and that's what
|
|
enables the use of swarms. Enabling swarm mode instantly makes the current
|
|
machine a swarm manager. From then on, Docker will run the commands you execute
|
|
on the swarm you're managing, rather than just on the current machine.
|
|
|
|
## Set up your swarm
|
|
|
|
A swarm is made up of multiple nodes, which can be either physical or virtual
|
|
machines. The basic concept is simple enough: run `docker swarm init` to enable
|
|
swarm mode and make your current machine a swarm manager, then run
|
|
`docker swarm join` on other machines to have them join the swarm as workers.
|
|
Choose a tab below to see how this plays out in various contexts. We'll use VMs
|
|
to quickly create a two-machine cluster and turn it into a swarm.
|
|
|
|
### Create a cluster
|
|
|
|
<ul class="nav nav-tabs">
|
|
<li class="active"><a data-toggle="tab" href="#local">Local VMs (Mac, Linux, Windows 7 and 8)</a></li>
|
|
<li><a data-toggle="tab" href="#localwin">Local VMs (Windows 10/Hyper-V)</a></li>
|
|
</ul>
|
|
<div class="tab-content">
|
|
<div id="local" class="tab-pane fade in active">
|
|
{% capture local-content %}
|
|
|
|
#### VMs on your local machine (Mac, Linux, Windows 7 and 8)
|
|
|
|
First, you'll need a hypervisor that can create virtual machines (VMs), so
|
|
[install Oracle VirtualBox](https://www.virtualbox.org/wiki/Downloads) for your
|
|
machine's OS.
|
|
|
|
> **Note**: If you are on a Windows system that has Hyper-V installed,
|
|
such as Windows 10, there is no need to install VirtualBox and you should
|
|
use Hyper-V instead. View the instructions for Hyper-V systems by clicking
|
|
the Hyper-V tab above. If you are using
|
|
[Docker Toolbox](/toolbox/overview.md), you should already have
|
|
VirtualBox installed as part of it, so you are good to go.
|
|
|
|
Now, create a couple of VMs using `docker-machine`, using the VirtualBox driver:
|
|
|
|
```shell
|
|
docker-machine create --driver virtualbox myvm1
|
|
docker-machine create --driver virtualbox myvm2
|
|
```
|
|
|
|
{% endcapture %}
|
|
{{ local-content | markdownify }}
|
|
|
|
</div>
|
|
<div id="localwin" class="tab-pane fade" markdown="1">
|
|
{% capture localwin-content %}
|
|
|
|
#### VMs on your local machine (Windows 10)
|
|
|
|
First, quickly create a virtual switch for your virtual machines (VMs) to share,
|
|
so they will be able to connect to each other.
|
|
|
|
1. Launch Hyper-V Manager
|
|
2. Click **Virtual Switch Manager** in the right-hand menu
|
|
3. Click **Create Virtual Switch** of type **External**
|
|
4. Give it the name `myswitch`, and check the box to share your host machine's
|
|
active network adapter
|
|
|
|
Now, create a couple of VMs using our node management tool,
|
|
`docker-machine`:
|
|
|
|
```shell
|
|
docker-machine create -d hyperv --hyperv-virtual-switch "myswitch" myvm1
|
|
docker-machine create -d hyperv --hyperv-virtual-switch "myswitch" myvm2
|
|
```
|
|
|
|
{% endcapture %}
|
|
{{ localwin-content | markdownify }}
|
|
</div>
|
|
<hr>
|
|
</div>
|
|
|
|
#### List the VMs and get their IP addresses
|
|
|
|
You now have two VMs created, named `myvm1` and `myvm2`.
|
|
|
|
Use this command to list the machines and get their IP addresses.
|
|
|
|
```shell
|
|
docker-machine ls
|
|
```
|
|
|
|
Here is example output from this command.
|
|
|
|
```shell
|
|
$ docker-machine ls
|
|
NAME ACTIVE DRIVER STATE URL SWARM DOCKER ERRORS
|
|
myvm1 - virtualbox Running tcp://192.168.99.100:2376 v17.06.2-ce
|
|
myvm2 - virtualbox Running tcp://192.168.99.101:2376 v17.06.2-ce
|
|
```
|
|
|
|
#### Initialze the swarm and add nodes
|
|
|
|
The first machine will act as the manager, which executes management commands
|
|
and authenticates workers to join the swarm, and the second will be a worker.
|
|
|
|
You can send commands to your VMs using `docker-machine ssh`. Instruct `myvm1`
|
|
to become a swarm manager with `docker swarm init` and you'll see output like
|
|
this:
|
|
|
|
```shell
|
|
$ docker-machine ssh myvm1 "docker swarm init --advertise-addr <myvm1 ip>"
|
|
Swarm initialized: current node <node ID> is now a manager.
|
|
|
|
To add a worker to this swarm, run the following command:
|
|
|
|
docker swarm join \
|
|
--token <token> \
|
|
<myvm ip>:<port>
|
|
|
|
To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.
|
|
```
|
|
|
|
> Ports 2377 and 2376
|
|
>
|
|
> Always run `docker swarm init` and `docker swarm join` with port 2377
|
|
> (the swarm management port), or no port at all and let it take the default.
|
|
>
|
|
> The machine IP addresses returned by `docker-machine ls` include port 2376,
|
|
> which is the Docker daemon port. Do not use this port or
|
|
> [you may experience errors](https://forums.docker.com/t/docker-swarm-join-with-virtualbox-connection-error-13-bad-certificate/31392/2){: target="_blank" class="_"}.
|
|
|
|
|
|
As you can see, the response to `docker swarm init` contains a pre-configured
|
|
`docker swarm join` command for you to run on any nodes you want to add. Copy
|
|
this command, and send it to `myvm2` via `docker-machine ssh` to have `myvm2`
|
|
join your new swarm as a worker:
|
|
|
|
```shell
|
|
$ docker-machine ssh myvm2 "docker swarm join \
|
|
--token <token> \
|
|
<ip>:2377"
|
|
|
|
This node joined a swarm as a worker.
|
|
```
|
|
|
|
Congratulations, you have created your first swarm!
|
|
|
|
Run `docker node ls` on the manager to view the nodes in this swarm:
|
|
|
|
```shell
|
|
$ docker-machine ssh myvm1 "docker node ls"
|
|
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS
|
|
brtu9urxwfd5j0zrmkubhpkbd myvm2 Ready Active
|
|
rihwohkh3ph38fhillhhb84sk * myvm1 Ready Active Leader
|
|
```
|
|
|
|
> Leaving a swarm
|
|
>
|
|
> If you want to start over, you can run `docker swarm leave` from each node.
|
|
|
|
## Deploy your app on the swarm cluster
|
|
|
|
The hard part is over. Now you just repeat the process you used in [part
|
|
3](part3.md) to deploy on your new swarm. Just remember that only swarm managers
|
|
like `myvm1` execute Docker commands; workers are just for capacity.
|
|
|
|
### Configure a `docker-machine` shell to the swarm manager
|
|
|
|
So far, you've been wrapping Docker commmands in `docker-machine ssh` to talk to
|
|
the VMs. Another option is to run `docker-machine env <machine>` to get
|
|
and run a command that configures your current shell to talk to the Docker
|
|
daemon on the VM. This method works better for the next step because it allows
|
|
you to use your local `docker-compose.yml` file to deploy the app
|
|
"remotely" without having to copy it anywhere.
|
|
|
|
Type `docker-machine env myvm1`, then copy-paste and run the command provided as
|
|
the last line of the output to configure your shell to talk to `myvm1`, the
|
|
swarm manager.
|
|
|
|
The commands to configure your shell differ depending on whether you are Mac,
|
|
Linux, or Windows, so examples of each are shown on the tabs below.
|
|
|
|
<ul class="nav nav-tabs">
|
|
<li class="active"><a data-toggle="tab" href="#mac-linux-machine">Mac, Linux</a></li>
|
|
<li><a data-toggle="tab" href="#win-machine">Windows</a></li>
|
|
</ul>
|
|
<div class="tab-content">
|
|
<div id="mac-linux-machine" class="tab-pane fade in active">
|
|
{% capture mac-linux-machine-content %}
|
|
|
|
#### Docker machine shell environment on Mac or Linux
|
|
|
|
Run `docker-machine env myvm1` to get the command to configure your shell to
|
|
talk to `myvm1`.
|
|
|
|
```shell
|
|
$ docker-machine env myvm1
|
|
export DOCKER_TLS_VERIFY="1"
|
|
export DOCKER_HOST="tcp://192.168.99.100:2376"
|
|
export DOCKER_CERT_PATH="/Users/sam/.docker/machine/machines/myvm1"
|
|
export DOCKER_MACHINE_NAME="myvm1"
|
|
# Run this command to configure your shell:
|
|
# eval $(docker-machine env myvm1)
|
|
```
|
|
|
|
Run the given command to configure your shell to talk to `myvm1`.
|
|
|
|
```shell
|
|
eval $(docker-machine env myvm1)
|
|
```
|
|
|
|
Run `docker-machine ls` to verify that `myvm1` is now the active machine, as
|
|
indicated by the asterisk next to it.
|
|
|
|
```shell
|
|
$ docker-machine ls
|
|
NAME ACTIVE DRIVER STATE URL SWARM DOCKER ERRORS
|
|
myvm1 * virtualbox Running tcp://192.168.99.100:2376 v17.06.2-ce
|
|
myvm2 - virtualbox Running tcp://192.168.99.101:2376 v17.06.2-ce
|
|
```
|
|
|
|
{% endcapture %}
|
|
{{ mac-linux-machine-content | markdownify }}
|
|
|
|
</div>
|
|
<div id="win-machine" class="tab-pane fade">
|
|
{% capture win-machine-content %}
|
|
|
|
#### Docker machine shell environment on Windows
|
|
|
|
Run `docker-machine env myvm1` to get the command to configure your shell to
|
|
talk to `myvm1`.
|
|
|
|
```shell
|
|
PS C:\Users\sam\sandbox\get-started> docker-machine env myvm1
|
|
$Env:DOCKER_TLS_VERIFY = "1"
|
|
$Env:DOCKER_HOST = "tcp://192.168.203.207:2376"
|
|
$Env:DOCKER_CERT_PATH = "C:\Users\sam\.docker\machine\machines\myvm1"
|
|
$Env:DOCKER_MACHINE_NAME = "myvm1"
|
|
$Env:COMPOSE_CONVERT_WINDOWS_PATHS = "true"
|
|
# Run this command to configure your shell:
|
|
# & "C:\Program Files\Docker\Docker\Resources\bin\docker-machine.exe" env myvm1 | Invoke-Expression
|
|
```
|
|
|
|
Run the given command to configure your shell to talk to `myvm1`.
|
|
|
|
```shell
|
|
& "C:\Program Files\Docker\Docker\Resources\bin\docker-machine.exe" env myvm1 | Invoke-Expression
|
|
```
|
|
|
|
Run `docker-machine ls` to verify that `myvm1` is the active machine as indicated by the asterisk next to it.
|
|
|
|
```shell
|
|
PS C:PATH> docker-machine ls
|
|
NAME ACTIVE DRIVER STATE URL SWARM DOCKER ERRORS
|
|
myvm1 * hyperv Running tcp://192.168.203.207:2376 v17.06.2-ce
|
|
myvm2 - hyperv Running tcp://192.168.200.181:2376 v17.06.2-ce
|
|
```
|
|
|
|
{% endcapture %}
|
|
{{ win-machine-content | markdownify }}
|
|
</div>
|
|
<hr>
|
|
</div>
|
|
|
|
### Deploy the app on the swarm manager
|
|
|
|
Now that you have my `myvm1`, you can use its powers as a swarm manager to
|
|
deploy your app by using the same `docker stack deploy` command you used in part
|
|
3 to `myvm1`, and your local copy of `docker-stack.yml.`
|
|
|
|
You are connected to `myvm1` by means of the `docker-machine` shell
|
|
configuration, and you still have access to the files on your local host. Make
|
|
sure you are in the same directory as before, which includes the
|
|
[`docker-compose.yml` file you created in part
|
|
3](/get-started/part3/#docker-composeyml).
|
|
|
|
Just like before, run the following command to deploy the app on `myvm1`.
|
|
|
|
```
|
|
docker stack deploy -c docker-compose.yml getstartedlab
|
|
```
|
|
|
|
And that's it, the app is deployed on a swarm cluster!
|
|
|
|
Now you can use the same [docker commands you used in part
|
|
3](/get-started/part3.md#run-your-new-load-balanced-app). Only this time you'll
|
|
see that the containers have been distributed between both `myvm1` and `myvm2`.
|
|
|
|
|
|
```
|
|
$ docker stack ps getstartedlab
|
|
|
|
ID NAME IMAGE NODE DESIRED STATE
|
|
jq2g3qp8nzwx test_web.1 username/repo:tag myvm1 Running
|
|
88wgshobzoxl test_web.2 username/repo:tag myvm2 Running
|
|
vbb1qbkb0o2z test_web.3 username/repo:tag myvm2 Running
|
|
ghii74p9budx test_web.4 username/repo:tag myvm1 Running
|
|
0prmarhavs87 test_web.5 username/repo:tag myvm2 Running
|
|
```
|
|
|
|
> Connecting to VMs with `docker-machine env` and `docker-machine ssh`
|
|
>
|
|
> * To set your shell to talk to a different machine like `myvm2`, simply re-run
|
|
`docker-machine env` in the same or a different shell, then run the given
|
|
command to point to `myvm2`. This is always specific to the current shell. If
|
|
you change to an unconfigured shell or open a new one, you need to re-run the
|
|
commands. Use `docker-machine ls` to list machines, see what state they are in,
|
|
get IP addresses, and find out which one, if any, you are connected to. To learn
|
|
more, see the [Docker Machine getting started topics](/machine/get-started.md#create-a-machine).
|
|
>
|
|
> * Alternatively, you can wrap Docker commands in the form of
|
|
`docker-machine ssh <machine> "<command>"`, which logs directly into
|
|
the VM but doesn't give you immediate access to files on your local host.
|
|
>
|
|
> * On Mac and Linux, you can use `docker-machine scp <file> <machine>:~`
|
|
to copy files across machines, but Windows users need a Linux terminal emulator
|
|
like [Git Bash](https://git-for-windows.github.io/){: target="_blank" class="_"} in order for this to work.
|
|
>
|
|
> This tutorial demos both `docker-machine ssh` and
|
|
`docker-machine env`, since these are available on all platforms via the `docker-machine` CLI.
|
|
|
|
### Accessing your cluster
|
|
|
|
You can access your app from the IP address of **either** `myvm1` or `myvm2`.
|
|
|
|
The network you created is shared between them and load-balancing. Run
|
|
`docker-machine ls` to get your VMs' IP addresses and visit either of them on a
|
|
browser, hitting refresh (or just `curl` them).
|
|
|
|

|
|
|
|
You'll see five possible container IDs all cycling by randomly, demonstrating
|
|
the load-balancing.
|
|
|
|
The reason both IP addresses work is that nodes in a swarm participate in an
|
|
ingress **routing mesh**. This ensures that a service deployed at a certain port
|
|
within your swarm always has that port reserved to itself, no matter what node
|
|
is actually running the container. Here's a diagram of how a routing mesh for a
|
|
service called `my-web` published at port `8080` on a three-node swarm would
|
|
look:
|
|
|
|

|
|
|
|
> Having connectivity trouble?
|
|
>
|
|
> Keep in mind that in order to use the ingress network in the swarm,
|
|
> you need to have the following ports open between the swarm nodes
|
|
> before you enable swarm mode:
|
|
>
|
|
> - Port 7946 TCP/UDP for container network discovery.
|
|
> - Port 4789 UDP for the container ingress network.
|
|
|
|
## Iterating and scaling your app
|
|
|
|
From here you can do everything you learned about in part 3.
|
|
|
|
Scale the app by changing the `docker-compose.yml` file.
|
|
|
|
Change the app behavior by editing code.
|
|
|
|
In either case, simply run `docker stack deploy` again to deploy these
|
|
changes.
|
|
|
|
You can join any machine, physical or virtual, to this swarm, using the
|
|
same `docker swarm join` command you used on `myvm2`, and capacity will be added
|
|
to your cluster. Just run `docker stack deploy` afterwards, and your app will
|
|
take advantage of the new resources.
|
|
|
|
## Cleanup
|
|
|
|
You can tear down the stack with `docker stack rm`. For example:
|
|
|
|
```
|
|
docker stack rm getstartedlab
|
|
```
|
|
|
|
> Keep the swarm or remove it?
|
|
>
|
|
> At some point later, you can remove this swarm if you want to with
|
|
> `docker-machine ssh myvm2 "docker swarm leave"` on the worker
|
|
> and `docker-machine ssh myvm1 "docker swarm leave --force"` on the
|
|
> manager, but _you'll need this swarm for part 5, so please keep it
|
|
> around for now_.
|
|
|
|
[On to Part 5 >>](part5.md){: class="button outline-btn"}
|
|
|
|
## Recap and cheat sheet (optional)
|
|
|
|
Here's [a terminal recording of what was covered on this
|
|
page](https://asciinema.org/a/113837):
|
|
|
|
<script type="text/javascript" src="https://asciinema.org/a/113837.js" id="asciicast-113837" speed="2" async></script>
|
|
|
|
In part 4 you learned what a swarm is, how nodes in swarms can be managers or
|
|
workers, created a swarm, and deployed an application on it. You saw that the
|
|
core Docker commands didn't change from part 3, they just had to be targeted to
|
|
run on a swarm master. You also saw the power of Docker's networking in action,
|
|
which kept load-balancing requests across containers, even though they were
|
|
running on different machines. Finally, you learned how to iterate and scale
|
|
your app on a cluster.
|
|
|
|
Here are some commands you might like to run to interact with your swarm a bit:
|
|
|
|
```shell
|
|
docker-machine create --driver virtualbox myvm1 # Create a VM (Mac, Win7, Linux)
|
|
docker-machine create -d hyperv --hyperv-virtual-switch "myswitch" myvm1 # Win10
|
|
docker-machine env myvm1 # View basic information about your node
|
|
docker-machine ssh myvm1 "docker node ls" # List the nodes in your swarm
|
|
docker-machine ssh myvm1 "docker node inspect <node ID>" # Inspect a node
|
|
docker-machine ssh myvm1 "docker swarm join-token -q worker" # View join token
|
|
docker-machine ssh myvm1 # Open an SSH session with the VM; type "exit" to end
|
|
docker-machine ssh myvm2 "docker swarm leave" # Make the worker leave the swarm
|
|
docker-machine ssh myvm1 "docker swarm leave -f" # Make master leave, kill swarm
|
|
docker-machine start myvm1 # Start a VM that is currently not running
|
|
docker-machine stop $(docker-machine ls -q) # Stop all running VMs
|
|
docker-machine rm $(docker-machine ls -q) # Delete all VMs and their disk images
|
|
docker-machine scp docker-compose.yml myvm1:~ # Copy file to node's home dir
|
|
docker-machine ssh myvm1 "docker stack deploy -c <file> <app>" # Deploy an app
|
|
```
|