Upload
wataru-miyaguni
View
3.313
Download
1
Embed Size (px)
Citation preview
あなたの安心を高速に守る
Container-based CI
宮國 渡 (@gongoZ)
2015-4-18
Agenda
� 背景 (過去にあった問題解決の話)
� 新たなる問題
� 振り返らない高速化が生んだ弊害
� 高速と安定の join
� まとめ
自己紹介
社会人
� 宮國 渡 (MIYAGUNI Wataru)
� 株式会社OCC
� PHP な Web アプリケーション開発・運用保守
� ↑ これにまつわる話します
プライベート
� github.com/gongo
� Just Do Eat
自己紹介
社会人
� 宮國 渡 (MIYAGUNI Wataru)
� 株式会社OCC
� PHP な Web アプリケーション開発・運用保守
� ↑ これにまつわる話します
プライベート
� github.com/gongo
� Just Do Eat
プロローグ(長いです)
Long long ago..
PHP 3, 4, 5 の時代を渡り歩いてきた、とある Web アプリケーションが居ました
� PHP がテンプレートエンジンであった頃
� HTML にビジネスロジックを書き始めた頃
� PHP がアクセス修飾子の無いオブジェクト指向を取り入れた頃
� 各人から生まれる複数のオレオレフレームワーク
�全部混ざってる!
�何よりテストが無い!
Long long ago..
PHP 3, 4, 5 の時代を渡り歩いてきた、とある Web アプリケーションが居ました
� PHP がテンプレートエンジンであった頃
� HTML にビジネスロジックを書き始めた頃
� PHP がアクセス修飾子の無いオブジェクト指向を取り入れた頃
� 各人から生まれる複数のオレオレフレームワーク
�全部混ざってる!
�何よりテストが無い!
Long long ago..
PHP 3, 4, 5 の時代を渡り歩いてきた、とある Web アプリケーションが居ました
� PHP がテンプレートエンジンであった頃
� HTML にビジネスロジックを書き始めた頃
� PHP がアクセス修飾子の無いオブジェクト指向を取り入れた頃
� 各人から生まれる複数のオレオレフレームワーク
�全部混ざってる!
�何よりテストが無い!
現代の PHP (Versions)
version initial release EOL
5.4 2012/03/01 2015/09/14
5.5 2013/06/20 2016/06/20
5.6 2014/08/28 2017/08/28
(new! →) 7.0 2015/11/xx
via PHP: Supported Versions
現代の PHP (周辺)
� PHP-FIG — PHP Framework Interop Group� PSR-FIG なるコミュニティが策定する規約� PSR-0 —Autoloading Standard� PSR-2 —Coding Style Guide� PSR-3 —Logger Interface など
� Composer� パッケージ依存管理ツール (Ruby でいう Bundler)
� Packagist� Composer リポジトリ (Ruby でいう RubyGems)
やりたいことは
� 例: PHP 5.5 に上げたい
� 例: もうメンテされてないライブラリから移行したい
� 例: リファクタしたい
でも今の僕たちの装備じゃ
現代への 快適でスムーズな移動 のクリア難しい
装備 = 自動テスト
「これまで」
� テスト仕様書 (Excel) を見ながら手動テスト
� 新機能開発であれば手動テストでも良い
� 自動テストの主流である回帰テストはあくまで 回帰
� 新規のバグは発見できない。経験と勘が必要
「これから」
� 中身が変わっても外側が変わらないことを保証したい
� 古いライブラリからの引越しのたびに全手動テスト?
� PHP バージョンアップのたびに全手動テスト?
まずは装備を整える
手動人海戦術はプラン B
via http://migo0110.blog.jp/archives/7187299.html
安心に満たされた穏かな日々を過ごすため
テストの自動化を目指してみることに
前提条件
Resource
� 仕様書はない (ERD 含む)
� 手動テストで使っていた Excel ならある
Policy
� 仕様/UI の変更は無い想定とする
Unit Test ?
テスト書き辛さ問題
� クラスベースなコードの方が少ない
� グローバル関数、グローバル変数が主流
� そもそも <html> の中にビジネスロジックが…
古い PHP に縛られる
� 綺麗に書けなくてイライラ 問題
� trait 使いたいし array(’a’) を [’a’] って書きたい
� バージョンアップに影響をうける
� 本体コード、テストコード両方同時に手を…?
End-to-End test ?
ブラウザ操作してその結果 (画面)をチェック系
� Excel から、ある程度機械的にテストケース作成可能
テストコードが サーバーサイドに依存しない
� 将来的にも使い続けていける可能性が高い
PHP に拘らずに 書ける
� バージョンを気にするのは本体のコードだけ
� テスト環境は現代のツールを導入できる
というわけで
現状の仕様を保ちつつ サーバーサイドの
現代化を行うにはEnd-to-end テストの自
動化が最適と判断
Environment ofintegration testing
簡潔に書けそうな Ruby 製を選択
� ブラウザの操作
� Selenium - Web Browser Automation
� Selenium Ruby binding� RubyBindings - selenium
� Selenium Ruby binding のラッパー� jnicklas/capybara
� Ruby テストフレームワーク� RSpec: Behaviour Driven Development for Ruby
Example (Capybara)
構成(個人バージョン)
Next stage after testautomation
テスト実行するのに疲れてしまう (人間だもの)
続けなければ意味がない
自動テストも手動で実行し続けるのはつらい
� たかが「ポチッ」されど「ポチッ」
つらくなるとテストしなくなる
� 「これぐらいの修正なら大丈夫だろう」
自動テストも動かなければただのテキスト
� 動かさないテストは無いほうが良い (メンテ含む)
僕たちがやりたかったのは
テストを実行することなの?
僕たちの力は
テストを動かすためではなく
価値を生み出すため に使うべき
価値を生み出す力を
継続して支えるためには
(機械的な)新たな仕組みを入れるべき
Continuous Integration
継続したいことは
� コードが commit (push) される度にテスト走るとか
� 深夜に、その日 commit されたコードでテストとか
� 他にもいろいろ定型的な作業とか
via Jenkins CI
構成 -Prologue version-
構成 -Prologue version-
構成 -Prologue version-
構成 -Prologue version-
構成 -Prologue version-
構成を組んだあとは
以下のことが実現
� 深夜に 1回、全 Integration test を実行� 前日に master branch にマージされたコードに対して
� 朝、出勤してテストが通ったかどうかチェックできる
� テスト失敗した瞬間のスクショをまとめたレポート
隙があまり生じぬ二段構え
� 「定時内の開発 and レビュー and テスト」
� 「深夜に全テスト」
コアな部分の修正にも安心感が増した
プロローグ
完
本編
テストを書いて
コードを書いて
を繰り返していたある日
速さが足りない!
テストに
1時間半も掛かってる!!
What happened?
テストケースの増大
� テストケースが 600 越えてきた
� 1テストケース 10秒で終わるものもあれば 1分かかるものもある
� ログインして 3画面ぐらい遷移してフォーム入力して…
テストが遅いこと自体は問題ない
What happened?
テストケースの増大
� テストケースが 600 越えてきた
� 1テストケース 10秒で終わるものもあれば 1分かかるものもある
� ログインして 3画面ぐらい遷移してフォーム入力して…
テストが遅いこと自体は問題ない
テストが実行されていれば
遅いフルテストの
真の弊害
テスト実行するのに疲れてしまう (みんな人間だもの)
CI に任せてるから大丈夫だよね?
深夜に 1回走らせてはいるけど
1時間半も掛かるテストを定時前に走らせるとか無理
僕たちは安心を求めていた
� フルテストによってそれを担保していたはずだった
� フルテストが億劫になると
� 最小限のテストだけ走らせるようになる
� 見ないふりし始める ( まあ大丈夫だろ 問題)� 深夜のテスト実行まで待てないし手元で動かすのもめ
んどくさい。リリースが大事だろ ( 任務遂行絶対 問題)
� その結果
� エンバグ、デグレード多発
� その他いろいろな事故リスク増大
「これでは駄目だ」
僕たちは安心を取り戻すために高速化を開始した
まず最初に思いついたのは高速化と言えば並列実行
並列実行 (RSpec)
現状
� Test runner である RSpec を並列実行したい
� 参考: The Ruby Toolbox - Distributed Testing
今回は test-queue を採用
� GitHub 本体のテストにも使われているライブラリ
� 既存 (RSpec)の環境をほぼそのまま使える� 実行コマンドが rspec-queue に変わるだけ
� 暇なプロセスを作らず、テストをいい感じに分配
実行例
$ bundle exec rspec-queue spec/
※ 数値はてきとうです
並列実行 (Browser)
\ Selenium Grid /
See: Grid2 SeleniumHQ/selenium Wiki
Selenium Grid
Selenium Grid
Selenium Grid
Selenium Grid
構成 -test-queue +selenium grid-
構成 -test-queue +selenium grid-
3 プロセスで実行したら3倍の早さで終わった
10 プロセスで実行したら8倍ぐらい早く終わった
ぼくたちは
速さを手に入れた
その代わりに
失なったものがある
安定しなくなった
いろいろ原因はあったが
主な原因は、(テストデータの入った)DB の同一テーブルを閲覧・変更・削除するテストケースが複数あり、それらが
同時に実行されていたため
Application Server と書いてたが実は全部入っていたのさ
主な失敗パターン
主な失敗パターン
主な失敗パターン
回避策思案 -App いっぱい-「App + DB」で 1組のサーバを一杯立てれば?
回避策思案 -App いっぱい-
なんとなくいけそう
� test-queue の各プロセス毎に「この URL にアクセスして」と固定できるので、使えそう
� See: 参考ページ
サーバ管理めんどくさい
� 構築だけでなく起動や終了も
� テストデータが更新されたら全サーバに rsync してrestore して..
「App + DB」を持ってて
テストデータの更新もすぐにできて
遅くてもいい時は3台だったり
早く終わって欲しいときは20台だったり
好きに起動したり停止できる環境を
お手軽にポチッって操作できる
便利なものないかなー
Docker
Docker(ドッカー)はソフトウェアコンテナ内のアプリケーションのデプロイメントを自動化する
オープンソースソフトウェアである。
Linuxカーネルにおける LXCと呼ばれる Linuxコンテナ技術と Aufsという特殊なファイルシステムを利用してコンテナ型の仮想化を行う。
� Docker - Wikipedia
� What Is Docker? An open platform for distributed apps
Docker を採用した理由はただひたすら「手軽」に起動できる
� 環境 (コンテナ)の起動や停止が速い� 「テストしたい時」「特に必要ない時」でカジュアルに
ON/OFF できる (数秒規模)
� やろうと思えば複数プロセス動かせる (※)� 僕たちが望んだ「App + DB」の 1環境ができる
※ 「1コンテナ 1プロセス」とよく言われるけど
� 起動して数分で消えるテスト環境のために「web」「app」「db」に分割するのはコストが高すぎる
� 手軽な使い捨て環境 だし全部入りコンテナで良い
構成 -Docker 導入-
構成 -Docker 導入-(Cont’d 1st)
Dockerfile を作成する
# Dockerfile
FROM centos:centos6
COPY scripts/* /opt/scripts
RUN sh /opt/scripts/install_dependency_libraries.sh \
&& sh /opt/scripts/install_application.sh \
&& sh /opt/scripts/restore_test_data.sh
CMD service postgresql start \
&& service httpd start \
&& tail -f /var/log/httpd/access_log
EXPOSE 80
$ # Docker コンテナイメージをビルド
$ docker build -t app-php55 .
構成 -Docker 導入-(Cont’d 2nd)
Fig (現 Docker Compose) を使う
# fig.yml
app:
image: app-php55
ports:
- "80"
$ fig scale app=5 # 5台起動
$ fig scale app=10 # 5台追加で *計 10台* 起動
$ fig scale app=3 # 7台減らして *計 3台* 起動
アプリコンテナを Dockerで動かしてると
� selenium node も fig scale でぱぱっと起動したい
� この時点では オレオレ.sh で起動してた
探してみた
Docker Hub
� docker build で作成されたイメージが置かれている
� Ubuntu 公式 だったり 個人で作ったもの だったり
� Dockerfile 公開してないとちょっと心配になる
ありました
� registry.hub.docker.com/repos/selenium/
� Selenium 開発コミュニティが出しているイメージ
� Dockerfile も GitHub に公開 されている
最終形態 (準備)
# fig.yml
app:
image: app-php55
ports:
- "80"
node:
image: selenium/node # <= Docker Hub にあるやつ
links:
- hub
hub:
image: selenium/hub
ports:
- "4444:4444"
最終形態 (召喚)
$ fig up -d hub
$ fig scale app=15 node=15
最終形態
最終形態
最終形態
最終形態
最終形態の実行結果
最終形態の実行結果
最終形態の実行結果
エンディング
以下を両立した Container-Based CI の誕生
� テスト 10分以内に終わる手軽さ
� 「なぜか失敗する」という暗黒の排除
僕たちは
安心と速度の両方を手にした
エピローグ
近況報告
無事 PHP のバージョン上げられました
1 PHP 5.X と PHP 5.Y それぞれの Docker コンテナを用意して検証
2 両方でテストが通るか。通らないなら何が原因か
� バージョンの違いによるものか、潜在的なバグか
複数バージョンを高速にチェックできてよかった
近況報告 (Cont’d)
開発期間の変化 (高速化する前から取り組んでいましたが)
� 以前までは 3ヶ月 - 6ヶ月などの長期間
� 現在は 2、3週間スプリントで回している� 開発 (1、2週間。この間はレビュー & 自動テストチェックをメインに)
� 検証 (4日間。メインの CHANGE に対する手動テスト)� ふりかえり (1日。本スプリントでの KPT)
テストの高速化、および安定化の維持は良い効果
現在の開発フロー
1 手元で開発
2 git push
3 GitHub で Pull Request (以下、PR) を作成
4 Jenkins Master が PR(commit) を検知し、そのブランチを Jenkins Slave に教える
5 Jenkins Slave が教えられたブランチを元に Docker のアプリコンテナを起動
� ついでに Selenium Hub/Node も起動 (停止していれば)
6 Integration test に加えて Unit Test も実行
7 約 10分ぐらいでフルテスト完了
8 もし fail だったら修正を commit 。以後 4 - 7 を繰り返す
後輩のコメント
ずっと目指していた
via full test also want to end within 50ms // Speaker Deck
まとめ
手持ちのリソースにあった
パターンを
今回は高スペックなワークステーションがあったので
Docker コンテナを大量召喚できた
� 低スペックだがマシンが大量にあるなら、並列化だけ
ではなく 分散化 も考えるべき
勘違いしてはいけないのは
高速化を目指すことでテストが安定しないのであれば
高速化を諦めてテストの安定に集中すべき
� × 目指すはテストの高速化
� ○ 開発者に心の安らぎを
おわり
PEACE OF MIND
安らぎが力になる