パフォーマンス計測CIサービスを
作って得た知見を共有したい
Rails Developers Meetup #5
2017-09-28 @zaru
@zaru
株式会社ベーシックCTO
株式会社ベーシック
最近の活動
ブラウザにプッシュ通知できるライブラリwebpush (gem)
最近マストドンにマージされた
URLをQRコードに変換するMacアプリLightningQR
GitHubの通知をするMacアプリNotifyHub
seedを自動で作ってくれるseedbuilder (gem)
スクラムマスター
コードを音にしたり、ドット絵にしたり、リズムゲームにしたり
今日話したいこと
raysCI について
GitHub Apps について
Docker / kubernetes について
個人開発プロジェクトについて
raysCI
個人で開発している
パフォーマンス計測をするCIサービス
プルリクを作ったら自動でMasterとPRブランチのベンチマーク
ベンチマークの種類
ApacheBench
ヘッドレスブラウザ
First paint
DOM content load
assets network (HAR)
stackprof (gem)
開発中の画面
Railsのみサポート
将来的には他の言語やフレームワークに対応したい
プライベートベータ
近いうちにオープンベータへ
計測に使ったビルド時間の従量課金を予定
その他、予定している機能
Viewレンダリング結果のdiff
ステージング環境自動構築
プロダクション環境への定期パフォーマンスチェック
GitHub Apps
OAuth Apps とGitHub Apps
OAuth Apps (従来のもの) はユーザに対してインストール
ユーザが削除されたりリポジトリへの権限を失うと消える
アクセストークンはユーザが取り消さない限り永久有効
GitHub Apps はリポジトリに対してインストール
正確にはリポジトリのオーナー(Organization やUser)に
インストールして、配下のリポジトリに付与するようなイメージ
アクセストークンの有効期限は1時間
GitHub Apps のOAuth も可能
こちらはユーザ情報を取得するために使う(限定的)
マーケットプレイスに出せる
OAuth Apps ではbot がコメントするためには実ユーザになりすます
GitHub Apps は一人格としてコメントできる
OAuth したユーザの代わりにもコメントできる
人に依存しないサービス連携!
業務では非常に嬉しい気持ち
マーケットプレイス
GitHub 上でアプリをインストール・決済できる
提供者は売上の25%をGitHub に献上
決済の仕組みを作らなくて済むので楽?
でもAPIのコールバックとかで、ある程度開発は必要
登録する条件
250人以上にインストール済み<厳しい
セキュリティ審査や申請ドキュメントなど結構たいへんそう…
アクセスログとか保持する基盤とか全部そろえて説明できる資料
とか
GitHub Appsの作り方
とても簡単
アクセストークンとAPI
秘密鍵を使ってJWTに署名して生成(有効期限最大10分)
JWTを使ってアクセストークンを取得(有効期限1時間)
アクセストークンで各種APIにアクセス(一部のAPIのみ)
JWT コード例
private_pem = File.read(path_to_pem)
private_key = OpenSSL::PKey::RSA.new(private_pem)
payload = {
iat: Time.now.to_i,
exp: Time.now.to_i + (10 * 60),
iss: <GitHub App ID>
}
jwt = JWT.encode(payload, private_key, "RS256")
JWTのexpireに注意
最大で10分の有効期限を指定できる
なぜか10分ぴったり指定すると、時々未来の日付だよと怒られる
8分にしても怒られる時がある
10分も必要ないと思うので、5分程度で運用したほうが良さ気
GitHub Apps のOAuth
GitHub Apps にもユーザ情報を取得するOAuth がある
取得できるユーザの情報は限定されている
ユーザ名やID・アバター
公開設定のEmail
非公開の場合はnull
/user/emails にはアクセスできない
将来的には取得できるようになるっぽいが…
インストール済みリポジトリ/ Public リポジトリ
インストールしてないPrivate リポジトリは取得できない
リポジトリの検索などはできない
ユーザ中心のサービスは向いてない…
GitHub Apps のAPI
現時点で全てのAPIにアクセスできるわけではない
少しずつ解放される様子
9月21日にも66個のAPIが解放されている
もし要望があるならフォーラムに投稿すると早くなるかも?
Request support for additional endpoints for Integrations
リポジトリ検索ができないのがつらい
GitHub Enterpriseでの利用
GitHub Enterprise v2.11 で利用可能になるアナウンス
しかしv2.11 のリリースノートには記載なし…
GitHub Apps で気をつける所
Webhook は取りこぼさないように
取りこぼすとデータをロストしてしまう
一応、設定画面から再送処理は可能だが…現実的ではない
タイムアウトや例外などで死なないようにする
例えばsidekiq などに逃がす
将来的にはエラーを通知する仕組みを用意してくれるっぽい
パーミッションの変更はなるべくしない
変更後、ユーザが承認しないと反映しない
すごく分かりにくいUIなので気が付きにくい…
通知も何もなく、ひっそりとSettingsにあるだけ
メールでの通知はしてくれる
GitHub Apps のインストール導線
不親切な流れになってしまう
サービスページ(インストール誘導)
→ GitHubのインストール承認ページ
→ インストール完了
→ 終了(github.comのまま…)
インストール後、サービスページに自動で戻らない
理想なのはOAuth Appsのような流れ
サービスサイトでちゃんとフローを案内する必要がある(厳しい)
要望は出ていて検討中らしいので改善されるかもしれない
マーケットプレイス中心の導線設計なのかも
Privateリポジトリ一覧が取れないので、他のリポジトリに
追加インストールするのもGitHubページ上でないとできない
サービスサイトとGitHub App設定画面を行ったり来たり
追加/削除自体はAPIでできるが…
GitHub Apps ローカル開発
ローカルでWebhook 受け取る方法
ngrok で外部に解放
無料だと起動のたびにホスト名が変わるので面倒
nginx でproxy サーバをたてる
ルータでポートフォワーディングが必要
payload の例があるので、curl などで叩く
https://developer.github.com/v3/activity/events/types/
upstream http_backend {
# server example.ngrok.io; # ngrokの場合
server 10.0.0.10:3000; # ローカルのグローバルIP
keepalive 128;
}
server {
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $http_host;
proxy_set_header X-CSRF-Token $http_x_csrf_token;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $http_host;
# proxy_set_header Host example.ngrok.io; # ngrokの場合
proxy_redirect off;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_pass http://http_backend/;
}
}
Docker
CI, どうやってアプリを動かすか
CIは、ユーザのアプリ毎・ビルド毎に分離した動作環境が必要
今ならDockerが良い気がする
どうやってDockerビルド/ コンテナ管理しよう?
CIサービス自体もDockerで動かしたい
Docker in Docker
docker.sock 共有
自前ビルドサーバ+ オーケストレーション
kubernetes + GCP Container Builder
Docker in Docker
jpetazzo/dind
マトリョーシカのようにコンテナ内でコンテナを起動
Dockerのprivilegedオプションを利用してDockerを操作
良い点
階層構造なので綺麗で管理しやすい
悪い点
パフォーマンス
privilegedオプションで怖い
ボリュームが残ってしまう
そもそも制作者自身があまり推奨していない
Using Docker-in-Docker for your CI or testing environment?
Think twice.
docker.sock 共有
ホストの/var/lib/docker.sock を共有する
階層構造はなく、単にホストのDockerエンジンにアクセスできる
良い点
普通にDockerを使っているようなものなのでシンプル
ビルドキャッシュも簡単に使える
悪い点
コンテナ内から他のコンテナが丸見え…
内部利用限定なら良いかもしれない
自前ビルド+ オーケストレーション
つらい
kubernetes
+
GCP Container Builder
コンテナの管理はkubernetesに全部任せる
ビルドはGCP Container Builderに全部任せる
超絶楽
いつどれくらいコンテナが立てられるか予測できない
GKE + k8sならコンテナ作成時にリソースが不足していたら自動で
ノードを起動してくれる(オートスケール)
リトライ処理やライフサイクルなど機能が豊富
複雑になりがちなビルドステップどうしよう…
Container Builder自体が簡易CIみたいなもの
簡単にステップを記述でき、公式で用意してあるイメージが便利
(gitコンテナ・dockerコンテナ・gcloudコンテナなど)
悪い点
Container Builderがビルドキャッシュをサポートしていない
毎回フルビルドなので時間がかかる
ところで
GKE + kubernetes かHeroku か
細かくコントロールとビルドをするならGKE + kubernetes
kubernetesだけやってればOKな感じある
小規模/シンプルなアーキテクチャならHerokuオススメ
むしろHerokuライクなサービスが主流に…なってほしい
詳しくは普通のRailsアプリをDockerで本番運用する知見を
CI, 計測の仕方
計測の流れ
Container Builder にビルド依頼
ビルドコンテナがAppコンテナに計測用のコードを仕込む
Gem le やinitializer / environment など
ビルドコンテナがk8s デプロイファイルを作成
k8s にデプロイ
計測コンテナがApp の立ち上がりを待機
計測開始
計測後、CIコンテナにレポート送信
Pod を削除して終了
CIサービスを作るのに使った小技
appコンテナ起動時にDB作成/インポートを行う
正直、力技…どうにかしたい
command:
- "sh"
- "-c"
- >
bundle exec rails db:create
&&
bundle exec rails db:schema:load
&&
bundle exec rails db:seed
&&
bundle exec rails db < import.sql
&&
bundle exec rails s -p 3000 -b 0.0.0.0
k8s で、コンテナ起動前と終了時にコマンド実行できる
spec:
containers:
- name: lifecycle-demo-container
image: nginx
lifecycle:
postStart:
exec:
command: ["/bin/sh", "-c", "echo Hello from the postStart handler > /usr/sha
preStop:
exec:
command: ["/usr/sbin/nginx","-s","quit"]
k8s で、share dirを作成して各コンテナでファイル共有
k8sのボリュームは以下の3種類
EmptyDir
HostDir
GCEPersistentDisk / awsElasticBlockStore
EmptyDir とHostDir は永続化されない。Pod 内で一時的に使いたいな
らEmptyDir、ホスト側でも使いたいならHostDir、永続化したいのな
ら外部ストレージのGCEPersistentDisk。
raysCI ではベンチマーク結果や、設定ファイルなどをEmptyDir に格納
して利用。
resources を指定してリソースを固定する
resources:
requests: # 最低限
cpu: 200m
memory: 512M
limits: # 最大
cpu: 200m
memory: 512M
ちゃんと指定しないとパフォーマンスがかなりブレる
1CPUに対しての割合を指定する。 0.1 は 100m
m はミリコアと読んだりする
メモリ使用が指定量を超えたらPod が死ぬ(再起動)
CPU は指定量を超えたら水平オートスケールする(設定が必要)
環境変数・秘匿情報について
env:
- name: FOO_KEY # 普通の環境変数
value: "foo value"
- name: SECRET_KEY # 秘匿情報
valueFrom:
secretKeyRef:
name: secret_name
key: secret_key
k8sクラスタに接続できるなら誰でも閲覧可能
秘匿情報はsecrets API 経由で取得するようにする
Container Builder のローカル開発
GoogleCloudPlatform/container-builder-local
ローカルで動かせる
いちいちdocker push とかしなくて済む
気の済むまでビルドデバッグ可能
個人開発プロジェクトについて
raysCI の開発ステータス
5月15日開発開始/ 約70日間の開発
1日平均2 時間程度/ 累計で約142 時間の開発
業務でやったら1ヶ月くらい?
とにかく
個人開発のプロジェクトは
頓挫しやすい
今のところうまくいってる
僕の開発スタイル
毎日コードを書く
土日にまとめてやると状態を忘れがち
習慣にする
まずはPCの前に座る これだけで継続しやすい
自分を甘やかすことも必要
夜ご飯のビール は飲んでもいい(1本まで)
今日はなんにもしないで映画見る、など
コードを捨てるのを躊躇しない
作ってみて知見を得たら再度作り直す
妥協を積み重ねたコード、さわるのつらい
モチベーションの作り方
業務とリンクしやすい領域を作る
相互作用で実益があると継続しやすく、お賃金が上る?!
「この問題進研ゼミでやったやつだ」
サービスを作りきるまでの余計な壁を取り払う
新しい技術にふれるのが目的なら離れていても問題ないが…
その場合は短期決戦にしぼったほうが良い
小さいミニアプリを大量に作るとか
何のために作るのかを明確にする
お金のため
技術習得のため
課題解決のため
目的が明確になると、何を優先して、何を捨てるのか
判断ができてストレスが減る
途中経過をなんでも良いからアウトプットする
完成してからアウトプットでは道程が長い
改善の参考になるフィードバックがあるかも
手に入った知見はその場でアウトプット
「やるぞ」駆動
Twitterにやる気はなくても「やるぞ」とつぶやく
自然とやる気が出てくる
公開に到達するために
ゴールを大きくしすぎない
小さく始めて素早く公開できる仕様にする
やらないことを決める
対象ユーザの範囲を限定する
なにかを妥協する
例えば完璧なテストコードや管理画面
raysCI はDocker 環境のみに対応、今はRails だけビルドできるように
制限を加えることで作りやすくなる。ある程度は拡張できるように事前
設計するけど、まだ見えない未来に投資しすぎない。
まとめ
GitHub Apps は作るのは簡単だけど、発展途上中
Docker自前でやるならkubernetes
ContainerBuilder が優秀すぎて泣く
個人開発は習慣化と実益をとれるようにする
小さく作って、早く公開できるようにする
ありがとうございました。
docker stop $(docker ps -a -q)

パフォーマンス計測Ciサービスを作って得た知見を共有したい