我是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
谢谢您的见解。
答案 0 :(得分:0)
depends_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 application命令从run.sh文件中运行go应用。
不要忘记在 Dockerfile 中添加 ENTRYPOINT 到run.sh文件
答案 1 :(得分:0)
我相信解决此问题的方法是具有更好的连接等待/重试逻辑。似乎数据库在第一次失败时便被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
,因为我的二进制文件已过期(建立在较早的代码上)。
可悲的是,问题已解决,但我没有跟踪代码或过程中的更改。但是,这是我发现的仅有的两件事,在解决问题和解决问题之间是不同的。