Docker+GolangをVisualStudioCodeでリモートデバッグする

  • 12
    いいね
  • 0
    コメント

はじめに

DockerにGolangの環境を閉じ込めたときデバッグどうするの?と思ってなかなか情報を集められなかったのでまとめます。

検証環境

ホスト側
  • Windows10 + Boot2Docker + VisualStudioCode
  • Sierra + Docker for Mac + VisualStudioCode
コンテナ側
  • golang:onbuildを使用(執筆時点ではgolang 1.7)

Golangのデバッガについて

VisualStudioCodeではDelveを使うようなので、Delveを使います。
ドキュメントやissuse見てるとリモートデバッグもサポートしていて、もしかしてDockerもいけるかもってところが発端。

Dockerのリモートデバッグについて

リモートデバッグが可能なようで、こちらをベースにGolang用に書き換えます。
Live Debugging With Docker

用意したファイル

$GOPATH/src/github.com/user
|--.vscode
|  launch.json
|--docker-compose.yml
|--Dockerfile
|--main.go

main.go
// main.go
package main

import (
    "fmt"
    "log"
    "net/http"

    "github.com/garyburd/redigo/redis"
)

func main() {
    redi, err := redis.Dial("tcp", "redis:6379")
    if err != nil {
        log.Fatal(err)
    }

    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        res, err := redi.Do("incr", "counter")
        if err != nil {
            w.WriteHeader(500)
            w.Write([]byte(err.Error()))
            return
        }

        if res, ok := res.(int64); ok {
            w.Write([]byte(fmt.Sprintf("counter: %d", res)))
        } else {
            w.WriteHeader(500)
            w.Write([]byte("unexpected value"))
        }
    })
    log.Fatal(http.ListenAndServe(":5000", nil))
}
Dockerfile
# Dockerfile
FROM golang:onbuild
RUN go get github.com/derekparker/delve/cmd/dlv
docker-compose.yml
# docker-compose.yml
app:
  build: .
  ports:
    - "5000:5000" #golang webserver用のポート
    - "5050:5050" #delve用のポート
  links:
    - redis
  volumes:
    - ".:/go/src/app"
  privileged: true # Delveで必要なので付与します。セキュリティ面から本番環境では使うべきではありません。 
  command: dlv debug --headless --listen=:5050 --log 

redis:
  image: redis
launch.json
{
    "version": "0.2.0",
    "configurations": [
         {
            "name": "Remote",
            "type": "go",
            "request": "launch",
            "mode": "remote",
            "remotePath": "/go/src/app", //コンテナ内のパス
            "port": 5050, // dlv debugのポートを指定(WEBのポートではない)
            "host": "192.168.99.100", //左の設定はToolBoxを使った場合のDocker MachineのIP. for Macの場合はlocalhostでよかった.
            "program": "${workspaceRoot}", //${workspaceRoot} = $GOPATH/src/github.com/user
            "env": {},
            "args": [],
            "showLog": true
        }
    ]
}

main.goはこちらの記事を参考にしています。
redisを使って数字をインクリメントして表示するシンプルな処理。
参考)Dockerを使ったGolang開発環境

リモートデバッグを行う

コンテナを起動する

main.goのカレントでコンテナをビルドして、起動

> docker-compose build
redis uses an image, skipping
Building app
Step 1 : FROM golang:onbuild
# Executing 3 build triggers...
Step 1 : COPY . /go/src/app
Step 1 : RUN go-wrapper download
 ---> Running in ff0ae2dae819
+ exec go get -v -d
github.com/garyburd/redigo (download)
Step 1 : RUN go-wrapper install
 ---> Running in d778a728a336
+ exec go install -v
github.com/garyburd/redigo/internal
github.com/garyburd/redigo/redis
app
 ---> 0b0ffd816054
Removing intermediate container ff0ae2dae819
Removing intermediate container d778a728a336
Removing intermediate container 677062574a96
Step 2 : RUN go get github.com/pilu/fresh
 ---> Running in 06b38d625b20
 ---> 3bc412d615be
Removing intermediate container 06b38d625b20
Step 3 : RUN go get github.com/derekparker/delve/cmd/dlv
 ---> Running in 10b51b638607
 ---> 206e4fb5f2c0
Removing intermediate container 10b51b638607
Successfully built 206e4fb5f2c0
> docker-compose up -d
> docker-compose ps
      Name                    Command               State                       Ports

----------------------------------------------------------------------------------------------------------
user_app_1     dlv debug --headless --lis ...   Up      0.0.0.0:5000->5000/tcp, 0.0.0.0:5050->5050/tcp
user_redis_1   docker-entrypoint.sh redis ...   Up      6379/tcp

コンテナが二つ立ち上がってればオッケーです。

VisualStudioCodeでデバッグを開始する

ミソはVisualStudioCodeからコンテナ内のコードを起動することらしい。
まずはVisualStudioCodeのデバッグビューを開いて、ブレークしたいコードにブレークポイントを置いておきます。

image

そんで左上のデバッグをRemoteにして実行。
※うまくいくと下のバーがオレンジになる。

image

この状態になれば、コンテナ内のmain.goが起動されているので、ブラウザでIPを叩く。(docker-machineのIPかlocalhostでいけるはず)
ブレークポイントを置いた場所にブレークするようになります。

image

あとは通常のデバッグと同じく、好きなようにデバッグできますね。

解決できていない問題点

リモートデバッグはできるようになったものの以下の問題点があって、まだ使いづらさあります。

  1. 実行中にブレークポイントの付け替えができない
  2. リモートデバッグを停止したのち再度実行するとデバッガがハングアップする
  3. DockerのprivilegedをTrueにする必要があってよろしくない

1.に関しては再起動やホットデプロイを仕込めばいけそうですが、
2.はVSCode上で停止しても新しくリモートデバッグが開始できず、結局コンテナの再起動が必要になっています。(コンテナ内にプロセスが残りっぱなしになるのがだめっぽい。killもできない。)
3. はDelveで必要な権限がわからず、全権限渡してますが、セキュリティ的によくないですね。開発環境だけにするとかの対処が必要そうです。

何かいい方法はないでしょうか・・・

さいごに

VisualStudioCodeとGolangの相性はとてもよく、エディタでありながら、IDEのように使えるし、とても使いやすいです。
Dockerを使った開発手法を確立させて、効率を上げていきたいですね。