Edited at

Docker上のGoLangをリモートデバッグする


概要

Docker上で動作するGoLangアプリケーションをデバッグする方法を解説します。


環境


  • ホストOS(macOS Mojave バージョン 10.14.6)

  • GoLand 2019.1.3

  • Docker Desktop for Mac(Docker version 18.09.2)

  • 利用するDockerイメージ(golang:1.13.0-alpine3.10)

この記事ではホストPC上で利用するツールとしてGoLandを利用しますが、Visual Studio Code 等でも同じことは実現出来ると思います。


2019-09-11 追記

Go 1.13でもこの記事の内容で動作する事を確認済です👍


2019-09-14 追記

2019-09-11の時点では動作していたのですが、現在 go get -u github.com/oxequa/realize で以下のエラーが発生します。

build github.com/oxequa/realize: cannot load gopkg.in/urfave/cli.v2: cannot find module providing package gopkg.in/urfave/cli.v2


2019-09-21 追記

回避策を教えてもらったので、記載しておきます。

下記のように realize のインストール前に gopkg.in/urfave/cli.v2 をインストールします。

  go get gopkg.in/urfave/cli.v2@master && \

go get github.com/oxequa/realize && \

realize の依存関係が修正されれば、この対応は必要なくなりますが、現時点ではこうしないと正常にインストールする事が出来ません。

この件に関しては realize にissueを作っておきました。

https://github.com/oxequa/realize/issues/253

最終的に動作したDockerfileの内容も載せておきます。

realizego get する際に -u が消えている点にご注意下さい。

ENV GO111MODULE on が消えているのはGoのバージョンが 1.13 になっている為、明示的に指定が不要になった為です。


Dockerfile

FROM golang:1.13.0-alpine3.10 as build

LABEL maintainer="https://github.com/nekochans"

WORKDIR /go/app

COPY . .

RUN set -ex && \
apk update && \
apk add --no-cache git && \
go build -o portfolio-backend && \
go get gopkg.in/urfave/cli.v2@master && \
go get github.com/oxequa/realize && \
go get -u github.com/go-delve/delve/cmd/dlv && \
go build -o /go/bin/dlv github.com/go-delve/delve/cmd/dlv

FROM alpine:3.10

WORKDIR /app

COPY --from=build /go/app/portfolio-backend .

RUN set -x && \
addgroup go && \
adduser -D -G go go && \
chown -R go:go /app/portfolio-backend

CMD ["./portfolio-backend"]



Docker側の設定


Dockerfileの内容


Dockerfile

FROM golang:1.12.7-alpine3.10 as build

ENV GO111MODULE on

WORKDIR /go/app

COPY . .

RUN set -ex && \
apk update && \
apk add --no-cache git && \
go build -o portfolio-backend && \
go get -u github.com/oxequa/realize && \
go get -u github.com/go-delve/delve/cmd/dlv && \
go build -o /go/bin/dlv github.com/go-delve/delve/cmd/dlv

FROM alpine:3.10

WORKDIR /app

COPY --from=build /go/app/portfolio-backend .

RUN set -x && \
addgroup go && \
adduser -D -G go go && \
chown -R go:go /app/portfolio-backend

CMD ["./portfolio-backend"]


go get -u github.com/go-delve/delve/cmd/dlvgo build -o /go/bin/dlv github.com/go-delve/delve/cmd/dlv の部分が重要になります。

delve というGoLang用のDebuggerをインストールしています。

開発時にホットリロードを有効にしたいので go get -u github.com/oxequa/realize でrealizeをインストールしています。


docker-composeの設定

以下のように設定します。


docker-compose.yml

version: '3.7'

services:
portfolio-backend:
build:
context: .
dockerfile: Dockerfile
target: build
volumes:
- ./:/go/app
command: realize start --run
ports:
- 8888:8888
- 2345:2345
security_opt:
- apparmor:unconfined
cap_add:
- SYS_PTRACE


commandrealize を起動させています。

ports8888(WebServer用のポート)と 2345(デバッグ用のポート)を開放します。

security_opt, cap_add の2つはDocker上で delveを利用する為の設定です。

    security_opt:

- seccomp:unconfined

Dockerが行うシステムコールを制限するオプションです。

(参考)https://docs.docker.com/engine/security/seccomp/


cap_add:
- SYS_PTRACE

コンテナの capabilities の追加を行います。

(参考)https://docs.docker.com/engine/reference/run/#runtime-privilege-and-linux-capabilities

(参考)Docker コンテナ上で Go のプロジェクトを delve でデバッグするには ptrace の許可が必要


realizeの設定

下記の通りです。


.realize.yaml

settings:

legacy:
force: false
interval: 0s
schema:
- name: app
path: .
commands:
install:
status: true
method: go build -o portfolio-backend
run:
status: true
method: /go/bin/dlv
args:
- exec
- ./portfolio-backend
- --headless=true
- --listen=:2345
- --api-version=2
- --accept-multiclient
watcher:
extensions:
- go
paths:
- /
ignore:
paths:
- .git
- .realize
- vendor

重要なのは下記の部分です。

  commands:

install:
status: true
method: go build -o portfolio-backend
run:
status: true
method: /go/bin/dlv
args:
- exec
- ./portfolio-backend
- --headless=true
- --listen=:2345
- --api-version=2
- --accept-multiclient

ファイルの変更が行われる度にBuildを実行し delve を再読み込みします。

argsは delve の実行時に渡す引数になります。


GoLand側の設定


GoLandデバッグのデバッグ設定を作成する

Run → Debug → 0. Edit Configurations... から設定を作成します。

createDebug1.png

この記事の通りにやれば delve での利用ポートはデフォルトの2345なので Go Remote のTemplateを使って作成すれば良いです。

createDebug2.png

Name はこの記事では app としていますが、わかり易ければ何でも構いません。


GoLand側からDockerコンテナに接続する

Run → Debug → から先程作成したデバッグ設定を開きます。

ConnectRemoteServer1.png

下記のように表示されていれば、接続が完了しています。

ConnectRemoteServer2.png

ちなみにDockerのログを見ると下記のようにアプリケーションの起動を確認出来るかと思います。

$ docker logs -f 0957f7698bbc

[08:35:25][APP] : Watching 1 file/s 2 folder/s
[08:35:25][APP] : Install started
[08:35:26][APP] : Install completed in 0.585 s
[08:35:26][APP] : Running..
[08:35:26][APP] : API server listening at: [::]:2345

[08:46:10][APP] : 2019/07/27 08:46:10 env: develop
[08:46:10][APP] : 2019/07/27 08:46:10 Starting app


GoLandでブレークポイントを設定してデバッグ

ブレークポイントを設定してアプリケーションのURLにアクセスしましょう。

ブレークポイントでアプリケーションが一時停止し変数の中身等が確認出来るかと思います。

ConnectRemoteServer3.png


あとがき

ここまでのコードはGitHubに公開してあります。

https://github.com/nekochans/portfolio-backend

Debuggerが使えると開発効率が上がりますので、少々時間をかけてでも設定を行うことをオススメします。

以上になります。最後まで読んで頂きありがとうございます。