「GolangでCUIでWebsocketを試したい」のserver.goをdockerコンテナに閉じ込めたい

前提の記事はこちら

このサーバをさらにDockerのコンテナに搭載します。

サーバも何も入っていないが、Golang開発環境だけが入っているコンテナを作ります。DockerでGoの開発環境を構築するを参考させて頂きました。

mkdir work # workディレクトリ作成
cd work # 作成したworkディレクトリに移動

ここに、Dockerfileとdocker-compose.ymlを作ります。

(Dockerfileの内容)
# ベースとなるDockerイメージ指定
FROM golang:latest
# コンテナ内に作業ディレクトリを作成
RUN mkdir /go/src/work
# コンテナログイン時のディレクトリ指定
WORKDIR /go/src/work
# ホストのファイルをコンテナの作業ディレクトリに移行
ADD . /go/src/work

上記の、/go/src/work は、ローカル(PC)のディレクトリではなくて、コンテナの中のディレクトリなので、パスの構成は気にしなくてよい。

(docker-compose.yml
の中身)
version: '3' # composeファイルのバーション指定
services:
  app: # service名
    build: . # ビルドに使用するDockerfileがあるディレクトリ指定
    tty: true # コンテナの起動永続化
    volumes:
      - .:/go/src/work # マウントディレクトリ指定

次に、

docker-compose build

を実行する。

$ docker-compose build
Building app
Step 1/4 : FROM golang:latest
 ---> a794da9351a3
Step 2/4 : RUN mkdir /go/src/work
 ---> Using cache
 ---> 094b928e9bfb
Step 3/4 : WORKDIR /go/src/work
 ---> Using cache
 ---> b7a938d02446
Step 4/4 : ADD . /go/src/work
 ---> 9d95f42d64e3
Successfully built 9d95f42d64e3
Successfully tagged work_app:latest

さらに、docker-compose up -d を実行する。

$ docker-compose up -d
Creating network "work_default" with the default driver
Creating work_app_1 ... done

コンテナができているか同化を確認します。

$ docker-compose ps
   Name      Command   State   Ports
------------------------------------
work_app_1   bash      Up

現在、~/go_echo にある、server.goを、~/go_echo/workにコピーします。

これで、server.go が、ローカル(PC)と、コンテナの中で共有されるようになります。

では、コンテナの中に入ります。

$ winpty docker container exec -it work_app_1 bash # 江端のMSYS2 のシェルでは、"winpty"を付ける必要があるが、通常のシェルでは不要

root@abe44622eccf:/go/src/work# ls
'#main.go#'   Dockerfile   Dockerfile~   server.go   docker-compose.yml   docker-compose.yml~   main   main.go   main.go~   websocket-server.go

ここで、Goプログラムを実行します。

root@abe44622eccf:/go/src/work# go run server.go
server.go:21:2: cannot find package "github.com/gorilla/websocket" in any of:
        /usr/local/go/src/github.com/gorilla/websocket (from $GOROOT)
        /go/src/github.com/gorilla/websocket (from $GOPATH)

ふむ、やはり、こうなりますね。では、パッケージをインストールしましょう。

root@abe44622eccf:/go/src/work#  go get github.com/gorilla/websocket

何のメッセージも出さずに20秒後くらいに静かにプロンプトが戻ってきました(ちょっと不安になる)。

root@abe44622eccf:/go/src/work# go run client_multi_agent.go
connecting to ws://localhost:8080/echo
dial:dial tcp 127.0.0.1:8080: connect: connection refused
exit status 1

うーん、やっぱりネットワーク回りの設定をしていないから当然か。ポート開けていないし。docker-compose downして、docker-compose.ymlに以下を追加してみました。

ports:
  - "8080:8080"
$ more docker-compose.yml
version: '3' # composeファイルのバーション指定
services:
  app: # service名
    build: . # ビルドに使用するDockerfileがあるディレクトリ指定
    tty: true # コンテナの起動永続化
    volumes:
      - .:/go/src/work # マウントディレクトリ指定
    ports:
      - "8080:8080"

それと、"go get github.com/gorilla/websocket"を毎回実行しない為には、Dockerfileに "RUN go get github.com/gorilla/websocket"を1行加えれば良いみたい。

# ベースとなるDockerイメージ指定
FROM golang:latest
# コンテナ内に作業ディレクトリを作成
RUN mkdir /go/src/work
# コンテナログイン時のディレクトリ指定
WORKDIR /go/src/work
# ホストのファイルをコンテナの作業ディレクトリに移行
ADD . /go/src/work

# gorillaのパッケージを入れる
RUN go get github.com/gorilla/websocket

そんでもって、再びdocker-compose build → docker-compose up -d を実施して、docker container exec -it work_app_1 bash をして、コンテナの中に入って、go run server.goをやったんだけど、ブラウザで、http://localhost:8080やっても表示されない。

C:\Users\ebata\go_echo\work>curl http://localhost:8080/
curl: (52) Empty reply from server

となっているところを見ると。8080ポートは外側に見えていないみたい。

でも、

C:\Users\ebata\go_echo\work>netstat -a | grep 8080
  TCP         0.0.0.0:8080           DESKTOP-P6KREM0:0      LISTENING
  TCP         [::]:8080              DESKTOP-P6KREM0:0      LISTENING

にはなっているんだよなぁ。

試しに、コンテナの中で"curl http://localhost:8080/"をやってみたら、

oot@b30e685f9cc6:/go/src/work# curl http://localhost:8080/

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script>
window.addEventListener("load", function(evt) {
    var output = document.getElementById("output");
    var input = document.getElementById("input");
    var ws;

てな感じで、ばっちり見えます。

うーん、変だなぁ。"8080:8080"ってポートフォワードしているじゃないのか? WebSocketの場合は、特殊な設定がいるのか?分かりません。

もう6時間くらい闘ったので、引き上げようとして、最後に、"curl: (52) Empty reply from server" でググってみたら、ドンピシャな感じの記事を見つけました。

docker上のアプリにlocalhostでアクセスしたらERR_EMPTY_RESPONSEが出る

まさに、同じ現象が表われていて、『そう! そう!』と言いながら読み進めていき、結果として、

(解決) アプリの設定を0.0.0.0でLISTENするよう変更する

と記載されていましたので、server.goのソースコードを、

//var addr = flag.String("addr", "localhost:8080", "http service address")
var addr = flag.String("addr", "0.0.0.0:8080", "http service address") // テスト

と変更してみたところ、http://localhost:8080 でWebもcurlも表示されるようになりました。

ああ、これで、サーバのコンテナ化にメドがついた ―― と思ったら、どっと疲れが出てきました。

『Dockerのコンテナは、江端が作れ』と、指示されていましたので。


Dockerfileの最後に、

CMD ["go", "run", "server.go"]

をつけると、dockerコンテナをサーバ化にできる。

ただし、

# docker-compose build, 
# docker-compose up (-d ← これを付けたらダメ。バックグラウンドで起動するとDockerが終了してしまう)

で起動すること。

2020/08,江端さんの技術メモ

Posted by ebata