最終目的
- go言語でWebアプリを構築するためのgo開発環境をDockerで作成する。
目標
- Docker上にコンソール立ち上げ環境を作る。
- フレームワークginを使った簡単なREST APIサーバーを立ち上げる。
- イメージビルド時にプログラムをコンパイルしコンテナ起動と同時にホストPCからAPIにアクセスできるようにする。
- realizeでホットリロードできるようにする。←今ここ
やること
- realizeをイメージビルド時にインストール
- コンテナ立ち上げ時にrealizeでgo-blogをホットリロード
開発環境の構成
開発環境 go-env の構成は下記の通り。
go-env/ ★ 開発環境構成go-envはGitHubのレポジトリ
├── docker-compose.dev.yml
├── env/
│ └── Dockerfile.blog.dev ★ go-blogのDockerイメージを作成するためのDockerfile
├── Makefile
└── src/
└── go-blog/ ★ go-blogディレクトリはgo-envとは別のGitHubレポジトリ
├── .realize.yaml ★ realizeの設定ファイル
└── app/
├── entrypoints/
│ └── production/
│ └── main.go
├── go.mod
└── go.sum
realizeをイメージビルド時にインストール
ホットリロードとは、ソースコードが変更されたことを検知し、自動的にリビルドおよびプログラムを再起動すること。便利。
realizeとは、go言語で書かれたホットリロードアプリ。
realizeは2年近く更新されておらず、最近ではairが使われているようだが、airは使いにくい。
いつまで使えるかはわからないが、まだ手放せない...
Dockerfile.blog.devの修正(realizeのイメージビルド時インストール)
下記の通り、root権限で動作する位置に、realizeをインストールするように定義を追記する。
realizeの@latestはmodule modeでの
go install
でのインストールが現在(2022/06/12)できない。
一度、module modeをoffにして、realizeをgo get
でインストールする。
なお、現在、go1.15以下ではgo get
でのインストールもエラーとなる。
--- a/env/Dockerfile.blog.dev
+++ b/env/Dockerfile.blog.dev
@@ -11,12 +11,18 @@ RUN ln -sf /usr/share/zoneinfo/$TZ /etc/localtime
ENV DEBIAN_FRONTEND=noninteractive
RUN apt update && apt install -y sudo
RUN apt install -y jq tree diffutils nano vim
+# realizeのインストール
+# module modeではインストールできない。
+ENV DEBIAN_FRONTEND=noninteractive
+RUN GO111MODULE=off go get github.com/oxequa/realize
+ENV GO111MODULE=on
+
# システムアカウントの作成(ホームディレクトリは作成しない)
RUN groupadd -g $GROUP_ID $GROUP_NAME && \
useradd -s /bin/bash -u $USER_ID -g $GROUP_ID -G sudo $USER_NAME -r -d /home/$USER_NAME -M && \
echo "$USER_NAME ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers
realizeの設定
realizeの設定は.realize.yamlに記述する。コンテナ内のディレクトリ構成を意識して、設定を記述する。
コンテナ内のディレクトリ構成は下記の通り。
/
├── dist/ ★ go-blogアプリを起動するディレクトリ
│ └── go-blog ★ webアプリケーションの実行バイナリ
└── src/
└── go-blog/ ★ コンテナ起動時にホストPCのディレクトリをマウント
├── .realize.yaml ★ realizeの設定ファイル
└── app/
├── go.mod
└── entrypoints/production
└── main.go ★ mainパッケージのエントリープログラム
realizeでやりたいこと
/src/go-blog/appディレクトリ配下のgoソースファイルが変更になったとき...
-
go mod tidy
を実行したい - entrypoints/production/main.goから/dist/go-blogコマンドをビルドしたい
- /dist/go-blogコマンドを再起動したい
airを使わない理由
- Mac/Linuxではファイルの変更を検知できるが、WSL上ではある条件下でファイル変更が検知できない。
/mnt/<drive>
配下のファイルはinotifyが働かないため。go mod tidy
をどうやったら実行できるかわからない...
realizeの設定ファイルを作成
.realize.yaml
ファイルを作成。
WindowsでDockerを動かしているときは注意。
settings:
# WindowsのWSLでdockerを動作させている場合で、コードが/mnt/<drive>に配置されている場合、
# inotifyが働かないので、ポーリングで動作するようにforce:trueに変更する
legacy:
force: false # ★ Windowsではtrueにする ★
interval: 1s # ポーリング間隔になるので短くするとPCに負荷がかかる
# サーバー設定(状態をWebで確認できる)
server:
open: false
port: 5002
host: 0.0.0.0
# ホットロード設定
schema:
- name: go-blog # プロジェクト名(好きな名前で)
path: app # 監視対象パス(.realize.yamlがあるディレクトリからの相対パス)
# コマンド実行設定
commands:
build:
status: true # ビルドを再起動前に実施する
# ビルド時のコマンド(/dist/go-blogにビルド)
method: go build -o /dist/go-blog entrypoints/production/main.go
run:
status: true # コマンド再起動する
# 再起動時のコマンド
method: /dist/go-blog
# ファイル監視設定
watcher:
extensions: # 監視対象ファイルの拡張子
- go
paths: # 監視対象ディレクトリ(schema/pathからの相対パスで指定)
- .
scripts:
# go mod tidyを実施する
- command: go mod tidy
type: before # ビルドの前
path: app # 実行ディレクトリ(.realize.yamlがあるディレクトリからの相対パス)
output: true
ignored_paths:
- .git
- .realize
コンテナ起動時にrealizeを起動させる
Dockerfile.blog.devの修正(realizeを立ち上げるように変更)
コンテナ起動時にrealizeを起動するように Dockerfile.blog.dev を修正する。
# ワークディレクトリを /dist に切り替え(システムアカウント実行)
WORKDIR /dist
# テンポラリの/tmp/appディレクトリを削除(システムアカウント実行)
RUN rm -rf /tmp/app
# # コンテナ起動時にgo-blogコマンドが実行されるように設定 --------------
# CMD ["./go-blog"]
# realizeを使ったホットリロード --------------------------------------
WORKDIR /src/go-blog
CMD [ "realize", "start", "--run", "--build" ]
# CMD [ "realize", "start", "--run", "--build", "--server" ] # realizeサーバーを建てる場合
起動確認
コンテナを起動する。
$ docker-compose -f docker-compose.dev.yml up --build blog
Building blog
...
Recreating dev-env-blog ... done
Recreating dev-env-blog ... done
Attaching to dev-env-blog
dev-env-blog | [01:30:09][GO-BLOG] : Watching 4 file/s 8 folder/s
dev-env-blog | [01:30:09][GO-BLOG] : Command "go mod tidy" ★ビルド前にgo mod tidyが走ってる
dev-env-blog | [01:30:09][GO-BLOG] : Build started ★ビルド
dev-env-blog | [01:30:11][GO-BLOG] : Build completed in 1.971 s
dev-env-blog | [01:30:11][GO-BLOG] : Running.. ★go-blogの実行
dev-env-blog | [01:30:11][GO-BLOG] : [GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.
...
dev-env-blog | [01:30:11][GO-BLOG] : [GIN-debug] Environment variable PORT is undefined. Using port :8080 by default
dev-env-blog | [01:30:11][GO-BLOG] : [GIN-debug] Listening and serving HTTP on :8080
コンテナで起動されているプロセスを確認。
realizeのプロセスは17504で、go-blogのプロセスは17623で親プロセスは17504。realizeがgo-blogを立ち上げていることがわかる。
docker-compose -f docker-compose.dev.yml top
dev-env-blog
UID PID PPID C STIME TTY TIME CMD
-------------------------------------------------------------------------------
1000 17504 17483 0 16:32 ? 00:00:00 realize start --run --build
1000 17623 17504 0 16:32 ? 00:00:00 /dist/go-blog
ソースコードを変更すると、
dev-env-blog | [01:35:16][GO-BLOG] : GO changed /src/go-blog/app/routers/api_router.go ★変更検知
dev-env-blog | [01:35:16][GO-BLOG] : Command "go mod tidy" ★ビルド前にgo mod tidyが走ってる
dev-env-blog | [01:35:16][GO-BLOG] : Build started ★ビルド
dev-env-blog | [01:35:17][GO-BLOG] : Build completed in 0.818 s
dev-env-blog | [01:35:17][GO-BLOG] : Running.. ★go-blogの実行
...
dev-env-blog | [01:35:17][GO-BLOG] : [GIN-debug] Listening and serving HTTP on :8080
再度、コンテナで起動されているプロセスを確認。
realizeのプロセスは17504で変わりなく、go-blogの親プロセスは17504で変わりがないが、go-blogのプロセスが変更になっている。realizeがgo-blogを立ち上げ直していることがわかる。
docker-compose -f docker-compose.dev.yml top
dev-env-blog
UID PID PPID C STIME TTY TIME CMD
-------------------------------------------------------------------------------
1000 17504 17483 0 16:32 ? 00:00:01 realize start --run --build
1000 17744 17504 0 16:35 ? 00:00:00 /dist/go-blog
realizeサーバー(必要であれば)
コンテナをdeamonで起動するとgo-blogの動作が見えにくい。機能としてはログ確認しかできないが、簡易的にrealizeの状態をブラウザで確認することができる。
実際にはlogを出力するようにして確認できるようにするのが正当。
realizeサーバーを立ち上げるようにDockerfileを変更
コンテナ起動時にrealizeのサーバーも起動するように Dockerfile.blog.dev を修正する。
# realizeを使ったホットリロード --------------------------------------
WORKDIR /src/go-blog
# CMD [ "realize", "start", "--run", "--build" ]
CMD [ "realize", "start", "--run", "--build", "--server" ] # realizeサーバーを建てる場合
realizeサーバーは、.realize.yamlの設定どおりポート5002で待ち受けるので、ホストPCから接続できるように docker-compose.ymlにポートフォワード設定を追加する。
--- a/docker-compose.dev.yml
+++ b/docker-compose.dev.yml
@@ -19,12 +19,13 @@ services:
- ./src/go-blog:/src/go-blog
env_file: .env.dev
environment:
- GO111MODULE=on
ports:
- 8888:8080
+ - 5002:5002 # realizeサーバー用のPortFoward設定
networks:
dev-env-link:
ipv4_address: $NETWORK_BASE.20
golang:
コンテナを立ち上げると、下記のようにサーバーが起動したことがわかる。
Recreating dev-env-blog ... done
Attaching to dev-env-blog
dev-env-blog | [01:41:02][REALIZE] : Started on 0.0.0.0:5002 ★realizeサーバー起動
dev-env-blog | ⇨ http server started on [::]:5002
dev-env-blog | [01:41:02][GO-BLOG] : Watching 4 file/s 8 folder/s
dev-env-blog | [01:41:03][GO-BLOG] : Command "go mod tidy"
...
dev-env-blog | [01:41:04][GO-BLOG] : [GIN-debug] Listening and serving HTTP on :8080 ★go-blogも起動
後は、ホストPC上のブラウザで、http://localhost:5002
にアクセスするとrealizeの状態を確認できる。
まとめ
ここまでで、go言語でWebアプリを構築し、プログラム変更をすぐに確認できるgo開発環境をDockerで作成できた。
現状の環境は下記の図の通り。
ここまでの一式