docker-machine
(CLI) :: create
…
docker
(CLI) :: swarm
| stack
| service
| Compose (YAML)
Single-node
docker swarm init
# Init swarm mode
docker swarm init
# Validate swarm mode
docker node ls
docker service create
# Create services' overlay network
docker network create -d overlay 'appnet'
# Create service(s)
docker service create --name 'rds' --network 'appnet' -p 6379:6379 -d \
redis:6.0.8-buster redis-server --requirepass ${_REDIS_PASSWORD:-foob1234}
docker service create --name 'api' --network 'appnet' -p 80:5555 -d gd9h/cache-redis:latest
# Update :: Add healthcheck https://docs.docker.com/engine/reference/commandline/service_update/
docker service update --health-cmd "curl -I -f -s --connect-timeout 2 localhost:5555 || exit 1" api
docker service rm 'api'
# Create api service (again), but w/ healthcheck
docker service create --name 'api' --network 'appnet' -p 80:5555 -d \
--health-cmd "curl -I -f -s --connect-timeout 2 localhost:5555 || exit 1" \
--health-interval 10s \
--health-retries 3 \
--health-start-period 30s \
--health-timeout 2s \
gd9h/cache-redis:latest
# Add Visualizer service (SUCCESS @ WSL; FAIL @ MINGW64 due to volume-mount pathing)
docker service create --name 'viz' --network 'appnet' -p 8080:8080 -d \
--mount type=bind,source=/var/run/docker.sock,target=/var/run/docker.sock \
--label app.label=visualizer \
dockersamples/visualizer:stable
docker service
[update
| scale
]
# Update api service; mod to 3 replicas
docker service update --replicas 3 api
# Test healthcheck
# Shutdown rds (cache) service
docker service scale rds=0
# View services state (replicas running)
docker service ls # api replicas should go to zero
# Restart rds (cache) service
docker service scale rds=1
# View services state (replicas running)
docker service ls # api replicas should return to original number
# Teardown
docker service rm $(docker service ls -q)
# Disintegrate Swarm
docker swarm leave -f
# Cleanup (remove custom networks and such)
docker system prune -f
docker-compose up
VERSUS docker stack deploy ...
… docker stack deploy supports "Variable Substitution" but you need to set the environment variables from the .env file in some way. Several options have been explained above. One of the neatest workarounds is using docker-compose config as pre-processor. So you can create your stack with …
:
- Variable Substitution
env_file
- SOLUTIONs:
Use
docker-compose
tool as a preprocessor fordocker stack deploy
:docker-compose config | docker stack deploy -c - $_STACK_NAME # OR docker stack deploy -c <(docker-compose config) $_STACK_NAME
Use
Makefile
(PRRED)# OR make ...
Multi-node | docker-machine
| Infra/Architecture
docker-machine create
| swarm.machine-create.sh
Create VMs
machines='h1,h2,h3'
switch='External Switch'
ram=1024
hdd=10000
echo "=== Create machines (idempotent)"
for vm in $(printf $machines | sed 's/,/ /g'); do
echo "=== @ '$vm'"
[[ $( docker-machine ls -q | grep $vm ) ]] && {
echo "Machine '$vm' already exists."
} || \
docker-machine create -d hyperv \
--hyperv-virtual-switch $switch \
--hyperv-memory $ram --hyperv-disk-size $hdd \
$vm # MUST be sequential; FAILs as background process(es).
done
Add an existing machine
docker-machine create \
--driver generic \
--generic-ip-address=${_IP_or_DNS_NAME_of_EXISTING_MACHINE} \
--generic-ssh-key ${_PRIVATE_KEY_of_EXISTING_MACHINE} \
${_NEW_NAME_for_EXISTING_MACHINE}
- By this scheme, we can use Terraform to generate the infra, and then use
docker-machine
to init/join into swarm, thereby removing the per-driver (per vendor) limitations ofdocker-machine create
….
Make Swarm Cluster | swarm-make.sh
docker-machine ssh $vm "docker swarm init"
Remotely create a swarm cluster of managers (and/or workers). If a VM has more than one NIC (network interface), then must declare one, per its IP (--advertise-addr $LEADER_IP
).
export vm='h1' # reset to master
export m1=$vm # Swarm Master/Leader
# Set Control Plane comms (if more than one interface)
LEADER_IP=$(docker-machine ip $m1)
# Swarm Init
docker-machine ssh $m1 docker swarm init --advertise-addr "$LEADER_IP"
# Add managers
JOIN_TOKEN_MGR=$(docker-machine ssh $m1 docker swarm join-token -q manager)
echo $JOIN_TOKEN_MGR
for i in 2 3; do
echo "Joining @ node: 'vm$i'"
docker-machine ssh vm$i docker swarm join --token "$JOIN_TOKEN_MGR" "$LEADER_IP"
done
# Validate cluster
dm ssh $m1 docker node ls
docker-machine env $vm
Configure local Docker client to remote engine (@ swarm leader VM); allows remote access per node; same tools/scripts as for single-host mode, but only for containers on the confgured node.
export vm='h1' # reset to master
export m1=$vm # Swarm Master/Leader
# Configure docker client to swarm leader engine
docker-machine env $m1
eval $(docker-machine env $m1)
# Verify
docker node ls
Stack Deploy | swarm.stack-deploy.sh
docker stack deploy
# Deploy stack ...
app='app'
svc='api'
docker stack deploy -c 'stack-cache-redis.yml' $app
# Stack
docker stack ls
# Services
docker service ls || docker stack services $app ## EQUIV
# All Tasks of ALL Services
docker stack ps $app --format "table {{.ID}}\t{{.Name}}\t{{.Image}}\t{{.Node}}\t{{.CurrentState}}"
# All Tasks of ONE Service
docker service ps "${app}_${svc}" \
--format "table {{.ID}}\t{{.Name}}\t{{.Image}}\t{{.Node}}\t{{.DesiredState}}\t{{.CurrentState}}"
# else
docker container ls --format "table {{.ID}}\t{{.Image}}\t{{.Command}}\t{{.Ports}}\t{{.Names}}"
# All Tasks of ALL Services across ALL Nodes
docker node ps $(docker node ls -q) \
--format "table {{.ID}}\t{{.Name}}\t{{.Image}}\t{{.Node}}\t{{.DesiredState}}\t{{.CurrentState}}" \
| uniq | grep -v Shutdown
# Use the following pair to match container ID to host (VM) name (to response @ cURL):
# All per Node ID + Container ID
for f in $(docker service ps -q "${app}_${svc}" -f desired-state=running); do
docker inspect --format "{{.NodeID}} {{.Status.ContainerStatus.ContainerID}}" $f
done
# All Node ID + Host Name
docker node ls
# ---------------------------------------------
# Validate SWARM LOAD BALANCING and redis count
# Should serve round-robin or random container (id), and redis count, per request:
ip=0.0.0.0 # @ Single/local-host engine swarm
ip=$(docker-machine ip $m1)
dns='kvpairs.com'
for i in {1..9};do curl $ip --connect-timeout 1 && echo '';sleep 1;done
#ip=$(docker-machine ip a2)
for i in {1..5}; do
#curl $ip --connect-timeout 1 && echo ''
curl $dns --connect-timeout 1 && echo ''
done
# -----------------------------------------------------------------------------
# Teardown
docker stack rm $app
export m='h'
# Disintegrate Swarm ...
# for i in 3 2 1; do
# echo "Leaving swarm :: node '${m}$i'"
# docker-machine ssh ${m}$i docker swarm leave -f
# done
seq {4,-1,1} | xargs -n 1 -I {} sh -c 'docker-machine ssh ${m}$1 docker swarm leave -f' _ {}
# Prune ...
# for i in 4 3 2 1; do
# echo "Prune system :: node '${m}$i'"
# docker-machine ssh ${m}$i docker system prune -a -f
# done
seq {1,1,4} | xargs -n 1 -I {} sh -c 'docker-machine ssh h$1 docker system prune -a -f' _ {}
# Unconfigure (reset docker tool to docker engine at host)
docker-machine env --unset
eval $(docker-machine env --unset)
# Shutdown servers
docker-machine stop {dn3,dn2,dn1,aa3,aa2,aa1}