docker swarm - how to balance already running containers in a swarm cluster? - docker

I have docker swarm cluster with 2 nodes on AWS. I stopped the both instances and initially started swarm manager and then worker. Before stopped the instances i had a service running with 4 replicas distributed among manager and worker.
When i started swarm manager node first all replica containers started on manager itself and not moving to worker at all.
Please tell me how to do load balance?
Is swarm manager not responsible to do when worker started?

Swarm currently (18.03) does not move or replace containers when new nodes are started, if services are in the default "replicated mode". This is by design. If I were to add a new node, I don't necessarily want a bunch of other containers stopped, and new ones created on my new node. Swarm only stops containers to "move" replicas when it has to (in replicated mode).
docker service update --force <servicename> will rebalance a service across all nodes that match its requirements and constraints.
Further advice: Like other container orchestrators, you need to give capacity on your nodes in order to handle the workloads of any service replicas that move during outages. You're spare capacity should match the level of redundancy you plan to support. If you want to handle capacity for 2 nodes failing at once, for instance, you'd need a minimum percentage of resources on all nodes for those workloads to shift to other nodes.

Here's a bash script I use to rebalance:
#!/usr/bin/env bash
set -e
EXCLUDE_LIST="(_db|portainer|broker|traefik|prune|logspout|NAME)"
for service in $(docker service ls | egrep -v $EXCLUDE_LIST |
awk '{print $2}'); do
docker service update --force $service
done

Swarm doesn't do auto-balancing once containers are created. You can scale up/down once all your workers are up and it will distribute containers per your config requirements/roles/etc.
see: https://github.com/moby/moby/issues/24103
There are problems with new nodes getting "mugged" as they are added.
We also avoid pre-emption of healthy tasks. Rebalancing is done over
time, rather than killing working processes. Pre-emption is being
considered for the future.
As a workaround, scaling a service up and down should rebalance the
tasks. You can also trigger a rolling update, as that will reschedule
new tasks.

In docker-compose.yml, you can define:
version: "3"
services:
app:
image: repository/user/app:latest
networks:
- net
ports:
- 80
deploy:
restart_policy:
condition: any
mode: replicated
replicas: 5
placement:
constraints: [node.role == worker]
update_config:
delay: 2s
Remark: the constraint is node.role == worker
Using the flag “ — replicas” implies we don’t care on which node they are put on, if we want one service per node we can use “ — mode=global” instead.
In Docker 1.13 and higher, you can use the --force or -f flag with the docker service update command to force the service to redistribute its tasks across the available worker nodes.

Related

Docker swarm run on multible machines

I deploy a docker swarm on 5 nodes and I have 5 microservices. The docker swarm assigns the services only in one node. Is there is any way to tell to docker swarm which node to use for every service in order to assign 1 service in every node.
Yes you can do this with the "deploy" configuration option in your compose file. For example:
deploy:
placement:
constraints:
- "node.hostname == desired_machine_hostname"

How to recreate docker containers for single service synchronize?

I have 3 container for single service that created with --scale option on docker-compose. when I tried to recreate them, all of containers stop and remove, and after that, docker start to recreate one by one.
how can I to complete this process one by one, for example, stop first container and recreate them , after container up complete, go next one.
Unfortunately docker-compose don't have this feature, but Docker Swarm does!
Just init your docker machine to swarm cluster with
docker swarm init
and then reconfigure your compose file and add rolling updates like so:
deploy:
replicas: 2
update_config:
parallelism: 2
delay: 10s
order: stop-first

Rolling update in Docker Swarm across services with health check

I am currently attempting a Kafka cluster deployment in Docker Swarm. Kafka does not work with the replica feature of Swarm because each Kafka broker (node) needs to be configured and reachable individually (i.e. no load balancer in front of it). Therefore, each broker is configured as an individual service with replicas=1, e.g. kafka1, kafka2 and kafka3 services.
Every now and then the configuration or image for the Kafka brokers will need to be changed via a docker stack deploy (done by a person or CI/CD pipeline). Then Swarm will recreate all containers simultaneously and as a result, the Kafka cluster is temporarily unavailable, which is not acceptable for a critical piece of infrastructure that is supposed to run 24/7. And I haven't even mentioned the Zookeeper cluster underneath Kafka yet, for which the same applies.
The desired behavior is that Swarm recreates the container of the kafka1 service, waits until it has fully started up and synchronized with the other brokers (all topic partitions are in sync), and only then Swarm restarts kafka2 service and so on.
I think I can construct a health check within the Kafka Docker image that would tell the Docker engine when the Kafka broker is fully synchronized. But how make Swarm perform what amounts to a rolling update across service boundaries? It ignores the depends_on setting that Docker Compose knows, and rolling update policies apply to service replicas only. Any idea?

Can docker swarm stack distribute services averagely to all nodes?

I have:
three 1 swarm manager and 2 swarm working nodes
an application cluster that connected to each other
docker-compose.yml
services:
service1:
ports:
- 8888:8888
environment:
- ADDITIONAL_NODES=service2:8889,service3:8890
service2:
ports:
- 8889:8889
environment:
- ADDITIONAL_NODES=service1:8888,service3:8890
service3:
ports:
- 8890:8890
environment:
- ADDITIONAL_NODES=service1:8888,service2:8889
If I run docker stack deploy -c docker-compose.yml server :
swarm manager(service1), swarm node1(service2), swarm node2(service3)
swarm manager(service1、service2、service3), swarm node1(service1、service2、service3), swarm node3(service1、service2、service3)
Which one will be the result?
If it is 2, how can I deploy like 1 using docker swarm? I need to use docker swarm because I'm also using docker network overlay.
If it is 1, then how does my services distributed? Is it "averagely" distributed? If true then in what perspective is it "averagely" distributed?
Docker swarm has some logic which it uses to decide which services run on which nodes. It might now be 100% what you expect but they are smart people working on this and might consider things that you don't (such as CPU load, available ram....)
The goals is to spread the load evenly so like your example 1. If some services can for some reason not start on one node (like you use a private registry but didn't specify --with-registry-auth in stack deploy) then the services will all start on those nodes who can run them after failing on the other nodes.
From personal experience I can tell you that it spreads tasks nicely accross the swarm but theres no guarantee where which service ends up.
If you want to force where services run use constraints.

Adding new containers to existing cluster (sworm)

I am having a problem trying to implement the best way to add new container to an existing cluster while all containers run in docker.
Assuming I have a docker swarm, and whenever a container stops/fails for some reason, the swarm bring up new container and expect it to add itself to the cluster.
How can I make any container be able to add itself to a cluster?
I mean, for example, if I want to create a RabbitMQ HA cluster, I need to create a master, and then create slaves, assuming every instance of RabbitMQ (master or slave) is a container, let's now assume that one of them fails, we have 2 options:
1) slave container has failed.
2) master container has failed.
Usually, a service which have the ability to run as a cluster, it also has the ability to elect a new leader to be the master, so, assuming this scenerio is working seemlesly without any intervention, how would a new container added to the swarm (using docker swarm) will be able to add itself to the cluster?
The problem here is, the new container is not created with new arguments every time, the container is always created as it was deployed first time, which means, I can't just change it's command line arguments, and this is a cloud, so I can't hard code an IP to use.
Something here is missing.
Maybe trying to declare a "Service" in the "docker Swarm" level, will acctualy let the new container the ability to add itself to the cluster without really knowing anything the other machines in the cluster...
There are quite a few options for scaling out containers with Swarm. It can range from being as simple as passing in the information via a container environment variable to something as extensive as service discovery.
Here are a few options:
Pass in IP as container environment variable. e.g. docker run -td -e HOST_IP=$(ifconfig wlan0 | awk '/t addr:/{gsub(/.*:/,"",$2);print$2}') somecontainer:latest
this would set the internal container environment variable HOST_IP to the IP of the machine it was started on.
Service Discovery. Querying a known point of entry to determine the information about any required services such as IP, Port, ect.
This is the most common type of scale-out option. You can read more about it in the official Docker docs. The high level overview is that you set up a service like Consul on the masters, which you have your services query to find the information of other relevant services. Example: Web server requires DB. DB would add itself to Consul, the web server would start up and query Consul for the databases IP and port.
Network Overlay. Creating a network in swarm for your services to communicate with each other.
Example:
$ docker network create -d overlay mynet
$ docker service create –name frontend –replicas 5 -p 80:80/tcp –network mynet mywebapp
$ docker service create –name redis –network mynet redis:latest
This allows the web app to communicate with redis by placing them on the same network.
Lastly, in your example above it would be best to deploy it as 2 separate containers which you scale individually. e.g. Deploy one MASTER and one SLAVE container. Then you would scale each dependent on the number you needed. e.g. to scale to 3 slaves you would go docker service scale <SERVICE-ID>=<NUMBER-OF-TASKS> which would start the additional slaves. In this scenario if one of the scaled slaves fails swarm would start a new one to bring the number of tasks back to 3.
https://docs.docker.com/engine/reference/builder/#healthcheck
Docker images have a new layer for health check.
Use a health check layer in your containers for example:
RUN ./anyscript.sh
HEALTHCHECK exit 1 or (Any command you want to add)
HEALTHCHECK check the status code of command 0 or 1 and than result as
1. healthy
2. unhealthy
3. starting etc.
Docker swarm auto restart the unhealthy containers in swarm cluster.

Resources