Ryota Onuma

Dev Container CLIとdelveでdocker compose管理のGoコンテナをデバッグできるようにする

 
#Go
サムネイル

はじめに

Dev Container CLIdelveでGoコンテナをデバッグできるようにするやり方をまとめました。 ローカル開発では複数コンテナと合わせてdocker composeを用いて開発することが多いかと思いますので、今回はdocker composeで動いているGoコンテナに対してdelveでデバッグできるようにするやり方を紹介したいと思います。

Dev Container CLIとは?

Dev Container CLIとはこちらにも書いてあるように、devcontainer.jsonから開発用のコンテナを立ち上げることができるツールです。既存のコンテナに対して追加でデバッグツールが入れることができるため、チーム管理をしているDockerコンテナのconfiguration fileを汚さずに自分好みの開発ツールを追加で入れることができます。

delveとは?

delveはGoのプログラムをデバッグできるツールです。Goのデバッガーでググるとだいたい上のほうにでてきます。

やり方

前提

今回のディレクトリ構成は最終的に以下のようになります。

├── Dockerfile
├── docker-compose.yml
├── go.mod
├── go.sum
├── main.go
├── main_test.go
├── .devcontainer
│   ├── devcontainer.json
│   └── scripts
│       └── post-start-command.sh

また、今回の例では以下のようなdocker-compose.ymlが定義されているものとします。

docker-compose.yml
version: "3.8"
services:
  go-server:
    build: 
      context: .
      dockerfile: ./Dockerfile
    tty: true 
    ports:
      - 3000:3000
    command: sleep infinity
    volumes:
      - .:/go/src/

Dockerfileの中身は以下としておきます。

Dockerfile
FROM golang:1.21.1-bullseye

WORKDIR /go/src/

COPY ./go.mod ./go.sum  ./

RUN go mod download

COPY . .

RUN useradd -m test-user

USER test-user

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

完成後のサンプルはこちらに置いてあります。

手順

Dev Container CLIをインストールする

https://github.com/devcontainers/cli を参考にCLIをインストールします。

$ npm install -g @devcontainers/cli
$ devcontainer --version
0.51.3

devcontainer.jsonを作成する

.devcontainerディレクトリを作成し、中にdevcontainer.jsonを作成します。

今回はdocker-compose.yamlで定義したgo-serverサービスに、後述するdevcontainer execコマンドを用いてアクセスさせるようにします。

中身は以下のようにします。

devcontainer.json
{
    "name": "go-server",
    "dockerComposeFile": ["../docker-compose.yml"],
    "service": "go-server", 
    "remoteUser": "test-user",
    "workspaceFolder": "/home/test-user/workspace",
    "mounts": [
        { 
            "source": ".devcontainer/scripts/post-start-command.sh",
            "target": "/home/test-user/workspace/.devcontainer/scripts/post-start-command.sh",
            "type": "bind" 
        }
    ],
    "postStartCommand": "bash .devcontainer/scripts/post-start-command.sh"
}

devcontainer.jsonではpostStartCommandというフックを利用できます。コンテナがスタートした直後に任意のスクリプトを実行することができます。今回は次のステップにてdelveをインストールするようにします。 devcontainer.jsonには他にもいくつかフックがあるので、ライフサイクルの中で任意の処理をはさみたいときは利用することが可能です。 詳しくはこちらをご覧ください。

delveをGoコンテナにインストールする設定追加

今回の例では、.devcontainerディレクトリにscriptsディレクトリを作成し、中にpost-start-command.shを作成します。

post-start-command.sh
#!/bin/bash

go install github.com/go-delve/delve/cmd/dlv@latest

dlv version

このスクリプトをpostStartCommandのフックで呼び出すことで、既存のGoコンテナ内にdelveがインストールされます。

devcontainerを動かしてみる

docker-compose.yamlが存在するディレクトリにて、以下を実行することで、devcontainer.jsonの設定を反映した状態でdocker compose upが実行されます。--workspace-folder . がないと動かないので注意してください。

$ devcontainer up --workspace-folder .

upした後は、以下コマンドでコンテナ内部に入ります。

$ devcontainer exec --workspace-folder . bash

また、2023年10月時点ではDevcontainer CLIはstopやdownを提供していないので、コンテナを止めたり削除したいときは明示的にdocker compose stopdocker compose downしてあげる必要があります。(将来的に対応する可能性はありそうです

使えるコマンド

delveを動かしてみる

実際にdelveを動かしてみます。

まずは、きちんとdelveが入っているかを確認してみます。

まずは、きちんとdelveが入っているかを確認してみます。

入っていることが確認できたので、main.goを作成し、break pointを設定してみます。

main.go
package main

import (
	"fmt"
)

func main() {
	hoge := 1
	fmt.Println(hoge)  //  ここにbreak pointを設定する予定
	hoge = 2
	fmt.Println(hoge)
	fmt.Println("Hello World")
}

実行してみます。

実行してみる

動くことが確認できました!

きちんとステップ実行していることがわかります。

続いて、testも実行できることを確認してみます。

main_test.goを作成します。

main_test.go
package main

import (
	"testing"
)

func TestRun(t *testing.T) {
	hoge := struct {
		fuga bool
	}{
		fuga: false,  //  ここにbreak pointを設定する予定
	}

	if !hoge.fuga {
		t.Errorf("fuga is not true")
	}
}

delveを実行してみます。

実行してみる

動くことが確認できました! 値を途中で上書いて実行できることがわかります。

delveでは他にも様々なことができるので、詳しくは以下のような記事を読んでみてください。

Golangのデバッガdelveの使い方

参考記事