Goで作ったアプリケーションをWebサーバーに上げて活用したいなと思ったときに
無料だし、ある程度使っていていて慣れてたしという理由で、Herokuを使おうと思ったわけです。
ただ、いざやってみると色んな方法がありすぎて、いい感じの情報を見つけるのが難しく
デプロイするのに色々詰まったので、備忘録として残しておきます。
よりよい方法やバグ等ございましたら、アドバイスいただけると光栄です。
事前準備
1.Herokuアカウントの作成
Herokuの公式サイトから、アカウントを作成してください。
2.Heroku CLIのインストール
次にターミナルからHerokuを操るための Heroku CLI
を
こちらのページから、ダウンロード・インストールしてください。
下記のように入力して、結果が返ってくれば完了です。
% heroku -v
heroku/7.47.5 darwin-x64 node-v12.16.2
3.動作環境
その他、下記環境で動作しています。
適宜必要なものをインストールしてあるとスムーズです。
% go version
go version go1.15.5 darwin/amd64
% docker -v
Docker version 19.03.13, build 4484c46d9d
$ docker-compose -v
docker-compose version 1.27.4, build 40524192
ローカルで動かす
Herokuにデプロイする前に、まずはローカルで動かせるように準備します。
1.簡単なリクエストを受け付けるコードの作成
まずは簡単なリクエストを受け付ける
以下のようなコードを含むファイルを作成します。
※今回はフレームワークに Gin を使用しています。
package main
import (
"net/http"
"os"
"github.com/gin-gonic/gin"
)
func main() {
r:= gin.Default()
r.GET("/", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "hello world",
})
})
port := os.Getenv("PORT")
if port == "" {
port = "8080"
}
if err := r.Run(":" + port); err != nil {
panic(err)
}
}
HerokuではWeb worker processにポートが紐付けられるので、os.Getenv("PORT")
でPORT番号を取ってきます。
Herokuのこちらのドキュメントに記載があります。
2.Docker関連の準備
2−1.docker-compose.yml
ローカル開発では、Dockerイメージのビルドや各コンテナの起動・停止などをより簡単に行える docker-compose
を使用します。
dockers
ディレクトリを作成し、その中に下記のような docker-compose.yml
を作成します。
version: "3.5"
services:
app:
build:
context: ..
target: builder
ports:
- 8080:8080
volumes:
- ../:/app
command: air
2−2.Dockerfile
上述の docker-compose.yml を実行するのに必要な Dockerfile を用意します。
アプリのルートディレクトリに下記をコピーして作成してください。
FROM golang:1.15.6-alpine3.12 as builder
RUN apk update \
&& apk add --no-cache git \
&& go get -u github.com/cosmtrek/air \
&& chmod +x ${GOPATH}/bin/air
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
ファイルの中身を詳細に見ていきます。
各種ライブラリのインストール
RUN apk update \
&& apk add --no-cache git \
&& go get -u github.com/cosmtrek/air \
&& chmod +x ${GOPATH}/bin/air
それぞれ細かく見ていきます。
&& apk add --no-cache git \
今回は、軽量な Alpine Linux の Docker image を採用しているため、
Go Modules のインストールに必要な git
をインストールします。
※使用する Docker image がデフォルトのイメージであれば、インストール済みのため不要です。
&& go get -u github.com/cosmtrek/air \
開発時にホットリロードを有効にしたいので、Air をインストールします。
※有名なライブラリで Realize がありますが、 2020年12月23日現在メンテナンスが活発でないため、
メンテナンスが継続されており、GoModuleにも対応している Air を使用します。
&& chmod +x ${GOPATH}/bin/air
上述でインスールした Air の実行権限を付与します。
各種ファイルのビルド/コピー
COPY go.mod go.sum ./
RUN go mod download
COPY . .
アプリのルートディレクトリをコンテナ内にコピーする前に
go.mod
と go.sum
をコピーして、go mod download
でライブラリをインストールしています。
こうすることで、次回以降、ソースコードの変更のみ(go.mod
と go.sum
に変更がない場合)であれば
ソースコード全体をコピーする COPY . .
の行から実行されるようになります。
それより前のステップはキャッシュが活用されスキップされ、ビルド速度が向上します。
3.Makefileの作成
docker-compose.yml
起動のためのコマンドが長くなるので、Makefileを設定して立ち上げやすくします。
up:
docker-compose -f dockers/docker-compose.yml build && docker-compose -f dockers/docker-compose.yml up -d
down:
docker-compose -f dockers/docker-compose.yml down
logs:
docker-compose -f dockers/docker-compose.yml logs -f app
こうすることで下記コマンドを利用できます。
# コンテナを作成、起動
% make up
# コンテナを停止
% make sown
# ログを出力
% make logs
4.ホットリロード設定
ホットリロードには Air を使用します。
公式の サンプル を基に、設定ファイルを下記のように作成します。
# Config file for [Air](https://github.com/cosmtrek/air) in TOML format
# Working directory
# . or absolute path, please note that the directories following must be under root.
root = "."
tmp_dir = "build"
[build]
# Just plain old shell command. You could use `make` as well.
cmd = "go build -o ./build/main ./cmd"
# Binary file yields from `cmd`.
bin = "build/main"
# Customize binary.
full_bin = "./build/main web"
# Watch these filename extensions.
include_ext = ["go"]
# Ignore these filename extensions or directories.
exclude_dir = ["build"]
# Watch these directories if you specified.
include_dir = []
# Exclude files.
exclude_file = []
# It's not necessary to trigger build each time file changes if it's too frequent.
delay = 1000 # ms
# Stop to run old binary when build errors occur.
stop_on_error = true
# This log file places in your tmp_dir.
log = "air_errors.log"
[log]
# Show log time
time = false
[color]
# Customize each part's color. If no color found, use the raw app log.
main = "magenta"
watcher = "cyan"
build = "yellow"
runner = "green"
[misc]
# Delete tmp directory on exit
clean_on_exit = true
5.サーバー立ち上げ準備
docker-compose 起動時に必要な go.mod
と go.sum
を作成するため
下記コマンドを事前に実行しておきます。
# モジュール管理ファイルを用意
% go mod init {モジュール名}
# ビルド && 依存するモジュールをダウンロード
% go build
6.サーバーの立ち上げ
実際に下記コマンドでサーバーを立ち上げましょう。
% make up
...
Creating dockers_app_1 ... done
http://localhost:8080 にアクセスして、下記の様に返り値が取れれば完了です。
% curl 'http://localhost:8080'
{"message":"hello world"}
Herokuにデプロイする
いよいよ作成したソースをHerokuにデプロイします。
1.Herokuアプリの準備
今回は go-heroku
という名前でアプリを作成します。
別の名前で作成したい場合は、go-heroku
となっている箇所を
作成したいアプリ名に変えてもらえればOKです。
# アプリのルートディレクトリへ移動
% cd /hoge
# herokuログイン
% heroku container:login
# herokuアプリの作成
% heroku create -a go-heroku
# herokuリポジトリをgit登録
% heroku git:remote -a go-heroku
2.必要なファイルを準備
2−1.heroku.yml
Herokuに自分でビルドしたDockerイメージをデプロイするために
アプリのルートディレクトリに下記のような heroku.yml
を作成します。
build:
docker:
web: Dockerfile
run:
web: /main
詳細な設定については、こちらのドキュメントに記載があります。
2−2.Dockerfile
上述の heroku.yml
で実行するため、
ローカル環境用に作成した Dockerfile
を下記のように修正します。
FROM golang:1.15.6-alpine3.12 as builder
RUN apk update \
- && apk add --no-cache git \
+ && apk add --no-cache git curl \
&& go get -u github.com/cosmtrek/air \
&& chmod +x ${GOPATH}/bin/air
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
+
+ RUN GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -ldflags="-w -s" -o /main ./cmd
+
+ FROM alpine:3.12
+
+ COPY --from=builder /main .
+
+ ENV PORT=${PORT}
+ ENTRYPOINT ["/main web"]
各種ライブラリのインストール
RUN apk update \
&& apk add --no-cache git curl \
&& go get -u github.com/cosmtrek/air \
&& chmod +x /go/bin/air
HerokuのUI上でログを残すために、インストールするライブラリに curl
を追加します。
入れないとリリースが途中で死んでも何故落ちたかが追えなくなるので、入れたほうがいいです。
公式でも以下のようにアナウンスしています。
If you would like to see streaming logs as release phase executes, your Docker image is required to have curl
.
出典:Building Docker Images with heroku.yml | Heroku Dev Center
ビルド
RUN GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -ldflags="-w -s" -o /main ./cmd
クロスコンパイル用の環境変数を指定し、-ldflags="-w -s" でバイナリを削減しています。
アプリの実行
FROM alpine:3.12
COPY --from=builder /main .
前のステージでビルドしたアプリケーションを次のステージにコピーしています。
--from=[NAME]
をコピーするファイルの前に宣言することでコピー元のステージが指定できます。
ENV PORT=${PORT}
先程もHerokuではWeb worker processにポートが紐付けられるので、
ENTRYPOINT ["/main web"]
最後に ENTRYPOINT でアプリケーションを実行しています。
3.リリースする
herokuリポジトリをgit登録済みなので、下記コマンドのみでリリースできます。
※サンプルではデフォルトブランチを main
にしていますが、必要あれば適宜変更してもらってもリリースできます。
% git add .
% git commit -m "make it better"
% git push heroku main
上記コマンドで build
から deploy
までやってくれるので、
あとは完了するのを待つだけです。
完了したら下記コマンドを叩くことで画面で反映確認ができます。
% heroku open
終わりに
今回のサンプルは下記で公開していますので、ご参考までにどうぞ
https://github.com/genki-sano/go-heroku