2

So I'm building a simple load balancer and I'm facing this problem only in docker environment. Without docker it works normally and healthcheck enables all 3 of my servers and then weighted round robin works fine. But in docker environment i can't figure out what is happening.

This is my Healthcheck function:

func (lb *LoadBalancer) HealthCheckPeriodically() {
    for {
        lb.mutex.Lock()
        for _, server := range lb.servers {
            resp, err := http.Get(server.URL)
            log.Printf("server.URL: %s ", server.URL)
            if err != nil {
                log.Printf("Server %s is down\n", server.Name)
                lb.activeServers[server.URL] = false
            } else {
                log.Printf("Server %s is up\n", server.Name)
                lb.activeServers[server.URL] = true
            }
            if resp != nil {
                resp.Body.Close()
            }
        }
        lb.mutex.Unlock()

        time.Sleep(5 * time.Second)
    }
}

Here is my round robin function:

func (lb *LoadBalancer) chooseServerWeightedRoundRobin() *Server {
    var selected *Server
    totalWeight := 0

    for _, server := range lb.servers {
        if lb.activeServers[server.URL] {
            totalWeight += server.Weight
            server.Current += server.Weight

            if selected == nil || server.Current > selected.Current {
                selected = server
            }
        }
    }

    if selected != nil {
        selected.Current -= totalWeight
        if selected.Current < 0 {
            selected.Current = 0
        }
    }

    return selected
}

So this is the route I'm trying to reach:

http.HandleFunc("/roundRobin", lb.handleRequest)

And I get the message: No active servers available

Here is also handleRequest function:

func (lb *LoadBalancer) handleRequest(w http.ResponseWriter, r *http.Request) {
    lb.mutex.Lock()
    defer lb.mutex.Unlock()

    if len(lb.servers) == 0 {
        http.Error(w, "No servers available", http.StatusServiceUnavailable)
        return
    }

    selected := lb.chooseServerWeightedRoundRobin()
    if selected == nil {
        http.Error(w, "No active servers available", http.StatusServiceUnavailable)
        return
    }

    proxyURL, err := url.Parse(selected.URL)
    if err != nil {
        log.Printf("Failed to parse target URL: %s\n", err)
        http.Error(w, "Internal server error", http.StatusInternalServerError)
        return
    }

    proxy := httputil.NewSingleHostReverseProxy(proxyURL)
    proxy.ServeHTTP(w, r)
}

And in load balancer log I'm getting these messages:

load-balancer_1  | 2023/07/01 00:21:32 server.URL: http://localhost:8001
load-balancer_1  | 2023/07/01 00:21:32 Server Server 1 is down
load-balancer_1  | 2023/07/01 00:21:32 server.URL: http://localhost:8002
load-balancer_1  | 2023/07/01 00:21:32 Server Server 2 is down
load-balancer_1  | 2023/07/01 00:21:32 server.URL: http://localhost:8003
load-balancer_1  | 2023/07/01 00:21:32 Server Server 3 is down

Also here is Dockerfile:

FROM golang:1.16-alpine AS builder

WORKDIR /app

COPY . .

RUN go build -o loadbalancer loadbalancer.go

FROM alpine:latest

WORKDIR /app

COPY --from=builder /app/loadbalancer .

EXPOSE 8000

CMD ["./loadbalancer"]
Jan Tuđan
  • 233
  • 3
  • 17
  • 2
    In the context of a container `localhost` means the container itself. If your target instances are running within another container 1. You have to use `container IP or System IP` to connect with them instead of `localhost`. 2. Or you can use a docker network and run target instance and loadbalancer within the same network. – PRATHEESH PC Jul 01 '23 at 02:16
  • This was the problem, I was calling my servers from localhost container, when I changed an address to my system IP it worked, thanks alot! – Jan Tuđan Jul 03 '23 at 19:30

0 Answers0