Upgrade to Pro — share decks privately, control downloads, hide ads and more …

JavaでWebサービスを作り続けるための戦略と戦術

 JavaでWebサービスを作り続けるための戦略と戦術

Japan Java ユーザーグループ クロスコミュニティ カンファレンス 2018 Spring 登壇資料

Yu Watanabe

May 29, 2018
Tweet

More Decks by Yu Watanabe

Other Decks in Technology

Transcript

  1. #ccc_g1 published Who? • 渡辺 祐 • (株)ビズリーチ • SREグループ

    ◦ Site Reliability Engeneering • twitter: @nabedge 5
  2. #ccc_g1 published 仮想OS on 仮想OS 13 OracleVirtualBox CentOS Docker (MySQL)

    Docker (Redis) CentOS Docker (PgSQL) Docker (fakes3) Aサービス のコード Bサービス のコード 172.16.1.1 172.16.2.2 setup.sh setup.sh Mac OS
  3. #ccc_g1 published setup.shがやってること 1. vagrant up 1.1. 仮想OS(CentOS)起動 1.2. yum

    install docker-ce 2. vagrant ssh -c “docker-compose up -d” 2.1. mysql, solr, localstack, etc... 3. DBにテストデータをINSERTするスクリプト 15
  4. #ccc_g1 published Eclipse -> IntelliJ IDEA 1. ライブラリプロジェクト、アプリケーションプロジェクトの絶対数 が増 a.

    フラットなプロジェクトレイアウトが前提のEclipseだと何かと不都合 2. Python, TypeScript, Android Java... 3. Scalaのチームもいる 4. 全社の全エンジニアまとめてIntelliJ 18
  5. #ccc_g1 published Maven -> Gradle • ライブラリプロジェクト、アプリケーションプロジェクトの絶対数 の増加 • CPUのコア数に合わせて並列ビルドできるほうが有利

    • フロントエンドのビルドツールもろともgradleから制御したい • pom.xmlよりもbuild.gradleのほうが自由度が高いわりに読み やすい 19
  6. #ccc_g1 published Jenkins1から2へ。そしてJenkinsfileへ 20 • Maven -> Gradle移行の過程でjenkinsジョブの 調整コストが増 •

    全ソース, build.gradle, Jenkinsfile まで含め git管理するだけで楽勝。 • っていうのを見越してまず先にJenkins2に バージョンアップしていた
  7. #ccc_g1 published • “public static void main”ならIDEだろうと サーバ上のCLIだろうと、どうにでも動かせる 27 public

    class AppStarter { public static void main(String args[] argv) { Tomcat tomcat = new Tomcat(); tomcat.start();
  8. #ccc_g1 published JasperReportはオワコン • PDF出力ライブラリ • Java8だとデザインツールが事実上動かない ◦ (2016年当時です。今は知らない) •

    全体のJava8化を阻害する意外なボトルネックだった • flying-soucer-pdf でPDF関連機能を全部作り直し 30
  9. #ccc_g1 published “JSESSIONID”はオワコン • サブシステムAは http://localhost:8001/ • サブシステムBは http://localhost:8002/ •

    両方ともセッションCookie名が ”JSESSIONID” • ローカル開発環境でA, B同時に起動すると 片方でしかログイン状態を保てない • エンジニアの生産性低下 33
  10. #ccc_g1 published // JavaConfigの場合 @WebListener public class SessionTrackingListener implements ServletContextListener

    { @Override public void contextInitialized(ServletContextEvent event) { SessionCookieConfig config = event.getServletContext().getSessionCookieConfig(); config.setName("MY_HOGE_SESSIONID"); config.setHttpOnly(true); 35
  11. #ccc_g1 published sticky-session-cookieもオワコン • APサーバ2台の冗長構成だとして • リリースのときは1台ずつ止めてデプロイ • リリース中は片系統にアクセス集中 •

    リリース後も片系統にアクセス集中したまま (sticky-session-cookieとはそういうもの) • 同時利用ユーザー数が増えると... !! 38
  12. #ccc_g1 published おすすめの「JavaConfigはじめかた」 <beans xmlns:context="http://www.springframework.org/schema/context"> <context:component-scan base-package="jp.bizreach.foo"/> </beans> 44 package

    jp.bizreach.foo @Configuration public BarConfig { @Bean public HogeService hogeService() {...} } xmlファイルにほんの数行だけ残しておく
  13. #ccc_g1 published src/main/webappの存在がオワコン • クラスパスではないからJUnitから読み込めない ◦ 単体テスト自動化を妨げる元凶 • MVCフレームワークの機能が中途半端だった時代の遺跡 ◦

    ほとんどのMVC-FWは、”src/main/resources/static” に置 いた静的リソースをそのままレスポンスする機能が既にあ る。 ◦ TomcatのDefaultServletの出番はもはや無いのに、それ を使うためのディレクトリ構造を残すの? 49
  14. #ccc_g1 published ├ build.gradle └ src/ ├ main/ │ ├

    java/ │ ├ resources/ │ └ webapp/ │ └ WEB-INF/ └ test/ ├ java/ └ resources/ 普通のクラスパス テストのクラスパス ロストテクノロジー置き場 50
  15. #ccc_g1 published • IDEはもちろんIntelliJ IDEA ◦ Java, Scala, TypeScript, JavaScript,

    Python 全部対応 • npm + gulp/webpack から yarn + gradle へ 56
  16. #ccc_g1 published • わざわざgradleからyarnを呼ぶ理由 ◦ gradleのほうがなんでも書ける(groovy)わりに、bashより も読みやすい ◦ mavenではまず無理 ◦

    並列実行ですばやくビルドできる ◦ 並列実行対象タスクを任意に設定しやすい ◦ Jenkinsfileが短くなる 57
  17. #ccc_g1 published テストの理想と現実 • 自動化に倒すべきなのは確か ◦ JUnit, spring-test, Selenium... •

    「テスト項目表を書いての手動テスト」も 結局は存在し続けるだろう ◦ その良し悪しはともかく 63
  18. #ccc_g1 published • 良い兆候=ビズリーチシステムの現状 ◦ 若手のエンジニアが増、ソースコードの絶対量も増。 ◦ にも関わらずテストカバレッジ率は下がってない。 むしろジリジリと上がってる。 ◦

    “@Test”メソッドの数は明らかに増えている • よくある悪い兆候 ◦ テストが書いてあるのに実行されてない。 ◦ ソース規模やカバレッジ率が 毎日測定&周知されてない or 不正確。 66
  19. #ccc_g1 published • あるエンジニアのPC上でなら動く • 他のエンジニアのPCでも動くけど 準備に時間がかかる • 検証環境でも動くけど、いま別のチームが テストのために検証環境にデプロイしているのは

    別ブランチのコードなのでやっぱり動かない 「動くソフトウェアで進捗を確認せよ」 しかし、なぜ動かないのか?(再掲) だから手動テストが面倒くさくなる 70
  20. #ccc_g1 published 歴史を紐解いてみる • 2001年: アジャイルソフトウェア宣言 ◦ 「動くソフトウェアこそが最重要な進捗の尺度だ」 • 2004年:

    レガシーコード改善ガイド ◦ 「レガシーコードとはテストが無いコードのことだ」 • 2005年: JUnit4 71
  21. #ccc_g1 published 歴史を紐解いてみる • 2001年: アジャイルソフトウェア宣言 ◦ 「動くソフトウェアこそが最重要な進捗の尺度だ」 • 2004年:

    レガシーコード改善ガイド ◦ 「レガシーコードとはテストが無いコードのことだ」 • 2005年: JUnit4 • この頃無かったもの ◦ 分散VCS(git), AWS-EC2, IaaC, Docker 72
  22. #ccc_g1 published 分散VCS, コンテナ, IaaC時代の手動テスト(案) 1. プルリクエスト毎にそれ専用の検証環境が 自動構築される 2. pushされる都度mergedな状態のコードで

    1の環境にビルド&デプロイされる 3. そのプルリクに対するテストをいつでも誰でもできる a. エンジニア、デザイナー b. 社内の手すきの人員? c. クラウドソーシング? まだ出来てませんが、 目指すところです。 74
  23. #ccc_g1 published • AWS-S3のバケットがうっかりpublic設定 • パスワードを平文でログ出力 • 割賦販売法改正, PCI-DSS…? •

    AWSのシークレットキーを ソースコードに埋め込んだままn年変えてない • JRE/ライブラリ/フレームワークで 脆弱性報告からのバージョンアップ祭り 77
  24. #ccc_g1 published • ベストなのはOS環境変数 • キーを変えたくなってもソースコードを変更する必 要をなくす。 • OS環境変数を変更してアプリケーションを再起動 するだけ。

    • OS環境変数をどう管理するかは別途 ◦ Hashicorp Vault, AWSパラメータストア アプリケーションの外から差し替え可能にする 80
  25. #ccc_g1 published それ、Spring3.1 (2011年)から普通にできるよ! $ export FOO_KEY=HOGE $ java -jar

    application.jar @Component public class FooBean { @Value("${foo.key}") private String key; 1. FOO_KEY は foo.key プロパティに 当てられる 2. 変数 “key” に “HOGE” が入る See: SystemEnvironmentPropertySource.java 81
  26. #ccc_g1 published たとえばlogbackの脆弱性 • logback1.2.0未満にはシリアライゼーション機構に脆弱性が ある • SocketServer と ServerSocketReciever

    要するにソケット通 信でログを中継する機能を使ってる場合は危険 • 逆にいうとそれを使っていなければ大丈夫そうだね 85
  27. #ccc_g1 published たとえばTomcatの脆弱性 @WebServlet (name = "Root", urlPatterns = {

    "/" }) @ServletSecurity( value=@HttpConstraint(rolesAllowed={"admin"}) ) public class HelloServlet extends HttpServlet { 最後に自前でサーブレットクラスを書いたのは いつだっけ?(遠い目) 87
  28. #ccc_g1 published 1. 脆弱性Bはやばい!xxxライブラリを1.1.2まで バージョンアップしよう! 2. あれ?アプリケーションが 動かなくなった...だと?! 3. 1.1.0

    の段階で自分たちが使っている メソッドの仕様が変更されてました。 「リリースノートやChangeLogに 全てが書いてあるなんて誰が言った?」 4. 詰んだ!\(^o^)/ 90
  29. #ccc_g1 published ビズリーチシステムでは • Java6 -> Java8 • Spring 3.0

    -> 3.2 -> 4.0 -> 4.1 -> 4.2 -> 4.3 -> 5.0 • Tomcat6(インストール方式) -> tomcat-embed-8.5 • Maven -> Gradle ◦ Mavenの基本は「先勝ち」でjarが入ってくるため 意図せず古いor新しいjarが使われることがある ◦ Gradleではデフォルト”Newest” 95
  30. #ccc_g1 published 1. 開発環境とCI戦略 2. 老朽化した技術からの脱却 3. フロントエンドと仲良くする 4. 手動テストも、自動テストも。

    5. セキュリティ 6. バージョンアップする 98 おっ、すべて語りきれたかな? 1. 水を飲む 2. 時間を確認 あと何分?
  31. #ccc_g1 published 102 Mac Book Pro 3GHz Core i7 16GB

    memory 250GB SSD Jet Brains All Products Pack ビズリーチのエンジニアとデザイナーの基本装備
  32. #ccc_g1 published A. MacBook Pro + 外付けディスプレイ B. iMac Pro

    + MacBook (Proじゃないやつ) 106 好きなほうを選べ。ってことになってます
  33. #ccc_g1 published リファクタリングとは(若干のこじつけあり) • オワコンは乗り換えるしかない • ゴミは断捨離するしかない • 新しい息吹を吹き込む ◦

    フロントエンド技術、Python... • 遅くなってしまったものを速くする ◦ Maven -> Gradle • CIまで含めて管理しやすくする ◦ Jenkins2 と Jenkinsfile 111
  34. #ccc_g1 published リファクタリングを支えるもの • setup.sh 一発で準備できるローカル開発環境 • 基本FW/ライブラリのこまめなバージョンアップ • 自動テストコード

    • いつでも誰でも確実に手動テストできる環境 ◦ プルリクエストひとつに対して 検証環境ひとつ(を目指してます) 112
  35. #ccc_g1 published 目指す世界線のための布石は? • IaaCとコンテナ技術の知見を身につけておく ◦ ローカル開発環境でDockerを使ってみる ◦ vagrant, docker,

    setup.shの存在理由が “ローカル開発環境構築手順書” (A4 15枚) の代替だけだと思ってると、もったいない。 116
  36. #ccc_g1 published JJUG-CCC-2014 Fall 「JavaでやってみるThe Twelve Factor App」 Y.Watanabe JJUG-CCC-2015

    Fall 「Java8移行から始まる技術的負債との戦い」 Daisuke.Sei JJUG-CCC-2016 Spring 「テストゼロからイチに進むための戦略と戦術」 Y.Watanabe JJUG-CCC-2018 Spring 「JavaでWebサービスを作り続けるための戦略と戦術」 Y.Watanabe 117