🔖

Golang の Docker 環境はこれで落ち着いた

2021/02/12に公開

Golang をはじめて1週間ほどですが、最初に Docker を使って環境を構築する際に結構悩みました😅
自分の備忘録も兼ねて記述しておきます。

前提

  • RDB が必要
  • なるべく Mac の環境は汚したくない
  • go module を使う
  • IDE の利用を前提として良い

この構成は gin を使ってバックエンド API を作りたいとなった時に作成したものです。
https://github.com/gin-gonic/gin
よってそういった用途にしか適さない可能性もあるのでご了承ください。

全体的に改善の余地はあると思うので、教えて頂けると嬉しいです!!

こうしたら良いのではないか

Dockerfile
FROM golang:1.15.7-alpine as dev

ENV ROOT=/go/src/app
ENV CGO_ENABLED 0
WORKDIR ${ROOT}

RUN apk update && apk add git
COPY go.mod go.sum ./
RUN go mod download
EXPOSE 8080

CMD ["go", "run", "main.go"]


FROM golang:1.15.7-alpine as builder

ENV ROOT=/go/src/app
WORKDIR ${ROOT}

RUN apk update && apk add git
COPY go.mod go.sum ./
RUN go mod download

COPY . ${ROOT}
RUN CGO_ENABLED=0 GOOS=linux go build -o $ROOT/binary


FROM scratch as prod

ENV ROOT=/go/src/app
WORKDIR ${ROOT}
COPY --from=builder ${ROOT}/binary ${ROOT}

EXPOSE 8080
CMD ["/go/src/app/binary"]
docker-compose.yml
version: "3.8"

services:
  db:
    image: postgres:13.1-alpine
    volumes:
      - ./tmp/db:/var/lib/postgresql/data
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: password
      PGDATA: /var/lib/postgresql/data/pgdata

  web:
    build:
      context: .
      dockerfile: Dockerfile
      target: dev
    tty: true
    stdin_open: true
    volumes:
      - .:/go/src/app
    ports:
      - 8080:8080
    depends_on:
      - db

解説

開発中は基本的に dev ステージを利用します。
builder 以降のステージは本番運用のためです。ビルドしてバイナリを実行するだけなので今回は特に解説しません。面倒臭いので。
Dockerfile ごと分けても良いでしょう。

FROM golang:1.15.7-alpine as dev

ENV ROOT=/go/src/app
ENV CGO_ENABLED 0
WORKDIR ${ROOT}

RUN apk update && apk add git
COPY go.mod go.sum ./
RUN go mod download
EXPOSE 8080

CMD ["go", "run", "main.go"]

今自分が書いているソースコードを配置するディレクトリは、 /go/src/app あたりにしておくのが無難だと思います🙌

module をインストールする際に流石に git は必要なので、 alpine をベースに git だけは入れておきます。
最後の CMD でメインプロセスを動かします。なので docker-compose などで上書きしない限りはコンテナが起動したタイミングで、 main.go が実行されることになります。

開発用のイメージですので、ソースコードの変更が反映されるように docker-compose 側でボリュームマウントしています。

    volumes:
      - .:/go/src/app

経緯とか。。。

以下、時系列ではありませんが、考えたことをつらつら書いておきます。。。

  • IDE の入力補完のため、どうしても Mac に直接 Golang を入れる必要があった
  • どのみちローカルを汚すのだから、Docker を使わない方が良いかと思ったが、RDB との連携もしたかったのでやはり Docker を使いたい
  • 調べていくと Realize などのホットリロードツールが見つかるが、開発も止まっているようだし採用を見送った
  • ソースコード変更時のコンパイル、リロードは自力で行うが、IDE(私の場合は Goland を使用)に頼ってショートカット1つで実行できるようにする
  • dev ステージのイメージサイズは大体 500MB ほどだったのでもうちょっと軽くしたい気もする
  • prod ステージ(scratchにバイナリファイルをコピーしただけ)のイメージサイズは何と 15MB だった😇

おまけ

この構成はホットリロードのために余計なライブラリを入れていないのは良いのですが、流石にソースコードを変更する度にコマンドを数回叩いて再起動するのは面倒です😢

なので Goland の利用が前提になりますが、どうしたかを書いておこうと思います。VSCode とかでも出来ると思います。知らんけど。
https://www.jetbrains.com/ja-jp/go/

  1. 画面右上の Add Configuration をクリックします

  2. 既に見えちゃっていましたが、 Docker の実行構成を作ります。

こんな感じです。なんてことはなく、単にプロジェクトルートにある docker-compose.yml の web サービスを実行するだけです。
先ほども見たように、コンテナを立ち上げると CMD ["go", "run", "main.go"] が走りますので、これでソースコードの変更が反映された状態でコンテナが再起動します。
ここまでやれば後は簡単です。

  1. 右上に作成した実行構成が表示されていることを確認した上で、右隣の Run ボタンをクリックします。

これだけでも良いのですが、何回も実行することになると思うので、できればショートカットを覚えてしまいましょう。
ショートカットは、 Ctrl + R です😁

実行するとウィンドウが表示され、ログも確認することができます。

gin のログが表示されていますね。

追記

Docker コンテナの実行構成を使っても良いのですが、なんかタブの移動が必要だったりサクサク感が無いので、シェルスクリプトを使う方法に切り替えました。

#!/bin/bash

echo "restarting..."
docker-compose restart web
docker-compose logs -f web

やることとしては同じです。
上のようなファイルをプロジェクトルートに置きます。
Docker の時と同じようにシェルスクリプトの実行構成を作れますので、配置したスクリプトのパスを設定してください。
あとは Ctrl + R ですぐに以下のようになります。こっちの方が良いかもしれません。

もっと良いやり方がある気がしますが、今のところはこんな感じで落ち着きました!
以上現場からでした😎

Discussion