Your SlideShare is downloading. ×
あなたの安心を高速に守る Container-based CI
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×
Saving this for later? Get the SlideShare app to save on your phone or tablet. Read anywhere, anytime – even offline.
Text the download link to your phone
Standard text messaging rates apply

あなたの安心を高速に守る Container-based CI

105
views

Published on

Agile Japan 2015 サテライト<沖縄> …

Agile Japan 2015 サテライト<沖縄>
https://java-kuche.doorkeeper.jp/events/22305

---

開発を安心して高速に回すための取り組みの一つに

「テストの実行時間を短縮する」

というものがある。

今回はその一例として、Docker のコンテナ技術を用いてテスト並列実行の高速化かつ安定化を目指した記録を紹介する

Published in: Technology

0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total Views
105
On Slideshare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
0
Comments
0
Likes
0
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide

Transcript

  • 1. あなたの安心を高速に守る Container-based CI 宮國 渡 (@gongoZ) 2015-4-18
  • 2. Agenda ˆ 背景 (過去にあった問題解決の話) ˆ 新たなる問題 ˆ 振り返らない高速化が生んだ弊害 ˆ 高速と安定の join ˆ まとめ
  • 3. 自己紹介 社会人 ˆ 宮國 渡 (MIYAGUNI Wataru) ˆ 株式会社OCC ˆ PHP な Web アプリケーション開発・運用保守 プライベート ˆ github.com/gongo ˆ Just Do Eat
  • 4. 自己紹介 社会人 ˆ 宮國 渡 (MIYAGUNI Wataru) ˆ 株式会社OCC ˆ PHP な Web アプリケーション開発・運用保守 ˆ ↑ これにまつわる話します プライベート ˆ github.com/gongo ˆ Just Do Eat
  • 5. プロローグ(長いです)
  • 6. Long long ago.. PHP 3, 4, 5 の時代を渡り歩いてきた、とある Web アプリ ケーションが居ました ˆ PHP がテンプレートエンジンであった頃 ˆ HTML にビジネスロジックを書き始めた頃 ˆ PHP がアクセス修飾子の無いオブジェクト指向を取り 入れた頃 ˆ 各人から生まれる複数のオレオレフレームワーク
  • 7. Long long ago.. PHP 3, 4, 5 の時代を渡り歩いてきた、とある Web アプリ ケーションが居ました ˆ PHP がテンプレートエンジンであった頃 ˆ HTML にビジネスロジックを書き始めた頃 ˆ PHP がアクセス修飾子の無いオブジェクト指向を取り 入れた頃 ˆ 各人から生まれる複数のオレオレフレームワーク ˆ 全部混ざってる!
  • 8. Long long ago.. PHP 3, 4, 5 の時代を渡り歩いてきた、とある Web アプリ ケーションが居ました ˆ PHP がテンプレートエンジンであった頃 ˆ HTML にビジネスロジックを書き始めた頃 ˆ PHP がアクセス修飾子の無いオブジェクト指向を取り 入れた頃 ˆ 各人から生まれる複数のオレオレフレームワーク ˆ 全部混ざってる! ˆ 何よりテストが無い!
  • 9. 現代の 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
  • 10. 現代の 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)
  • 11. やりたいことは ˆ 例: PHP 5.5 に上げたい ˆ 例: もうメンテされてないライブラリから移行したい ˆ 例: リファクタしたい
  • 12. でも今の僕たちの装備じゃ 現代への 快適でスムーズな移動 のクリア難しい
  • 13. 装備 = 自動テスト 「これまで」 ˆ テスト仕様書 (Excel) を見ながら手動テスト ˆ 新機能開発であれば手動テストでも良い ˆ 自動テストの主流である回帰テストはあくまで 回帰 ˆ 新規のバグは発見できない。経験と勘が必要 「これから」 ˆ 中身が変わっても外側が変わらないことを保証したい ˆ 古いライブラリからの引越しのたびに全手動テスト? ˆ PHP バージョンアップのたびに全手動テスト?
  • 14. まずは装備を整える 手動人海戦術はプラン B via http://migo0110.blog.jp/archives/7187299.html 安心に満たされた穏かな日々を過ごすため テストの自動化を目指してみることに
  • 15. 前提条件 Resource ˆ 仕様書はない (ERD 含む) ˆ 手動テストで使っていた Excel ならある Policy ˆ 仕様/UI の変更は無い想定とする
  • 16. Unit Test ? テスト書き辛さ問題 ˆ クラスベースなコードの方が少ない ˆ グローバル関数、グローバル変数が主流 ˆ そもそも <html> の中にビジネスロジックが… 古い PHP に縛られる ˆ 綺麗に書けなくてイライラ 問題 ˆ trait 使いたいし array(’a’) を [’a’] って書きたい ˆ バージョンアップに影響をうける ˆ 本体コード、テストコード両方同時に手を…?
  • 17. End-to-End test ? ブラウザ操作してその結果 (画面) をチェック系 ˆ Excel から、ある程度機械的にテストケース作成可能 テストコードが サーバーサイドに依存しない ˆ 将来的にも使い続けていける可能性が高い PHP に拘らずに 書ける ˆ バージョンを気にするのは本体のコードだけ ˆ テスト環境は現代のツールを導入できる
  • 18. というわけで 現状の仕様を保ちつつ サーバーサイドの 現代化を行うにはEnd-to-end テストの自 動化が最適と判断
  • 19. Environment of integration testing 簡潔に書けそうな Ruby 製を選択 ˆ ブラウザの操作 ˆ Selenium - Web Browser Automation ˆ Selenium Ruby binding ˆ RubyBindings - selenium ˆ Selenium Ruby binding のラッパー ˆ jnicklas/capybara ˆ Ruby テストフレームワーク ˆ RSpec: Behaviour Driven Development for Ruby
  • 20. Example (Capybara)
  • 21. 構成(個人バージョン)
  • 22. Next stage after test automation テスト実行するのに疲れてしまう (人間だもの)
  • 23. 続けなければ意味がない 自動テストも手動で実行し続けるのはつらい ˆ たかが「ポチッ」されど「ポチッ」 つらくなるとテストしなくなる ˆ 「これぐらいの修正なら大丈夫だろう」 自動テストも動かなければただのテキスト ˆ 動かさないテストは無いほうが良い (メンテ含む)
  • 24. 僕たちがやりたかったのは テストを実行することなの?
  • 25. 僕たちの力は テストを動かすためではなく 価値を生み出すため に使うべき
  • 26. 価値を生み出す力を 継続して支えるためには (機械的な)新たな仕組み を入れるべき
  • 27. Continuous Integration 継続したいことは ˆ コードが commit (push) される度にテスト走るとか ˆ 深夜に、その日 commit されたコードでテストとか ˆ 他にもいろいろ定型的な作業とか via Jenkins CI
  • 28. 構成 -Prologue version-
  • 29. 構成 -Prologue version-
  • 30. 構成 -Prologue version-
  • 31. 構成 -Prologue version-
  • 32. 構成 -Prologue version-
  • 33. 構成を組んだあとは 以下のことが実現 ˆ 深夜に 1 回、全 Integration test を実行 ˆ 前日に master branch にマージされたコードに対して ˆ 朝、出勤してテストが通ったかどうかチェックできる ˆ テスト失敗した瞬間のスクショをまとめたレポート 隙があまり生じぬ二段構え ˆ 「定時内の開発 and レビュー and テスト」 ˆ 「深夜に全テスト」 コアな部分の修正にも安心感が増した
  • 34. プロローグ 完
  • 35. 本編
  • 36. テストを書いて コードを書いて を繰り返していたある日
  • 37. 速さが足りない! テストに 1時間半も掛かってる!!
  • 38. What happened? テストケースの増大 ˆ テストケースが 600 越えてきた ˆ 1 テストケース 10 秒で終わるものもあれば 1 分かかる ものもある ˆ ログインして 3 画面ぐらい遷移してフォーム入力して… テストが遅いこと自体は問題ない  
  • 39. What happened? テストケースの増大 ˆ テストケースが 600 越えてきた ˆ 1 テストケース 10 秒で終わるものもあれば 1 分かかる ものもある ˆ ログインして 3 画面ぐらい遷移してフォーム入力して… テストが遅いこと自体は問題ない テストが実行されていれば
  • 40. 遅いフルテストの 真の弊害 テスト実行するのに疲れてしまう (みんな人間だもの)
  • 41. CI に任せてるから 大丈夫だよね? 深夜に 1 回走らせてはいるけど 1 時間半も掛かるテストを定時前に走らせるとか無理
  • 42. 僕たちは安心を求めていた ˆ フルテストによってそれを担保していたはずだった ˆ フルテストが億劫になると ˆ 最小限のテストだけ走らせるようになる ˆ 見ないふりし始める ( まあ大丈夫だろ 問題) ˆ 深夜のテスト実行まで待てないし手元で動かすのもめ んどくさい。リリースが大事だろ ( 任務遂行絶対 問題) ˆ その結果 ˆ エンバグ、デグレード多発 ˆ その他いろいろな事故リスク増大 「これでは駄目だ」 僕たちは安心を取り戻すために高速化を開始した
  • 43. まず最初に思いついたのは 高速化と言えば並列実行
  • 44. 並列実行 (RSpec) 現状 ˆ Test runner である RSpec を並列実行したい ˆ 参考: The Ruby Toolbox - Distributed Testing 今回は test-queue を採用 ˆ GitHub 本体のテストにも使われているライブラリ ˆ 既存 (RSpec) の環境をほぼそのまま使える ˆ 実行コマンドが rspec-queue に変わるだけ ˆ 暇なプロセスを作らず、テストをいい感じに分配
  • 45. 実行例 $ bundle exec rspec-queue spec/ ※ 数値はてきとうです
  • 46. 並列実行 (Browser)
  • 47. \ Selenium Grid / See: Grid2 SeleniumHQ/selenium Wiki
  • 48. Selenium Grid
  • 49. Selenium Grid
  • 50. Selenium Grid
  • 51. Selenium Grid
  • 52. 構成 -test-queue + selenium grid-
  • 53. 構成 -test-queue + selenium grid-
  • 54. 3 プロセスで実行したら 3倍の早さで終わった 10 プロセスで実行したら 8倍ぐらい早く終わった
  • 55. ぼくたちは 速さを手に入れた
  • 56. その代わりに 失なったものがある
  • 57. 安定しなくなった
  • 58. いろいろ原因はあったが 主な原因は、(テストデータの入った)DB の同一テーブルを 閲覧・変更・削除するテストケースが複数あり、それらが 同時に実行されていたため Application Server と書いてたが実は全部入っていたのさ
  • 59. 主な失敗パターン
  • 60. 主な失敗パターン
  • 61. 主な失敗パターン
  • 62. 回避策思案 -App いっぱい- 「App + DB」で 1 組のサーバを一杯立てれば?
  • 63. 回避策思案 -App いっぱい- なんとなくいけそう ˆ test-queue の各プロセス毎に「この URL にアクセス して」と固定できるので、使えそう ˆ See: 参考ページ サーバ管理めんどくさい ˆ 構築だけでなく起動や終了も ˆ テストデータが更新されたら全サーバに rsync して restore して..
  • 64. 「App + DB」を持ってて テストデータの更新もすぐにできて 遅くてもいい時は3台だったり 早く終わって欲しいときは20台だったり 好きに起動したり停止できる環境を お手軽にポチッって操作できる 便利なものないかなー
  • 65. via Docker
  • 66. Docker Docker(ドッカー)はソフトウェアコンテナ内の アプリケーションのデプロイメントを自動化する オープンソースソフトウェアである。 Linux カーネルにおける LXC と呼ばれる Linux コン テナ技術と Aufs という特殊なファイルシステムを 利用してコンテナ型の仮想化を行う。 ˆ Docker - Wikipedia ˆ What Is Docker? An open platform for distributed apps
  • 67. Docker を採用した理由は ただひたすら「手軽」に起動できる ˆ 環境 (コンテナ) の起動や停止が速い ˆ 「テストしたい時」「特に必要ない時」でカジュアルに ON/OFF できる (数秒規模) ˆ やろうと思えば複数プロセス動かせる (※) ˆ 僕たちが望んだ「App + DB」の 1 環境ができる ※ 「1 コンテナ 1 プロセス」とよく言われるけど ˆ 起動して数分で消えるテスト環境のために「web」 「app」「db」に分割するのはコストが高すぎる ˆ 手軽な使い捨て環境 だし全部入りコンテナで良い
  • 68. 構成 -Docker 導入-
  • 69. 構成 -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 .
  • 70. 構成 -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 台* 起動
  • 71. アプリコンテナを Docker で動かしてると ˆ selenium node も fig scale でぱぱっと起動したい ˆ この時点では オレオレ.sh で起動してた
  • 72. 探してみた Docker Hub ˆ docker build で作成されたイメージが置かれている ˆ Ubuntu 公式 だったり 個人で作ったもの だったり ˆ Dockerfile 公開してないとちょっと心配になる ありました ˆ registry.hub.docker.com/repos/selenium/ ˆ Selenium 開発コミュニティが出しているイメージ ˆ Dockerfile も GitHub に公開 されている
  • 73. 最終形態 (準備) # fig.yml app: image: app-php55 ports: - "80" node: image: selenium/node # <= Docker Hub にあるやつ links: - hub hub: image: selenium/hub ports: - "4444:4444"
  • 74. 最終形態 (召喚) $ fig up -d hub $ fig scale app=15 node=15
  • 75. 最終形態
  • 76. 最終形態
  • 77. 最終形態
  • 78. 最終形態
  • 79. 最終形態の実行結果
  • 80. 最終形態の実行結果
  • 81. 最終形態の実行結果
  • 82. エンディング 以下を両立した Container-Based CI の誕生 ˆ テスト 10 分以内に終わる手軽さ ˆ 「なぜか失敗する」という暗黒の排除 僕たちは 安心と速度の両方を手にした
  • 83. エピローグ
  • 84. 近況報告 無事 PHP のバージョン上げられました 1 PHP 5.X と PHP 5.Y それぞれの Docker コンテナを用 意して検証 2 両方でテストが通るか。通らないなら何が原因か ˆ バージョンの違いによるものか、潜在的なバグか 複数バージョンを高速にチェックできてよかった
  • 85. 近況報告 (Cont’d) 開発期間の変化 (高速化する前から取り組んでいましたが) ˆ 以前までは 3 ヶ月 - 6 ヶ月などの長期間 ˆ 現在は 2、3 週間スプリントで回している ˆ 開発 (1、2 週間。この間はレビュー & 自動テスト チェックをメインに) ˆ 検証 (4 日間。メインの CHANGE に対する手動テスト) ˆ ふりかえり (1 日。本スプリントでの KPT) テストの高速化、および安定化の維持は良い効果
  • 86. 現在の開発フロー 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 を繰り 返す
  • 87. 後輩のコメント
  • 88. ずっと目指していた via full test also want to end within 50ms // Speaker Deck
  • 89. まとめ
  • 90. 手持ちのリソースにあった パターンを 今回は高スペックなワークステーションがあったので Docker コンテナを大量召喚できた ˆ 低スペックだがマシンが大量にあるなら、並列化だけ ではなく 分散化 も考えるべき
  • 91. 勘違いしてはいけないのは 高速化を目指すことでテストが安定しないのであれば 高速化を諦めてテストの安定に集中すべき ˆ × 目指すはテストの高速化 ˆ ○ 開発者に心の安らぎを
  • 92. おわり PEACE OF MIND 安らぎが力になる