はじめに
言語: golang
コンテナ: docker-compose
RDB: mysql
ORM: gorm
マイグレーション: migrate
な環境をherokuにデプロイするまで結構ハマったので残す。
コード
実行したherokuのコマンド一覧
$ cd /hoge/huga # アプリケーションのコードがあるところに移動
$ heroku container:login # ログイン
$ heroku create -a app_name # herokuアプリの作成
$ heroku git:remote -a app_name # herokuリポジトリをgit登録
$ heroku addons:add cleardb:ignite # mysqlのアドオンを追加
$ heroku config # CLEARDB_DATABASE_URLが登録されていることを確認
$ heroku config:set DATABASE_URL="<ユーザー名>:<password>@tcp(<ホスト名>:3306)/<DB名>?parseTime=true" # CLEARDB_DATABASE_URLの値を元にsql.Open()に渡す用の文字列に整形
$ heroku config # DATABASE_URLが登録されていることを確認
$ heroku stack:set container # heroku.ymlを使う時はこれがいるぽい
$ git push heroku master # リリース
heroku.yml
heroku.ymlを使うとCI/CDみたいなことができる。アプリのルートディレクトリに置いて使う。
buildにはdockerのビルドの指定ができる。
releaseにはリリースする際に挟みたい処理があれば書くことができる。ここではマイグレーションの実行をしている。
runはプロセスタイプ1ごとに実行するコマンドを指定する。
build:
docker:
web: Dockerfile
worker:
dockerfile: Dockerfile
target: builder
release:
image: worker
command:
- make up_migrate_prod
run:
web: /main
Dockerfile
上述のheroku.ymlが参照するDockerfile。アプリのルートディレクトリに置く。
いくつかポイントがある。
FROM golang:alpine as builder
RUN apk update \
&& apk add --no-cache git curl make gcc g++ \
&& go get github.com/oxequa/realize
WORKDIR /app
COPY go.mod .
COPY go.sum .
RUN go mod download
COPY . .
RUN GOOS=linux GOARCH=amd64 go build -o /main
FROM alpine:3.9
COPY --from=builder /main .
ENV PORT=${PORT}
ENTRYPOINT ["/main"]
ライブラリのインストール
RUN apk update \
&& apk add --no-cache git curl make gcc g++ \
&& go get github.com/oxequa/realize
realizeは開発時のホットリロードのため。
make、gcc、g++はheroku.ymlのreleaseフェーズにてmakeコマンドでマイグレーションを流せるようにするため
curlはherokuのUI上でログを残すため(※)。
※ curlを入れていないとこんな感じで何も表示されない。リリースが途中で死んでもなんで落ちたかが追えなくなるので入れておいた方がいいと思う。ログを出すためにcurlが必要なことは公式にも記載されている。
ビルド
builderのイメージはheroku.ymlでイメージのビルドをする際に使ったり、マイグレーションを実行する時のイメージとして利用している。
FROM golang:alpine as builder
build:
docker:
web: Dockerfile
worker:
dockerfile: Dockerfile
target: builder # builderのイメージをbuildする際に使う
release:
image: worker # 上記のworkerのイメージをreleaseフェーズでも使う
command:
- make up_migrate_prod # マイグレーションを流す
アプリの実行
RUN GOOS=linux GOARCH=amd64 go build -o /main
でビルドしたファイルを実行する
FROM alpine:3.9
COPY --from=builder /main .
ENV PORT=${PORT}
ENTRYPOINT ["/main"]
run:
web: /main
docker-compose.yml
ローカルで開発する時のみに利用するdocker-compose.yml。
realizeを使ってホットリロードするようにしている。
また、mysqlのコンテナが立ち上がる際に docker-entrypoint-initdb.d
を利用して CREATE DATABASE
をするようにしている。
herokuの本番環境ではdatabaseは heroku addons:add cleardb:ignite
で用意されたものを利用する。
そのため、本番環境ではこのdocker-compose.ymlは利用しない。
version: "3.5"
services:
mysql:
container_name: push_study_db
image: mysql:5.7.22
volumes:
- ./mysql/:/docker-entrypoint-initdb.d/
- ./mysql/my.cnf:/etc/mysql/conf.d/my.cnf
environment:
- MYSQL_ALLOW_EMPTY_PASSWORD=yes
ports:
- 4306:3306
app:
build:
context: ..
target: builder
volumes:
- ../:/app
command: realize start --server
environment:
- API_VERSION=development
ports:
- 7777:7777
depends_on:
- mysql
起動するポート
herokuはアプリが起動するたびにポートが変わるらしい。$PORTを指定して起動するようにする。
router.Run(":" + os.Getenv("PORT"))
CLEARDB_DATABASE_URL
mysqlのアドオンを追加するとCLEARDB_DATABASE_URLという環境変数が自動で設定される。
heroku.ymlのreleaseフェーズで流れるようにしたマイグレーションだが、そのコードでは以下のようにしてdbと接続していた。
dbURL := os.Getenv("CLEARDB_DATABASE_URL")
db, _ := sql.Open("mysql", dbURL)
リリースを実行すると invalid memory address or nil pointer dereference
のエラーが出る。
結果として、herokuが自動で作成してくれるCLEARDB_DATABASE_URLの書式をsql.Openが求めている書式に変換する必要があった。こちらの記事を参考にさせていただきました。
冒頭のherokuのコマンド一覧のところでも記載しているが、"<ユーザー名>:<password>@tcp(<ホスト名>:3306)/<DB名>?parseTime=true"
の形にしてあげる必要があった。
最後に
herokuのリリース方法、色々ありすぎてまとまった情報を見つけるのが難しい。
-
ここで使ってるのはwebのプロセスタイプ。herokuが動くコンテナであるdynoのプロセスタイプは他にworker、one-offの計3つのプロセスタイプがあるぽい ↩