GoのHOTリロード機能(air)付きコンテナの環境構築

Goはコンパイラ言語なので`go build`でコンパイルが必要だが、パッケージを導入することでフロントエンドの yarn run watchみたいなHOTリロードができるらしい。

https://github.com/cosmtrek/air

https://github.com/oxequa/realize

調べてみたところ、realiseは2018年以降メンテされてない様子。。。
最近はメンテナンスが続いてる`air`の方が導入事例が多いようなので、air導入済みのgo環境をを作ってみました。

### 作成する環境
- Go v1.19
- air v.1.40.4

2022年10月末時点ではGoの最新版は`v1.19`です。
[airもv1.19に対応している](https://github.com/cosmtrek/air/pull/327)ため、最新版の環境を構築します。

今回作成した環境のリポジトリ

https://github.com/hiro-lapis/go-docker

### 手順は省略してHOTリロードするとこまでいきたい場合
下記のコマンドを上から順に実行すればOKです。

```shell
git clone https://github.com/hiro-lapis/go-docker && cd go-docker && docker compose up -d --build
```

```shell
docker exec -it go-docker-go-1 ash
```

```shell
air -c .air.toml
```



### ファイル構成
最終的なファイル構成は以下のとおりです。

```
src
│
├── app
│   ├── .air.toml
│   └── main.go
├── go.mod
├── Dockerfile
└── docker-compose.yml
```

はじめに`docker-compose.yml`と`Dockerfile`を作成し、
次いで`go.mod`と`.air.toml`と`main.go`を用意します。

## 手順

#### step1 go.mod作成
go.modは、go組み込みのmodule管理ファイルです。
アプリケーションはさまざまな部品同士を組み立ててシステムを構成していますが、その部品の依存関係を解決しているのがgo modです。

airを使うのにもgo.modが必要なので、作成します。
その後、appディレクトリを作成し、main.goを作りましょう。


go.modの置き場所は色々やりようがあるみたいですが、プロジェクトのrootディレクトリ(今回でいえばsrcディレクトリ内)が一番よかろうと判断しておいてあります。



```go.mod
module go_docker

go 1.19
```

#### step2 docker-compose.ymlの作成
こちらもsrc内にて作成。
最低限の記述で記載します。

```yaml
version: "3"
services:
    go:
        build: .
        tty: true
        ports:
            - "8080:8080"
        volumes:
            - ./app:/go/src/app

```

ttyについては↓が詳しいです。

https://zenn.dev/hohner/articles/43a0da20181d34

また、フロントエンドやDB用のコンテナ含め作成したい時は、以下資料等を参考にするといいかも
[dockerでSPAなgin + vue + mysqlの環境をさくっと作った話](https://zenn.dev/diwamoto/articles/e6e4be8728c2e2)

#### step3 Dockerfileの作成
こちらを作り終えたら、src直下で作成するファイルは準備完了です。

```Dockerfile
FROM golang:1.19.2-alpine3.16

ENV GOPATH /go
ENV GO111MODULE on
RUN mkdir ${ROOT}

RUN apk update && \
    apk --no-cache add git

RUN mkdir ${ROOT}
WORKDIR ${ROOT}

ADD . ${ROOT}

RUN go mod tidy && \
    go install github.com/cosmtrek/air@v1.40.4

CMD ["air", "-c", ".air.toml"]
```

##### 各記述の意味

`go mod tidy`でairを入れる準備をした上でインストール
`go get`は非推奨になっているので、`go install`を使って、バージョン指定してインストールしています。

その他、各記述の意味については↓がわかりやすいです。

https://zenn.dev/optimisuke/articles/105feac3f8e726830f8c#go-mod-tidy

#### step4 サンプルgoプログラムを作成
appディレクトリ内にgoのコードを作っていきましょう。
まずはHOTリロードでコンパイルされるgoプログラムを作ります。

```go:main.go
package main

import "fmt"

func main() {
	fmt.Println("Hello golang from docker!")
}

```

#### step5 air.tomlの作成・編集

この時点でgoとdocker周りの記述はできたので、コンテナを起動してみます。



Dockdrfile内で実行される`air`コマンドの実行のために必要な`.air.toml`がないためエラーが発生しているので、`.air.toml`を作成します。

↓公式リポジトリから`.air.toml`のテンプレートをコピー、
`app`配下に`.air.toml`を作りましょう。

https://github.com/cosmtrek/air/blob/master/air_example.toml

#### step6 コンテナの起動(HOTリロードできていることの確認)

再度コンテナを起動してみます。

```shell
docker compose up
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/420171/226d5e9b-6a0f-bc3b-14bc-65a93750c443.png)

コンパイル&実行されました！
コンパイル後のバイナリは`app/tmp`配下に置かれています。

さらに、Printlの内容を少し変更してsaveしてみます。

```go:main.go
func main() {
	fmt.Println("Hello golang from docker!")
}
// ↓変更
func main() {
	fmt.Println("Hello golang from docker with air!")
}

```

saveと同時に再コンパイルされました!

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/420171/3bb59d40-23c6-fd85-bf83-d8752e0d50eb.png)


バックグラウンドで起動する`docker compose up -d`で立てたコンテナに入って実行することもできます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/420171/5cb49d9a-7175-5e6a-7bf0-1d81c3bd0ba4.png)

### その他注意点

構築時にファイル構成、具体的には`go.mod`と`.air.toml`を置く場所には気をつけましょう。

1. ワーキングディレクトリに.air.tomlを配置する
2. go.modとDockerfileと同じrootディレクトリに配置する

1については、ワーキングディレクトリに.air.tomlがない時にairを実行すると、以下のようなエラーログが出力されます。

```
 2021/10/30 12:09:04 open .air.toml: no such file or directory
```

2については、↓go.modが.air.tomlと異なる階層にある状態でairを実行すると、以下のようなエラーログが出力されます。

```
go_1  | go: go.mod file not found in current directory or any parent directory; see 'go help modules'
go_1  | failed to build, error: exit status 1
```


### 参考資料

[【Go言語】ホットリロード可能なHTTPサーバのDocker環境構築手順](https://nishinatoshiharu.com/docker-go-air-server/)
 [Go+gin+Air環境をDockerで構築](https://zenn.dev/hrs/articles/go-gin-air-docker)
 [【Golang/Go言語】Goのホットリロードライブラリをrealizeからairに移行しました](https://blog.homie.co.jp/entry/golang-air)
[go.modってどこにおくのがいいのだろう](https://qiita.com/gal1996/items/02308c6b231872d74acc)