ブログ・ア・ラ・クレーム

技術的なメモとかライフログとか。

Apache Avro について知っていることを書いていく その1

Apache Avro になにかと縁があり、かつ普及しているテクノロジーの割に日本語の情報がそんなにない(個人の意見です、意外とあるかも)のでつらつら書いてみます。 整理はされておらずシーケンシャルに要素を並べています。 実装についてとくに言及がされていなければ、 Java の 1.8.2 について触れているものとします。

Apache Avro はデータフォーマットとエコシステムのひとつ

Apache AvroApache トップレベルプロジェクトのひとつでファイルフォーマットとその周辺エコシステムです。 比較される技術として Protocol Buffers や Message Pack が挙げられると思います。 Apache Avro はそれらの中でも主に Hadoop エコシステムなどビッグデータ絡みの文脈でよく登場するように思えます。

以下のディレクトリ構成の通り、公式でいくつかのプログラミング言語をサポートしているようです。

github.com

筆者はほとんど Java のパッケージしか使わず正確な比較も行ってないのですが、おそらく Java 向けの機能がサポート厚めだと思われます。 avro-toolsJava で書かれてたり avro-protobuf という Protocol Buffers との変換ライブラリが提供されていたりするので。 また非公式ですが以下のような他言語向けライブラリもあったりします。

github.com

Avro はスキーマの表現力が強い

Avro はスキーマに従いシリアライゼーションとでシリアライゼーションをするわけですが、このスキーマの表現力が割と強いです。 雑に列挙すると以下の通りです。

  • record 型(構造体)のサポート

    • エイリアスや doc 、 namespace の宣言もできる(オプション)
  • array 型(配列)のサポート

  • union 型(共用体)のサポート

    • null 型との union を取り、デフォルト値を null にすることで nullable な型を表現できる
  • map 型のサポート

  • enum 型のサポート
  • fixed 型という固定長バイト配列のサポート(使ったこと無いのでよくわからん)
  • デフォルト値が設定できる
  • logical types を使うことで型に別の解釈をもたせることができる

    • メジャーな使い方として long 型に timestamp-millis logical type をもたせるなど
    • timestamp の制度は micros も選べる

これらにより、かなりリッチなスキーマを記述することができるはずです。

Avro のスキーマを記述するのがつらい

表現力が高いこともあるのですが、基本的にスキーマJSON で記述することになる上か結構冗長でかつ人間にとって読みにくい内容になりがちです。 公式リポジトリに簡単な例があるのですが、簡単なものでもある程度事前知識を求められるでしょう。 さらに型をネストし始めたら地獄です。 筆者は特殊な訓練を繰り返すことにより最近手でスキーマを書けるようになってきた気がしますが、このスキーマの記載を同僚に求めるのは酷なものです。

github.com

この問題はかなり認知されているのか、公式が IDL を提供しています。 一見 Protocol Buffers を彷彿とさせる気がするシンタックスですね。

avro.apache.org

また、プログラムから動的にスキーマを記述、生成することもできます。 Java であれば Schema.createRecord() して List<Schema.Field> オブジェクトを埋めて・・・といった流れで実現できます。

Avro 自体の圧縮効率はそんな高くない?

int, long は zigzag encoding してくれますが、 bytes, string は length + バイナリなどかなりシンプルなシリアライズをします。 結果としてシリアライズされたあとのバイナリのサイズは Protocol Buffers や Message Pack と比較してそんなに潰れないと思います。 (実測した訳でなく、単にこれまでの経験則程度の話です) ただ Object Container Files フォーマットを取るとブロック単位に圧縮をかけてくれたりします。

Object Container Files フォーマットの便利さ

あなたがデータアナリストやデータサイエンティストなどのポジションで働いているなら、このフォーマットで Avro と対面することが多いかもしれません。 .avro 拡張子をよく取る、 Object Container Files フォーマットに従うレイアウトを取ったファイルです。

実体は well-known な Avro スキーマに従ってシリアライズされたファイルになります。 そのスキーマの中にはレコードのスキーマとコーデック、フィンガープリント情報などが含まれます、 ここのスキーマ情報により、ファイルを読むことで中のバイナリがどんなスキーマシリアライズされたかわかるので動的にデシリアライザを生成して読める状態になってるわけですね!すぐれもの!

このファイルフォーマットは BigQuery や Redshift でもサポートされており、 Hadoop エコシステムにあまり関わらずとも Avro のファイルをオブジェクトストレージに書き出しておけば、あとで DHW に統合して安全便利にクエリを投げられるわけですね!すぐれもの!

avro-tools が開発運用に便利

あまり言及されてない気がしますが、 avro-tools という Java 製のツールが便利です。 事前定義した Avro のスキーマを記載した.avsc ファイルから Specific クラスを自動生成するのはもちろん、 Object Container Files フォーマットのファイルから schema を読んで吐いてくれたりレコードを json に変換して出力してくれたりします。 特にレコードを json で出力してくれると、 jq などと組み合わせてフィルタしたり内容確認できたりして便利ですね。

mvnrepository.com

飽きてきたので

本記事ではこれくらいにしておきます。 気力があったり次回書きたいことがまとまってきたらまた書きます。

「データとML周辺エンジニアリングを考える会」という勉強会の第二回を開催しました

TL;DR

2019/07/19(金)に、ヤフー株式会社様コワーキングスペースの LODGE において、「データとML周辺エンジニアリングを考える会」という勉強会の第二回目を開催しました。

data-engineering.connpass.com

データエンジニアリングとサイエンス、アナリティクスにざっくり被るような少し広く曖昧なドメインで開始した勉強会です。

今回は前回より少々多めで、 40 人強の参加者が参加してくれました。 発表、 LT に合わせて懇親会も議論が弾み、主催のひとりとしては実現したかったことがある程度できたのではないかと考えています。

つらつら発表内容

せっかくなので覚えているうちに発表資料のリンク記載と超個人的な感想載せてみます。

15 分枠

GCPでStreamなデータパイプライン運用しはじめた by @shoe116

メルカリでのログ収集のためのパイプラインの構築の話です。 マイクロサービスアーキテクチャへの移行やビジネス・組織のスケールに合わせた試行錯誤の跡が伺えます。 というかわたしも業務で参加してるやつです。実際上記の試行錯誤をしています。

speakerdeck.com

行動ログ処理基盤の構築 by @hirosassa

サービスにおける行動ログの収集基盤の刷新の話です。 現行システムが pull 型で基盤側がサービスの内情を知ってしまう問題を、 push 型のアーキテクチャにしたあたりが今後の投資になりうるおもしろい点なのではと思います。 (基盤システムってどこまでサービスのことを知るか、責任分界をどこでするかしばしば悩ましくなりますよね)

speakerdeck.com

LT 枠

Google Cloud ML Engineに浸かってみる by @yudeayase

GCP の ML Engine の話です。 ML Engine, 便利そうではあるもののこの仕組みに特化してしまうのは良いのか?などと考えさせられました。

cloud.google.com

(資料アップロードなし?あとで調べる

PoC案件が多すぎてつらいので、パイプラインを使いまわすツールを入れた。 by @mori_kaz0429

繰り返し発生する PoC 案件で、似たようなクエリを投げたりすることが多い処理を共通化、再利用可能にする話です。 最後の方には Apache Airflow などワークフローエンジンを今後使ってみたい話も。

(資料アップロードなし?あとで調べる

Cloud Composer & Cloud Dataflow によるバッチETLの再構築 by @yuzutas0

Cloud Composer (Apache Airflow のマネージドサービス) を使って壊れかけのデータ同期の仕組みを立て直す話です。 データエンジニアとデータアナリスト、両方に対してヒアリングをかけつつ現状を鑑みて良いバランスのところを攻めるというマネジメントに近い側面もあれば、 Cloud Dataflow によるクレンジング処理に触れたりする話もあり盛りだくさんでした。

speakerdeck.com

DigdagでETL処理をする by @nakano_shota

今度は Digdag でワークフローを組んだ話です。 s3_touch というオペレーターを開発して、 s3_wait と組み合わせてプロジェクト間依存関係も対応できるようにしていて良い。 あまり深く触れられなかったけどリトライ処理や冪等性担保大事ですよね。もっとお話聞いてみたい気がします。

speakerdeck.com

Comet.ml で AutoML ライブラリ開発(仮) by @Y_oHr_N

Comet.ml 、初めて知ったのですがかなり良さそう! codecov でカバレッジを可視化するのと同じようにモデルの可視化をできるのは好感触!

speakerdeck.com

データ活用の際にハマってしまったログ・データスキーマ設計 by @yu-ya4

この手の苦労話、これこそこういう勉強会で話し合いたかったことな気がします。 テーブルの日付が何の時間を表すか問題、スキーマ更新にどう立ち向かうか、 STRING 型フィールドに JSON 突っ込むのどうなんだ話、 null の扱いと結構あるある話な気がするが・・・。 二番目の話題は個人的にも刺さるものがありました。簡易にアプリケーションからログを出力して、読み出す時に苦労するスキーマオンリードの戦略自体は間違ってはいないはずだし・・・。 触れられていなかった別の点として、 BigQuery はカラムナでデータを持ってくれているはずなのですが、 JSON を突っ込んで読む時にパースするではパフォーマンスが落ちる(課金が増える?)かもしれないなと思いました。

speakerdeck.com

今後に向けて

幸いなことに参加者の方々からもそれなりに好評を得られたようだし、自分としても知見を得たい気持ちもあるため、主催メンバーで話し合いつつ第三回を企画していきたいと考えています。 ジャストアイデアですが、初心者枠?というかこれからデータ基盤を作っていこうとする人たちが発表しやすい枠を設けるとかもあるとイベントの雰囲気変わるかなとか。 その時が来たらまたアナウンスしますので、ご興味ある方々いらっしゃいましたらぜひぜひ!!

技術書典5で配布した同人誌の原稿データを GitHub で公開しました

こちらで紹介した、僕が主催するサークルで技術書典5で配布した同人誌の原稿データを公開しました。

syucream.hatenablog.jp

リポジトリはこちらになります。

github.com

前回と同様、 Re:VIEW を使って記述しています。 epub / pdf ファイルが欲しい方はいい感じにビルドしてください。

もし多少でも気に入った方がいらっしゃれば、 Kindle 版を購入していただけると幸いです! 僕のジュース代の足しくらいにはなります。

www.amazon.co.jp

Kubernetes と CSI(Container Storage Interface) について

この記事は Kubernetes2 Advent Calendar 4 日目の記事です。

本記事は CSI(Container Storage Interface) と Kubernetes での CSI のサポートについて触れます。 執筆に時間があまり割けなかった為、後でもう少し加筆する、あるいは別途続きの記事を書くかもしれません。

CSI(Container Storage Interface) とは

CSI(Container Storage Interface, 以下 CSI) は Kubernetes など複数の Container Orchestration (CO) から共通のプラグインを使って SP (Storage Provider) とやり取りするためのインタフェース仕様です。 より具体的には、 CO からストレージを抽象化した Volume をアタッチ・デタッチしたりスナップショットを取ったりする機能のためのインタフェースを提供します。 CSIKubernetes とは独立したプロジェクトで進行しております。

github.com

また Kubernetes のプロジェクト以下で、 Kubernetes から SP が提供する CSI プラグインへ通信するための CSIDriver と関連実装を以下プロジェクトで進行しています。

github.com

Kubertenes としては CSI は 1.9 で Alpha 、 1.10 で Beta サポート対象になっています また Kubernetes での CSI 利用については他に既に試されている方もいるようです。 こちらの記事は詳細に踏み込んだ構成説明などもされており、オススメできる内容になっています。

qiita.com

CSI の仕様

CSI のインタフェース仕様は Protocol Buffer で定義されており、以下リポジトリで管理されています。 またこのリポジトリ内の lib/go/ ディレクトリには .proto ファイルの定義に従ったライブラリの Go 実装が配置されています。

github.com

CSIプラグインは Controller plugin, Node plugin の二種類が想定されております。 また CO とこれら CSI プラグイン間は gRPC で通信することが想定されています。

CSI のサービス

CSI のサービスとして以下三種類が想定されています。 構成によっては実装しなくても良いサービスやメソッドがありますが、少なくとも Node Service の NodePublishVolume, NodeUnpublishVolume は実装する必要があります。 (でないと Node から Volume を参照できないはず)

  • Identity Service
    • CO から Plugin のケーパビリティ (後述) やヘルスチェック、メタデータ参照を可能にするサービス
  • Controller Service
    • Node をまたいで Volume の構成を管理するためのサービス
    • Volume のスナップショット操作もサポートする
  • Node Service
    • 各 Node から Volume の操作を可能にするためのサービス

CSI における Volume の状態遷移

CSI で Node から Volume を利用されるまでに、 Volume は基本的に以下のような状態遷移をしていきます。 幾つかの状態は、プラグインが示す後述の CSI ケーパビリティによって存在しなかったりします。

   CreateVolume +------------+ DeleteVolume
 +------------->|  CREATED   +--------------+
 |              +---+----+---+              |
 |       Controller |    | Controller       v
+++         Publish |    | Unpublish       +++
|X|          Volume |    | Volume          | |
+-+             +---v----+---+             +-+
                | NODE_READY |
                +---+----^---+
               Node |    | Node
              Stage |    | Unstage
             Volume |    | Volume
                +---v----+---+
                |  VOL_READY |
                +------------+
               Node |    | Node
            Publish |    | Unpublish
             Volume |    | Volume
                +---v----+---+
                | PUBLISHED  |
                +------------+

ref. https://github.com/container-storage-interface/spec/blob/master/spec.md#volume-lifecycle

CSI のケーパビリティ

CSI プラグインでどのような操作をサポートするかの情報です。 CSI の仕様としては以下 4 種類のケーパビリティをサポートします。

  • PluginCapability
    • CSI プラグイン全体でサポートする機能
    • Controller Service を提供するか、オンラインボリューム拡張をサポートするかなどの情報を含む
    • CO は GetPluginCapabilities メソッドで取得可能
  • VolumeCapability
    • Volume のファイルシステムのタイプやアクセス制御(リードオンリーなのかなど)、容量などの情報を含む
    • CO は CreateVolume, ControllerPublishVolume などのメソッドで指定や値の検証が可能
  • ControllerServiceCapability
    • Create/Delete , Publish/Unpublish などの操作をサポートするかの情報を含む
    • CO は ControllerGetCapabilities メソッドで取得可能
  • NodeServiceCapability
    • Stage/Unstage などの操作をサポートするかの情報を含む
    • CO は NodeGetCapabilities メソッドで取得可能

CSI の利用用途

CSI プラグインとしてはすでに、 NFSiSCSI などの公式のサンプルプラグインを含め、 AWSGCP のストレージサービスの利用を可能とするプラグインが存在します。 以下の公式ドキュメントに既に存在する CSI プラグインに関する記述があります。

kubernetes-csi.github.io

簡単な CSI Plugin の実装を追ってみる

以上だけだと CSI プラグインの動作が把握し難いので、 kubernetes-csi の drivers リポジトリにある HostPath plugin の動作を追ってみます。 このプラグインリポジトリkubernetes への deploy 用 yaml ファイルも同梱されており動作を追うのに便利です。

HostPath plugin では CSI の Identity, Controller, Node 三種類の gRPC サービスを提供します。 その実装は github.com/kubernetes-csi/drivers/tree/master/pkg/hostpath に存在します。

Identity Server は特に特殊な実装をしていません。 github.com/kubernetes-csi/drivers/blob/master/pkg/csi-common に存在する、他の Driver と共通のデフォルトメソッド実装をそのまま使用しています。

Controller Server では CreateVolume, DeleteVolume , CreateSnapshot, DeleteSnapshot, ListSnapshots を実装します。 CreateVolume, DeleteVolume では複雑なことをしておらず、 UUID 付きの名前のディレクトリを掘って Volume に見立て、 map で Volume の管理を行います。 CreateSnapshot, DeleteSnapshot はそれに似て、 Volume のディレクトリを tar czf で固めて同じく UUID 付きのファイルに保存・管理します。

Node Server では NodePublishVolume, NodeUnpublishVolume, NodeStageVolume , NodeUnstageVolume を実装します。 NodePublishVolume, NodeUnpublishVolume は作成された Volume のディレクトリを k8s.io/kubernetes/pkg/util/mount パッケージを介して mount/unmount します。 NodeStageVolume , NodeUnstageVolume は単にリクエストパラメータ中の Volume や ターゲットパスが空でないことをチェックしているだけになります。 HostPath プラグインでは他に複雑なチェックをする必要が無いためこうなっているのではと思われます。

kubernetes 上で想定される構成は以下の通りになっているようです。

おわりに

以上、 CSI とその Kubernetes へのインテグレーションに関する記事でした。 CSI は可搬性を担保しつつ Kubernetes での利用を重要視されて作られているようですが、 CSI の仕様含めエコシステムもまだ枯れているとは言えず、機能も多くはありません。 Kubernetes 用の単なるサンプルとはいえ HostPath プラグインで tar コマンドを実行してスナップショットを作成したりなど、もう少し抽象化されてほしい箇所もまだ存在します。

しかしながら個人的には Kubernetes での柔軟なストレージ利用には、以前の記事で挙げた通りまだまだ課題があると思っており、今後の発展を期待したいと考えています。

syucream.hatenablog.jp

Kubernetes 上で動作するコンテナから安全に FUSE を利用したかった

本題の通りの気持ちがあったのですが、結論としては手軽にできる良い方法は無いようでした。 備忘録的に挑戦した事を記録しておきます。

背景: FUSE 利用のモチベーション

言うまでもなくファイル I/O はシステム開発においてよく使われる機構であり、多くのプログラミング言語において標準ライブラリまたはそれに類するライブラリでファイル I/O をサポートしていると思われます。

さて Kubernetes 上でマイクロサービスを実装していく事が多々あるこのご時世、各マイクロサービスで実装言語非依存で使えるデータの読み書きのロジックがあると、例えば何らかの設定ファイルの動的受け渡しやロギングにおいて便利な可能性があります。 これに似た課題を抱えて解決に向かったプロダクトとして Envoy Proxy があるかと思われます。 これは元々ネットワーキングやトレーシングの課題などを各言語ごとのライブラリで対応していたもののつらくなり、 HTTP や gRPC を解釈するプロキシを導入することで緩和に成功しています。

前述の通りファイル I/O は HTTP や gRPC をしゃべるより更にリーズナブルな共通プロトコルであると考えられもしそうです。 さらに FUSE を使えばファイルシステムとしてインタフェースを提供して、かつ裏側で複雑なロジックを動かすこともできるように考えられます。 そんなわけで、 Kubernetes 上で動作するコンテナからいい感じに FUSE で実装したファイルシステムをマウントして利用できると良いかもと着想した次第です。

FUSE 利用の限界

同じような思考をしたり、あるいは既存 FUSE 資産を利用したい人たちが多々居たようで、 Kubernetes 公式リポジトリの Issue でも議論がされていたりします。

github.com

残念ながらこの 2015 年に open した issue は 2018 年も終わりを迎える今日においても close されていません。 どういう点がネックになるかというと

  • /dev/fuse を open するのに特権が必要
  • mount, umount するのに SYS_ADMIN ケーパビリティが必要

が挙げられ、一応利用できなくは無いものの利用条件を満たすため現状は privileged mode でコンテナを動かす羽目になり、やや安全面でリスクがあるように思えます。

FUSE 利用パターンいくつか

前述の Issue の中で幾つかのアイデアも提示されています。少しこれらを実際に試してみることにしました。 多少このリスクを緩和するため、大きく分けて以下の方針がありそうです。

  • コンテナ外でなんとかするパターン
  • 特権を持つコンテナをアプリケーションを動作させるコンテナと分離するパターン

前者は場合によっては可搬性を損なうか導入が非常に困難になる恐れがあり、また後者であればコンテナ内の世界で完結させることができそうです。 従ってまずは後者のアイデアを試してみます。

以下に検証用に用意した Dockerfile や設定ファイルを配置しました。 FUSE でマウントするのは libfuse に含まれる、ファイルを read すると "Hello, World!" を返す hello.c を利用しておきます。

github.com

こちらのリポジトリでは initContainers で mount するのと、 postStart , preStopサイドカーコンテナで mount/umount する案を試してみました。 が、結局マウント先を参照するアプリケーションコンテナからはマウントした先は参照できなくなります。 結局この例だと特権を渡さざるを得ませんでした。

次の一手のアイデア

今回特権を要求されたのが、コンテナ内から /dev/fuse が open できない点なので、この点を何とかできれば 多少課題は緩和されるかもです。 Kubernetes は Device Plugin という機構を最近サポートしているようで、これで何とかできる可能性があるような全く無いような気がしています。 また Kubernetes v1.10 からベータになったというストレージ接続インタフェース CSI は将来も見据えてこの問題に取り組むのに、もしかしたら良い機構かもしれません。

いずれにせよ Kubernetes 自体の調査をほとんどやっていないので次の一手はこれら周辺技術の調査からかと考えております。

ISUCON8 に参加して最終成績が本戦3位だった

タイトルのとおりです。何やかんやあり ISUCON8 の予選を無事突破した後に 10/20(土) LINE さんのオフィスにて本戦に参加して、最終成績 3 位に収まりました。 3 位だと特に表彰されるわけでもなく気持ちのみなのですが。

チーム構成と役割分担について

職場のよしみで cubicdaiya さん、 catatsuy さんと何らか SRE チームらしき雰囲気を醸し出しながら参戦しました。 大枠としては以下の役割分担を組んでいました。

  • インフラ担当
    • cubicdaiya
  • アプリ担当
    • catatsuy
    • syucream

全体チーム構成やツール・環境構築お膳立ては catatsuy さんがいい感じにやってくれていました。

予選について

本記事執筆にあたり予選実施日と期間が空いて、ほぼすべての記憶を損失しました。 様子はたぶん catatsuy さんの以下記事にある気がします。

medium.com

本戦について

だいたい出題された課題の要点は以下記事で説明されています。

isucon.net

3 位に入賞できた決めては突出したコレという点は無く、チームでそれぞれ貢献できた、バランス的には良い塩梅なのではと感じました。 具体的なタイムラインとしては覚えている限り以下のとおりです。

前半: 好調な滑り出し

  • とりあえず各々初期準備をする

    • 自分はここでは DB スキーマを取り出して共有したり WebUI 調べたり
  • catatsuy さんが早期にログ分析APIの叩き方問題に気づく

    • 着想としてはrate limitとかより重要なパスでI/Oしていそうなことがやばそう
    • 11 時台に議論して、ハマりそうだから手を打とうと結論づけて catatsuy さんが着手しだす
  • syucream が LIMIT 1 問題に気づく

    • 最初はサブクエリ書く感じで改善したが LIMIT 1 するのと課題的に差分がそれほどなかった
    • ここで一時的に 5000 点ほどスコアを叩き出しトップに躍り出る
    • 時間軸が入れ替わるが、この改善の実施直前に cubicdaiya さんが slow query を出してくれていてプロファイルの裏付けもした上で実施した
  • cubicdaiya さんがインフラ周り、特に nginx 周辺のチューニングを徐々にする

    • 冗長そうなコンテナ化をやめる
    • 静的ファイルを nginx で配信

中盤: 次ステップへの到達との葛藤

  • 会場提供でランチとして弁当が出る

    • AbemaTV のニュースで鯖の漁獲量が減って値段が若干上がった話を思い出し、僕は思わず鯖味噌煮弁当を確保
  • catatsuy さんのログの集約送信が動き出す

    • send_bulk API の利用がうまくいかず、時間ももったいないのでバッファリングして send するだけにとどめたり
    • この瞬間はスコアに響かなかったが、後々ボディブローのように効いてきた感じがある
  • syucream が地味に無駄そうな user table のレコードロックを外す

    • slow query ではあったが、全くスコアに響かなかった
  • cubicdaiya さんがそろそろいいでしょと良い SNS share 機能を有効化する

  • syucream が雑にロウソク足チャートのオンメモリキャッシュを入れる

    • マージした後に、一度キャッシュをすると別タイミングに対してのクエリにもキャッシュヒットしてしまうバグに気づく
    • が、ベンチマーカが文句を言わなかった!
    • 一瞬チームで相談して、問題が出てないなら後で考えようと結論づける
  • cubicdaiya さんが複数台構成を考え始める

    • 特に異論無く、 1: DB, 2: App, 1: nginx という意思決定が即なされる

後半: 着眼点は悪くなかったがもう一歩だったか

  • catatsuy & syucream で orders & trade の課題について考え出す

  • その裏で cubicdaiya さんが App サーバを 2 台構成にしていた

    • このあたり?でスコアが 10000 近くに到達
  • catatsuy さんがミクロな最適化したり、 syucream が銀行 API アクセスを並列化したり N+1 を無くそうとして爆死したり

  • その裏で cubicdaiya さんが 4 台フルに使い切る構成への移行を追えてチューニングに入ったり

  • DB の負荷をさげたくて ORDER BY を使うクエリを Go のアプリレベルに移植して時間がたりなかったり

  • 一部さばけない負荷に対して cubicdaiya さんが async sleep 入れたり

    • syucream はこれが大きな差別化ポイントだった気がしている

そうこうしている間に 17:50 頃になって収束させて終了

'もう一歩' に対する所感

  • 着眼点は非常に良くて、 PARTITION に対する疑惑や POST /ordersRunTrade() 最適化は課題に感じていて一部取り組んでいました
  • あらかじめ配布された spec を読み込んだり、外部 API に対して向ける疑惑がもっとあっても良かったかも

結論: ISUCON はいいぞ

今回の出題の外部 API が絡む話は個人的によく練られていて、個人的に非常に好ましい話でした。なぜならこういう外部APIとその挙動の差異は現実問題に起こりうりそうな話だからです。 そういう観点から言うと、学生チームが優勝したという展開はとても興味深いようにも思えます。 個人的課題として、自分が当日見ていた箇所で見どころは誤っていないけど一歩足りなかったことが挙げられ、狂おしいほど悔しいです。

もし次回参加して同じメンバーでチームを組むのなら、 catatsuy さんが優勝するのに並々ならぬ熱意を抱いているのでたぶん僕らが優勝するでしょう。

POSIX message queue を Go のコードから利用するためのライブラリ posix_mq を作った

表題の通りです。

github.com

cgo を使って POSIX message queue の基本的な操作、 open/close と send/receive とその他細々とした機能を実装しています。 とは言っても、それほど複雑なことはしておらず、 POSIX の関数呼び出しを愚直に Go の func にラップしているだけなのですけどね。

なぜやったのか

入門 Kubernetes などを読むと、 Pod 内のコンテナ同士では SysV / POSIX の IPC namespace を共有している記述があります。

www.oreilly.co.jp

Pod 内の別コンテナへの通信となると、 sidecar パターンで Envoy を動かすようなネットワーキング用プロキシを介するのに利用したり fluentd などのロギングエージェントにログを送ったり、お決まりのパターンがあると思います。 そういった際に低コストで非言語依存なプロトコルが欲しくなることが多々あるように考えられます。

POSIX message queue は POSIX としての仕様も存在し、低コストな IPC 手段のひとつです。 これを選択肢のひとつとして用意しておくことは発生する課題に柔軟に対応するのに重要であると考えます。 手段は他にもあるし、同様の機能であれば SySV message queue やいっそ AMQP などをしゃべっても良い気もするのですが、手段を増やす意味でも今回のライブラリを開発してみた次第です。

動作例

sender / receiver の通信例

シンプルなsender と receiver であれば以下のコードで実装してやり取りできます。

  • sender.go
package main

import (
    "fmt"
    "log"
    "time"

    "github.com/syucream/posix_mq/src/posix_mq"
)

const maxTickNum = 10

func main() {
    oflag := posix_mq.O_WRONLY | posix_mq.O_CREAT
    mq, err := posix_mq.NewMessageQueue("/posix_mq_example", oflag, 0666, nil)
    if err != nil {
        log.Fatal(err)
    }
    defer mq.Close()

    count := 0
    for {
        count++
        mq.Send([]byte(fmt.Sprintf("Hello, World : %d\n", count)), 0)
        fmt.Println("Sent a new message")

        if count >= maxTickNum {
            break
        }

        time.Sleep(1 * time.Second)
    }
}
  • receiver.go
package main

import (
    "fmt"
    "log"

    "github.com/syucream/posix_mq/src/posix_mq"
)

const maxTickNum = 10

func main() {
    oflag := posix_mq.O_RDONLY
    mq, err := posix_mq.NewMessageQueue("/posix_mq_example", oflag, 0666, nil)
    if err != nil {
        log.Fatal(err)
    }
    defer mq.Close()

    fmt.Println("Start receiving messages")

    count := 0
    for {
        count++

        msg, _, err := mq.Receive()
        if err != nil {
            log.Fatal(err)
        }
        fmt.Printf(string(msg))

        if count >= maxTickNum {
            break
        }
    }
}

Kubernetes の同一 Pod 上 container 通信例

折角なので上記の sender / receiver を Kubernetes の Pod に押し込んで通信させてみます。 あらかじめてきとうに sender / receiver 用の Docker イメージを作っておいてください。

Pod の定義なのですが、愚直に container の設定を羅列していくだけです。 IPC namespace は勝手に共有されるのでそれに関する設定や準備は必要ありません。

apiVersion: v1
kind: Pod
metadata:
  name: posixmq-pod
spec:
  containers:
    - name: posixmq-sender
      image: "posix_mq_sender"
      imagePullPolicy: IfNotPresent
    - name: posixmq-receiver
      image: "posix_mq_receiver"
      imagePullPolicy: IfNotPresent
  restartPolicy: Never

Pod の動作確認をさくっとしてみましょう。

$ kubectl apply -f example/kubernetes/pod-posixmq.yaml
pod "posixmq-pod" created
...
$ kubectl logs posixmq-pod -c posixmq-sender
go run example/exec/sender.go
Sent a new message
Sent a new message
Sent a new message
Sent a new message
Sent a new message
Sent a new message
Sent a new message
Sent a new message
Sent a new message
Sent a new message
$ kubectl logs posixmq-pod -c posixmq-receiver
go run example/exec/receiver.go
Start receiving messages
Hello, World : 1
Hello, World : 2
Hello, World : 3
Hello, World : 4
Hello, World : 5
Hello, World : 6
Hello, World : 7
Hello, World : 8
Hello, World : 9
Hello, World : 10

この出力結果を見るに、 sender の送ったメッセージがちゃんと receiver に届いていそうです!

余談

POSIX の機能となると可搬性を期待してしまいますが、 POSIX message queue は darwinwindows では実装されていなかったりと意外に可搬性に欠けます。 対して SysV の message queue はこれに比べて可搬性が高く、より多くの環境でサポートされています。 (このあたりは Linuxプログラミングインタフェース にも記述されていますね!)

www.oreilly.co.jp

とは言っても本記事で書くようにあらかじめ環境が定められている Kubernetes クラスタ上で動かす場合は、それほど気にすることでも無いのかもしれません。 また、 SysV message queue の Go ラッパーライブラリは Shopify により実装されているのでこれを試すのもアリかもです。

github.com

ちなみに少し前のベンチマーク内容ですが、 POSIX message queue は IPC の手段として結構高パフォーマンスであるような調査結果もあります。

www.programering.com