とよぶ

歌いながらコード書いてます

Dockerを使って本番さながらのGolangの環境を構築してIntelliJで開発する

やっと季節は三寒四温の状態から抜け、もう春だと言っていい季節ですが、僕は痔に苦しんでいてやっとの思いで回復に向かっております。
春って素敵。

というわけで、業務で新しいプロジェクトがGolangで実装する方針になってしまったので、仕方なくGolangの開発環境を構築します。
Scalaどこ行った。
僕自身はレガシープロジェクトに長いこといたので今更ながらDockerへの入門レベルの情報を書き並べつつ環境構築していきます。

準備

  • VirtualBoxのインストール
  • Vagrantのインストール

環境

  • Mac OSX 10.9.5 Mavericks
  • Vagrant 1.7.2

目標

MacのIntelliJからGolangのソースコードを修正して、Dockerコンテナ内に反映させ、
Macのブラウザ経由でアクセスしてレスポンスを確認できる。

仮想OSを準備してMacとファイルの同期を行う。

仮想OSはCoreOSを使います。
Vagrantfileは

github.com

から拝借します。

拝借したらひとつ細工を施します。

というのも、Macと(CoreOSと)Dockerのディレクトリを共有する方法がいくつかあるのですが、
MacからCoreOSがデフォルトではNFSになっており、
MacのIntelliJでファイルを修正した場合にCoreOSにファイルを同期してくれません。

linux - Vagrant, shared folder: take advantage of inotify over NFS - Stack Overflow

そこで同期の方法をrsyncに変更します。

#config.vm.synced_folder ".", "/home/core/share", id: "core", :nfs => true, :mount_options => ['nolock,vers=3,udp']

こう書いてあるのを

config.vm.synced_folder ".", "/home/core/share", id: "core", type: "rsync"

こう。 修正が終わったら仮想OSを立ち上げましょう。

$ vagrant up

立ち上げたら vagrant rsync-autoを実行すればIntelliJで修正したファイルがCoreOSに同期されます。
vagrant ssh して確認してみてください。
rsyncが上手く動かない時は下の方に書いてある「注意」の項を読んでください。

CoreOSでDockerコンテナを立ち上げる

上記のCoreOSには既にDockerがインストールされているので、早速Dockerを立ち上げましょう。
今回は以下のスクリプトを用意したので、それを実行してみてください。

docker-golang/create_container.sh at master · takasing/docker-golang · GitHub

まずはGolangコンテナの起動

core@core-01 ~ $ ./share/create_container.sh golang api

このスクリプト内で実行されている docker コマンドのざっくり解説はこんな感じ。

内容 意味 参考
docker run 指定したDockerコンテナを立ち上げる
--name コンテナの名前を指定
これがない場合はデフォルトでなんかかっちょいい名前をつけてもらえます(^q^)
-itd コンテナの標準入力を開いてtty(端末デバイス)を確保するそう CoreOS docker のコマンド - わすれないうちにメモしよう
-d コンテナをバックグラウンドで実行
これがないとコンテナからexitするとコンテナごとstopしてしまいます
-p ポートフォワーディングの設定
ホストOS(CoreOS)のポートとコンテナのポートを指定する
-h ホスト名を指定
-v ホストOSのファイルシステムをマウントする
vはvolumeの意味っぽい
golang:1.4 起動するイメージを指定
ローカルに無かったらpullしてくる

というわけで先ほどのコマンドを実行すればGolangのインストールされたコンテナが立ち上がります。

core@core-01 ~ $ docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS                    NAMES
1eb71b579780        golang:1.4            "/bin/bash"         12 minutes ago      Up 12 minutes       0.0.0.0:8080->8080/tcp   api
core@core-01 ~ $ docker exec -it api bash
root@golang:/go# echo $GOPATH
/go

golangのコンテナが何やってるかはDockerfileを見ればわかります。
Dockerfileあたりは次回ブログに書こうかなと思ってます。

Nginxのコンテナを立ち上げる

これもさっき用意したスクリプトを使います。

core@core-01 ~ $ ./share/create_container.sh nginx web

コンテナ間の通信には --link を使う

このnginxでは、リクエストを受け取ったらgolangのコンテナの8080ポートへプロキシしています。
コンテナ間で通信を行うための機構をDockerは用意していて、それが --link です。
--link hoge:fuga と書くことによって、
「hogeというコンテナにfugaというエイリアスでアクセス」できます。
ここではapiとそのままです。
ここで先ほどのリポジトリに含まれているnginxの設定ファイルを見てみます。

docker-golang/golang-app.conf at master · takasing/docker-golang · GitHub

--link を指定したことによって、nginxコンテナの中からapiというエイリアスでコンテナへの通信ができるようになったので、nginxの設定ファイルの中でapiというエイリアスが使えます。
これでシンプルにプロキシを設定されます。

IntelliJ側の設定

以下を参照(手抜き)。

stormcat.hatenablog.com

外部パッケージを利用したいけど、参照解決してくれないって時は

Go の開発環境は IntelliJ IDEA + golang plugin がマトモだった - Qiita
GAE - go getしたライブラリをIntelliJ IDEAの補完候補に追加するには - Qiita

このへんを参考にすれば良い。

ちなみにIntelliJを開発で使うためにはMacのローカル環境にGolangを入れなければならないようです。
DokcerのGolangのランタイム参照できたりしないのかな、しないだろうな。

ブラウザからアクセス!

あとはIntelliJで作ったプロジェクトのディレクトリを golang/go にシンボリックリンクなどをおいて自分のリポジトリとかをGolangのDockerコンテナに共有して実行してみましょう。 今回は適当にginフレームワークでGETのエンドポイントを幾つか試しに作ります。

$ cat golang/go/src/hello/hello.go
package main

import (
  "net/http"
  "github.com/gin-gonic/gin"
)

func main() {
  router := gin.Default()
  router.GET("/", func(c *gin.Context) {
    c.String(http.StatusOK, "hello world")
  })
  router.GET("/ping", func(c *gin.Context) {
    c.String(http.StatusOK, "pong")
  })
  router.GET("/pong", func(c *gin.Context) {
    c.String(http.StatusOK, "ping")
  })
  router.Run(":8080")
}
root@golang:/go# go get
root@golang:/go# go run hello.go

go get ではパッケージ内の*.goファイルのimport文を見て必要なパッケージのソースコードを$GOPATH/srcに落としてきます。
正常に立ち上がったらブラウザでVagrantのCoreOSの80ポートにアクセスすればpingだったりpongが返ってくると思います。

IntelliJでプロジェクトを作るときは

注意

もしVPN接続などで、CiscoのAnyConnect使ってる人はvagrantまでのrouteが生えなかったりしそうなので、一度切断して以下のコマンドを打ってください。もちろん環境に合わせて。

$ sudo route -nv add -net 172.17.8 -interface vboxnet0

-netに指定するところは vagrant sshした後に

core@core-01 ~ $ cat /etc/systemd/network/50-vagrant1.network

などとコマンドを実行してあげるとCoreOSまでのIPがわかるので、そのプライベートネットワークへのrouteを追加してやればよいです。
route通っていてもダメっぽい時は vagrant rsync-auto --pollとオプションを付けてみてください。

TODO

  • dockerコマンドの箇所をDockerfileにする
    GOPATHとか弄りたいかも。
  • vagrant rsync-auto でsyncされない時があるのでboot2dockerにした方がいい気がしてきたが要検証
    一応 --poll オプションをつけたら動くんですがPC重くなるっていう。
  • DBのlinkもする
    ポスグりたい。
  • --linkじゃなくてUnixドメインソケットで連携する
    Docker を利用した Web アプリケーションのデプロイ - クックパッド開発者ブログ
  • 依存関係解決ツール周りとワークスペース
    なんか公式見解含めてあんまイケてない気がする。
    godepとかgomとか使っても結局カレントディレクトリ配下にディレクトリ作って入れる感じになっていて、GOPATHをDockerfileとかで設定したとしても $GOPATH/src 以下に入れてくれないし意味あるの?みたいになる。
    ワークスペースについてはこういうのがいいらしい。
    Goコードの書き方 - The Go Programming Language
    なんかまだまだ未開拓ですね。
    誰か詳しい人いたら教えて下さい。

まとめ

とりあえず入門してみてざっと書いてみたけど、まだまだDockerとかGolang周りはベストプラクティス定まってなかったりとか議論されてたりとかするので悩ましいことがいっぱいだった。