DRYな備忘録

Don't Repeat Yourself.

HerokuにDockerで動くアプリケーション(Go)をデプロイする

追記(2016/12/22)

任意のパッケージがインストールされた環境をHerokuにつくりたい、という目的でDockerを選択したけれど、Dockerfileに書かれているinstallコマンドは有効に動かない、みたいな情報を得ました。で、どうやらさいきんはbuildpackをうまいことして、任意のパッケージがインストールされた環境で動くアプリケーションをつくることができます、というのがこちらです↓

otiai10.hatenablog.com

以下、当エントリ原文

背景

  • apt-getなど、パッケージに依存する環境で動かすアプリのデモサイトを立てたい
  • herokuでDockerが動くらしい
  • Docker動くなら依存も解決したimageつくればいいだけっしょ

お断り: パッケージのインストールはできない

tl;dr

  1. heroku plugins:install heroku-docker
  2. アプリケーションを書く
  3. heroku create
  4. heroku docker:init
  5. heroku docker:release

参考

Herokuの'docker:release'の動き | SOTA

herokuでdockerコンテナを動かす(2015年10月版) - Qiita

0. herokuのinstall

Heroku CLI | Heroku Dev Center ここからインストーラがダウンロードできるはず

1. heroku plugins:install heroku-docker

% heroku plugins:install heroku-docker
% heroku help docker

2. アプリケーションを書く

適当に、main.mainでサーバが立つようなアプリケーションパッケージを書いた

https://github.com/otiai10/marmoset/tree/master/examples/pygmy

3. heroku create

ご存知heroku create。これは、herokuというサービス上にネームスペースを確保するぐらいの意味合いしかない。

% cd %GOPATH/src/github.com/otiai10/marmoset
% heroku create pygmy-marmoset
https://pygmy-marmoset.herokuapp.com/

4. heroku docker:init

これが今回の最重要ポイント。とりあえず何も考えずに、

% heroku docker:init
 !    Error: Procfile required; aborting

おこられた。 Procfile というのが要る。これはheroku上で動くプロセスのエントリーポイントを定義するファイル。今回は、当該アプリケーションがbinでインストールされるはずということで、以下の内容でProcfileを作成。

web: pygmy

これでバイナリを叩いてもらう。webというキーで、pygmyという実行ファイルを指定している。

ふたたび

% heroku docker:init
 !    Error: docker image required: provide an --image flag or 'image' key in app.json

おこられた。app.jsonというのに、このアプリケーションの依存するdocker imageなどを指定する。今回のアプリケーションはデータベースなど使わないし、herokuが提供するgoのイメージだけでよいので、以下の内容でapp.jsonを作成。

{
    "image": "heroku/go"
}

ふたたび

% heroku docker:init
%

成功した。これによってこのプロジェクトに追加されたファイルは、じつは4つある。

  1. Procfile さっき書いたやつ
  2. app.json これもさっき書いたやつ
  3. Dockerfile おなじみのDockerファイル。app.jsonのimageをベースに書かれていることがわかる
  4. docker-compose.yml herokuのdockerイメージは、これをベースにupコマンドを打つと思われる

なので、ローカルで挙動を確認するために、いますぐ以下のコマンドを実行してください。

% docker-compose up web

これが成功したら、基本的にはheroku上でもこのアプリケーションは動くと思ってよい。

5. heroku docker:release

いよいよリリースする。ローカルのファイルをまんまリリースするので、git repositoryにpushしておく必要はない。

% heroku docker:release
Remote addons:  (0)
Local addons:  (0)
Missing addons:  (0)
Creating local slug...
Building web
Step 1 : FROM heroku/go
# Executing 2 build triggers...
Step 1 : COPY . /app/.temp
Step 1 : RUN /app/.cache/gotools/bin/compile
 ---> Running in 79ea8d976df3
godep go install -tags heroku ./...
 ---> f214d7b10780
Removing intermediate container a4907e576b66
Removing intermediate container 79ea8d976df3
Successfully built f214d7b10780
extracting slug from container...
creating remote slug...
language-pack: heroku-docker (heroku/go)
remote process types: { web: 'cd /app/user && pygmy' }
uploading slug [====================] 100% of 2 MB, 0.0s
releasing slug...
Successfully released pygmy-marmoset!
%

いけたっぽいので、

% heroku open

で確認。marmoset sample page、いけた。

つまずいたポイント

slug.tgz no such file

  • heroku/goイメージを使わずに、サーバのエントリポイントまで込み込みのDocker Imageをつくって、それを立てるつもりだった
  • herokuにdocker:releaseすると、docker-composeにしたがってイメージからslugファイルを取り出そうとしている?
  • 自前のイメージだとそれが無いので、docker-compose upの段階で以上のようにコケる

解決

app.json

{
-     "image": "otiai10/marmoset"
+    "image": "heroku/go"
}

Dockerfile

- FROM otiai10/marmoset
+ FROM heroku/go

/app/.temp/Godeps/Godeps.json: No such file or directory

  • /app/.cache/gotools/bin/compileの中で、godep go installしてるのまじ余計なんだけどで、Godeps/Godeps.jsonを探している。ふつうにgo installでもええんやで...

解決

% godep save

Godeps/Godeps.json

{
    "ImportPath": "github.com/otiai10/marmoset",
    "GoVersion": "go1.5.3",
    "Deps": []
}

Update Godeps/Godeps.json or choose a different version tag

  • ほら、godep使うからそういうことになんねん
  • タグ無しのheroku/goイメージは1.5.2なのに対して、自分のローカルでgodep saveを実行した環境のGoは1.5.3だったのでこうなる

解決

Godeps/Godeps.json

-    "GoVersion": "go1.5.3",
+    "GoVersion": "go1.5.2",

image指定のheroku/goheroku/go:1.5.2にしといたほうがよさそう

stat ./examples/pygmy/main.go: no such file or directory

  • Procfileで指定するエントリポイントを、go run ./examples/pygmy/main.goとしていた
  • heroku/goのDockerfileを見る限り、カレントdirを/app/.tempにCOPYとかしてる
  • なんかworkdirがどこなのかよくわからんし、main.goを叩くのはよくなさそう
  • また、/app/.cache/gotools/bin/compile 内では、 godep go install -tags heroku ./...が走っているので、プロジェクト内のmain.mainを置いて、installによってつくられたbinを叩いてもらったほうがよさそう

解決

Procfile

- web: go run ./examples/pygmy/main.go
+ web: pygmy

docker-compose.yml

web:
  build: .
-  command: 'bash -c ''go run ./examples/pygmy/main.go'''
+  command: 'bash -c ''pygmy'''
  working_dir: /app/user

Error R10 (Boot timeout) -> Web process failed to bind to $PORT within 60 seconds of launch

解決

main.go

-      http.ListenAndServe(":8080", r)
+      http.ListenAndServe(":"+os.Getenv("PORT"), r)
}

雑感

好きなイメージを立てれるわけじゃないのだったら、repositoryにdocker-compose.ymlだけ置いといて、「あとお前ら勝手にupしてくれ」のほうがサンプルページとしての価値高いんじゃないかと思った。herokuでdocker動きました、やったー、以上の成果が無い気がする。

DRYな備忘録