2018-12 / 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31

2018-12-21 (金)

Heroku の Slug は友達 [Heroku]

Heroku Logo

この記事は heroku Advent Calendar 2018 の 21 日目の記事です。

20 日目は @pukka さんの『【Heroku検討者向け】デプロイ方法5選!』でした。(4)マニフェストでの heroku.yml の使い方は初めて知りました。デプロイしたところ、heroku18 Stack の Docker Image が使われているようでした。Slug を作る従来の非 Docker デプロイも Docker に寄せられていくのかな?

今日はそんな Slug に注目した記事をお届けします。


Slug とは


みなさん、Heroku の Slug はご存知でしょうか?

ダッシュボードの Settings タブからサイズを確認できます。

Heroku Slug Dyno

でもこれしか情報がないのですよね。恥ずかしがり屋さんかな?

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 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 の例もあります。

1. ruby buildpack をダウンロードして展開する
2. server.rb を追加して、tar で固め直す。実はこの tar ball がもう Slug
3. Heroku App を作る
4. Slug Create API を使って、Heroku App に Slug アップロード用の箱を作る
5. 4 で PUT 用の URL が返るので、2 の tar ball をアップロードする
6. 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

1. GET https://api.heroku.com/apps/{app-name}/build-metadata して、キャッシュ GET 用 URL を取得する
2. ダウンロードすると tar ball が得られる
3. heruku run して、ダウンロードした tar ball の中から vendor/heroku 以外をすべて削除する
4. tar ball に固めて、1 で得られた PUT 用 URL にアップロードする
5. 次回のデプロイでは新規に 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)
}



そりゃそうかあー。ダッシュボード上だとこのように見えます。

Heroku Slug Container

まとめ


Slug をダウンロードしたり、自分で作ったり、サイズを減らしたりすることで、グッと身近に感じられました。もう友達ですね!

明日の heroku Advent Calendar 2018@sho7650 さんが Private Space について書くようです。Private Space は全く使ったことがないので、楽しみです。

この記事に言及しているこのブログ内の記事

2018-12 / 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31

最終更新時間: 2019-10-18 15:51 JST

検索


最近の話題
- 2019-10-10
  terraform-provider-healthchecksio を Terraform Plugin SDK に移行した
- 2019-09-20
  本番環境で heroku run する時の tips
- 2019-08-16
  『改訂2版 みんなのGo言語』を読んだ
- 2019-08-13
  『カード決済業務のすべて』を読んだ
  『実行力 結果を出す「仕組み」の作りかた』を読んだ
- 2019-08-05
  Heroku Logging Add-on Papertrail と LogDNA の比較
- 2019-07-16
  ジム通いと食生活改善が 13 ヶ月目に突入した
- 2019-06-10
  esa の記事を Emacs から素早く開ける helm-esa.el を作った
最近追記された記事
- 2019-02-11-1 (25日前)
- 2019-02-03-1 (25日前)
- 2019-01-28-1 (25日前)
- 2019-02-03-1 (67日前)
- 2018-11-28-1 (81日前)
- 2019-05-07-1 (161日前)
- 2018-04-30-1 (176日前)
- 2018-01-28-1 (188日前)
- 2019-02-11-1 (248日前)
- 2019-01-20-1 (266日前)
カテゴリ
- Anthy (3)
- Apache (11)
- Apple (1)
- ATOK (4)
- au (3)
- AWS (23)
- Bazaar (1)
- Berkshelf (2)
- BigQuery (1)
- BitBar (4)
- Book (106)
- Boxen (2)
- Bugsnag (1)
- capistrano (4)
- chalow (57)
- ChatWork (1)
- Chef (17)
- Chrome (3)
- Chromecast (1)
- CircleCI (10)
- clang (26)
- Comics (2)
- Cooking (10)
- cvs (15)
- cygwin (12)
- D3.js (1)
- Debian (55)
- Docker (4)
- E-mail (9)
- elasticsearch (4)
- Emacs (223)
- Emacs講座 (10)
- English (4)
- feedforce (7)
- fetchmail (3)
- Firefox (20)
- Fluentd (4)
- ftp (2)
- Game (21)
- GCP (1)
- Gem (5)
- Git (9)
- GitHub (23)
- golang (11)
- Google (1)
- gpg (4)
- GrowthForecast (7)
- Health (6)
- Heroku (20)
- Homebrew (10)
- HTML (6)
- iBook (1)
- iPad (1)
- iPhone (17)
- IRC (1)
- Jenkins (8)
- JS (1)
- Karabiner (1)
- KeySnail (3)
- Kibana (1)
- Kindle (1)
- Kubernetes (2)
- Langrich (7)
- LDAP (6)
- Life (23)
- Linux (7)
- Mackerel (1)
- macOS (1)
- Mew (18)
- MongoDB (1)
- Mozilla (19)
- Music (1)
- MySQL (1)
- NAS (4)
- nginx (6)
- NHK (1)
- Node (1)
- ntp (4)
- OOP (2)
- OpenID (2)
- openssl (1)
- Opera (2)
- OSX (41)
- Perl (14)
- PHP (19)
- PostgreSQL (1)
- procmail (4)
- Programing (3)
- Puppet (1)
- Python (2)
- Rails (13)
- Rake (2)
- RaspberryPi (2)
- Redash (1)
- RedHat (29)
- Redmine (3)
- RSpec (2)
- Ruby (52)
- samba (3)
- screen (7)
- sed (5)
- serverspec (6)
- sh (8)
- Slack (2)
- Solaris9 (22)
- Spring (2)
- ssh (4)
- StatusNet (21)
- svn (12)
- Swift (1)
- Tablet (1)
- tdiary (3)
- Terraform (2)
- Twitter (15)
- Twmode (6)
- Ubuntu (5)
- UNIX (102)
- vagrant (8)
- Video (21)
- vim (1)
- Wercker (9)
- Windows (29)
- Wine (3)
- XML (11)
- XP (1)
- zsh (26)
- インストールメモ (33)
- クイックシェイプ (12)
- ネタ (15)
- 勉強会 (17)
- 携帯 (6)
- 正規表現 (4)
過去ログ
2019 : 01 02 03 04 05 06 07 08 09 10 11 12
2018 : 01 02 03 04 05 06 07 08 09 10 11 12
2017 : 01 02 03 04 05 06 07 08 09 10 11 12
2016 : 01 02 03 04 05 06 07 08 09 10 11 12
2015 : 01 02 03 04 05 06 07 08 09 10 11 12
2014 : 01 02 03 04 05 06 07 08 09 10 11 12
2013 : 01 02 03 04 05 06 07 08 09 10 11 12
2012 : 01 02 03 04 05 06 07 08 09 10 11 12
2011 : 01 02 03 04 05 06 07 08 09 10 11 12
2010 : 01 02 03 04 05 06 07 08 09 10 11 12
2009 : 01 02 03 04 05 06 07 08 09 10 11 12
2008 : 01 02 03 04 05 06 07 08 09 10 11 12
2007 : 01 02 03 04 05 06 07 08 09 10 11 12
2006 : 01 02 03 04 05 06 07 08 09 10 11 12
2005 : 01 02 03 04 05 06 07 08 09 10 11 12
2004 : 01 02 03 04 05 06 07 08 09 10 11 12
2003 : 01 02 03 04 05 06 07 08 09 10 11 12
2002 : 01 02 03 04 05 06 07 08 09 10 11 12
2001 : 01 02 03 04 05 06 07 08 09 10 11 12