L4 (TCP) Load Balancing in Docker Swarm

Can Docker Swarm use layer 4 (TCP) load balancing with HAproxy or NGINX load balancer instead of using ingress load balancing?

You can use the long syntax for port publishing by explicitlity setting the mode: host (which override the detault mode: ingress).

ports:
  - target: 80
    published: 8080
    protocol: tcp
    mode: host

This will bypass ingress for the published port (=host port) and the service endpoint, which depending on the endpoint_mode is either a vip ip that balances amongst all healthy target containers or a dnsrr multivalue dns response with the ip of all healty target containers.

Note: as a result the host port will only be bound on the nodes that match the placement constraint of the deployed service (=where a task is actualy running!). Make sure to use node labels and placement constraints that target the node labels, preferably with deploy.mode: global.

Thank you for your reply…
I’ve tried, but it doesn’t work. Can you help me?
This is what I’ve tried

docker service create --name nginx-Service --mode replicated --replicas 4 --replicas-max-per-node 2 --constraint 'node.role==worker' --network lb-net --endpoint-mode dnsrr nginx:1.23`

haproxy.cfg

global
    log          fd@2 local2
    chroot       /var/lib/haproxy
    pidfile      /var/run/haproxy.pid
    maxconn      4000
    user         haproxy
    group        haproxy
    stats socket /var/lib/haproxy/stats expose-fd listeners
    master-worker

resolvers docker
    nameserver dns1 127.0.0.11:53
    resolve_retries 3
    timeout resolve 1s
    timeout retry   1s
    hold other      10s
    hold refused    10s
    hold nx         10s
    hold timeout    10s
    hold valid      10s
    hold obsolete   10s

defaults
    timeout connect 10s
    timeout client 30s
    timeout server 30s
    log global
    mode tcp

frontend  fe_web
    bind *:8085
    use_backend stat if { path -i /my-stats }
    default_backend be_ng_service

backend be_ng_service
    balance roundrobin
    server-template ng- 4 nginx-Service:80 check resolvers docker init-addr libc,none

backend be_ng_service_wrong_case
    balance roundrobin
    server-template ng- 4 nginx-service:80 check resolvers docker init-addr libc,none

backend stat
    stats enable
    stats uri /my-stats
    stats refresh 15s
    stats show-legends
    stats show-node
docker service create --mode global --constraint 'node.role==manager'  --name haproxy-lb --network lb-net --publish published=8085,target=80,protocol=tcp,mode=host --mount type=bind,src="$(pwd)"/haproxy-lb/,dst=/etc/haproxy/,ro=true --dns=127.0.0.11 haproxytech/haproxy-debian:2.0

Please wrap you code in Preformated Text block (</> icon) for a better readability.

I am not sure what the objective of your first command is, but at least for me it does not allign with the goal to bypass the ingress routing mesh.

I can not say anything about the haproxy config - never used it and I have no need to use it. I leave this one to someone else to validate.

Your last command on the other hand translates the suggested docker compose v3 elements to docker service create (not sure if the syntax is correct, as I don’t use swarm without docker stack deploy) arguments.

Are you sure the mount makes sense? A bind is local to a node - therefore the folder and its content must exist on every node a service task can potentialy be scheduled to. If it’s just about the haproxy conf, then please use docker config instead, which will be available on all swarm nodes.Note: configs are immutable, if you need to update their config you will have to modify the name and refer to the new config.

May I ask why you feel the need for --dns=127.0.0.11?