dockerを使ってgoの環境構築
これからデータベースやストレージなどGo以外も使って開発していくので、そのまま自分のPC上にこれらの環境を構築していくのはいいものではありません。そこでDockerを使ってコンテナ上で開発をしていこうと思います。
今回のデータはgithubの07go_dockerにあります。
Dockerとは
コンテナという技術を使って仮想環境を構築できるツールです。例えば私が使っているMacOS上にWindowsのソフトが使えるような場所を作成します。これが仮想環境です。
仮想環境といえばVMWareやVirtualBoxなどがありますが、それらと異なりゲストOSを挟まないので軽量に動作する利点があります。
私もここら辺は全然詳しくないのですが、Dockerは開発環境を持つコンテナを簡単に立ち上げたり潰したりできるので重宝しています。またいろいろなものがコンテナ内で閉じているので自分のPC環境を汚すことがありません(これが今回Dockerを使う1番の目的でした)。
他にもコンテナ作成用のファイルを共有するだけでDockerを使えるPCならどれでも同じ環境が作れます。チームで開発する時にも重宝されるツールです。
Dockerのインストール等は各々に任せます。とても話題になったツールなので調べればいくらでもインストール方法は出てきます。また基本的な使い方も本筋から逸れるのでここでは説明しません。しかしマネするだけでも同じ環境が作れると思います。
コンテナの作成
ファイルの作成
開発環境であるコンテナを作成するためにはコンテナの元になるイメージファイルが必要です。
イメージファイルを1から作るのは素人の私には無理なのでDocker Hubから取得します。DockerHubにはいろいろな種類のイメージが管理されているので我々がやろうと思うことに使用するイメージは大体あるのでまずはDockerHubをみてみることをオススメします。
まずは07go_dockerというディレクトリを作成し、以下のように2つのファイルを作成してください。
$ tree
.
├── Dockerfile
└── docker-compose.yml
それぞれのファイルを説明します。
- Dockerfile
DockerHubから取得してくるイメージの種類や自分たちの開発に合わせた設定を行います。今回はLinuxベースOSであるalpine上にgoの最新verをインストールしたイメージを使います。alpineを使う理由は容量が他と比べて小さいからです。# 2020/10/14最新versionを取得 FROM golang:1.15.2-alpine # アップデートとgitのインストール!! RUN apk update && apk add git # appディレクトリの作成 RUN mkdir /go/src/app # ワーキングディレクトリの設定 WORKDIR /go/src/app # ホストのファイルをコンテナの作業ディレクトリに移行 ADD . /go/src/app
そのあとはコメントアウトに書いているようにワーキングディレクトを設定します。 - docker-compose.yml
単体のコンテナだけを使う場合は上のDockerfileだけで良いのですが、今後データベースやストレージのコンテナなど複数のコンテナを立てる場合は、このdocker-composeファイルを書いておくと便利です。これでコンテナどうしの接続などの設定をすることが出来ます。version: "3" # composeファイルのバージョン services: app: # サービス名 build: . # ①ビルドに使うDockerfileの場所 tty: true # ②コンテナの永続化 volumes: - ./app:/go/src/app # ③マウントディレクトリ
- 書き方は上のような感じです。buildでビルドに使うDockerfileを指定します。
- コンテナは基本的に立ち上がって所定の処理が終わったら自動で閉じます。コンテナの永続化が必要な場合は
tty:true
を入れてください。 - コンテナ内の状態は変更を加えてもコンテナを閉じると元に戻ってしまいます。そこでワーキングディレクトリなど頻繁に変更を加えるディレクトリなどはホストOS上のディレクトリをマウントするのが一般的です。ここでは/appディレクトリをコンテナ内のワーキングディレクトリにマウントするようにしています。
ファイルを実行しコンテナを作成
ファイルを作成したらそのディレクトリで以下を実行します。
まずはコンテナを作成します。以下のコマンドでイメージの取得します。
# コンテナイメージのビルド
$ docker-compose build
Building app
Step 1/4 : FROM golang:1.15.2-alpine
1.15.2-alpine: Pulling from library/golang
df20fa9351a1: Already exists
ed8968b2872e: Pull complete
a92cc7c5fd73: Pull complete
e871e8e8d7a9: Pull complete
e73272ec9a57: Pull complete
Digest: sha256:4d8abd16b03209b30b48f69a2e10347aacf7ce65d8f9f685e8c3e20a512234d9
Status: Downloaded newer image for golang:1.15.2-alpine
---> b3bc898ad092
Step 2/4 : RUN mkdir /go/src/app
---> Running in 2825baa5ff8b
Removing intermediate container 2825baa5ff8b
---> 1d9cfa183605
Step 3/4 : WORKDIR /go/src/app
---> Running in 6634eaf2d95d
Removing intermediate container 6634eaf2d95d
---> 45a9a0733c47
Step 4/4 : ADD . /go/src/app
---> ce52e2af08cd
Successfully built ce52e2af08cd
Successfully tagged 07go_docker_app:latest
次にきちんとイメージが出来ているか以下のコマンドで確認します。
# イメージの確認
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
07go_docker_app latest e61c3be1f5be 31 seconds ago 321MB
07go_db_app latest 5cf69e7bd132 14 hours ago 321MB
<none> <none> 1022eb1f93f7 14 hours ago
略
イメージが出来ていたらマウントするためのappディレクトリとファイルを作成します。
$ touch app/main.go
$ tree
.
├── Dockerfile
├── app
│ └── main.go
└── docker-compose.yml
main.goには以下を記述します。コンテナから実行出来たことを確認するためにメッセージを出力するだけのものです。
package main
import "fmt"
func main() {
fmt.Println("Hello golang from docker!")
}
コンテナを立ち上げます。-d
オプションをつけるとバックグラウンドでの起動になります。
# コンテナの立ち上げ
$ docker-compose up -d
Removing 07go_docker_app_1
Recreating c8e4321b21d2_07go_docker_app_1 ... done
コンテナからgo run main.go
コマンドを実行します。メッセージが返ってきたので上手くいっています。
# 作成したファイルの実行
$ docker-compose exec app go run main.go
Hello golang from docker!
最後はコンテナを閉じることを忘れないようにしてください。
$ docker-compose down
Stopping 07go_docker_app_1 ... done
Removing 07go_docker_app_1 ... done
Removing network 07go_docker_default
コンテナのポートをホストOSのポートにつないでブラウザで閲覧
次はこれまでに作ったウェブサーバをDockerコンテナ上に作成しましょう。以前作成した04templateを07go_docker/appにコピーします。
$ tree
.
├── Dockerfile
├── app
│ ├── assets
│ │ ├── img
│ │ │ └── biplane.png
│ │ ├── js
│ │ │ └── script.js
│ │ └── style
│ │ └── style.css
│ ├── main.go
│ └── root
│ ├── index.html
│ └── template
│ ├── footer.html
│ └── header.html
└── docker-compose.yml
これで実行します。
# go-docker-1
$ docker-compose up -d
Creating network "07go_docker_default" with the default driver
Creating 07go_docker_app_1 ... done
$ docker-compose exec app go run main.go
2020/10/15 02:10:14 Server listening on http://localhost:8080/
これでhttp://localhost:8080/
にアクセスしても何も表示されないと思います。これはコンテナ内のlocalhostにウェブサーバをつなげているだけで自分たちが使用しているホストOSには繋がっていないので表示されません。コンテナの8080ポートとホストOSの8080ポートをつないであげる必要があります。docker-compose.ymlを少し書き換えます。
version: "3" # composeファイルのバージョン
services:
app: # サービス名
build: . # ビルドに使うDockerfileの場所
tty: true # コンテナの永続化
ports: # ホストOSのポートとコンテナのポートをつなげる
- "8080:8080"
volumes:
- ./app:/go/src/app # マウントディレクトリ
これでお互いのポートがつながります。
これでもう一度go-doccker-1を実行してみてください。次は上手くいくはずです。
ホットリロード機能をつける
ここまでで開発をすることは可能ですが、ファイルを書き換える度にdocker-compose exec app go run main.go
コマンドを打たなければいけないので少し面倒です。ここではrealize
を使用してファイルを保存するとgoファイルを立ち上げ直さなくても変更が反映されるホットリロードを実現したいと思います。
ここら辺は素人すぎたのでrealize + dockerでGolangのホットリロード付き開発環境を構築するを参考にさせてもらっています。
Dockerfileの最後に以下を追加します。
RUN go get -u github.com/oxequa/realize
CMD ["realize", "start"]
これでrealizeをインストールし、実行します。
$ docker-compose build
$ docker-compose up -d
これで/appに.realize.yaml
というファイルが出来ます(もしかしたらbuildの時点で作成されるかもしれません)。これに以下を書き加えます。
settings:
legacy:
force: false
interval: 0s
schema:
- name: app
path: .
commands: {}
# ここから
commands:
run:
status: true
# ここまでを追加する
watcher:
extensions:
- go
paths:
- /
ignored_paths:
- .git
- .realize
- vendor
今回はルートにmain.goがあるシンプルな構成なので設定はほとんど必要ありませんでしたが、もしファイル名をserver.goにしたりmain関数ファイルの場所が異なる場合はさらに設定が必要です。上で紹介した参考記事をみてください。
念のため一度コンテナを停止して、再度起動し実行します。
$ docker-compose down
$ docker-compose up -d
$ docker-compose exec app go run main.go
2020/10/15 03:47:08 Server listening on http://localhost:8080/
2020/10/15 03:47:08 listen tcp :8080: bind: address already in use
例えばhandleFuncの第1引数(パス)を変えるなど変更を加えて保存をすると自動的に変更が反映されていると思います。
これでホットリロードができるようになったので毎回実行を止める必要はなくなりました。開発に専念することが出来ます。
追記
以下のようにgo run main.go
ではなくrealize start
でサーバを立ち上げるとログが出ます。
$ docker-compose up -d
$ docker-compose exec app realize start
[01:34:05][APP] : Watching 1 file/s 7 folder/s
[01:34:05][APP] : Install started
[01:34:06][APP] : Install completed in 0.273 s
[01:34:06][APP] : Running..
[01:34:06][APP] : 2020/10/18 01:34:06 &[] &[] &[]
[01:34:06][APP] : 2020/10/18 01:34:06 map[1:{1 gophar 5555}]
[01:34:06][APP] : 2020/10/18 01:34:06 map[1:{1 gophar 5555} 2:{2 octcat 0000}]
[01:34:06][APP] : 2020/10/18 01:34:06 map[1:{1 gophar 5555} 2:{2 octcat 0000} 3:{3 gophar 5555}]
[01:34:06][APP] : 2020/10/18 01:34:06 Server listening on http://localhost:8080/
[01:35:06][APP] : GO changed /go/src/app/main.go
[01:35:06][APP] : Install started
[01:35:08][APP] : Install completed in 1.700 s
[01:35:08][APP] : Running..
[01:35:08][APP] : 2020/10/18 01:35:08 &[] &[] &[]
[01:35:08][APP] : 2020/10/18 01:35:08 map[1:{1 gophar 5555} 2:{2 octcat 0000} 3:{3 gophar 5555}]
[01:35:08][APP] : 2020/10/18 01:35:08 Server listening on http://localhost:8080/
Discussion