Furyu

[フリュー公式] Tech Blog

フリュー株式会社の技術ブログです

2016年03月30日

Kayo

OSインストールの自動化

自己紹介

Furyu Tech Blogを読んで下さっているみなさん、初めまして。 コンテンツ・メディア第1事業部のインフラチームに所属していると藤本佳世と申します。今回が初投稿となりますが、これからみなさんに「フリューのインフラ技術」をご紹介できればと思っています。 どうぞよろしくお願い致します。

インフラの仕事

インフラの仕事は、サーバの選定から始まり、OS・ミドルウェアのインストールや監視など様々な仕事があります。 開発メンバーがスムーズに仕事をこなせるよう、日々「縁の下の力持ち」として頑張っています。 今回は、インフラ業務の中でも、「OSインストールの自動化」について、お話したいと思います。

PXEブート

みなさん、「PXEブート」はご存知ですか?

(さらに…)


2016年03月24日

sasaki

Play Framework 2.1系と2.3系に依存するライブラリのクロスビルド

ソーシャルゲーム2部 佐々木です。最近はなかなかコーディングの時間が取れず、ストレスフルな日々を送っています。今回は、半年くらい前に行った Play Framework に関する話題です。

背景

経緯から説明すると、Play Framework 2.1系であるサービス(以後、サービスA)を開発・運用していました。 そのサービスのシステムを流用して新たにサービス(以後、サービスB)を立ち上げることになり、その機会に Play Framework 2.3系に移行することにしました。 ただ、大きなシステム変更があるわけではなく、サービスAとサービスBでは特に大きな機能改修はありませんでした。 そこで、サービスAのシステムの中で再利用性の高い機能をライブラリ化して、サービスA/Bそれぞれがそのライブラリに依存するような形にすれば保守管理しやすいと考えました。

クロスビルドできない

いざ、Build.scala でクロスビルドの設定をしてみると、コンパイルエラーになってしまいました。

ここで問題になったのが、Play Framework の互換性でした。 Play Framework のリリースノートや移行ガイドを見るとわかりますが、Play Framework 2.2系から play.api.mvc.Result 関連のクラスがdeprecatedになり、Play Framework 2.3系では完全に取り除かれました。それにより、単純に依存するライブラリのバージョンを変えるようなクロスビルドの設定だけでは対応できませんでした。

ソースコードのパスを切り替える

そこで、Play Framework 2.1系とPlay Framework 2.3系でソースコードのパスを別々に設定することにしました。

この設定は、Play Framework 2.1系(Scala 2.10.4)でビルドする時には、src/main/scalasrc/main/play21/scalaをコンパイル対象とし、Play Framework 2.3系(not Scala 2.10.4)でビルドする時には、 src/main/scalasrc/main/play23/scalaをコンパイル対象とするものです。

あとは src/main/play--/scalaにそれぞれのplay.api.mvc.Result関連のクラスを定義することによって、当初の目的を達成することができました。

まとめ

WEBフレームワークやライブラリを使う側としては、バージョンアップや互換性にどう向き合うかは、悩ましいところでもあり面白いところでもあると思います。今回の件では、ソースコードのパスを切り替えるという発想に至るまでは紆余曲折あり、互換性を無視したバージョンアップをした Play Framework を憎んだこともありました(笑)が、今となってはこの方法以外無いのではと思っています。別の方法があればぜひぜひ教えていただきたいです!


2016年03月3日

sakata

JavaOne 2015 サンフランシスコに参加しました!(3日目以降)

Hello World! コンテンツ・メディア第1事業部の、jyukutyoこと阪田です。JavaOneではランチボックスが配られるのですが、聞いていたほどおいしくないという感じではなかったです。味はイマイチだけど食べたくないほどではないな、という感じでした。外で食べるとなると確実に$20は超えてしまうので、私はほぼこのランチボックスを食べていました。

私はランチボックスの写真を撮っていなかったので、JavaOneのランチボックス写真をflickrからお借りしました(さくらばさんありがとうございます)。 2014年のものですが、2015年でも同じ感じでした。

Lunchbox, JavaOne 2014 San Francisco

Protecting Java Bytecode from Hackers with the InvokeDynamic Instruction

Javaのバイトコードは高レベルのJVM命令セットです。スタック指向であり、命令フォーマットがあります。

ラムダ式を使った以下のコードがあるとします。

これはバイトコードでは次のように表現されます。

Procyon(https://bitbucket.org/mstrobel/procyon/wiki/Java%20Decompiler)というでコンパイラを使って起動してみます。$java –jar procyon.jar HelloJavaOneWithLamdas.class出力は次のようになります。

Javaのバイトコードへの攻撃としてはリバースエンジニアリングや、重要なルーティンへのバイパス、デコンパイルと変更が容易であることなどです。有名なJavaのデコンパイラは次にようなものです。Procyon + LuytenやCFR、JD、Fernflower、Krakatau、Candleです。 Javaのバイトコード保護としては、コードフロー難読化や名前の難読化、呼び出しの隠蔽などです。ここでObfuscatorという製品をデモします。

次にコンスタントプールとバイトコードについて考えます。コンスタントプールはすべてのシンボル情報を含んでいます。invoke*という命令はメソッド呼び出しに使われます。JVMはメソッド実行の前にスタックが矛盾しないことを要求します。

リフレクションを使って呼び出しを隠蔽することができます。

そうすると、メリットとしてコンスタントプールからMethodRefとFieldRefを取り除けます。デメリットとしてはパフォーマンス、バイトコードサイズのオーバーヘッド、引数や戻り値でボクシング/アンボクシングが必要となること、スーパークラスのメソッドを呼び出せないこと、プライベートメソッドやフィールドへアクセスするときにセキュリティ違反になること、などがあります。

そこで、JSR-292のInvokeDynamicです。Java 7から導入されています。InvokeDynamicを使って呼び出しを隠蔽します。プランとしては、ブートストラップメソッドを生成し、invokevirtual/invokeinterface/invokestaticの呼び出しをinvokedynamicに書き換えます。それにはASMを使います。ASMは低レベルのバイトコード操作のためのライブラリです。SAXのようなVisitor APIとDOMのようなTree APIがあります。ASMでHello Worldすると次のようになります。

ソースコードを読み込み、ASMを使ったコードに書き換えるわけです。ブートストラップを生成するコードはこのようになります。

次に、invoke*の置き換えですが、MethodVisitorを実装します。

visitMethodInsn()メソッドは次のような実装となります。

結果として、たとえばJDででコンパイルするとエラーになります。他のデコンパイラでも読めません。結論としては、invoke*命令はすべて保護できました。さらにパフォーマンスのインパクトはありません。JVMがInvokeDynamicを最適化してくれます。

Having Fun with Javassist

JRebleなどを作っているZEROTURNAROUNDの方のセッション。 XRebelやJRebelでJavassistを多く活用しています。バイトコード操作はいたるところで行われています。Hibernateではプロキシ生成に使われています。主要なユースケースとして、バイトコード操作はJavaのフレームワークにおいてプロキシを生成するために使われています、がその内容だと退屈ですね。 JavassistはリフレクションAPIのような感じです。ClassPoolにCtClassがあり、CtClassにはCtFieldやCtMethod、CtConstがあります。CtMethodにはinsertBefore、insertAfter、instrumentがあります。

ビルド時にメタデータからクラスを生成できます。またはコンパイルしたクラスに対してあとから処理することができます。

こうして、トレーシングを実装したりログを追加したり、AOPを実装したりできます。不要な呼び出しを削除できます。フィールドに直接アクセスしていたところをsetter呼び出しに変えることができます。

次にJava Agentについて。$java –javaagent:agent.jar application.MainでAgentをつけて実行できます。Agentの実装はこうなります。

さらに、META‐INF/MANIFEST.MFにこう書きます。

ClassFileTransformerの実装としては、たとえばこんな感じです。

JRebleプラグインでもこの仕組みを使っています。JRebleプラグインでは、JRebleのコアとSpringやHibernate、EJBといった個別のプラグインがあります。jrebel.jarはすべてのプラグインを含んでいます。クラスをリロードすると、JRebleコアが各プラグインに通知します。そして、実際に設定が更新されたりします。Javassistは各プラグインで使っています。

Beyond top: Command-Line Monitoring on the JVM

サーバがたびたび遅くなる問題がありました。チームはアプリケーションサーバを手動で再起動していました。レスポンスタイムが5分ほどかかることがあありました。何かが起こっています。

事実に基づいてみると、すべてのリクエストが影響を受けていました。JVMは落ちていませんでした。JVMを再起動するとすべてがうまくいきました。何ができるのでしょう?さらなる事実として、フルGCが頻発していました。

ヒープに何があるか、どのアプリケーションコードを実行しているかを確認するために、いいツールがあります。まずvmstat。システムレベルでCPUやメモリ、ディスク、コンテキストスイッチが見れます。topも。プロセスごとのCPUとメモリが見れます。jpsでPIDがわかります。jstackでスレッドの状態がわかります。jcmdは何でもできます。jstatでGCやクラスローダ、コンパイラがわかります。

これで謎は解けます。たとえば、リークを除去する、キャッシュを除去する、機能を削除する、全文検索エンジン…”遅い”には意味がたくさんあります。”CPU負荷が高い”には意味がたくさんあります。データを集めることは危機的状況において極めて重要です。

ほかの役立つツールとしては、ヒープアナライザやプロファイラ、絶え間ないモニタリングやアラート、動的なトレースなどがあります。

Java Community Keynote

Javaコミュニティ(JUG)によるキーノート。これだけ別の大きな会場(Marriott Marquis)でした。

今回は、Java仕様に関わった人やJUGリーダなどが寸劇をするという衝撃的なものでした。ストーリは、JavaのマスコットであるDukeが、未来の世界で歯が生えて(!?)暴れまわっているため、世界を救うために過去にタイムスリップしながら破滅を避けるキーワードを探す、という内容でした。

キーノートのMCはStephen Chinさんで、去年私もJUGリーダとして少し話したことがあり親近感がありました。劇ではジェームズ・ゴスリン本人が登場したり、10年前くらいの世界で「Project Jigsawは完成しているんだろ!?(実際はJava 9なのでまだ)」という少し皮肉ったセリフが出たりと、とても楽しいものでした。

キーワード自体は”Kids are future(未来は子どもたちである)”というものでした。技術的な内容はまったくなく、純粋にJavaのお祭りを楽しむ、そういうセッションでした。

The Rise of 1.0: How Reactive Streams and Akka Streams Change the JVM Ecosystem

リアクティブ・ストリームのお話でした。リアクティブ・ストリームは.NET 3.5で導入されたのが最初です。その後、PlayフレームワークがIterateesを導入しました。AkkaはAkka-IOを、BenがRxJavaを開始しました。ただ2013年時点ではこの3つには違いがありました。Iterateesはpull back-pressureであり、APIが複雑です。Akka-IOはNACK back-pressureであり、低レベルIO、メッセージングAPIです。RxJavaはno back-pressureでいいAPIです。そこでエキスパートグループが創設されました。ゴールは、非同期で、ブロックせず、安全で、純粋に局所抽象化され、同期型も許容するということです。リアクティブ・ストリームには仕様とTCKがあります。バージョン1.0は2015/04/28にリリースされました。

なぜback-pressureなのか?負荷でクラッシュしないようにするためです。back-pressureとは何か? Publisher-Subscriberモデルで考えます。まず、Publisherのpushが早くてSubscriberが遅い場合、Subscriberにはバッファできる何かが必要です。でももしバッファがあふれたら?Subscriberはメッセージを捨てて、Publisherはメッセージを再送する必要があります。カーネルやルータはこうしています。メモリがあるときはバッファを増やせますが、いずれOutOfMemoryになります。Negative ACKnowledgementではバッファオーバーフローは差し迫ったものです。Publisherにペースを落とすか送信を停止してもらわなければなりません。しかしNACKではそれは間に合いません。そうしている間にもメッセージが来るからです。Publisherの速度よりSubscriberの速度が上回っているとき、back-pressureは必要ありません。

リアクティブ・ストリームは動的なPush/Pullです。pushだけでは遅いSubscriberのときに効果がなく、pullだけでは早いSubdcriberのときには遅すぎるのです。解決策としては動的な調整です。遅いSubscriberでは、3つの要素だけバッファに取ります。Publisherはたとえもっと多く遅れる場合でも多くて3つだけにします。これがpull-based-backpressureです。早いSubscriberはもっと要求します。Publisherは要求をためておきます。Publisherは各Subscriberの総要求数をためておき、オーバーフローしないように送ります。

Inter OPでは、異なる実装でもお互いにうまくやれるようにします。RxJavaとAkkaなど。リアクティブ・ストリームがプロトコルとなります。リアクティブ・ストリームはデイリーユースのエンドユーザAPIではありません。これはSPIです。Service Provider Interfaceです。SPIはサードパーティによって実装または拡張されることを意図したAPIです。

Play 2.5はAkka Streamsを使います。ScalaでもJavaでもDSLで同じパワーを得られます。Akka 2.4は2.3とバイナリ互換です。

カニパーティー

これはJavaOneとは直接関係がありませんが、JavaOneの日本人参加者で最終日の夜にカニを食べに行くというイベントが毎年あります。30名以上の方が来られていました。

IMG_1620

カニはダンジネスクラブというサンフランシスコでは有名なカニです。ガーリックソースでローストしており、日本では味わえないおいしさです。手はドロドロになりますので、汚れてもいい服の方がよいです。

話は自然とJavaのこと、いろいろな方と知り合うことができました!

JavaOne全体をふりかえって

最高でした!学べる内容としては、サンフランシスコまで行かなくてもプレゼンテーションの動画を見ても同じです。ただ、刺激という面では、やはり現地で受ける刺激はディスプレイの前で動画を見るよりも何十倍、何百倍も多いです。

英語のセッションということで心配になる方も多いかと思います。ふだん英語の技術文書を読まれている方なら、リスニングに不安があっても大丈夫です!スライドが補助となり、セッションが全部理解できないということはありません。迷うより飛び込んでしまうのが楽です!

私は今年もJavaOneに参加しようと計画しています。2016年のJavaOneは9/18〜22です。日本はちょうどシルバーウィークとなり、2日休暇を取るだけでJavaOneに参加できます!ぜひいっしょにJavaOneに参加しましょう!