追記(2016/12/22)
任意のパッケージがインストールされた環境をHerokuにつくりたい、という目的でDockerを選択したけれど、Dockerfileに書かれているinstallコマンドは有効に動かない、みたいな情報を得ました。で、どうやらさいきんはbuildpackをうまいことして、任意のパッケージがインストールされた環境で動くアプリケーションをつくることができます、というのがこちらです↓
以下、当エントリ原文
背景
apt-get
など、パッケージに依存する環境で動かすアプリのデモサイトを立てたい- herokuでDockerが動くらしい
- Docker動くなら依存も解決したimageつくればいいだけっしょ
お断り: パッケージのインストールはできない
apt-get 意味ない.
- はい終了〜解散〜
- というのもアレなので、Dockerで動くGoのアプリケーションをHerokuにデプロイするまでのことを書きます
tl;dr
- heroku plugins:install heroku-docker
- アプリケーションを書く
- heroku create
- heroku docker:init
- 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つある。
- Procfile さっき書いたやつ
- app.json これもさっき書いたやつ
- Dockerfile おなじみのDockerファイル。app.jsonのimageをベースに書かれていることがわかる
- 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/go
もheroku/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
- Heroku + node.js error (Web process failed to bind to $PORT within 60 seconds of launch) - Stack Overflow
Heroku dynamically assigns your app a port, so you can't set the port to a fixed number.
- docker-composeにいやっていうほど
8080
って書いてあるし、environments PORT 8080
ってあるし、コンテナ単位かあるいはVM単位で8080をexposeしててその前の段階でdynamicなIPわりふりは済んでいるものと思ってた。 - まあとりあえず$PORTを取れば解決
解決
main.go
- http.ListenAndServe(":8080", r) + http.ListenAndServe(":"+os.Getenv("PORT"), r) }
雑感
好きなイメージを立てれるわけじゃないのだったら、repositoryにdocker-compose.ymlだけ置いといて、「あとお前ら勝手にupしてくれ」のほうがサンプルページとしての価値高いんじゃないかと思った。herokuでdocker動きました、やったー、以上の成果が無い気がする。
DRYな備忘録