I have a little project in Go basically with a connection.
Dockerfile
FROM golang:1.22.3 AS build-stage
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o /go-start ./cmd/server/main.go
FROM scratch
WORKDIR /
COPY --from=build-stage /go-start /go-start
COPY --from=build-stage /app/.env .env
EXPOSE 8080
ENTRYPOINT ["/go-start"]
Then I have a docker-compose.yml with my Dockerfile and a MySQL image
services:
zumenu-backoffice-api:
container_name: zumenu-backoffice-api
build:
context: .
dockerfile: ./Dockerfile
ports:
- 8080:8080
env_file:
- ./.env
depends_on:
- mysql
networks:
- zumenu-network
mysql:
image: mysql:5.7
container_name: mysql-zumenu-backoffice
platform: linux/amd64
volumes:
- ./build/mysql:/var/lib/mysql
environment:
- MYSQL_ROOT_PASSWORD=${DB_PASSWORD}
- MYSQL_DATABASE=${DB_NAME}
- MYSQL_PASSWORD=${DB_PASSWORD}
ports:
- 3312:3306
networks:
- zumenu-network
networks:
zumenu-network:
driver: bridge
And my Go code to establish the connection
package configs
import (
"errors"
_ "github.com/go-sql-driver/mysql"
"github.com/jmoiron/sqlx"
)
func LoadConDB() (*sqlx.DB, error) {
db, err := sqlx.Connect("mysql", "root:root@(localhost:3312)/zumenu_db")
// or mysql:3306 or mysql-zumenu-backoffice:3306 don't work
if err != nil {
return nil, errors.New("failed to connect to the database")
}
return db, nil
}
package main
import (
"fmt"
"github.com/andremartinsds/api-backoffice-zumenu/configs"
)
func main() {
_, err = configs.LoadConDB()
if err != nil {
panic(err)
}
fmt.Println("ok!!!")
}
When I start the MySQL container and run the Go application locally without Docker, it works. But when everything is run with Docker, it doesn't work.
I expect to run everything with Docker.
I have consolidated your Go code into a single file:
package main
import (
"fmt"
"log"
"os"
_ "github.com/go-sql-driver/mysql"
"github.com/jmoiron/sqlx"
)
func main() {
user := os.Getenv("DB_USER")
password := os.Getenv("DB_PASSWORD")
connection := fmt.Sprintf("%s:%s@(mysql:3306)/zumenu_db", user, password)
fmt.Println("Connecting to database: ", connection)
db, err := sqlx.Connect("mysql", connection)
if err != nil {
log.Fatal(err)
}
err = db.Ping()
if err != nil {
log.Fatal(err)
}
fmt.Println("Database connection successful")
}
That uses DB_USER
and DB_PASSWORD
from .env
to construct the connection string. Note that the connection string uses the name of the MySQL service (mysql
) rather than localhost
. This is important for making connections between the containers since localhost
for the Go process is not the same as localhost
for the MySQL process.
I also updated docker-compose.yml
with a health check to ensure that the database is ready for connections before starting the Go code.
services:
zumenu-backoffice-api:
build:
context: .
dockerfile: ./Dockerfile
ports:
- 8080:8080
env_file:
- ./.env
depends_on:
mysql:
condition: service_healthy
mysql:
image: mysql:5.7
environment:
- MYSQL_ROOT_PASSWORD=${DB_PASSWORD}
- MYSQL_DATABASE=${DB_NAME}
- MYSQL_USER=${DB_USER}
- MYSQL_PASSWORD=${DB_PASSWORD}
ports:
- 3312:3306
healthcheck:
test: ["CMD", "mysqladmin", "-u${DB_USER}", "-p${DB_PASSWORD}", "ping"]
interval: 5s
timeout: 5s
retries: 5
The backoffice API will only start once the mysql
service is marked as "healthy", and that only happens when mysqladmin
is able to ping the database using the username and password provided in .env
.