├── .bashrc ├── .gitignore ├── README.md ├── compose └── consul │ ├── agent.yml │ ├── config │ └── consul.json │ └── server.yml ├── drivers └── digitalocean.env ├── media └── docker-consul-swarm-with-edge.png └── scripts ├── setup-machines.sh ├── setup-swarm.sh └── teardown-swarm.sh /.bashrc: -------------------------------------------------------------------------------- 1 | export MACHINE_STORAGE_PATH="$( cd "$(dirname "${BASH_SOURCE:-$0}")" ; pwd -P )" 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### 2 | certs/ 3 | machines/ 4 | 5 | ### Eclipse template 6 | *.pydevproject 7 | .metadata 8 | .gradle 9 | bin/ 10 | tmp/ 11 | *.tmp 12 | *.bak 13 | *.swp 14 | *~.nib 15 | local.properties 16 | .settings/ 17 | .loadpath 18 | 19 | # Eclipse Core 20 | .project 21 | 22 | # External tool builders 23 | .externalToolBuilders/ 24 | 25 | # Locally stored "Eclipse launch configurations" 26 | *.launch 27 | 28 | # CDT-specific 29 | .cproject 30 | 31 | # JDT-specific (Eclipse Java Development Tools) 32 | .classpath 33 | 34 | # Java annotation processor (APT) 35 | .factorypath 36 | 37 | # PDT-specific 38 | .buildpath 39 | 40 | # sbteclipse plugin 41 | .target 42 | 43 | # TeXlipse plugin 44 | .texlipse 45 | ### Go template 46 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 47 | *.o 48 | *.a 49 | *.so 50 | 51 | # Folders 52 | _obj 53 | _test 54 | 55 | # Architecture specific extensions/prefixes 56 | *.[568vq] 57 | [568vq].out 58 | 59 | *.cgo1.go 60 | *.cgo2.c 61 | _cgo_defun.c 62 | _cgo_gotypes.go 63 | _cgo_export.* 64 | 65 | _testmain.go 66 | 67 | *.exe 68 | *.test 69 | *.prof 70 | ### Windows template 71 | # Windows image file caches 72 | Thumbs.db 73 | ehthumbs.db 74 | 75 | # Folder config file 76 | Desktop.ini 77 | 78 | # Recycle Bin used on file shares 79 | $RECYCLE.BIN/ 80 | 81 | # Windows Installer files 82 | *.cab 83 | *.msi 84 | *.msm 85 | *.msp 86 | 87 | # Windows shortcuts 88 | *.lnk 89 | ### NetBeans template 90 | nbproject/private/ 91 | build/ 92 | nbbuild/ 93 | dist/ 94 | nbdist/ 95 | nbactions.xml 96 | nb-configuration.xml 97 | .nb-gradle/ 98 | ### Linux template 99 | *~ 100 | 101 | # KDE directory preferences 102 | .directory 103 | 104 | # Linux trash folder which might appear on any partition or disk 105 | .Trash-* 106 | ### JetBrains template 107 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio 108 | 109 | *.iml 110 | 111 | ## Directory-based project format: 112 | .idea/ 113 | # if you remove the above rule, at least ignore the following: 114 | 115 | # User-specific stuff: 116 | # .idea/workspace.xml 117 | # .idea/tasks.xml 118 | # .idea/dictionaries 119 | 120 | # Sensitive or high-churn files: 121 | # .idea/dataSources.ids 122 | # .idea/dataSources.xml 123 | # .idea/sqlDataSources.xml 124 | # .idea/dynamic.xml 125 | # .idea/uiDesigner.xml 126 | 127 | # Gradle: 128 | # .idea/gradle.xml 129 | # .idea/libraries 130 | 131 | # Mongo Explorer plugin: 132 | # .idea/mongoSettings.xml 133 | 134 | ## File-based project format: 135 | *.ipr 136 | *.iws 137 | 138 | ## Plugin-specific files: 139 | 140 | # IntelliJ 141 | /out/ 142 | 143 | # mpeltonen/sbt-idea plugin 144 | .idea_modules/ 145 | 146 | # JIRA plugin 147 | atlassian-ide-plugin.xml 148 | 149 | # Crashlytics plugin (for Android Studio and IntelliJ) 150 | com_crashlytics_export_strings.xml 151 | crashlytics.properties 152 | crashlytics-build.properties 153 | ### OSX template 154 | .DS_Store 155 | .AppleDouble 156 | .LSOverride 157 | 158 | # Icon must end with two \r 159 | Icon 160 | 161 | # Thumbnails 162 | ._* 163 | 164 | # Files that might appear in the root of a volume 165 | .DocumentRevisions-V100 166 | .fseventsd 167 | .Spotlight-V100 168 | .TemporaryItems 169 | .Trashes 170 | .VolumeIcon.icns 171 | 172 | # Directories potentially created on remote AFP share 173 | .AppleDB 174 | .AppleDesktop 175 | Network Trash Folder 176 | Temporary Items 177 | .apdisk 178 | 179 | 180 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [Toward a Production-Ready Docker Swarm Cluster with Consul] (https://medium.com/@dweomer/toward-a-production-ready-docker-swarm-cluster-with-consul-9ecd36533bb8) 2 | > Wherein I explore the requirements of, and develop a repeatable process for, standing up a moderately opinionated, production-ready Docker cluster using the community standard Engine, Machine, Swarm and Compose. HashiCorp Consul will be used as the key-value store for Swarm as well as providing a common discovery mechanism across all nodes. 3 | 4 | ## Guiding Principles 5 | * Highly available and fault-tolerant key-value store 6 | * Highly available Swarm masters 7 | * Overlay networking 8 | * Repeatable, automation-ready setup 9 | * All cluster and node services delivered as containers 10 | * Smarter-than-default logging 11 | * Memory accounting configured in kernel 12 | 13 | ## Getting Started 14 | Initially this project is setup to support only Digital Ocean droplets running Ubuntu 15.10. It should be trivial to support other virtualization providers. See [drivers/digitalocean.env](drivers/digitalocean.env) 15 | 16 | ### Install Docker Engine, Compose, and Machine 17 | See: 18 | * [docker-engine v1.10.2](https://github.com/docker/docker/releases/tag/v1.10.2) downloads. 19 | * [docker-compose v1.6.2](https://github.com/docker/compose/releases/tag/1.6.2) installation instructions. 20 | * [docker-machine v0.6.0](https://github.com/docker/machine/releases/tag/v0.6.0) installation instructions. 21 | 22 | ### Digital Ocean API Access Token 23 | Mosey on over to [Digital Ocean](https://cloud.digitalocean.com/settings/api/tokens) and setup an access token called `docker-swarm-consul` (or whatever floats your boat) and create a rcfile named `~/.digitalocean/docker-swarm-consul` with content that looks something like this: 24 | ``` 25 | export DIGITALOCEAN_ACCESS_TOKEN="my-super-cool-access-token-hash" 26 | ``` 27 | This will be picked up automatically by [drivers/digitalocean.env](drivers/digitalocean.env) and sourced. If no such file exists you will want to otherwise setup an environment variable named `DIGITALOCEAN_ACCESS_TOKEN` before these humble scripts will work. 28 | 29 | -- 30 | 31 | ![A Docker Swarm cluster leveraging Consul](media/docker-consul-swarm-with-edge.png) 32 | -------------------------------------------------------------------------------- /compose/consul/agent.yml: -------------------------------------------------------------------------------- 1 | server: 2 | command: -dc ${MACHINE_DATACENTER} -domain ${MACHINE_DOMAIN}. -advertise ${MACHINE_CLUSTER_PARTICIPANT_ADDRESS} -retry-interval 10s -retry-join ${MACHINE_CLUSTER_REPLICANT_ADDRESS_0} -retry-join ${MACHINE_CLUSTER_REPLICANT_ADDRESS_1} -retry-join ${MACHINE_CLUSTER_REPLICANT_ADDRESS_2} 3 | container_name: consul-agent 4 | environment: 5 | - "GOMAXPROCS=2" 6 | image: gliderlabs/consul-agent:0.6 7 | net: host 8 | restart: always 9 | volumes: 10 | - "consul-agent-${MACHINE_NAME}:/data" 11 | - "/etc/consul/consul.json:/config/consul.json:ro" 12 | - "/etc/docker/ca.pem:/certs/ca.pem:ro" 13 | - "/etc/docker/server.pem:/certs/server.pem:ro" 14 | - "/etc/docker/server-key.pem:/certs/server-key.pem:ro" 15 | - "/var/run/docker.sock:/var/run/docker.sock" 16 | -------------------------------------------------------------------------------- /compose/consul/config/consul.json: -------------------------------------------------------------------------------- 1 | { 2 | "ca_file": "/certs/ca.pem", 3 | "cert_file": "/certs/server.pem", 4 | "key_file": "/certs/server-key.pem", 5 | 6 | "ports": { 7 | "dns": 53 8 | }, 9 | 10 | "recursors": [ 11 | "8.8.8.8", 12 | "8.8.4.4" 13 | ], 14 | 15 | "verify_incoming": true, 16 | "verify_outgoing": true 17 | } 18 | -------------------------------------------------------------------------------- /compose/consul/server.yml: -------------------------------------------------------------------------------- 1 | server: 2 | command: -dc ${MACHINE_DATACENTER} -server -bootstrap-expect 3 -domain ${MACHINE_DOMAIN}. -advertise ${MACHINE_CLUSTER_PARTICIPANT_ADDRESS} -retry-interval 10s -retry-join ${MACHINE_CLUSTER_REPLICANT_ADDRESS_1} -retry-join ${MACHINE_CLUSTER_REPLICANT_ADDRESS_2} 3 | container_name: consul-agent-server 4 | environment: 5 | - "GOMAXPROCS=2" 6 | image: gliderlabs/consul-server:0.6 7 | net: host 8 | restart: always 9 | volumes: 10 | - "consul-agent-${MACHINE_NAME}:/data" 11 | - "/etc/consul/consul.json:/config/consul.json:ro" 12 | - "/etc/docker/ca.pem:/certs/ca.pem:ro" 13 | - "/etc/docker/server.pem:/certs/server.pem:ro" 14 | - "/etc/docker/server-key.pem:/certs/server-key.pem:ro" 15 | - "/var/run/docker.sock:/var/run/docker.sock" 16 | -------------------------------------------------------------------------------- /drivers/digitalocean.env: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | : ${DIGITALOCEAN_ENVFILE:=~/.digitalocean/docker-swarm-consul} 4 | 5 | if [ -z ${DIGITALOCEAN_ACCESS_TOKEN} ] && [ -f ${DIGITALOCEAN_ENVFILE} ]; then 6 | . ${DIGITALOCEAN_ENVFILE} 7 | fi 8 | 9 | #: ${DIGITALOCEAN_BACKUPS:=true}; export DIGITALOCEAN_BACKUPS 10 | : ${DIGITALOCEAN_IMAGE:=ubuntu-15-10-x64}; export DIGITALOCEAN_IMAGE 11 | #: ${DIGITALOCEAN_IPV6:=true}; export DIGITALOCEAN_IPV6 12 | : ${DIGITALOCEAN_PRIVATE_NETWORKING:=true}; export DIGITALOCEAN_PRIVATE_NETWORKING 13 | : ${DIGITALOCEAN_REGION:=sfo1}; export DIGITALOCEAN_REGION 14 | : ${DIGITALOCEAN_SIZE:=512mb}; export DIGITALOCEAN_SIZE 15 | : ${DIGITALOCEAN_SSH_PORT:=22}; export DIGITALOCEAN_SSH_PORT 16 | : ${DIGITALOCEAN_SSH_USER:=root}; export DIGITALOCEAN_SSH_USER 17 | #: ${DIGITALOCEAN_USERDATA:=}; export DIGITALOCEAN_USERDATA 18 | 19 | : ${MACHINE_CLUSTER_INTERFACE:=eth1}; export MACHINE_CLUSTER_INTERFACE 20 | : ${MACHINE_DATACENTER:=${DIGITALOCEAN_REGION}}; export MACHINE_DATACENTER 21 | : ${MACHINE_SSH_USER:=${DIGITALOCEAN_SSH_USER}}; export MACHINE_SSH_USER 22 | -------------------------------------------------------------------------------- /media/docker-consul-swarm-with-edge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dweomer/docker-swarm-consul/844ff9f76f52b3d02a92f6c59ad7584869edd11c/media/docker-consul-swarm-with-edge.png -------------------------------------------------------------------------------- /scripts/setup-machines.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | : ${MACHINE_STORAGE_PATH?required} 4 | : ${MACHINE_DRIVER:=digitalocean} 5 | 6 | . ${MACHINE_STORAGE_PATH}/drivers/${MACHINE_DRIVER}.env 7 | 8 | : ${MACHINE_CLUSTER_INTERFACE?required} 9 | 10 | : ${MACHINE_DOMAIN:=example.com} 11 | 12 | : ${MACHINE_BRIDGE_ADDRESS:=172.17.0.1} 13 | : ${MACHINE_BRIDGE_INTERFACE:=docker0} 14 | 15 | : ${MACHINE_COUNT:=${1:-7}} 16 | : ${MACHINE_PREFIX:=doccur} 17 | 18 | declare -a MACHINE_NAMES 19 | declare -a MACHINE_CLUSTER_ADDRESSES 20 | declare -a MACHINE_PUBLIC_ADDRESSES 21 | 22 | for i in $(seq 1 ${MACHINE_COUNT}); do 23 | MACHINE_NAME="${MACHINE_PREFIX}${i}" 24 | MACHINE_NAMES+=(${MACHINE_NAME}) 25 | MACHINE_NODE_NAME="${MACHINE_DRIVER}${MACHINE_NAME}" 26 | 27 | if [ ! -d ${MACHINE_STORAGE_PATH}/machines/${MACHINE_NODE_NAME} ]; then 28 | echo "### ${MACHINE_NODE_NAME} >>>" 29 | docker-machine ${MACHINE_OPTS} create --driver ${MACHINE_DRIVER} ${MACHINE_NODE_NAME} 2>&1 | while read line; do 30 | echo "# ${line}" 31 | done 32 | echo "### ${MACHINE_NODE_NAME} <<<" 33 | fi 34 | 35 | MACHINE_PUBLIC_ADDRESSES+=($(docker-machine inspect --format '{{.Driver.IPAddress}}' ${MACHINE_NODE_NAME})) 36 | MACHINE_CLUSTER_ADDRESSES+=($(docker-machine ${MACHINE_OPTS} ssh ${MACHINE_NODE_NAME} "echo \$(ip addr show ${MACHINE_CLUSTER_INTERFACE} | head -3) | sed -e 's/.*[:] \(.*\)[:] .* inet \([0-9]*[.][0-9]*[.][0-9]*[.][0-9]*\)[/].*/\2/g'")) 37 | done 38 | 39 | declare -x MACHINE_STORAGE_PATH MACHINE_DRIVER MACHINE_DOMAIN MACHINE_DATACENTER MACHINE_BRIDGE_ADDRESS MACHINE_BRIDGE_INTERFACE MACHINE_CLUSTER_INTERFACE MACHINE_NAMES MACHINE_CLUSTER_ADDRESSES MACHINE_PUBLIC_ADDRESSES 40 | declare -p MACHINE_STORAGE_PATH MACHINE_DRIVER MACHINE_DOMAIN MACHINE_DATACENTER MACHINE_BRIDGE_ADDRESS MACHINE_BRIDGE_INTERFACE MACHINE_CLUSTER_INTERFACE MACHINE_NAMES MACHINE_CLUSTER_ADDRESSES MACHINE_PUBLIC_ADDRESSES 41 | -------------------------------------------------------------------------------- /scripts/setup-swarm.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | : ${MACHINE_STORAGE_PATH?required} 4 | 5 | MTEMP=$(mktemp) 6 | ${MACHINE_STORAGE_PATH}/scripts/setup-machines.sh > ${MTEMP} 7 | . ${MTEMP} 8 | 9 | : ${MACHINE_DATACENTER?required} 10 | 11 | docker_machine_recreate() { 12 | MACHINE_TYPE=${1?required} 13 | 14 | [ ${MACHINE_TYPE} = 'server' ] && local MACHINE_CREATE_OPTS="${MACHINE_CREATE_OPTS} 15 | --swarm-master 16 | --swarm-opt replication 17 | --swarm-opt advertise=${MACHINE_CLUSTER_PARTICIPANT_ADDRESS}:3376 18 | " 19 | 20 | : ${MACHINE_NAME?required} 21 | : ${MACHINE_DOMAIN?required} 22 | : ${MACHINE_SSH_KEY?required} 23 | : ${MACHINE_SSH_USER?required} 24 | : ${MACHINE_BRIDGE_ADDRESS?required} 25 | : ${MACHINE_BRIDGE_INTERFACE?required} 26 | : ${MACHINE_CLUSTER_CONSUL:="consul.service.${MACHINE_DOMAIN}:8500"} 27 | : ${MACHINE_CLUSTER_INTERFACE?required} 28 | : ${MACHINE_CLUSTER_PARTICIPANT_ADDRESS?required} 29 | : ${MACHINE_CLUSTER_REPLICANT_ADDRESS_0?required} 30 | : ${MACHINE_CLUSTER_REPLICANT_ADDRESS_1?required} 31 | : ${MACHINE_CLUSTER_REPLICANT_ADDRESS_2?required} 32 | 33 | export MACHINE_CLUSTER_CONSUL="consul.service.${MACHINE_DOMAIN}:8500" 34 | export MACHINE_PATH="${MACHINE_STORAGE_PATH}/machines/${MACHINE_NAME}" 35 | export MACHINE_DATE="$(date '+%Y%m%d-%H%M%S')" 36 | 37 | echo "### ${MACHINE_NAME} (MACHINE_*) >>>" 38 | declare -p | egrep '^MACHINE|[ ]MACHINE_' | sed -e 's/^/# /g' 39 | echo "### ${MACHINE_NAME} (MACHINE_*) <<<" 40 | 41 | if [ ! -d ${MACHINE_PATH} ]; then 42 | docker-machine ${MACHINE_OPTS} create --driver generic \ 43 | --engine-opt "cluster-advertise ${MACHINE_CLUSTER_INTERFACE}:2376" \ 44 | --engine-opt "cluster-store consul://${MACHINE_CLUSTER_CONSUL}" \ 45 | --engine-opt "dns ${MACHINE_CLUSTER_PARTICIPANT_ADDRESS}" \ 46 | --engine-opt "dns-search ${MACHINE_DOMAIN}" \ 47 | --engine-opt "log-driver json-file" \ 48 | --engine-opt "log-opt max-file=10" \ 49 | --engine-opt "log-opt max-size=10m" \ 50 | --generic-ip-address ${MACHINE_PUBLIC_ADDRESS} \ 51 | --generic-ssh-key ${MACHINE_SSH_KEY} \ 52 | --generic-ssh-user ${MACHINE_SSH_USER} \ 53 | --swarm \ 54 | --swarm-discovery "consul://${MACHINE_CLUSTER_CONSUL}" \ 55 | --tls-san ${MACHINE_NODE_NAME} \ 56 | --tls-san ${MACHINE_CLUSTER_PARTICIPANT_ADDRESS} \ 57 | --tls-san ${MACHINE_PUBLIC_ADDRESS} \ 58 | ${MACHINE_CREATE_OPTS} \ 59 | ${MACHINE_NAME} 2>&1 | while read line; do 60 | echo "# ${line}" 61 | done 62 | fi 63 | 64 | echo "### ${MACHINE_NAME} (backup consul config) >>>" 65 | docker-machine ${MACHINE_OPTS} ssh ${MACHINE_NAME} "[ -d /etc/consul ] && sudo mv -vf /etc/consul /etc/consul.${MACHINE_DATE}; sudo rm -rvf /tmp/consul" 2>&1 | while read line; do 66 | echo "# ${line}" 67 | done 68 | echo "### ${MACHINE_NAME} (backup consul config) <<<" 69 | 70 | echo "### ${MACHINE_NAME} (upload consul config) >>>" 71 | docker-machine ${MACHINE_OPTS} scp -r ${MACHINE_STORAGE_PATH}/compose/consul/config ${MACHINE_NAME}:/tmp/consul 2>&1 | while read line; do 72 | echo "# ${line}" 73 | done 74 | echo "### ${MACHINE_NAME} (upload consul config) <<<" 75 | 76 | echo "### ${MACHINE_NAME} (install consul config) >>>" 77 | docker-machine ${MACHINE_OPTS} ssh ${MACHINE_NAME} "sudo mv -vf /tmp/consul /etc" 2>&1 | while read line; do 78 | echo "# ${line}" 79 | done 80 | echo "### ${MACHINE_NAME} (install consul config) <<<" 81 | 82 | eval $(docker-machine env ${MACHINE_NAME}) 83 | 84 | echo "### ${MACHINE_NAME} (DOCKER_*) >>>" 85 | declare -p | egrep '^DOCKER|[ ]DOCKER_' | sed -e 's/^/# /g' 86 | echo "### ${MACHINE_NAME} (DOCKER_*) <<<" 87 | 88 | echo "### ${MACHINE_NAME} (docker-compose -f ${MACHINE_TYPE}.yml) >>>" 89 | docker-compose -f ${MACHINE_STORAGE_PATH}/compose/consul/${MACHINE_TYPE}.yml up -d 90 | echo "### ${MACHINE_NAME} (docker-compose -f ${MACHINE_TYPE}.yml) <<<" 91 | 92 | eval $(docker-machine env --unset) 93 | 94 | echo "### ${MACHINE_NAME} (replace /etc/resolv.conf) >>>" 95 | docker-machine ${MACHINE_OPTS} ssh ${MACHINE_NAME} "sudo rm /etc/resolv.conf" 2>&1 | while read line; do 96 | echo "# ${line}" 97 | done 98 | docker-machine ${MACHINE_OPTS} ssh ${MACHINE_NAME} "echo 'nameserver ${MACHINE_CLUSTER_PARTICIPANT_ADDRESS}' | sudo tee /etc/resolv.conf" 2>&1 | while read line; do 99 | echo "# ${line}" 100 | done 101 | echo "### ${MACHINE_NAME} (replace /etc/resolv.conf) <<<" 102 | 103 | echo "### ${MACHINE_NAME} (restart docker) >>>" 104 | docker-machine ${MACHINE_OPTS} ssh ${MACHINE_NAME} "sudo systemctl restart docker || sudo service docker restart" 2>&1 | while read line; do 105 | echo "# ${line}" 106 | done 107 | echo "### ${MACHINE_NAME} (restart docker) <<<" 108 | } 109 | 110 | for i in $(seq 0 $((${#MACHINE_NAMES[*]}-1))); do 111 | export MACHINE_NAME=${MACHINE_NAMES[${i}]} 112 | export MACHINE_NODE_NAME="${MACHINE_NAME}.node.${MACHINE_DOMAIN}" 113 | export MACHINE_PUBLIC_ADDRESS=${MACHINE_PUBLIC_ADDRESSES[${i}]} 114 | export MACHINE_CLUSTER_PARTICIPANT_ADDRESS=${MACHINE_CLUSTER_ADDRESSES[${i}]} 115 | 116 | if [ -z ${MACHINE_SSH_USER} ]; then 117 | export MACHINE_SSH_USER=$(docker-machine inspect --format '{{.Driver.SSHUser}}' ${MACHINE_NODE_NAME}) 118 | fi 119 | 120 | if [ -f ${MACHINE_STORAGE_PATH}/machines/${MACHINE_NODE_NAME}/id_rsa ]; then 121 | export MACHINE_SSH_KEY="${MACHINE_STORAGE_PATH}/machines/${MACHINE_NODE_NAME}/id_rsa" 122 | elif [ -f ${HOME}/.ssh/id_rsa ]; then 123 | export MACHINE_SSH_KEY="${HOME}/.ssh/id_rsa" 124 | fi 125 | 126 | if [ ${i} -eq 1 ]; then 127 | export MACHINE_CLUSTER_REPLICANT_ADDRESS_0=${MACHINE_CLUSTER_ADDRESSES[1]} 128 | export MACHINE_CLUSTER_REPLICANT_ADDRESS_1=${MACHINE_CLUSTER_ADDRESSES[0]} 129 | export MACHINE_CLUSTER_REPLICANT_ADDRESS_2=${MACHINE_CLUSTER_ADDRESSES[2]} 130 | elif [ ${i} -eq 2 ]; then 131 | export MACHINE_CLUSTER_REPLICANT_ADDRESS_0=${MACHINE_CLUSTER_ADDRESSES[2]} 132 | export MACHINE_CLUSTER_REPLICANT_ADDRESS_1=${MACHINE_CLUSTER_ADDRESSES[0]} 133 | export MACHINE_CLUSTER_REPLICANT_ADDRESS_2=${MACHINE_CLUSTER_ADDRESSES[1]} 134 | else 135 | export MACHINE_CLUSTER_REPLICANT_ADDRESS_0=${MACHINE_CLUSTER_ADDRESSES[0]} 136 | export MACHINE_CLUSTER_REPLICANT_ADDRESS_1=${MACHINE_CLUSTER_ADDRESSES[1]} 137 | export MACHINE_CLUSTER_REPLICANT_ADDRESS_2=${MACHINE_CLUSTER_ADDRESSES[2]} 138 | fi 139 | 140 | if [ ${i} -lt 3 ]; then 141 | docker_machine_recreate 'server' 142 | else 143 | docker_machine_recreate 'agent' 144 | fi 145 | 146 | done 147 | -------------------------------------------------------------------------------- /scripts/teardown-swarm.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | : ${MACHINE_STORAGE_PATH?required} 4 | 5 | for m in $(docker-machine ls -q | sort -r | egrep -v '[.]node[.]'); do 6 | docker-machine ssh ${m} ' 7 | set -x; 8 | apt-get purge --auto-remove docker-engine -y; 9 | rm -rvf /etc/consul \ 10 | /etc/default/docker \ 11 | /etc/docker \ 12 | /etc/systemd/system/docker.service \ 13 | /var/lib/docker \ 14 | /var/run/docker; 15 | rm -vf /etc/resolv.conf; 16 | resolvconf -u; 17 | ln -vrs /run/resolvconf/resolv.conf /etc/resolv.conf 18 | ' 19 | rm -rvf ${MACHINE_STORAGE_PATH}/machines/${m} 20 | done --------------------------------------------------------------------------------