Search code examples
dockergodocker-compose

Problem to connect container golang with container mysql


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.


Solution

  • 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.