最終目的
- go言語でWebアプリを構築するためのgo開発環境をDockerで作成する。
目標
- Docker上にコンソール立ち上げ環境を作る。
- フレームワークginを使った簡単なREST APIサーバーを立ち上げる。
- イメージビルド時にプログラムをコンパイルしコンテナ起動と同時にホストPCからAPIにアクセスできるようにする。←いまここ
開発環境の構成
開発環境 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レポジトリ
└── app/ ★ go-blogアプリのソースコードルートディレクトリ
├── entrypoints/
│ └── production/
│ └── main.go ★ go-blogアプリのmainパッケージファイル
├── go.mod
└── go.sum
やりたいことは下記の通り。
- go-blogコマンドを
docker-compose build
でイメージ作成時にビルドしたい。- 別レポジトリ管理のgo-blogソースコードをgo-envレポジトリ管理下のDockerfile.blog.devでビルドする。
- go-blogコマンドを
docker-compose up
でコンテナ起動時に自動起動したい。- go-blogコマンドはrootアカウントではなく、システムアカウントで起動する。
- コンテナ起動時に起動したgo-blogアプリにホストPCからアクセスしたい。
- コンテナ上で起動したgo-blogは8080ポートで受け付けるので、ホストのlocalhost:8888でアクセスできるようにポートフォワードする。
blogサービスのコンテナ内ディレクトリ構成
blogコンテナ内のディレクトリ構成(抜粋)は下記の通りとする。
/
├── dist/ ★ go-blogアプリを起動するディレクトリ
│ └── go-blog
├── home/
│ └── gouser/ ★ コンテナ起動時にホストPCのディレクトリをマウント(イメージビルド時には存在しない)
├── src/
│ └── go-blog/ ★ コンテナ起動時にホストPCのディレクトリをマウント(イメージビルド時には存在しない)
│ └── app/ ★ コンテナ動作時のgo-blogのソースディレクトリ
└── tmp/
├── app/ ★ イメージビルド時のgo-blogのソースディレクトリ(イメージビルドの最後で削除)
└── go-cache/ ★ go build時に作成されるキャッシュを格納するディレクトリ
構成ファイルの変更
変更するのは、Dockerfile.blog.dev
とdocker-compose.dev.yml
の2ファイル。
Dockerfile.blog.devの変更
go-blogコマンドの格納/実行ディレクトリ、go build時のキャッシュディレクトリをシステムアカウント用に作成、go-blogコマンドをイメージビルド時にコンパイルし、コンテナ起動時にgo-blogコマンドを実行するように変更。
FROM golang:1.18.3-buster
ARG USER_NAME=$USER_NAME
ARG USER_ID=$USER_ID
ARG GROUP_NAME=$GROUP_NAME
ARG GROUP_ID=$GROUP_ID
ARG TZ=$TZ
ENV TZ=$TZ
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
# システムアカウントの作成(ホームディレクトリは作成しない)
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
# ------ これより下が書き変わった部分 ------
# ビルドしたgo-buildコマンドの格納先ディレクトリ作成(root実行)
# コンテナ上でのgo-blogコマンド実行ディレクトリでもある
# ディレクトリのオーナーはシステムアカウント
RUN mkdir /dist && \
chown $USER_NAME.$GROUP_NAME /dist && \
chmod 750 /dist
# go buildに必要なキャッシュディレクトリの作成(root実行)
# ディレクトリのオーナーはシステムアカウント
RUN mkdir /tmp/go-cache && \
chown $USER_NAME.$GROUP_NAME /tmp/go-cache && \
chmod 755 /tmp/go-cache
# go buildに必要なキャッシュディレクトリの設定(root実行)
ENV GOCACHE=/tmp/go-cache
# ホスト環境のソースコードをテンポラリの/tmp/appにコピー(root実行)
COPY ./src/go-blog/app /tmp/app
# ソースコードディレクトリのオーナー変更(root実行)
# ビルド時にgo.mod、go.sumを書き換えるため、オーナー変更が必要
RUN chown -R $USER_NAME.$GROUP_NAME /tmp/app
# システムアカウントに切り替え
USER $USER_NAME
# ワークディレクトリをソースコードディレクトリに切り替え(システムアカウント実行)
WORKDIR /tmp/app
# ソースコードをコンパイルし、/distディレクトリgo-blogコマンドを作成(システムアカウント実行)
RUN go mod tidy
RUN go build -o /dist/go-blog entrypoints/production/main.go
# ワークディレクトリを /dist に切り替え(システムアカウント実行)
WORKDIR /dist
# テンポラリの/tmp/appディレクトリを削除(システムアカウント実行)
RUN rm -rf /tmp/app
# コンテナ起動時にgo-blogコマンドが実行されるように設定
CMD ["./go-blog"]
docker-compose.dev.ymlの変更
コンテナ起動時にbashを起動せずDockerfileのCMDを実行する、ホストのポート8888をコンテナの8080ポートにポートフォワードするように変更する。
diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml
index 38dffdc..3a7b975 100644
--- a/docker-compose.dev.yml
+++ b/docker-compose.dev.yml
@@ -1,42 +1,44 @@
version: "3"
services:
blog:
build:
context: .
dockerfile: ./env/Dockerfile.blog.dev
args:
- USER_NAME=$USER_NAME
- USER_ID=$USER_ID
- GROUP_NAME=$GROUP_NAME
- GROUP_ID=$GROUP_ID
- TZ=$TZ
image: dev-env-blog-image
container_name: dev-env-blog
- command: bash
+ # command: bash # DockerfileのCMDでgo-blogを起動するように変更したためコメントアウト
tty: true
volumes:
- ./src/golang:/home/$USER_NAME
- ./src/go-blog:/src/go-blog
env_file: .env.dev
environment:
- GO111MODULE=on
+ ports:
+ - 8888:8080 # ホストPCのポート8888への通信をコンテナの8080ポートにフォワード
networks:
dev-env-link:
ipv4_address: $NETWORK_BASE.20
golang:
動作確認
Makefaileは下記のような設定。
# 変数 --------------------------
DC=docker-compose -f docker-compose.dev.yml
# 環境 --------------------------
# top
env-top:
$(DC) top
# blog環境 ---------------------
# blog環境 imageビルド
blog-build:
$(DC) build --force blog
# blog環境 container実行(deamon)
blog-up:
$(DC) up -d blog
# blog環境 リビルド、container実行(コンソール出力)
blog-run:
$(DC) up --build blog
# blog環境 container実行、同一コンテナでbash実行
blog-console: blog-up
$(DC) exec blog bash
# blog環境 container停止
blog-stop:
-$(DC) stop blog
# blog環境 container停止/破棄
blog-down: blog-stop
-$(DC) rm -f blog
# blog環境 container停止/破棄、image破棄
blog-rmi: blog-down
-$(D) rmi -f dev-env-blog-image
イメージビルドとコンテナ起動
イメージビルドとコンテナ起動
$ make blog-build && make blog-run
docker-compose -f docker-compose.dev.yml build --force blog
Building blog
[+] Building 0.2s (19/19) FINISHED
...
=> => naming to docker.io/library/dev-env-blog-image 0.0s
Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them
docker-compose -f docker-compose.dev.yml up blog
Starting dev-env-blog ... done
Attaching to dev-env-blog
dev-env-blog | [GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attache
d.
dev-env-blog |
dev-env-blog | [GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
dev-env-blog | - using env: export GIN_MODE=release
dev-env-blog | - using code: gin.SetMode(gin.ReleaseMode)
dev-env-blog |
dev-env-blog | [GIN-debug] GET /api/health-check --> github.com/jun00rbiter/go-blog/controllers.GetHealthChec
k (3 handlers)
dev-env-blog | [GIN-debug] [WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.
dev-env-blog | Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.
dev-env-blog | [GIN-debug] Environment variable PORT is undefined. Using port :8080 by default
dev-env-blog | [GIN-debug] Listening and serving HTTP on :8080
別コンソールでコンテナを確認するとgo-blog
プロセスがUID:1000(gouser)で動作しているのが分かる。
また、docker ps
コマンドでコンテナ状態を確認し、ホストPCポート8888からコンテナポート8080にポートフォワードしていることを確認。
$ make env-top
docker-compose -f docker-compose.dev.yml top
dev-env-blog
UID PID PPID C STIME TTY TIME CMD
-------------------------------------------------------------
1000 15965 15945 2 15:54 ? 00:00:00 ./go-blog
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a3c5331c38ae dev-env-blog-image "./go-blog" 4 minutes ago Up 4 minutes 0.0.0.0:8888->8080/tcp dev-env-blog
動作確認
ホストPCのコンソールからlocalhost:8888
に対してhealth-checkのAPIを叩くと、応答が帰ってくることを確認。
もちろん、Chrome等のブラウザからアクセスしても応答が帰ってくる。
$ curl -v http://localhost:8888/api/health-check | jq
...
> GET /api/health-check HTTP/1.1
> Host: localhost:8888
> User-Agent: curl/7.74.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Content-Type: application/json; charset=utf-8
< Date: Tue, 07 Jun 2022 16:00:35 GMT
< Content-Length: 15
<
{ [15 bytes data]
100 15 100 15 0 0 1363 0 --:--:-- --:--:-- --:--:-- 1500
* Connection #0 to host localhost left intact
{
"status": "OK"
}
もちろん、今まで通り別のgolang
コンテナのコンソールからも通信は可能。
gouser@c25cfea93c64:~$ curl http://blog:8080/api/health-check | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 15 100 15 0 0 7500 0 --:--:-- --:--:-- --:--:-- 7500
{
"status": "OK"
}
ソースコード修正と再起動
go-blogの動作中に、ソースコードを書き換えても、起動中のコンテナには反映されない。
コンテナを停止し、イメージビルド、コンテナ再起動が必要。
$ make blog-run ★ ビルド/コンテナ起動
docker-compose -f docker-compose.dev.yml build --force blog
Building blog
...
dev-env-blog | [GIN-debug] Listening and serving HTTP on :8080
Gracefully stopping... (press Ctrl+C again to force) ★ Ctrl+Cで強制終了
Stopping dev-env-blog ... done
$ make blog-rmi && make blog-run ★ イメージ破棄およびビルド/コンテナ起動
docker-compose -f docker-compose.dev.yml stop blog
docker-compose -f docker-compose.dev.yml rm -f blog
Going to remove dev-env-blog
Removing dev-env-blog ... done
docker rmi -f dev-env-blog-image
Untagged: dev-env-blog-image:latest
Deleted: sha256:4438573e4a774f522ec7d8425ee02aa399126c5fb35401f6d0c31276f29ee43a
docker-compose -f docker-compose.dev.yml up --build blog
Building blog
...
dev-env-blog | [GIN-debug] Listening and serving HTTP on :8080
ひとまず、go言語でWebアプリを構築するためのgo開発環境をDockerで作成することができた。
現状の環境は下記の図の通り。ポートフォワード設定をしたことで、ホストPCの8888ポートに接続することで、blogコンテナの8080ポートにアクセスできる。
課題としては、ソースコードを変更しても即座にコンテナに反映されない点。
この課題については、ホットリロードのプログラムを用いることで解決できる。
ここまでの一式