└── README.md /README.md: -------------------------------------------------------------------------------- 1 | # Docker Swarm Hands-on Lab 2 | 3 | In this lab, we're going to dig into using Docker Swarm, Docker's native orchestration tool. To learn about Swarm, check out the slides [here](https://somewhere). 4 | 5 | 1. [Setting up VMs](#setting-up-vms) 6 | 2. [Creating the Swarm](#creating-the-swarm) 7 | 3. [(Optional, but recommended) Updating hosts file](#optional-but-recommended-updating-hosts-file) 8 | 4. [(Optional) Start the Visualizer](#optional-start-the-visualizer) 9 | 5. [Deploying a Service](#deploying-a-service) 10 | 6. [Scaling up the App](#scaling-up-the-app) 11 | 7. [Rolling out an update](#rolling-out-an-update) 12 | 8. [Cleaning up](#cleaning-up) 13 | 14 | ## Setting up VMs 15 | 16 | In case you didn't know, when you installed Docker on your machine, a tool named `docker-machine` was also installed! This tool helps make it easy to create your own cluster of nodes directly on your machine. 17 | 18 | 1. If you don't have VirtualBox installed yet, [download](https://www.virtualbox.org/wiki/Downloads) and install it now. 19 | 2. Run the following commands. Each `create` command will take a little while to create the VM, start it, and configure it. But, once it's done, you'll have a machine with Docker installed and ready to go! 20 | 21 | ```bash 22 | docker-machine create -d virtualbox node-1 23 | docker-machine create -d virtualbox node-2 24 | docker-machine create -d virtualbox node-3 25 | docker-machine ls 26 | ``` 27 | 28 | The output from `docker-machine ls` should now look something like this... 29 | 30 | ``` 31 | NAME ACTIVE DRIVER STATE URL SWARM DOCKER ERRORS 32 | node-1 - virtualbox Running tcp://192.168.99.100:2376 v1.12.3 33 | node-2 - virtualbox Running tcp://192.168.99.101:2376 v1.12.3 34 | node-3 - virtualbox Running tcp://192.168.99.102:2376 v1.12.3 35 | ``` 36 | 37 | 38 | ## Creating the Swarm 39 | 40 | Now that we have three VMs ready to go, how do we run Docker commands on them? Docker Machine comes with a command that helps setup our console with various environment variables used to configure Docker. Running `docker-machine env [node-name]`, we get something like this... 41 | 42 | ```bash 43 | export DOCKER_TLS_VERIFY="1" 44 | export DOCKER_HOST="tcp://192.168.99.100:2376" 45 | export DOCKER_CERT_PATH="/Users/mikesir87/.docker/machine/machines/node-1" 46 | export DOCKER_MACHINE_NAME="node-1" 47 | # Run this command to configure your shell: 48 | # eval $(docker-machine env node-1) 49 | ``` 50 | 51 | So, what's going on here? Each environment variable is providing configuration to Docker to connect to a remote daemon (which is running locally), using the TLS certificate at the `DOCKER_CERT_PATH`. Then, all Docker commands will be executed on the remote daemon. 52 | 53 | 1. Run `eval $(docker-machine env node-1)` to setup your environment 54 | 2. Setup `node-1` as a manager for the swarm. The `docker-machine ip [node-name]` commands simply get the IP address for that node, allowing each of the VMs to talk to each other. 55 | 56 | ``` 57 | docker swarm init --advertise-addr $(docker-machine ip node-1) --listen-addr $(docker-machine ip node-1):2377 58 | ``` 59 | 60 | 3. The output from the previous command should include a command like the one below. Copy that to make it easier to enroll the other nodes. 61 | 62 | ```bash 63 | docker swarm join \ 64 | --token some-crazy-long-token \ 65 | 192.168.99.100:2377 66 | ``` 67 | 68 | 4. Enroll the other nodes, using the command from above. If you lose the command or need the enrollment token again, you can use `docker swarm join-token -q worker` while connected to the manager (node-1). 69 | 70 | ```bash 71 | eval $(docker-machine env node-2) 72 | docker swarm join --token some-crazy-long-token 192.168.99.100:2377 73 | 74 | eval $(docker-machine env node-3) 75 | docker swarm join --token some-crazy-long-token 192.168.99.100:2377 76 | ``` 77 | 78 | 5. We can now confirm that the other nodes are enrolled as agents in the cluster... 79 | 80 | ```bash 81 | eval $(docker-machine env node-1) 82 | docker node ls 83 | ``` 84 | 85 | We should see something like this (nodes may not be in any order and IDs will obviously be different)... 86 | 87 | ```bash 88 | ID HOSTNAME MEMBERSHIP STATUS AVAILABILITY MANAGER STATUS 89 | cfnrj7w7pfuf3lpe4f43l31oo * node-1 Ready Active Leader 90 | dwcb99gklxwzx1qp3zem4zcjl node-2 Ready Active 91 | 3fbascszs3xnpbagm2puzy7m3 node-3 Ready Active 92 | ``` 93 | 94 | Running in a production environment, we would want more than one manager. But, for demo purposes (and to prevent you from having to spin up 5 VMs), we'll just do one manager. The manager enrollment process is very similar to enrolling agents into the swarm. 95 | 96 | ## (Optional, but recommended) Updating hosts file 97 | 98 | To make things easier when trying to open the applications we're going to launch modify your `/etc/hosts` (Mac/Linux) or `c:\Windows\System32\Drivers\etc\hosts` (Windows) and put in entries for `node-1`, `node-2`, and `node-3`. 99 | 100 | ``` 101 | 192.168.99.100 node-1 102 | 192.168.99.101 node-2 103 | 192.168.99.102 node-3 104 | ``` 105 | 106 | Of course, be sure your the IP addresses are correct. You can use `docker-machine ip [node-name]` to get the IP address for a specific node. 107 | 108 | 109 | ## (Optional) Start the Visualizer 110 | 111 | If you want to see what's going on as you're spinning up services, scaling up/down, etc., start the following service to get a nice visualizer. 112 | 113 | ```bash 114 | docker service create --name=viz -p=8081:8080 \ 115 | --constraint=node.role==manager \ 116 | --mount=type=bind,src=/var/run/docker.sock,dst=/var/run/docker.sock \ 117 | manomarks/visualizer 118 | ``` 119 | 120 | Now, open up [http://node-1:8081](http://node-1:8081) and you should see the visualizer! 121 | 122 | ![Sample image of nodes with data](https://raw.githubusercontent.com/ManoMarks/docker-swarm-visualizer/master/nodes.png) 123 | 124 | 125 | ## Deploying a Service 126 | 127 | Before we start up a service, we need to create an overlay network that will allow communication between all services, no matter which node they're running on. 128 | 129 | ```bash 130 | docker network create --driver overlay demo 131 | ``` 132 | 133 | If we run `docker network ls`, you should then see something like this... 134 | 135 | ```bash 136 | NETWORK ID NAME DRIVER SCOPE 137 | 3ff7b4f01158 bridge bridge local 138 | 0g34wmgwmmst demo overlay swarm 139 | 0c56ef6f629d docker_gwbridge bridge local 140 | 1939160e0fb7 host host local 141 | cdny2xekiar7 ingress overlay swarm 142 | b324682c7e01 none null local 143 | ``` 144 | 145 | Let's start up a MySQL database service... 146 | 147 | ```bash 148 | docker service create --name db -e MYSQL_ROOT_PASSWORD=p4SSW0rd -e MYSQL_DATABASE=app -e MYSQL_USER=mysql -e MYSQL_PASSWORD=mysql --network demo mysql:5.7 149 | ``` 150 | 151 | The above command will create a new database named `app` with credentials `mysql:mysql`. 152 | 153 | Now, let's add an application! 154 | 155 | ```bash 156 | docker service create --name app --network demo -p 8080:8080 mikesir87/spring-boot-jpa-docker-webapp 157 | ``` 158 | 159 | Watch the visualizer (if you have it running) and wait for the container to enter the _RUNNING_ state. It will probably hang out in the _PREPARING_ state for a while, as it has to download the image and setup the container. 160 | 161 | After it's up, you should be able to open [http://node-1:8080](http://node-1:8080) and see the app! Try going to [http://node-2:8080](http://node-2:8080) and [http://node-3:8080](http://node-3:8080). Any difference? Shouldn't be! 162 | 163 | 164 | ## Scaling up the App 165 | 166 | Our application has started to get a lot of traffic! Let's scale up the number of containers we're running. 167 | 168 | ```bash 169 | docker service scale app=3 170 | ``` 171 | 172 | Check out the visualizer and you'll see that three containers are now running! If you open the app in several different tabs, you should see the hostname at the bottom change, as the app is being served by several containers now! 173 | 174 | 175 | ## Rolling out an update 176 | 177 | Our app is cool and all, but management now wants to ability to add superheroes to the list. Fortunately, our developers are quick and the image is available with the `add-form` tag. So, let's update our service to deploy this updated version. 178 | 179 | ```bash 180 | docker service update --image=mikesir87/spring-boot-jpa-docker-webapp:add-form --update-delay 10s app 181 | ``` 182 | 183 | After running this, if you watch the visualizer, you'll see one task be pulled at a time and a new one deployed. After ten seconds, another task will be pulled and updated. This will continue until all services are updated. 184 | 185 | 186 | ## Cleaning Up 187 | 188 | Not bad for a quick run-through eh? To cleanup, you can do the following... 189 | 190 | ```bash 191 | docker service rm app 192 | docker service rm db 193 | ``` 194 | 195 | If you wish to completely remove the VMs you created, you can run... 196 | 197 | ```bash 198 | docker-machine rm node-1 199 | docker-machine rm node-2 200 | docker-machine rm node-3 201 | ``` 202 | --------------------------------------------------------------------------------