2013/09/05
株式会社 FLECT
小西俊司
 Herokuで開発をする際に知っておいた方が良いこと
はだいたい書いたつもり
◦ 初版は2012年12月
◦ Herokuは機能アップデートが頻繁にあるし、自分の理解が
進んだ部分もあるので全面改稿
◦ 古い情報を残してもしょうがないのでスライドは置き換え
 基本Java(Play)での開発経験をベースに書いている
が他の言語を使用する場合でもほとんど変わらない
 Heroku内部のアーキテクチャについては半分想像
だが中の人と話してもそんなに外してない
 各種言語で構築したWebアプリケーションを実行するプラット
フォーム
 標準サポート言語
◦ Ruby
◦ Node.js
◦ Clojure
◦ Python
◦ JVM(Java, Gradle, Grails, Scala, Play)
 標準サポートではない言語もカスタムbuildpackを使用するこ
とで利用可能
◦ PHPは標準サポートではないがFacebook連携のデフォルトとなって
いるため山ほど使われている
 プラットフォーム全体がEC2上にある
◦ 現在はUS-EASTとEUリージョンがある
◦ Tokyoにはいつ来るんだー
 Git
◦ ソースコードのやり取りはすべてgit経由
 Dyno
◦ Herokuがユーザーアプリケーションを実行する環境のこと
◦ 役割によってWebDyno, WorkerDynoのように呼ばれることもある
◦ 課金の単位でもある
 Slug
◦ Dynoに転送されるアプリケーションの塊(最大300MB)
◦ これが大きければ大きいほどDynoの起動に時間がかかる
 Stack
◦ DynoのベースとなるOS環境
◦ 過去にはStackの違いを意識する場面もあったが、Cedar(Ubuntu)
がデフォルトになって以降、それ以外を使用することはまずない
 Buildpack
◦ RubyやJavaなどの各種言語毎の実行環境を構築するモジュール
実際のところHeroku固有の用語や独自の技術はほとんどない。
トリッキーなことはせずに標準技術のみで実装されている。
 http://www.heroku.com/
◦ まずはアカウント作成
◦ 必要な情報はメールアドレスだけ
 https://toolbelt.heroku.com/
◦ herokuクライアントのインストール
◦ 現行のインストーラではgitも一緒にインストールしてくれるらしい。
◦ Gettinng Started(https://devcenter.heroku.com/articles/quickstart)
を見るとSSHの鍵の生成とherokuへのアップロードまで半自動で
やってくれるっぽい
 Playアプリケーションを新規作成してHeroku上で動
作確認するまでの全コマンド
$ play new test
$ cd test
$ git init
$ git add .
$ git commit -m "Initial put"
$ heroku create
$ git push heroku master
$ heroku open
たったこれだけでまたひとつインターネットにゴミが増える。。。(--
(実際のところHeroku上で動いているアプリの半分はこんなんだと思う)
あとはコードを書いてはgit pushの繰り返し
• Gitでソースコードを管理する上では「.gitignore」
は常に作成した方が良いがここでは省略
• PlayのbuildpackではProcfileが存在しない場合
は自動生成されるのでなくても良い
 Buildpackはpushされたファイルの種別から選択される
◦ pom.xmlがあればJavaプロジェクトなど
◦ またはアプリ毎に明示的にGitHubにあるカスタムbuildpackを指定することもで
きる
 SlugCompilerがpushされたソースをコンパイルしてSlugを作成
 Slugサイズが大きいほどコピーは遅い
 Http(s)リクエストはHerokuRouterが受けてDynoにhttpで転送
 Dynoの数を増やすことで負荷分散が可能
 Dynoが複数ある場合単純なラウンドロビンでリクエストは割り振られる
◦ セッションは考慮されないので単一マシンからのリクエストがばらばらのDynoに
割り振られる
◦ Dyno間で情報を共有するためにはMemcache, RDB, Cookieなどを使用する
必要がある
 Dynoが30秒間レスポンスを返さない場合はHerokuRouterがClient
に503を返す
 Dyno起動時に実行するコマンドが定義されたファイル
web: play run --%web --http.port=$PORT $PLAY_OPTS
hoge: play run --%hoge --http.port=$PORT $PLAY_OPTS
fuga: play run --%fuga --http.port=$PORT $PLAY_OPTS
 [名前]: に続けて実行コマンドを記述
 上の例では引数だけ変えてすべてPlayを起動している
 $PORTはHttpRequestを受け付けるポートを定義した環境
変数(web以外ではここにリクエストが来ることはない)
 名前が「web」の定義だけは特別扱いされており、これだけが
Routerからのhttpリクエスト転送を受ける(WebDyno)
 それ以外の定義行はすべてWorkerDynoと呼ばれバックエンド
での処理を構築する際に使用する
 メールやキューをポーリングして処理を実行する、など
 Procfileに設定した各行がそれぞれWebDyno、WorkerDyno
となり個別に起動する台数を指定できる
◦ 一般的にはWebDynoは負荷によって台数を増減させ、WorkerDyno
は1台のみ起動させることが多い
◦ いずれのDynoもオートスケールはしない
 WebDyno1台のみ、WorkerDynoなしという構成の場合料金
は無料(厳密な説明は次ページ)
◦ ただし、WebDynoが1台だけの場合は1時間程度リクエストがない状
態が続いた際にDynoがシャットダウンする(次のリクエストが来た時に
Dynoをまた起動するので初回リクエストのレスポンスが遅くなる)
 WebDynoが2台以上の場合はアイドル状態によるシャットダウンはない
 WorkerDynoは1台でもアイドル状態によるシャットダウンはない
 WebDynoとWorkerDynoはコードベースとしては同じもので、
ただWebDynoはHerokuRouterからのHttpRequestの転送
を受けるという点だけが異なる。
 デフォルトでDynoでの使用可能メモリは512MB
◦ Javaアプリを動かすことを考えるとかなり少ない
 WebコンソールでDyno種別ごとにメモリを512MBで
使用するか倍の1GBで使用するかを選択できる
 今のところ1GB以上のメモリを使用することはできな
い
 ちなみにメモリを倍にすると価格も倍
 Dynoが起動している時間をDyno時間と呼ぶ
◦ 1Dyno時間は1台のDynoが1時間起動していたことを表す
 1Dyno時間の料金=0.05$
 ただし750Dyno時間までは無料
◦ つまりWebDyno1台をまる一ヶ月動かしたとしても
1 × 24 × 31 = 744Dyno時間にしかならないため料金はかからな
い
 WebDynoがアイドル状態であったとしてもDyno時間を消費す
る
 スケジューラやheroku runコマンドの実行もDyno時間を消費
する
 2XDynoは1時間の起動で2Dyno時間を消費すると考える
 これ以外にアドオンの料金がかかる
 heroku domainsコマンドで使用するドメイン名を追
加
 DNSをCNAMEで「xxx.herokuapp.com」を指すよ
うに設定すればOK
 ただしSSLを使用する場合はホスト名が変わるので要
注意
$ heroku domains:add www.xxx.com
 SSLのアドオンを追加(20$/month)
 heroku certsコマンドで証明書と秘密鍵を追加
◦ 中間証明書がある場合は証明書に追加しておく
 証明書がインストールされると新しいホスト名
「xxx.herokussl.com」が生成されるのでDNSでそのホ
ストを設定
◦ ちなみにxxx.herokussl.comの実体はELB
◦ なのでSSLではhttpの転送が1段増えていると思う
 herokuapp.comには「*.herokuapp.com」の証明書が
入っているのでSSLアドオンを使用しなくてもSSL通信自体
は可能
$ heroku addons:add ssl:endpoint
$ heroku certs:add <PEM> <KEY>
 例えば開発環境をコピーしてステージング環境を作成
したい場合は開発環境のディレクトリで。。。
 heroku createで新しいアプリを作成
 作成後にgitのURIが表示されるので適当な名前で
git remoteに登録
 その名前でgit push
$ heroku create staging-xxxx
$ git remote add staging git@heroku.com:staging-xxxx.git
$ git push staging master
heroku createコマンドはそこにあるリポジトリに
「heroku」というremote名が登録されていない
場合は自動的に登録する。
そのため、最初にアプリを作成した直後はgit
remote addを実行する必要はない
heroku forkというアプリをコピーするコマンドもあるが
課金アドオンが元と同じプランで作成されるためあまり使わない
 herokuコマンドはgit remoteに登録されたエイリア
スからアプリを判断する
◦ 「git@heroku.com」で始まるリモートリポジトリの「:」以降が
Herokuアプリケーション名
◦ Herokuのリモートリポジトリが一つだけの場合はherokuコ
マンド実行時にアプリケーション名を省略可
◦ 複数ある場合はコマンド実行時に「-a <アプリケーション名
>」を明示的に指定する必要がある
$ git remote –v
heroku git@heroku.com:prod-myapp.git (fetch)
heroku git@heroku.com:prod-myapp.git (push)
stg git@heroku.com:staging-myapp.git (fetch)
stg git@heroku.com:staging-myapp.git (push)
$ heroku logs –a staging-myapp
 環境によってアプリの動作を変えたい場合には環境
変数を利用する
 環境変数を変更した場合WebDyno、WorkerDyno
ともにすべて再起動する
heroku config
heroku config:add hoge=fuga aaa=bbb
 HerokuのGit上にmaster以外のブランチを作成して
も無視される
◦ masterへのpushをフックしてビルドプロセスが実行される
◦ ブランチ自体は普通に使えるがブランチのソースでSlugコン
パイルを行うことはできない
 チーム開発を行う場合、GitHub等の外部リポジトリ
はほぼ必須
◦ Herokuのgitはアプリのリリース専用と割り切るべき。
◦ 開発環境では検証のために「push -f」で強制上書きが必要
になることもあるかもしれない
 あんまり気が進まないので実際にはやったことはないがアリだと
は思っている
 Slugサイズが大きいとDynoへのデータ転送が遅くなり結
果として起動に時間がかかることになる
◦ JavaアプリはJDKがSlugサイズに含まれるためサイズ的には不利
◦ Play2などは最初から100MB超
 サイズの大きなファイルはgit pushしない
◦ PDFや大量の画像ファイルなどはS3などの外部ストレージに置く
 「.slugignore」ファイルを使用する
◦ ここで指定したファイルはslugに含まれない
◦ 書式は「.gitignore」とほぼ同じ(「!」など一部書式未サポート)
◦ テストでしか使用しないクラスなどSlugに必要ない物はできるだけ
指定した方が良い。
 Dynoは以下のタイミングで再起動する
◦ Git push
◦ heroku configの変更
◦ おおむね1日1度の頻度で自動的に再起動
 ここで再起動と言っているのは実際にはインスタンス
が切り替わる
◦ Herokuはマルチテナントなので一つのEC2インスタンス上に
複数のDynoが同居
◦ このためHerokuのIPはころころ変わる
WebDynoはともかくWorkerDynoが毎日再起動するのは
とても困る(--
 最初にルーティングを止めて、
 全てのDynoをシャットダウン
◦ シャットダウンの前に実行中のリクエストのレスポンスを待っている?
 その後再起動
◦ つまりコードや環境変数の異なるDynoが同時に起動しているという状態はない
 その後ルーティングを再開
◦ その間のリクエストはRouterのキューに入ると思われる
 再起動中にブラウザでF5を連打した場合レスポンスは遅いがエラーとはならない
 もちろんリクエストが多くてキューがあふれた場合はエラーになると思われる
Preboot(現在ベータ
扱い)を使用すると
DownTimeも短くなるDown
Time
 WorkerDynoは外部から現在仕事中かどうかを知る
すべがない
◦ 例えば5分おきにメールポーリングしているDynoのステータ
スは外からはわからない
◦ これに対してWebDynoはRouterから回されている仕事がな
ければアイドル中と判断できる。(もちろん作りによるけど)
 にも関わらず1日に1度容赦なく再起動がかかる
 Herokuのドキュメントには「SIGTERMを受け取ったら
10秒以内に安全にシャットダウンしろ」とか書かれて
いるが、現実には実行中のタスクのどのタイミングで
再起動が来るかわからないので対応不能。。。。(--
 HerokuAPIとは
◦ herokuコマンド相当のことをプログラムから実行するための
WebAPI
 24時間未満で自動再起動がかかることはない(多分)ので
その前でWorkerプロセスの中から自力で再起動してしまう
HerokuAPI api = new HerokuAPI(“xxxx”);//引数のAPIKeyはWebConsoleで取得
api.restartProcessByType(“yyyy”,”worker”);//heroku ps:restart worker
api.scaleProcess(“yyyy”, “worker”, 0);//heroku ps:scale worker=0
 Workerが常時動いている必要がない場合は
scaleProcessメソッドでWorkerを停止するのもアリ
◦ この場合どうやってWorkerを起動するかは別途検討が必要
 https://devcenter.heroku.com/articles/labs-
preboot
 Heroku labsは正規リリース前の機能を実験的にユーザー
に使用できるようにしたもの
 Prebootを使用すると再起動時の動作が
新Dyno起動 > ルーティングスイッチ > 旧Dyno停止
の順になり、ダウンタイムを短縮できる
◦ WebDynoにのみ適用
◦ WebDynoが2台以上起動している場合のみ適用
 Prebootを使用するとSlugサイズやアプリの起動時の重たい
処理のダウンタイムへの影響もなくなる
Dynoの再起動問題/Prebootについて別スライドあり
http://www.slideshare.net/shunjikonishi/heroku-dyno
 指定時間間隔でコマンドを実行するアドオン
◦ 間隔としては24時間毎、1時間毎、10分毎が選択できる
 実行時にはWebDynoでもWorkerDynoでもない新し
いDynoが作成され、そこでコマンドが実行される
◦ つまり自分で作ったアプリが実行できる
 スケジューラでのコマンド実行はDyno時間を消費する
 標準ではスケジューラでherokuコマンドは実行できな
い
◦ 後述のheroku-buildpack-toolbeltをいれることで実行可
能になる
 Dyno上で任意のコマンドを実行するHerokuコマンド
◦ viがないなどコマンドはかなり制限されている
◦ アプリのルートに「.profile」を作成しておくと起動時にそれが実
行される(WebDynoやWorkerDynoでも実行される)
 heroku runコマンドでも新しいDynoが作成される
◦ 既存のWebDynoやWorkerDynoに接続できるわけではない
◦ もちろんDyno時間を消費する
 「heroku run bash」とたたくと。。。
◦ telnet的な操作が可能
◦ スケジューラでどういうコマンドが実行できるかを確認するのに
便利
◦ つなぎっぱなしで放置してはいけない(Dyno時間を消費する)
 Herokuはgit pushや環境変数の変更毎にそのリビ
ジョンを保存している
$ heroku releases
v35 Add hoge config k-shunji@flect.co.jp 2013/09/03 13:28:53
v34 Deploy 8740ef9 k-shunji@flect.co.jp 2013/08/29 12:27:29
v33 Deploy 2469798 k-shunji@flect.co.jp 2013/08/23 20:25:13
 「heroku releases:rollback <リビジョン番号>」を叩くこ
とで、任意のバージョンにロールバックすることが可能
 ロールバックはS3に保存されている各リビジョンのコン
パイル済みSlugと差し替えることで実現
 ロールバック後に再度git pushするとpushされた再
度最新のソースからSlugが作成される。(git内で
revert等のロールバック操作は一切行われてない)
 2013年9月現在、US-EASTとEUの2つのリージョンが利
用可能
 リージョンはアプリ作成時に指定する(デフォルトはUS-
EAST)
◦ コードベースが同じでもリージョンが違えば別アプリになる。
 データベースやMemcacheなどのAddonは自動的に
Herokuアプリ本体のリージョンと同じになる
◦ ただし、EUリージョンに対応していないAddonもあるらしい
◦ DBを統一したいなどの理由でEUからUS-EASTのAddonを利用す
ることはできるがネットワークレイテンシ―が問題となる
 Tokyoにはいつ来るんだー
 クラウド上にあるというだけでごく普通のPostgreSQL
 制限事項
◦ パラメータを変更できない
◦ Userを作れない
◦ 接続を制限できない(URLを知っていればどこからでもアクセス可
能)
 複数のHerokuアプリから同じDBを使用しても良い
◦ この場合heroku configのDATABSE_URLをコピーする
◦ Heroku以外からも使用できるがUS-EASTなので日本からの接続
は遅い
 価格毎に土台となるEC2のインスタンスが異なる
◦ メモリ(キャッシュサイズ)の差異よりもCPUパワーの差が大きい
 pgbackups
◦ アドオンの一つ
◦ 毎日自動でバックアップを取ってくれる
◦ 無料なので必ず使うべき
◦ ただしバックアップを取る時間帯は指定できない(アクセスの少
ない夜間に、という設定はできない)
◦ heroku pgbackupsコマンドで手動でのバックアップ/リスト
ア/ダウンロードが可能
 Fork
◦ 既存のDBをコピーして新しいDBを作成
 Follow
◦ 既存のDBのレプリケーションDBを作成
◦ 本番DBの障害時に切り替えて使用することが可能(手動)
 https://dataclips.heroku.com/
 SELECT文を登録しておいてその結果をいつでも参照でき
る
◦ ExcelやCSV形式でダウンロード
◦ GoogleDocsへのExport
 GoogleSpreadsheetのURLが生成されてそこにアクセスするといつで
も最新のデータを参照できる
 グラフもそこで作れたりする
 色々と惜しい
◦ セキュリティのデフォルト設定が「URLを知っている人は誰でも使え
る」のは危険
◦ パラメータが使えない
◦ 登録したSELECT文を整理できない
不便なので代替ツールを自分で作ってみた
https://github.com/shunjikonishi/sqltool
http://www.slideshare.net/shunjikonishi/furoku-sqltool
 Localeが「en_US.UTF-8」固定
◦ このためCREATE TABLE時に「COLLATE “C”」を設定しない
と日本語列のソートがおかしくなる
◦ 何故か日本語Localeは指定できない
 実行に2秒以上かかる遅いSQLはデータ込みでログ
出力される
◦ データがログに残ることが一切NGな場合は注意が必要。
◦ 原則はそんな遅いSQLは実行しないこと(だがデータ量や負
荷によってはいかんともしがたい場合もあるので。。。(--)
◦ 別アプリでDBを作ってDATABASE_URLをコピーする運用に
したこともある。(Papertrailにログを流さないようにするため)
 Herokuのログはデフォルトで1500行しか保存され
ないのでなんらかのログAddonは必須
 ログAddonは複数あるがこれが一番使いやすかった
◦ 1年位前の評価だが他のに変える動機が無い
 機能
◦ ブラウザ上でのログ表示
◦ DailyでのS3へのログアーカイブ
◦ 監視文字列を設定してメール通知
 料金体系
◦ 1日当たりのログ出力量によって課金
◦ 10MB/日まで無料、有償版は50MB/日で月間$7から
◦ Limitに到達するとそれ以降のログは無視される
 パフォーマンス監視
 Herokuで一番使われているAddonらしい
◦ 1年半くらい前の情報
 有償版は高いので使ったことが無い
◦ Dyno数に応じた従量課金
◦ しかし無償版でも十分高機能
 Memcacheサーバー
 WebDynoを複数使用する場合はほぼ必須
 料金体系
◦ キャッシュサイズによって課金
◦ 25MBまで – 無料
◦ 100MBまで - $15
◦ …
◦ 10GBまで - $550
Papertrail, NewRelic, Memcachierの3つは
はほとんど毎回利用する鉄板Addon
 メール送信Addon
 機能
◦ SMTP/WebAPIでのメール送信
◦ Webコンソールでの送信/不達メール確認
◦ メール中のURLのクリック数カウント
 メール中のURLをSendGridが自動的にリダイレクトURLに書き換えて、
そのリクエスト数を数える
◦ メールの開封確認
 HTMLメールのみ(メール中に非表示の画像URLを埋め込むことでその
画像URLのリクエスト数を数える)
 価格体系
◦ 200通/日まで無料
◦ 有償版は40000通/月で9.95$から
 https://github.com/ddollar/heroku-
buildpack-multi
 複数のbuildpackを組み合わせることができる
buildpack
 任意のバイナリをDynoに組み込む場合に使用する
◦ ffmpegをherokuに組み込むブログ記事
http://blog.flect.co.jp/labo/2013/06/herokubuildp
ack-c488.html
 https://github.com/gregburek/heroku-
buildpack-toolbelt
 Dyno上からherokuコマンドを実行するための
buildpack
 heroku-buildpack-multiと組み合わせて使用する
 これを利用することでスケジューラからherokuコマン
ドを発行できるようになるので便利
 アクセスの少ない夜間にheroku ps:scaleでDyno数を減らしたり
とか
 各アプリに個別にこのbuildpackを組み込む必要はなく、管理用
のアプリを一つ建てておくと良い。
 HerokuAPI
◦ herokuコマンドが裏で実行しているREST API
◦ Heroku APIのJavaラッパー
https://github.com/heroku/heroku.jar
認証にはHEROKU_API_KEYを使用
 PlatformAPI
◦ Oauth経由でHerokuを操作するREST API
◦ 現在ベータ版のため、APIの変更はあと数ヶ月はかなりある
見込み
◦ APIのJavaラッパーは今のところない
 Heroku社内で優先して作っているのはRubyとNode.jsらしい
 Herokuアプリの作成はコマンドでしかできないので、課金
アカウントが個人アカウントと別な場合ちょっと面倒
◦ いつのまにかWebコンソールからアプリ作成ができるようになって
いたのでほとんどアカウントを切り替える機会はなくなった。
 https://github.com/ddollar/heroku-accounts
◦ 複数のHerokuアカウントを切り替えるプラグイン
 またはこれだけでもOK
$ set HEROKU_API_KEY=xxxxx
 この場合SSHのkeyは入れ替わらないのでgitへのアクセス
はできないがそれ以外のherokuコマンドは普通に使える
 gitも切り替える場合は「.ssh/config」で鍵の切り替えを設
定する
 Tokyoにはいつ来るんだー

特盛!Heroku