realize + dockerでGolangのホットリロード付き開発環境を構築する

5 min読了の目安(約3300字TECH技術記事

今回は、gin + gqlgen + gormなGoプロジェクトのホットリロード付き開発環境を構築します。

環境

  • Go 1.15.2

プロジェクトの構造

.
├── db
│   └── db.go
├── models
│   ├── model_a.go
│   └── ...
├── schema
│   ├── schema.graphql
│   └── ...
├── server
│   └── server.go
├── docker-compose.yml
├── go.mod
├── go.sum
├── gqlgen.yml
└── ...

使用するホットリロードツール

realizeというツールを使用します。

他にもfreshなどのホットリロードツールがありますが、realizeの方が設定が細かくできるため、realizeを選択しました。

実は最初、freshで試していたのですが、Go Modulesモードに対応できないっぽかったです。
realizeでは適切に設定を行うことで、対応可能となっていることがわかりました。

Dockerfileとdocker-compose.ymlの準備

以上を踏まえて、ホットリロードを行うためのDockerfiledocker-compose.ymlを準備します。

FROM golang:1.12.6-alpine
WORKDIR /app

RUN apk add --no-cache alpine-sdk git && go get -u github.com/oxequa/realize

EXPOSE 8080
CMD ["realize", "start"]

docker-compose.ymlにて、開発環境のディレクトリをコンテナにアタッチする設定を行います。
そのため、ここではソースをコピーは行わず、WORKDIRと必要なパッケージのインストールのみ行います。

version: '3'
services:
  db:
    image: mysql:5.7
    ports:
      - "3306"
    restart: always
    command: ['mysqld', '--character-set-server=utf8mb4', '--collation-server=utf8mb4_unicode_ci']
    environment:
      MYSQL_DATABASE: sample
      MYSQL_ROOT_PASSWORD: password
    volumes:
      - mysql-data:/var/lib/mysql

  application:
    build: .
    environment:
      MYSQL_DATABASE: sample
      MYSQL_HOST: db
      MYSQL_PASSWORD: password
      MYSQL_PORT: 3306
      MYSQL_USER: root
    depends_on:
      - db
    ports:
      - "8095:8080"
    restart: always
    volumes:
      - .:/app

volumes:
  mysql-data:
    driver: local

以下の部分で、開発環境のディレクトリをコンテナにアタッチする場所を選びます。
アタッチ先として、DockerfileWORKDIRと同じ場所が設定されていることを確認します。

    volumes:
      - .:/app

realizeの設定

DockerfileCMDにてrealize startを実行するようにしていますが、このコマンド単体ではアプリケーションが起動しません。

まずは手元でrealize startを実行します。

$ go get -u github.com/oxequa/realize
# $GOPATH/binにPATHを通っていることを確認した後
$ realize start

すると.realize.ymlという設定ファイルが生成されます。
適当なgoファイルを変更しセーブすると、その旨がログに出力されることを確認し、Ctrl+Cで実行を終了します。

settings:
  legacy:
    force: false
    interval: 0s
schema:
- name: sample
  path: .
  commands: {}
  watcher:
    extensions:
    - go
    paths:
    - /
    ignore:
      paths:
      - .git
      - .realize
      - vendor

アプリケーションを起動するにはrealize start --runというふうに引数に--runを与えるか、.realize.ymlを以下のように修正します。

-  commands: {}
+  commands:
+    run:
+      status: true

通常、この状態でrealize startするとアプリが立ち上がるのですが、Go Modulesモードである場合はエラーが発生して起動しません。
また、今回の場合はmain関数が定義されているファイルがmain.goではなく、server/server.goにあるため、その点も特殊です。

これを吸収するための設定を追加します。

commands:
+ install:
+   status: true
+   method: go build -o app.out ./server/server.go
  run:
    status: true
+   method: ./app.out

これでホットリロードが有効な状態で、アプリケーションが立ち上がるようになります。
手元でrealize startを実行し、挙動を確認しましょう。

realizeはデフォルトでプロジェクトをリビルドする際に$GOPATH/binにリビルドするためにgo installを使用します。
Go Modulesモードでは$GOPATHが存在しないので、go installに失敗してしまいます。
これを避けるため、go installではなく、go build -o app.out ./server/server.goを使用するように設定し、起動時はその成果物を利用して起動するようにしています。

docker-composeで起動

ここまでできれば後はdocker-composeで起動するだけです。

docker-compose build
docker-compose up -d

参考