mysql - Docker 应用程序容器不会与我的 MySQL 容器通信

标签 mysql docker go docker-compose

我正在使用 docker compose 将两个容器放在一起,以帮助我更好地熟悉 docker,但我似乎无法弄清楚为什么我的两个容器无法相互通信。

我的应用程序的 dockerfile 是:

FROM golang
ADD . /go/src
WORKDIR /go/src

RUN go get github.com/go-sql-driver/mysql
RUN go get github.com/gorilla/mux
RUN go build -o bin/main main.go app.go model.go

ENTRYPOINT /go/src/bin/main

EXPOSE 8080

我的 docker-compose.yml 是

version: '3'

services:
  db:
    image: mysql:5.7
    volumes:
      - db_data:/var/lib/mysql
    restart: always
    ports:
      - "3306:3306"
    expose:
      - "3306"
    environment:
      MYSQL_ROOT_PASSWORD: testrootpassword
      MYSQL_DATABASE: testing
      MYSQL_USER: testuser
      MYSQL_PASSWORD: testpassword

  api:
    depends_on:
      - db
    build: .
    ports:
      - "8080:8080"
    # restart: always
    environment:
      APP_DB_HOST: db:3306
      APP_DB_NAME: testing
      APP_DB_USERNAME: testuser
      APP_DB_PASSWORD: testpassword
volumes:
  db_data:

主应用程序只是在端口 8080 上启动一个服务器,并尝试建立到 SQL 的连接。完整的实现在这里:https://github.com/bliitzkrieg/go-sql-testing

package main

import (
    "os"
)

func main() {
    a := App{}
    a.Initialize(
        os.Getenv("APP_DB_USERNAME"),
        os.Getenv("APP_DB_PASSWORD"),
        os.Getenv("APP_DB_NAME"))

    a.Run(":8080")
}

应用程序去

package main

import (
    "database/sql"
    "encoding/json"
    "fmt"
    "log"
    "net/http"
    "strconv"

    _ "github.com/go-sql-driver/mysql"
    "github.com/gorilla/mux"
)

type App struct {
    Router *mux.Router
    DB     *sql.DB
}

func (a *App) Initialize(user, password, dbname string) {
    connectionString :=
        fmt.Sprintf("%s:%s@/%s", user, password, dbname)

    var err error
    a.DB, err = sql.Open("mysql", connectionString)
    if err != nil {
        log.Fatal(err)
    }

    a.ensureTableExists()
    a.Router = mux.NewRouter()
    a.initializeRoutes()
}

func (a *App) ensureTableExists() {
    if _, err := a.DB.Exec(tableCreationQuery); err != nil {
        log.Fatal(err)
    }
}

func (a *App) Run(addr string) {
    log.Fatal(http.ListenAndServe(":8080", a.Router))
}

func (a *App) initializeRoutes() {
    a.Router.HandleFunc("/", a.sayHello).Methods("GET")
    a.Router.HandleFunc("/products", a.getProducts).Methods("GET")
    a.Router.HandleFunc("/product", a.createProduct).Methods("POST")
    a.Router.HandleFunc("/product/{id:[0-9]+}", a.getProduct).Methods("GET")
    a.Router.HandleFunc("/product/{id:[0-9]+}", a.updateProduct).Methods("PUT")
    a.Router.HandleFunc("/product/{id:[0-9]+}", a.deleteProduct).Methods("DELETE")
}

func (a *App) sayHello(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hello Luca!")
}

func (a *App) getProduct(w http.ResponseWriter, r *http.Request) {
    vars := mux.Vars(r)
    id, err := strconv.Atoi(vars["id"])
    if err != nil {
        respondWithError(w, http.StatusBadRequest, "Invalid product ID")
        return
    }

    p := product{ID: id}
    if err := p.getProduct(a.DB); err != nil {
        switch err {
        case sql.ErrNoRows:
            respondWithError(w, http.StatusNotFound, "Product not found")
        default:
            respondWithError(w, http.StatusInternalServerError, err.Error())
        }
        return
    }

    respondWithJSON(w, http.StatusOK, p)
}

func (a *App) getProducts(w http.ResponseWriter, r *http.Request) {
    products, err := getProducts(a.DB)
    if err != nil {
        respondWithError(w, http.StatusInternalServerError, err.Error())
        return
    }

    respondWithJSON(w, http.StatusOK, products)
}

func (a *App) createProduct(w http.ResponseWriter, r *http.Request) {
    var p product
    decoder := json.NewDecoder(r.Body)
    if err := decoder.Decode(&p); err != nil {
        respondWithError(w, http.StatusBadRequest, "Invalid request payload")
        return
    }
    defer r.Body.Close()

    if err := p.createProduct(a.DB); err != nil {
        respondWithError(w, http.StatusInternalServerError, err.Error())
        return
    }

    respondWithJSON(w, http.StatusCreated, p)
}

func (a *App) updateProduct(w http.ResponseWriter, r *http.Request) {
    vars := mux.Vars(r)
    id, err := strconv.Atoi(vars["id"])
    if err != nil {
        respondWithError(w, http.StatusBadRequest, "Invalid product ID")
        return
    }

    var p product
    decoder := json.NewDecoder(r.Body)
    if err := decoder.Decode(&p); err != nil {
        respondWithError(w, http.StatusBadRequest, "Invalid resquest payload")
        return
    }
    defer r.Body.Close()
    p.ID = id

    if err := p.updateProduct(a.DB); err != nil {
        respondWithError(w, http.StatusInternalServerError, err.Error())
        return
    }

    respondWithJSON(w, http.StatusOK, p)
}

func (a *App) deleteProduct(w http.ResponseWriter, r *http.Request) {
    vars := mux.Vars(r)
    id, err := strconv.Atoi(vars["id"])
    if err != nil {
        respondWithError(w, http.StatusBadRequest, "Invalid Product ID")
        return
    }

    p := product{ID: id}
    if err := p.deleteProduct(a.DB); err != nil {
        respondWithError(w, http.StatusInternalServerError, err.Error())
        return
    }

    respondWithJSON(w, http.StatusOK, map[string]string{"result": "success"})
}

func respondWithError(w http.ResponseWriter, code int, message string) {
    respondWithJSON(w, code, map[string]string{"error": message})
}

func respondWithJSON(w http.ResponseWriter, code int, payload interface{}) {
    response, _ := json.Marshal(payload)

    w.Header().Set("Content-Type", "application/json")
    w.WriteHeader(code)
    w.Write(response)
}

const tableCreationQuery = `CREATE TABLE IF NOT EXISTS products
(
id SERIAL,
name TEXT NOT NULL,
price NUMERIC(10,2) NOT NULL DEFAULT 0.00,
CONSTRAINT products_pkey PRIMARY KEY (id)
)`

为了运行应用程序,我使用了 docker 命令 docker-compose up -d --build 来构建应用程序。当我运行 docker ps 时,它只显示我的 SQL 服务器已启动,当我查看我的 api 容器的日志时,它显示一行内容为 2017/10/01 06:54:14调用 tcp 127.0.0.1:3306: getsockopt: 连接被拒绝。我使用硬编码连接字符串在本地运行该应用程序并且运行良好。我不太确定发生了什么,希望有人能帮助我!

干杯!

最佳答案

您的代码中有两个问题。一个是

a.Initialize(
    os.Getenv("APP_DB_USERNAME"),
    os.Getenv("APP_DB_PASSWORD"),
    os.Getenv("APP_DB_NAME"))

您还没有使用APP_DB_HOST 来初始化数据库,所以您将直接转到localhost。

其次,您在程序开始时连接到数据库,数据库需要一些时间才能真正启动。所以你需要在你的代码中有一些重试和超时,或者你应该等待数据库启动然后运行主命令。

参见 https://github.com/vishnubob/wait-for-it一个bash脚本,可以在容器中使用,等待mysql db启动,然后运行主程序

Edit-1:更新 dockerfile

以下是您的 github 存储库的更新 dockerfile https://github.com/bliitzkrieg/go-sql-testing/

FROM golang
ADD . /go/src
WORKDIR /go/src

RUN go get github.com/go-sql-driver/mysql
RUN go get github.com/gorilla/mux
RUN go build -o bin/main main.go app.go model.go
RUN git clone https://github.com/vishnubob/wait-for-it.git

CMD ./wait-for-it/wait-for-it.sh --host=db --port=3306 --timeout=60 -- /go/src/bin/main

EXPOSE 8080

关于mysql - Docker 应用程序容器不会与我的 MySQL 容器通信,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46510166/

相关文章:

python - Django 创建和更改 mysql 数据库以供学习之用

mysql - 简单的SQL查询永远持续下去

docker - 在Docker容器中运行Scrapy

json - 使用 Go 获取 JSON 数组中的特定键

testing - Golang 有 libfaketime 替代品吗?

regex - 用于验证长度的正则表达式

MySQL一对多连接?

mysql - 从 AMI 创建的 AWS EC2 实例无法运行(错误 504)- WEBb 无法运行

browser - Docker:如何运行grunt-open?

ruby-on-rails - 在 Docker 中运行 Rails 应用程序时 Yarn 包已过时