3.5K Views
November 15, 14
スライド概要
JJUG-CCC 2014 Fall
Japan Java Users Group Cross Community Conference 2014
Engineer. Java, Kotlin(Server Side), JavaScript, Vue.js, Spring boot, CI/CD tool, build tool, monitoring, and activity as SRE
Javaでやってみる The Twelve Factor App JJUG-‐CCC 2014 Fall R1-‐5 ベルサール西新宿 2014-‐11-‐15(Sat)
自己紹介 • • • • • 渡辺 祐 (株)ビズリーチ @nabedge hCp://mixer2.org hCp://nabedge.blogspot.jp 2
hCp://12factor.net 3
このセッションのポイント 1. 12-‐Factor Appという論文に合わせ、 2. 妄信せず、現実と折り合いをつけ ながら、 3. Java言語 + α でどう実現するか? 4
注意 • 12-‐Factorの主張の是非はここでは議論しない。 • 1要素 ⇔ 1ツール/ライブラリで解決ということはない。 • 12要素全部やらないとダメってことでもない。 • 12-‐factorに書いてある通りにやるわけでもない (臨機応変に解釈してアレンジ) 5
主に使う道具 • • • • • • • Java7 or higher Spring Framework 4.x Spring Boot Tomcat-‐embed 7 or higher maven (gradle) Sonatype NEXUS Jenkins 6
話の順序 VII ポートバインディング IX 廃棄容易性 XI ログ III 設定 V ビルド、リリース、実行の分離 II 依存関係の明示的な宣言 VI ステートレスなプロセス 最後に番外編 7
VII 『ポートバインディングを通じて サービスを公開せよ』 8
VII ポートバインディング • 『Webアプリは自らポートを開いて ユーザーからのリクエストを待て。』 • 『RubyならThin, JavaならJeCyが いいかもね。』 9
Tomcatが好きなので 今回は下記の構成 1. Tomcat embed 8.x (7.xでもよい) 2. Spring Boot 経由で起動 10
結論を先に • [CATALINA_HOME]/webapp に foobar.warを置いてTomcatを 起動する • 開発したアプリの中で、その いち依存ライブラリたるTomcatが ポートを開いてリクエストを待つ。 11
組み込みTomcatとは • Tomcat 7.0.1x (2011年初頭)くらいか ら安定リリース • tomcat-‐embed-‐core-‐7.0.x.jar • tomcat-‐embed-‐logging-‐log4j-‐7.0.x.jar 12
そのまま使おうとすると public stadc void main(String[] args) { Tomcat tomcat = new Tomcat(); tomcat.setPort(8080); tomcat.addWebapp("/”, new File(“/var/webAppDir) .getAbsolutePath()); tomcat.start(); tomcat.getServer().await(); } 13
ここでSpring Boot登場 2014年4月 1.0.0.RELEASE 14
SpringのBeanとしてTomcatを定義 @Configuradon public class WebMvcConfig extends WebMvcConfigurerAdapter { @Bean public EmbeddedServletContainerFactory embeddedServletContainerFactory() { TomcatEmbeddedServletContainerFactory factory = new TomcatEmbeddedServletContainerFactory(); factory.setPort(8080); factory.setUriEncoding(“UTF-‐8”); factory.setContextPath(""); } 15
MainメソッドでSpringを起動 public stadc void main(String[] args) { SpringApplicadonBuilder builder = new SpringApplicadonBuilder(); builder.sources(WebMvcConfig.class); ConfigurableApplicadonContext context = builder.run(args); context.registerShutdownHook(); 16
main()メソッド起動ということは これでローカル環境で Tomcatが8080ポートを開き、 リクエストを待つ。 (本番環境でも同じこと) 17
プラグインは、もう、いらない。 WTP Plugin Sysdeo Tomcat Plugin 18
Tomcatのバージョンアップもお手軽 <dependency> <groupId>org.apache.tomcat.embed</groupId> <ardfactId>tomcat-‐embed-‐core</ardfactId> <version>7.0.56</version> </dependency> <dependency> <groupId>org.apache.tomcat.embed</groupId> <ardfactId>tomcat-‐embed-‐logging-‐log4j</ardfactId> <version>7.0.56</version> </dependency> 19
APサーバという固定観念を捨てよう • Linuxマシンを用意して、Tomcat/GlassFishをイ ンストールして...ビルドジョブを書いて... アプリをデプロイして... 20
IX 『廃棄容易性』 − 高速な起動と グレースフルなシャットダウンを 心がけよ − 21
高速な起動? あきらめよう • 理想: 一ケタ秒 • 現実: 20秒〜30秒 – サンプルアプリ程度なら10秒以内 • Web●ogicよりは速かろう... 22
グレースフルなシャットダウン? • 意訳:「中途半端な残タスクや ゴミ掃除を終えてからアプリを終了さ せましょう」 • きちんとShutdownHookを 指定すればいい。 23
@PreDestroy public void lazyUpdate() { // 例:DB操作を遅延実行するメソッド } @Bean(destroyMethod=“close”) public DataSource dataSource() { // 例:DBのデータソース生成メソッド } 24
Springならメソッド1個呼ぶだけ public stadc void main(String[] args) { SpringApplicadonBuilder builder = new SpringApplicadonBuilder(); builder.sources(WebMvcCfg.class); ConfigurableApplicadonContext context = builder.run(args); // ここで起動 context.registerShutdownHook(); } 25
30秒ほどCMと休憩 1. 水を飲む 2. 時間を確認:15分くらい Javaな人、絶賛採用中 hCp://www.bizreach.co.jp/recruit/ 26
IX 『ログをイベントストリーム として扱え』 27
Twelve-‐Factor Appはアプリケーションの出力ス トリームの送り先やストレージについて一切関 知しない。 アプリケーションはログファイルに書 き込んだり管理しようとするべきではない。代わ りに、それぞれの実行中のプロセスはイベント ストリームをstdout(標準出力)にバッファリング せずに書きだす。ローカルでの開発中、開発者 はこのストリームをターミナルのフォアグラウン ドで見ることで、アプリケーションの挙動を観察 する。 28
とにかくやることはひとつ 「すべてのログを 標準出力に集めよ」 29
アプリはどこで動く? • • • • • • Windows7 + Eclipse OracleVirtualBox + CentOS Dockerコンテナ AWSのEC2 Heroku オンプレミス 30
すべての状況に対応しうるのは 標準出力しかない 31
• 自作アプリのログ • ライブラリ/フレームワーク のログ(Tomcat含む) 標準出力 Fluentd /dev/null rsyslog TreasureData GoogleBigQuery ElasdcSearch 32
※ SYSLOGに中継したい場合 • Logback(Log4jも)のSyslogAppenderはUDPを 使うのでログのロストが怖い • Java Service Wrapper提供のラッパーのログ 中継機能が便利 – Linux,Win,MacOS用の各バイナリあり – 詳しくは hCp://wrapper.tanukisowware.com/ 33
ログの集約は、 ロギングライブラリの集約から。 34
• Spring Framework commons-‐logging • Tomcat log4j または java.udl.logging • 他のライブラリ slf4j、commons-‐logging、log4j • 自分のライブラリ slf4j 35
Slf4jのブリッジライブラリで集約 log4j commons -‐logging log4j-‐over-‐slf4j Slf4j-‐api jcl-‐over-‐slf4j 従来型ライブラリは すべて依存関係から除 外(exclusion)しておく! logback ConsoleAppender 36
SpringBootなら折り込み済み 37
logbackの設定 • すべてConsoleAppender • 以下二つは変数化して外部から調整可能に しておく。 – ログのレベル(DEBUG / INFO) – タイムスタンプの有無 • 例:ローカル環境で開発中はタイムスタンプ有り • 例:本番環境では、ログ集約システムの側で タイムスタンプを付加する 38
ところで、GCログどうする? • フォーマットが独特すぎて 他のログと混ぜると…. • どうしようもないかもしれない。 ログの先頭に任意の固定プレフィクスでも 付けられればいいんですが… • -‐Xloggc:/var/log/gc.log.. とかはもうやめて、 JMX経由でGC状況をモニタリング? 39
III 『設定はOS環境変数に 格納せよ』 40
「設定ファイルもソースコードも 同じツリーでVCSに入れて管理し、 ビルドのたびにjar(war)に含めて いますが、何か?」 本番DBのパスワードも そんな管理でいいの? 41
「本番用の設定ファイルだけは別 のVCSで管理しています。」 1. 面倒くさい 2. そもそも言語/ライブラリ/ フレームワークによって フォーマットが異なる 42
だから「OS環境変数」 1. 2. 3. 4. LinuxでもWindowsでも OS環境変数の設定方法はほとんど同じ。 気の利いたライブラリの多くはOS環境変 数からの設定値読み込みに対応してい る。 対応していなくても起動時のコマンドライ ン引数にOS環境変数をあてるとか。 どうせ ansible/chef/puppet で自動設定 するし。 43
<logger
name=”com.example.myapp"
level="${AP_LOG_LEVEL:-‐DEBUG}"
/>
上の“$AP_LOG_LEVEL”
変数の優先順位
1.
logback.xml上のproperty値
2.
Javaシステムプロパティ
-‐DAP_LOG_LEVEL=INFO
3.
OS環境変数
export
AP_LOG_LEVEL=INFO
4.
上記のどこにも無い場合にはDEBUG
44
自作するアプリ自体の設定値を どうやって可変にするか? 45
SpringBoot流 設定値の埋め込み方 @Component public class Foo { @Value(“foo.bar”) private String bar = “hoge”; // default値 } 46
さっきのfoo.barの値の変え方 1. コマンドライン引数 java –cp … -‐-‐foo.bar=xxxxx //ハイフン2個 2. OS環境変数 3. コマンドラインで指定した位置にある ”applicadon.properdes” 4. クラスパス直下のapplicadon.properdes 5. ソースコード上の値 実際はもう少し細かいので詳細は hCp://docs.spring.io/spring-‐boot/docs/1.1.7.RELEASE/reference/html/boot-‐features-‐external-‐config.html 47
II 『依存関係を明示的に 宣言し分離せよ』 V 『ビルド/リリース/実行は 厳密に分離せよ』 48
それはCM②のあとで 1. 水を飲む 2. 時間を確認:30分くらい 49
Scalaな人も大歓迎 hCp://www.bizreach.co.jp/recruit/ 50
II 『依存関係を明示的に 宣言し分離せよ』 V 『ビルド/リリース/実行は 厳密に分離せよ』 51
まずはビルドツール • バイナリ間の依存関係管理機能 のあるビルドツールを使う Maven Gradle –以上、終わり…でもない(後述) 52
一方、これもある意味NGらしい • 「サーバにリリースだ!」 1. 2. 3. 4. 5. 6. ソースをチェックアウト 設定ファイル差し替え mvn compile package app.warの完成 scpして sshでAPサーバ再起動 53
ビルド、リリース、実行を、分離せよ。 54
ビルドという作業 GOAL パッケージ リポジトリ VCS checkout mvn compile package jar mvn deploy 55
ポイント • 「ビルド」の結果はjar(war)という バイナリ • バイナリは パッケージリポジトリサーバ (以下PKGリポジトリ)にdeploy(格納) しておくものである • MavenもGradleもバイナリ間の依存性 解決にはPKGリポジトリの存在が必須 56
PKGリポジトリを構築しておく • Sonatype NEXUS • Ardfactory – Bintray? • Apache + mod_webdav (おすすめできない) 57
ビルドという作業(再掲) GOAL パッケージ リポジトリ VCS checkout mvn compile package jar mvn deploy 58
リリースという作業 GOAL PKGリポジトリ サーバ上でunzip • 自分が開発したアプリjar • 依存ライブラリjar 1. mvn copy-‐dependencies 2. zip 3. scp 59
実行という作業 “*”(ワイルドカー ド指定)でおk (java6以降) $ java \ –cp “jar群の展開dir/*” \ com.example.MyAppMainClass ※設定値はOS上に環境変数として保存済み 60
1. Javaアプリはバイナリ(jar)の集合体 2. 自分で書いたソースもビルドすれば バイナリ(jar)になる 3. バイナリの間に依存関係がある 4. すべてのバイナリはPKGリポジトリ に格納して管理する (VCSに入れるな!) 61
5. リリース作業では依存関係を たどってバイナリ(群)をPKGリポジ トリから取得してサーバに配置 6. 設定はOS環境変数に書いてある 7. よって、どのサーバにも同じバイナ リを配置して使い回し可能 62
II 依存関係の管理 III 設定 V ビルド、リリース、実行の分離 Javaアプリでは、下の4つ全てが絡み合って 3要素が成立 1. ビルドツール 2. PKGリポジトリ 3. OS環境変数(chef/puppet/ansible) 4. CIツール 63
VI 『アプリケーションをステートレスな プロセスとして実行せよ』 64
しつこいようですがCMです 時間を確認:45分くらい 65
ちょっとくらい興味あるかもという方に CTO トップライブ 11/30(Sun) − キャリア採用イベント − hCp://www.bizreach.co.jp/recruit/event 66
VI 『アプリケーションをステートレスな プロセスとして実行せよ』 67
ポイント 「ステートレスなプロセス」 ≒ sdckyセッション禁止 68
いろいろ難しいので • Sdckyセッション + ELB でいいのでは。 • Tomcatのセッションストアプラグイン (keyValue, RDBMS)は serialize/deserialize がらみでいろいろトラブル • 今回は、いくつかのプランの紹介のみ 69
プランA: Stateless Session Cookie • セッション情報自体を暗号化してCookie の値に埋め込む手法 • Play Frameworkが採用 • 大きな情報をセッションに格納できない 70
プランB: Memcached • memcached-‐session-‐manager – Tomcatのvalveとして使うプラグイン – SpringFrameworkとは無関係に動かせる • あくまで個人的な検証結果: – SpringMVC + SpringSecurity + FlashScope変数を 組み合わせるとserialize失敗の例外が出やすい 71
プランC: Spring-‐Session • Spring-‐session • Spring-‐session-‐redis • どちらも正式リリース前 • 未検証(だれか人柱頼む) 72
番外編 -‐ 12-‐Factorには書いてないけれど -‐ 73
*.war, web.xml, JSP禁止 あくまでも個人的な意見です • Servlet3.xでGO • src/main/webapp すら無しの方向で • ビューはThymeleafかMixer2で。 – src/main/resources/templates に配置 • こうすると、もうwarに固める必要なし。 • *.jar群をクラスパスに入れてjavaコマンドで 起動(単純!) – SpringBootの独自one-‐jar形式は、、、 74
まとめ、、、ている時間は たぶんないだろう 75
ありがとうございました! 76