Windows11+VSCode+Dev ContainersでGo言語開発環境構築

Windows11+VSCode+Dev ContainersでGo環境を構築する手順をまとめます。前提条件として、WSL2、WSL Dockerのインストールが完了済みとします。Docker Desktopのライセンスがある場合や個人での利用の場合は通常のDocker Desktopを入れてもOKです。

環境

  • プロセッサ 12th Gen Intel(R) Core(TM) i7-12700 2.10 GHz
  • メモリ 64 GB
  • OS Windows11 22H2 ビルド22621.1265
  • VSCode 1.76
    • Remote Development v0.24.0
    • Dev Containers v0.282.0
  • WSL
PS C:\> wsl --version
WSL バージョン: 1.0.3.0
カーネル バージョン: 5.15.79.1
WSLg バージョン: 1.0.47
MSRDC バージョン: 1.2.3575
Direct3D バージョン: 1.606.4
DXCore バージョン: 10.0.25131.1002-220531-1700.rs-onecore-base2-hyp
Windowsバージョン: 10.0.22621.1265
  • WSL Docker
Client: Docker Engine - Community
 Version:           23.0.1
 API version:       1.42
 Go version:        go1.19.5
 Git commit:        a5ee5b1
 Built:             Thu Feb  9 19:47:01 2023
 OS/Arch:           linux/amd64
 Context:           default

Server: Docker Engine - Community
 Engine:
  Version:          23.0.1
  API version:      1.42 (minimum version 1.12)
  Go version:       go1.19.5
  Git commit:       bc3805a
  Built:            Thu Feb  9 19:47:01 2023
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          1.6.16
  GitCommit:        31aa4358a36870b21a992d3ad2bef29e1d693bec
 runc:
  Version:          1.1.4
  GitCommit:        v1.1.4-0-g5fd4c4d
 docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0

構築手順

1.VSCodeのRemote DevelopmentでWSL上の開発用ディレクトリを開く

2. ctrl+shift+pでコマンドパレットを表示し、devと入力、Dev Containers: Add Dev Container Configuration Files…を選択

3. goと入力し、最初に出た項目を選択

4. 1.19-bullseyeを選択

5. Select features画面はそのままEnterでスキップ。.devcontainersディレクトリとdevcontainers.jsonが生成される

6. ctrl+shift+pでコマンドパレットを表示し、reopenと入力。Rebuild and Reopen in Containerを選択

7. 開発コンテナがセットアップされ、環境構築完了!いかがでしたか?

何が入ったのか

一昔前は環境構築で何時間と費やしていたのに今はこんなに簡単に1環境が作れるなんて便利ですね。でもよしなにやってくれて何が起きたかわからないので、詳しく見ていくことにします。まず、ウィザード形式で作られたdevcontainer.jsonの中身は下のようになっています。

// For format details, see https://aka.ms/devcontainer.json. For config options, see the
// README at: https://github.com/devcontainers/templates/tree/main/src/go
{
	"name": "Go",
	// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
	"image": "mcr.microsoft.com/devcontainers/go:0-1.19-bullseye"

	// Features to add to the dev container. More info: https://containers.dev/features.
	// "features": {},

	// Use 'forwardPorts' to make a list of ports inside the container available locally.
	// "forwardPorts": [],

	// Use 'postCreateCommand' to run commands after the container is created.
	// "postCreateCommand": "go version",

	// Configure tool-specific properties.
	// "customizations": {},

	// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
	// "remoteUser": "root"
}

各キーの意味は以下の通りのようです2

キー意味
namedev containerの名前
imageベースイメージ。ローカルのDockerfileからビルドしない場合はこれを指定。
featuresdev containerのfeature ID。featureに関しては後述。
forwardPortsコンテナからVSCodeを利用しているマシンへのポートフォワード内容
postCreateCommandコンテナ構築完了後に実行されるシェルコマンド
customizationsdev containerを利用しているプログラム(今回はVSCode)特有の設定。
remoteUserコンテナに接続する際に使うユーザー

今回imageにmcr.microsoft.com/devcontainers/go:0-1.19-bullseyeを指定しているのでこちらのイメージが使用されています。ではこのイメージはどうやって作られているかというと、dev containerのDockerfileをメンテしているリポジトリにDockerfileが置かれています。現在の内容は以下の通りです。

# [Choice] Go version (use -bullseye variants on local arm64/Apple Silicon): 1, 1.20, 1.19, 1-bullseye, 1.20-bullseye, 1.19-bullseye, 1-buster, 1.20-buster, 1.19-buster
ARG VARIANT=1.20-bullseye
FROM golang:${VARIANT}

# [Optional] Uncomment the next line to use go get to install anything else you need
# RUN go get -x <your-dependency-or-tool>

# [Optional] Uncomment this section to install additional OS packages.
# RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
#     && apt-get -y install --no-install-recommends <your-package-list-here>

# [Optional] Uncomment this line to install global node packages.
# RUN su vscode -c "source /usr/local/share/nvm/nvm.sh && npm install -g <your-package-here>" 2>&1

ということで単にGoのイメージを使っているだけに過ぎないようです。では、Goのイメージは何をやっているのか。GoのイメージのDockerfileはこちらです。

#
# NOTE: THIS DOCKERFILE IS GENERATED VIA "apply-templates.sh"
#
# PLEASE DO NOT EDIT IT DIRECTLY.
#

FROM buildpack-deps:bullseye-scm

# install cgo-related dependencies
RUN set -eux; \
	apt-get update; \
	apt-get install -y --no-install-recommends \
		g++ \
		gcc \
		libc6-dev \
		make \
		pkg-config \
	; \
	rm -rf /var/lib/apt/lists/*

ENV PATH /usr/local/go/bin:$PATH

ENV GOLANG_VERSION 1.19.6

RUN set -eux; \
	arch="$(dpkg --print-architecture)"; arch="${arch##*-}"; \
	url=; \
	case "$arch" in \
		'amd64') \
			url='https://dl.google.com/go/go1.19.6.linux-amd64.tar.gz'; \
			sha256='e3410c676ced327aec928303fef11385702a5562fd19d9a1750d5a2979763c3d'; \
			;; \
		'armel') \
			export GOARCH='arm' GOARM='5' GOOS='linux'; \
			;; \
		'armhf') \
			url='https://dl.google.com/go/go1.19.6.linux-armv6l.tar.gz'; \
			sha256='1f5900567595366fbb3b9ea02cee8ea67ac4b87234fb246549b902cde1013821'; \
			;; \
		'arm64') \
			url='https://dl.google.com/go/go1.19.6.linux-arm64.tar.gz'; \
			sha256='e4d63c933a68e5fad07cab9d12c5c1610ce4810832d47c44314c3246f511ac4f'; \
			;; \
		'i386') \
			url='https://dl.google.com/go/go1.19.6.linux-386.tar.gz'; \
			sha256='da4546cc516ae88698e8643d6d22fe1465b44dd49fc36abb34d53a93c19581ad'; \
			;; \
		'mips64el') \
			export GOARCH='mips64le' GOOS='linux'; \
			;; \
		'ppc64el') \
			url='https://dl.google.com/go/go1.19.6.linux-ppc64le.tar.gz'; \
			sha256='4ce62f9bad666e7aa73171c3056fe89b23548d1a78de8be5f30b64ccd10990de'; \
			;; \
		's390x') \
			url='https://dl.google.com/go/go1.19.6.linux-s390x.tar.gz'; \
			sha256='1673f748e25acbb2504536f41239231ac658c99e06d04ba68d51123ee62108a5'; \
			;; \
		*) echo >&2 "error: unsupported architecture '$arch' (likely packaging update needed)"; exit 1 ;; \
	esac; \
	build=; \
	if [ -z "$url" ]; then \
# https://github.com/golang/go/issues/38536#issuecomment-616897960
		build=1; \
		url='https://dl.google.com/go/go1.19.6.src.tar.gz'; \
		sha256='d7f0013f82e6d7f862cc6cb5c8cdb48eef5f2e239b35baa97e2f1a7466043767'; \
		echo >&2; \
		echo >&2 "warning: current architecture ($arch) does not have a compatible Go binary release; will be building from source"; \
		echo >&2; \
	fi; \
	\
	wget -O go.tgz.asc "$url.asc"; \
	wget -O go.tgz "$url" --progress=dot:giga; \
	echo "$sha256 *go.tgz" | sha256sum -c -; \
	\
# https://github.com/golang/go/issues/14739#issuecomment-324767697
	GNUPGHOME="$(mktemp -d)"; export GNUPGHOME; \
# https://www.google.com/linuxrepositories/
	gpg --batch --keyserver keyserver.ubuntu.com --recv-keys 'EB4C 1BFD 4F04 2F6D DDCC  EC91 7721 F63B D38B 4796'; \
# let's also fetch the specific subkey of that key explicitly that we expect "go.tgz.asc" to be signed by, just to make sure we definitely have it
	gpg --batch --keyserver keyserver.ubuntu.com --recv-keys '2F52 8D36 D67B 69ED F998  D857 78BD 6547 3CB3 BD13'; \
	gpg --batch --verify go.tgz.asc go.tgz; \
	gpgconf --kill all; \
	rm -rf "$GNUPGHOME" go.tgz.asc; \
	\
	tar -C /usr/local -xzf go.tgz; \
	rm go.tgz; \
	\
	if [ -n "$build" ]; then \
		savedAptMark="$(apt-mark showmanual)"; \
		apt-get update; \
		apt-get install -y --no-install-recommends golang-go; \
		\
		export GOCACHE='/tmp/gocache'; \
		\
		( \
			cd /usr/local/go/src; \
# set GOROOT_BOOTSTRAP + GOHOST* such that we can build Go successfully
			export GOROOT_BOOTSTRAP="$(go env GOROOT)" GOHOSTOS="$GOOS" GOHOSTARCH="$GOARCH"; \
			./make.bash; \
		); \
		\
		apt-mark auto '.*' > /dev/null; \
		apt-mark manual $savedAptMark > /dev/null; \
		apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \
		rm -rf /var/lib/apt/lists/*; \
		\
# remove a few intermediate / bootstrapping files the official binary release tarballs do not contain
		rm -rf \
			/usr/local/go/pkg/*/cmd \
			/usr/local/go/pkg/bootstrap \
			/usr/local/go/pkg/obj \
			/usr/local/go/pkg/tool/*/api \
			/usr/local/go/pkg/tool/*/go_bootstrap \
			/usr/local/go/src/cmd/dist/dist \
			"$GOCACHE" \
		; \
	fi; \
	\
	go version

ENV GOPATH /go
ENV PATH $GOPATH/bin:$PATH
RUN mkdir -p "$GOPATH/src" "$GOPATH/bin" && chmod -R 777 "$GOPATH"
WORKDIR $GOPATH

まず、実行環境のCPUアーキテクチャを取得し、https://go.dev/dl/から対応するリリースと署名をwgetします。gpgでtgzが改ざんされていないか署名と照合します。照合したらgo.tgzを/usr/localにインストールします。もし実行環境のCPUアーキテクチャに対応するGoのリリースが存在しなければビルドに必要なものをインストールしてGoをビルドします。本当に最低限の内容が入るみたいですね。

VSCode拡張の自動インストール

customizationsの項目を次のように編集します。

  "customizations": {
    // Configure properties specific to VS Code.
    "vscode": {
      // Add the IDs of extensions you want installed when the container is created.
      "extensions": ["golang.go","mhutchie.git-graph"]
    }
  }

devcontainer.jsonを編集するとVSCodeにリビルドするよう促されるのでリビルドします。といっても、Dockerfileを編集したわけではないのでキャッシュが効いてすぐリビルドは終わります。

動作確認

開発環境で

go mod init github.com/K-Nksm/hello

します。適当な位置にhello.goを作成して

package main

import "fmt"

func main() {
	fmt.Println("hello world")
}

と書きます。エディタでhello.goを開いたままF5を押すとコンパイルと実行が行われます。下みたいな表示が出ればOK。

  1. ただしVSCode/WSL/Docker導入は難しくないとする
  2. https://containers.dev/implementors/json_reference/

コメント

タイトルとURLをコピーしました