新しめのSpringプロダクトの話も、事例の話もおもしろく、Spring Cloudがあるからspring bootでマイクロサービスを作るというのを改めて感じた。
Springコンテナの起動が遅くなる対応にそれぞれ取り組んでいるようで(アメブロのBean化するクラスを絞り込むとか)、Spring 5で入るらしい@Indexedが気になったものの、英語が聞き取れなかった・・・
事例では、Swaggerがどこでも使われている印象だった。
そして、ワークスさんの発表が圧巻だった(開発者2,000名のプロダクトをMonolithic⇒MSA化)。
機能で分割とかドメインで分割といっても、実際どう分割すべきかは分からない。インフラドリブンで分割して具体的な課題に対応していく中で、自然とMSA的なアプローチになっていった。その中で、ドメインとは何かという本質的な議論が徐々にできるようになってきたというのが印象に残った。
途中でデータの分割(検索index含め)は難しいという話が出ていたけれど、現在どうしているんだろうか。
keynote Spring Framework 5.0
- spring 5
- 5.0: JDK 8+, Servlet 3.1+, JUnit 5
- spring 5.1でservlet4.0対応
Performance improvements
- Alternative to component scanning
- Pre-computed index at compilation time: @Indexed
- 具体的な仕組みは聴きとれず・・・
- ASM meta-data cache
- これも分からなかった・・・
- Rewrite of AntPathMatcher
- Zero-copy transfer of org.springframwork.core.io.Resource
JDK 9
- Jigsaw: naming convention based on maven metadata
- symbolic name
- spring-context-4.2.4 RELEASE.jar => spring.context
module.my.app {
requires spring.jdbc;
}
HTTP/2
- HTTPは20年経っている
- Servlet 4.0 - September 2017 -> spring 5.1
- tomcat / jetty / Undertowでは既に使える
Reactive Programming
- more for scalability and stability than for speed
- CPUを最大限活用
Functional-style web routing
Application Re-Architecture Technology
~ StrutsからSpring MVCへ ~
- spring mvcとspring bootでshare 70%超
- 初期開発時に採用されたフレームワークは長期で使われがち
- 初回更改時は、システム基盤更改(OS/HW/MWのEOL対応)と簡単な機能追加にとどまるケースが多い
- 2回目の公開時は、システム全体の刷新を前提にした可初代さんが確保されやすい
- 移行
- strutsはフルスタックなフレームワークではないため、ビジネスロジック以降の構成は様々、汎用的なツール化は困難
- 効率化のための各種ツール、プロセスや実施要項、見積もり方法など多岐にわたる
- そっと移行して延命する?、アクティブに改修していくならいっそ作り直す?
- 移行を機械的に移行すれば、テストを省略できる
- 移行元がSSH構成だと・・・
- Action -> Controllerが一番つらい(Actionをどう使っているかがプロジェクトによって様々)
- ドメイン層は、元々レイヤリングされていれば対処しやすい(refactoringせずに流用する工夫が重要)
- インフラ層は、ORMの親和性が高いものを選べば比較的対応しやすい
事例
- strutsにもspring mvcにもあるが微妙に異なる仕様/挙動のものを、事前に検出して手当てすることが重要
アーキテクチャ面
- session/request格納データの取得方法の違い
- struts1ではrequest/sessionスコープに同じキー名で異なるデータを利用していた
- @ModelAttribtes, @sessionAttributesを採用(スコープの違いはアプリケーション側からは透過的)
- 1 requestで複数のビジネスロジックを呼び出した際に、前段のビジネスロジックの処理結果が引き継げない
- 複数Actionをforwardでchainしていた場合に発生
- Spring MVCでは、modelからrequest copeへのデータ反映はviewの解決時のみ(forward毎にmodelが初期化される)
- forwardをやめて、methodに切替
機能面
- transaction tokenがないことにより、画面遷移の挙動が変わってしまった
- TELASOLUNAで代替機能を開発
- validation
- 正規表現で空文字がチェック対象になる
- 日本語用の独自チェックルール
Cloud Foundry x Wagby
- Cloud Foundry:再起動するとローカルデータが消えるので、本格的な運用には有料サービス必須だが、大企業にはメリットある
- データベース
- ログ保管
- バイナリファイル保存
- 全文検索エンジンのindex
- 10年前からwagby販売開始、直販せず全国20社のパートナーと協業体制
- Cloud Foundry
- ssh, scp, sftpもできる
- アプリケーションを停止、再起動するとdiscの内容はすべて消える(必要なファイルはバックアップ)
- marketplace、manifest.ymlでbindする、push時に設定が自動的に反映される(CloudConfig, Cloud Profile)
- ファイルはネットワークストレージに書くとして、Azure/S3などでプログラムを切り替えないといけない
- SalesforceやKintoneのようなインフラがないwagbyにとっては、Cloud Foundryに乗せれば同等の信頼性を担保できるためありがたい
Spring Security で作る Web API アクセス制御の最適解
〜 Basic認証? APIキー? OAuth 2.0? OpenID Connect? 〜
http://www.slideshare.net/daisuke_m/spring-day-2016-web-api
- 認証と認可
- 認証:通信相手が誰か、確認すること(=なりすましでないこと)
- 認可:リクエストが許可されるかどうかを決めること
- 多くの場合、認証した上で、「その主体が権限を持つかどうか」の観点で認可する
- private networkであれば、認証そもそもいる?、SSLそもそもいる?
- global networkの場合は、SSL/TLS必須(天気予報ですらOpenWeatherMapは最近は認証している)
- 認証:what you are(顔、声、指紋), what you have(身分証、携帯電話), what you know(パスワード、秘密の質問)
- credentialの分類
- 静的・無期限:パスワード
- 静的・有期限:OAuthのアクセストークン・セッションID
- 動的:リクエスト署名(HMAC)
BASIC認証&Digest認証
- ブラウザ:HTTPではパスワードが平文で流れるので論外、ログアウトできない
- APIにおけるBASIC認証 over HTTPS:決定的な否定理由が見つからない
- OAuth2.0でも技術要素として使っている
- ただし、静的・無期限なcredentialが毎回流れるのでAttack Windowが大きい、SSL/TLSとて万能ではない
- Digest認証
- nonceを利用したchallenge&responseでHTTPリクエストが必ず2往復する⇒Web APIには向かない
API署名
- AWSのAPIが採用
- HMAC(ハッシュ関数に基づくメッセージ認証符号)
- 秘密鍵を共有し、元のメッセージを秘密鍵でhash化したものを一緒に送って署名する
- OAuth1.0aはこの仕組みを利用、リクエストごとに計算が必要
- クライアントを神に位置づけるのであれば、適切な選択肢だが標準化が望まれる
OAuth
- 認証をするための仕組みではない、認可をするための仕組みでもない
- Aがあらかじめもっている認可をBに対して部分的に委譲するための仕組み
- アクセストークン:静的だが有期限
- 考え方
- 一般的なアクセス制御はAPI clientが全権を持っていて、エンドユーザは認証/認可をして一部を利用
- OAuthはAPI clientが、エンドユーザから移譲を受けた権限を代理で実行
- Spring Security
- Authentication:認証
- grantedAuthority: エンドユーザが持つ権限(認可)
- oauth2authentication:クライアントの認証
- scope:OAuth2においてエンドユーザから移譲を受けた権限の範囲
- Client Credentials認証においてクライアントが持つ権限(認可):例)twitterのpublic timeline
OpenID Connect
- OAuth2.0よりシンプル、Web APIの認証にはあまり関係ない
- 自身でユーザのパスワードを管理したくないアプリが認証するための仕組み
- OpenID ProviderはID/passを確認したら、「この人は〇〇さんだよ」というデータに電子署名をつけて持ち帰らせる
Let's Visualize Your Spring Cloud Applications!
~ElasticsearchとSleuthを使った可視化の実際〜
https://speakerdeck.com/shintanimoto/lets-visualize-your-spring-cloud-applications-number-jsug-number-springday
マイクロサービス開発の苦悩
- Devの苦悩
- サービスの数が膨れてしまい、サービス同士の依存関係が分からなくなる(open call hierarchyで見られない)
- 改修の影響範囲、エラーの原因、そもそも使われていないサービス
- Opsの苦悩
- 監視すべきサーバやプロセスの数が多い
- ログファイルが分散してしまう、どのログファイルを見ればいいか
- Opsを考慮したDevの苦悩
- 問題の兆候をログから検知しづらい
- 無視していいログか
- ロギング仕様を事前に全て決めきって守らせる?Agilityが落ちる・・・
- 見るものが多い⇒可視化(グラフにしてトレンドを掴むところから始める)
tool群
- サービス間の依存性:Spring Cloud Sleuth + Zipkin
- ボトルネックを探す、サービス間の依存性を把握
- ログファイル:Elasticsearch + Logstash + Kibana + Filebeat
- リソース状況:Elasticsearch + Kibana + Metricbeat
- 1人月くあいあれば組めるよ!
サービス依存性の可視化
- 主に開発者向けの可視化
- Distributed Tracing:リクエストに共通のIDを振って、HTTP/AMQP headerなどで伝播させて処理を追跡
- Spring Cloud Sleuth: filter的に動くイメージ(HTTP request/response時にZipkin Serverに呼び出し情報を送る)
- dependenciesを追加コードの変更は一切なし
- Zipkin
- mainメソッドを持つクラス1つで立ち上がる
ログの可視化
- (問題が起きたときだけでなく)日常的にログを見て想定外の事象が発生していないかを確認することでサービスレベルが格段に上がる
- ごく少数の人がエラーになっているが、問い合わせが来ずに気がつかないこともある
- アプリケーションログの可視化
- システムが安定稼働しているのかを把握
- validationエラーが特定のフォームに偏っていたらおかしい、とか
- アクセスログの可視化
- 適切なレスポンスを返せているのかの把握(400/500を返していないか、レスポンスタイムが大きくなっていないか)
- 急激なアクセス増などが起きていないかの把握(攻撃を受けた)
- Elasticスタック(ELKスタック+Beatsシリーズ)
- Logstash: Filebeatからデータを受け取り、加工して(parseしてjsonに変換)、Elasticsearchに転送する
- kibana: 想定しているものを非表示にしてfilterしていく⇒毎日0件であることを確認すれば想定外の事象が発生していることに気がつける
- Cloud Watch Logsより複雑なfilterをかけられる
- OSSでは他の選択肢がほぼない、商用製品ではSplunkがある
- 雑にログを入れておいて「あとから」集計軸を変えられる(時系列の数値データを記録するツールでは難しい):Elasticsearchの検索の高速さ
システムリソースの可視化
- Zabbix, Mackerel, DataDog, Cloud Watchなどツールは多数ある
- アクセス数とCPU利用率の相関を見たい⇒Elasticsearchで並べてみる
- MetrixbeatはjsonをElasticsearchに送る(Logstashの加工は不要)
- 売上とかも並べてみるとおもしろい
可視化とKafka
- 可視化システムの安定化、情報の欠落を防ぐ
- キューのようなもの、ただし過去1週間分のデータを保持しており、いつでも取り出し可能(AWS Kinesisに近い)
- マイクロサービスとZipkinとの間、Logstashとの間にKafkaを挟む
- Elasticsearch/Logstashを停止する、Logstashの設定誤りを修正するとき
- Spring Cloud Stream
- サービス/ミドルウェアを止めても、メッセージを欠落させることなく処理できるか?
- 実は、BeatsとLogstashの間はバックプレッシャー型の独自プロトコル
Microservices Architectureを超大規模プロジェクトでやってみた
株式会社ワークスアプリケーションズ
https://speakerdeck.com/kingofreverb/microservices-architecturewochao-da-gui-mo-puroziekutodeyatutemita
HUE
- HUE:COMPANYの機能を基本的に全部入れる
- Cloud Native
- 分散処理機構:膨大な量の帳票の集計の高速化(一億明細とか)
- 人工知能:学習に基づく、Input Less化
- 応答速度にコミット:100msで応答するという挑戦
- 超大規模
- 開発者3000+(日本、上海、シンガポール、インド)
- CI 30名、運用チーム 15名
- MSAへの移行は難しい、でも出来る!
- 技術的な難しさは、言うても組み合わせ、実はそんなに新奇性はない
- 精神的な難しさ:「諦める事」への慣れ、「仕様」という言葉の魔法感、パラダイムシフトを許容できない(目に見える範囲で「改善」を積み上げてしまう)
HUEの変遷
- RDBMSからの脱却:Cassandra、Spark
- バージョンアップ:3週間かかる⇒monolithicってこと・・・
- 年に百万ステップとか増える
- ビルドに14時間(テストも5hrかかって流せない)、浪費壁(warを動かすには32GBのメモリが必要)
- サービスとしても、本来は業務単位で切り売りしたかった
dependency戦国時代
- まずは依存性の調査(mvn dependency:tree, Dependency Analyzer)⇒分からない・・・
- レポジトリ分割闘争
- 分割する:管理する単位自体を変えるということ
- 1レポジトリ?細かく分けていくか?(treeは1つにしたかった)
- 1レポジトリ
- インテグレーションレベルでの問題発見の早さ
- cloneに時間がかかる、Googleはbuildツールも自作している
- レポジトリ分割
- clone, build, testが早い
- HUE
- Pre-marge build
- Smoke Test(Dockerベースで起動確認だけする、health check)
- インテグレーションレベルの問題:0から環境を毎日作ってテスト、Immutable Infrastructure
- 分割(「意味ある単位」が分からない)
- 業務機能での何となくの塊(完璧でなくていい)
- 共通機能と業務機能は絶対に別
- 切ってみてから分かることもある
- イテレーション単位で狙いを定めて切り離す
- 共通部分は意外と切り出せた(refactoringは独立宣言後でも大丈夫)
MSA黎明期
- 結局は依存の問題
- 分けられない理由:分けると何が起こるか分からない(どうなっているか分からない不安)
- FWに入っていてもサービスに切り出せるもの(承認ワークフロー、spreadsheet)
- 普通の景色に戻す:ApplicationとLibrary
- Libraryは使われるだけ、中にしか依存がない(ユーティリティなど)
- Applicationはサービスそのもの、Application間はWebAPIでやり取り
- ドメインが分割不可なら、開発組織単位で分ける(Stakeholderが減る)
- データの分割はとてつもなく難しい(承認ワークフローってみんな使うけど誰のもの?)
- 検索Indexどうするかとか
- そろそろ本質に気づいてくる⇒ドメインとは
- 売りたい単位、バージョンアップしたい単位、ライセンスの単位
MSAなう
- インスタンスコストは高い
- Kubernetesを導入
- M/Wリソースは共有できないといけない⇒Multi-tenancy
- AWS LambdaなどのManaged Service活用(時間課金⇒100ms課金)
- 出納大臣(課金API)、性能大臣(負荷試験と緻密なCP)
- war ⇒ Docker
- Dockerfileを成果物にする(Buildまで開発チームで責任もってもらう)
- 運用も移譲
- build:開発者が決める
- deploy:Ansibleベースで環境構築スクリプトも書いてもらう
- Scaling Factorとか非機能要件
- モニタリング:Prometeus
- CIチームの役割
- Jenkinsで魔法をかけない(Monolithicアプリは往々にして魔法でできている)
- インテグレーションレベルでの問題解決
- 開発クオリティを担保する方向へ
まとめ
- インフラドリブンでいく
- Jenkins的なビルドツール
- Gitレポジトリ申請フロー
- 依存性をチェックする仕組み
- レビュー機関
- 製品としてのコンセプトを判断
- Architeccture
- M/Wリソース消費
- 800万⇒20-30万くらい(JSも含めて)に分割
アメブロの大規模システム刷新とそれを支えるSpring
- 5300万人、サーバ数百台、60万リクエスト/m
- ページが見られない = 広告が表示されないため売上に直結
- DB構造や、フロントエンドのHTML/JSは書き換えずに、バックエンドを刷新
- Springを選んだ理由:@Controller, @Service, @Repository⇒レイヤーの共通認識になる
保守性の低下
- レイヤー
- Controller:requestを受けて結果を返す
- Facade:ユーザの一操作に必要なserviceの呼び出し(ブログ投稿⇒記事投稿、twitter連携)
- Service:最低限まとめておくべきビジネスロジック(記事のデータ保存と、記事数のインクリメント)
- Repository
- Springfox + Swagger
- annotationから生成
- endpointのきれいな一覧だけでもありがたい
開発コスト
- ローカル開発環境を構築
- spring bootでfat jarを作成、gradle bootRunで起動
- プロダクトごとにリポジトリが分散:ソースコードもリリースジョブも再利用性が低い
- リポジトリを1つにして、マルチプロジェクト化
- 共通化したことで起動の際に不要なクラスが大量にBean生成される(設定も必要)
- FacadeクラスがAutowiredしているServiceクラスだけをBean化するInitializerを作った
- 宣言した接続先に関連するrepositoryだけをBean化するInitializerを作った
viewとビジネスロジックの強い依存関係
- API Centric
- AMP対応やスマホ版フロントエンド刷新(React/Redux)も特に対応なく完了
- 通信コストの増大
- 複数レイヤにキャッシュを入れて対応
- Repositoryでキャッシュ(データ更新時に消す)
- clientでキャッシュ
- API呼び出し元/先が分かりづらい(method callでなくなったので)
Data Microservices with Spring Cloud Stream, Task, and Data Flow
- Spring XD(eXtreme Data)
- XD Admin、ZooKeeperを介して協調、DI Container間にMessage Brokerが挟まる
- stream1 = http | cassandra
- アーキテクチャが古い、Modern PlatformsではCordinationをしてくれる
- Cloud Native Redesign
- standalone executable applications(spring boot)
- 既存のmodern platformで動かす
- Data Microservices
- unixのpipe
- pipeがMessage Brokers
- spring bootのapplicationをmessage brokerで繋ぐ
- Architeccture
- orchestrate: Spring Cloud Data Flow
- Long Lived Stream Applications: Spring Cloud Stream
- Short Lived Task Applications: Spring Cloud Task
- Spring Cloud Deployer(SPI)
- Spring Integrationのコンポーネントを利用している
Spring Cloud Stream
- source | processor | sink
- @EnableBinding
- Rabbit MQ, Apache KafkaがProduction-Ready
- Reactive API Support
- Fluxを使うとWindow処理ができるようになる(集計とか)
- backpressureがまだ効かない
- Spring Cloud Stream App Service
- Spring Orchestration in SCDF
- maven/docker
- Consumer Group -> Spring Cloud Data Flowでデプロイすると自動でグルーピングされる
- groupの中のどれかにデータが流れる
- Partitioning Support(Stateful Stream)
- Spring Flo: GUI
- Programmable Reactive Processor
Spring Cloud Task
- CommandLineRunner or Spring Batch
- 結果はTask Repositoryに格納される
- cloudにdeployして一瞬実行して終了できる、結果は永続化する
- Spring Cloud Data Flowを使うと、クラウドプラットフォーム上の短命処理として利用できる
Install PCF Dev
- Cloud Foundry on your laptop