skip to Main Content

I try to connect from backend container to postgres container.
Here is my docker-compose file:

version: "3.9"

services:
  imgress-producer:
    build:
      context: ./producer
      dockerfile: Dockerfile.producer
      target: prod
    container_name: imgress-producer
    ports:
      - 8080:8080
    environment:
      - POSTGRES_USER=${POSTGRES_USER}
      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
      - POSTGRES_DB=${POSTGRES_DB}
      - DATABASE_HOST=${DATABASE_HOST}
      - DATABASE_PORT=${DATABASE_PORT}
    depends_on:
      - imgress-db
    volumes:
      - ./:/app
    networks:
      - imgress-network

  imgress-db:
    image: postgres
    container_name: imgress-db
    environment:
      - POSTGRES_USER=${POSTGRES_USER}
      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
      - POSTGRES_DB=${POSTGRES_DB}
      - DATABASE_HOST=${DATABASE_HOST}
    volumes:
      - postgres-data:/var/lib/postgresql/data
    ports:
      - 5432:5432
    networks:
      - imgress-network
    restart: always

volumes:
  postgres-data:

networks:
  imgress-network:
    driver: bridge

The .env file:

POSTGRES_USER=postgres
POSTGRES_PASSWORD=root
POSTGRES_DB=imgress
DATABASE_HOST=imgress-db
DATABASE_PORT=5432

And here is how I try to connect to db:

package database

import (
    "fmt"
    "log"
    "os"
    "time"

    "gorm.io/driver/postgres"
    "gorm.io/gorm"
)

var GDB *gorm.DB

func ConnectDB() {
    var err error
    pgPort := os.Getenv("DATABASE_PORT")
    pgHost := os.Getenv("DATABASE_HOST")
    pgUser := os.Getenv("POSTGRES_USER")
    pgPassword := os.Getenv("POSTGRES_PASSWORD")
    pgName := os.Getenv("POSTGRES_DB")

    configData := fmt.Sprintf("postgres://%v:%v@%v:%v/%v?sslmode=disable",
        pgUser,
        pgPassword,
        pgHost,
        pgPort,
        pgName,
    )

    for i := 0; i < 5; i++ {
        GDB, err = gorm.Open(postgres.Open(configData), &gorm.Config{})
        if err == nil {
            break
        }
        time.Sleep(10 * time.Second)
    }
    if err != nil {
        log.Println("Producer: Error Connecting to Database")
    } else {
        log.Println("Producer: Connection Opened to Database")
    }
}

So, in the last part, I retry until db container is ready. So it should log an error when db connection is unsuccessful. But instead, it fails to connect and logs a success.

imgress-producer    | 2022/12/03 20:28:24 /go/pkg/mod/gorm.io/[email protected]/gorm.go:206
imgress-producer    | [error] failed to initialize database, got error failed to connect to `host=imgress-db user=postgres database=imgress`: dial error (dial tcp 172.23.0.3:5432: connect: connection refused)
imgress-producer    | 2022/12/03 20:28:34 Producer: Connection Opened to Database

There is a lot of connection refused related questions asked on SO, but none of them helped me. So any kind of help is appreciated.

2

Answers


  1. I simplified your code a little bit and I was able to make it work on my machine. Let’s see it. The repo structure is:

    1. .env
    2. docker-compose.yaml
    3. Dockerfile
    4. main.go

    Let’s start with the main.go file.

    main.go

    package main
    
    import (
        "fmt"
        "log"
        "os"
        "time"
    
        "gorm.io/driver/postgres"
        "gorm.io/gorm"
    )
    
    var GDB *gorm.DB
    
    func ConnectDB() {
        var err error
        pgPort := os.Getenv("DATABASE_PORT")
        pgHost := os.Getenv("DATABASE_HOST")
        pgUser := os.Getenv("POSTGRES_USER")
        pgPassword := os.Getenv("POSTGRES_PASSWORD")
        pgName := os.Getenv("POSTGRES_DB")
    
        configData := fmt.Sprintf("postgres://%v:%v@%v:%v/%v?sslmode=disable",
            pgUser,
            pgPassword,
            pgHost,
            pgPort,
            pgName,
        )
    
        for i := 0; i < 5; i++ {
            GDB, err = gorm.Open(postgres.Open(configData), &gorm.Config{})
            if err == nil {
                break
            }
            time.Sleep(1 * time.Second) // change back to 10s
        }
        if err != nil {
            log.Println("Producer: Error Connecting to Database")
        } else {
            log.Println("Producer: Connection Opened to Database")
        }
    }
    
    func main() {
        ConnectDB()
    }
    

    No relevant changes here.

    .env

    POSTGRES_USER=postgres
    POSTGRES_PASSWORD=postgres
    POSTGRES_DB=postgres
    DATABASE_HOST=imgress-db
    DATABASE_PORT=5432
    

    Also here nothing to mention.

    Dockerfile

    FROM golang:1.19.3-alpine3.17 AS build
    WORKDIR /go/src/app
    COPY ./main.go ./main.go
    RUN go mod init pgdockercompose
    RUN go mod tidy
    RUN go build -o ./bin/webserver ./main.go
    
    FROM alpine:3.17
    COPY --from=build /go/src/app/bin /go/bin
    EXPOSE 8080
    ENTRYPOINT go/bin/webserver
    

    Here we used the multi-staged build to build and copy our Go program. In the build stage we used a bigger image to initialize a go module, restore the dependencies and, to build the source code. While in the leaner image we copy the outcome of our build process.

    docker-compose.yaml

    version: "3.9"
    
    services:
      imgress-producer:
        build: "."
        ports:
          - 8080:8080
        environment:
          - POSTGRES_USER=${POSTGRES_USER}
          - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
          - POSTGRES_DB=${POSTGRES_DB}
          - DATABASE_HOST=${DATABASE_HOST}
          - DATABASE_PORT=${DATABASE_PORT}
        depends_on:
          - imgress-db
        networks:
          - imgress-network
    
      imgress-db:
        image: postgres
        container_name: imgress-db
        environment:
          - POSTGRES_USER=${POSTGRES_USER}
          - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
          - POSTGRES_DB=${POSTGRES_DB}
          - DATABASE_HOST=${DATABASE_HOST}
        ports:
          - 5432:5432
        restart: always
        networks:
          - imgress-network
    
    networks:
      imgress-network:
        driver: bridge
    

    For the sake of the demo, I’ll leave out the volumes. It shouldn’t big a pain to integrate also them too.
    Hope that this clarifies a little bit your doubt!

    Login or Signup to reply.
  2. A workaround solution.
    if you looked here I bet you have been through a log of "changing hostname from localhost to DB". None of that worked for me. I have gone through every documentation for the docker-compose network but it just does not work. Here is the solution

    app:
     // other stuff
      - network_mode: "host"
    db:
      - expose: 
        - "5432" // or the port you want
    
    

    this will expose your DB to the host machine network and your app will connect to your DB through the host machine. No very ideal, but the best solution found so far. If anyone has a real solution and has tried it yourself. Please let me know.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search