Report to Spring Developer from CyberAgent

Preview:

Citation preview

CyberAgent, Inc. All Rights Reserved

Report to Spring Developer from CyberAgent

自己紹介

● 池田 裕介(@yukung)

● 株式会社サイバーエージェント 技術本部

● サーバサイドエンジニア

● Ameba スマートフォンプラットフォームのAPI設計や

運用を担当Java や Groovy の

コミュニティによく出没します

アジェンダ

● Ameba スマートフォンプラットフォーム

● 抱えていた問題

● 解決策としての Spring Boot と Docker

● 導入結果

● Spring Boot + Docker 3分クッキング

CyberAgent, Inc. All Rights Reserved

Ameba スマートフォンプラットフォーム

Ameba スマートフォンプラットフォーム

Ameba スマートフォンプラットフォーム

● スマホ向けコミュニティサービス+ソーシャルゲームのプ

ラットフォーム

● 2012年4月スタート

● 延べサービス数

○ 約300サービス(2015年8月時点)

Ameba 上のサービス提供フロー

ディベロッパー

App App App

2.開発Ameba Developer Center 1.登録

3.申請

プラットフォームで提供するサービスの情報を集約・管理し、審査や公開を行う 5. 公開

認証システム

課金システム

分析クラスタ

ソーシャルグラフAPI

Ameba スマートフォンプラットフォーム

4. データ連携

CyberAgent, Inc. All Rights Reserved

Ameba Developer Center

Ameba Developer Center

サンドボックス環境の提供

Ameba Developer Center の主な役割

ディベロッパー申請・契約

提供アプリケーションの審査・公開・停止

API 仕様・規約等の各種ドキュメントの提供

各種KPIレポートの提供(PV, DAU/MAU, 消費額など)

プラットフォームからのお知らせ・ニュース

障害情報・システムステータスの開示

問い合わせフォーム

サンドボックス環境の提供

ディベロッパー申請・契約

提供アプリケーションの審査・公開・停止

API 仕様・規約等の各種ドキュメントの提供

各種KPIレポートの提供(PV, DAU/MAU, 消費額など)

プラットフォームからのお知らせ・ニュース

障害情報・システムステータスの開示

問い合わせフォーム

運用に問題があった場合の影響

ディベロッパーの業績に影響

正確なデータ処理

安定したサービスの提供

障害からの迅速な復旧

CyberAgent, Inc. All Rights Reserved

抱えていた問題

保守・運用コストの増大

設定やプロビジョニングの暗黙知化・属人化

保守・運用コストの増大

● サービスのレガシー化

○ フレームワークがメンテされていない

○ 積み重なった場当たり対処

● 冗長なシステムアーキテクチャのオーバーヘッド

設定やプロビジョニングの暗黙知化・属人化

● フレームワークやミドルウェアの設定値がアヤシイ

○ 一貫性がない

● 秘伝のタレ化した Chef cookbook やデプロイスクリプト

○ 怖くて sudo chef-client 叩けない

増え続ける技術的負債

CyberAgent, Inc. All Rights Reserved

リファクタリングしよう!

現チーム構成

Ameba Developer Center

サーバサイドエンジニア

他サービス他サービス

インフラエンジニア

フロントエンドディベロッパー デザイナー

ディレクター

サーバサイドエンジニア

フロントエンドディベロッパー

サーバサイドエンジニア

デザイナー

ディレクター

インフラエンジニア

1人 ><

昔は居たんだけどね…

兼務です!

https://www.flickr.com/photos/cozycozy/4300869920/

Ameba Developer Center の主要スキルセット

● サーバサイド Java(REST API)

○ 内製フレームワーク

● Node.js(Web UI)

○ Express フレームワーク

● フロントエンド

○ Jade, Stylus, jQuery…

所属組織のスキルセット

● 諸事情により Java エンジニアが多い

● システムの特性上、それ自身は利益を上げない

○ 内製フレームワークの学習コスト

○ 外注は難しい…

● では内製?

○ Node.js と Java 両方のスキルが必要

リソースという制約

増えない仲間

CyberAgent, Inc. All Rights Reserved

困った…

アプリケーション用データベース

外部向けAPI サーバ

KPI 取得用API

認証システム

全体アーキテクチャ

ソーシャルグラフAPI

課金システム

経理システム

社内向け管理画面

集計用バッチサーバ

社内向けAPI サーバ

Ameba Developer Center

ディベロッパー向け画面

アプリケーション用データベース

外部向けAPI サーバ

KPI 取得用API

認証システム

全体アーキテクチャ

ソーシャルグラフAPI

課金システム

経理システム

社内向け管理画面

集計用バッチサーバ

社内向けAPI サーバ

Ameba Developer Center

ディベロッパー向け画面

アプリケーション用データベース

Ameba Developer Center アーキテクチャ

集計用バッチサーバ

ディベロッパー向け画面

Webサーバ

外部向けAPI サーバ

社内向け管理画面Webサーバ

社内向けAPI サーバ

Ameba Developer Center アーキテクチャ

Ameba Developer Center

ディベロッパー向け画面

社内向け管理画面

InternalAPI サーバ

OracleDatabase

自作バッチフレームワーク

ExternalAPI サーバ

Jackson

こ…これ1人で見るの><

CyberAgent, Inc. All Rights Reserved

そもそも作りなおした方がよくね?

http://martinfowler.com/bliki/SacrificialArchitecture.html

犠牲的アーキテクチャ

● 少し前に話題になった考え方

○ “作っているものを捨てることになる現状を受け入れ

る”

○ “捨ててしまうソフトウェアも多くの価値を生み出しうる

と認めること”

○ “犠牲的アーキテクチャのコードを書いたチームが、そ

の潮時を決める”

http://capsctrl.que.jp/kdmsnr/wiki/bliki/?SacrificialArchitecture

よし、作りなおそう!

解決に向けたアプローチ

● コストとリソースのバランス崩壊

○ 要件や勘所は自分が一番良く知っている

○ 内製フレームワークからの脱却

○ 生産性が高いものを

解決に向けたアプローチ

● 知識やノウハウの暗黙知化・属人化

○ 時間の経過とともに積み上がるもの

■ ドキュメント化をし続けるのは結構大変

○ 暗黙知を(できるだけ)状態として管理しない

■ コードで宣言的にする

■ 環境変数、設定ファイル、イメージファイル

○ GHE 上で変更の差分も追える

CyberAgent, Inc. All Rights Reserved

これが実現できそうなもの

アプリケーション用データベース

Ameba Developer Center アーキテクチャ(再掲)

集計用バッチサーバ

ディベロッパー向け画面

外部向けAPI サーバ

社内向け管理画面

内部向けAPI サーバ

アプリケーション用データベース

新アーキテクチャ

集計用バッチサーバ

社外用Dockerコンテナ

Single Page Application + REST API サーバ

社内用Dockerコンテナ

Single Page Application + REST API サーバ

Dockerコンテナ

Dockerコンテナ

Dockerコンテナ

Dockerコンテナ

新アーキテクチャ技術スタック

社外用Dockerコンテナ

Single Page Application + REST API サーバ

社内用Dockerコンテナ

Single Page Application + REST API サーバ

コンテナ管理はDocker Compose で!

CyberAgent, Inc. All Rights Reserved

どうなったか

作りなおした結果

サービスのレガシー化

少ない人数(実質1人)で技術的負債が返済できた→ Spring Boot の高い生産性のおかげ

冗長なアーキテクチャ

アーキテクチャをシンプルにでき、メンテも楽ちん→ Spring Boot の柔軟さのおかげ

設定やプロビジョニングの

暗黙知

Spring profile や Dockerfile で秘伝のタレをなくし、Docker Compose で安心してプロビジョニング→ Spring Boot と Docker のポータビリティのおかげ

システムそのもののポータビリティも高まり、Cloud Ready に

Spring Boot + Dockerさまさまです 

CyberAgent, Inc. All Rights Reserved

Spring Boot + Docker3分クッキング

くらいかも?

Spring Boot + Docker Compose レシピ

1. Spring Boot でアプリのプロジェクトを作る

2. Maven / Gradle で build して実行可能 jar を作る

○ mvn package か ./gradlew build

3. アプリコンテナ用の Dockerfile を書く

4. 各コンテナを制御する docker-compose.yml を書く

5. docker-compose up -d!

FROM java:8 EXPOSE 8080 WORKDIR /opt/webapp ADD build/libs/external-1.0.0.jar /opt/webapp/external.jar ENTRYPOINT [“java”,“-Djava.security.edg=file:/dev/./urandom”, “-jar”, “external.jar”]

Dockerfile

Ameba Developer Center の例

external: build: ./external ports: - 8080:8080 envirionment: _JAVA_OPTIONS: -Xms512m -Xmx1g command: --spring.profiles.active=development links: - mysql - memcachedinternal: build: ./internal ports: - 8081:8080 envirionment: _JAVA_OPTIONS: -Xms512m -Xmx1g command: --spring.profiles.active=development links: - mysql - memcached

docker-compose.ymlmysql: image: mysql:5.6 ports: - 3306:3306 envirionment: MYSQL_ROOT_PASSWORD: "" MYSQL_USER: dbuser MYSQL_PASSWORD: pass MYSQL_ALLOW_EMPTY_PASSWORD: yesmemcached: image: memcached:latest ports: - 11211:11211

Spring の profile を指定

CyberAgent, Inc. All Rights Reserved

サービスの運用について

自己紹介

● 山田 岳人

● Amebaポイント事業室

● ポイントサービス運用

コンテンツ

1. Spring Boot 導入の背景

2. Spring Boot Actuator による運用

a. ヘルスチェック

b. 充実した統計情報

c. リモートシェル

3. まとめ

導入の背景

コレ

Amebaのポイントサービス

https://d-money.jp

2015年4月にローンチした、Amebaの内外で

利用可能な、新しいポイントサービス。

ユーザー

Ameba

1. ポイントユーザー向けのWEBサイト

a. 1,000,000 PV/Day2. Amebaサービスや提携企業向けのAPI

a. 1,000,000 Req/Dayb. 1,000 Req/Sec

→ それなりの規模

システム要件

ユーザー

Ameba

提携企業

セキュリティ

資金決済法

セキュリティ要件

● 仮想通貨の性質を持つため、利用できる

ミドルウェア等にも制限

● ポイント事業をBtoBとして提供するため、ダウ

ンタイムは賠償問題

● 法的にも定期的な監査があり、

1円のズレで業務改善命令

諸事情につき3ヶ月でリリースしたい

諸事情につきエンジニアは3人で

● JVM と Spring Framework の信頼性

● Spring Boot の高い生産性と保守性

● 既存サービスの Javaリソースの再利用

● 深夜残業

● 休日出勤

→ Spring Boot の選定に至る

どう解決するか

Spring Boot の恩恵

● Spring Boot + Lombok + Gradleでの

少ないコード量・高い生産性

● Spring Boot Actuator、CLI等による

充実した開発・運用ツール

Spring Boot の恩恵

● Spring Boot + Lombok + Gradleでの

少ないコード量・高い生産性

● Spring Boot Actuator、CLI等による

充実した開発・運用ツール

↑今回のテーマはこっち

ユーザー

Ameba

提携企業

セキュリティ

資金決済法

業務改善

損害賠償

ユーザー

Ameba

提携企業

セキュリティ

資金決済法

Spring BootActuator

Spring BootActuator

Spring Boot Actuator とは

1. アプリケーションの保全性や統計情報を監視

するための、Spring Bootの追加機能

2. HWリソースの他、 Spring Boot で設定された

DBやAPサーバーの情報を自動で収集

3. 一般的な HTTP によるエンドポイントの他、 JMX や SSH でも利用可能

Gradle なら以下の定義を追加。

dependencies { compile "org.springframework.boot:spring-boot-starter-actuator" }

Actuator の導入

Maven なら以下の定義を追加。

<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> </dependencies>

Actuator の導入

これだけ

CyberAgent, Inc. All Rights Reserved

ヘルスチェック

/health により以下のような情報を取得できる

{ "status": "UP", "diskSpace": {"status": "UP", "free": 152873189376,"threshold": 10485760}, "db": {"status": "UP", "database": "MySQL","hello": 1}, "redis": {"status": "UP", "version": "3.0"} }

ヘルスチェック

ヘルスチェック

{ "status": "UP", "diskSpace": {"status": "UP", "free": 152873189376,"threshold": 10485760}, "db": {"status": "UP", "database": "MySQL","hello": 1}, "redis": {"status": "UP", "version": "3.0"} }

1. アプリケーションの状態

2. ディスクの空き容量

3. バリデーションクエリの結果

HealthIndicatorの実装で拡張も容易

ヘルスチェックの拡張

public class MemcachedHealthIndicator extends AbstractHealthIndicator { @Autowired private MemcachedClient client;

@Override protected void doHealthCheck(Health.Builder builder) throws Exception { Assert.notNull(client.getConnector()); builder.up().withDetail("threadCount", client.getThreadCount()); }}

Memcached の Indicator を作成してみる

ヘルスチェックの拡張

public class MemcachedHealthIndicator extends AbstractHealthIndicator { @Autowired private MemcachedClient client;

@Override protected void doHealthCheck(Health.Builder builder) throws Exception { Assert.notNull(client.getConnector()); builder.up().withDetail("threadCount", client.getThreadCount()); }}

Memcached の Indicator を作成してみる

1. HealthIndicatorの実装

2. 任意の情報を付加

以下のように拡張して使用してみる

{ "status": "UP", "diskSpace": {"status": "UP", "readOnly": false, "free": 152873189376, "threshold": 10485760,}, "db": {"status": "UP", "database": "MySQL","hello": 1}, "redis": {"status": "UP", "version": "3.0"}, “memcached”: {"status": "UP", "version": "3.0"} }

ヘルスチェックの拡張

1. DiscSpaceIndicatorの拡張

2. MemcachedIndicatorの作成

/health でできること

1. 実装なしで利用できるヘルスチェックに最適な

エンドポイント

2. HWリソースの他、Spring Bootで自動設定さ

れたDB等の保全性の情報も自動で収集

3. HealthIndicator の実装により拡張も容易

CyberAgent, Inc. All Rights Reserved

メトリクス

こんなグラフを出している

/metricsにより以下のような情報を取得できる

メトリクス

{ "mem": 8178944, "mem.free": 5761390, "processors": 4, "uptime": 123532217, "instance.uptime": 123511729, "systemload.average": 0, "heap.committed": 8178944, "heap.init": 8388608, "heap.used": 2417553, "heap": 8178944, "threads.peak": 100, "threads.daemon": 88, "threads": 91, "classes": 9240, "classes.loaded": 9240, "classes.unloaded": 0,   ・・(略)・・

}

{   ・・(略)・・

"datasource.primary.active": 0, "datasource.primary.usage": 0, "counter.status.200.account.id": 24, "counter.status.200.account.id.deposit": 12, "counter.status.200.internal-query.account.id.deposit": 12535, "counter.status.200.internal.account.id": 3934268, "counter.status.200.internal.account.id.accumulation": 12751, "counter.status.200.internal.account.id.achievement": 157807, "counter.status.200.internal.account.id.deposit": 259011, "counter.status.200.internal.account.id.expiration": 3908684, "counter.status.200.internal.account.id.withdrawal": 12228, "counter.status.200.internal.id.notification": 92097, "counter.status.200.internal.allcomponent": 2, "counter.status.200.internal.component": 612553, "counter.status.200.manage.health": 101106, "counter.status.400.internal.account.id.deposit": 11, "counter.status.400.internal.account.id.withdrawal": 11, "gauge.response.account.id": 3, "gauge.response.account.id.deposit": 17,   ・・(略)・・

}

System metrics

1. トータルやフリーのメモリ量

2. サーバーやアプリケーションの起動時間

3. サーバーのロードアベレージ

4. init, commited, used等のヒープ情報

5. peak, deamon等のスレッド情報

6. クラスロード情報

7. GCの時間や回数       …等々

Datasource metrics, Tomcat metrics

1. 有効なDB接続数

2. 使用中のDB接続プールの接続数

3. Tomcatの有効セッション数

4. Tomcatの最大セッション数

CounterService, GaugeService

1. エンドポイントとステータス毎の件数

2. エンドポイント毎の応答時間

こんなグラフを出している

エンドポイント毎に

PublicMetricsでメトリクスの追加も容易

Memcached の Metrics を作成してみる

メトリクスの拡張

public class MemcachedMetrics implements PublicMetrics { private MemcachedContext context; @Override public Collection<Metric<?>> metrics() { List<Metric<?>> metrics = new ArrayList<Metric<?>>(2); metrics.add(new Metrics<Integer> ("active", context.getActive())); return metrics; } }

Memcached の Metrics を作成してみる

メトリクスの拡張

public class MemcachedMetrics implements PublicMetrics { private MemcachedContext context; @Override public Collection<Metric<?>> metrics() { List<Metric<?>> metrics = new ArrayList<Metric<?>>(2); metrics.add(new Metrics<Integer> ("active", context.getActive())); return metrics; } }

1. PublicMetricsの実装

2. Key-Valueで情報を追加

GaugeServiceでゲージの追加も容易

CounterServiceでカウンタの追加も容易

提携先企業様毎のAPI呼び出し

カウンタの拡張

@Service public class ClientCounterService { @Autowired private CounterService counterService;

public void callApi(String clientId) { counterService.increment("services.client.invoked." + clientId); } }

提携先企業様毎のAPI呼び出し

カウンタの拡張

@Service public class ClientCounterService { @Autowired private CounterService counterService;

public void callApi(String clientId) { counterService.increment("services.client.invoked." + clientId); } } 1. 任意の文字列で

こんなグラフを出している

提携先毎のAPIコール数

/metrics でできること

1. 実装なしで利用できる、システムの統計情報取

得に最適なエンドポイント

2. Spring Boot で自動設定されたDBやAPサー

バー等のメトリクス情報も自動で収集

3. 簡単な実装で拡張も容易

ID Description

/autoconfig 自動設定される全ての情報を取得

/beans SpringのBean情報を全て取得

/configprops @ConfigurationProperties で設定された全ての値を取得する

/env アプリケーションの環境情報を取得

/mappings @RequestMappings のパスの一覧を取得

開発で便利なエンドポイント

ID Description

/health アプリケーションのヘルス情報を取得

/metrics アプリケーションのメトリクス情報を取得

/trace 直近のHTTPリクエスト情報を取得

/dump スレッドダンプを取得する

/shutdown アプリケーションをgracefulに停止する

運用で便利なエンドポイント

いずれのエンドポイントも実装なしで利用できて、簡単な実装で拡張も容易

CyberAgent, Inc. All Rights Reserved

リモートシェル

Gradle なら以下の定義を追加。

dependencies { compile "org.springframework.boot:spring-boot-starter-remote-shell" }

Remote shell の導入

Maven なら以下の定義を追加。

<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-remote-shell</artifactId> </dependency> </dependencies>

Remote shell の導入

これだけ

g-mbp:~ gakuto$ ssh -p 2000 user@localhost . ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v1.3.0.RELEASE) on g-mbp:>

Remote shell の起動

> metrics

> dashboard

驚くほど豊富な機能がある詳しくは...

> help

JavaやGroovyでコマンドの追加も容易

class changeMode { @Usage("Change Mode") @Command def change(InvocationContext context) { return CacheContext.changeMode() } }

Remote shell Command の作成

JavaやGroovyでコマンドの追加も容易

class changeMode { @Usage("Change Mode") @Command def change(InvocationContext context) { return CacheContext.changeMode() } }

Remote shell Command の作成

@Commandを付けるだけ

● Actuator の機能を対話的に実行できる

● SSH で繋ぐことにより、サーバー管理者がより

手軽に、より安全に利用できる

● JavaやGroovyによるコマンドの追加も容易

Remote shellのススメ

CyberAgent, Inc. All Rights Reserved

まとめ

● Spring Boot + Lombok + Gradleでの

少ないコード量・高い生産性

● Spring Boot Actuator、CLI等による

充実した開発・運用ツール

まとめ

まとめ

● Spring Boot + Lombok + Gradleでの

少ないコード量・高い生産性

● Spring Boot Actuator、CLI等による

充実した開発・運用ツール

↑今回のテーマはこっち

まとめ

● Actuatorの充実した機能により、

少ない工数で安全なシステム運用を実現

● 面倒な運用ツールの心配が少ないので、

短い開発期間でも事業的要件の機能化に

コミットすることができる

ご清聴ありがとうございました