20200917のGoに関する記事は6件です。

Go入門 Part.1:パッケージ名のベストプラクティス

Goのパッケージ名のつけ方が理解できていなかったため、まとめてみました。
詳細は以下のドキュメントを参照ください。
Package names
Effective Go Package names

パッケージ名の仕組み

  • パッケージ名はそのソースファイルのベースディレクトリ名となる
  • 違う名前でインポートできる
    • よって先験的に衝突を恐れる必要はない
  • 違うパッケージに同じ名前の型があっても、パッケージで区別できる
import (
    pg "~省略~/external/database/postgres" // ローカルパッケージ名:pg 
    "gorm.io/driver/postgres"             // パッケージ名:       postgres
)

jpeg.Reader        // jpeg パッケージのReader関数
bufio.Reader       // bufio パッケージのReader関数
csv.Reader         // csv パッケージのReader関数

シンプルで意味のある命名をする

  • 慣習的に lowercase で一語の名前として与えられる
  • :ok_hand: 短くて簡潔で中身を想起させるようなシンプルな名詞であるべき
    • アンダースコアや mixedCaps は必要なし
time (provides functionality for measuring and displaying time)
list (implements a doubly linked list)
http (provides HTTP client and server implementations)
  • :ok_hand: 一般的に馴染みがあったり、意味を断定できるなら省略してよい
    • idiomatic(言葉として通じる単語)でなくていい
      • computeServiceClient は他の言語ではいい命名かもしれないが、Goではそうでない
strconv (string conversion)
syscall (system call)
fmt     (formatted I/O)
  • インポートしたパッケージのローカル名も、パッケージ名の命名規則に従う

アンチパターン

  • :x: 意味のないパッケージ名
    • :x: util, common, misc など
  • :x: 一つのパッケージで全てのAPIを扱う
  • :x: 不必要な衝突を招く命名
  • :x: 一般的によく使われる単語や、他のパッケージでも使っていそうなパッケージ名
  • :x: 省略によって意味が分からなくなってしまっているもの

リファクタリング

  • パッケージのいい名前が思いつかない場合、パッケージの抽象境界が間違っているかもしれない
    • その場合はクライアントコードを書いてみて、必要に応じてパッケージを再構成する

おまけ:パッケージ内の型名・関数名

ベストプラクティス

  • :ok_hand: パッケージの関数名は、しばしば型名を省略しても混乱しない
    • New 関数は、パッケージの型を返す
      • list.New()list.List を返す
    • パッケージの型ではない別の型を返す場合、その型名を関数名に含める
      • time.NewTicker() における Ticker は、パッケージの型 time とは異なると分かる
    • 関数がパッケージの型を返す場合、その型名は省略する
      • start := time.Now() において、 timeNow() とつけない
        • Now()time.Time を返すと分かるため

アンチパターン

  • :x: パッケージの型を型名につける
    • :x: http パッケージの HTTP Server の型名を HTTPServer とする
    • :x: bufio パッケージの型 ReaderBufReader などとする
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Cloud Run Go echoへの Cloud Schedulerからのリクエストで415エラー

掲題のとおりCloud Runで以下のエラーが発生

POST 415 324B 389ms Google-Cloud-Scheduler

echoはGoのwebフレームワーク
path parameterを使いたくて利用

echoのエラーログは以下

error: code=415, message=Unsupported Media Type

Unsupported Media Type とのことなのでContent-Typeが原因っぽい

結論

Cloud scheduler からくるリクエストヘッダはGUIから設定するとContent-Typeが設定されず、
echo.Context.Bindを使ったリクエストのパースをすると上記エラーが発生する

事象発生環境

  • go 1.14
  • echo 3.3.10

echo リクエストパース部分

Content-Type: application/jsonを想定していたのでリクエストボディのjsonを読み込んでstructにする部分を下記のように実装してCloud Runにデプロイ

Message struct {
    RoomNames []string `json:"RoomNames"`
}

func Handler(c echo.Context) error {
    var m Message
    m := new(Message)
    if err := c.Bind(m); err != nil {
        return err
    }
...
}

下記のcurlでは成功

curl -H "Content-Type: application/json" -d '{"RoomNames":["living", "study"]}'

Cloud Schedulerの設定

HTTPメソッドをPOSTで下記Bodyを設定

{"RoomNames":["living", "study"]}

上記415エラーが発生

解決

gcloud scheduler jobs update http のオプションで --update-headers=Content-Type=application/json をつけてリクエストヘッダを変更する

もしくは以下

echo リクエストパース部分を下記の通り変更したところCloud Schedulerからリクエストしても成功するようになった

func Handler(c echo.Context) error {
    var m Message
    b, err := ioutil.ReadAll(c.Request().Body)
    defer c.Request().Body.Close()
    if err != nil {
        log.Printf("ioutil.ReadAll: %v", err)
        return echo.ErrBadRequest
    }
    if err := json.Unmarshal(b, &m); err != nil {
        log.Printf("json.Unmarshal: %v", err)
        return echo.ErrBadRequest
    }
}

おわり

Cloud Schedulerの設定GUIにはヘッダを設定する場所がなく、勝手にContent-Typeが付いてるもんだと思ってた
単なる思い込み

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

A Tour of Go メモ 【6】 四日目 インターフェースについて

インターフェース

インターフェースの概念や用途が全然わからないので、インターフェースのチュートリアルを始める前に下調べしました。
なるべく消化できるようにわかりやすく、理解しやすく、咀嚼して説明を書いてみました。

参考

上から順に読むと理解が進みました。

初心者に送りたいinterfaceの使い方
Goの実装例で理解するダックタイピング
【Golang】Golangのinterfaceで知っておくとお得なTips
Goのinterfaceがわからない人へ

*補足

Goの実装例で理解するダックタイピング
サンプルコードが動かないので、以下に修正

type Income interface {
    × calculate() string 
    ◯ calculate() int /   / 戻り値の型をstring型からint型へ
}

× (g google) calculate() int {
◯ func(g google) calculate() int {  // "func"が抜けているので、追加
    return g.baseSalary + (1000 * g.performance)
}

インターフェースとは何か

どんな型でも入れられる

package main

import "fmt"

func main() {
     var i interface{}
     i = 10
     fmt.Println(i)             // 10
     i = "string"
     fmt.Println(i)             // string

// iは現在インターフェース型である
// そのため、例えば変数 i にある文字列と他の文字列を結合したい時
// インターフェース型とstring型は結合できないというエラーになる
    fmt.Println("この中身は" + i)      // invalid operation:  (mismatched types interface {} and string)

// だから、インターフェース型からstring型に戻してあげる必要がある
// (型変更した物を受け取る変数) , ok := (型を変更したいインターフェース型の変数).(変更後の型名)
// okは、型変更できたかどうかをtrue、falseで返す
    str_i , ok := i.(string)
    if ok {
        fmt.Println("この中身は" + str_i)     // この中身はstring
    }
}

ダックタイピング

"If it walks like a duck and quacks like a duck, it must be a duck"
(もしもそれがアヒルのように歩き、アヒルのように鳴くのなら、それはアヒルに違いない)

上記の文をインターフェースのダックタイピングに言い換えると、

"もし、構造体がインターフェース型と同じ関数を持っているなら、その構造体はインターフェース型に違いない"

ダックタイピングの流れ

1.インターフェースを宣言
2.そのインターフェースが持つ関数を宣言
3.構造体を宣言する
4.構造体にインターフェースで宣言した関数名と同じ関数を宣言する
5.結果、明示的にその構造体はインターフェースであると宣言しなくても、自動的にその構造体はインターフェース型になる

宇宙飛行士に例えると

1.宇宙飛行士というインターフェース型を宣言
2.宇宙飛行士ができることを宣言(例えば、宇宙にいく、ロケットの操縦をする)
3.とある青年を宣言する
4.その青年のできることを宣言(宇宙飛行士と同じで、宇宙にいく、ロケットの操縦をする)
5.わざわざその青年は宇宙飛行士であると宣言しなくても、宇宙に行ったり、ロケットを操縦するなら、彼は宇宙飛行士に違いない

インターフェースの利用法

例えば、アームストロングさんと毛利さんという宇宙飛行士を作りたいとします。
同じ宇宙飛行士でも、アームストロングさんは月へ、毛利さんは火星へ行こうとしているとします。
そこで先ほどの宇宙飛行士のインターフェース型がすることに"宇宙にいく"というメソッドがあリます。
しかし、同じ宇宙でも、アームストロングさんは月、毛利さんは火星、と目的地が違います。
そこで、"宇宙にいく"のメソッドに
アームストロングさんの場合は、"月にいく"
毛利さんの場合は、"火星にいく"
というように同じ"宇宙にいく"というメソッドでも、それぞれの違いを設定することができる。

宇宙飛行士の例を元にインターフェースを実装してみる

package main

import "fmt"

// 【ステップ1】 宇宙飛行士というインターフェースを宣言
type Astronaut interface {
    //【ステップ2】宇宙飛行士のできることを宣言
        go_to_space()
}

// 【ステップ3】とある青年を宣言
type Armstrong struct {
        name string
        destination string
        experience int
}

// 【ステップ3】とある青年を宣言
type Mouri struct {
        name string
        destination string
        experience int
}

// 【ステップ4】とある青年がすること(メソッド)を宣言(宇宙飛行士(インターフェース型)で設定したメソッド名(go_to_space)と同じ)
func (a Armstrong) go_to_space() {
    fmt.Printf("%sへは%d回行ったけど、もう一回行きます!\n", a.destination, a.experience)
}

//【ステップ4】とある青年がすること(メソッド)を宣言(宇宙飛行士(インターフェース型)で設定したメソッド名(go_to_space)と同じ)
func (m Mouri) go_to_space() {
    fmt.Printf("%sに行ってきます!\n", m.destination)
}

func main() {
    //
    armstrong := Armstrong {
        name: "armstrong",
        destination: "月",
        experience: 1,
    }
    mouri := Mouri {
        name: "mouri",
        destination: "火星",
        experience: 0,
        }
    // 【結果】 armstrongとmouriを明示的にAstronaut型だと宣言しなくても、Astronaut型のスライスに入れることができている
    // つまり、自動的にarmstrong型とmouri型がAstronaut型になっている!!
    astronauts := []Astronaut{armstrong, mouri}
    greet(astronauts)
}

func greet(astronauts []Astronaut) {
    for _, astronaut := range astronauts {
        astronaut.go_to_space()
        }
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

go.modの参照先をローカルのパスに向けてデバッグする

Goは依存ライブラリを go.mod で管理しますが、依存ライブラリの中にバグがあった場合に、一時的にライブラリコード内にデバッグ用のコードを差し込みたいことがあります。

例えば、 github.com/hashicorp/hcl/v2 => /path/to/hcl に参照先を変えるには、こんなかんじで go.modreplace を書きます。手元の環境はGo 1.14です。

go.mod
module tmp

go 1.14

require (
    github.com/davecgh/go-spew v1.1.1
    github.com/hashicorp/hcl/v2 v2.6.1-0.20200915195656-bf0a7fe4fe09
    github.com/zclconf/go-cty v1.2.0
)

replace github.com/hashicorp/hcl/v2 => /path/to/hcl
main.go
package main

import (
    "fmt"

    "github.com/davecgh/go-spew/spew"
    "github.com/hashicorp/hcl/v2"
    "github.com/hashicorp/hcl/v2/hclwrite"
)

func main() {
    src := `
resource "foo_bar" "baz" {
  disabled = (true)
}
`
    f, _ := hclwrite.ParseConfig([]byte(src), "", hcl.Pos{Line: 1, Column: 1})
    got := f.BuildTokens(nil)
    fmt.Printf("got:\n%s\n", string(got.Bytes()))
    fmt.Printf("dump:\n%s\n", spew.Sdump(got))
}

あとは /path/to/hcl のコードを適当にいじればよいだけ。

参考: https://github.com/golang/go/wiki/Modules#when-should-i-use-the-replace-directive

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Goで予約された名前のCの構造体フィールドにアクセスする。

問題

typeという名前のフィールドを参照したいがGoに予約されているため下記のようなエラーがでる。

package main

/*
typedef struct {
    int type;
} Struct;
*/
import "C"

import "fmt"

func main() {
  test := C.Struct{1}
  fmt.Println(test.type)
}
エラー
./Main.go:14:20: expected selector or type assertion, found 'type'

解決策

参照時にフィールド名にアンダーバーを付けることで参照できるようになる。

package main

/*
typedef struct {
    int type;
} Struct;
*/
import "C"

import "fmt"

func main() {
  test := C.Struct{1}
  fmt.Println(test._type) // typeではなく_typeで参照。
}
出力
1

参考

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

WSL2 + Docker Desktop + VSCode(Remote - Containers) でGoの開発環境構築

概要

バージョン2004で正式導入されたWSL2ですが、先日バージョン19091903へのバックポートが発表されました!
https://devblogs.microsoft.com/commandline/wsl-2-support-is-coming-to-windows-10-versions-1903-and-1909/
筆者の開発PCは2004へのアップデートが許可されていないので、非常に助かります。
そこで今回は、Docker Desktop for WindowsのWSL2インテグレーション機能と、VSCodeのRemote - Containers拡張機能を使って、Dockerfileでコンテナ内にGoの「快適な」開発環境を作ってみるところまでやってみようと思います。
※既に以下のような素晴らしい記事があり書くのを迷いましたが、自身の理解を深めるのと備忘録も兼ねて書きました。
https://tech-lab.sios.jp/archives/21675

筆者の環境

エディション バージョン OSビルド
Windows 10 Pro 1909 18363.1049

システム要件

エディション バージョン OSビルド
Windows 10(全エディション) 2004 -
Windows 10 x64(全エディション) 1909 18363.1049 以上
Windows 10 x64(全エディション) 1903 18362.1049 以上

手順

  1. WSLをインストールし、WSL2に更新する

  2. Docker Desktop for Windows をインストールする

  3. VSCoceの拡張機能 Remote Containers を使って、Goの開発環境を構築する

1. WSLをインストールし、WSL2に更新する

以下URLの公式手順通りにやれば問題ありませんが、一応この記事でも手順を書いておきます。
https://docs.microsoft.com/ja-jp/windows/wsl/install-win10

1‐1. CPU仮想化が有効になっているか確認

タスクマネージャを開き、CPUの仮想化が有効となっていることを確認してください。
もし有効になっていない場合は、BIOS(UEFI)で仮想化を有効にしてください。(メーカーによって設定方法が異なるので説明は割愛します)
image.png

1‐2. Windows Subsystem for Linuxをインストールする

GUI(マウス操作)でやる場合

コントロールパネル > プログラムと機能 > Windowsの機能の有効化または無効化の順に選択して以下画面を開き、Linux用Windowsサブシステムにチェックを入れます。
image.png

CUI(コマンド操作)でやる場合

管理者権限でPowerShellを開き、以下コマンドを実行してください。

powershell
dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart

成功すると以下のようなメッセージが表示されます。

powershell
展開イメージのサービスと管理ツール
バージョン: 10.0.18362.900

イメージのバージョン: 10.0.18363.1049

機能を有効にしています
[==========================100.0%==========================]
操作は正常に完了しました。

1‐3. 仮想化マシンプラットフォームのオプションコンポーネントを有効にする

GUI(マウス操作)でやる場合

先程と同様に、コントロールパネル > プログラムと機能 > Windowsの機能の有効化または無効化の順に選択して以下画面を開き、仮想マシンプラットフォームにチェックを入れます。
image.png

CUI(コマンド操作)でやる場合

管理者権限でPowerShellを開き、以下コマンドを実行してください。

powershell
dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart

成功すると以下のようなメッセージが表示されます。

powershell
展開イメージのサービスと管理ツール
バージョン: 10.0.18362.900

イメージのバージョン: 10.0.18363.1049

機能を有効にしています
[==========================100.0%==========================]
操作は正常に完了しました。

1‐4. PCを再起動する

PCを再起動してWSLのインストールとWSL2の更新を完了させます。

1‐5. WSL2を既定のバージョンとして設定する

新しいLinuxディストリビューションをインストールする際の既定のバージョンをWSL2に変更します。
PowerShellを開き、以下コマンドを実行してください。

powershell
wsl --set-default-version 2

以下のメッセージが表示された場合はLinuxカーネルを更新する必要があります。

powershell
WSL 2 を実行するには、カーネル コンポーネントの更新が必要です。詳細については https://aka.ms/wsl2kernel を参照してください
WSL 2 との主な違いについては、https://aka.ms/wsl2 を参照してください

メッセージに表示されている以下URLにアクセスし、Linuxカーネル更新プログラムパッケージをダウンロードします。
https://aka.ms/wsl2kernel

ダウンロードしたインストーラーを起動し、Nextをクリックすればインストール完了です。
image.png

インストールが完了したら、再び以下コマンドを実行してみてください。今度は成功するはずです。

powershell
wsl --set-default-version 2

1‐6. Linuxディストリビューションのインストール

Microsoft Storeを開き、Linuxディストリビューションをインストールします。
wslで検索すればいくつか候補が出てきます。
image.png

何でもいいですが、今回はUbuntuをインストールします。
image.png

インストールが完了したら、Ubuntuを起動します。
しばらく待つとユーザー名とパスワードの入力を促されるので、任意のものを入力します。
※今後Ubuntu内で何かするときはパスワード入力を求められるので、パスワードは忘れないでください。
image.png

これで、Windows上でLinux(Ubuntu)を起動できるようになりました!
image.png

念のためPowerShellを開き、以下コマンドを実行してWSLのバージョンが2になっていることを確認しましょう。

powershell
wsl -l -v
  NAME      STATE           VERSION
* Ubuntu    Running         2

2. Docker Desktop for Windows をインストールする

次に、Docker Desktop for Windowsをインストールし、Dockerを使えるようにします。

ここで注意点ですが、以下のリリースノートに記載してある通り、バージョンが1909 1903の場合はDocker Desktop Community 2.3.5.0 以上をインストールする必要があります。
https://docs.docker.com/docker-for-windows/edge-release-notes/

執筆時点(2020年9月4日)では、Stable版(安定板)の最新バージョンはDocker Desktop Community 2.3.0.4です。
筆者の環境はバージョン1909なので、今回はEdge版をインストールします。
バージョン2004の方はStable版でも問題ないと思います。

以下URLにアクセスし、Docker Desktop for WindowsのEdge版のインストーラーをダウンロードします。
https://hub.docker.com/editions/community/docker-ce-desktop-windows/

インストーラーを起動すると、最初にConfiguration画面が立ち上がります。
今回はDockerのバックエンドをWSL2にしたいので、Install required Windows components for WSL2にチェックが入っていることを確認します。
従来のHyper-V型は使わないので、Enabled Hyper-V Windows Featuresはオフにしておきます。
image.png

OKボタンをクリックするとインストールが始まります。以下画面が表示されたら成功です。
Close and log outボタンをクリックするとログアウトされるので、再度Windowsにログインします。
image.png

ログイン後、Docker Desktop for Windowsが自動的に起動します。
しばらく待って起動に成功すると以下画面が表示されます。
Startボタンをクリックするとチュートリアルが始まりますが、今回はSKIPします。
image.png

画面上部の歯車アイコンをクリックして、Generalの設定でUse the WSL2 backend engineがチェックされていることを確認します。
image.png

dockerコマンドが使用できるかどうか確認します。
PowerShellを起動してdocker versionコマンドを実行し、バージョンが表示されるかどうか確認します。
image.png

同じくUbuntuを起動してdocker versionコマンドを実行し、バージョンが表示されるかどうか確認します。
image.png
これで、WindowsでDockerが使用できるようになりました!

3. VSCoceの拡張機能 Remote-Containers を使って、Goの開発環境を構築する

最後に、Dockerコンテナ内でGoの開発を「快適に」行うための準備をしていきます。

Dockerコンテナで開発する際のメリットとしては以下のようなものがあります。

  • 開発に必要な手順が記載されているDockerfileがあれば、コンテナを起動するだけですぐに開発できる環境が整います。
  • チーム内にDockerfileを配布すれば、チーム全員が同じ環境で開発することができます。
  • ランタイムやツールのインストールなどをする必要がないので、ホスト環境を汚しません。

Remote-Containersを使えば、上記のメリットに加えて、VSCodeで開発作業が行えるようになるので、コードの編集はもちろん、インテリセンス、構文チェック、定義ジャンプ、デバッグなどの豊富な支援機能も使えるようになり、コンテナ内での開発が非常にスムーズになります。

それでは早速やっていきましょう。

3-1. VSCoceの拡張機能 Remote Developmentのインストール

まず、VSCodeの拡張機能に、Remote Developmentをインストールします。
こちらはRemote-WSLRemote-ContainersRemote-SSHのセットになっています。
image.png

3-2. VSCodeをWSL(Ubunt)から起動する

次に、Ubuntuを起動します。
mkdirgo-sampleフォルダを作成し、cdで作成したフォルダに移動してから、code .コマンドでVSCodeを起動します。
image.png

すると、VSCodeWSL(Ubuntu)go-sampleフォルダを開いた状態になります。
image.png

3-3. Dockerfileの作成

次に、go-sample直下に.devcontainerというフォルダを作成します。
.devcontainerフォルダ内に、Dockerfileという名前のファイルを作成します。
内容は以下の通りにします。

goのバージョンは1.13.15にしてますが、1.14系やlatestでも問題ないです。
go getでインストールしているのはGo開発で必要なツール群です。
※コンテナをリビルドする度にインストールするのが面倒だったので、Dockerfile内に書きました。もっと良いやり方があったら教えてください!

Dockerfile
FROM golang:1.13.15

RUN go get -v -u \
    github.com/mdempsky/gocode \
    github.com/uudashr/gopkgs/v2/cmd/gopkgs \
    github.com/ramya-rao-a/go-outline \
    github.com/acroca/go-symbols \
    golang.org/x/tools/cmd/guru  \
    golang.org/x/tools/cmd/gorename \
    github.com/cweill/gotests/... \
    github.com/fatih/gomodifytags \
    github.com/josharian/impl \
    github.com/davidrjenni/reftools/cmd/fillstruct \
    github.com/haya14busa/goplay/cmd/goplay \
    github.com/godoctor/godoctor \
    github.com/go-delve/delve/cmd/dlv \
    github.com/stamblerre/gocode \
    github.com/rogpeppe/godef \
    golang.org/x/tools/cmd/goimports \
    golang.org/x/lint/golint \
    golang.org/x/tools/gopls

3-4. devcontainer.jsonの作成

同じく.devcontainerフォルダ内にdevcontainer.jsonという名前のファイルを作成します。
内容は以下の通りにします。

settingsで設定、extentionsで拡張機能を指定できるので、お好みでどうぞ。
今回はGoの拡張機能をインストールして、設定をちょっと変えています。
エディタの環境まで共有できるのですごく便利ですね!

devcontainer.json
{
    // VSCodeに表示されるワークスペース名
    "name": "go-sample",

    // Docker buildを実行するディレクトリ。devcontainer.jsonファイルからの相対パスで設定します。
    "context": "..",

    // コンテナの内容を定義するDockerfileのパス。devcontainer.jsonファイルからの相対パスで設定します。
    "dockerFile": "Dockerfile",

    // コンテナ側のVSCodeの設定値(setting.json)を変更したい場合、設定します。
    "settings": {
        "terminal.integrated.shell.linux": "/bin/bash",
        "go.formatTool": "goimports",
        "go.useLanguageServer": true,
    },

    // コンテナ側のVSCodeに拡張機能をインストールする必要がある場合、拡張機能のIDを配列で設定します。
    "extensions": [
        "golang.Go"
    ]
}

3-5. Remote-Containersでコンテナにリモート接続する

次に、左下にある>< WSL: Ubuntuをクリックし、Remote-Containers: Reopen in Containerを選択します。
image.png

VSCodeが再読込され、コンテナ内で起動します。
先程WSL(Ubuntu)側で作成したフォルダ/ファイルがマウントされているのがわかります。
※初回はコンテナの作成に時間がかかりますが、次回からはもう少し早く起動すると思います。
image.png

ターミナルでBashを起動し、go versionを実行します。
Goのバージョン1.13.15がインストールされていることを確認できます。
image.png

左下の歯車アイコンから設定を開き、@modifiedと入力して変更した設定のみを表示します。
devcontainer.jsonsettingsで記載した内容が、リモート側のVSCode設定に反映されていることを確認できます。
image.png

拡張機能はローカルにインストールするものとリモート側にインストールするものとで管理が分かれます。
devcontainer.jsonextensionsで設定したGoの拡張機能がリモート側にインストールされていることを確認できます。
image.png

Dockerfileでインストールするよう設定したGoのツール群もインストールされています。
image.png

Docker Desktopでもイメージが作成されていることが確認できます。
image.png

コンテナも作成され、起動中(RUNING)になっています。
image.png

3-6. コンテナ内でのGo開発(hello world)

環境が整ったので、コンテナ内でGohello worldを作ってみます。
ワークスペース直下にmain.goという名前のファイルを作成します。
内容は以下の通りにします。

コピペでなく自分で入力すると、インテリセンスやスニペットなどの支援機能が効いていることが実感できると思います。

main.go
package main

import "fmt"

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

ターミナルでBashを開き、go run main.goを実行します。hello worldと出力されれば成功です!
image.png

デバッグもやってみます。
main.goのソース上でfmt.Println("hellow world")にブレイクポイントを設定し、F5キーを押します。
image.png

しばらくするとデバッグが実行され、設定したブレイクポイントで止まります。
image.png

デバッグツールバーのステップオーバーをクリックするか、F10キーを押すと次のステップへ進みます。
fmt.Plintlnが実行されて、デバッグコンソールにhello worldを出力されることが確認できます。
image.png

これでGoのデバッグが出来るようになりました!
※デバッグを終了させる場合は再度F5キーを押してください。

3-7. コンテナへのリモート接続を終了する

コンテナへのリモート接続を終了させる場合は、左下の>< Dev Containerからリモート接続を終了するを選択するか、右上の×ボタンでVSCodeを閉じてください。
image.png

3-8. コンテナへのリモート接続を再開する

再度コンテナへのリモート接続をする場合は、WSL(Ubunt)go-sampleフォルダを開き、左下の>< WSL: UbuntuからRemote-Containers: Reopen in Containerを選択してください。
image.png

初回起動では32.373秒かかりましたが、2回目は18.217秒で起動できました。
image.png

3-9. (おまけ)Windowsから直接Remote-Containersでコンテナにリモート接続する

今回はWSL(Ubuntu)内にソースコードを配置して、WSL(Ubuntu)からVSCodeRemote-Containersでコンテナを起動しましたが、Windows上にソースコードを配置して、Windowsからコンテナを起動することもできます。

やり方はWSL(Ubuntu)の時と同じで、3-33-5の手順をWindows上で行うだけです。
ソースコードはWindowsで管理したいよ!っていう方はその方法でも良いかもです。
image.png

初回起動では39.594秒かかりました。WSL(32.373秒)より若干遅い?です。
image.png

注意点としては、Windowsからコンテナを起動すると、Docker Desktopが以下の警告を表示します。
image.png

Docker Desktopは、WindowsファイルをWSL2コンテナーに共有したことを検出しました。これはパフォーマンスが低下する可能性があります。

https://docs.docker.com/docker-for-windows/wsl/#best-practices
上記URLのDocker Desktop WSL2 Backendのベストプラクティスにもあるように、Linuxファイルシステムからマウントされるとパフォーマンスが大幅に向上するようです。Windowsファイルシステムからコンテナを起動するのは避けるように、ともあるので、気になる方はWSLでソースコードを管理したほうがよさそうです。

まとめ

WSL2Docker Desktop for WindowsVSCodeの相性が良く、WindowsでもDocker開発がスムーズに行えるようになってきました。
Dockerfileで開発環境を共有できるのはもちろんですが、devcontainer.jsonVSCodeの設定や拡張機能まで共有できるのもいいですね!
Goはまだいいですが、ReactVueなどのフロントエンドの開発だとESLintPrettierの設定などでかなり労力を持っていかれるので、、、
これからは開発で積極的に取り入れていきたいと思います!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む