batboy
batboy

Reputation: 93

Can't connect to MySQL container from Golang container

I have 2 containers: a simple golang program and mysql. I can start both but the golang application can not connect to mysql. I receive Connection Refused.

If I docker exec into the mysql container and try to connect locally with mysql -u root -p all works well.

If on the the host I try to connect to the container with mysql -h 0.0.0.0 -P 10000 -u root -p all works well.

I added MYSQL_ROOT_HOST: '%' in docker-compose and bind-address = 0.0.0.0 into mysqld.cnf but still not working.

What I'm doing wrong?

docker-compose

    version: '3'
    services:
      web:
        build:
          context: .
          dockerfile: docker/web/Dockerfile
        ports:
          - "8081:8081" # http
          - "443:443" # https
        links:
          - db_private
        volumes:
         - ../../../../.:/go
      db_private:
        image: mysql:5.7
        restart: always
        environment:
          MYSQL_DATABASE: ${MYSQL_DATABASE}
          MYSQL_USER: ${MYSQL_USER}
          MYSQL_PASSWORD: ${MYSQL_PASSWORD}
          MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
          MYSQL_ROOT_HOST: '%'
        ports:
          - '10000:3306'
        expose:
          - '3306'
        volumes:
          - ./mysql-entry-point.sql:/docker-entrypoint-initdb.d
          - private-db:/var/lib/mysql
          - ./mysqld.cnf:/etc/mysql/mysql.conf.d/mysqld.cnf
    # Names our volume
    volumes:
      private-db:

Web container Dockerfile

    FROM golang:alpine
    RUN mkdir /app
    ADD . /app/
    
    WORKDIR /app
    
    RUN apk update && apk upgrade && apk add --no-cache bash git openssh
    
    RUN go get github.com/jinzhu/gorm
    RUN go get github.com/jinzhu/gorm/dialects/mysql
    RUN go get github.com/gin-gonic/gin
    RUN go get github.com/gin-contrib/cors
    
    
    RUN go build -o main .
    RUN adduser -S -D -H -h /app appuser
    USER appuser
    CMD ["./main"]

Golang file

    package main
    
    import (
        "fmt"
        "github.com/gin-gonic/gin"
        "github.com/jinzhu/gorm"
        _ "github.com/jinzhu/gorm/dialects/mysql"
        "net/http"
        "time"
    )
    
    var db *gorm.DB
    
    type (
        FooModel struct {
            Id        int64 `gorm:"primary_key"`
            name      string
        }
    )
    
    func init() {
        //open a db connection
        var err error
        db, err = gorm.Open("mysql", "user:pwd@(0.0.0.0:10000)/myDB?charset=utf8&parseTime=true")
        if err != nil {
            fmt.Println(err)
            panic("failed to connect database")
        }
        //Migrate the schema
        db.AutoMigrate(&FooModel{})
    
        db.LogMode(true)
    }
    
    // main inits routes
    func main() {
    
        router := gin.Default()
    
        router.GET("/getExample", getExample)
        
        router.Run("0.0.0.0:8081")
    }
    
    
    func getExample(c *gin.Context) {
    
        c.JSON(http.StatusOK, gin.H{"status": http.StatusOK, "data": "bye"})
    }

I copy the mysqld.cnf in the container with this

    [mysqld]
    pid-file    = /var/run/mysqld/mysqld.pid
    socket      = /var/run/mysqld/mysqld.sock
    datadir     = /var/lib/mysql
    #log-error  = /var/log/mysql/error.log
    # By default we only accept connections from localhost
    #bind-address   = 127.0.0.1
    bind-address   = 0.0.0.0
    # Disabling symbolic-links is recommended to prevent assorted security risks
    symbolic-links=0

Upvotes: 2

Views: 5201

Answers (5)

SeeSharp
SeeSharp

Reputation: 698

This can be helpful for someone! I had the same issue and spent 5 hours to figure out the issue. In my case, I was using "sqlc" for golang and also wanted to pass connection string via compose file.

enter image description here

But I got error like access denied to 'root'@'x.x.x.x' when I tried to call api by postman. Long story short, I remove double quotation marks form DB_SOURCE, and it worked!

- DB_SOURCE=root:my-secret-pw@tcp(mysql:3306)/simple_bank?parseTime=true&autocommit=true

Upvotes: 0

David SHainidze
David SHainidze

Reputation: 237

You can simply solve the problem in just 3 steps:

  1. Change your ports from 10000:3306 to 3306:3306 in the docker-compose file.
  2. Change 0.0.0.0:10000 to db_private:3306 in the Golang file.
  3. There is no need to prune containers and images. Run the following command instead:
docker-compose up --build

Upvotes: 0

jnovack
jnovack

Reputation: 8877

0.0.0.0 is not your container's IP address. It's a special address meaning "all available interfaces".

You are connecting from your Host to the Container with 0.0.0.0 because they do share an interface because you exposed the ports in your docker-compose.yml file.

If your application is always going to live in a container, and your database is always going to live in a container, Docker provides a method for you to connect one to the other.

db_private, the service name, can be found from your web (or golang) container.

This will always work, because Docker has an internal DNS server which your containers can use, no matter which IP address your containers have.

host> docker-compose up -d
host> docker-compose exec web /bin/sh
 web$ ping db_private

Yes, you always want to use 0.0.0.0 as bind interfaces within containers because you never know WHICH IP address you will get, so that's like a shortcut to say "all".

Upvotes: 1

In my case, instead use db_private of Docker host, I get my local IP by running:

ipconfig getifaddr en0 => 172.16.4.227

Then I use this IP in my gorm SQL connection:

db, err = gorm.Open("mysql", "user:pwd@(172.16.4.227:10000)/myDB?charset=utf8&parseTime=true")

This worked for me

Upvotes: 0

Burak Serdar
Burak Serdar

Reputation: 51632

Your web app is running in a container, which means the address 0.0.0.0 is the container itself, not the host it is running on. Try connecting the db using db_private name instead of the address 0.0.0.0.

Upvotes: 2

Related Questions