株式会社クリアコード > ククログ

ククログ

タグ:

Fluentd pluginのconfigure内でのアンチパターン

多くのFluentdのプラグインを見てきて知見が増えてきたのでまとめます。

config_sectionを使っていない

Fluentdのプラグインでは、configureメソッド内で設定を自力で解釈することによりconfig_sectionconfig_paramで定義していない設定値も扱えるようになっています。

以下のような設定ファイルを

<source>
  @type sample
  user hoge
  pass secret_passowrd
  <pattern>
    name foo
  </pattern>
  <pattern>
    name bar
  </pattern>
</source>

次のコードで自力で解釈することができます。

def configure(conf)
  super
  @patterns = conf.select {|element| element.name == "pattern" }
  @user = conf["user"]
  @pass = conf["pass"]
end

このように自力で解釈すると、テストも書かなければならないし、間違いも発生しやすくなるので以下のように、Fluentdの本体でよくテストされているconfig_sectionconfig_paramを組み合わせて使いましょう。config_sectionconfig_paramを使うと設定を宣言的に書くことができて、とてもわかりやすくなります。その上、fluent-plugin-config-formatというコマンドでMarkdownやJSONで出力することができます。descを使って説明も加えておくと、fluent-plugin-config-formatでも説明が出力されて便利です。

config_section :pattern, param_name: "patterns", multi: true do
  desc "name for pattern"
  config_param :name, :string
end
desc "user name for login"
config_param :user, :string
desc "password for login"
config_params :pass, :string, secret: true

def configure(conf)
  super
  # 何か追加の処理
end

config_paramのarrayを使っていない

<source>
  @type github-activities
  users ashie,cosmo0920,kenhys,kou
</source>

上記のような設定を以下のようなコードで、配列化して使っているプラグインがありました。

desc "user names e.g. user1,user2,user3"
config_param :users, :string, default: ""
def configure(conf)
  super

  @users = @users.split(",").map(&:strip)
end

これは以下のように書き換えることができます。

desc "user names e.g. user1,user2,user3"
config_param :users, :array, default: [], value_type: :string
def configure(conf)
  super
end

config_paramのenumを使っていない

<source>
  @type groonga
  protocol http
  # ...
</source>

上のような設定を次のようなコードでパースして使用しているプラグインがありました。

config_param :protocol, default: :http do |value|
  case value
  when "http", "gqtp", "command"
    value.to_sym 
  else
    rails Fluent::ConfigError, "must be http, gqtp or command: <#{value}>"
  end
end
def configure(conf)
  super
  # @protocol を使う
end

これは以下のように書き換えることができます。

config_param :protocol, :enum, list: [:http, :gqtp, :command], default: :http
def configure(conf)
  super
  # @protocol を使う
end

書き換えることで、コードがすっきりしました。

秘密の値をsecretにしていない

<match dummy.log>
  @type honeycomb
  writekey very-secret-key
  # ...
</match>

パスワードやAPIキーなどの秘密にしておきたい情報を設定ファイルに書かせるときは、secretオプションを付けましょう。 secretオプションをtrueにするとFluentdの起動時に表示されるダンプなどで値がマスクされます。

config_param :writekey, :string, secret: true

以下のようにsecretオプションを忘れてしまうと、Fluentd起動時のダンプなどにそのまま出力されてしまうので、バグ報告などのときにユーザーが自分でマスクしなければなりません。

config_param :writekey, :string

必須チェックを自分でしている

config_param :tag, :string, default: nil
def configure(conf)
  super
  raise Fluent::ConfigError, "tag is required" unless @tag
end

上のように必須の値のデフォルト値をnilにして、自分でチェックしているコードをたまに見かけます。 下のようにデフォルト値を省略するとその設定は必須になり、Fluentd起動時にチェックが実行されます。

config_param :tag, :string
def configure(conf)
  super
end

desc を書いていない

config_param :tag, :string

descを書くとfluent-plugin-config-formatというコマンドで、説明を見やすくフォーマットしたものを出力することができます。これはREADMEに設定の説明を書くときに使うととても便利です。

desc "tag for records"
config_param :tag, :string

まとめ

Fluentdで用意されている機能を活用して、メンテナンスを続けやすいプラグインを開発しましょう。

タグ: Fluentd
2017-06-21

Firefoxの先読み機能の無効化とその確認手順

※この記事の情報はFirefox ESR52を対象としています。これより新しいバージョンではこれらの情報は当てはまらない場合がありますので、ご注意下さい。

Firefoxの先読み機能の種類と無効化の方法

FirefoxにはWebサイトの閲覧時の体感的な快適さを向上するため、実際にリクエストが行われる前から接続やデータのダウンロードを行っておくという「先読み」の機能があります。ただし、無差別に全てのリンクを辿るのではなく、いくつかの条件が揃った時に初めて先読みが発動するようになっています。具体的には以下の要領です。

ユーザーから見れば快適さが増して嬉しい機能ですが、反面、ネットワークのトラフィックやセッション数は増大する事になります。弊社のMozillaサポート事業のお客様からも、契約回線の帯域が限られているなどの理由で、情報システム管理担当者の意向として「先読み機能を無効化したい」というご相談を頂く事があります。また、「既に担当者側でそのような設定を行ったつもりだが、実際にその指定が機能しているかどうかを確認したい」というご相談もあります。

前述の先読みに類する機能を全て無効化する場合、指定は以下のようになります。

  • 全般的な制御
    • network.predictor.enabledfalse(真偽型)
    • network.predictor.enable-hover-on-sslfalse(真偽型)
    • network.predictor.enable-prefetchfalse(真偽型)
  • 次のページを示すリンクの先読み
    • network.prefetch-nextfalse(真偽型)
  • DNSプリフェッチ
    • network.dns.disablePrefetchtrue(真偽型)
    • network.dns.disablePrefetchFromHTTPStrue(真偽型):TLSを使用したページでの挙動の制御。
  • リンクの上にポインタが載った時の投機的接続の抑止
    • network.http.speculative-parallel-limit0(整数型)

では、これらの設定が期待通りに反映されている事をどのように確認すればよいのでしょうか。これがこの記事の本題です。

設定が反映されているかどうかの確認

設定の書き間違いのようなケアレスミスや、それ以外にも何らかの理由から、設定したはずの値が反映されないままになっているという事は度々あります。そのため、この手のカスタマイズはなるべく、実際の挙動から設定の反映状況を確認する事が望ましいです。

データのダウンロードまでも行う場合の確認

HTTP接続からデータのダウンロードまでを行うフルスペックの「先読み」については、先読み対象となるHTTPリクエストが実際に処理されたかどうかを見るのが確実です。例えばこのブログの2016年5月10日の記事同年5月18日の記事へのリンクを含んでいますが、このリンクにはrel="next"という属性が指定され次のページへのリンクである事が示されているため、先読みの対象となります。

このような先読みは、ブラウザコンソールで動作を確認できます。 キーボードショートカットの「Ctrl-Shift-J」を使うかパネルメニューの「開発ツール」(またはメニューバーの「Web開発」)で「ブラウザコンソール」をクリックするとブラウザコンソールのウィンドウが開かれますので、そのウィンドウ上部の「ネットワーク」がハイライトされている状態でこのブログの2016年5月10日の記事を開いてみて下さい。先読みが機能していればGET http://www.clear-code.com/blog/2016/5/18.htmlというメッセージがコンソールに出力され、機能していなければこのメッセージは出力されません。

コンソールに出力される出力が多すぎて見分けが難しいといった場合には、低レベルのログでも動作を確認できます。この方法については後述します。

DNSの名前解決のみ行われる場合の確認

先読み機能には、DNSでの名前解決のみを先行して行う機能(DNSプリフェッチ)もあります。例えば、ページ内に<link rel="dns-prefetch" href="http://dns-prefetch.example.com">のような記述が含まれていると、FirefoxはHTTPでのリクエストではなく、dns-prefetch.example.comの名前解決の問い合わせのみを単独で行います。

この機能が動作しているかどうかを確認するためは、DNSへの問い合わせを行うモジュールが内部的に発行するイベントを監視する必要があります。この確認にもブラウザコンソールを使います。

そのためには、about:configで以下のように設定します。

  • devtools.chrome.enabledtrue(真偽型):スクリプトの実行を可能にする。
  • network.dns.notifyResolutiontrue(真偽型):DNSでの名前解決時に内部的なイベントを発行するようにする。

上記の設定を反映した状態でブラウザコンソールを開くと、コンソール下部に入力欄が出現します。この入力欄に Services.obs.addObserver(function(aSubject, aTopic, aData) { console.log(aTopic+': '+aData); }, 'dns-resolution-request', false); と入力してEnterキーを押すと、この内部的なイベントを捉えてコンソールにメッセージとして出力する事ができます。DNSプリフェッチのための指定が含まれているWebページ(テストケース)を開いた時にdns-resolution-request: dns-prefetch.example.comのようなメッセージがブラウザコンソールに出力されれば、DNSプリフェッチのための指定が処理されたと分かりますし、逆に、そのようなWebページを開いてもこのメッセージがコンソールに出力されなければ、DNSプリフェッチは無効化されていると判断できます。

TCP接続のみ行われる場合の確認

Firefoxの先読み機能の中には、データの読み込みは行わないまでも、ソケット接続のみ確立しておくという機能もあります。リンクの上にポインタが載った時点でまずソケット接続だけ先行して行っておき、クリックされた時にすぐにHTTPのリクエストを送出できるようにするというもので、この機能は投機的接続と呼ばれます。

ソケット接続を確立しただけの段階ではコンソールには何も出力されず、また、DNSプリフェッチのような内部的なイベントも発行されないため、投機的接続が行われているかどうかは、低レベルのログを解析して調べる必要があります。

Firefox 52ではログ収集対象のモジュールやログファイルの出力先は環境変数ではなく設定値で指定します。about:configで以下の設定を作成して下さい。

  • logging.nsHttp5(整数型)
  • logging.NetworkPredictor5(整数型)
  • logging.config.synctrue(真偽型)
  • logging.config.add_timestamptrue(真偽型)
  • logging.config.clear_on_startupfalse(真偽型)
  • logging.config.LOG_FILEC:\Users\Public\predict.log(文字列型)

この状態でFirefoxを再起動すると、logging.config.LOG_FILEで指定したパスの位置にpredict.log-main.123のような名前でログファイルが出力され始めます。(mainは親プロセスのログであることを示します。ファイル名の末尾の数字はプロセスIDです。)投機的接続の機能は通常のWebブラウズの中で暗黙的に行われるため、この状態のまましばらくWebページを2〜3画面分ほど見て回り、十分な量のログを溜めて下さい。

ログが溜まったら、上記のログ出力用の設定をリセットしてFirefoxを再起動し、出力されたログファイルをブラウザで開きます。この時、ログは以下のような内容になっているはずです。

2017-06-02 09:51:57.565000 UTC - [Main Thread]: I/Logger Flushing old log files
2017-06-02 09:51:57.659000 UTC - [Main Thread]: D/nsHttp Creating nsHttpHandler [this=9fe1400].
2017-06-02 09:51:57.659000 UTC - [Main Thread]: D/nsHttp nsHttpHandler::Init
2017-06-02 09:51:57.659000 UTC - [Main Thread]: D/nsHttp nsHttpHandler::PrefsChanged [pref=(null)]
2017-06-02 09:51:57.659000 UTC - [Main Thread]: D/nsHttp nsHttpHandler::PrefsChanged Security Pref Changed (null)
2017-06-02 09:51:57.659000 UTC - [Main Thread]: D/nsHttp nsHttpHandler::MakeNewRequestTokenBucket this=9fe1400 child=0
2017-06-02 09:51:57.659000 UTC - [Main Thread]: D/nsHttp nsHttpAuthCache::Init
2017-06-02 09:51:57.659000 UTC - [Main Thread]: D/nsHttp nsHttpAuthCache::Init
2017-06-02 09:51:57.659000 UTC - [Main Thread]: V/nsHttp Creating nsHttpConnectionMgr @b5a1420
...

続けて、キーボードショートカットの「Ctrl-Shift-K」を使うかパネルメニューの「開発ツール」(またはメニューバーの「Web開発」)で「Webコンソール」をクリックし、ログファイルを読み込んだタブに対してコンソールを表示します。コンソール下部の入力欄に以下のログ解析用のスクリプトをコピー&ペーストし、Enterキーを押して実行します。

var body = document.body.textContent;
var requested = body.match(/nsHttpConnectionMgr::OnMsgSpeculativeConnect/g) || [];
var skipped = body.match(/Transport not created due to existing connection count/g) || [];
!requested.length ? 'not working' : requested.length == skipped.length ? 'all skipped' : 'preconnected';

すると、以下の3つのいずれかの解析結果がコンソールに出力されます。

  • preconnected:投機的接続が行われた。
  • all skipped:投機的接続が無効化されている(もしくは、同時接続数が最大値に達しているため投機的接続が行われなかった)。
  • not working:ネットワーク環境の都合等で投機的接続が行われていない。

3番目の「投機的接続が行われていない」というケースは、そのクライアントが接続しているネットワーク環境によって発生し得ます。 Firefoxは投機的接続を開始する前に接続先ホストがプライベートなネットワークに属しているかどうかを見ており、DNSで名前解決した結果が192.168.1.1010.21.0.100といったプライベートネットワーク上のIPアドレスだった場合には投機的接続の処理を中断するようになっています。 これは、投機的接続は元々、遠隔地にあるホストとのソケット接続の確立に時間がかかるためにフライングで接続しておくという趣旨の機能なので、ネットワーク的に近いホストに対しては行う意義が薄いからです。

他の先読み機能が働いているかどうかもデバッグ用ログから確認する

なお、前項で取得したログを解析すると、データの読み込みを伴う先読みが行われたかどうかも判別できます。今度は以下の解析用スクリプトをWebコンソールで実行します。

var body = document.body.textContent;
var success = body.match(/Predictor::Predict.*\n.*called on parent process.*\n.*not enabled/g) || [];
var all = body.match(/Predictor::Predict.*\n.*called on parent process/g) || [];
(success.length == all.length) ? 'all canceled' ? 'some canceled';

出力結果は以下の2つのうちいずれかです。

  • all canceled:先読み処理が無効化されている。
  • some canceled:先読み処理が有効である。

まとめ

以上、Firefox ESR52において先読みに類する機能の無効化の方法と、その検証方法をご案内しました。

ここで解説している情報の中には、まとまった情報源が無く、お客様のご要望を受けて調査した結果判明したという情報も含まれています。クリアコードではOSSのドキュメント化されていない仕様を調査して明らかにする業務も行っておりますので、導入をお考えのOSSが想定通りの動作をするかどうかに不安がある場合や、既にお使いのOSSが期待通りに動作せずお困りの場合などには、お気軽にお問い合わせ下さい

タグ: Mozilla
2017-06-16

Fluentd pluginでconfig_paramとconfig_sectionを使いこなす

Fluentdのプラグインの開発をする上で避けて通れないのが設定です。ユーザーに設定ファイルを書いてもらってプラグインの動作をカスタマイズできるようにすることは必須です。

config_paramconfig_sectionという2つのAPIを使いこなすとさまざまな設定を簡単にきれいに書くことができます。

config_param

config_paramは設定を定義するために使います。

次のように名前と型を指定するのが基本形です。

desc "description for name"
config_param :name, :string

この場合は:nameという名前の文字列型の設定を定義しています。型によって、使えるオプションが異なるので型ごとに説明します。なお、config_paramの直前にdescを書くと設定の説明を書くことができます。説明を書いておくとfluent-plugin-config-formatコマンドで説明付きで設定の定義をダンプすることができるようになります。

typeとして以下の9つの型を指定可能です。

  • :string
  • :integer
  • :float
  • :size
  • :time
  • :bool
  • :enum
  • :array
  • :hash
string, integer, float, bool

それぞれの型に変換して、指定された名前のインスタンス変数に値をセットします。

config_param :name, :string, default: "", secret: true, alias: :full_name
  • default: デフォルト値を指定します。省略するとこの設定は必須になり、Fluentd起動時に設定されているかどうかチェックされます。
  • secret: 真を指定すると、Fluentd起動時のログなどで値がマスクされます。
  • alias: この設定の別名を指定します。
size

バイト単位のサイズを設定します。k,m,g,tというサフィックスを利用可能です。それぞれ、キロ、メガ、ギガ、テラを表します。大文字、小文字は区別しません。 サイズは以下のように整数に変換されます。

limit 10  # 10 byte
limit 10k # 10240 byte
limit 10m # 10485760 byte
limit 10g # 10737418240 byte
limit 10t # 10995116277760 byte

使えるオプションはstring等と同じです。

time

時間の長さを指定します。s,m,h,dというサフィックスを利用可能です。それぞれ、秒、分、時、日を表します。小文字のみ有効です。 単位を省略するとto_fした値を使用します。秒に変換されます。

interval 0.5 # 0.5秒
interval 1s  # 1秒
interval 1m  # 1分 = 60秒
interval 1h  # 1時間 = 3600秒
interval 1d  # 1日 = 86400秒

使えるオプションはstring等と同じです。

enum

列挙型です。ユーザーに特定の値のリスト内から設定値を選ばせたいときに使います。ユーザーがリスト内に存在しない値を設定した場合、Fluentd起動時にエラーが発生します。

config_param :backend_library, :enum, list: [:geoip, :geoip2_c, :geoip2_compat], default: :geoip
  • default: デフォルト値を指定します。
array

配列です。

config_param :users, :array, default: [], value_type: :string

このように書くと、以下のような設定で@users = ["user1", "user2", "user3"]と設定されます。

users user1,user2,user3
  • default: デフォルト値を指定します。
  • value_type: 値の型を指定します。:string, :integer, :float, :size, :bool, :timeのいずれかを指定できます。
hash

ハッシュです。設定ファイルの書き方は2通りあります。

config_param :key_values, :hash, default: {}, symbolize_keys: true, value_type: :string

設定例:

key_values {"key1": "value1", "key2": "value2"} # JSONで書く
key_values key1:value1,key2:value2

どちらも以下のようにパースされます。

{ key1: "value1", key2: "value2" }
  • default: デフォルト値をハッシュリテラルで指定します。
  • symbolize_keys: 真を指定するとキーをシンボル化します。
  • value_type: 値の型を指定します。全ての値で同じ型を使用します。

config_section

config_sectionconfig_paramをグループ化するために使用します。 具体例としては、組み込みのバッファーの設定、パーサーの設定などがあります。

例えば、fluent-plugin-s3では以下のように認証情報の設定、バッファーの設定、フォーマッターの設定をグループ化して記述することができます。fluent-plugin-s3では認証方法が複数あるので、認証方法ごとにセクションを作ることによって、利用している認証方法がわかりやすくなっています。このように設定をグループ化することによって設定ファイルの可読性が向上しています。

<match *.log>
  @type s3
  <assume_role_credentials>
    role_arn xxxxx
    role_session_name xxxxx
  </assume_role_crecentials>
  <buffer tag,time>
    @type file
    path /var/log/fluent/s3
    timekey 3600 # 1 hour partition
    timekey_wait 10m
    timekey_use_utc true # use utc
  </buffer>
  <format>
    @type json
  </format>
</match>

config_section のメソッドシグニチャーは以下の通りです。

config_section(name, root: false, param_name: nil, final: nil, init: nil, required: nil, multi: nil, alias: nil, &block)
  • name: セクション名をシンボルで指定します。デフォルトではここで指定した名前と同じ名前のインスタンス変数をプラグインインスタンス内で参照することができます。
  • キーワード引数
    • root: ルートセクションである場合に真を指定します。Fluentd内部で利用するもので、一般のプラグインでは利用しません。
    • param_name: プラグインのインスタンス内で利用する、インスタンス変数名を@を除いたシンボルまたは文字列で指定します。
    • final: プラグインのサブクラスでこのセクションの上書きを禁止するならば真を指定します。一般のプラグインでは、あまり意識しなくてもよいです。
    • init: このセクションでは初期値が必須パラメータのみを定義します。一般のプラグインでは、あまり意識しなくてもよいです。
    • required: このセクションが必須であれば真を指定します。真を指定した場合、設定ファイルにこのセクションがないとFluentdの起動時にエラーが発生します。
    • multi: このセクションを複数指定可能であれば真を指定します。
    • alias: このセクションの別名を指定します。

例: sample というプラグインに以下のようなセクションがある場合の設定ファイルの書き方を例示します

config_section :child, param_name: 'children', required: false, multi: true, alias: 'node' do
  config_param :name, :string
  config_param :power, :integer, default: nil
  config_section :item, multi: true do
    config_param :name, :string
  end
end
<source>
  @type sample
  <child>
    name  gohan
    power 100
    <item>
      name senzu
    </item>
  </child>
  <child>
    name  goten
    power 10
  </child>
</source>

まとめ

config_paramconfig_sectionを使うことで得られる利点をまとめます。

  • plus1 設定を宣言的に定義できる
  • plus1 fluent-plugin-config-formatコマンドでMarkdownやJSONで定義を出力することができる
    • Markdownで出力したものはREADMEにそのまま貼り付けることも可能 100
  • plus1 コード量を少なくすることができる

config_paramconfig_sectionを使うことで、デメリットはありません。 Fluentd組み込みのAPIを使いこなして、使いやすくメンテナンスしやすいプラグインを書きましょう。

タグ: Fluentd
2017-06-14

データ分析用次世代データフォーマットApache Arrow勉強会(大阪) #osaka_arrow

須藤です。このあいだ試したらApache Arrowのリポジトリーにpushできたので私のコミット権関連の設定は完了しているようです。

東京近郊に住んでいる人向け情報:この大阪で開催したイベントと同じような内容のイベントを6月13日(明日!)に東京でも開催します!大阪だったので参加できなかったという方は、ぜひ東京でのイベントに参加してください。

2週間ほど前になりますが、2017年5月28日に大阪でApache Arrowの勉強会を開催しました。前日に関西Ruby会議2017で発表していたのですが、せっかく大阪に来たのでApache Arrowの普及活動をしようと思い、開催しました。会場はクラスメソッドさんに貸してもらいました。日曜日なのに会社を開けてもらってありがとうございます!

なお、クラスメソッドさんに会場提供をお願いしたのはOSS Gate大阪ワークショップでつながりがあったからです。思わぬところで意外なつながりが活きていて、ありがたいことです。

当日使った資料は次の通りです。中盤までは既存の情報を紹介したので、最初の方のページはそれらの情報へのリンク集になっています。終盤は今回用にまとめた情報を紹介したので、それらの情報がこのスライドのメインの内容になります。イベントの内容はクラスメソッドさんがまとめてくれたイベントレポートを参照してください。

関連リンク:

大阪で開催したApache Arrow勉強会を紹介しました。なんと、6月13日(明日!)に同様のイベントが東京でも開催されます!

会場提供はSpeeeさんです。SpeeeさんとはOSS開発支援DataScience.rbなどでつながりがあり、会場を提供してもらえました。ありがとうございます!

2017-06-12

RubyKaigiのCFPへの応募例 #rubykaigi

須藤です。関西Ruby会議2017が終わってからRubyKaigi 2017の発表を応募しました。

RubyKaigi 2017CFPがでています。CFPとはもともと(?学会の文脈で)はCall For Papersの略ですが、RubyKaigiの文脈ではCall For Proposalsの略で、「RubyKaigiでの発表を募集しています」という意味です。RubyKaigi 2017の発表の応募は6月17日まで受け付けています。

応募する人が増えるといいなぁと思うので、実際の応募例として私のRubyKaigi 2015からRubyKaigi 2017の分の応募内容を紹介します。どうしてRubyKaigi 2015の分からかというと、CFPアプリケーションができて応募の記録が残るようになったのがRubyKaigi 2015からだからです。

項目

実際の応募内容を紹介する前に、どのような項目を書く必要があるかを紹介します。

  • Title: 発表のタイトル(60文字以下。英語。)
  • Abstract: 発表の概要(600文字以下。英語。)
  • Details: 発表の詳細(日本語可日本語でも検討してもらえるが、RubyKaigiは国際カンファレンスなので英語が望ましい。)
  • Pitch: どうして自分がRubyKaigiでこの発表をするべきなのかの説明(日本語可日本語でも検討してもらえるが、RubyKaigiは国際カンファレンスなので英語が望ましい。)

TitleにはRubyKaigiに参加する人が「どの発表を聞くか」をパッと選ぶときに参考になりそうなものを書きます。

AbstractにはRubyKaigiに参加する人が「どの発表を聞くか」を検討するときに参考になりそうな説明を書きます。

Detailsには発表を選考する人が「どの発表を選ぶか」を検討するときに参考になりそうな発表の説明を書きます。発表を聞いた人が何を得られる発表なのかを書くとよいでしょう。

Pitchには発表を選考する人が「どの発表を選ぶか」を検討するときに参考になりそうな発表者の説明を書きます。たとえば、「私が実装した人なので私が一番詳しいです。なので、私が話すのが一番いいんです!」ということを書きます。

これらのことは応募フォームの説明にも書いているので、応募するときには説明をちゃんと読んで応募内容がずれていないか確認するとよいでしょう。

応募の書き方については以下の情報が参考になります。

明示的に書かれてい情報をインターネット上に見つけられませんでしたが、RubyKaigiは「Ruby」の「Kaigi」なので、Rubyで作られたなにかの発表よりもRubyそのものの発表の方がRubyKaigiにあっていそうです。

応募例:RubyKaigi 2015

RubyKaigi 2015での応募例です。この応募は採択されました。

発表内容:The history of testing framework in Ruby

Title:

The history of testing framework in Ruby

Abstract:

This talk describes about the history of testing framework in Ruby.

Ruby 2.2 bundles two testing frameworks. They are minitest and test-unit. Do you know why two testing frameworks are bundled? Do you know that test-unit was bundled and then removed from Ruby distribution? If you can't answer these questions and you're interested in testing framework, this talk will help you.

This talk describes about the history of bundled testing framework in Ruby and some major testing frameworks for Ruby in chronological order.

Details:

このトークは参加者が「自分にとって適切なテスティングフレームワークを選ぶための知識」を持ち帰れることを目標とします。

テスティングフレームワークはRubyで開発する上で重要な役割を持つソフトウェアです。Rubyを使うと非常に柔軟にプログラムを書くことができるため、意図せずに既存の挙動を変えてしまうことが起こしやすいです。自動化されたテストも一緒に開発すると、できるだけ低いコストでその問題の発生を抑えつつ、楽しくプログラムを書きやすくなります。それを支援するのがテスティングフレームワークです。

テスティングフレームワークはRubyで楽しくプログラムを書くために重要な役割にも関わらず、自分にとって適切なテスティングフレームワークを選ぶという活動はあまり行われていないように感じています。多くの人が使っているから、というのも選んだ理由としては妥当な理由ではあるのですが、流行が変わったとき・テスティングフレームワークのAPIが変わったときなど、今までと状況が変わったときに適切な対応を取りづらくなります。大きな流れが1つではなくなるからです。

自分で「どうしてこのテスティングフレームワークを選んでいるのか」を理解していれば、たとえ状況が変わったときでも適切な基準で適切なフレームワークを選びなおすことができるでしょう。

このトークではRubyにバンドルされたテスティングフレームワークの歴史といくつかの主要なRuby用のテスティングフレームワーク(RSpec, minitest, test-unitと関連ソフトウェアをいくつか)の歴史について時系列で紹介します。歴史を知ることで、それぞれのテスティングフレームワークが何を重視しているのか、そして、それが自分が大事にしていることと合致するのかを判断する材料になるはずです。これにより、参加者が「自分にとって適切なテスティングフレームワークを選ぶための知識」を持ち帰ることの実現を目指します。

Rubyにバンドルされたテスティングフレームワークの歴史については↓にまとめたものをベースとします。 http://www.clear-code.com/blog/2014/11/6.html

Pitch:

最新のRubyである2.2でtest-unitがバンドルされた背景(*)を知る人は少ないでしょう。RubyKaigiでのトークとして、RubyのユーザーがRubyの開発の歴史(の一部)を知る機会があるのは妥当だと考えます。

(*) Test::Unit互換APIを提供するため。そうしないと既存のテストが動かなくなってRubyのバージョンアップの障害になる人がでてバージョンアップの障害になるかもしれない。Python 3のように新しいバージョンがでても古いバージョンを使い続けるユーザーがたくさんいる状況は開発チームとしてはうれしくないので避けたい。

私はRubyのテスティングフレームワークの歴史に最初から関わっているわけではありません。Ruby 1.8と1.9の間くらいからだけです。しかし、その頃からRubyのテスティングフレームワークまわりについては観測してきましたし、このトークで登場するtest-unit gemテスティングフレームワークの現在のメンテナーです。そのため、私はこのトークをする人として適切だと考えます。

応募例:RubyKaigi 2016

RubyKaigi 2016での応募例です。この応募は採択されました。

発表内容:How to create bindings 2016

Title:

How to create bindings 2016

Abstract:

This talk describes how to create Ruby bindings of a C library. I want to increase Ruby bindings developers. If you're interested in using C libraries from Ruby and/or using Ruby more cases, this talk will help you.

This talk includes the following topics:

  • List of methods to create Ruby bindings of a C library.
  • Small example of each method.
  • Pros and cons of each method.

Details:

このトークの目標は参加者が「自分のユースケースにあったRubyバインディングの作り方を知ること」です。まったく知らないという参加者でもわかるようにします。「昔は知っていたけど最近のことはわからない」という参加者でも、2016年時点での最新情報を提供することで、得られるものがあるようにします。

多くのRubyistはRubyバインディングを作る機会はありませんし、作る必要もありません。しかし、作り方を知っていればいざというときにRubyバインディングを作るという選択肢が生まれ、よりRubyを活用できる可能性が高くなります。

Cライブラリーと連携できることはRubyのよいところの1つです。Cライブラリーと連携できると、Rubyスクリプトを高速化したり、既存のCライブラリーの機能を利用したりできます。速さが足りなくてRubyを使えない、機能が足りなくてRubyを使えない、そのようなときの解決策としてRubyバインディングの作り方が役に立ちます。

このトークではRubyバインディングの作り方として次の方法を紹介します。

  • RubyのC APIを使ったRubyバインディングの作り方
  • SWIGを使ったRubyバインディングの作り方
  • libffiを使ったRubyバインディングの作り方
  • GObject Introspectionを使ったRubyバインディングの作り方

それぞれについて具体的なコードとメリット・デメリットを紹介します。この情報をもって「自分のユースケースに適切なCライブラリーのRubyバインディングの作り方を知ること」の実現を目指します。

参考URL:スクリプト言語の拡張機能の作り方とGObject Introspectionの紹介

Pitch:

多くのRubyistはRubyバインディングを作る機会はありませんし、作る必要もありません。しかし、作り方を知っていればいざというときにRubyバインディングを作るという選択肢が生まれ、よりRubyを活用できる可能性が高くなります。

Cライブラリーと連携できることはRubyのよいところの1つです。Cライブラリーと連携できると、Rubyスクリプトを高速化したり、既存のCライブラリーの機能を利用したりできます。速さが足りなくてRubyを使えない、機能が足りなくてRubyを使えない、そのようなときの解決策としてRubyバインディングの作り方が役に立ちます。

以上より、Rubyを活用する機会を増やすため、RubyKaigiでRubyバインディングを作る方法を知る機会があるのは妥当だと考えます。

私は10個以上のRubyバインディングを作った経験があり、このトークで紹介するすべての作り方を使ってきました。そのため、私はこのトークをする人として適切だと考えます。

応募例:RubyKaigi 2017

RubyKaigi 2017での応募例です。この応募が採択されるかどうかはわかりません。

Title:

Improve extension API: C++ as better language for extension

Abstract:

This talk proposes better extension API.

The current extension API is C API. In the past, some languages such as Rust (RubyKaigi 2015), Go (Oedo RubyKaigi 05), rubex (RubyKaigi 2016) were proposed as languages to write extension.

This talks proposes C++ as a better language for writing extension. Reasons:

  • C++ API can provide simpler API than C API.
  • C++ API doesn't need C bindings because C++ can use C API including macro natively. Other languages such as Rust and Go need C bindings.
  • Less API maintenance cost. Other approaches need more works for Ruby evolution such as introduces new syntax and new API.

Details:

このトークの目標は参加者が「現在のC APIの課題を知ること」と「このトークで提案するC++ APIがよいかどうかを判断する材料を十分に得ること」です。前者に関しては拡張ライブラリーを書いたことがない人でもわかるようにします。後者に関しては拡張ライブラリーを書いたことががない人には判断できないでしょう。よって、このトークのメインターゲットは拡張ライブラリーを書いたことがある人です。この人たちはこのトークでよりよい拡張ライブラリーのAPIについての知見を得るはずです。拡張ライブラリーを書いたことがない人たちは拡張ライブラリーのAPIそのものについての知見が増えているはずです。

このトークではC APIの課題として次のことを説明します。

  • 関数のシグネチャーを2回書かないといけず、それらがズレていてもコンパイル時に発見できないため、実行時にクラッシュしてしまう可能性が高まる。
  • 冗長である。たとえば、メソッドを登録するときにCの関数を定義してそれを別途登録するケース、eachbegin rescueを実現するためにCの関数を定義するケース、などである。
  • CのデータとRubyのデータ間の変換が面倒であり、入力チェックのコードが多くなってコードの見通しが悪くなり、メンテナンス性が下がる。
  • Rubyは例外が発生するとlongjumpするので、例外発生前に動的に確保したメモリーの開放処理が煩雑になる。

それぞれについて具体的なコードを紹介します。たとえば、関数のシグネチャーを2回書かないといけないとは次のようなコードのことです。

static VALUE
rb_sample_hello(VALUE self)
{
  return rb_str_new_static("Hello");
}

void
Init_sample(void)
{
  VALUE sample = rb_define_class("Sample", rb_cObject);
  rb_define_method(sample, "hello", rb_sample_hello, 0);
}

このコードではhelloメソッドの引数の数が0であることを示すために、rb_sample_hello(VALUE self)と関数を定義し、それをRubyに伝えるためにrb_define_method(..., 0)を実行しています。引数の数が増えた時は両方更新する必要があります。片方の更新を忘れるとクラッシュします。

このような情報をもって「現在のC APIの課題を知ること」の実現を目指します。

それぞれの課題について、課題を解決するC++ APIを提案します。APIだけでなくどうして解決できるのか、および、実装も説明します。このAPIとその解説および実装の説明により「このトークで提案するC++ APIがよいかどうかを判断する材料を十分に得ること」の実現を目指します。

たとえば、関数のシグネチャーを2回書かないといけないケースについては、次のようなC++ APIを提案します。

void
Init_sample(void)
{
  rb::Class("Sample", rb_cObject).
    define_method("hello", [](VALUE self) {return rb_str_new_static("Hello");});
}

このAPIではC++のラムダ式[](VALUE self)から引数が0であることがわかるため、その情報を自動的にRubyにも伝えます。これにより、シグネチャーの不整合によるクラッシュを防ぐことができます。[](const char *self)のようにシグネチャーが不正な時はコンパイル時にエラーにすることもできます。

このような情報をもって「このトークで提案するC++ APIがよいかどうかを判断する材料を十分に得ること」の実現を目指します。

また、RustやGoやrubexなど他のアプローチについても紹介し、それらのアプローチの課題も紹介します。

たとえば、RustやGoを使うアプローチではC APIのマクロの扱いに課題があります。これらの言語ではCのマクロを扱えないため、RSTRING_LEN()のようなC APIを使うためにはそれぞれの言語で同様の処理を実現する必要があります。rubexのアプローチではrubexという独自の言語の文法を覚える必要があるという学習コスト面と、rubex自体がRuby本体の進化(たとえば文法の追加)に追従しなければいけないというメンテナンス面の課題があります。

このような情報も「このトークで提案するC++ APIがよいかどうかを判断する材料を十分に得ること」の実現で役立つはずです。

もしかしたら、参考情報としてRubyのC++ APIを提供するRiceも紹介します。RiceはBoost.PythonのようなAPIを提供します。単なる拡張ライブラリーではなくバインディングを書くときにより便利です。「このトークで提案するC++ APIがよいかどうかを判断する材料を十分に得ること」の実現に役立つ情報なはずです。

Pitch:

Rubyは3.0で高速化を目指しています。Rubyが高速になることで、より速度を期待するユーザーが増えると予想します。Rubyレベルの高速化で満足できるユーザーも多いでしょうが、さらなる高速化を期待するユーザーも増えるはずです。

そうなったとき、より簡単に拡張ライブラリーを書けるようにすることでより簡単にRubyスクリプトを高速に実行できるようになります。そうなることで、そのようなユーザーも楽しくRubyを使えます。楽しくプログラムを書ける人が増えることはRubyのポリシーとあっています。

このトークで紹介する既存のアプローチは私のアイディアではありませんが、提案するC++ APIの部分は私のアイディアです。

私は10個以上の拡張ライブラリーを作った経験があり、C APIのよいところも課題も知っています。そのため、私はこのトークをする人として適切だと考えます。

まとめ

RubyKaigiの発表の応募例として私のここ3年の応募内容を紹介しました。RubyKaigi 2017の発表の応募は6月17日まで受け付けています。ぜひ応募してみてください。

タグ: Ruby
2017-06-06

Firefox 52以降でのルート証明書の自動インポート機能でできること、できないこと

Firefox ESR52以降のバージョンでは、隠し設定のsecurity.enterprise_roots.enabledtrueに設定することで、Windowsの証明書ストアに登録されたルート証明書をFirefox側で認識して使えるようになりました。

ただ、この機能の背景や使い方についてユーザー側の期待と実際の挙動との間に若干の齟齬が見られるため、ここで改めて状況を整理してみます。

この機能を有効にすると、以下の事を実現できます。

  • 組織内ネットワークに設置したSSLプロキシやSSLロガーの使用に必要な専用のルート証明書をFirefoxで認識させる。
  • 組織内ネットワークで運用しているサーバーの証明書専用のルート証明書をFirefoxで認識させる。

それに対し、以下のことは依然としてできません

  • Windowsの証明書ストアに元から含まれている証明書、例えば政府認証基盤のルート証明書をFirefoxで認識させる。 (「Firefoxでは日本の官公庁関係のWebサイトの一部が証明書エラーで見られない」という問題を解消する。)
  • 管理者でないユーザーがWindowsの証明書ストアに追加した証明書をFirefoxでも認識させる。

何故こうなっているのかは、機能の背景を知ることで理解できます。

この機能の導入経緯

上記の設定が導入されたBugを見ると、これは「エンタープライズでFirefoxを使いやすくする」という文脈に基づく機能だということが読み取れます。

「エンタープライズ」とは、従業員規模が千人や万人といった単位に達するような大規模な組織での使用ということです。このような規模の組織では組織内専用のルート証明書が必要になる事がままあり、それをFirefoxで使うためには証明書のインポート機能を提供するアドオンを使うか、集中管理の仕組みの実装の裏をかいて強引にインポートさせるかしかありませんでした。ところが、現在Firefoxは古い基盤技術からの脱却を進めているため、これらの裏技的なやり方は早晩使えなくなる見込みが立っています。そこで、裏技ではなくきちんとした正当な機能としてルート証明書をインポートする方法を設ける必要があった、というのがこれらのBugの背景にある事情です。

実際の動作

「Windowsの証明書ストア」は、実際にはそれ専用のデータベースがあるわけではありません。Windowsのレジストリ内には以下のようなレジストリキーの配下に証明書の情報が分散して格納されており、それらをマージして一覧表示した物を「証明書ストア」として見せているということになります。

  • HKEY_CURRENT_USER\Software\Microsoft\SystemCertificates
  • HKEY_LOCAL_MACHINE\Software\Microsoft\SystemCertificates
  • HKEY_LOCAL_MACHINE\Software\Microsoft\EnterpriseCertificates
  • HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\SystemCertificates

エンタープライズ運用における「ルート証明書の追加」とは、Active Directoryなどを使って、これらの中で特に以下の位置に証明書を登録する事を指しています。

  • HKEY_LOCAL_MACHINE\Software\Microsoft\SystemCertificates\Root\Certificates
  • HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\SystemCertificates\Root\Certificates
  • HKEY_LOCAL_MACHINE\Software\Microsoft\EnterpriseCertificates\Root\Certificates

このことから、Firefoxは前述の隠し設定が有効な場合、起動時に上記のレジストリキー配下を走査して、追加された証明書があればそれらをインポートするという設計となっています。

ただし、この機能によってインポートされた証明書は、Firefox自身の証明書ストアに永続的に保存されるわけではありません。複数のレジストリキーをマージした結果がWindowsの証明書ストアとして扱われるのと同様に、Firefoxでも「Firefox自身の元々の証明書ストアの内容」+「この機能によって認識されたWindowsの証明書ストア内の証明書」が、実際の認証処理における証明書ストアとして使われるという形となります。(そのため、不要になった証明書はWindowsの証明書ストアから削除するだけで、Firefoxからも認識されなくなります。Firefoxの証明書マネージャで証明書を削除する、という事をする必要はありません。)

動作確認の方法

この機能でインポートされた証明書はFirefoxの証明書マネージャには表示されないため、期待通りに証明書がインポートされているかどうかは、デバッグ用の詳細なログを見て判断する必要があります。

MCD用設定ファイルを使う場合には、例えば以下のようにします。

// 証明書のインポート機能を有効化する設定
lockPref('security.enterprise_roots.enabled', true);

// NSS(Firefoxのセキュリティモジュール)のログを出力するための設定
lockPref("logging.pipnss", 5);
lockPref("logging.config.LOG_FILE", "C:\\Users\\Public\\certlog.txt");
lockPref("logging.config.add_timestamp", true);
lockPref("logging.config.clear_on_startup", false);
lockPref("logging.config.sync", true);

この設定を反映した上で、実験用のダミーの証明書をHKEY_LOCAL_MACHINE\Software\Microsoft\EnterpriseCertificates\Root\Certificates配下に加えるレジストリファイルを使って「example.com」という名前の証明書をインポートした状態でFirefoxを起動すると、C:\Users\Public\certlog.txtに出力されたログに以下のような箇所が含まれるようになります。

...
2017-05-09 10:21:23.017000 UTC - [Main Thread]: D/pipnss certificate is trust anchor for TLS server auth
2017-05-09 10:21:23.017000 UTC - [Main Thread]: D/pipnss Imported 'example.com'
2017-05-09 10:21:23.017000 UTC - [Main Thread]: D/pipnss imported 1 roots
...

このD/pipnss Imported '(証明書の一般名)'というログが出ていれば、その証明書はFirefoxから見えています。逆に、そのようなログが現れていない場合、証明書は無視されているということになります。

ユーザーが自分で操作してWindowsの証明書ストアにインポートした証明書をFirefoxが認識しない理由

Windowsでルート証明書のファイルをダウンロードしてダブルクリックすると、その証明書を証明書ストアにインポートすることができます。しかし、この方法でインポートされた証明書は上記のFirefoxの機能では認識されません。何故でしょうか。

これは、証明書が保存されるレジストリ上の位置に理由があります。この方法で手動でインポートした証明書は、HKEY_CURRENT_USER配下の以下の位置に保存されます。

  • HKEY_CURRENT_USER\Software\Microsoft\SystemCertificates

それに対し、Firefoxの証明書インポート機能はHKEY_LOCAL_MACHINE配下の以下のキーのみを走査します。エンタープライズ向けの機能としてはそれで正解で、個々のユーザーがWindowsの証明書ストアに追加した物まで認識するのはお門違いだからです。

  • HKEY_LOCAL_MACHINE\Software\Microsoft\SystemCertificates\Root\Certificates
  • HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\SystemCertificates\Root\Certificates
  • HKEY_LOCAL_MACHINE\Software\Microsoft\EnterpriseCertificates\Root\Certificates

政府認証基盤の証明書など、「Firefoxの証明書ストアには無いがWindowsの証明書ストアには含まれている」証明書がインポートされない理由と、その対処

2017年6月1日現在、政府認証基盤(GPKI)のルート証明書はFirefoxの証明書ストアには含まれていません。要望は上がっているのですが、政府側の対応がMozillaの定めるルート証明書の登録基準を満たしていないために作業が滞っているという状態です。

Windowsの証明書ストアには政府認証基盤の証明書も含まれているため、「Windowsの証明書ストアのルート証明書をFirefoxでインポートできるようになったのなら、政府認証基盤の証明書もインポートされて、証明書のエラーに悩まされることもなくなるのでは?」と期待される方もいらっしゃることでしょう。

ですが残念ながら、この機能では政府認証基盤のルート証明書はインポートされません。これは、Firefoxの証明書インポート機能の処理対象があくまで「管理者によってWindowsの証明書ストアに追加された証明書」に限られているからです。

Bugzilla上のコメント等で度々言及されていますが、Mozillaはユーザーの安全を守るために、どのルート証明書を登録するかを独自に判断しており、他社の判断を鵜呑みにはしないというポリシーで証明書ストアを管理しています。FirefoxがWindowsの全ての証明書を無条件で信頼してしまっていては、このポリシーが無意味になり、ユーザーの安全やプライバシーを保護するための判断を全てMicrosoftに委ねることになってしまいます。ですので、Mozillaとしてはあくまでユーザー(エンタープライズの文脈では、組織のシステム管理者)により追加された証明書のみをインポート対象にするというのが、この機能の趣旨となっています。

なお、以上のような趣旨のため、政府認証基盤(GPKI)のルート証明書を改めて「システム管理者が追加した証明書」としてWindowsの証明書ストアに登録すれば、これはFirefoxのインポート対象となります。

まとめ

以上、Firefox 52以降で使えるWindowsの証明書ストアからのルート証明書のインポート機能について、詳細と検証手順をご案内しました。

弊社で取り扱うFirefoxの導入・サポート案件においても、ルート証明書のインポートについては度々ご相談を頂いています。ニーズの高い機能でありながら今まで対応が進んでこなかったのには、アドオンや裏技的な方法で強引に解決してしまえるからという言い訳が立っていたからという側面は否定できないでしょう。従来からあるアドオンが切り捨てられるという方針の転換には批判の声も多く挙がっていますが、本体で対応される事が望ましい機能が本来あるべき形で実装されるきっかけとなったという事で、ここは素直に喜んでおきたいところです。

ちなみに、Firefox 52の段階ではこの機能はWindows限定の物となっています。macOSやLinuxでは設定を有効化しても効果を得られませんのでご注意下さい。

タグ: Mozilla
2017-06-01

関西Ruby会議2017:株式会社クリアコード #kanrk2017

須藤です。今年2回目の大阪です。(今年1回目の大阪はOSS Gate大阪ワークショップ2017-02-25でした。)

2017年5月27日に関西Ruby会議2017が開催されました。「Ruby Community and Ruby Business」がテーマということだったので、会社の話をするのがいいだろうなぁと思い、「株式会社クリアコード」というタイトルで基調講演をしました。

関連リンク:

内容

「Ruby Community and Ruby Business」というテーマなので、クリアコードは「Ruby Community」と「Ruby Business」を相互に活かしながらクリアコードが大事にしていることを実現している、という話をしました。クリアコードでの「Ruby Community」は「Ruby関連のフリーソフトウェア開発活動」、「Ruby Business」は「クリアコードでの仕事の仕方・作り方」です。クリアコードが大事にしていることは「フリーソフトウェアの推進と稼ぐことの両立」です。

1つのストーリーになった話ではなく、関連する話をたくさん集めた話にしました。具体的には次の話をしました。

  • 問題はupstreamで直す
  • 開発を続けられるコードを書く
  • 相手が想像しなくてもわかるように説明する
  • 楽しく開発する
  • 非難するよりも手を動かす
  • 回避策よりも根本解決
  • 受託開発
  • FLOSSサポート
  • OSS開発支援
  • 仕事の作り方:お客さん探しを頑張らない
  • Apache Arrow
  • 採用

最初の方の話は開発スタイルにまとまっている話です。

テーマに沿った内容になったと思っているのですが、いかがだったでしょうか?

まとめ

関西Ruby会議2017で基調講演をしてきました。「株式会社クリアコード」というタイトルでフリーソフトウェア開発と会社の話をしました。

この話を読んで仕事が決まる・採用の応募があるとすごく話がキレイにまとまるので、関西Ruby会議2017ではタイミングを逃してしまって話しかけられなかったという人は、遠慮せずにまだ間に合うのでご連絡ください。

タグ: Ruby | 会社
2017-05-29

Speee Cafe Meetup #07:OSSの開発に参加しよう! - OSS Gate #speee_lounge #oss_gate

須藤です。今月はあと2つ発表があります。(関西Ruby会議2017Apache Arrow勉強会。)

2017年5月23日にSpeee Cafe Meetup #07が開催されました。「OSS開発」がテーマということだったので、「OSSの開発に参加しよう!」という話をしました。

関連リンク:

内容

この発表では次のことを紹介しました。

  • 「OSSの開発に参加」というとガリガリ難しいコードを書くというイメージがあるかもしれないけど、こんなことやあんなことも「OSSの開発に参加」だよ。
  • 「OSSの開発に参加したい!」という気持ちはあるけどまだ行動できていない人のために、「OSSの開発に参加」をサポートする仕組みがあるよ。

こんなことやあんなことも「OSSの開発に参加」だよ、の具体例として次の例を紹介しました。

  • gemのインストールが失敗したとき:それを開発元にフィードバックする
  • ドキュメントにtypoがあった:それを開発元にフィードバックする
  • 使っているgemに機能が足りない:それを開発元にフィードバックする

関連しそうな話として「OSSのエコシステムに参加」ってどういうことだろう?という話もしました。「OSSのエコシステムに参加」の私の説明はこうです。

自分たちのソフトウェアもオープンソースのソフトウェアも同じように扱うこと

問題があれば直すし、気になることがあれば共有する。つまり、いつも自分たちのソフトウェアでやっているようにオープンソースのソフトウェアでのやればいいんです。

「OSSの開発に参加したい!」という気持ちはあるけどまだ行動できていない人のために、OSS GateOSS開発支援サービスを紹介しました。個人的にOSSの開発に参加したいという方はOSS Gateの方が向いています。業務の中でOSSの開発に参加したいという方はOSS開発支援サービスの方が向いています。(あるいは、個人的にOSS Gateに参加して、そこで得た知見を業務に活かす、というやり方もあります。)

OSS Gateの方に興味がある人は今週の土曜日(5月27日)にOSS Gate東京ワークショップ2017-05-27があるのでそれに参加することをオススメします。

OSS開発支援サービスの方に興味がある人は、Speeeさんでの事例を参考にしつつ、お問い合わせください。

まとめ

2017年5月23日開催のSpeee Cafe Meetup #07でOSSの開発に参加しよう!という話をしてきました。業務でOSSの開発に参加したい、という方には、私の話以外の話も興味深いと思います。すでにいくつか資料が公開されているのでそちらも見てみてください。

2017-05-25

Fluentd v0.14のEventTimeに関する話

はじめに

Fluentd v0.14ではログをmsgpackでエンコードし、新たに時間をForwardプロトコルで送る際に時間をEventTimeへエンコードして送信することができるようになりました。 このエンコード形式を用いて時間をForwardプロトコルで送るようにすると、秒よりもさらに細かな精度でログのやりとりができるようになります。 Fluent Loggerでログを送る際に、一秒間に2つ以上のログが発生する環境で秒精度までのログ転送を行った場合、Fluentdが扱うログの順番が送り先で発生した順ではなくなることがあります。 そのため、ログの順番をより正確に時刻でソートするために考えられたv0.14で新たにログの時刻の形式として秒精度以下も扱えるEventTimeが追加されました。

EventTime

Forwardプロトコルのv1の仕様にEventTimeについての解説があります。

仕様を見ると、EventTimeはmsgpackの拡張型として表されることがわかります。 また、fixext8とext8のどちらの場合でも秒とナノ秒は32bit integerで表し、msgpackへエンコードする必要があります。 fixext8形式は実行時に長さを取る必要がないので、構造体を直にEventTimeへエンコードする使い方をすると実行時のことを考えなくてもよくなります。

fruentlyではfixext8を使ったEventTime対応を行いました。

fixext8を用いたEventTime拡張型の場合もext8を用いたEventTime拡張型も秒とナノ秒を表す32bit Integerをエンコードする際にはBig Endianでエンコードする必要があることに注意してください。

fixext8
+-------+----+----+----+----+----+----+----+----+----+
|     1 |  2 |  3 |  4 |  5 |  6 |  7 |  8 |  9 | 10 |
+-------+----+----+----+----+----+----+----+----+----+
|    D7 | 00 | second from epoch |     nanosecond    |
+-------+----+----+----+----+----+----+----+----+----+
|fixext8|type| 32bits integer BE | 32bits integer BE |
+-------+----+----+----+----+----+----+----+----+----+
ext8
+--------+----+----+----+----+----+----+----+----+----+----+
|      1 |  2 |  3 |  4 |  5 |  6 |  7 |  8 |  9 | 10 | 11 |
+--------+----+----+----+----+----+----+----+----+----+----+
|     C7 | 08 | 00 | second from epoch |     nanosecond    |
+--------+----+----+----+----+----+----+----+----+----+----+
|   ext8 | len|type| 32bits integer BE | 32bits integer BE |
+--------+----+----+----+----+----+----+----+----+----+----+

Unix epoch時間(v0.12互換)

EventTimeなしでFluent Loggerを動かす場合は、秒精度までのログしか送ることができません。Unix epochで表現が可能な精度の時刻がログに含まれます。

2017-05-17 17:29:57.000000000 +0900 test: {"name":"fruently"}
EventTime(v0.14から)

EventTime形式にエンコードして時間をログに含めた際には、処理系がサポートする秒以下の精度の時間も含めてログに含めることができます。

2017-05-17 17:29:57.587226000 +0900 test: {"name":"fruently"}

まとめ

Fluent LoggerのEventTime対応をする際に参照する必要になったEventTimeの仕様を解説しました。 EventTimeを用いてエンコードされた時刻はこれまでのものよりもさらに精度の高い時刻情報を持てるようになります。 1秒に1つ以上のログをFluent Loggerに送る必要がある際にFluent LoggerがEventTime対応をしているかどうかを調べて、未対応であればバグ報告やプルリクエストをしてみるのはいかがでしょうか。

タグ: Fluentd
2017-05-24

Fluentd v0.14 API移行のすすめ

クリアコードではFluentd本体の開発だけでなくFluentdの600以上あるプラグインの開発にも参加しています*1。具体的にどういうことをやっているかは過去の記事を参照してください。

FluentdのプラグインをFluentd v0.14 APIに移行するための記事がいくつかあります。

具体的な移行方法についてはそれぞれの記事で解説しましたが、肝心のFluentd v0.14 API移行のPros/Consを説明していませんでした。 この記事ではプラグイン開発者視点でFluentd v0.14 APIへの移行について扱います。既に稼働しているFluentdをv0.14へ移行することについては扱いません。

Pros
  • 洗練されたプラグインAPIを使うことでメンテナンスしやすいプラグインを開発することが可能
  • non-buffered/buffered/async な output プラグインを1つのファイルで開発できる
  • non-buffered/buffered/async な output プラグインを設定ファイルで切り替えできる
  • 便利なプラグインヘルパーによってコードの見通しがよくなる
  • 簡単に使える本物のテストドライバーによって、メンテナンスしやすいテストコードを書くことができる
  • 組み込みのマルチプロセスサポート
  • bufferのchunkの分け方を柔軟に指定できる
    • タグごとにchunkを分ける
    • 時間ごとにchunkを分ける
    • chunkに含まれる任意のレコードごとにチャンクを分ける
  • storageプラグインのAPIが追加され、プラグインの状態を管理する汎用的な機構が備わった
Cons
  • 新APIは後方互換性がないので、基本的には全部書き直しが必要
    • ただし、Fluentd v0.14には互換レイヤーがあるので、移行しなくても基本的にはそのままで動作する
  • テストコードは全面的に書き直しが必要
    • Fluentd本体ではtest-unitを使用してテストコードを書いているので本体のテストコードを見ればどう書けばよいかわかる
  • Fluentd v0.14 API に移行すると Fluentd v0.12 以前でも動くようにするのは不可能*2
まとめ

Fluentd v0.14 APIに移行するPros/Consをまとめてみました。既に多くのプラグインがFluentd v0.14のAPIに移行済みです。最近は、最初からFluentd v0.14のAPIを使用している3rdパーティのプラグインも見かけるようになりました。

まだ移行していないプラグイン作者の方はtd-agent3が正式リリースされる前に、自分たちのプラグインをFluentd v0.14 APIに移行するとよいのではないでしょうか。

*1 すべてではありません

*2 Fluentd v0.10.x と Fluentd v0.12.x はプラグイン開発者が少しコードを追加することでFluentd v0.10.xとFluentd v0.12.xのどちらでも動くようにできた

タグ: Fluentd
2017-05-23

2008|05|06|07|08|09|10|11|12|
2009|01|02|03|04|05|06|07|08|09|10|11|12|
2010|01|02|03|04|05|06|07|08|09|10|11|12|
2011|01|02|03|04|05|06|07|08|09|10|11|12|
2012|01|02|03|04|05|06|07|08|09|10|11|12|
2013|01|02|03|04|05|06|07|08|09|10|11|12|
2014|01|02|03|04|05|06|07|08|09|10|11|12|
2015|01|02|03|04|05|06|07|08|09|10|11|12|
2016|01|02|03|04|05|06|07|08|09|10|11|12|
2017|01|02|03|04|05|06|
タグ:
RubyKaigi 2015 sponsor RubyKaigi 2015 speaker RubyKaigi 2015 committer RubyKaigi 2014 official-sponsor RubyKaigi 2014 speaker RubyKaigi 2014 committer RubyKaigi 2013 OfficialSponsor RubyKaigi 2013 Speaker RubyKaigi 2013 Committer SapporoRubyKaigi 2012 OfficialSponsor SapporoRubyKaigi 2012 Speaker RubyKaigi2010 Sponsor RubyKaigi2010 Speaker RubyKaigi2010 Committer badge_speaker.gif RubyKaigi2010 Sponsor RubyKaigi2010 Speaker RubyKaigi2010 Committer
SapporoRubyKaigi02Sponsor
SapporoRubyKaigi02Speaker
RubyKaigi2009Sponsor
RubyKaigi2009Speaker
RubyKaigi2008Speaker