Herokuで動いているGolangプロジェクトをDockerコンテナで動くように

前回の記事で、Herokuで動くようになったGolangプロジェクト(フレームワークはEcho)を今回Dockerコンテナで動くようにしてみました。 simple-minds-think-alike.hatenablog.com

前提

  • Herokuアカウント作成済
  • ローカル環境にHeroku CLIインストール済
  • ローカル環境にDockerインストール済

Dockerfileの作成

こんな感じで動くDockerfileを作ってみます。 f:id:moritamorie:20200521113452j:plain

以下のようなDockerfileをプロジェクトに追加しました。一つづつ解説していきます。

# Dockerfile

# Herokuで実行するGoのバイナリを作る
FROM golang:latest as builder

ENV CGO_ENABLED=0
ENV GOOS=linux
ENV GOARCH=amd64
WORKDIR /app
COPY . .
RUN go build main.go

# 作ったGoのバイナリを実行する
FROM alpine:latest  
COPY --from=builder /app /app

CMD /app/main $PORT

実行バイナリファイルを作るイメージ

FROM golang:latest as builder

実行バイナリを作るためにgo build を使いたいので、golang:latestイメージを使っています。

変数ENV_CGO_ENABLED

ENV CGO_ENABLED=0

netパッケージを使う場合には、CGO_ENABLEDを無効にしないと複数のバイナリができてしまう(Dynamic link)ので無効にしておきます。

参考にしたStackoverflowのリンクを貼っておきます。 stackoverflow.com

Golangのドキュメントにも書いてありました。

Go 1.2 Release Notes - The Go Programming Language

The net package requires cgo by default because the host operating system must in general mediate network call setup. On some systems, though, it is possible to use the network without cgo, and useful to do so, for instance to avoid dynamic linking. The new build tag netgo (off by default) allows the construction of a net package in pure Go on those systems where it is possible.

変数GOOS, GOARCH

ENV GOOS=linux
ENV GOARCH=amd64

以下のHerokuのドキュメントに

Container Registry & Runtime (Docker Deploys) | Heroku Dev Center

Docker images run in dynos the same way that slugs do, and under the same constraints:

と書いてあり、Docker使わない場合とホストOS自体は同じになるようなので、OSはUbuntu 18.04ベース(2020年5月時点で最新のHerokuスタック)ということになります。

実行環境であるalpineはマルチアーキテクチャイメージで、ホストOSと同じlinuxアーキテクチャのイメージになるので、Ubuntu 18.04と同じになるように

  • ENV GOOS=linux
  • ENV GOARCH=amd64

を指定します。

バイナリを実行するイメージ

バイナリを実行するコンテナにシンプルなalpineイメージを使います。

FROM alpine:latest  

ビルドしたバイナリをコピー

COPY --from=builder /app /app

バイナリ実行

CMD /app/main $PORT

前回の記事でも書きましたが、HerokuではWeb worker processに紐付けられるPORT番号は環境変数PORTから取れます。 Herokuの以下のドキュメントに記載があります。

Runtime Principles | Heroku Dev Center

ローカルで動作確認

$ docker build -t golang-sample .
$ docker run -e "PORT=3000" -p 3000:3000 -t golang-sample

Heroku container registoryにイメージをpush

Heroku container registoryにログイン

$ heroku container:login

イメージビルドして、Heroku container registoryにイメージをpushします

$ heroku container:push web

moritamorie:~/go/src/github.com/golang-sample$ heroku container:push web
 ›   Warning: heroku update available from 7.39.0 to 7.39.3.
=== Building web (/home/moritamorie/go/src/github.com/golang-sample/Dockerfile)
Sending build context to Docker daemon   11.7MB
Step 1/10 : FROM golang:latest as builder
 ---> 7e5e8028e8ec
〜〜〜
Step 10/10 : CMD /app/main $PORT
 ---> Using cache
 ---> 4f60b0d6aaa9
Successfully built 4f60b0d6aaa9
Successfully tagged registry.heroku.com/golang-sample/web:latest
=== Pushing web (/home/moritamorie/go/src/github.com/golang-sample/Dockerfile)
The push refers to repository [registry.heroku.com/golang-sample/web]
Your image has been successfully pushed. You can now release it with the 'container:release' command

リリース

Heroku container registoryにpushしたイメージをリリースします

$ heroku container:release web

イメージビルド、Heroku container registoryへのpush、リリースの手間を無くす

以下の heroku.yml ファイルを追加すると、buildプロセスをHeroku側に移すことができるので、Dockerを使わないときと同様の手順( git push heroku master )で済みます。

# heroku.yml

build:
  docker:
    web: Dockerfile

Procfile削除

コンテナ側でバイナリの実行までやってくれるので、Docker対応が済んだらProcfileは削除してしまってOKです。

参考