Javaでやってみる The Twelve Factor App JJUG-CCC 2014 Fall 講演資料
Post on 27-Jun-2015
4305 Views
Preview:
DESCRIPTION
Transcript
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登場
14
2014年4月 1.0.0.RELEASE
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()メソッド起動ということは
17
これでローカル環境で Tomcatが8080ポートを開き、 リクエストを待つ。 (本番環境でも同じこと)
プラグインは、もう、いらない。
18
Sysdeo Tomcat Plugin
WTP Plugin
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
public stadc void main(String[] args) { SpringApplicadonBuilder builder = new SpringApplicadonBuilder(); builder.sources(WebMvcCfg.class); ConfigurableApplicadonContext context = builder.run(args); // ここで起動 context.registerShutdownHook();
}
25
Springならメソッド1個呼ぶだけ
30秒ほどCMと休憩
1. 水を飲む 2. 時間を確認:15分くらい
26
Javaな人、絶賛採用中 hCp://www.bizreach.co.jp/recruit/
IX 『ログをイベントストリーム として扱え』
27
Twelve-‐Factor Appはアプリケーションの出力ストリームの送り先やストレージについて一切関知しない。 アプリケーションはログファイルに書き込んだり管理しようとするべきではない。代わりに、それぞれの実行中のプロセスはイベントストリームをstdout(標準出力)にバッファリングせずに書きだす。ローカルでの開発中、開発者はこのストリームをターミナルのフォアグラウンドで見ることで、アプリケーションの挙動を観察する。
28
とにかくやることはひとつ
「すべてのログを 標準出力に集めよ」
29
アプリはどこで動く?
• Windows7 + Eclipse • OracleVirtualBox + CentOS • Dockerコンテナ • AWSのEC2 • Heroku • オンプレミス
30
31
すべての状況に対応しうるのは 標準出力しかない
32
• 自作アプリのログ • ライブラリ/フレームワーク
のログ(Tomcat含む) 標準出力
Fluentd
TreasureData GoogleBigQuery
rsyslog
/dev/null
ElasdcSearch
※ 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のブリッジライブラリで集約
36
log4j log4j-‐over-‐slf4j
commons -‐logging jcl-‐over-‐slf4j
Slf4j-‐api
logback
ConsoleAppender 従来型ライブラリは すべて依存関係から除外(exclusion)しておく!
SpringBootなら折り込み済み
37
logbackの設定
• すべてConsoleAppender • 以下二つは変数化して外部から調整可能に
しておく。 – ログのレベル(DEBUG / INFO) – タイムスタンプの有無
• 例:ローカル環境で開発中はタイムスタンプ有り
• 例:本番環境では、ログ集約システムの側で タイムスタンプを付加する
38
ところで、GCログどうする?
• フォーマットが独特すぎて 他のログと混ぜると….
• どうしようもないかもしれない。 ログの先頭に任意の固定プレフィクスでも 付けられればいいんですが…
• -‐Xloggc:/var/log/gc.log.. とかはもうやめて、
JMX経由でGC状況をモニタリング?
39
III 『設定はOS環境変数に 格納せよ』
40
41
「設定ファイルもソースコードも 同じツリーでVCSに入れて管理し、 ビルドのたびにjar(war)に含めていますが、何か?」
本番DBのパスワードも そんな管理でいいの?
42
「本番用の設定ファイルだけは別のVCSで管理しています。」
1. 面倒くさい 2. そもそも言語/ライブラリ/
フレームワークによって フォーマットが異なる
だから「OS環境変数」
1. LinuxでもWindowsでも OS環境変数の設定方法はほとんど同じ。
2. 気の利いたライブラリの多くはOS環境変数からの設定値読み込みに対応している。
3. 対応していなくても起動時のコマンドライン引数にOS環境変数をあてるとか。
4. どうせ ansible/chef/puppet で自動設定するし。
43
<logger name=”com.example.myapp" level="${AP_LOG_LEVEL:-‐DEBUG}" />
44
上の“$AP_LOG_LEVEL” 変数の優先順位 1. logback.xml上のproperty値 2. Javaシステムプロパティ -‐DAP_LOG_LEVEL=INFO 3. OS環境変数 export AP_LOG_LEVEL=INFO 4. 上記のどこにも無い場合にはDEBUG
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. ソースコード上の値
47
実際はもう少し細かいので詳細は hCp://docs.spring.io/spring-‐boot/docs/1.1.7.RELEASE/reference/html/boot-‐features-‐external-‐config.html
II 『依存関係を明示的に 宣言し分離せよ』
V 『ビルド/リリース/実行は 厳密に分離せよ』
48
49
それはCM②のあとで
1. 水を飲む 2. 時間を確認:30分くらい
50
Scalaな人も大歓迎
hCp://www.bizreach.co.jp/recruit/
II 『依存関係を明示的に 宣言し分離せよ』
V 『ビルド/リリース/実行は 厳密に分離せよ』
51
まずはビルドツール
• バイナリ間の依存関係管理機能のあるビルドツールを使う Maven Gradle
– 以上、終わり…でもない(後述)
52
一方、これもある意味NGらしい
• 「サーバにリリースだ!」 1. ソースをチェックアウト 2. 設定ファイル差し替え 3. mvn compile package 4. app.warの完成 5. scpして 6. sshでAPサーバ再起動
53
54
ビルド、リリース、実行を、分離せよ。
ビルドという作業
55
VCS
checkout
mvn compile package
jar mvn deploy
パッケージ リポジトリ
GOAL
ポイント
• 「ビルド」の結果はjar(war)という バイナリ • バイナリは
パッケージリポジトリサーバ (以下PKGリポジトリ)にdeploy(格納) しておくものである • MavenもGradleもバイナリ間の依存性
解決にはPKGリポジトリの存在が必須
56
PKGリポジトリを構築しておく
• Sonatype NEXUS • Ardfactory – Bintray?
• Apache + mod_webdav (おすすめできない)
57
ビルドという作業(再掲)
58
VCS
checkout
mvn compile package
jar mvn deploy
パッケージ リポジトリ
GOAL
リリースという作業
59
PKGリポジトリ
1. mvn copy-‐dependencies 2. zip 3. scp
サーバ上でunzip
• 自分が開発したアプリjar • 依存ライブラリjar
GOAL
実行という作業
60
$ java \ –cp “jar群の展開dir/*” \ com.example.MyAppMainClass
※設定値はOS上に環境変数として保存済み
“*”(ワイルドカード指定)でおk (java6以降)
1. Javaアプリはバイナリ(jar)の集合体
2. 自分で書いたソースもビルドすれば バイナリ(jar)になる
3. バイナリの間に依存関係がある
4. すべてのバイナリはPKGリポジトリ
に格納して管理する (VCSに入れるな!)
61
5. リリース作業では依存関係を たどってバイナリ(群)をPKGリポジ
トリから取得してサーバに配置
6. 設定はOS環境変数に書いてある
7. よって、どのサーバにも同じバイナ
リを配置して使い回し可能
62
63
II 依存関係の管理 III 設定 V ビルド、リリース、実行の分離
Javaアプリでは、下の4つ全てが絡み合って 3要素が成立
1. ビルドツール 2. PKGリポジトリ 3. OS環境変数(chef/puppet/ansible) 4. CIツール
VI 『アプリケーションをステートレスなプロセスとして実行せよ』
64
65
しつこいようですがCMです
時間を確認:45分くらい
66
CTO トップライブ 11/30(Sun)
hCp://www.bizreach.co.jp/recruit/event
ちょっとくらい興味あるかもという方に
− キャリア採用イベント −
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
73
番外編 -‐ 12-‐Factorには書いてないけれど -‐
*.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
top related