I'm new to Docker. This project is just for my own understanding. It's likely that I use incorrect terminology and/or am more confused than I think I am. Corrections are gratefully accepted.
I'm using two docker images: the official postgres image and my own Go app and Dockerfile. Using docker-compose up
I get the connection refused
error.
I think that there are potentially two different problems:
One, the database is not running when the app is trying to connect.
Two, the app is simply using an invalid IP.
I have app code that should be giving the database time to start up to address the first potential problem (see code below). Judging by the error message, I don't think I'm even getting that far.
I have two services: db-access (that's the Go app) and postgres-db.
I have tried using these host names in the app connection string:
"localhost",
"postgres-db" (as it's named in the docker-compose.yml),
"0.0.0.0".
Using the postgres-db
as the hostname:
The app container is trying: dial tcp 172.22.0.2:5432.
Postgres is saying: listening on IPv4 address "0.0.0.0", port 5432.
In the docker-compose.yml
I have tried using these statements:
depends_on:
- postgres-db
and
links:
- postgres-db
I have tried reversing the order of the services in the docker-compose.yml but they appear to start up in the same order either way.
When I run the postgres container and the Go app separately I get the expected behavior. To run them separately I'm using these commands:
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
followed by
go run basicapi
I can also run docker-compose up
, which gives the connection refused error, then ctrl+C
, then run go run basicapi
and get the expected behavior.
Here are the project files . . .
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"
Expected results: Navigating to localhost:3000/users shows:
1, Someone, Alastname,
2, SomeoneElse, AnotherLastName,
etc...
Actual results:
browser: This site can't be reached
terminal:
~/ >> 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
Thank you for any insights.
depends_on doesn't guarantee to wait till the database starts successfully. So you will have to change your approach and implement your usecase using shell file that will wait for database to start successfully and will then proceed the execution further.
For this you will have to add this tool in your app Dockerfile that will do the actual wait job.
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
And then you will have to create a shell file say run.sh with following line to wait for the database start.
dockerize -wait http://$DB_HOST:$DB_PORT -timeout 3000s
and then you can call your actual go application command to run your go application from within run.sh file.
Do not forget to add the ENTRYPOINT in Dockerfile to run.sh file
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With