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

我使用docker工具来组装两个容器,以帮助我更好地熟悉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上启动服务器,并尝试build立与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") } 

app.go

 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 dial tcp 127.0.0.1:3306: getsockopt: connection refused 。 我用硬编码的连接string在本地运行应用程序,它工作正常。 我不确定发生了什么,所以希望有人能帮助我!

干杯!

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

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

你还没有使用APP_DB_HOST来初始化数据库,所以你直接去localhost。

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

请参阅https://github.com/vishnubob/wait-for-it可以在容器中使用的bash脚本等待mysql db起床,然后运行主程序

编辑1:更新dockerfile

以下是您的github repo更新的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