2017-10-01 10 views
1

私はドッカーを使って2つのコンテナをまとめてドッキングすることができます。なぜ私の2つのコンテナが互いに通信できないのか分かりません。DockerアプリケーションコンテナはMySQLコンテナと通信しません

私のアプリのための私の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 

と私のドッキングウィンドウ-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への接続。完全な実装はここにある:私は、アプリケーションの罰金を構築するドッキングウィンドウコマンドdocker-compose up -d --buildを使用していたアプリケーションを実行するには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:%[email protected]/%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 psを実行すると、SQL Serverが起動していることがわかり、apiコンテナのログを見ると、1行で2017/10/01 06:54:14 dial tcp 127.0.0.1:3306: getsockopt: connection refusedと表示されます。ハードコードされた接続文字列を使ってアプリケーションをローカルに実行したところ、正常に動作しました。私は本当に何が起こっているか分からないので、うまくいけば誰かが私を助けることができます!

乾杯!

答えて

2

コードに2つの問題があります。一つは、あなたがDBを初期化するためにAPP_DB_HOSTを使用していないので、あなたは直接ローカルホストしようとしている

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

です。

2番目はプログラムの開始時にDBに接続しています。実際にはDBが実際に起動するまでにかかります。だから、あなたのコードにいくつかの再試行とタイムアウトを持たせるか、DBが立ち上がってメインコマンドを実行するのを待つ必要があります。

まで取得して、メインプログラムを実行するために、MySQLのDBを待つためにコンテナで使用することができますbashスクリプトが

編集-1 https://github.com/vishnubob/wait-for-itを参照してください。以下は

dockerfile更新が更新dockerfileですあなたのgithubのレポ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 
+1

のために彼はまた、そうでない場合は、コンテナがドッカーネットワーク上の名前 'db'と使用できなくなり、コンテナに名前' db'を与える必要があります。 'container_name:db'です。 – blacklabelops

+0

@maybeg、デフォルトでは、作成ネットワーク内でサービス名を検出できます。だからあなたは 'container_name:db'を必要としません。彼のサービス名はすでに' db'なので、これはまったく必要ではありません。そのような場合には、コンテナ名を変更するのではなく、ネットワークエイリアスを使用する方が良い方法もあります。 –

+0

ありがとうございました!それによって私の設定も改善されます! – blacklabelops

関連する問題