Docker と WSL2 で開発する方法を勉強しなおす

Docker を WSL2 で動かすための設定を半年ほど前にやった。
その後普通に今まで使っていた設定ファイルで Docker を起動したところ「Windows 側にファイルがあるから遅い」みたいな警告が出て、具体的にどうすればいいかよくわからないまま完全に放置していた。
そろそろ自宅学習を再開するため、ちゃんと理解して環境構築できるようにする。

開発環境

  • Windows 10 Pro バージョン20H2
  • docker desktop 3.3.1
  • WSL2 のディストリビューションは Ubuntu 20.04

留意事項

初学者の立場だとどのディレクトリでコマンドを打てばいいのかわからなくなることがあるので、むしろどこで打ってもいいようになるべく絶対パスで記述する。

本文

以前警告が出たときに一応調べてはいて『Windows 側にあるファイルを Linux 側で参照するのは遅いので、ファイルは Linux 側に置いたほうがいい』ということは理解したのだが、具体的にどこにファイルを置いてどういう設定をすればいいかまで理解していなかった。

今回改めて公式のベストプラクティスを読んだのだが
https://docs.docker.com/docker-for-windows/wsl/#best-practices
つまり Windows の存在は完全に忘れ、自分の目の前にあるのは WSL2 の環境だけであると思っていればいいようだ。
前は Windows 上で作っていた設定ファイルがあったから混乱したのだが、最初から WSL2 の環境上で環境構築すれば問題ない。

ということでまずは VSCode に下記の拡張機能を入れて WSL2 に接続する。
https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-wsl

WSL2 に接続済みの VSCode でターミナルを開いて pwd コマンドを打ち、WSL2側のホームディレクトリにいることを確認。
適当なディレクトリを作ってその中に移動する。今回は Go の環境を作りなおそうと思うので go_docker_test とする。

$ mkdir ~/go_docker_test
$ cd ~/go_docker_test

ここにまず Dockerfile を作成する。

$ mkdir -p ~/go_docker_test/build/package
$ touch ~/go_docker_test/build/package/Dockerfile

ここで一旦 VSCode で ~/go_docker_test ディレクトリを開き、ファイルを VSCode で開きやすいようにする。

Dockerfile は最低限動くものとして下記の内容とする。

# 2021/04/17 現在の最新バージョン
FROM golang:1.16.3-alpine

WORKDIR /go/src/goapp

今回作るものの名前を goapp とし、コンテナ内の /go/src の下に goapp というディレクトリを作って作業ディレクトリとすることにした。
なお WORKDIR /go/src/goapp を書く前に mkdir /go/src/goapp と書く必要はなかった。ディレクトリが無ければ勝手に作ってくれるらしい。

次に go のファイルを作成する。

$ mkdir ~/go_docker_test/cmd
$ touch ~/go_docker_test/cmd/main.go

中身には Go 公式チュートリアルの Hello,World! するコードをコピペしておく。

package main

import "fmt"

func main() {
    fmt.Println("Hello, World!")
}

ここでイメージを構築してみる。

$ docker build --tag goapp:1.0 ~/go_docker_test/build/package

このコマンドの意味は ~/go_docker_test/build/package ディレクトリにある Dockerfile を使って、goapp という名前でタグが 1.0 のイメージを作るという感じである。  

コマンドの実行が終わったらイメージができていることを確認する。

$ docker images
REPOSITORY   TAG       IMAGE ID       CREATED          SIZE
goapp        1.0       a163110192af   31 seconds ago   301MB

このイメージを使ってコンテナを作成する。

$ docker run --name goapp -v ~/go_docker_test:/go/src/goapp -it goapp:1.0

このコマンドの意味は

run [オプション] goapp:1.0 -> goapp:1.0 のイメージを使ってコンテナを起動する

であり、[オプション]の意味はだいたい下記の通り。

  • --name goapp -> コンテナに「goapp」という名前をつける
  • -v ~/go_docker_test:/go/src/goapp -> ~/go_docker_test ディレクトリの中身をコンテナ内の /go/src/goapp ディレクトリ(※Dockerfile で作業ディレクトリに設定したディレクトリ)にマウントする
  • -it -> 疑似 TTY(pseudo-TTY)をコンテナの標準入力に接続する。つまりコンテナ内で bash シェルを作成する。このオプションをつけないとコンテナ起動した瞬間に終了してしまう。

docker run コマンドのその他オプションは下記参照。
https://docs.docker.jp/engine/reference/commandline/run.html

無事起動すると bash シェルがコンテナ内の /go/src/goapp をカレントディレクトリとして起動しているはず。

/go/src/goapp # 

ls コマンドを打ってみると、ホスト側の ~/go_docker_test ディレクトリの中身があることが確認できる。

/go/src/goapp # ls
build   cmd

(私の環境だとなぜか ls 打った後で VSCode 内のターミナルがうまく反応してくれず、自力でターミナルを一度上にスクロールしてから下にスクロールし直さないと ls の結果が見えなかった。謎。ls 打っても反応無いって人は一度がちゃがちゃスクロールしてみて。)

Go Module の初期化を行う。Module path については公式チュートリアルでソースコードの保管先、要するに github とかの URL がいいよって書いてあるのでそのようにする。(k1350 の部分はご自分のユーザー名で。)
/go/src/goapp ディレクトリで go mod init [Module path] コマンドを打つ。

/go/src/goapp # go mod init github.com/k1350/goapp

コマンドを打つと go.mod というファイルがカレントディレクトリに生成される。

これで無事 main.go を実行できるので go run [main.go があるディレクトリ] コマンドを打って実行する。

/go/src/goapp # go run /go/src/goapp/cmd/
Hello, World!

Hello World! と表示されればOK。

ついでに公式チュートリアルの先に進み、外部モジュールも呼び出してみる。
main.go を下記のように書き換える。

package main

import "fmt"
import "rsc.io/quote"

func main() {
	fmt.Println("Hello, World!")
    fmt.Println(quote.Go())
}

新しいモジュールを追加するため、/go/src/goapp ディレクトリで go mod tidy コマンドを打つ。

/go/src/goapp # go mod tidy

コマンドの実行が終わったら再び main.go を実行する。

/go/src/goapp # go run /go/src/goapp/cmd/
Hello, World!
Don't communicate by sharing memory, share memory by communicating.

Hello World! の次に Don’t ~ communicating. と出力されれば成功。

/go/src/goapp # exit

と打ってコンテナから抜け、ホスト側の ~/go_docker_test ディレクトリに戻る。
今回の場合、コンテナから抜けるとすぐコンテナが終了するはず。

$ docker ps -a

コマンドで停止したコンテナを含めた全コンテナを見ることができる。

なおこのコンテナを再度起動したい場合、コンテナに goapp という名前を付けたので下記コマンドで起動する。初回起動時は -it というオプションを付けたが、2回目以降のコマンドでは -i でいい。

$ docker start goapp -i

ここから更に docker-compose で実行できるようにしていきたいが、今回はここまでとする。

参考記事