Отказ в соединении: доступ к контейнеру Postgres из контейнера приложения с помощью docker-compose - PullRequest
1 голос
/ 17 мая 2019

Я новичок в Докере. Этот проект только для моего собственного понимания. Вполне вероятно, что я использую неправильную терминологию и / или я более запутан, чем я думаю. Исправления принимаются с благодарностью.

Я использую два образа докера: официальный образ postgres и мое собственное приложение Go и Dockerfile. Используя docker-compose up, я получаю ошибку connection refused.

Я думаю, что потенциально есть две разные проблемы:

  • Во-первых, база данных не работает, когда приложение пытается подключиться.

  • Во-вторых, приложение просто использует неверный IP-адрес.

У меня есть код приложения, который должен дать базе время для запуска, чтобы решить первую потенциальную проблему (см. Код ниже). Судя по сообщению об ошибке, я не думаю, что зашел слишком далеко.

У меня есть две службы: db-access (это приложение Go) и postgres-db.

Я пытался использовать эти имена хостов в строке подключения приложения:

  • "localhost",

  • "postgres-db" (как его называют в docker-compose.yml),

  • "0.0.0.0".

Использование postgres-db в качестве имени хоста:

  • Контейнер приложения пытается: наберите tcp 172.22.0.2:5432.

  • Postgres говорит: прослушивание IPv4-адреса "0.0.0.0", порт 5432.

В docker-compose.yml я пытался использовать эти утверждения:

depends_on:
      - postgres-db

и

links:
      - postgres-db

Я попытался изменить порядок служб в docker-compose.yml, но, похоже, они запускаются в том же порядке в любом случае.

Когда я запускаю контейнер postgres и приложение Go по отдельности, я получаю ожидаемое поведение. Чтобы запустить их отдельно, я использую эти команды:

docker run --rm --name postgres-db -e POSTGRES_PASSWORD=docker -d -p 5432:5432 -v /Users/ForeignFood/Development/go/src/github.com/skillitzimberg/docker/volumes/postgres:/var/lib/postgresql/data postgres

с последующим

go run basicapi

Я также могу запустить docker-compose up, который выдает ошибку отказа в соединении, затем ctrl+C, затем запустить go run basicapi и получить ожидаемое поведение.

Вот файлы проекта. , .

main.go:

package main

import (
    "basicapi/models"
    "fmt"
    "net/http"

    _ "github.com/lib/pq"
)

const (
    host     = "postgres-db"
    port     = 5432
    user     = "postgres"
    password = "docker"
    dbname   = "myfirstdb"
)

var psqlDatabaseConnectionString = fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=disable",
    host, port, user, password, dbname)

func main() {
    models.InitDB(psqlDatabaseConnectionString)
    http.HandleFunc("/users", usersList)
    http.ListenAndServe(":3000", nil)
}

func usersList(w http.ResponseWriter, r *http.Request) {
    if r.Method != "GET" {
        http.Error(w, http.StatusText(405), 405)
        return
    }

    usrs, err := models.AllUsers()
    if err != nil {
        fmt.Println(err)
        http.Error(w, http.StatusText(500), 500)
        return
    }

    for _, usr := range usrs {
        fmt.Fprintf(w, "%d, %s, %s, %.s\n", usr.ID, usr.FirstName, usr.LastName, usr.Email)
    }
}

модели / db.go:

package models

import (
    "database/sql"
    "fmt"
    "log"
    "time"

    _ "github.com/lib/pq"
)

var db *sql.DB

func InitDB(dataSourceName string) {
    var err error

    for i := 0; i < 10; i++ {
        db, err = sql.Open("postgres", dataSourceName)

        if err != nil {
            fmt.Println(i)
            fmt.Println(err)
            time.Sleep(time.Second * 10)
        }
    }
    if err != nil {
        log.Panic(err)
    }
    if err = db.Ping(); err != nil {
        log.Panic(err)
    }
    fmt.Printf("Connection to database successful!\n")
}

модели / users.go:

package models

import "fmt"

type User struct {
    ID        int
    Age       int
    FirstName string
    LastName  string
    Email     string
}

func AllUsers() ([]*User, error) {
    fmt.Println("Got to AllUsers")
    rows, err := db.Query("SELECT * FROM users")

    if err != nil {
        fmt.Println(err)
        return nil, err
    }
    defer rows.Close()

    users := make([]*User, 0)
    for rows.Next() {
        user := new(User)
        err := rows.Scan(&user.ID, &user.Age, &user.FirstName, &user.LastName, &user.Email)
        if err != nil {
            return nil, err
        }
        users = append(users, user)
    }
    if err = rows.Err(); err != nil {
        return nil, err
    }
    return users, nil
}

Dockerfile:

FROM golang

WORKDIR /app

COPY ./go.mod ./go.mod
COPY ./go.sum ./go.sum

RUN go mod download

COPY . .

RUN go build -o /bin/app

CMD [ "app" ]

докер-compose.yml

services:
  db-access:
    build: .
    depends_on:
      - postgres-db
    ports:
      - "3000:3000"
  postgres-db:
    image: postgres
    volumes:
      - /Users/ForeignFood/Development/go/src/github.com/skillitzimberg/docker/volumes/postgres:/var/lib/postgresql/data
    ports:
      - "5432:5432"
    environment:
      POSTGRES_USER: "postgres"
      POSTGRES_PASSWORD: "docker"
      POSTGRES_DATABASE: "myfirstdb"

Ожидаемые результаты: Переход к localhost: 3000 / пользователи показывает:

1, Someone, Alastname, 
2, SomeoneElse, AnotherLastName,
etc...

Фактические результаты:

  • браузер: This site can't be reached

  • терминал:

~/ >> docker-compose up                                                 
Starting basicapi_postgres-db_1 ... done
Starting basicapi_db-access_1   ... done
Attaching to basicapi_postgres-db_1, basicapi_db-access_1
db-access_1    | 2019/05/17 16:53:54 dial tcp 172.22.0.2:5432: connect: connection refused
db-access_1    | panic: dial tcp 172.22.0.2:5432: connect: connection refused
db-access_1    | 
db-access_1    | goroutine 1 [running]:
db-access_1    | log.Panic(0xc0000c3f40, 0x1, 0x1)
db-access_1    |    /usr/local/go/src/log/log.go:333 +0xac
db-access_1    | basicapi/models.InitDB(0xc000062120, 0x55)
db-access_1    |    /app/models/db.go:30 +0x27c
db-access_1    | main.main()
db-access_1    |    /app/main.go:23 +0x3d
basicapi_db-access_1 exited with code 2
postgres-db_1  | 2019-05-17 16:53:58.770 UTC [1] LOG:  listening on IPv4 address "0.0.0.0", port 5432
postgres-db_1  | 2019-05-17 16:53:58.770 UTC [1] LOG:  listening on IPv6 address "::", port 5432
postgres-db_1  | 2019-05-17 16:53:58.776 UTC [1] LOG:  listening on Unix socket "/var/run/postgresql/.s.PGSQL.5432"
postgres-db_1  | 2019-05-17 16:53:58.905 UTC [22] LOG:  database system was shut down at 2019-05-17 16:53:23 UTC
postgres-db_1  | 2019-05-17 16:53:58.952 UTC [1] LOG:  database system is ready to accept connections

Спасибо за любые идеи.

Ответы [ 2 ]

0 голосов
/ 10 июня 2019

Я полагаю, что решение этой проблемы заключается в улучшении логики ожидания / повторного подключения. Кажется, что база данных пинговала и паниковала при первом сбое. Единственные изменения, внесенные в код, были в файле models/db.go.

package models

import (
    "database/sql"
    "fmt"
    "log"
    "time"

    _ "github.com/lib/pq"
)

var db *sql.DB

// InitDB initializes a connection to the database.
func InitDB(dataSourceName string) {
    var err error
    fmt.Println("Initializing database connection . . .")

    for i := 0; i < 10; i++ {
        db, err = sql.Open("postgres", dataSourceName)

        if err != nil {
            fmt.Printf("Unable to Open DB: %s... Retrying\n", err.Error())
            time.Sleep(time.Second * 2)
        } else if err = db.Ping(); err != nil {
            fmt.Printf("Unable to Ping DB: %s... Retrying\n", err.Error())
            time.Sleep(time.Second * 2)
        } else {
            err = nil
            break
        }
    }
    if err != nil {
        log.Panic(err)
    }

    fmt.Printf("Connection to database successful!\n")
}

Еще одна возможность для решения проблемы состояла в том, что мне нужно было использовать docker-compose up --build, потому что мой двоичный файл устарел (построен на более старом коде).

К сожалению, проблема была исправлена, но я не отслеживал изменения в коде или процессе. Но это единственные две вещи, которые я обнаружил, которые отличались между моей проблемой и ее решением.

0 голосов
/ 17 мая 2019

depen_on не гарантирует ожидания успешного запуска базы данных. Поэтому вам придется изменить свой подход и реализовать свой сценарий использования с помощью файла оболочки, который будет ожидать успешного запуска базы данных, а затем продолжит выполнение.

Для этого вам нужно будет добавить этот инструмент в ваш Dockerfile приложения, который будет выполнять фактическую работу ожидания.

ENV DOCKERIZE_VERSION v0.6.0
RUN wget 
https://github.com/jwilder/dockerize/releases/download/$DOCKERIZE_VERSION/dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz \
&& tar -C /usr/local/bin -xzvf dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz \
&& rm dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz

И тогда вам нужно будет создать файл оболочки, скажем run.sh со следующей строкой, чтобы дождаться запуска базы данных.

dockerize -wait http://$DB_HOST:$DB_PORT -timeout 3000s

и затем вы можете вызвать команду фактического приложения go для запуска приложения go из файла run.sh.

Не забудьте добавить ENTRYPOINT в Dockerfile для запуска файла .sh

...