Golang + Gin + Dockerのホットリロード、VSCodeデバッグ環境の構築

# 概要
Go言語のフレームワークGinの開発環境の構築。（ホットリロード、VSCodeデバッグ対応）

今回のコードはGitHubに公開してあります。
https://github.com/yolo-lin/go-demo

# 環境
- MacOS 10.15.6
- go version go1.15.2 darwin/amd64

# 導入するパッケージ
[Gin](https://github.com/gin-gonic/gin)：Goのフレームワーク
[cosmtrek/air](https://github.com/cosmtrek/air)：ホットリロード
[delve](https://github.com/go-delve/delve)：デバッグ

# 事前準備
- Goのインストール：[公式](https://golang.org/)からインストール
- Go Modulesで依存関係を管理するため、環境変数GO111MODULEをonにする

```bash:.zshrc
export GO111MODULE=on
```

- Go Modulesの初期化

```bash:/src
$ go mod init パッケージ名
```

# ディレクトリ構成
Ginのディレクトリ構成は特に決まってないので、以下は自己流です。
メインのソースコードは全部srcの下に配置する。今後は`models`や`controllers`もsrcに追加する予定です。

```
.
├── .vscode
│   └── launch.json
├── docker
│   ├── go
│   │   └── Dockerfile
│   └── mysql
│       └── Dockerfile
├── src
│   ├── go.mod
│   ├── go.sum
│   ├── .air.toml
│   └── main.go
│
└── docker-compose.yml
```

# Docker
### Go
```dockerfile:/docker/go/Dockerfile
FROM golang:1.15.2

ENV GO111MODULE=on

COPY src/ /go/src/app/

WORKDIR /go/src/app

# cosmtrek/airのインストール
RUN go get -u github.com/cosmtrek/air
# delveのインストール
RUN go get -u github.com/go-delve/delve/cmd/dlv

# airの起動
CMD air -c .air.toml
```

### MySQL
```dockerfile:/docker/go/Dockerfile
FROM mysql:8.0

RUN chown -R mysql /var/lib/mysql && \
    chgrp -R mysql /var/lib/mysql
```

### docker-compose

```yml:./docker-compose.yml
version: '3'

networks:
    backend:
      driver: bridge

services:
    go:
        container_name: go
        build:
            context: .
            dockerfile: ./docker/go/Dockerfile
        ports:
            - 8080:8080
            - 2345:2345
        links:
            - mysql
        tty: true
        volumes:
            - ./src:/go/src/app
        depends_on:
            - mysql
        security_opt:
            - seccomp:unconfined
        cap_add:
            - SYS_PTRACE
        networks:
            - backend

    mysql:
        container_name: mysql
        build:
            context: .
            dockerfile: ./docker/mysql/Dockerfile
        environment:
            MYSQL_USER: root
            MYSQL_ROOT_PASSWORD: password
            MYSQL_DATABASE: demo
        hostname: mysql
        ports:
            - "3306:3306"
        volumes:
            - ./docker/mysql/data:/var/lib/mysql
        security_opt:
            - seccomp:unconfined
        networks:
            - backend

    phpmyadmin:
        image: phpmyadmin/phpmyadmin
        environment:
            - PMA_ARBITRARY=1
            - PMA_HOST=mysql
            - PMA_USER=root
            - PMA_PASSWORD=password
        ports:
            - "8081:80"
        depends_on:
            - mysql
        networks:
            - backend
```

# cosmtrek/airの設定

公式の[air_example.toml](https://github.com/cosmtrek/air/blob/master/air_example.toml)を流用

以下デバッグ用の記述を追記

```toml:/src/.air.toml
# Debug
#full_bin = "APP_ENV=dev APP_USER=air /go/bin/dlv exec ./tmp/main --headless=true --listen=:2345 --api-version=2 --accept-multiclient"
```

```toml:/src/.air.toml
# Config file for [Air](https://github.com/cosmtrek/air) in TOML format

# Working directory
# . or absolute path, please note that the directories following must be under root.
root = "."
tmp_dir = "tmp"

[build]
# Just plain old shell command. You could use `make` as well.
cmd = "go build -o ./tmp/main ."
# Binary file yields from `cmd`.
bin = "tmp/main"

# Customize binary.
full_bin = "APP_ENV=dev APP_USER=air ./tmp/main"

# Debug
#full_bin = "APP_ENV=dev APP_USER=air /go/bin/dlv exec ./tmp/main --headless=true --listen=:2345 --api-version=2 --accept-multiclient"

# Watch these filename extensions.
include_ext = ["go", "tpl", "tmpl", "html"]
# Ignore these filename extensions or directories.
exclude_dir = ["assets", "tmp", "vendor", "frontend/node_modules"]
# Watch these directories if you specified.
include_dir = []
# Exclude files.
exclude_file = []
# This log file places in your tmp_dir.
log = "air.log"
# It's not necessary to trigger build each time file changes if it's too frequent.
delay = 1000 # ms
# Stop running old binary when build errors occur.
stop_on_error = true
# Send Interrupt signal before killing process (windows does not support this feature)
send_interrupt = false
# Delay after sending Interrupt signal
kill_delay = 500 # ms

[log]
# Show log time
time = false

[color]
# Customize each part's color. If no color found, use the raw app log.
main = "magenta"
watcher = "cyan"
build = "yellow"
runner = "green"

[misc]
# Delete tmp directory on exit
clean_on_exit = true
```

これで、`docker-compose up -d`したら、自動的に設定ファイルを適応し、ホットリロード化される。

# デバッグ

### vscode
[Go](https://marketplace.visualstudio.com/items?itemName=golang.Go)の拡張機能をインストール、以下デバッグ設定ファイルを追加する。

```json:/.vscode/launch.json
{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Remote",
            "type": "go",
            "request": "launch",
            "mode": "remote",
            "program": "${workspaceFolder}",
            "port": 2345,
            "host": "127.0.0.1",
            "showLog": true,
        }
    ]
}
```

デバッグが必要な場合、cosmtrek/airの設定を変更する。
開発用とデバッグ用の`full_bin`を交換し、docker-compose再起動したら、vscodeでデバッグ可能になります。

```toml:/src/.air.toml
# Customize binary.
# full_bin = "APP_ENV=dev APP_USER=air ./tmp/main"

# Debug
full_bin = "APP_ENV=dev APP_USER=air /go/bin/dlv exec ./tmp/main --headless=true --listen=:2345 --api-version=2 --accept-multiclient"
```

ただし、コード更新した場合、cosmtrek/airより自動ビルドされるが、vscode側が切断され、手動で再接続が必要です。
![CleanShot 2020-09-27 at 16.22.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/746948/be7b702f-920d-e20b-95c6-2810bbdb89e6.png)