41
42

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Go 5Advent Calendar 2020

Day 23

Goで作ったアプリケーションをHerokuで動かす

Last updated at Posted at 2020-12-22

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 を使用しています。

./cmd/main.go
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 を作成します。

./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 を用意します。
アプリのルートディレクトリに下記をコピーして作成してください。

./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.modgo.sum をコピーして、go mod download でライブラリをインストールしています。

こうすることで、次回以降、ソースコードの変更のみ(go.modgo.sum に変更がない場合)であれば
ソースコード全体をコピーする COPY . . の行から実行されるようになります。
それより前のステップはキャッシュが活用されスキップされ、ビルド速度が向上します。

3.Makefileの作成

docker-compose.yml 起動のためのコマンドが長くなるので、Makefileを設定して立ち上げやすくします。

./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 を使用します。
公式の サンプル を基に、設定ファイルを下記のように作成します。

./.air.toml
# 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.modgo.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 を作成します。

./heroku.yml
build:
  docker:
    web: Dockerfile
run:
  web: /main

詳細な設定については、こちらのドキュメントに記載があります。

2−2.Dockerfile

上述の heroku.yml で実行するため、
ローカル環境用に作成した Dockerfile を下記のように修正します。

./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

参考記事

41
42
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
41
42

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?