江副浩正

江副浩正

江副浩正

年の瀬になぜこれをたまたまKindleで買ったのか分からない。けど夢中になって一気に読破してしまった。 江副氏との接点が1つだけあって、東京大学新聞OBだということだ。ぼくも大学新聞の営業を少ししていたので親近感を持って読んだ。

自ら機会を創り出し、機会によって自らを変えよ

江副氏自身の著書も読みたくなった。

Who Gets What - マッチメイキングとマーケットデザインの新しい経済学

マッチメイキングとマーケットデザインについて。

この分野がとてもいいと思うのは、ゼロサム・ゲームでないところ。仕事の多くは、何らかの資源の取り合いであることが多くて、競争を通じて市場や付加価値が拡大することはもちろんあるにせよ、競争が激しすぎて消耗していると感じることもある。

マッチメイキングは、その市場の作り方を工夫しようというもの。

腎臓交換、就職、学校選択、など必ずしもお金がやりとりされないものについても、何らかの取引を行う市場といえ、適切なデザインによって取引が改善する余地がある。

ゲーム理論をもう一度おさらいしたくなった。

HeapAnalyzerを使う

なぜかEclipseのMATが手元のMacでうまく動作しないので同様の機能があるIBMのHeapAnalyzerを使う。

IBM HeapAnalyzer

下段の"Download HeapAnalyzer"からダウンロードできる。 2017年12月時点での最新のバージョンは4.5.6。

実行するには

java -Xmx12G -jar ha456.jar

ヒープダンプのサイズにもよるが、ヒープサイズはできるだけ多く確保したほうがよい。足りないとこのプロセス自体がOOMで落ちる。

jvisualvmでKubernetesのScalaアプリケーションのチューニング

GKEで走るScalaアプリのチューニングをjvisualvmでやったので手法をあとで振り返れるようにメモしておく。

要件としては、所与のマシンスペック・台数の範囲内で、スループットを最大化したいというもの。 さらに、RTBなので、安定した高速なレスポンス速度が求められる。具体的には1レスポンス20msecくらい。そのレスポンス速度自体はすでに達成できているので、それを崩さずに、スループットを上げる必要があった。

チューニングは、locust で負荷をかけながら行った。はじめはローカル環境からlocustを起動していたが、ある程度以上の負荷をかけようと思うと、ローカルからだとネットワークやCPUがボトルネックになってかけられない。そこでGCEでインスタンスを作って、そこからlocustを起動した。

locustは簡単にマスター・スレーブ構成のクラスタを作ることができる。もちろん、複数のインスタンスクラスタを組んでもいいが、マルチコアのインスタンスを1つ起動して、その中で複数のスレーブを起動する形でもいい。

  • マスター側
locust --host=[負荷をかけるURL] --master
  • スレーブ側
locust --host=[負荷をかけるURL] --slave --master-host=127.0.0.1 &

この状態で、負荷をかけたまま、コンテナに kubectl exec -it でログインして、

をみて概況を掴んだ。

その後、 kubectl port-foward してコンテナのJMXローカルにポートフォーワードして、jvisualvmを起動した。

jvisualvmは高機能なツールで、これ1つで稼働中のアプリのjvmに関する多くの情報が得られる。今回使ったのは、

スレッドダンプをとる

スレッドダンプはボタン1つでとれる。各スレッドのスタックトレースをみていき、各スレッドがどのように使われているか、ロックしているスレッドがないかを調べる。例えば

"pool-11-thread-231" - Thread t@1001
   java.lang.Thread.State: WAITING
        at sun.misc.Unsafe.park(Native Method)
        - waiting to lock <2ffa4815> (a java.util.concurrent.locks.ReentrantLock$NonfairSync) owned by "pool-11-thread-215" t@984
        at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
        at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836)
        at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:870)
        at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1199)
        at java.util.concurrent.locks.ReentrantLock$NonfairSync.lock(ReentrantLock.java:209)
        at java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:285)
        at ch.qos.logback.core.OutputStreamAppender.writeBytes(OutputStreamAppender.java:197)
        at ch.qos.logback.core.OutputStreamAppender.subAppend(OutputStreamAppender.java:231)
        at ch.qos.logback.core.OutputStreamAppender.append(OutputStreamAppender.java:102)

これはlogbackのログ出力だが実行がロックされていて、その先の pool-11-thread-215 をみると、ファイルIOを待っていることがわかる。 今回の場合は不要なログ出力だったので、それをしなくするようにした。

サンプラを使って遅い箇所を特定する

CPUサンプリングをすると、CPUを使っているホットスポットのメソッドを特定できる。

f:id:road288:20171222204436p:plain

「セルフ・タイム(CPU)」のほうをみるのがポイント。これが実際にCPUを消費しているメソッドになるので、これをなんとかすることが、スループットを上げるためには必要になる。 今回は、この箇所から無駄な処理やIOを特定し、減らすということをした。

CPUサンプルのスナップショットをとれば、さらに呼び出しの経路とかスタックを細かく見ることができる。

f:id:road288:20171222205650p:plain

参考

www.techscore.com

d.hatena.ne.jp

Djangoで実行したSQLログ出力したい(デバッグ用途)

トランザクションまわりの挙動を調べるため。デバッグ用途なので、プロダクションで動かす想定は不要。

Logging | Django documentation | Django https://docs.djangoproject.com/en/1.8/topics/logging/

Django ORM の SQL を出力する方法まとめ - akiyoko blog http://akiyoko.hatenablog.jp/entry/2016/08/04/232531#%EF%BC%97settingspy-%E3%81%AE-LOGGING-%E3%82%92%E8%A8%AD%E5%AE%9A

DjangoSQLログを出力したい - Qiita https://qiita.com/ariarijp/items/f6bfe69bd42896571eda

あとでやってみる。

Macでポートフォワード

今回のケースでいうと、Kubernetes内のPodにローカルからJMXにつないでjvisualvmを使ってチューニングすることが多かったのだが、下記の記事でやったように127.0.0.1のホスト指定が使えないケースが出てきてしまった。

road288.hatenablog.com

-Djava.rmi.server.hostname=127.0.0.1の設定を抜いてしまうと、そのままではつなぐことができなくなってしまう。というのは、Kubernetesのport-fowardコマンドは、127.0.0.1にBindする仕様のため(2017/12/14時点)。

github.com

Pull Requestも上がっているが、マージできない状況になってしまっている。

github.com

なんとかならないかと考えていたら、ポートフォワードすることによって解決できた。

github.com

 echo "rdr pass inet proto tcp from any to any port 7000 -> 127.0.0.1 port 7000" | sudo pfctl -ef -

上記の設定は、7000番ポートにきたパケットを全て127.0.0.1:7000に送る。

ᐅ sudo pfctl -s nat                                                                             (yamada/default)
No ALTQ support in kernel
ALTQ related functions disabled
rdr pass inet proto tcp from any to any port = 7900 -> 127.0.0.1 port 7900

これによって、kubectl port-forwardでした際に、これまで通りjvisualvmにつなぐことができた。

元に戻すときは、

sudo pfctl -F all -f /etc/pf.conf

参考:

PFCTL(8) - Maintenance Commands - YOS OPENSONAR

Mac pfctl Port Forwarding | Sal Ferrarello

[Kubernetes][JMX] Kubetenetesで動作するコンテナにjmxで接続する

stackoverflow.com

基本的にはここの通り。

Kubernetesの中で動くPodにJMXでつないで様子をみたいときがある。 そういうときは、まずPod名を調べて、kubectlコマンドでポートフォーワードして、jvisualvmなどのコマンドを使えばOK。 例えばJMXが7000番ポートで受け付ける場合は、

kubectl get pods

でPod名を調べ、

kubectl port-forward [Pod名] 7000

とすると、 

Forwarding from 127.0.0.1:7900 -> 7900
Handling connection for 7900

このようにポートフォワーディングが始まるので、あとはjconsoleなり、jvisualvmなりでつなげる。 例えばjvisualvmでつなぐ場合は、下記のように設定する。

f:id:road288:20171214194911p:plain

いろいろな情報を見るにはGUIがやっぱり便利。

f:id:road288:20171214194948p:plain