この記事は株式会社クロノスの「~2020年春~勝手にやりますアドベントカレンダー」の10日目の記事です。
「~2020年春~勝手にやりますアドベントカレンダー」インデックスはこちら(随時更新)
はじめに
開発環境、どうやって構築していますか?
ローカルに色々インストールしちゃう人も多いと思いますが、言語のバージョン違いや環境変数などで、「自分の環境では動くんやけど、他人の環境では動けへん。なんでや・・・」みたいな光景を開発者であれば一度は目にしてきたのではないでしょうか。
解決方法はいくつかありますが、今回はVSCodeのRemote DevelopmentとDockerを使った方法を紹介したいと思います。
DockerはもはやWebエンジニアにとっての必須教養と言っても過言ではないと思いますので、これを期にDockerでの環境構築を始めてみても良いのではないでしょうか。
今回は僕の中で話題のGolangの環境を構築してみます。(GoもDockerもマジで社内で流行ってほしい)
最終的に出来上がったものはこちら
Remote Developmentとは
VSCodeの拡張機能で、その名の通りリモートでの開発をサポートする拡張機能です。
Remote SSH、Remote Containers、Remote WSLの3つの拡張機能がセットになっています。
今回はRemote Containersを使って、Dockerで立てたコンテナの中で開発していきます。
動作環境
- Mac
- VSCode
- Remote Development(VSCode拡張機能)
- Docker for Mac
(Win HomeのDocker ToolBoxだとたぶん上手くいかないので注意。ここにもDocker Desktop for Windows/Macって書かれてる)
ちょっと試してみる
Microsoftさんがいい感じのプロジェクトを用意してくれているのでcloneします。(他の対応言語もいくつかあって便利そう)
git clone https://github.com/microsoft/vscode-remote-try-go.git
cloneできたらVSCodeで開きましょう
code vscode-remote-try-go
開くと以下のようなダイアログが出てくるので「Reopen in Container」をクリック
最初だけDockerfileからイメージの作成と、コンテナの作成を行うので結構時間かかります。
で、VSCodeのターミナルが以下のようになったらオッケーです。
以上でGolangで開発できる環境が整いました。
開発用のコンテナが作成され、その中で開発を進めることができます。
めっちゃ簡単ですよね。
動かしてみる
F5かデバッグのメニューから三角のアイコンをクリックすることででアプリケーションの起動、デバッグができます。(デフォルトだと9000番ポートで起動します)
アプリの標準出力等はデバッグコンソールに出てきます。
補完機能も快適です。
リポジトリ一つ落としてくるだけで環境作れるなんて素敵じゃないですか?
この辺りは公式のREADME読めば大体書かれています。
実運用で使えそうなことあれこれ
リポジトリの作成
ここはGitの使い方のような感じになります。
先程の手順でリポジトリをcloneする際、そのままcloneしてきましたが
実運用で使用する場合、以下のように別名を指定してcloneするのが良いでしょう。
git clone https://github.com/microsoft/vscode-remote-try-go.git [別名]
で、このままだとリモートがMicrosoftのリポジトリのままなので
git remote -v
// 結果
origin https://github.com/microsoft/vscode-remote-try-go.git (fetch)
origin https://github.com/microsoft/vscode-remote-try-go.git (push)
リモートに自分で作成したリポジトリのURLを設定し、pushしましょう
git remote set-url origin [自分で作成したリポジトリのURL]
git push
開発用コンテナの作成をDocker Composeで行う
Docker Composeで環境を立ち上げたいことも多いと思います。
今回はMySQLのコンテナを立ち上げ、Golangで作ったアプリケーションからアクセスする想定で設定を修正してみます。
まずはdocker-compose.yml、DB周りのファイルを追加しましょう。
.
├── .devcontainer
│ ├── Dockerfile
│ ├── devcontainer.json // 修正
│ └── docker-compose.yml // 追加
├── db // 追加
│ ├── conf
│ │ └── my.conf // 追加(MySQL設定)
│ └── init
│ └── 00_init.sql // 追加(テストデータ)
└── src
{
"name": "Go Sample",
// dockerFile -> dockerComposeFile
"dockerComposeFile": [
"docker-compose.yml"
],
// docker-compose.servicesのどのコンテナで作業するのかを設定する
"service": "app",
// workspaceを設定する
"workspaceFolder": "/app",
"runArgs": [ "--cap-add=SYS_PTRACE", "--security-opt", "seccomp=unconfined" ],
// docker-compose.yml内で設定できるので不要
//"appPort": [9000],
"settings": {
"terminal.integrated.shell.linux": "/bin/bash",
"go.gopath": "/go",
"go.inferGopath": true,
"go.useLanguageServer": true
},
"remoteUser": "vscode",
"extensions": [
"ms-vscode.go"
]
}
version: '3'
services:
db:
image: mysql
command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
ports:
- '33306:3306'
expose:
- '3306'
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: sample
MYSQL_USER: test
MYSQL_PASSWORD: test
TZ: "Asia/Tokyo"
volumes:
- ../db/conf:/etc/mysql/conf.d
- ../db/init:/docker-entrypoint-initdb.d
app:
build: ./
working_dir: /app
ports:
- '11323:1323'
volumes:
- ../:/app
expose:
- '1323'
command: sleep infinity
[mysql]
default-character-set=utf8
[mysqld]
character-set-server=utf8
USE sample;
SET CHARACTER_SET_CLIENT = utf8;
SET CHARACTER_SET_CONNECTION = utf8;
CREATE TABLE users(
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50) NOT NULL
);
INSERT INTO users(name) VALUES ('Alex');
ファイルが追加できたら再度コンテナ内で開き直しましょう。
次にコンテナ内でMySQLのドライバを取得します。
go get github.com/go-sql-driver/mysql
ドライバが取得できたらMySQLにアクセスしデータを取得するようにserver.goを修正します。
package main
import (
"database/sql"
"fmt"
"io"
"net/http"
_ "github.com/go-sql-driver/mysql"
)
// 修正部分
func handle(w http.ResponseWriter, r *http.Request) {
// DB接続部分。接続文字列は(MySQLユーザー名:パスワード@tcp(ホスト名:ポート)/DB名)
db, err := sql.Open("mysql", "test:test@tcp(db:3306)/sample")
if err != nil {
panic(err)
}
defer db.Close()
stmt, err := db.Prepare("select * from users where id < ?")
if err != nil {
panic(err)
}
defer stmt.Close()
var (
id int
name string
)
rows, err := stmt.Query(2)
for rows.Next() {
err := rows.Scan(&id, &name)
if err != nil {
panic(err)
}
fmt.Println(id, name)
}
defer rows.Close()
if err = rows.Err(); err != nil {
panic(err)
}
io.WriteString(w, "Hello, "+name)
}
func main() {
// docker-composeで設定したポートを設定する
portNumber := "1323"
http.HandleFunc("/", handle)
fmt.Println("Server listening on port ", portNumber)
http.ListenAndServe(":"+portNumber, nil)
}
設定できたらブラウザからlocalhost:11323にアクセスしてみましょう。
Hello, Alexと表示されたら成功です。
また、以下のようなエラーが出たら、goplsをUpdateしてあげると直るかもしれません。
The gopls server crashed 5 times in the last 3 minutes. The server will not be restarted.
Go Modulesで依存関係を解決する
ライブラリの管理をGo Modulesで行うようにしてみましょう。
まずはworkspace直下でgo mod initを実行します。(initの後のモジュール名は任意です)
go mod init example.com/sample
次にgo buildを実行します。
go build src/main/server.go
実行できたらgo.sumファイルが作成され、
go.modにmysql driverが追加されるはずです。
次にsettings.jsonをGo Modules用に修正します。
{
"go.useLanguageServer": true,
// true -> false
"go.inferGopath": false,
// 追加
"go.toolsEnvVars": {
"GO111MODULE": "on",
},
}
これでGo Modulesでライブラリを管理できるようになりました。
試しにGolangのWebフレームワーク、Echoをインストールしてみましょう
go get -u github.com/labstack/echo/...
go.modが更新されます。
module example.com/sample
go 1.14
require (
github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect
github.com/go-sql-driver/mysql v1.5.0
github.com/labstack/echo v3.3.10+incompatible // indirect
github.com/labstack/gommon v0.3.0 // indirect
github.com/mattn/go-colorable v0.1.6 // indirect
github.com/valyala/fasttemplate v1.1.0 // indirect
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073 // indirect
golang.org/x/net v0.0.0-20200301022130-244492dfa37a // indirect
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527 // indirect
golang.org/x/text v0.3.2 // indirect
)
ついでにserver.goを以下のように修正してみましょう。
package main
import (
"database/sql"
"fmt"
"net/http"
_ "github.com/go-sql-driver/mysql"
"github.com/labstack/echo"
"github.com/labstack/echo/middleware"
)
func handle(c echo.Context) error {
// DB接続部分。接続文字列は(MySQLユーザー名:パスワード@tcp(ホスト名:ポート)/DB名)
db, err := sql.Open("mysql", "test:test@tcp(db:3306)/sample")
if err != nil {
return c.String(http.StatusInternalServerError, "Error")
}
defer db.Close()
stmt, err := db.Prepare("select * from users where id < ?")
if err != nil {
return c.String(http.StatusInternalServerError, "Error")
}
defer stmt.Close()
var (
id int
name string
)
rows, err := stmt.Query(2)
for rows.Next() {
err := rows.Scan(&id, &name)
if err != nil {
return c.String(http.StatusInternalServerError, "Error")
}
fmt.Println(id, name)
}
defer rows.Close()
if err = rows.Err(); err != nil {
return c.String(http.StatusInternalServerError, "Error")
}
return c.String(http.StatusOK, "Hello, "+name)
}
func main() {
// Echo instance
e := echo.New()
// Middleware
e.Use(middleware.Logger())
e.Use(middleware.Recover())
// Routes
e.GET("/", handle)
// Start server
e.Logger.Fatal(e.Start(":1323"))
}
FWも導入して実運用っぽい雰囲気が出てきたのではないでしょうか?
結局何が嬉しいのか
- GOPATHなど、Golang動かすのに必要な設定とかもろもろ面倒くさいのが楽になる
- ローカル環境汚さずに動かせるのがめっちゃいい
- コマンド一発で環境ができるので楽ちん
- 全く同じ環境を他人に共有できる(環境構築の手順を間違えることほとんどがなくなる)
などなど。
settings.jsonやdevcontainer.jsonなどの設定項目がたくさんあるので最初は大変かもしれませんが、上記のようなメリットもかなり大きいと思います。
参考
VS Code Remote Development(公式)
Go言語の依存モジュール管理ツール Modules の使い方
Go の echo ってWebサーバーでサクッと REST しちゃう
VS Code Remote Development で Docker 開発環境を利用する