はじめに
概要
Docker と VSCode Remote Container による Go の Web サーバ開発環境を構築する記事です。
ローカルに Go をインストールすることなく、
- コード補完
- コード変更があるたびに再ビルド(ライブリロード)
の Go 開発環境を構築します。
リポジトリ
この記事で作成する内容は GitHub に上げています。
背景
業務で Go を触ることとなり、勉強するにあたって手軽な Go の開発環境の構築方法を調べていました。
Docker を使った Go の開発環境を構築する方法はいくつか情報があったのですが、コード補完やフォーマッタを効かせるために結局ローカルに Go をインストールする必要があり、少し微妙でした。
また、簡単な Web サーバを書いていたのですが、ソースコードを修正するたびにいちいちビルドし直すのが面倒でした。
色々と試している中で、
- ローカルに Go を入れないといけない問題: VSCode の拡張機能 Remote Container
- いちいちビルドし直す問題: Go のパッケージ Air
で解決できることが分かり、本記事はその成果物です。
できるだけ手軽に、Go の Web サーバ開発環境を整えたい人向けの記事となっています。
開発環境の構築
動作環境
ディレクトリ構造
.
|
├── .devcontaier/
| └── devcontainer.json
├── .air.toml
├── docker-compose.yml
└── Dockerfile
コンテナ設定
FROM golang:1.16.3-alpine
ENV GO111MODULE on
WORKDIR /go/src/app
RUN apk update \
&& apk add git \
&& go install github.com/cosmtrek/air@latest \
&& go get -u golang.org/x/tools/gopls \
github.com/ramya-rao-a/go-outline
ここでは、Go 拡張機能で使用する ranguage server に必要な gopls や、ライブリロードに必要な Air のバイナリインストール&ビルドを行っています。
version: "3.8"
services:
web:
build:
context: .
dockerfile: Dockerfile
command: "air"
tty: true
stdin_open: true
command: "air"
volumes:
- ./app:/go/src/app
ports:
- 8080:8080
security_opt:
- apparmor:unconfined
cap_add:
- SYS_PTRACE
ライブリロード設定
.air.toml で、ライブリロードの設定を行います。
今回は、公式のサンプルコードをそのまま拝借して使用します。
コンテナ接続設定
VSCode Remote Container 用の設定をします。
{
"name": "Go-Practice",
"dockerComposeFile": [
"../docker-compose.yml",
],
"service": "web",
"workspaceFolder": "/go/src/app",
"settings": {
"terminal.integrated.shell.linux": "/bin/ash",
"go.toolsManagement.checkForUpdates": "off",
"go.gopath": "/go",
"go.gocodeAutoBuild": true,
"go.formatTool": "gofmt",
"go.useLanguageServer": true,
"editor.formatOnSave": false,
"[go]": {
"editor.formatOnSave": true
}
},
"extensions": [
"golang.go"
],
}
extensions
の部分で、ワークスペースに任意の VSCode 拡張をインストールすることができます。今回は、最低限の設定として Go の VSCode 拡張を入れています。
他にも、今回は軽量なalpine
イメージを使用するので、
"terminal.integrated.shell.linux": "/bin/ash",
の部分でワークスペースで使用するシェルにash
を指定しています。
alpine
でもbash
を使いたい方は、イメージビルド時に bash を入れたりするなど、適宜カスタマイズしてください。
開発環境の起動
Docker イメージのビルド
docker-compoase build
コンテナの立ち上げ
docker-compose up -d
docker-compose logs -f web
でコンテナのログを見ると、ライブリロードのair
が立ち上がっていることがわかります。
web_1 | __ _ ___
web_1 | / /\ | | | |_)
web_1 | /_/--\ |_| |_| \_ // live reload for Go apps, with Go
web_1 |
開発コンテナ接続
Remote Container でコンテナに接続する方法はいくつかあります。以下のうちどれかを行います。(どれでもいい)
以下、全て VSCode での作業
-
[Cmd + Shift + P] > [reopen in container]を選択
-
VSCode の左下にある >< を押す > [reopen in container]を選択
-
左側のアイコン(Remote Explerer) > Containers > 該当のコンテナを選択
Docker のコンテナ内で、VSCode を開くことができました。
コーディングしてみる
Go コンテナの中に入ることができたので、とりあえず Gin で Web サーバーを立てて動作を確認します。
パッケージのインストール
go mod init プロジェクト名
で go.mod を作成したら、
go get -u github.com/gin-gonic/gin
でgin
をインストール。
サーバー立ち上げ
touch main.go
package main
import "github.com/gin-gonic/gin"
func main() {
r := gin.Default()
r.GET("/ping", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "pong",
})
})
r.Run() // listen and serve on 0.0.0.0:8080 (for windows "localhost:8080")
}
動作確認
{ "message": "pong" }
ちゃんと動いてそうです。
ライブリロードの確認
コードを少し変更してみます。
package main
import "github.com/gin-gonic/gin"
func main() {
r := gin.Default()
r.GET("/ping", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "pong",
})
})
+ r.GET("/hoge", func(c *gin.Context) {
+ c.JSON(200, gin.H{
+ "message": "fuga",
+ })
+ })
r.Run() // listen and serve on 0.0.0.0:8080 (for windows "localhost:8080")
}
再度動作確認
web_1 | main.go has changed
web_1 | building...
web_1 | running...
web_1 | [GIN-debug] GET /ping --> main.main.func1 (3 handlers)
web_1 | [GIN-debug] GET /hoge --> main.main.func2 (3 handlers)
{ "message": "fuga" }
ちゃんと変更が反映されていることがわかります。
おわりに
手軽な Go の開発環境構築として、Docker・Air・VSCode Remote Container を使用する方法を紹介させていただきました。
「DB コンテナも使いたい」となったらdocker-compose.yml
に DB コンテナの記述を加えたり、「もっといろいろワークスペースをカスタマイズしたい」となったら拡張機能をワークスペースに入れたりなど、色々と柔軟に拡張できると思います。
最後に、メリットデメリットをまとめておきます。
本記事で紹介する環境のメリット/デメリット
メリット
- Docker, VSCode があれば作れる
- ローカルに Go をインストールせずに、コード補完等のサポートが効く
- コードを変更すると、自動でソースコードをリビルドしてくれる
- docker コマンドをいちいち流さなくても良い
デメリット
- git 管理の構成を考えるのが難しい
- 1 コンテナにつき開ける VSCode のウィンドウが一つ
- 何らかのポートを開けっぱなしにするとき、VSCode のデバッグが使えない
メリットもある一方、デメリットも多くあるなぁと筆者自身感じています。特にデバッグ構成については、Web サーバの開発であればライブリロードと相性が悪いです。
(以下、デバッグと Air が共存できない例)
- 変更があるたびにソースコードがビルドされ、ポート
:8080
で Gin の Web サーバが立ち上がっている。 - ここで、VSCode でデバッグを行うと、デバッグのためにソースコードがビルドされる。
- デバッグでのビルド時、同じくポート
:8080
で Gin をサーバを立ち上げようとするため、ポートの競合が発生し、デバッグモードにできない。
こちらについては、未だベストな解決方法が思いついておりません。結局、ライブリロードをやめてデバッグモードでの開発が良いのかなと思っています。
以上のメリット・デメリットを踏まえた上で、今回の環境構築を試していただければと思います。
もっと良い方法があるよ!等ありましたら、是非教えていただきたいです 🙇♂️
参考資料