この記事は heroku Advent Calendar 2018 の 21 日目の記事です。
20 日目は @pukka さんの『【Heroku検討者向け】デプロイ方法5選! 』でした。(4)マニフェストでの heroku.yml の使い方は初めて知りました。デプロイした ところ、heroku18 Stack の Docker Image が使われているようでした。Slug を作る従来の非 Docker デプロイも Docker に寄せられていくのかな?
今日はそんな Slug に注目した記事をお届けします。
Slug とは
みなさん、Heroku の Slug はご存知でしょうか?
ダッシュボードの Settings タブからサイズを確認できます。
でもこれしか情報がないのですよね。恥ずかしがり屋さんかな?
Slug Compiler のドキュメントによると、
Slugs are compressed and pre-packaged copies of your application optimized for distribution to the dyno manager . When you git push to Heroku, your code is received by the slug compiler which transforms your repository into a slug. Scaling an application then downloads and expands the slug to a dyno for execution.
だそうです。git push すると Slug Compiler によって作られるみたいです。
自分なりの解釈を図にしてみました。
Heroku では Dyno という軽量コンテナ上で、アプリケーションが動作します。アプリケーションは ↑ のように Slug というパッケージに固められます。Slug にはアプリケーションコードの他に Buildpack や、Ruby なら bundle install されたライブラリ等が含められています。Dyno が起動する時は Slug がダウンロードされ、展開して使われます。
という理解です。
Slug が何者かは分かりました。では実際にダウンロードして確認してみましょう。
Slug をダウンロードしてみよう
Slug Info API を使うとダウンロードできます。APP_NAME はご自分の Heroku App に置き換えてください。
$ APP_NAME=github-organization-watcher
$ LATEST_SLUG_ID=$(heroku releases --json -a $APP_NAME | jq -r '.[0].slug.id')
$ SLUG_URL=$(curl -n https://api.heroku.com/apps/${APP_NAME}/slugs/${LATEST_SLUG_ID} \
-H "Accept: application/vnd.heroku+json; version=3" | jq -r '.blob.url')
$ curl -Lo ${LATEST_SLUG_ID}.tar.gz $SLUG_URL
$ tar xzf ${LATEST_SLUG_ID}.tar.gz
heroku run と同じ光景が広がっていますね。
$ tree -L 2 .
.
├── 50d9bdde-3ea4-42c0-8b05-d063164c0081.tar.gz
└── app
├── Dockerfile
├── Gemfile
├── Gemfile.lock
├── Procfile
├── README.md
├── Rakefile
├── app
├── app.json
├── bin
├── config
├── config.ru
├── db
├── docker-compose.yml
├── lib
├── log
├── public
├── spec
├── tmp
└── vendor
11 directories, 10 files
Heroku slugs プラグイン を使うともっと簡単です。
$ heroku plugins:install heroku-slugs
$ APP_NAME=github-organization-watcher
$ heroku slugs:download -a $APP_NAME
Slug を手動で作ってリリースしてみよう
前述のとおり、Slug は Slug Compiler が作ってくれます。ですが、Heroku Dev Center > Creating Slugs from Scratch に従えば、自分で作ることが出来ます。
Ruby の場合だとこんな感じです。Node.js と Go の例もあります。
- ruby buildpack をダウンロードして展開する
- server.rb を追加して、tar で固め直す。実はこの tar ball がもう Slug
- Heroku App を作る
- Slug Create API を使って、Heroku App に Slug アップロード用の箱を作る
- 4 で PUT 用の URL が返るので、2 の tar ball をアップロードする
- Release Create API を使って、手動でリリースする
4 で process_types を指定することにより、Procfile を使わないのは面白いと思いました。
あと、curl の -n オプションを初めて知りました。
-n, –netrc Must read .netrc for user name and password
Heroku CLI と同様、認証のために ~/.netrc を参照します。Heroku CLI と curl は相性が良かったのですね・・・!
Slug のサイズを減らしてみよう
現在の上限は 500MB なのでほとんどのアプリは下回るはずですが、300MB を超えると Heroku は警告を出すそうです。Dyno の起動時間が遅くなる可能性があるので、サイズを減らすと良いと思います。
1 つは単純に Slug に含めるファイルを減らすことです。git リポジトリから不要なファイルを削除したり、Slug に含めたくないファイルを .slugignore に書くことでサイズを減らすことが出来ます。
もう 1 つは heroku-repo プラグイン を使う方法です。前述の図にあるキャッシュのサイズが減るため、Slug サイズも減ります。
$ heroku plugins:install heroku-repo
$ heroku repo:purge_cache -a $APP_NAME
$ git commit --allow-empty -m 'Decrease slug size'
$ git push heroku master
こんな感じにキャッシュサイズを減らしています。
https://github.com/heroku/heroku-repo/blob/v1.0.13/commands/purge_cache.js
GET https://api.heroku.com/apps/{app-name}/build-metadata
して、キャッシュ GET 用 URL を取得する- ダウンロードすると tar ball が得られる
- heruku run して、ダウンロードした tar ball の中から vendor/heroku 以外をすべて削除する
- tar ball に固めて、1 で得られた PUT 用 URL にアップロードする
- 次回のデプロイでは新規に bundle install 等行われる
ちなみに 1 の API は Platform API Reference に載っていません。
呼び出し箇所はここです。
https://github.com/heroku/heroku-repo/blob/v1.0.13/lib/repo.js#L6-L9
こんなレスポンスです。Heroku は AWS 上に構築されていることがよく分かります。
$ curl -n https://api.heroku.com/apps/{app-name}/build-metadata \
-H "Accept: application/vnd.heroku+json; version=3.build-metadata"
{
"app":{
"id":"<UUID>",
"name":"{app-name}"
},
"cache_delete_url":"<CACHE_URL>",
"cache_get_url":"<CACHE_URL>",
"cache_put_url":"<CACHE_URL>",
"repo_delete_url":"<REPO_URL>",
"repo_get_url":"<REPO_URL>",
"repo_put_url":"<REPO_URL>"
}
# <CACHE_URL>
# https://s3-external-1.amazonaws.com/heroku_repos/heroku.com/cache/<NUMBERS>.tgz?AWSAccessKeyId=<SECRET>&Signature=<SECRET>&Expires=<NUMBERS>
# <REPO_URL>
# https://s3-external-1.amazonaws.com/heroku_repos/heroku.com/<NUMBERS>.tgz?AWSAccessKeyId=<SECRET>&Signature=<SECRET>&Expires=<NUMBERS>
何がキャッシュされているか
Ruby だったらこんな感じです。
$ tree -L 5 .
.
├── heroku-18
│ └── vendor
│ └── bundle
│ ├── bin
│ │ ├── bundler
│ │ ├── erubis
│ │ ├── nokogiri
│ │ ├── puma
│ │ ├── pumactl
│ │ ├── rackup
│ │ ├── rails
│ │ ├── rake
│ │ ├── sass
│ │ ├── sass-convert
│ │ ├── scss
│ │ ├── slimrb
│ │ ├── sprockets
│ │ ├── thor
│ │ └── tilt
│ └── ruby
│ └── 2.4.0
├── public
│ └── assets
│ ├── application-2669f77215d52f68e22d0c5cf91d20a0833601d2eb3185776f03a6f59d7c3d2e.js
│ ├── application-2669f77215d52f68e22d0c5cf91d20a0833601d2eb3185776f03a6f59d7c3d2e.js.gz
│ ├── application-2bee43f63707bcd2903a160f8816cb729632b6d80a17add90f928f975b446312.css
│ └── application-2bee43f63707bcd2903a160f8816cb729632b6d80a17add90f928f975b446312.css.gz
├── tmp
│ └── cache
│ └── assets
│ └── sprockets
│ └── v3.0
└── vendor
└── heroku
├── buildpack_ruby_version
├── buildpack_version
├── bundler_version
├── ruby_version
├── rubygems_version
├── secret_key_base
└── stack
↑ は Rails アプリです。rails assets:precompile は毎回フルビルドしますが、Heroku は tmp/cache/assets をキャッシュすることで、ビルドを高速化しています。
Heroku Dev Center > Rails 4+ Asset Pipeline on Heroku > Caching
tmp/cache/assets は 50MB までキャッシュされます。50MB というのはストレージのブロックサイズを考慮しない、実際のサイズらしいです。GNU du だと –apparent-size オプションで分かります。
キャッシュサイズ削減で登場した vendor/heroku 以下のファイルはこんな感じです。メタデータですね。
ファイル名 | ファイルの中身 |
---|---|
buildpack_ruby_version | ruby-2.4.5 |
buildpack_version | v196 |
bundler_version | 1.15.2 |
ruby_version | ruby 2.4.5p335 (2018-10-18 revision 65137) [x86_64-linux] |
rubygems_version | 2.6.14.3 |
secret_key_base | [SECRET] |
stack | heroku-18 |
リポジトリサイズも減らしてみよう
heroku-repo プラグインを使うと、前述の図にあるリポジトリサイズも減らすことが出来ます。
$ heroku repo:gc -a $APP_NAME
ただし、Slug には .git ディレクトリは含まれないため、Slug サイズは減りません。Slug のコンパイル時間が減るでしょう。
Docker デプロイ使っていると、Slug はどうなる?
[2018-11-21-1] でも紹介した Docker デプロイでは、Slug はどうなるでしょう?
$ heroku releases --json -a aqueous-everglades-51851 | jq -r '.[0]'
{
(snip)
"slug": null,
(snip)
}
そりゃそうかあー。ダッシュボード上だとこのように見えます。
まとめ
Slug をダウンロードしたり、自分で作ったり、サイズを減らしたりすることで、グッと身近に感じられました。もう友達ですね!
明日の heroku Advent Calendar 2018 は @sho7650 さんが Private Space について書くようです。Private Space は全く使ったことがないので、楽しみです。