【问题标题】:Connection Refused: Accessing Postgres container from app container with docker-compose连接被拒绝:使用 docker-compose 从应用容器访问 Postgres 容器
【发布时间】:2019-10-05 01:39:03
【问题描述】:

我是 Docker 新手。这个项目只是为了我自己的理解。我可能使用了不正确的术语和/或比我想象的更困惑。欢迎指正。

我正在使用两个 docker 镜像:官方 postgres 镜像和我自己的 Go 应用程序和 Dockerfile。使用docker-compose up 我得到connection refused 错误。

我认为可能存在两个不同的问题:

  • 一,应用程序尝试连接时数据库没有运行。

  • 二,应用只是使用了无效的IP。

我的应用程序代码应该让数据库有时间启动以解决第一个潜在问题(请参见下面的代码)。从错误消息来看,我认为我什至没有走那么远。

我有两个服务:db-access(即 Go 应用程序)和 postgres-db。

我尝试在应用连接字符串中使用这些主机名:

  • “本地主机”,

  • “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)
    }
}

models/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")
}

models/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" ]

docker-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/users 显示:

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

感谢您提供任何见解。

【问题讨论】:

  • 数据库主机的应用容器配置应该是host.docker.internal 而不是localhost

标签: postgresql macos docker go docker-compose


【解决方案1】:

depends_on 不保证等到数据库成功启动。因此,您将不得不改变您的方法并使用 shell 文件实现您的用例,该文件将等待数据库成功启动,然后继续执行。

为此,您必须在您的应用程序 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

然后你将不得不创建一个 shell 文件说 run.sh 并带有以下行来等待数据库启动。

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

然后您可以调用实际的 go 应用程序命令从 run.sh 文件中运行您的 go 应用程序。

不要忘记将 Dockerfile 中的 ENTRYPOINT 添加到 run.sh 文件中

【讨论】:

    【解决方案2】:

    我相信解决此问题的方法是拥有更好的连接等待/重试逻辑。似乎数据库在第一次失败时被 ping 和恐慌。对代码所做的唯一更改是在 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,因为我的二进制文件已经过时(基于旧代码构建)。

    遗憾的是,问题已解决,但我没有跟踪代码或流程的变化。但这是我发现问题与解决问题之间仅有的两点不同。

    【讨论】:

    • 你能附上最终的 docker-compose.yml 文件吗?
    • @Neil,原帖中的 docker-compose.yml 是最终版本。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-10-23
    • 2017-10-12
    • 2021-04-12
    • 2021-02-21
    • 1970-01-01
    • 2021-10-12
    • 2017-05-09
    相关资源
    最近更新 更多