以前は、macOSでのGoのプロジェクトについて考えました。
今回はGo (golang)のDocker開発環境を考えてみます。 拡張性を意識して、docker-composeを利用してみます。
※ ヴァージョンの関係で1発でうまくいかない場合もあります。 分かり次第更新します!(2019/02/22更新、下部記載)
想定として、なる早で環境構築し、GoのWebアプリを開発していこう!みたいなノリです。 ちなみに、Goのフレームワークとして、echoをインストールしていきます。 ゴールは、ブラウザでのHello Worldを目指します。
私の環境は、macOSです。 事前にDocker (macならDocker for macをインストールしましょう!)
さて、順をおってみていきましょう!
プロジェクトの作成
今回、Dockerなので特にホストOS(私ならmac)のGOPATHを意識する必要はありません。 (Docker上でのGOPATHは指定します。) 気になる方は、この記事の冒頭であげた過去の記事を参考にしてみて下さい。
なので、任意のディレクトリに新規プロジェクトを作成しましょう。
必要なファイルを作成
コマンド docker-compose upで起動させるために、必要な設定ファイルを新規作成します。 この記事では必要最低限の記述にします。portの指定などご自身のやりたいことに合わせて設定してみて下さい。 ※ 自分の使用しているdocker及びdocker-composeのversionにも注目!
Dockerfile
FROM golang:latest WORKDIR /go/src/go-app2 COPY . . # "Go" Environment variable ENV GO111MODULE=on # install Go web framework RUN go get -u github.com/labstack/echo # install "Fresh" restart tool RUN go get github.com/pilu/fresh CMD ["fresh"]
上からみていきます。
goのヴァージョンは最新のものを取得しにいってます。(ヴァージョン指定も勿論可能)
GO111MODULEというGOの環境変数をonに設定します。 これでモジュールを自動的に読み込むことができます。
これをDockerfileに記述しないと、docker-compose upした時に以下のWARNINGが出ました。
go get: warning: modules disabled by GO111MODULE=auto in GOPATH/src; ignoring go.mod; see 'go help modules'
GO111MODULEを記述した際に、プロジェクトディレクトリに自動的にgo.modというファイルが作成されます。 (= 今回のプロジェクトに依存したgo.modファイルが生成される。)
そして、フレームワークのechoとホットリロードをするためのFreshをgo getでインストールします。 go getを使用するということは、今回作成されるDocker上には、複数のプロジェクトを共存させる目的ではないことを覚えておいて下さい。 仮に、go getをすると、GOPATH配下にインストールされて2つ目以降のパッケージの依存関係を管理しにくくなるからです。 (goから離れていたので、間違ってたらごめんなさい...!!!!)
GO111MODULE
さきほど定義した環境変数GO111MODULEについてです。 こちらヴァージョン1.11から追加された、モジュール管理モードの環境変数です。(正式リリースは、1.12かららしい)
on : モジュール対応モード off : GOPATH モード(今まで通り) auto : GOPATH以外のみモジュールモード
今までdepなどを使用していたと思いますが、これは便利! depからの乗り換えも楽みたいですね!
ちなみに、今回のDocker環境で開発中に新しいモジュールを追加すると、freshが検知し自動的にモジュールがインストールされます。
そうすると、go.modに require package/name v1.1x.2x
のように追記されていることが確認できました。
また、go.sumファイルも生成されます。これは、モジュールの依存関係を示すファイルのようです。
そして最後にコマンド実行です。
ホットリロードについてですが、どういうことかというと コンパイル不要な言語ならいいのですが、一旦コンパイルしてから動作させる言語において、 よくDockerで起動させられたけど、ファイルの編集をした際にもう一度docker runとかしないといけない...ということが起こります。
かなり面倒なので、タスクランナー的なものが必要です。 厳密に今回使用するFreshはタスクランナーとは書いてないようですが、 要はファイルを監視し変更が検知されると、自動的にrestartしてくれるものです。
なので、Dockerfileでは、最後にfresh
というコマンドで起動させます。
Freshについては、以下の公式を参照してみて下さい。 Fresh自体の設定ファイルも作成できるようです。
docker-compose.yml
続いてdocker-compose.ymlですが、そんなに記述がないので説明を割愛します。
version: '3' services: app: build: . container_name: <projecrt_name> volumes: - ./:/go/src/<project_name> ports: - "8080:8080" environment: GO111MODULE: "on"
ここまで作成できれば、ほぼ完成です。 Dockerfileを基に、1つのコンテナと1つのイメージが作成されるはずです。(golangのイメージ含めるなら2つかな?)
あとは適当にmain.goを作成します。
goファイル
- main.go
package main import ( "log" "net/http" "path/filepath" "sync" "text/template" ) type templateHandler struct { once sync.Once filename string templ *template.Template } func (t *templateHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { t.once.Do(func() { t.templ = template.Must(template.ParseFiles(filepath.Join("templates", t.filename))) }) t.templ.Execute(w, nil) } func main() { http.Handle("/", &templateHandler{filename: "top.html"}) if err := http.ListenAndServe(":8080", nil); err != nil { log.Fatal("ListenAndServe", err) } }
- templates/top.html
<p>Hellooooooooooooooooo World!!!!!!!!!!!!!!</p>
実行
いざ実行です!
$ docker-compose up Building app Step 1/7 : FROM golang:latest ---> d817ad5b9beb Step 2/7 : WORKDIR /go/src/go-app2 Removing intermediate container 816a33af9a01 ---> bb3d6012e557 Step 3/7 : COPY . . ---> e38c75d754d3 Step 4/7 : ENV GO111MODULE=on ---> Running in 57345b25fb4f Removing intermediate container 57345b25fb4f ---> d9500cf1f5cd Step 5/7 : RUN go get -u github.com/labstack/echo ---> Running in 01dc8d6ebd6c go: finding github.com/labstack/echo v3.2.2+incompatible go: downloading github.com/labstack/echo v3.2.2+incompatible go: finding github.com/labstack/gommon/log latest go: finding github.com/labstack/gommon/color latest ... 略 Creating goapp2_app_1 ... done Attaching to goapp2_app_1 app_1 | 11:2:58 runner | InitFolders app_1 | 11:2:58 runner | mkdir ./tmp app_1 | 11:2:58 runner | mkdir ./tmp: file exists app_1 | 11:2:58 watcher | Watching . app_1 | 11:2:58 watcher | Watching templates app_1 | 11:2:58 main | Waiting (loop 1)... app_1 | 11:2:58 main | receiving first event / app_1 | 11:2:58 main | sleeping for 600 milliseconds app_1 | 11:2:59 main | flushing events app_1 | 11:2:59 main | Started! (8 Goroutines) app_1 | 11:2:59 main | remove tmp/runner-build-errors.log: no such file or directory app_1 | 11:2:59 build | Building... app_1 | 11:3:00 runner | Running...
あとは、ブラウザでアクセス。
htmlの内容が表示されていたら成功です。
また、docker-composeを起動したまま編集してみましょう! リロードし、変更が反映されていたら大丈夫です。
Dockerコンテナの中へ
以下のコマンドでDockerコンテナの中に入って、ローカルとのファイルのシンクがされているか確認できます。
$ docker exec -i -t <container_name> bash
今回の例だと、ディレクトリ /go/src/<project_name>
が存在し、
$GOPATHは、/go
になっていることが確認できます。
まとめ
いつもホットリロードに悩まされていましたが、Freshは楽ですね! 他にもGoのプロジェクトで使用されているタスクランナーがあります。
※少しdockerで個人的に気になることがあるので、解決したら更新します!
トラブルシューティング
私が試したことしか記載できませんが、以下で1発でブラウザ確認までいけました。
go.modファイル作成(空でおk)
goのヴァージョンを確認
私の場合、1.12系で大丈夫でした。他のヴァージョンは試してないのでわかりません。。
FROM golang:1.12-rc
go.modファイル作成の理由としては、GO111MODULE=onにしているとgo.modファイルが設置されているディレクトリでのみgo getコマンドが実行できないためです。
以上.