アドテク×Scala×パフォーマンスチューニング

10 675 vues

Publié le

アドテク×Scala meetup 2014-11-20
http://connpass.com/event/8384/

Publié dans : Logiciels
0 commentaire
57 j’aime
Statistiques
Remarques
  • Soyez le premier à commenter

Aucun téléchargement
Vues
Nombre de vues
10 675
Sur SlideShare
0
Issues des intégrations
0
Intégrations
3 157
Actions
Partages
0
Téléchargements
64
Commentaires
0
J’aime
57
Intégrations 0
Aucune incorporation

Aucune remarque pour cette diapositive

アドテク×Scala×パフォーマンスチューニング

  1. 1. アドテクパフォーマンス × チューニング Scala × ~ パフォーマンス改善の心構えと勘ドコロ ~ アドテク × Scala meetup 2014-11-20 @mogproject
  2. 2. Agenda Demand Side Science の紹介 パフォーマンスチューニング概論 開発フェーズ別のポイント
  3. 3. 自己紹介 水谷 陽介 (@mogproject) Scala Conference in Japan 2013 の スタッフ参加を機に 2013年4月 Demand Side Science 入社 フルスタックエンジニア (笑) 前職は SIer のインフラエンジニア (9年)
  4. 4. http://about.me/mogproject
  5. 5. http://mogproject.blogspot.jp
  6. 6. http://demand-side-science.jp/blog
  7. 7. http://functional-news.com
  8. 8. DSS のこれまで 2012年11月 Demand Side Science 株式会社設立 Demand × Side × Science
  9. 9. DSS のこれまで 2013年 プライベート DSP パッケージ fractale を開発 Demand × Side × Platform
  10. 10. DSP とは リアルタイム広告取引 (RTB) における、広告主側のシステム Supply Side Platform
  11. 11. DSS のこれまで 2013年12月 インターネット広告代理店 オプト グループに参入 2014年10月 ダイナミック・クリエイティブツール unis 提供開始 × ×
  12. 12. unis ロジックに従って、動的に広告を生成 http://www.opt.ne.jp/news/pr/detail/id=2492
  13. 13. DSS のこれから ベンチャーマインドとオプトグループの 強みを活かしつつ Demand × Side × Science
  14. 14. DSS のこれから サイエンスを軸に 様々なプロダクトを生み出していきたい ??? × ??? × Science
  15. 15. DSS のこれから みんなが 広告主×メディア× 利用者 Marketer Publishers Consumer
  16. 16. DSS のこれから ハッピーになれる世界を目指して Win × Win × Win
  17. 17. DSS と Scala Demand Side Science は 創業以来× 全てのプロダクトで × Scala を採用しています
  18. 18. システム構成イメージ RDBMS NOSQL ログストレージ キャッシュ ログ集計 機械学習 キャッシュ生成 etc.
  19. 19. システム構成イメージ JavaScript のチューニングについては 今回は触れません
  20. 20. Agenda Demand Side Science の紹介 パフォーマンスチューニング概論 開発フェーズ別のポイント
  21. 21. パフォーマンスチューニングの目的 システムの課題の解消 インフラコストの削減
  22. 22. システムの課題の解消 高負荷時のアプリケーションの挙動に問題発生 ある条件下でレイテンシー(応答時間) が悪化 バッチ処理の所要時間が目標を超過 開発ツールの動作が遅い
  23. 23. インフラコストの削減 アドテク分野では特に重要 コストが肥大化しがち 大量トラフィック 厳しい応答性能 大規模データベース・ログデータ 配信量増による利益 > インフラ投資額 でなければ、そもそもビジネスが成り立たない
  24. 24. 目的を見失ってはいけない パフォーマンスチューニング自体の コスト (≒ エンジニアの労働時間) と リスク (未知のトラブルを引き起こす可能性) を意識 単純なインフラ増強が最適解の場合も多い 目標を達成できたら、適当な所で手を引く
  25. 25. パフォーマンスチューニングの基本 メトリクスの測定 × ボトルネックの特定 × 仮説を立てて調整 を繰り返す (場当たり的な対応はNG) 参考: http://www.atmarkit.co.jp/ait/articles/0501/29/news011.html
  26. 26. “プログラムの処理にかかる時間の 80% はコード全体の 20% の部分が占める” — パレートの法則 http://ja.wikipedia.org/wiki/パレートの法則
  27. 27. ボトルネックの傾向 ※個人の感想です。何の根拠もありません。 その他 1% ネットワーク 4% JVMパラメータ 5% ライブラリ 5% OS 10% Scala 10% 非同期処理・スレッド 15% データベース (RDBMS/NOSQL) 50%
  28. 28. “正しい実装さえしていれば これまで経験してきたシステムの大半は I/O バウンド だった” — 弊社フルスタックエンジニア ※個人的な感想です ※機械学習アルゴリズムは除く
  29. 29. I/O の 3大要素 メモリ× ディスク × ネットワーク
  30. 30. 一般的なPCのオペレーション処理時間 CPU命令1 ナノ秒 = 1/1,000,000,000 秒 CPU L1キャッシュから読み込み0.5 ナノ秒 CPU 分岐予測の失敗5 ナノ秒 CPU L2キャッシュから読み込み7 ナノ秒 mutex のロック/アンロック25 ナノ秒 メインメモリにアクセス100 ナノ秒 1Gbps ネットワークで 2KB 送信20,000 ナノ秒 メインメモリから 1MB 順次読み込み250,000 ナノ秒 ディスクのシーク8,000,000 ナノ秒 ディスクから 1MB 順次読み込み20,000,000 ナノ秒 日本~アメリカ西海岸間のパケット転送往復150,000,000 ナノ秒=150ミリ秒 http://norvig.com/21-days.html#answers http://itpro.nikkeibp.co.jp/article/COLUMN/20100119/343461/
  31. 31. もしもCPU命令1回に1秒かかるなら CPU命令1秒 CPU L1キャッシュから読み込み0.5秒 CPU 分岐予測の失敗5秒 CPU L2キャッシュから読み込み7秒 mutex のロック/アンロック0.5分 メインメモリにアクセス1.5分 1Gbps ネットワークで 2KB 送信5.5分 メインメモリから 1MB 順次読み込み3日 ディスクのシーク3ヶ月 ディスクから 1MB 順次読み込み6.5ヶ月 日本~アメリカ西海岸間のパケット転送往復5年 https://www.coursera.org/course/reactive week3-2
  32. 32. ちなみに 間寛平 アースマラソン ヨットで太平洋横断 千葉 ~ ロサンゼルス (片道) 2009-01-03 -> 2009-03-11 70日で達成 http://ja.wikipedia.org/wiki/%E3%82%A2%E3%83%BC%E3%82%B9%E3%83%9E %E3%83%A9%E3%82%BD%E3%83%B3#.E3.83.A8.E3.83.83.E3.83.88.EF.BC.9A.E5.8D.83.E8.91.89_- _.E3.83.AD.E3.82.B5.E3.83.B3.E3.82.BC.E3.83.AB.E3.82.B9
  33. 33. 本当にあった怖い話 バッチ処理で 10KB 程度の小さなファイルを ディスクから 100万個 読み込んでいた 合計サイズ: 10KB × 1,000,000 ≒ 10GB
  34. 34. 本当にあった怖い話 100万回のシークが発生すると 8ms × 106 + 20ms × 10,000 ≒ 8,200 sec ≒ 2.5 h もし10GBのファイル1個で1回のシークで済むと 8ms × 1 + 20ms × 10,000 ≒ 200 sec ≒ 3.5 min
  35. 35. ディスクヘッドの気持ちを考えよう √ http://en.wikipedia.org/wiki/Hard_disk_drive
  36. 36. JVM パフォーマンス・トライアングル JVM におけるトレードオフの関係 メモリ占有空間 ↓ Memory Footprint アプリケーションの 最長停止時間 ≒ Full GC の時間 情報処理量 ↑ 応答時間 ↓ Throughput Latency
  37. 37. JVM パフォーマンス・トライアングル 言い換えると Compactness 省メモリ 情報処理量応答速度 Throughput Responsiveness
  38. 38. JVM パフォーマンス・トライアングル C × T × R = k (定数) k に対して C, T, R のどこに 比重を置くか変えるのがチューニング k を増加させるのが最適化 (Optimization) (Twitter社ソフトウェアエンジニアの発表) http://www.beyondlinux.com/files/pub/qconhangzhou2011/Everything%20I%20ever%20learned %20about%20JVM%20performance%20tuning%20@twitter%28Attila%20Szegedi%29.pdf
  39. 39. Agenda Demand Side Science の紹介 パフォーマンスチューニング概論 開発フェーズ別のポイント
  40. 40. 開発フェーズ 1. 要件定義/フィージビリティ 2. 方式設計 3. 環境設計 4. 環境構築/プログラミング 5. システムテスト 6. 運用/保守 パフォーマンスに影響がありそうな ポイントを中心に取り上げます
  41. 41. 要件定義/フィージビリティ 性能要件について関係者の合意を得る 想定ユーザーID数 日本のインターネット人口: 約1億人 日本のユニークブラウザ数: 2億~数億 増加率は? 情報を保持する期間は? デバイス/ブラウザの種別は? オプトアウトしているユーザの割合は?
  42. 42. 要件定義/フィージビリティ 広告配信要求数 月間 impression 数 月間10億件の場合 => 平均すると 400 QPS (Query Per Second) => ピーク率 250% とすると 1,000 QPS RTBの場合、入札率は? 勝率は? 目標応答時間は? コンテンツの容量は? 増加計画は? Cookie Sync リクエストの量は?
  43. 43. 要件定義/フィージビリティ トラッカー受信量 トラッカー発生のタイミング 広告クリックの確率は? コンバージョン(商品購入など)の発生確率は?
  44. 44. 要件定義/フィージビリティ 集計に関する要件 集計すべき指標 ユニーク数の集計は必要か? 集計から除外すべき条件はあるか? 誰が、いつ見るのか 広告代理店が二次加工をして 広告主にレポートしている? 更新頻度 集計データの保持期間
  45. 45. 要件定義/フィージビリティ ビジネスサイドの制約 売上計画 年末商戦に合わせたい、 年度内に売上目標を達成したい、など インフラ予算
  46. 46. 要件定義/フィージビリティ 市場の変化が激しいアドテクの世界で 正確な見積りを行うのは至難の業であるが、 仮置きでもよいので数字を出すことが大事 アーキテクチャ設計には前提が必要 性能テストには目標が必要
  47. 47. 方式設計 アーキテクチャ設計 フレームワーク選択 Webフレームワーク データベース選択 RDBMS NOSQL
  48. 48. 方式設計 並行プログラミングモデルの設計 ブロッキングをいかに減らすか Future ベース コールバックを利用した関数合成が基本 Actor ベース メッセージパッシング スレッドプールの設計 スレッドプールのサイズの適正値は 本番環境で性能テストを行うまでわからない
  49. 49. 環境設計 データベース設計 アクセスパターン/ルックアップ回数 1レコードあたりのサイズ サイズが一定でない場合は分布をモデル化 レコード数 増加率/保持期間 メモリ使用量 DB単体の性能をまず実測する
  50. 50. 環境設計 ログ設計 ディスク容量の見積りでは圧縮率を考慮 キャッシュ設計 Redis など、バックアップのために 2倍の キャパシティが必要な場合もあるので注意
  51. 51. 環境構築/プログラミング この段階では、最適化よりも 簡潔さ・明快さを優先したほうがよい “正しいプログラムを速くすることは、 速いプログラムを正しくするより はるかに、はるかに簡単だ。” — C++ Coding Standards―101のルール、ガイドライン、ベストプラク ティス (C++ in-depth series)
  52. 52. “時期尚早な最適化は諸悪の根源だ。” — Donald Knuth
  53. 53. “一方で、効率性は無視できない。” — Jon Bentley
  54. 54. 環境構築/プログラミング 線形より悪いアルゴリズムは できるだけ避ける 推測するな、計測すべし http://ja.wikipedia.org/wiki/UNIX哲学
  55. 55. マイクロベンチマーク: sbt-jmh sbt コンソールの中で OpenJDK jmh (Java のベンチマークツール Java Microbenchmark Harness) を使うためのプラグイン https://github.com/ktoso/sbt-jmh
  56. 56. マイクロベンチマーク: sbt-jmh plugins.sbt addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.1.6") build.sbt jmhSettings YourBench.scala import org.openjdk.jmh.annotations.Benchmark class YourBench { @Benchmark def yourFunc(): Unit = ??? // 計測したい処理を書く } アノテーションを付けるだけ
  57. 57. マイクロベンチマーク: sbt-jmh sbt コンソールを起動し、ベンチマーク実行 > run -i 3 -wi 3 -f 1 -t 1 計測回数 ウォーミング アップ回数 スレッド数 全体の試行回数
  58. 58. マイクロベンチマーク: sbt-jmh 実行結果 (抜粋) [info] Benchmark Mode Samples Score Score error Units [info] c.g.m.u.ContainsBench.listContains thrpt 3 41.033 25.573 ops/s [info] c.g.m.u.ContainsBench.setContains thrpt 3 6.810 1.569 ops/s デフォルトではスループット (単位時間あたりの処理回数) が表示される。 (大きほど良い) http://mogproject.blogspot.jp/2014/10/micro-benchmark-in-scala-using-sbt-jmh.html
  59. 59. Scala 最適化の例 Scala コレクションを正しく使う 関数呼び出しよりも再帰を使う 今年の Scala Matsuri で Martin Odersky 先生が発言 高速化ライブラリを試してみる
  60. 60. 本当にあった怖い話 その2 List[Int] の要素を 4つずつグループにして それぞれの和を求める処理を再帰で def f(xs: List[Int], acc: List[Int] = Nil): List[Int] = { if (xs.length < 4) { (xs.sum :: acc).reverse } else { val (y, ys) = xs.splitAt(4) f(ys, y.sum :: acc) } } 実行例 scala> f((1 to 10).toList) res1: List[Int] = List(10, 26, 19)
  61. 61. 本当にあった怖い話 その2 List#length の計算量はリストの長さに比例 パラメータ xs の長さを n とすると O(n) 実装は LinearSeqOptimized#length https://github.com/scala/scala/blob/v2.11.4/src/library/scala/collection/ LinearSeqOptimized.scala#L35-43
  62. 62. 本当にあった怖い話 その2 関数 f を実行すると、xs.length は n / 4 + 1 回評価される。 つまり f の実行回数も n に比例する 従って、関数 f の計算量は O(n2) n が大きくなると急速に性能が劣化する
  63. 63. 本当にあった怖い話 その2 ちなみに、組み込みメソッドを使えば 1行で書ける scala> (1 to 10).grouped(4).map(_.sum).toList res2: List[Int] = List(10, 26, 19)
  64. 64. ScalaBlitz
  65. 65. ScalaBlitz マクロ機能を利用して Scala コレクション の利用を最適化するライブラリ http://scala-blitz.github.io/ Scala Days 2014 での発表 https://parleys.com/play/ 53a7d2c6e4b0543940d9e549/chapter0/ about
  66. 66. システムテスト システム機能テスト システム間インターフェーステスト 性能テスト 信頼性テスト セキュリティテスト 運用性テスト
  67. 67. 性能テスト 単体負荷テスト シナリオ負荷テスト 現実のユーザ操作を模した負荷 エージング(連続稼働)テスト
  68. 68. ab - Apache Bench Apache 付属 シンプルなベンチマークツール http://httpd.apache.org/docs/2.2/programs/ab.html 単純な要件であればこれで十分 最新バージョンの利用を推奨 (Amazon Linux プリインストールのバージョンでバグに苦しめられた) コマンド実行例 ab -C <Cookie名=値> -n <リクエスト件数> -c <同時実行数> “<URL>“
  69. 69. ab - Apache Bench 実行結果の例 (一部) Benchmarking example.com (be patient) Completed 1200 requests Completed 2400 requests (略) Completed 10800 requests Completed 12000 requests Finished 12000 requests (略) Concurrency Level: 200 Time taken for tests: 7.365 seconds Complete requests: 12000 Failed requests: 0 Write errors: 0 Total transferred: 166583579 bytes HTML transferred: 160331058 bytes Requests per second: 1629.31 [#/sec] (mean) Time per request: 122.751 [ms] (mean) Time per request: 0.614 [ms] (mean, across all concurrent requests) Transfer rate: 22087.90 [Kbytes/sec] received (略) Percentage of the requests served within a certain time (ms) 50% 116 66% 138 75% 146 80% 150 90% 161 95% 170 98% 185 99% 208 100% 308 (longest request) Requests per second = QPS
  70. 70. Gatling Scala で書かれた負荷テストツール http://gatling.io
  71. 71. Gatling Apache JMeter の時代は終わった GUI でシナリオを作るのはつらい Gatling なら 負荷シナリオを Scala DSL で書ける 必読記事 WEB+DB PRESS Vol.83 (2014/10月) 「Javaの鉱脈」 http://gihyo.jp/magazine/wdpress/archive/2014/vol83 筆者 @ryushi さんのブログ http://blog.satotaichi.info/gatling-is-awesome-loadtester
  72. 72. テスト & チューニングの日々 負荷をかける側のリソースにも注意 サーバ (PC) のリソース ネットワークルータの CPU が ボトルネックになったことも 一度に 2箇所以上の調整をしない 変更履歴やログファイルをきちんと残す
  73. 73. 運用/保守 ログ出力× 異常検知 × トレンド可視化
  74. 74. 運用/保守 日常的にログを記録し、監視する アプリケーションログ GCログ プロファイラー 各種メトリクスによる異常検知 サーバリソース (CPU, メモリ, ディスク等) レスポンスコード異常 レイテンシ 各種メトリクスのトレンド可視化
  75. 75. JVM の運用設定 GCログ JVM起動オプションで指定 -verbose:gc -Xloggc:<ログファイルのパス> -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10 -XX:GCLogFileSize=10M
  76. 76. “本番環境で GCログを出してない奴は 撃ってやる!” — Oracle 社の実際の顧客 “If someone doesn't enable GC logging in production, I shoot them!” http://www.oracle.com/technetwork/server-storage/ts-4887-159080.pdf p55
  77. 77. JVM の運用設定 JMX (Java Management eXtensions) JVM起動オプションに以下を指定 -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=<ポート番号> -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false
  78. 78. JVM の運用設定 標準出力/標準エラー出力 ファイルにリダイレクトさせる /dev/null に捨てない スレッドダンプ kill -3 <プロセスID> の結果はここに出る
  79. 79. プロファイラー SLF4J + Profiler http://www.slf4j.org/extensions.html コード例 import org.slf4j.profiler.Profiler val profiler: Profiler = new Profiler(this.getClass.getSimpleName) profiler.start(“A”) doA() profiler.start(“B”) doB() profiler.stop() logger.warn(profiler.toString)
  80. 80. プロファイラー SLF4J + Profiler 出力例 + Profiler [BASIC] |-- elapsed time [A] 220.487 milliseconds. |-- elapsed time [B] 2499.866 milliseconds. |-- elapsed time [OTHER] 3300.745 milliseconds. |-- Total [BASIC] 6022.568 milliseconds. 利用例: タイムアウトが発生時に プロファイラーの結果をログに出力
  81. 81. メトリクスの可視化 異常検知ではなく、トレンド把握が目的 変化の兆候を見逃さないような運用も大事 インフラ/アプリだけなく、ビジネス指標も 誰のための画面? システム利用者 システム管理者 アプリ開発者 経営者 (ビジネスの判断を下す人)
  82. 82. Grafana (+Graphite)
  83. 83. Grafana (+Graphite) Graphite - http://graphite.readthedocs.org 時系列の数値データを管理/可視化 Grafana - http://grafana.org/ Graphite のデータをカッコよく (Kibana っぽく) 可視化 Graphite にメトリクスを送る Scala ライブラリを公開予定
  84. 84. DSS 広告 Demand Side Science は 本物の Scala プログラマーを 募集しています。 相談・応募はこちら! ランチご馳走します! recruit@demand-side-science.jp
  85. 85. ご清聴ありがとう × ございました! どうも× ~ パフォーマンス改善の心構えと勘ドコロ ~ アドテク × Scala meetup 2014-11-20 @mogproject
  86. 86. "Yosuke Mizutani - Kanagawa, Japan | about.me" - http://about.me/mogproject "mog project" - http://mogproject.blogspot.jp/ "DSS Tech Blog - Demand Side Science ㈱ の技術ブログ" - http://demand-side-science. jp/blog/ "FunctionalNews - 関数型言語ニュースサイト" - http://functional-news.com/ "『ザ・アドテクノロジー』~データマーケティングの基礎からアトリビューション の概念まで~ / 翔泳社 新刊のご紹介" - http://markezine.jp/book/adtechnology/ "オプト、ダイナミック・クリエイティブツール「unis」の提供開始 ~ パーソナラ イズ化された広告を自動生成し、広告効果の最大化を目指す ~ | インターネット広 告代理店 オプト" - http://www.opt.ne.jp/news/pr/detail/id=2492 "The Scala Programming Language" - http://www.scala-lang.org/ "Finagle" - https://twitter.github.io/finagle/ "Play Framework - Build Modern & Scalable Web Apps with Java and Scala" - https://www.playframework.com/ "nginx" - http://nginx.org/ja/ "Fluentd | Open Source Data Collector" - http://www.fluentd.org/ "Javaパフォーマンスチューニング(1):Javaパフォーマンスチューニングのルー ル (1/2) - @IT" - http://www.atmarkit.co.jp/ait/articles/0501/29/news011.html "パレートの法則 - Wikipedia" - http://ja.wikipedia.org/wiki/パレートの法則 "Teach Yourself Programming in Ten Years" - http://norvig.com/21- days.html#answers "企業が作る国際ネットワーク最前線 - [4]いまさら聞けない国際ネットワークの 基礎知識:ITpro" - http://itpro.nikkeibp.co.jp/article/COLUMN/20100119/ 343461/ "Coursera" - https://www.coursera.org/course/reactive "アースマラソン - Wikipedia" - http://ja.wikipedia.org/wiki/アースマラソン "Hard disk drive - Wikipedia, the free encyclopedia" - http://en.wikipedia.org/ wiki/Hard_disk_drive "Everything I ever learned about JVM performance tuning @twitter(Attila Szegedi).pdf" - http://www.beyondlinux.com/files/pub/qconhangzhou2011/ Everything%20I%20ever%20learned%20about%20JVM%20performance %20tuning%20@twitter%28Attila%20Szegedi%29.pdf "Amazon.co.jp: C++ Coding Standards―101のルール、ガイドライン、ベストプ ラクティス (C++ in-depth series): ハーブ サッター, アンドレイ アレキサンドレス ク, 浜田 光之, Herb Sutter, Andrei Alexandrescu, 浜田 真理: 本" - http:// www.amazon.co.jp/gp/product/4894716860 "UNIX哲学 - Wikipedia" - http://ja.wikipedia.org/wiki/UNIX哲学 "ktoso/sbt-jmh" - https://github.com/ktoso/sbt-jmh "ScalaBlitz | ScalaBlitz" - http://scala-blitz.github.io/ "Parleys.com - Lightning-Fast Standard Collections With ScalaBlitz by Dmitry Petrashko" - https://parleys.com/play/53a7d2c6e4b0543940d9e549/chapter0/ about "mog project: Micro Benchmark in Scala - Using sbt-jmh" - http:// mogproject.blogspot.jp/2014/10/micro-benchmark-in-scala-using-sbt-jmh.html "Gatling Project, Stress Tool" - http://gatling.io/ "WEB+DB PRESS Vol.83|技術評論社" - http://gihyo.jp/magazine/wdpress/ archive/2014/vol83 "「Javaの鉱脈」でGatlingの記事を書きました — さにあらず" - http:// blog.satotaichi.info/gatling-is-awesome-loadtester "Garbage Collection Tuning in the Java HotSpot™ Virtual Machine" - http:// www.oracle.com/technetwork/server-storage/ts-4887-159080.pdf "SLF4J extensions" - http://www.slf4j.org/extensions.html "Graphite Documentation — Graphite 0.10.0 documentation" - http:// graphite.readthedocs.org/en/latest/ "Grafana - Graphite and InfluxDB Dashboard and graph composer" - http:// grafana.org/ "Grafana - Grafana Play Home" - http://play.grafana.org/#/dashboard/db/ grafana-play-home "不動産関係に使える 無料画像一覧" - http://free-realestate.org/information/ list.html "AI・EPSの無料イラストレーター素材なら無料イラスト素材.com" - http://www.無 料イラスト素材.com/ "大体いい感じになるKeynoteテンプレート「Azusa」作った - MEMOGRAPHIX" - http://memo.sanographix.net/post/82160791768 参考文献

×