LoginSignup
40
30

More than 3 years have passed since last update.

VSCode Remote DevelopmentでGolangの開発環境を爆速で構築する

Posted at

この記事は株式会社クロノスの「~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」をクリック
スクリーンショット 2020-03-01 14.19.02.png

最初だけDockerfileからイメージの作成と、コンテナの作成を行うので結構時間かかります。

で、VSCodeのターミナルが以下のようになったらオッケーです。

スクリーンショット 2020-03-01 14.28.50.png

以上でGolangで開発できる環境が整いました。
開発用のコンテナが作成され、その中で開発を進めることができます。
めっちゃ簡単ですよね。

動かしてみる

F5かデバッグのメニューから三角のアイコンをクリックすることででアプリケーションの起動、デバッグができます。(デフォルトだと9000番ポートで起動します)
アプリの標準出力等はデバッグコンソールに出てきます。
補完機能も快適です。

terminal.gif

リポジトリ一つ落としてくるだけで環境作れるなんて素敵じゃないですか?

この辺りは公式の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
devcontainer.json(元と差分のあるところのみコメントを付与している)
{
    "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"
    ]
}
docker-compose.yml
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

my.conf
[mysql]
default-character-set=utf8

[mysqld]
character-set-server=utf8
00_init.sql
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を修正します。

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用に修正します。

settings.json
{
    "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を以下のように修正してみましょう。

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 開発環境を利用する

40
30
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
40
30