4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Dockerコンテナ上で動くGinサーバーにアクセスできないエラーの解決法

Last updated at Posted at 2020-03-16

概要

GolangのWebフレームワークであるGinを使ったAPIサーバーを、Dockerコンテナ上にデプロイして動かそうとしたところ、APIにアクセスできずかなり長い時間悩まされました。

結果的には、Ginサーバーのコードの書き方の問題だったことが分かったのですが、解決方法を念のためここにメモしておきます。

ちなみに、このエラーはWindows10及びAWS上のUbuntuサーバー(t2.small)で起こりました(尤も、実行環境はこのエラーの発生にあまり関係ないようでしたが)。

状況

Ginを用いたAPIサーバーを立てようとしていました。まだ環境構築の段階なので、コードは以下のようなモックのものになっています。

package main

import (
	"log"
	"os"
	"github.com/gin-gonic/gin"
)

func main() {
	logConfig()

	r := gin.Default()
	r.GET("/accounting-api", func(c *gin.Context) {
		log.Println("GET")
		c.JSON(200, gin.H{
			"state": "success",
		})
	})
	r.DELETE("/accounting-api", func(c *gin.Context) {
		log.Println("DELETE")
		c.JSON(200, gin.H{
			"state": "success",
		})
	})
	log.Println("Start Server")
	r.Run()
}

func logConfig() {
	logFile, _ := os.OpenFile("log/log.txt", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
	log.SetOutput(logFile)
	log.SetFlags(log.LstdFlags | log.Lmicroseconds | log.Lshortfile)
	log.SetPrefix("[LOG] ")
}

単純に、GETDELETEメソッドで特定のパスへのリクエストが来たら、{"state": "success"}というjsonを返すだけのサーバーです。

そして、このサーバーを動かすためのDockerfileが以下です。

FROM golang:alpine
RUN apk update && apk add --no-cache git
RUN go get -u github.com/gin-gonic/gin && mkdir /usr/src && mkdir /usr/src/api
COPY ./api /usr/src/api
WORKDIR /usr/src/api
CMD ["go","run","main.go"]

ホスト上のapiというディレクトリには上記のGoファイル等があるため、それをコンテナ上にコピーして、サーバーを立ち上げます。このDockerfileをapiという名前でビルドして、それを以下のコマンドで立ち上げました。

docker run -p 8083:8080 api

ホスト上のポート8083をコンテナ上のポート8080にマッピングしています。上記のコマンドを実行すると、以下のような出力がなされ、Ginサーバーが立ち上がっていることが確認できます。

[GIN-debug] GET    /accounting-api           --> main.main.func1 (3 handlers)
[GIN-debug] DELETE /accounting-api           --> main.main.func4 (3 handlers)
[GIN-debug] Environment variable PORT is undefined. Using port :8080 by default
[GIN-debug] Listening and serving HTTP on localhost:8080

ポートを指定しなかったので、デフォルトでポート8080で動いています。このように一見ちゃんと動いているようにも見えますが、ホスト側でhttp:localhost:8083にcurlでアクセスしてみても、以下のようにエラーが出てしまいました。

curl: (52) Empty reply from server

エラー解消のため試みたこと

1. コンテナの中に入って、APIにアクセスできるか確認

まず最初に、コンテナ上で本当にGoのプログラムがちゃんと動いているのかを確認します。そのために立ち上げたdockerコンテナの中に入ってみます。

# apiサーバーの動くコンテナの中に入る
docker exec -it api /bin/ash

このコンテナのベースとなっているAlpineには/bin/bashがなかったので、/bin/ashを使います。そして以下のコマンドを打って、ちゃんとプログラムが動いているのか確かめます。

# そもそもcurlが入っていないので、インストールする
apk add --no-cache curl
# 念のためプロキシを無効にして、curlでapiサーバーにアクセスする
curl -x "" http://localhost:8080/accounting-api

curlの実行結果がこちら。

{"state": "success"}

ちゃんと結果が取れています。なので、コンテナ上ではちゃんとGinサーバーのプログラムが動いているようです。

2. DockerfileでポートをEXPOSE

コンテナ上ではプログラムは動いているようなので、次はポートマッピングの部分がうまくいっていないのではないかと疑ってみます。調べてみるとDockerfileにはEXPOSEというコマンドが書けるようなので、追加してみます。

EXPOSE 8080

これをやっても、解決しませんでした。

そもそも公式ドキュメントによると、EXPOSEコマンドは実際は何の働きもせず、特定のポートを開放する旨を開発者に知らせるための、ドキュメントのような役割しかもっていないようです。なので、EXPOSEコマンドをつけただけで問題が解決するはずがありませんでした。

3. Windowsのファイアウォールの設定の確認

開発は基本的にWindows上のDockerで行っていたため、Windowsのファイアウォールの設定を見直してみましたが、これも意味がありませんでした。

そもそも、このDockerfileをUbuntu上でビルドして立ち上げてみても、同様にAPIサーバーにアクセスできなかったため、最初からなんとなくWindowsのファイアウォールのせいではないことが分かっていましたが。

4. (これで解決)Ginサーバー側でポートの指定

Goのプログラムの中で、GInサーバーを立ち上げる部分でポートを指定するようにしたところ、上手くアクセスできるようになりました。具体的には、以下の部分です。

r := gin.Default()
r.Run(":8080")

何も指定しなくてもデフォルトで8080で立ち上がるため気にしていなかったのですが、しっかりと指定しないとどうやらダメなようです。

なぜポートはデフォルトではダメで、明示的に記さなければならないかは、よくわかりません。分かったら追記します。

4
3
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?