DockerでGoの開発環境を構築する方法

Goを書く環境を、Dockerのコンテナで作りました。
Macに直接Goを入れて、環境を汚したくなかったためです。
この記事は、DockerComposeでコンテナを立ち上げ、VSCodeからコンテナに入ってHelloWorldを動かすまでの記録です。
途中で出たエラーと、VSCodeのGo拡張を入れたときにつまずく点も書いています。
忙しい人向け
-
golang:1.22.2-alpineのイメージでコンテナを作り、VSCodeのDevContainers拡張で実行中のコンテナにアタッチします。 -
repository name ... must be lowercaseのエラーは、services:の下のサービス名に大文字があると出ます。DockerComposeはイメージ名を「プロジェクト名-サービス名」で付けますが、イメージ名には小文字しか使えないためです。
-
Go1.22.2のままだと、Go拡張が入れるgopls(Goの補完や定義ジャンプを担うツール)の最新版は入りません。
goplsは直近2つのGoの版しかサポートしないためです。
Go1.22.2では、goplsの
v0.16.2なら入りました。
前提条件
- Mac用のDockerDesktopが入っている。
- VSCodeにDevContainers拡張が入っている。
- Goのソースは、Macのフォルダをコンテナにマウントして置く。
発生環境
最初にこの記事を書いたときは、次の環境でした。
- OS: macOS(Sonoma)
- エディタ: VSCode
- Go:
1.22.2
記事を書き直すときに、同じ構成を手元のMacで試し直しました。
この記事のコマンドの結果は、そのときのものです。
- Mac: AppleSilicon、macOS
27.2 - DockerEngine:
29.1.3 - DockerCompose:
v5.0.0 - イメージ:
golang:1.22.2-alpine(Alpine3.19.1、go versionの結果はgo1.22.2 linux/arm64)
プロジェクトの構成
プロジェクト名は、AWSのSSM(パラメータストア)で試したいことがあったので、それに合わせて付けました。
AWS-SDK-GO-V2-SSM/
├── ssmTest/
│ ├── docker/
│ │ └── dockerfile
│ └── src/ # コンテナの /ssmTest にマウントする
└── docker-compose.yml

ファイル一式はGitHubのリポジトリに置いています。
手順
dockerfileを書く
公式のgolangイメージのうち、軽いAlpine版を使いました。
FROM golang:1.22.2-alpine
WORKDIR /src
RUN apk update && \
apk upgrade
docker-compose.ymlを書く
version: "3.8"
services:
aws-sdk-go-v2-ssm:
container_name: AWS-SDK-Go-v2-ssm
build:
context: .
dockerfile: ./ssmTest/docker/dockerfile
tty: true
volumes:
- ./ssmTest/src:/ssmTest
ports:
- 8080:8080
tty: trueは、コンテナを起動したままにしておくための指定です。
このイメージは起動すると/bin/shを実行するだけなので、これが無いとすぐ終了してしまいます。
tty: trueを付けずに起動して確かめると、2秒後にはExited (0)になっていました。
止まったコンテナには、VSCodeからアタッチできません。
volumesで、MacのssmTest/srcをコンテナの/ssmTestにマウントしています。
コンテナの中で書いたソースは、Macのフォルダにそのまま残ります。
先頭のversion: "3.8"は、今のDockerComposeでは使われていません。
起動すると次の警告が出ますが、動作には影響しません(DockerComposeの公式ドキュメント)。
the attribute `version` is obsolete, it will be ignored, please remove it to avoid potential confusion
コンテナを起動する
docker-compose.ymlがあるディレクトリで、次のコマンドを実行します。
docker compose up -d --build
--buildでイメージを作り、-dでコンテナを裏で動かしたままにします。
大文字のサービス名で出るエラー
services:の下のサービス名をAWS-SDK-Go-v2-ssmのように大文字で書くと、起動のときに次のエラーになります。
unable to get image 'aws-sdk-go-v2-ssm-AWS-SDK-Go-v2-ssm': Error response from daemon: invalid reference format: repository name (library/aws-sdk-go-v2-ssm-AWS-SDK-Go-v2-ssm) must be lowercase
原因は、DockerComposeが付けるイメージ名です。
image:を書かずにbuild:だけを書くと、イメージ名は「プロジェクト名-サービス名」になります。
小文字のサービス名で起動したときのログでも、イメージ名は次のようになっていました。
naming to docker.io/library/aws-sdk-go-v2-ssm-aws-sdk-go-v2-ssm:latest done
プロジェクト名は、docker-compose.ymlがあるディレクトリの名前から付きます。
ディレクトリ名はAWS-SDK-GO-V2-SSMと大文字ですが、プロジェクト名は小文字のaws-sdk-go-v2-ssmになっていました(プロジェクト名の規則)。
一方、サービス名はそのままイメージ名に入ります。
Dockerのイメージ名に使えるのは小文字・数字・区切り記号だけなので(イメージ名の文法)、大文字が混ざると上のエラーになります。
サービス名を小文字にすれば直ります。
container_nameは大文字のままでも、エラーにはなりませんでした。
コンテナ名はイメージ名とは別の扱いだからです。
VSCodeからコンテナにアタッチする
VSCodeの左下にある、大なり小なりが重なったようなボタンを押します。
ボタンが無いときは、DevContainers拡張が入っていません。

出てきたメニューから「実行中のコンテナーにアタッチ…」を選びます。
コマンドパレットからDev Containers: Attach to Running Container...を実行しても同じです(VSCodeの公式ドキュメント)。

起動したコンテナを選ぶと、コンテナの中を開いた新しいウィンドウが立ち上がります。
「フォルダーを開く」で、volumesで指定した/ssmTestを開きます。
dockerfileのWORKDIRは/srcですが、ソースを置くのはマウントした/ssmTestのほうです。
HelloWorldを動かす
VSCodeのターミナル(コンテナの中)で、モジュールを作ります。
go mod init main
go: creating new go.mod: module main
main.goを作り、HelloWorldを表示するだけのソースを書きます。
package main
import "fmt"
func main() {
fmt.Println("Hello, World!")
}
実行します。
go run main.go
Hello, World!
これで、Dockerのコンテナの中でGoを動かせる状態になりました。
Go拡張とツールを入れる
コンテナの中で開発を続けるなら、VSCodeのGo拡張も入れておきます。
コンテナにアタッチしたウィンドウの拡張機能の画面で、一番上に出る「Go」です。

入れたら、command+shift+pでGo: Install/Update Toolsを選び、すべてにチェックを入れて「OK」を押します。
補完や定義ジャンプを担うgoplsなどのツールが、コンテナの中に入ります。
最初にこの記事を書いたときは、Go1.22.2でもこれで入りました。
ところが、書き直すときに同じイメージでgoplsの最新版を入れようとすると、次のエラーになりました。
go install golang.org/x/tools/gopls@latest
go: golang.org/x/tools/gopls@latest: golang.org/x/tools/gopls@v0.24.0 requires go >= 1.27.0 (running go 1.22.2; GOTOOLCHAIN=local)
goplsの最新版が、Go1.27以上を求めているためです。
goplsの公式ドキュメントには、サポートするのは直近2つのGoの版だけだと書かれています。
時間がたつと、同じGoの版のままでも最新のgoplsは入らなくなります。
Goには、必要な版のGoを自動で取ってくる仕組み(GOTOOLCHAIN)がありますが、公式のイメージではGOTOOLCHAIN=localになっていて、入っているGoしか使いません(Goのツールチェーンの説明)。
Goの版を上げられないときは、古い版のgoplsを指定して入れます。
Go1.22.2では、v0.17.1はrequires go >= 1.23.1で入らず、v0.16.2は入りました。
go install golang.org/x/tools/gopls@v0.16.2
golang.org/x/tools/gopls v0.16.2
新しく環境を作るなら、dockerfileのFROMを新しいGoのイメージにするほうが手間はかかりません。



