【Nginx&Golang&MySql】Dockerで開発環境の構築

# 1. はじめに
[MYJLab Advent Calendar 2023](https://qiita.com/advent-calendar/2023/myjlab)の7日目の記事になります。
昨日は片平さんの[マイクラをヴァンサバライクにするプラグインの開発](https://qiita.com/kata110/items/2366effe7aa957e5be59)でした。ヴァンサバはやったことないですが、マイクラでできるのはおもしろそうでやってみたいです。

# 2. やること
Dockerを使ってgo,mysqlのAPIサーバー開発の環境を作ります。nginxはリバースプロキシのような形でリクエストをgoのコンテナに流すだけになってます。環境を構築するだけなので中身はとくに何もないです。

_ 自分で1から環境構築するのが初めてなのでわかってない部分や、間違っている部分があるかもしれません。 _

# 3.ディレクトリ構成
全体像は以下のようになります。できるだけ小さい構成にしたつもりです。viewの下にあるhtmlファイルは動作確認のためのものなので必須ではないです。
.air.tomlファイルはホットリロードのためのファイルです。
```
├── Dockerfile
├── backend
│   ├── .air.toml
│   ├── go.mod
│   ├── go.sum
│   ├── main.go
│   └── view
│       └── index.html
├── docker-compose.yml
├── mysql
│   ├── Dockerfile
│   ├── docker-entrypoint-initdb.d
│   │   └── init.sql
│   └── my.cnf
└── nginx
    ├── Dockerfile
    └── default.conf
```


## 4. docker-compose

#### dbコンテナ
* environment:でルートユーザーのパスワードと、ルートユーザー以外のユーザーとパスワードを設定しています。
* volumes:ではmysql/~にあるinit.sqlを指定しています。ファイルの中は何もないです。

#### backendコンテナ
* tty: trueでコンテナ起動後すぐに閉じないようにしています。
* environment:の部分はよくわからないですが参考にしたサイトの通り書いています。


```dockerfile:docker-compose.yml
version: "3"

services:
  db:
    container_name: "db"
    build: ./mysql
    environment:
      MYSQL_ROOT_PASSWORD: root
      MYSQL_USER: myuser
      MYSQL_PASSWORD: mypass
      TZ: Asia/Tokyo
    volumes:
      - ./mysql/docker-entrypoint-initdb.d:/docker-entrypoint-initdb.d
    ports:
      - "3306:3306"

  backend:
    container_name: "backend"
    build: .
    tty: true
    environment:
      APP_MODE: local
      DB_USER: localuser
      DB_PASSWORD: localpass
      DB_HOST: db
      DB_PORT: 3300
      DB_NAME: go_sample
      DB_NAME_TEST: go_sample_test
      DB_LOC: Asia/Tokyo
    volumes:
      - ./backend:/go/src/backend
    ports:
      - "3000:3000"

  nginx:
    container_name: "nginx"
    build: ./nginx
    ports:
      - "8080:80"
    depends_on:
      - backend
```


# 5. mysql
```dockerfile:mmysql/Dockerfile
FROM mysql:latest

ENV TZ Asia/Tokyo

#タイムゾーンの設定と/var/lib/mysql/ディレクトリ以下の所有者を変更しています。
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && chown -R mysql:root /var/lib/mysql/

COPY my.cnf /etc/mysql/conf.d/my.cnf
COPY docker-entrypoint-initdb.d/init.sql /docker-entrypoint-initdb.d/

CMD ["mysqld"]
```

```configuration:my.cnf
[mysqld]
character-set-server=utf8mb4
explicit_defaults_for_timestamp=1
collation-server=utf8mb4_general_ci

[client]
default-character-set=utf8mb4
```
* 余談ですが自分はmy.cnfの名前をmy.confとしていて何度もエラーが起きていました。


# 6. backend(go)
* backendのDockerfileです
```dockerfile:Dockerfile
FROM golang:latest

# timezoneの設定
ENV TZ Asia/Tokyo

## 作業ディレクトリをbackendディレクトリに指定
WORKDIR /backend

#ホットリロードのためのairをダウンロード
RUN go install github.com/cosmtrek/air@latest

# ローカルのgoディレクトリ全てをimage内の/go/src/
COPY . /go/src/backend
WORKDIR /go/src/backend
#　ホットリロードのairを実行
CMD ["air", "-c", ".air.toml"]
```

* ホットリロードのためのairファイルです
公式のgithubです
[https://github.com/cosmtrek/air](https://github.com/cosmtrek/air)
```conf:/backebd/.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, can setup environment variables when run your app.
full_bin = "APP_ENV=dev APP_USER=air ./tmp/main"
# 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 = []
# Exclude specific regular expressions.
exclude_regex = ["_test.go"]
# Exclude unchanged files.
exclude_unchanged = true
# Follow symlink for directories
follow_symlink = true
# 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
```

* 9000版ポートをリッスンしてview/いかにあるindex.htmlを返してます。
```go:/backend/main.go
package main

import (
    "github.com/gin-gonic/gin"
)

func main() {
    router := gin.Default()
    router.LoadHTMLGlob("view/*.html")

    router.GET("/", func(ctx *gin.Context){
        ctx.HTML(200, "index.html", gin.H{})
    })

    router.Run(":3000")
}
```

# 6. nginx
```dockerfile:/nginx/Dockerfile
FROM nginx:1.25.3-alpine

COPY default.conf /etc/nginx/conf.d/default.conf

CMD ["nginx", "-g", "daemon off;"]
```


```dockerfile:/nginx/Dockerfile
FROM nginx:1.25.3-alpine

COPY default.conf /etc/nginx/conf.d/default.conf

CMD ["nginx", "-g", "daemon off;"]
```

* ローカル8080で受け取ったリクエストを3000番(backend)に流す
```conf:/nginx/default.conf
server {
    listen 80;
    server_name localhost;

    location / {
        proxy_pass http://backend/cmd:3000;
    }

    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
}
```


# 6. 実行結果
* [http://localhost:8080](http://localhost:8080)へのアクセス結果
![スクリーンショット 2023-12-07 18.19.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3617893/240c18c0-806c-0550-9596-f3e507c4f29e.png)


今後はbackendコンテナはAPIサーバーとしてデータだけを返す形に作り変えたいと思います。

# 7.　参考資料
* [Go + MySQL + nginxの開発環境をDocker(docker-compose)で作る](https://qiita.com/locomotive/items/b2bcca90f738b2bca378)
* [とりあえずgolang+nginx+mysql+docker-composeの環境構築](https://qiita.com/greenteabiscuit/items/ef4ed1dfda0d396d9d0f)
* [GolangでAPIを開発する方法まとめ【Docker・Gin・Nginx・MySQL・Postman・Actions】
](https://tomoyuki65.com/how-to-develop-api-in-golang/#DockerNginxGolangGinAPI)