Home > database >  502 Bad gateway Nginx reversy proxy, connect() failed (111: Connection refused) while connecting to
502 Bad gateway Nginx reversy proxy, connect() failed (111: Connection refused) while connecting to

Time:11-11

I have a project, which consist of Go application Nginx Db(Postgres). All are building in docker containers. It is my docker-compose.yml file:

version: "3"
services:
  db:
    image: postgres:10
    environment:
      - POSTGRES_PASSWORD=DatabasePassword
      - POSTGRES_USER=egor
      - POSTGRES_DB=postgres
    expose:
      - 5432
    
  backend:
    build: .
    environment:
      - POSTGRES_URL=postgres://egor:DatabasePassword@db:5432/postgres?sslmode=disable
      - LISTEN_ADDRESS=:5432
    depends_on:
      - db
  
  proxy:
    image: nginx
    volumes:
      - type: bind
        source: ./nginx.conf
        target: /etc/nginx/nginx.conf
    ports:
      - 80:80
    depends_on: 
      - backend
      - db

it is my go application:

package main

import (
    "database/sql"
    "fmt"
    "time"
    _ "github.com/lib/pq"
    "log"
    "net/http"

    "github.com/caarlos0/env"
    "github.com/prometheus/client_golang/prometheus"
    "github.com/prometheus/client_golang/prometheus/promhttp"
)

type config struct {
    PostgresUri   string `env:"POSTGRES_URL" envDefault:"postgres://root:pass@localhost:5432/postgres?sslmode=disable"`
    ListenAddress string `env:"LISTEN_ADDRESS" envDefault:":7000"`
    //PostgresHost  string `env:"POSTGRES_HOST" envDefault:":l"`
    //PostgresUser  string `env:"POSTGRES_USER" envDefault:":root"`
    //PostgresPassword string `env:"POSTGRES_PASSWD" envDefault:":qwerty"`
    //PostgresName  string `env:"POSTGRES_NAME" envDefault:":postgres"`

}

var (
    db          *sql.DB
    errorsCount = prometheus.NewCounterVec(
        prometheus.CounterOpts{
            Name: "gocalc_errors_count",
            Help: "Gocalc Errors Count Per Type",
        },
        []string{"type"},
    )

    requestsCount = prometheus.NewCounter(
        prometheus.CounterOpts{
            Name: "gocalc_requests_count",
            Help: "Gocalc Requests Count",
        })
)

func main() {
    var err error

    // Initing prometheus
    prometheus.MustRegister(errorsCount)
    prometheus.MustRegister(requestsCount)

    // Getting env
    cfg := config{}
    if err = env.Parse(&cfg); err != nil {
        fmt.Printf("% v\n", err)
    }
    
    time.Sleep(time.Second)
    fmt.Println("Sleep over!")
    
    // Connecting to database
    //psqlInfo := fmt.Sprintf("host=%s user=%s password=%s dbname=%s port=5432 sslmode=disable",
    //                        cfg.PostgresHost,cfg.ListenAddress,cfg.PostgresUser,cfg.PostgresPassword,cfg.PostgresName)
    
    //db, err := sql.Open("postgres", "host=db user=egor password=DatabasePassword dbname=postgres port=5432 sslmode=disable")
    db, err = sql.Open("postgres",cfg.PostgresUri)
    if err != nil {
        log.Fatalf("Can't connect to postgresql: %v", err)
    }
    defer db.Close()

    err = db.Ping()
    if err != nil {
        log.Fatalf("Can't ping database: %v", err)
    }

    http.HandleFunc("/", handler)
    http.Handle("/metrics", promhttp.Handler())
    log.Fatal(http.ListenAndServe(cfg.ListenAddress, nil))
}

func handler(w http.ResponseWriter, r *http.Request) {
    requestsCount.Inc()

    keys, ok := r.URL.Query()["q"]
    if !ok || len(keys[0]) < 1 {
        errorsCount.WithLabelValues("missing").Inc()
        log.Println("Url Param 'q' is missing")
        http.Error(w, "Bad Request", 400)
        return
    }
    q := keys[0]
    log.Println("Got query: ", q)

    var result string
    sqlStatement := fmt.Sprintf("SELECT (%s)::numeric", q)
    row := db.QueryRow(sqlStatement)
    err := row.Scan(&result)

    if err != nil {
        log.Println("Error from db: %s", err)
        errorsCount.WithLabelValues("db").Inc()
        http.Error(w, "Internal Server Error", 500)
        return
    }

    fmt.Fprintf(w, "query %s; result %s", q, result)
}

And my nginx configuration:

events{
    worker_connections 1024;
}
http{
   server {
      listen 80;
      server_name  localhost;
      location / {
        proxy_pass  http://backend:7000;
      }
  }
}

But when i'm going to try page in browser, i see error page - 502 Bad Gateway nginx. It is my log:

2022/11/08 23:41:24 [error] 29#29: *1 connect() failed (111: Connection refused) while connecting to upstream, client: xxx.xx.x.x, server: localhost, request: "GET / HTTP/1.1", upstream: "http://xxx.xx.x.x:7000/", host: "0.0.0.0"

What is problem? All services work correctly, only nginx reversy proxy has error

CodePudding user response:

I just put together a small project that represents your scenario. This is the repository structure:

  • webapp/
    • nginx/
      • Dockerfile
      • nginx.conf
    • web/
      • Dockerfile
      • main.go
    • docker-compose.yaml

The content of each file are as follows.

nginx/nginx.conf

events{}

http {
   server {
      listen 80;
      location / {
        proxy_pass  http://backend:7000;
      }
  }
}

More or less is your same file.

nginx/Dockerfile

FROM nginx
EXPOSE 80
COPY nginx.conf /etc/nginx/nginx.conf

Here, we specify instructions to build the nginx container. We expose only the port 80.

web/main.go

package main

import (
    "fmt"
    "net/http"
)

func main() {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Hello, World!\n")
    })

    http.ListenAndServe(":7000", nil)
}

Simple HTTP server with a hard-coded reply. This HTTP server listens for requests on port 7000.

web/Dockerfile

FROM golang:1.12.7-alpine3.10 AS build
WORKDIR /go/src/app
COPY ./main.go ./main.go
RUN go build -o ./bin/gowebserver ./main.go

FROM alpine:latest
COPY --from=build /go/src/app/bin /go/bin
EXPOSE 7000
ENTRYPOINT go/bin/gowebserver

Here, we use the multi-stage build. In the first section we build the HTTP server while in the second one, we copy the executable on a leaner base image of Docker. We expose port 7000 of this container.

docker-compose.yaml

version: "3"
services:
  backend:
    build: "./web"
    expose:
      - "7000"
  nginx:
    build: "./nginx"
    ports:
      - "80:80"
    depends_on:
      - "backend"

Here, is the last part that connects all. We expose to the outside only the port 80. Internally, the backend service exposes port 7000 to be contacted by the nginx service.
To spin up everything, you've to run these two commands (in the root folder of the project):

  • docker-compose build
  • docker-compose up

To test this solution you've to use your internal IP address (in my case was something like 192.168.1.193) and navigate to the URL http://192.168.1.193/ which should give you an Hello, World! message.
Let me know if this solves your issue!

  • Related