1

I have a project with Golang backend, PostgreSQL DB and Redis.

I need to init PostgreSQL DB with this SQL:

ALTER USER postgres WITH password '123';

CREATE TABLE users
  (
     username VARCHAR (200) PRIMARY KEY,
     pass     VARCHAR (50)
  );

My Dockerfile is:

# syntax=docker/dockerfile:1
FROM postgres:latest AS go2
ENV POSTGRES_USER postgres
ENV POSTGRES_PASSWORD ''
ENV POSTGRES_DB postgres
EXPOSE 5432
ADD table.sql /docker-entrypoint-initdb.d/
FROM redis:latest AS go3
FROM golang:latest AS go
WORKDIR /app
COPY . .
RUN go build -o bin
ENTRYPOINT [ "/app/bin" ] 
EXPOSE 8000

I build it with:

docker build . -t mycontainer

And run it with:

docker run -t mycontainer --network=bridge

But I get the following error:

[error] failed to initialize database, got error failed to connect to `host=172.17.0.2 user=postgres database=postgres`: dial error (dial tcp 172.17.0.2:5432: connect: connection refused)

My problems are:

  1. I can't connect to PostgreSQL DB
  2. I can't connect to Golang backend

I tried to run the container with this command:

docker run -t mycontainer --network=host

But the problems remain.


Update:

I created a docker-compose.yaml file:

services:
  db:
    image: 'postgres:15.7'
    restart: always
    network_mode: bridge
    volumes:
      - './db/table.sql:/docker-entrypoint-initdb.d/init.sql'
    ports:
      - '5433:5432'
    environment:
      POSTGRES_DB: postgres
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: 123
  redis:
    image: 'redis:7.0.12'
    network_mode: bridge
    ports:
      - '6380:6379'
  backend:
    build: ./backend
    network_mode: bridge
    ports:
      - '8000:8089'
    depends_on:
      - db

My PostgreSQL DB and Redis work good and I can connect to them.

But when I run my backend container it throws this error:

[error] failed to initialize database, got error failed to connect to `host=127.0.0.1 user=postgres database=postgres`: dial error (dial tcp 127.0.0.1:5433: connect: connection refused)
3
  • 2
    docker run only runs one container, and a container only runs one process. You might be looking for a tool like Docker Compose that can run the three containers in parallel. The Dockerfile you show only generates an image that runs the Go application, the data-store blocks get completely ignored.
    – David Maze
    Commented Jul 29, 2024 at 15:52
  • i created docker compose file with 3 services db,redis,backend go lang file but i got this error in backend service [error] failed to initialize database, got error failed to connect to host=127.0.0.1 user=postgres database=postgres: dial error (dial tcp 127.0.0.1:5433: connect: connection refused)
    – Vali
    Commented Jul 31, 2024 at 13:31
  • Right, in Docker 127.0.0.1 and the corresponding hostname localhost almost always refer to the current container, not one of the other containers or the host system. Networking in Compose in the Docker documentation describes how to connect between containers.
    – David Maze
    Commented Aug 1, 2024 at 14:26

1 Answer 1

1

In your Dockerfile you combine PostgreSQL, Redis and your Go app.

But according to Docker's docs it's suggested to split them up:

It's best practice to separate areas of concern by using one service per container.

Thus, let's simplify your Dockerfile down to the Go app alone:

FROM golang
WORKDIR /app
COPY ./app .
RUN go build -o bin
ENTRYPOINT [ "/app/bin" ]

Fun fact, the EXPOSE instruction used by you does not expose anything:

The EXPOSE doesn't publish the port. It functions as a type of documentation between the person who builds the image and the person who runs the container.

Since you are the person who runs the container, it's unnecessary. I'd use it only if I publish the Dockerfile to a registry so that others could download and use it. That's why I removed it.

Optional: The following slightly improved version should allow faster rebuilds:

FROM golang
WORKDIR /app
COPY go.mod go.sum ./ # ddd
RUN go mod download
COPY *.go ./
RUN go build -o bin
CMD [ "/app/bin" ]

Now let's talk about the error you experience.

Each service is in a separate container, but you are trying to connect from backend to db like if they are running in a single container. Think about them as running on separate machines. This way it should be obvious why you can't access db on localhost of backend.

You defined network_mode: bridge for all containers. Thus they all will be attached to the default bridge network. It means they can access each other by their IPs. But it's tricky to get IPs. Thankfully, there is an easier way. Just define a custom bridge network:

networks:
  my_network:

Here my_network is a user defined network and it defaults to driver: bridge. As opposed to the default bridge network, user defined bridge networks allow automatic service discovery:

On user-defined networks containers can not only communicate by IPs, but can also resolve a container name to an IP. This is called automatic service discovery.

It means, in the code of your backend service you will be able to use db:5432 to access your PostgreSQL DB. The db here will be automatically replaced by IP of the db service.

But to access your services from the host use ports attribute to map host's port to container's port. Your backend service maps 8000:8089. It means, the backend service is accessible on the host as localhost:8000, but from other services as backend:8089.

This is a complete minimal example of compose file you need:

networks:
  my_network:
services:
  db:
    image: postgres
    volumes:
      - ./db/docker-entrypoint-initdb.d:/docker-entrypoint-initdb.d
    networks:
      - my_network
    ports:
      - 5433:5432
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: 123
    healthcheck:
      test: ["CMD-SHELL", "pg_isready"]
      interval: 1s
      retries: 100
  redis:
    image: redis
    networks:
      - my_network
    ports:
      - 6380:6379
  backend:
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_started
    build: ./backend
    networks:
      - my_network
    ports:
      - 8000:8089

Here, the ./postgres/docker-entrypoint-initdb.d:/docker-entrypoint-initdb.d maps data from ./postgres/docker-entrypoint-initdb.d on the host to docker-entrypoint-initdb.d within the db container. Any SQL there will init PostgreSQL DB.

The backend.depends_on together with db.healthcheck allow to make sure the backend starts when db is healthy (i.e. PostgreSQL DB is ready to accept connections).

Finally, a complete working example is here.

3
  • now every thing is work but when i want to access backend from 8089 with ip 127.0.0.1 i have problem . if i run main.go manually its works but i cant access backend container that i mapped port 8089:8089
    – Vali
    Commented Aug 1, 2024 at 16:52
  • and for last error i found i must set listing port backend :8000 not 127.0.0.1:8000 .why?i Dont know.but it works:) thanks for your help .
    – Vali
    Commented Aug 1, 2024 at 17:06
  • @Vali I adjusted the answer, added more details and a complete working example. The answer for your question is also there. In short, you should access backend service from host as localhost:8000, but from another service as backend:8089. The backend will be replaced by the container's IP thanks to automatic service discovery. Commented Aug 2, 2024 at 11:25

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.