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

ククログ


EPELでパッケージをリリースできるようにするには

はじめに

SentencePiece・fastTextをEPELのパッケージを使って簡単にインストールできるようになりますでEPELでパッケージをインストールできるようにしたことを紹介しました。
SentencePiece・fastTextを簡単に使えるようにすると嬉しい人向けの記事だったのですが、それを実現するまでにどんなことをやったのかは詳しく説明しませんでした。
今回はEPELでパッケージをリリースできるようにしたいという人向けの記事です。

実際にEPELでパッケージをインストールできるようにしたこと

以前Fedoraプロジェクトで新規パッケージをリリースする方法Fedoraプロジェクトでパッケージを更新するにはとしてFedoraプロジェクトでパッケージをリリースしたり、パッケージを更新したりするやりかたについて説明しました。

Fedoraだけでなく、EPELでもパッケージをリリースできるようにするには概ね次のような手順を踏みます。(SentencePieceもfastTextもFedoraにパッケージがないので新規パッケージをリリースするところからになります。)
Fedoraプロジェクトで新規パッケージをリリースする方法はやや古くなっていますが、基本的な流れはそれほど変わっていません。
そのため、一度Fedoraでパッケージをリリースしたことがあり、packagerグループの権限をもっているという前提で変わったところを中心に説明します。

  • パッケージを準備する
  • パッケージをレビュー依頼する
  • レビューが承認されたらSCMリクエストを送る
  • 必要なブランチをリクエストする
  • 各ブランチでリリースする
パッケージを準備する

レビュー依頼をするときには、specファイルとSRPMを提出する必要があります。
fedora-reviewを使うとある程度事前にパッケージの体裁をチェックすることも可能です。

$ fedora-review --rpm-spec --name (SRPMのファイル)

あらかじめチェックをしておくとよいでしょう。

パッケージをレビュー依頼する

手元でパッケージの用意ができたら、レビュワーが参照できるようにspecやSRPMを公開の場所にアップロードします。
アップロードができたら、パッケージのレビューを依頼します。

レビュー依頼には、request for review in bugzillaを使って、雛形から作成するとよいでしょう。

すでにpackagerグループに入っている場合、FE-NEEDSPONSORフラグの設定は不要です。
SentencePieceの場合以下のようなレビューリクエストを作成しました。

レビューが承認されたらSCMリクエストを送る

SCMリクエストはリポジトリを作成してもらうために行います。fedpkgコマンドを使ってパッケージ名と、承認されたBugzillaの番号を指定します。
SentencePieceの場合、次のようにコマンドを実行しました。

$ fedpkg request-repo sentencepiece 1758036

上記コマンドを実行すると、以下のようなissueが自動的に作成されます。

このissueはリポジトリが作成されると閉じられます。

必要なブランチをリクエストする

SCMリクエストが承認されたら、次は作業に必要なブランチをリクエストします。fedpkgコマンドのrequest-branchに必要なブランチを指定します。
SentencePieceの場合、f31とepel8、epel7のリクエストを行いました。

$ fedpkg clone sentencepieces
$ cd sentencepiece
$ fedpkg request-branch f31
https://pagure.io/releng/fedora-scm-requests/issue/18148
$ fedpkg request-branch epel8
https://pagure.io/releng/fedora-scm-requests/issue/18149
https://pagure.io/releng/fedora-scm-requests/issue/18150
$ fedpkg request-branch epel7
https://pagure.io/releng/fedora-scm-requests/issue/18151

リクエストを行うと、上記のようにブランチごとにissueが作られます。ブランチが作成されるとissueが閉じられる運用になっています。

各ブランチでリリースする

ブランチが作成されたら、次は各ブランチでリリースを行います。
specファイルやソースアーカイブをインポートしたら、fedpkg buildでパッケージをビルドします。その後、fedpkg updateを行うことで対象ブランチでリリースを行えます。
SentencePieceの場合、epel7ブランチのリリースは次のようになりました。

既定では2週間ほどでリポジトリに反映されるようです。

まとめ

今回はEPELでパッケージをリリースできるようにしたいという人向けに手順を説明しました。
手元で必要に迫られてパッケージ化したものがあれば、ぜひアップストリーム(Fedoraプロジェクト)へフィードバックしていきませんか?

2019-10-18

SentencePiece・fastTextをEPELのパッケージを使って簡単にインストールできるようになります

はじめに

SentencePieceは、言語に依存することなく学習データから文章をトークナイズすることができるものです。
MeCabでは言語に依存した辞書が必要でしたが、SentencePieceは言語非依存なので、辞書が不要になります。
SentencePieceについて詳しくは開発者による記事があるのでそちらを参照されると理解がより深まるでしょう。

一方のfastTextは文書から学習してテキストを分類し、類似する単語を単語の類似度を計算することができます。
類似度をうまくつかえば、似たような文章をサジェストすることができそうです。

どちらもデータを学習させて活用するためのものですが、SentencePieceやfastTextのことを知って、ちょっと試そうとするにもやや手間が必要でした。これまではソースコードを取得してコンパイルしたりする手順が必要だったからです。
今後はSentencePieceやfastTextをEPELのパッケージとして簡単にインストールできるようになります。ただし、まだepel-testingリポジトリからで、epelリポジトリからインストールできるようになるのはしばらく(2週間ほど)先になる見込みです。

epelリポジトリにパッケージが入ると以下のように簡単にSentencePieceやfastTextをパッケージでインストールできるようになります。

$ sudo yum install epel-release
$ sudo yum update
$ sudo yum install fasttext-tools
$ sudo yum install sentencepiece-tools

EPELでパッケージがインストールできるとどう嬉しいか

冒頭で述べたように、SentencePieceやfastTextをこれから触ってみようという人は、簡単にインストールして試せるようになるので嬉しいです。
他にもGroongaという、クリアコードが開発に関わっている全文検索エンジンGroongaのユーザーにもメリットが(今後)あります。

というのも、これらをうまく使えばGroongaでいい感じの類似文書検索(例えばRedmineで似たようなチケットをサジェストするようなこと)ができるようになるかもしれないからです。

Groongaにはトークナイザーをプラグインとして組み込む仕組みがあって、使用するトークナイザーを使い分けることで、検索時の振る舞いを調整する(いい感じに検索する)ことができます。
利用可能なトークナイザーにはTokenBigramTokenMecabなどがあります。TokenBigramだと漏れなく検索できますが、TokenMecabのように意味のある単位での検索は不得手といったような特色があります。逆にTokenMecabは意味のある単位でトークナイズできるのでよいかというと、辞書に依存する部分が大きく、未知の語句の検索に弱いため、必ずしも適切とは限りません。
また、TokenMecabは既存の辞書を利用するので、言語に依存した辞書が必要です。

より精度の高い検索を実現するには、SentencePieceやfastTextなどを使って、学習結果をもとにトークナイズできるトークナイザーを使えるようにするのがよさそうです。
学習結果をもとにトークナイズするので、MeCabほどそれらしい単語にトークナイズできるわけではなさそうです。ただし、類似文書検索の場合には、特徴的な言い回しがあるかどうかで判断することになるので、そのようなトークナイザーがあると有用です。

(まだそのようなトークナイザーはできていませんが)仮に新しいトークナイザーが使えるようになったとして、インストールに手間がかかるのでは、Groongaのユーザーにとってあまり嬉しくありません。
TokenMecabのようにパッケージでインストールできるようになっていると、インストールが容易で更新の手間も少なくできます。
パッケージ化する場合でも、Groongaプロジェクトで提供している独自リポジトリからインストールできるというのでは、Groongaをまだ使っていない人にはあまり嬉しくありません。(リポジトリを追加で登録しないといけないからです。)
また、独自のリポジトリでメンテナンスし続けないといけないというのも手間がかかります。それよりかはアップストリームでメンテンナンスするほうがより誰でも簡単にインストールできるようになって嬉しいので、EPELでパッケージを公開することにしました。*1

まとめ

今回は、SentencePiece・fastTextをEPELのパッケージを使って簡単にインストールできるようになることを紹介しました。
SentencePieceやfastTextを使ってみたい人だけでなく、(今後)Groongaでいい感じの類似文書検索をできるようにするのにも必要になってくるのでEPELのパッケージとしてインストールできるようにしました。独自リポジトリにパッケージを用意して終わりにするのではなく、クリアコードではアップストリーム(Fedoraプロジェクト)へフィードバックしていくことでフリーソフトウェアの推進を実施しています。

*1 EPELでパッケージを公開するためにはいろんな手順が必要でした。

2019-10-17

Apache Arrowの最新情報(2019年9月版)

Apache ArrowPMC(Project Management Commitee、プロジェクト管理チームみたいな感じ)のメンバーの須藤です。

みなさんはApache Arrowを知っていますか?Apache Arrowは数年後にはデータ処理界隈で重要なコンポーネントになっているだろうプロジェクトです。データ処理界隈に興味がある人は知っておくと役に立つはずなので1年ほど前にApache Arrowの最新情報(2018年9月版)をまとめました。この1年ですごくよくなったので2019年9月現在の最新情報を紹介します。

私は、PMCの中では唯一の日本人*1で、コミット数は2番目に多い*2ので、日本ではApache Arrowのことをだいぶ知っている方なはずです。Apache Arrowの情報は日本語ではあまりないので日本語で紹介します。

ちなみに、英語ではいろいろ情報があります。有用な情報源はApache Arrowの公式ブログ公式メーリングリストやそれぞれの開発者のブログ・発表などです。開発者のブログの中ではUrsa Labs Blogの隔月の開発レポートがオススメです。Ursa Labsはスポンサーを集めてフルタイムでApache Arrowの開発をしている非営利の組織です。(という説明でそんなに間違っていないはず。)

この記事ではそれらの情報へのリンクも示しながら最新情報を紹介するので、ぜひ英語の情報も活用してください。

Apache Arrowが実現すること

Apache Arrowが実現することは1年前と変わっていないので、Apache Arrowの必要性から知りたいという方はApache Arrowの最新情報(2018年9月版)を参照してください。まとめると次の通りです。

Apache Arrowは効率的に大量のデータをメモリー上で処理することを目指しています。そのためにしていることは次の通りです。

  1. データ交換・高速処理しやすいApache Arrowフォーマットの仕様を定義
  2. 各種言語用のApache Arrowフォーマットを読み書きするライブラリーを開発
  3. 大量のメモリー上のデータを高速処理するためライブラリーを開発

Apache Arrowが向いている用途は次の通りです。

  • 大量データの交換
  • メモリー上での大量データの分析処理

Apache Arrowの現状

それでは、2019年9月現在、Apache Arrowのデータフォーマットの仕様と実装がどのようになっているかを説明します。

データフォーマットの仕様

データフォーマットの仕様は去年中に固められるといいねと進んでいましたが、まだ固まっていません。しかし、今年中には固まるはずです。

現時点での最新リリースは0.14.1です。10月中に0.15.0がリリースされる予定です。順調にいけば、0.15.0の次が1.0.0になります。

1.0.0をリリースする段階でデータフォーマットの仕様が固まります。固まるというのはどういうことかというと互換性が保証されるということです。

互換性には後方互換性と前方互換性があります。

後方互換性が保証されるというのは新しい実装では新しい仕様のデータだけでなく古い仕様のデータも読めるということです。

前方互換性が保証されるというのは古い実装でも新しい仕様のデータを読めるということです。もし読めなくても読めないことがわかります。中途半端に読めるとか壊れたデータとして読めるということにはなりません。

データフォーマットの互換性は大事なので、今後、仕様を変更するときは、少なくともC++とJavaで実装し、それぞれで相互に正しくやりとりできるかを確認するテストを追加しなければいけなくなりました。そのため、C++でだけ実装しやすいがJavaでは実装しづらいような仕様は入りにくくなります。少なくともC++とJavaとなっているのは今のところC++とJavaの実装が進んでいるからです。今後、他の言語での実装が進んできたらC++とJava以外も候補に入ってくるかもしれません。

参考:[DISCUSS] Format changes: process and requirements

これまで、Apache Arrowフォーマットのデータはストレージに保存するのには向いていないという扱いでした。しかし、仕様が固まったら(1.0.0がリリースされたら)データが失われる心配をせずにストレージに保存することができます。

ただし、Apache Arrowフォーマットよりもストレージ保存に向いたフォーマットはたくさんある(たとえばApache Parquet)ので、ストレージに保存したい場合は、本当にApache Arrowフォーマットが適切なのかを考えてからどのフォーマットにするかを決めてください。たとえば、ストレージにはすごく空きがあるからデータの読み込みをすごく速くしたいということであればApache Arrowフォーマットを使ってもよいでしょうが、書き込むデータ量はApache Parquetなど他のストレージ向けのフォーマットよりも大きくなりがちなので書き込み速度はそこそこ(I/Oネック)になるかもしれません。

データフォーマットのバージョン

1.0.0がリリースされたらデータフォーマットのバージョン付けにはセマンティックバージョニングを採用する予定です。つまり、互換性が壊れるような変更がなければメジャーバージョンは同じまま(1.X.Yのまま)になります。互換性がある仕様変更の場合はマイナーバージョンが上がります。1.0.0のあとに互換性のある仕様変更があったら1.1.0になります。今のところ、パッチバージョンは使う予定はありません。

今までは明示的にデータフォーマットにバージョンを振っていませんでした。振っていませんでしたが、メタデータバージョンというものがありました。これは今のところV1, V2, V3, V4があります。データフォーマットのメジャーバージョンが上がったらここに新しい値が増えます。2.0.0になったらV5が増えます。

少し紛らわしいかもしれませんが、1.0.0以降は「ライブラリーのバージョン」と「データフォーマットのバージョン」は別々に採番されます。ライブラリーのバージョンは2.0.0でデータフォーマットのバージョンは1.0.0というような状況になります。

参考:[VOTE] Adopt FORMAT and LIBRARY SemVer-based version schemes for Arrow 1.0.0

ライブラリーのバージョン

1.0.0からはライブラリーのバージョンもセマンティックバージョニングに従います。つまり、互換性が壊れるような変更があればメジャーバージョンが上がるということです。

現時点ではリリースするたびに互換性が壊れる可能性があるので毎回メジャーバージョンが上がる予定です。2ヶ月くらいごとにリリースする予定なので2ヶ月くらいに1回メジャーバージョンが上がる予定です。

ライブラリーのメジャーバージョンは頻繁に上がりますが、データフォーマットのメジャーバージョンはそんなに上がらないはずです。そのため、次のようにライブラリーのバージョンとデータフォーマットのバージョンはズレます。

  • 1.0.0の次のリリース
    • ライブラリーのバージョン:2.0.0
    • データフォーマットのバージョン:1.0.0
  • 1.0.0の次の次のリリース
    • ライブラリーのバージョン:3.0.0
    • データフォーマットのバージョン:1.0.0

Apache Arrowにはたくさんのプログラミング言語向けのライブラリーがありますが、すべてこのルールで一緒にリリースされます。

扱えるデータ

バージョンの話はこれくらいにして、Apache Arrowが扱えるデータについて説明します。基本的なデータについてはApache Arrowの最新情報(2018年9月版)を参照してください。ここではこの1年で新しく扱えるようになったデータについて説明します。

新しく次のデータを扱えるようになりました。

また、ユーザーが独自に型を追加できるようになりました。これを拡張型と呼びます。

拡張型は次のように実現しています。

  • 生データはApache Arrowが提供するプリミティブ型を使って表現する
  • 生データにメタデータを付与して追加情報を表現する

たとえば、UUID型は次のように実現します。

  • 生データは16バイトの固定長バイナリーデータ型を使って表現する
  • 生データに次のメタデータを付与
    • 拡張型名:uuid
    • UUIDのバージョン:1

この実現方法のメリットは、使っているApache Arrowのライブラリーが対象の拡張型のことを知らなくても(UUID型について知らなくても)データを扱えるという点です。対象の拡張型を知らなかったら単に生データ(UUID型ならただの16バイトの固定長バイナリーデータ)として扱えばよいからです。もちろん、その拡張型固有の操作はできませんが、要素数を数えたり、複数のデータチャンクをまとめて次のデータ処理モジュールにデータを渡すといった基本的な操作はできます。

データ処理

Apache Arrowは各種データ処理ツールが共通で使える高速なデータ処理機能の開発も重視しています。去年の時点ではデータフォーマットの方に注力していたためあまり進んでいませんでした。しかし、この1年でデータ処理部分の実装が進んでいます。

特に進んだのがC++とRustです。C++のバインディングとして実装されているPython、R、Rubyもその恩恵を受けています。

それではそれぞれの言語毎にデータ処理部分がこの1年でどう改良されたかを紹介します。去年の時点で実装されている処理についてはApache Arrowの最新情報(2018年9月版)を参照してください。

C++

C++でこの1年で実装された処理は次の通りです。

  • 指定した値と各要素の比較
    • 結果:真偽値の配列
    • 比較方法:==, !=, <, <=, >, >=
    • 例:Compare([null, 1, 2, 3], 2, >=)[null, false, true, true]
  • 非NULLの要素数のカウント
    • 結果:要素数(数値)
    • 例:Count([null, 1, 2, 3])3
  • 真偽値配列による要素の選択
    • 結果:対応する真偽値配列の要素が偽ではない要素の部分だけが残った配列
    • 例:Filter([null, 1, 2, 3], [true, false, null, true])[null, null, 3]
  • 値ごとの要素数のカウント
    • 結果:構造体({"Values": 入力の配列と同じ型, "Counts": 64ビット整数})の配列
    • 例:ValueCounts([null, "a", "b", "a"])[{"Value": "a", "Counts": 2}, {"Value": "b", "Counts": 1}]
  • 対象の配列の中に指定した配列にある要素が含まれているか
    • 結果:真偽値の配列
    • 例:IsIn([null, 1, 2, 3], [1, 3])[null, true, false, true]
  • 算術平均(NULLを無視)
    • 結果:平均値(64bit浮動小数点数)
    • 例:Mean([null, 1, 2, 3])2.0
  • ソートし、ソート後の各要素がソート前は何番目の要素だったかを返す(NULLは最後になる)
    • 結果:64bit整数の配列
    • 例:SortToIndices([null, 3, 1, 2])[2, 3, 1, 0]
  • 合計(NULLを無視)
    • 結果:合計値(64bit整数または64bit非負整数または64bit浮動小数点数)
    • 例:Sum([null, 1, -2, 3])2
  • 配列から指定したインデックスの要素を抽出
    • 結果:対応する要素だけが残った配列
    • 例:Take([null, "a", "b", "c"], [2, null, 1])["b", null, "a"]

さらにGandivaという名前の式コンパイラーが取り込まれました。Apache Arrowの最新情報(2018年9月版)の段階で取り込まれようとしていたものが正式に取り込まれたということです。

前述の処理は配列単位の処理ですが、Gandivaはレコードバッチ(レコードの集合)に対する処理を扱います。たとえば、「record.column1 > 100 && record.column2 == "XXX"」のような処理を扱います。このような処理をLLVMを使って実行時にネイティブコードにコンパイルしてから実行するので高速に式を評価できます。

Gandivaは単に条件で絞り込むような簡単な式だけでなく、SQLで記述できるような集約処理や条件分岐(CASE相当)などの複雑な式も扱います。

参考:Gandiva: A LLVM-based Analytical Expression Compiler for Apache Arrow | Apache Arrow

現時点でGandivaはC++だけでなくJava、Python、Rubyから使えます。

現時点では前述の配列単位の処理(内部では計算カーネルと呼んでいます)とGandivaは特に連携していません。算術演算のような基本的な処理はどちらにもあります。では、どうやって使い分けるのかが気になりますね。おそらく、次のような使い分けになるでしょう。

  • 中間結果(最終結果じゃない)のレコードバッチの処理:Gandiva
    • 例:レコードのフィルター
  • 最終結果を生成する処理:計算カーネル
    • 例:合計値の計算

参考:Compute kernels and Gandiva operators

もう少し先になると、ユーザーはもう少し高レベルのAPIを使うことができるようになるはずです。どこかからデータを読み込み、読み込みながら指定したクエリーでデータを絞り込めるようなAPIです。

データ読み込み処理はデータセットAPIとして実装が始まっています。

今のところローカルにあるApache Parquetフォーマットのデータだけ読み込めますが、今後、CSVやJSONフォーマットのデータも読み込めるようになったり、S3上のファイルなどリモートにあるデータも読み込めるようになる予定です。条件のプッシュダウン(指定した条件のデータだけ読み込む)も対応します。プッシュダウンなしで、読み込んだ後に不要なデータを捨てる方法もありますが、それよりも、そもそも読まない方が圧倒的に高速なのです。

参考:

読み込んだデータはクエリーエンジンで処理できるようになる予定です。クエリーエンジンは内部で計算カーネルやGandivaを利用して効率的に処理します。

参考:

ユーザーがより扱いやすいAPIとしてデータフレームAPIも実装する予定です。

参考:

楽しみですね!

Rust

Rustでこの1年で実装された処理は次の通りです。

  • 要素ごとの算術演算(SIMD対応)
  • 要素ごとの論理演算(AND・OR・NOT)
  • 要素ごとの比較(SIMD対応)
  • キャスト(型の変換)
  • 配列から指定したインデックスの要素を抽出
  • 時刻配列の各要素を時間(hour)に変換

Rustでもクエリーエンジンの実装が始まりました。DataFusionという名前です。

C++のクエリーエンジンはSQLなど特定のフロントエンドを用意しない*3設計*4ですが、DataFusionにはSQLのフロントエンドがあります。

まだ簡単なSQLしか処理しかできませんが、より複雑なSQLも処理できるように開発を進めています。

参考:DataFusion: A Rust-native Query Engine for Apache Arrow | Apache Arrow

この1年でRustのApache Parquet実装がApache Arrowに取り込まれたので、Apache Parquetフォーマットのデータもデータソースとして扱えます。

現時点ではすべてRustで実装していますが、どうにかしてGandivaとつなげられないかという検討もしています。

参考:[DISCUSS] [Gandiva] Adding query plan to Gandiva protobuf definition

DataFusionの開発をしている@andygrove73はDataFusionベースの分散計算プラットフォームBallistaの開発をはじめました。PoCのプロジェクトだそうなので、ここでの実験の成果がDataFusionに還元されていくのだと思います。

データ処理のまとめ

データ処理まわりについては、この1年で特に進んだC++実装とRust実装を紹介しました。

Arrow Flight - 高速RPCシステム

この1年Arrow FlightというApache Arrowベースの高速RPCシステムの開発が始まりました。gRPCベースのプロトコルですが、できるだけ高速になるように設計されています。たとえば、データのコピーができるだけ少なくなるようになっています。

ここでもう少しArrow Flightについて紹介できればよかったのですが、私がまだ触っていないのでそんなに説明できることがありません。残念。

Java実装を主に実装したDremioの人たちが書いたArrow Flightの記事Understanding Apache Arrow Flightもあるのですが、ちょっとこれだけだとピンとこないと思います。

現時点でArrow Flightを使えるのはC++とJavaとPythonです。

来年にはRubyでも使えるようにして紹介できるようにしたいな。

各言語での実装の完成度

各言語での実装の完成度もApache Arrowの最新情報(2018年9月版)からの差分を紹介します。

まず、次の言語が増えました。

  • C#
  • R
  • MATLAB

C#はネイティブ実装で、RとMATLABはC++実装のバインディングです。

C#実装は基本的な型にだいたい対応しています。前述の新しく扱えるようになったデータはまだ対応していません。NuGetでインストールできます。

参考:NuGet Gallery | Apache.Arrow

R実装はかなりC++実装をカバーしています。Python実装やRuby実装ほどではありませんが、完成度が高めです。Apache Parquetの読み込みもサポートしました。RからApache Parquetを読み込めるとうれしい人が多いはずです。CRANからインストールできます。

参考:CRAN - Package arrow

MATLAB実装はFeatherフォーマットを読み書きできるだけです。今後、開発が活発になるかどうかはよくわかりません。

Apache Arrow利用者

Apache Arrowの利用者が増えています。

TensorFlowはApahche Arrowフォーマットのデータをデータセットとして使えるようにしました。

参考:TensorFlow with Apache Arrow Datasets

BigQuery Storage APIもApache Arrowフォーマットのデータをサポートしました。

参考:

PG-StromというPostgreSQLの拡張モジュールもApache Arrowフォーマットのデータをサポートしました。PG-StromはGPUを使って高速にデータ処理するための拡張モジュールです。PG-Stromの関連モジュールとしてArrow_Fdwがあり、これを使うとストレージに置いてあるApache ArrowフォーマットのデータをPostgreSQLから読み込めます。読み込んだデータはPG-Stromで高速に処理できます。

参考:PostgreSQLをどこまで高速化できるのか?〜ハードウェアの限界に挑むPG-Stromの挑戦〜

PG-StromにはPostgreSQLに接続して結果をApache Arrowフォーマットのデータとして保存するpg2arrowというコマンドもあります。この機能はC++のデータセットAPIで実現しようとしている機能の1つでもあります。この機能をApache Arrow本体に取り込まないか?という話もあるので楽しみですね。

参考:Contributing to Apache Arrow? · Issue #9 · heterodb/pg2arrow

1.0.0がリリースされたらもっとApache Arrowの利用者が増えるでしょう。

Apache Arrowの今後

ここまででApache Arrowのこの1年の最新情報を説明しました。最後に今後のことを説明します。

1.0.0リリース

前日の通り、近いうちに1.0.0がリリースされます。

去年、Apache Arrow東京ミートアップ2018というApache Arrowイベントを開催しましたが、1.0.0に合わせて今年もApache Arrowイベントを開催する予定です。楽しみにしていてください!

Apache Arrow利用者の増加

1.0.0がリリースされるとこれまで「様子見」だった人たちが使うようになるはずです。そうするといろんなところでApache Arrowを利用した高速なデータ交換ができるようになります。

次の1年でApache Arrow対応プロダクト・サービスはたくさん増えるでしょう。

データ処理機能の拡充

前述の通りC++実装ではデータセット・クエリーエンジン・データフレームなどデータ処理機能の実装に注力していきます。次の1年で使える機能がかなり増えるでしょう。

まとめ

2019年9月時点のApache Arrowの最新情報を、2018年9月からの差分という形でまとめました。Apache Arrowは数年後にはデータ処理界隈で重要なコンポーネントになっているだろうプロジェクトです。日本でもApache Arrowのことを知っている人が増えるといいと思うので日本語でまとめました。Apache Arrowを使う人が増えるといいなぁと思います。さらに言えば開発に参加する人も増えるといいなぁと思います。

私が知っていることはまとめたつもりですが、もしかしたらカバーできていない話があるかもしれません。もし、「○○についても知りたい!」という方がいたらApache Arrowのことを日本語で話せるチャットで声をかけてください。この記事に追加します。

Apache Arrowについて講演して欲しいという方はお問い合わせフォームからご連絡ください。

私はデータ処理ツールの開発という仕事をしたいと思っています。その中にはもちろんApache Arrowの開発も含まれています。一緒に仕事をしたい!(自社サービスをApache Arrow対応したいとか)という方はお問い合わせフォームからご連絡ください。

*1 去年はPMCメンバー・コミッター含めて日本人は私だけでしたが、今は私の他に2人日本人がいます。

*2 去年は3番目でした。

*3 そのようなフロントエンドのバックエンドになる

*4 前述の設計文書の「Non-goals」を参照。少なくとも今のところはそうしない設計。

2019-09-30

GitLab Runnerを使ったサポートコストの低減

GitLabでCI/CDを行う場合は、GitLabに組み込みのGitLab CI/CDを使用できます。
GitLab CI/CDは、GitLab上のコミットを検知して、GitLab Runnerが指定されたジョブを実行するという仕組みで動いています。

この時に使用されているGitLab RunnerというツールはGitLabからは独立しており、自前のホストにもインストールし実行できます。そのため、これを使ってWindows環境でのCI/CDを簡単に構築できます。

弊社のGroongaのサポートサービスを利用されているお客様の中には、Windows環境でGroongaを動かしているお客様もいらっしゃり、Windows環境でのビルドやテストも実施する必要があります。

以前は、お客様向けのWindowsパッケージを自社のWindows環境へインストールし、テストスクリプトを流して、問題なければリリースノートを書いて提供するという作業フローでした。
しかし、この作業フローではパッケージのインストールやテストスクリプトの実行は手作業で実施しているため、効率が悪いという問題がありました。
また、これらの手順は提供直前に実施していたため、この時点で問題が発覚するとお客様へパッケージ提供が遅れてしまうというリスクも孕んでいました。

GitLab RunnerはWindowsにも簡単にインストールできます。また、GitLab CIは定期的にジョブを実行することができるため、毎晩その時点の最新のGroongaを使って、お客様向けのパッケージをビルドおよびテストするようにもできます。

これらを実施したことにより、手作業で実行する作業がリリースノートの作成のみになり、パッケージ提供までの時間を短縮できました。
また、毎晩テストを実施するので問題を早期に発見しやすくなり、提供前に慌てて修正するというようなことがなくなりました。

では、具体的にどのようにWindow環境でのCI/CDを構築したかを以下に記載します。


インストール
  1. まずは、GitLab Runnerのバイナリーを格納するディレクトリーを作成します。
  2. 公式サイトからGitLab Runnerのバイナリーをダウンロードして、1で作成したディレクトリーに格納します。
    ダウンロードURLは https://docs.gitlab.com/runner/install/windows.html#installation に記載があります。
  3. 格納したバイナリーのファイル名をgitlab-runner.exeにリネームします。
  4. 管理者権限でコマンドプロンプトまたは、PowerShellを実行し、以下のコマンドを実行して、対象のプロジェクトにGitLab Runnerを登録します。
./gitlab-runner.exe register

上記のコマンドを実行すると、いくつか質問されるので、順に回答を入力していきます。

まずは、以下のように対象のGitLabプロジェクトのURLを聞かれるので、対象のプロジェクトのURLを入力します。

Please enter the gitlab-ci coordinator URL (e.g. https://gitlab.com )

次に gitlab-ci トークンを聞かれるので、入力します。

gitlab-ci トークンは、GitLabの対象のプロジェクトのSettings -> CI/CD -> Runners の "Set up a specific Runner manually" に記載されています。

Please enter the gitlab-ci token for this runner

次に登録するGitLab Runnerの説明を記載します。

Please enter the gitlab-ci description for this runner

次に登録するGitLab Runnerにタグをつけます。

Please enter the gitlab-ci tags for this runner (comma separated):

次にExecuterを指定します。
弊社で使用しているテストスクリプトはPowerShellで動くため、shellを指定しました。

Please enter the executor: ssh, docker+machine, docker-ssh+machine, kubernetes, docker, parallels, virtualbox, docker-ssh, shell:
  1. 管理者権限でコマンドプロンプトまたは、PowerShellを実行し、以下のコマンドを実行して、WindowsサービスとしてGitLab Runnerをインストールします。
./gitlab-runner.exe install
./gitlab-runner.exe start
  1. GitLabの対象のプロジェクトのページを開いて、Settings -> CI/CD -> Runnersに上記手順で登録したGitLab Runnerが表示されていれば、インストール完了です。

WindowsへのGitLab Runnerのインストールは、以下の公式ドキュメントにも記載されています。

https://docs.gitlab.com/runner/install/windows.html

ジョブの作成

GitLab Runnerに実行させたいジョブは、.gitlab-ci.ymlというファイルにYAML形式で記述し対象のリポジトリのトップレベルにpushします。
この例では、Executorshellを選択したので、実行するスクリプトはWindowsバッチで記述しています。

.gitlab-ci.ymlをpushすると、以後、コミットを契機に.gitlab-ci.ymlに記述したスクリプトがGitLab Runnerをインストールしたホスト上で実行されます。

ジョブの定期実行

ジョブは、リポジトリへのコミットのみを契機に実行されるだけではなく、定期的に実行することもできます。
定期的に実行するには、以下のように実施します。

対象プロジェクトのページを開いて CD/CI -> Schedulesを選択します。
"Scheduling Pipelines"というページに遷移するので、そこで、New scheduleボタンを押下します。

以下の項目を入力する画面に遷移するので、それぞれ適切な値を設定します。

  • Description
    • この定期実行の説明を書きます
  • Interval Pattern
    • どのくらいの間隔で実行するかを指定します。
  • Cron Timezone
    • タイムゾーンを指定します。
  • Target Branch
    • どのブランチを対象にジョブを実行するかを指定します。
  • Variables
    • ある特定のジョブだけ定期的に実行したい場合や、ある特定のジョブだけ実行間隔を変えたい場合に使用します。
    • 例えば、.gitlab-ci.ymlに以下のように記述すると、このVariablesの項目にWEEKLYが指定されているスケジュールに従ってtest regressionのジョブが実行されることになり、Variablesの項目にWEEKLYが指定されていないスケジュールの実行時は、test regressionのジョブは実行されなくなります。
"test regression":
  stage: test
  tags:
    - xxxx
  only:
    variables:
      - $WEEKLY

パッケージ提供のような定期的に発生する作業にかかる時間を減らすことで、別の課題に時間を使うことができ、お客様にとってもメリットになります。また、作業の過程で、GitLab Runnerの不具合を発見したので、以下の内容でアップストリームへ報告しました。

このように、クリアコードではお客さんのメリットになる作業をすると同時に、アップストリームへの問題報告やパッチ提供をして、フリーソフトウェアの推進を日常的に実施しています。

2019-09-26

フリーソフトウェアの法人向けサポートの一環で行った開発元へのフィードバックの事例紹介

当社の法人向けサポートサービスでは、企業のお客様がフリーソフトウェアを使わていて遭遇されたトラブルの解決や原因究明、文書化されていない技術情報の調査などを有償にて行っています。「特殊な労働力を提供してお客様の役に立ち、その対価としてお金を頂く」ということで、ビジネスモデルとしてはシンプルです。

ところで、当社の理念は「自由と稼ぐの両立」です。前述のビジネスモデルから「稼ぐ」は容易に読み取れますが、これがどのように「自由」に繋がっているかは見えにくいかもしれません。

まず単純に、「ユーザーが増える事が自由なソフトウェアの推進になる」という事が言えます。それは「そのソフトウェアを使うと開発チームに広告収入が入る」といった直接的な還元に限りません。例えば、そのソフトウェアのユーザーが増えると、業界内でのそのソフトウェアの影響力が増すので、プロプライエタリな製品にとってはそのデータ形式やプロトコルを無視しにくくなり、ユーザーが特定の製品に囲い込まれるリスクが低減する、という効果が期待できるでしょう。

また、こちらがこの記事の本題なのですが、このビジネスの中で実際の使用環境で見つかった不具合を報告したり、実際の使用環境でのニーズを汲み取った提案をしたりという形で、そのソフトウェアの改善に寄与できます

ということで、以下、Mozilla製品の法人向けサポート由来のフィードバックの事例を4つご紹介します。

Bug 1540943 - Broken message body on forwarding a mail including invalid character(Mozilla Thunderbird)

これは、メールの本文にエンコーディング上不正な文字が含まれていると転送メールの本文が文字化けしてしまうという問題です。法人利用では様々なメールが頻繁に・大量にやり取りされるため、このような「普通は起こらないエッジケース」が起こる事があり、また、それがビジネス上の支障になってしまう場合もあります。この現象に遭遇されたお客さまの場合、取引先からのメールを転送しようとすると文字化けしてしまうという事でお困りでした。

原因と回避策を調査した結果、本文として転送する場合には発生を避けられない問題である事が分かったため、お客さまには「添付として転送」などの別の操作を使って回避して頂く事をお薦めし、開発元には上記のBugの通り、その時点で判明していた発生条件などの情報を報告しました。

その後、開発者の方の目に留めて頂き調査が進んで、最終的にはソースコード中の1行をピンポイントで修正(呼び出す関数を変更)して問題を解消するパッチが投入されました。変更点が少なかった事から、この修正はThunderbird 60のメンテナンスリリースにも反映される結果となっています。

Bug 1563665 - Replied mails are not marked as "replied" if the folder has a comma (,) in its substance file name(Mozilla Thunderbird)

これは、メールフォルダに対応するローカルディスク上のファイルの名前に , (半角コンマ)が含まれているとそのフォルダ内のメールに返信などの操作をしても「返信済み」のようなマークが付かないという問題です。Thunderbirdではメールフォルダ1つがディスク上の1ファイルとなるmbox形式が初期設定となっており、ファイル名はフォルダ名そのままではなくMUTF-7という特殊なエンコーディング方式で英数字に変換されているのですが、台北 のような特定の文字列で変換結果が &U,BTFw- となり , が含まれる事になって、全く予想もしていなかった所でこの不具合に遭遇してしまう場合があるという状況でした。

お客さまには、MUTF-7でのエンコーディング結果に , が含まれないようにフォルダ名を工夫するという回避策をご案内していますが、どんな文字列がエンコーディング後にこのパターンになるのかについては事前の予想が難しいため、残念ながら万全とは言えない回答となってしまいました。

この問題は原因そのものは判明したものの、古くからある設計上の判断に起因する問題ということで、どのような方向で対処するのが妥当かという技術的判断の落とし所が見つかるまで、修正にはまだしばらく時間がかかりそうです。

Bug 1569089 - The "-version" ("-v") command line option doesn't report version information when using cmd.exe(Mozilla Firefox)

これは、Firefoxの起動オプションの一部が特定の状況下で動作しなくなっているという問題です。Firefoxは実は firefox.exe -v という風に -v オプションを指定して起動すると一般的なコマンドと同様にバージョン情報を文字列として出力するようになっており、これを使って現在インストール済みのFirefoxのバージョンを確認するという運用を取られているお客さまがいらっしゃったのですが、FirefoxをESR68に更新するとバージョン情報が出力されなくなったために運用に支障が生じている、という事でお問い合わせを頂きました。

調査の結果、Bugに記載しているとおりこの問題そのものは回避が不可能という事が分かったため、Firefoxのバージョンを調べるには何かしら別の方法*1を使わなくてはならない旨をお客さまにはお伝えしました。

-v はあまり使われる事の無さそうなオプションで、我々も正直な所、今回の調査を行うまでオプションの存在自体を把握していませんでした。しかし、報告したBugは優先度P2 (クリティカルな問題に対して付けられる P1 の次に高い優先度)と設定されています。本稿執筆時点では未解決ですが、もしかしたら案外早く修正される事になるかもしれません。

Add managed storage support (Duplicate Tabs Closer)

こちらはFirefox本体ではなくアドオンへの改善提案です。「Duplicate Tabs Closer」は同じURLのタブを重複して開く事を制限するアドオンで、重複の検出基準や検出時の挙動について様々な設定ができるようになっています。しかしながら、それらの設定値はローカルストレージ領域を使って保存されており、システム管理者の側では設定を制御できません。ですので、法人利用であらかじめ動作を決めておきたい場合、アドオンのソースコードを編集して組み込みの初期設定を変更する必要があります。

お問い合わせを頂いたお客さまの導入環境向けにそのように改造する事自体は容易ですが、提供開始後にアドオンが更新されたらそれへの追従の必要が生じます。また、その後同様のニーズをお持ちのお客さまから相談を頂く度に改造版を作る必要もあります。

その一方、Firefoxのアドオン向けAPIにはManaged Storageという機能があり、専用のファイルもしくはポリシー設定用の policies.json に書いた設定をアドオン側で読み取る事ができます。もしDuplicate Tabs CloserがManaged Storageに対応していれば、アドオンのソースコードを毎回編集する必要はありません。

以上の事を踏まえて、Managed Storageから設定値を読み込んで反映するという動作を追加する変更を提案した物が、上記のプルリクエストです。こちらは作者の方のご理解を得る事ができ、無事にマージして頂けました。

法人向けサポートからアップストリームへフィードバックする事の意義

アップストリームへのフィードバックは開発の現場に直接関わる事に等しいため、的確なフィードバックには様々な知識が必要になってきます。FirefoxやThunderbirdのように「ソフトウェア開発者でないエンドユーザーも使う物」ではこの点がネックとなりがちなためか、ユーザー自身によってなされた報告にはどうしても、不正確な報告や発生条件の絞り込みが不足した報告が散見されます。また、多くの日本人にとっては英語自体がハードルとなって余計にフィードバックしにくいという問題もあります。当社が法人サポートの中で行っているフィードバックは、これらの点を補ってフリーソフトウェアを推進していく物と言えます。

ビジネス的観点においても、アップストリームへのフィードバックには意義があります。問題点をそのプロダクトの開発元にフィードバックしないで手元での回避だけを行っていると、プロダクトの更新や変更でその回避策が取れなくなる恐れがあり、将来的なリスク要因になります。早め早めにフィードバックしてプロダクト本体側で問題を解決する事によって、その種のリスクが低減され、より安定して運用できるようになる事が期待できます*2。安物買いの銭失いにならなければ、それはサービスの付加価値と言えます。

そうしてプロダクト自体の品質が高まって企業で採用しやすくなると、法人向けサポートの需要の拡大にも期待できます。また、当社内でフィードバック対象のプロダクトへの理解が深まり知見が増えれば、それだけサービスの品質が向上します。

開発プロジェクトにとっても、ユーザーとなるお客さまにとっても、サポートを提供する当社にとっても利益になるという事で、これは「自由と稼ぐの両立」という理念の1つの実践例となっているわけです。

まとめ

以上、当社が法人向けサポートサービスを通じて行ったフィードバックの例を挙げて、会社の理念をどのように実践しているかをご紹介しました。

当社の法人向けサポート事業はいわゆるSI業界の周辺にあり、直接の取引先やその先のお客さまには「お堅い」業界の会社さまも多くあります。そのような業界はオープンさ・自由さと縁遠いという印象を持つ方が多いかもしれませんが、実際にはその内側ではフリーソフトウェアが使われていることは珍しくありません。やり方次第ではその領域のビジネスと自由なソフトウェアの推進を同時に行えるという事例の1つとして、参考にして頂ければ幸いです。

また、業務上でのフリーソフトウェアの使用においてトラブルに遭遇されていて、フリーソフトウェア製品やプロジェクト側の知識の不足でお困りの企業の担当者さまがいらっしゃいましたら、メールフォームよりお問い合わせ下さい。

*1 Windowsのレジストリの情報を参照する方法が一般的です。

*2 理論上、そうして問題が解消されていった先には全く仕事が無くなるという未来がいずれ訪れる事になりますが、実際にはプロダクトもユーザー側の要件も変化し続けるため、そのような未来がすぐに訪れるという事は、幸か不幸かまだ無さそうです。

2019-09-09

Firefox ESR60からESR68に移行するには

2019年7月9日に、Firefox ESR (Extended Support Release) の最新バージョンとなるFirefox 68.0esrがリリースされました。

この記事では、最新版への移行を検討されている企業ユーザーの皆様に向けて、ESR68に移行するための主要なスケジュールと、ESR60→ESR68の主な変更点について解説します。

移行スケジュール

ESR68に関連する主だったタイムラインを表として整理します(公式スケジュール表より整理・抜粋)

ESR60ESR68メモ
2019年05月21日 (火)Firefox 60.7 ESR-
2019年07月09日 (火)Firefox 60.8 ESRFirefox 68.0 ESRFirefox 68ESR正式リリース
2019年09月03日 (火)Firefox 60.9 ESRFirefox 60.1 ESR
2019年10月22日 (火)-Firefox 68.2 ESRFirefox 60ESRサポート終了
2019年12月10日 (火)-Firefox 68.3 ESR

この表の最も重要なポイントは 2019年10月22日のESR68.2のリリース です。この日をもって、現行バージョンのESR60のサポートが終了するので、これが実質的なバージョンアップのデッドラインとなります。運用を担当されている方は、この日までに検証を終えて、各端末上でアップデートを実行できるようにする必要があります。

なお、日付はいずれも太平洋標準時基準(PST)です。日本時間では、おおむね当日の深夜帯から翌早朝にかけてのリリースとなります。この時差の問題は、「リリース日に合わせて計画を立てたが、肝心のバージョンが当日の営業時間内にリリースされなかった」というミスにつながるので、段取りを組み立てる際に頭にとめておく必要があります。

ESR68の主なトピック

ここからは、ESR60からESR68で何が変わったのかという本題に移ります。変更箇所そのものは非常に膨大で、数万単位のコミットが加えられています。弊社の標準的なガイダンスでは、そこから要点を絞って約25項目を議論します。この記事では、そこから組織運用上、影響の大きい11項目をトピック別にお伝えします。

Symantec社の証明書がすべて失効します

ESR68からSymantecが発行した証明書は「信頼されない証明書」とみなされるようになります。具体的には、HTTPSでアクセスしようとした場合、警告画面が出現して、先に進めないようになります。

Symantecの証明書警告

対象となる証明書は、Symantecの傘下ブランドのVeriSign・RappidSSL・Thawte・GeoTrustが含まれる点に注意してください。2017年に、Symantecは証明書事業をDigicertに譲渡しているので、この譲渡後に発行された証明書であれば問題ありません。とくに法人環境では社内向けのサイトが古い証明書を使っていないか確認しておく必要があります。

自社のサイトが該当しているか確認したい場合は、Symantec SSL Checkerというツールでチェックできます。

本件の背景を含めた詳細は、Mozilla Security Blogの記事を参照してください。

コンテンツブロック機能の対象が拡張されました

Firefoxにはトラッキングスクリプトをブロックする機能が含まれています。この機能が、ESR68で拡張され、暗号通貨マイニングなどを目的とするスクリプトもブロックできるようになりました。

これによって、設定可能な項目は4項目に増えました。以下に一覧表を示します。

種別説明デフォルト設定
トラッカー ユーザーの行動を追跡するスクリプト
例 - google-analystics.com
プライベートウィンドウのみブロック
トラッキングクッキー トラッカーによってセットされるCookie ブロックしない
暗号通貨マイニング ブラウザで暗号通貨をマイニングするスクリプト
例 - coinhive.com
ブロックしない
フィンガープリント採取ユーザーを一意識別する特殊な追跡スクリプト
例 - simility.com
ブロックしない

ブロック対象のリストはGitHubのレポジトリ mozilla-services/shaver-prod-lists で管理されています。Firefoxは定期的にMozillaのリスト配信サーバーと同期して、リストを取得します。リストの取得状況を確認したい場合は、アドレスバーに「about:url-classifier」と打ち込んでください。

注意が必要な点として、この機能を有効化した場合に、一部のサイトの動作への影響(主に埋め込み動画が再生されなくなる問題)が報告されている事に注意してください。

例) Bugzilla 1557363 コンテンツブロック有効時にibm.comの動画が再生できない

互換性を優先するか、安全性を優先するかに応じて、導入する設定を決める必要があります。

プライベートウィンドウでのアドオンの実行に許可が必要になります

アドオンがプライベートウィンドウでは原則として実行されなくなります。実行にはユーザーの明示的な許可が必要となり、ユーザーは画面から個別にアドオンを許可できるようになります。

プライベートウィンドウのアドオン実行

この変更は、企業運用でアドオンを一括導入している場合に問題になります。例えば、管理用のアドオンを導入している場合に、ユーザーがプライベートウィンドウを開いたときにアドオンが実行されなくなってしまうためです。

従来の挙動(プライベートウィンドウでも原則としてアドオンを実行する)に戻したい場合は、次の設定を適用して下さい。

pref("extensions.allowPrivateBrowsingByDefault", true)
RSSフィード機能が削除されました

FirefoxからRSSフィード機能(ライブブックマーク機能)が削除されました。従来はFirefoxにRSSフィードリーダーが標準搭載されていましたが、このリーダーがESR68からは使えなくなります。

RSSフィードリーダー機能

設定などで従来の挙動に戻すことはできないため、代替としてはウェブベースのリーダーを使うか、RSSフィードに対応したアドオンを導入する必要があります。

なお、ESR68に更新する際に、ライブブックマークのバックアップが自動的に作成されます。具体的には「Firefox feeds backup.opml」というOPML形式のファイルがデスクトップに作成されるので、他のリーダーに移行する場合は、このファイルをインポートしてください。

この削除の背景については、ブログ記事「Firefox removes core product support for RSS/Atom feeds」を参照してください。

Ctrl-Tabのタブ切替が「最終アクセス順」に変更になりました

ESR68ではCTRL-TAB押下時のタブ切り替えの挙動が大きく変わっています。CTRL-TABを押すと次のようなUIが表示されるようになりました(また、タブの切り替えの順番も変わっています)。

CTRL-TAB押下時の表示

この変更が適用されるのは、新規にFirefoxをインストールした端末のみです。ESR60がインストールされている環境でバージョン更新を行った場合には、新しいUIは有効化されません(この挙動は設定画面の『Ctrl+Tabで最近使用した順にタブを切り替える』の設定で管理されています)。運用する端末で使い勝手を統一したい場合は、次の設定を導入してください。

// falseで従来の挙動、trueで新しいUIを適用する
pref("browser.ctrlTab.recentlyUsedOrder", false);
ツールバーにFirefox Accountアイコンが追加されました

Firefox Account (Firefox Sync) とは、Mozillaのサーバーにブックマークや履歴を保存できる機能です。
ESR68では、ツールバーにFirefox Account用のアイコンが追加され、ここから機能にアクセスできるようになりました。

Firefox Accountアイコン

多くの企業では、情報セキュリティの観点からFirefox Account機能自体を無効化している場合が多いと思います。このアイコンを非表示にしたい場合には次の設定を導入します。

pref("identity.fxaccounts.toolbar.enabled", false);
インストールパスごとにユーザープロファイルが新規作成されます

Firefoxのインストールパスごとにプロファイルが新規作成されるようになります。たとえば、次のように2つのFirefoxをインストールしていたとします。

  1. /usr/lib/firefox
  2. /usr/local/lib/firefox

従来、この2つのFirefoxは起動時に同じユーザープロファイルを読み込んでいました。しかし、2つのFirefoxのバージョンが異なる場合に、(Firefoxのプロファイルは前方互換性がないので)バージョンの異なるFirefoxが交互にプロファイルを読み書きする結果、ブックマークの情報などが破損してしまう可能性がありました。

例)Support 1214384 Firefox 53にバージョンを戻すとブックマークが消失する

そこで、Firefox 68 ESRからは(1)と(2)のFirefoxは、起動時に別個のプロファイルを作成するようになりました。これによって、1つの端末に複数のFirefoxをインストールした場合にもプロファイルの破損が起こらなくなりました。

この仕組みの詳細については、Firefox 67以降のユーザープロファイルの仕様の詳細をご参照ください。

Windows 10の再起動時に自動起動するようになりました

Windows 10で、セッション終了時にFirefoxを開いていた場合、次の起動時にFirefoxが自動的に起動するようになっています。
一般的なアプリケーションの挙動と揃えた形になりますが、この機能を無効化したい場合は次の設定を導入します。

pref("toolkit.winRegisterApplicationRestart", false);
データ流出を起こしたサイトが検出されるようになりました

Firefoxに「データ流出を起こしたサイトを検知する機能」(Firefox Monitor機能)が導入されました。過去にデータ流出を引き起こしたサイトにアクセスした場合に、次のようなポップアップが表示されます。

Firefox Monitor機能

警告の表示対象となるサイトの一覧はFirefox Monitorの公式ページで確認できます。本記事の執筆時点で、約300サイトが対象サイトとして登録されています。

この機能を無効化したい場合は、次の設定を導入します。

pref("extensions.fxmonitor.enabled", false);
MSI形式インストーラの提供開始

従来はWindows向けにはexe形式のインストーラのみが提供されていましたが、MSI形式のインストーラが提供されるようになりました。Windows 7以降で利用することができます。

ダウンロード方法などの詳細についてはMozillaの公式ヘルプを参照ください。

自動更新の停止方法が変更になりました

企業環境では更新タイミングをコントロールするため、Firefoxの自動更新機能を無効化している場合が多いと思います。従来は、この無効化はAutoConfigを通じて実現することができましたが、ESR68からは「Policy Engine」という新しい仕組みの導入が必須になっています。

Policy Engineの詳細は、[Mozilla Firefox ESR60でのPolicy Engineによるポリシー設定の方法と設定項目のまとめ][20180512]を参照ください。自動更新の無効化が運用の前提になっている場合は、こちらの記事のガイドに沿ってPolicy Engineを導入してください。

まとめ

上述の通り、Firefox 68.0esrには、企業での運用に影響を与える変更が入っています。移行期限まで、あと2ヶ月を切りましたので、計画的に移行をお進め下さい。

タグ: Mozilla
2019-08-22

Fluent BitからGrafana Lokiに転送するには

はじめに

Fluent BitはFluentdファミリーを構成するソフトウェアの一つです。
Fluent BitはGo Pluginプロキシが提供されており、Golangにて共有ライブラリを作成することにより、プラグインとして振る舞わせることのできるインターフェースが提供されています。
この機能については、fluent-bit-go-s3とfluent-bitのGo Pluginプロキシの話でも解説しました。
Fluent BitのGolang製のプラグインのDockerfileを作った話にて突然fluent-bit-go-lokiプラグインが登場してしまっていたので、そのプラグインについての解説を書きます。

Grafana Lokiとは

Lokiとは、新しく開発されたGrafanaのデータソースです。
Lokiにはログを入力するためのAPIが整備されています。

Lokiにレコードを送信するには

ログをPushするのであればPOST /api/prom/pushがAPIのエンドポイントになります。

このAPIのエンドポイントにはJSONまたはProtocol BufferでログをPushできます。
JSON形式でログをLokiに送るにはlabelsを用意するのが少々面倒だったため、fluent-bit-go-lokiではProtocol Bufferでやり取りを行うLokiのクライアントライブラリを使用することにしました。

これをGolangのコードで表現すると次のようになります。

package main

import "github.com/grafana/loki/pkg/promtail/client"
import "github.com/sirupsen/logrus"
import kit "github.com/go-kit/kit/log/logrus"
import "github.com/cortexproject/cortex/pkg/util/flagext"
import "github.com/prometheus/common/model"

import "fmt"
import "time"

func main() {
	cfg := client.Config{}
	// Init everything with default values.
	flagext.RegisterFlags(&cfg)
	var clientURL flagext.URLValue

	url := "http://localhost:3100/api/prom/push"
	// Override some of those defaults
	err := clientURL.Set(url)
	if err != nil {
		fmt.Println("Failed to parse client URL")
		return
	}
	cfg.URL = clientURL
	cfg.BatchWait = 1
	cfg.BatchSize = 10 * 1024

	log := logrus.New()

	loki, err := client.New(cfg, kit.NewLogrusLogger(log))

	line := `{"message": "Sent from Golang!"}`

	labelValue := "from-golang"
	labelSet := model.LabelSet{"lang": model.LabelValue(labelValue)}
	err = loki.Handle(labelSet, time.Now(), line)
	if err != nil {
		fmt.Println("Failed to send Loki")
	} else {
		fmt.Println("Success")
	}
	// Ensure to send record into Loki.
	time.Sleep(3 * time.Second)
}

このLoki向けのクライアントライブラリはバッチ単位で送るため、Handleを呼び出してもすぐにはLokiのAPIエンドポイントには送られないことに注意してください。

Fluent BitのGolang製のプラグインでLokiへイベントを送る

前節でLokiへアクセスするためのGolangのクライアントライブラリの使い方が分かったので、実際にfluent-bit-go-lokiへ組み込んでみます。
FLBPluginInitでLokiにアクセスするための設定を組み立て、FLBPluginFlushでLokiに一行づつイベントを送信するためのバッファに溜めています。
また、Fluent Bitのレコードの情報を余さずLokiに送信するためにJSONへエンコードし直しています。

package main

import "github.com/fluent/fluent-bit-go/output"
import "github.com/grafana/loki/pkg/promtail/client"
import "github.com/sirupsen/logrus"
import kit "github.com/go-kit/kit/log/logrus"
import "github.com/prometheus/common/model"
import "github.com/cortexproject/cortex/pkg/util/flagext"
import "github.com/json-iterator/go"

import (
	"C"
	"fmt"
	"log"
	"time"
	"unsafe"
)

var loki *client.Client
var ls model.LabelSet

//export FLBPluginRegister
func FLBPluginRegister(ctx unsafe.Pointer) int {
	return output.FLBPluginRegister(ctx, "loki", "Loki GO!")
}

//export FLBPluginInit
// (fluentbit will call this)
// ctx (context) pointer to fluentbit context (state/ c code)
func FLBPluginInit(ctx unsafe.Pointer) int {
	// Example to retrieve an optional configuration parameter
	url := output.FLBPluginConfigKey(ctx, "url")
	var clientURL flagext.URLValue
	err := clientURL.Set(url)
	if err != nil {
		log.Fatalf("Failed to parse client URL")
	}
	fmt.Printf("[flb-go] plugin URL parameter = '%s'\n", url)

	cfg := client.Config{}
	// Init everything with default values.
	flagext.RegisterFlags(&cfg)

	// Override some of those defaults
	cfg.URL = clientURL
	cfg.BatchWait = 10 * time.Millisecond
	cfg.BatchSize = 10 * 1024

	log := logrus.New()

	loki, err = client.New(cfg, kit.NewLogrusLogger(log))
	if err != nil {
		log.Fatalf("client.New: %s\n", err)
	}
	ls = model.LabelSet{"job": "fluent-bit"}

	return output.FLB_OK
}

//export FLBPluginFlush
func FLBPluginFlush(data unsafe.Pointer, length C.int, tag *C.char) int {
	var ret int
	var ts interface{}
	var record map[interface{}]interface{}

	dec := output.NewDecoder(data, int(length))

	for {
		ret, ts, record = output.GetRecord(dec)
		if ret != 0 {
			break
		}

		// Get timestamp
		timestamp := ts.(output.FLBTime).Time

		js, err := createJSON(timestamp, record)
		if err != nil {
			fmt.Errorf("error creating message for Grafana Loki: %v", err)
			continue
		}

		err = loki.Handle(ls, timestamp, string(js))
		if err != nil {
			fmt.Errorf("error sending message for Grafana Loki: %v", err)
			return output.FLB_RETRY
		}
	}

	// Return options:
	//
	// output.FLB_OK    = data have been processed.
	// output.FLB_ERROR = unrecoverable error, do not try this again.
	// output.FLB_RETRY = retry to flush later.
	return output.FLB_OK
}

func createJSON(timestamp time.Time, record map[interface{}]interface{}) (string, error) {
	m := make(map[string]interface{})

	for k, v := range record {
		switch t := v.(type) {
		case []byte:
			// prevent encoding to base64
			m[k.(string)] = string(t)
		default:
			m[k.(string)] = v
		}
	}

	js, err := jsoniter.Marshal(m)
	if err != nil {
		return "{}", err
	}

	return string(js), nil
}

//export FLBPluginExit
func FLBPluginExit() int {
	loki.Stop()
	return output.FLB_OK
}

func main() {
}

このファイルをout_loki.goとして保存します。
依存関係のパッケージを準備した後*1、以下のコマンドを実行するとFluent Bit用のLokiプラグインの振る舞いをする共有オブジェクトが作成できます。

$ go build -buildmode=c-shared -o out_loki.so .
Golang製のプラグインの動かし方

Fluent BitのGolang製の共有オブジェクトのプラグインを動かすには例えば、以下のような設定ファイルとコマンドが必要です。

[INPUT]
    Name cpu
    Tag  cpu.local
    # Interval Sec
    # ====
    # Read interval (sec) Default: 1
    Interval_Sec 1

[OUTPUT]
    Name  loki
    Match *
    Url http://localhost:3100/api/prom/push
$ fluent-bit -c /path/to/fluent-bit.conf -e /path/to/out_loki.so

Fluent Bitが以下のようなログを吐き出していれば読み込みに成功して動作しています。


Fluent Bit v1.2.2
Copyright (C) Treasure Data

[2019/07/31 12:15:20] [ info] [storage] initializing...
[2019/07/31 12:15:20] [ info] [storage] in-memory
[2019/07/31 12:15:20] [ info] [storage] normal synchronization mode, checksum disabled, max_chunks_up=128
[2019/07/31 12:15:20] [ info] [engine] started (pid=13346)
[flb-go] plugin URL parameter = 'http://localhost:3100/api/prom/push'
[2019/07/31 12:15:20] [ info] [sp] stream processor started
まとめ

Fluent BitのGo製の共有オブジェクトでのプラグインについてまとまった解説を書きました。
実際のfluent-bit-go-lokiはlabelSetsが複数指定できるようになっていたり、テストが書きやすいようにFluent Bitが関わる部分をinterfaceに分離しています。*2
GolangでもFluent Bitのプラグインを書くことが出来ますからぜひ試してみてください。

*1 筆者は執筆時点ではGolangの依存関係を管理するパッケージマネージャーはdepを使用しています。depでの依存パッケージの管理の開始方法はdepのドキュメントを参照してください。

*2 実際のコードはGitHubリポジトリを参照してください。

タグ: Fluentd
2019-07-31

Fluent BitのGolang製のプラグインのDockerfileを作った話

はじめに

Fluent BitはFluentdファミリーを構成するソフトウェアの一つです。
Fluent BitはGo Pluginプロキシが提供されており、Golangにて共有ライブラリを作成することにより、プラグインとして振る舞わせることのできるインターフェースが提供されています。
この機能については、fluent-bit-go-s3とFluent BitのGo Pluginプロキシの話でも解説しました。

最近はDockerイメージ上での問題が報告される事が多くなって来たので、Dockerfileを眺めている事が多くなりました。

Golang製のツールをビルドするDockerfileの書き方

Golangはコンパイルをしなければ動作しない静的型付きの言語です。
また、Golangの実行バイナリは使用したライブラリを静的にリンクしています。*1
このことが、libcすら何もないLinuxのユーザーランドでのGolang製の実行バイナリを実行することを可能にしています。

Golangのコードをビルドするのに必要なツールチェインが揃っているDockerイメージがDockerHubで提供されています。

例えば、GolangのGOPATHを指定してビルドするDockerfileの例は次のようになります。

FROM golang:1.12.7-stretch
ENV GOOS=linux
ENV GOARCH=amd64
ENV GOPATH=/go
ADD . /go/src/path/to/githubrepo
WORKDIR /go/src/path/to/githubrepo
RUN go build .

Golang製の共有オブジェクトをDockerイメージに載せるには

Golangから作成した共有オブジェクトは基本的に共有ライブラリへの依存が少ないため、Golangのコンパイラで作成された実行ファイルや共有オブジェクトは更に別のDockerfileへコピーして載せることが容易です。
また、最近のDockerではmulti stage buildという機能が入っているため、Dockerfileを多段階に組み合わせることなく一つのDockerfileで多段階のDockerイメージのビルドができるようになっています。

では、ここまでの背景知識を元に、筆者が作成しているfluent-bit-go-lokiプラグインの実例*2を出しつつ解説します。

Golangの共有ライブラリをビルドするには前述のGolangツールチェインがインストールされているイメージを使います。また、fluent-bit-go-lokiは共有ライブラリをCGOを経由して作成するため、それに必要な環境変数も有効にしておきます。

FROM golang:1.12.7-stretch AS build-env
ENV CGO_ENABLED=1
ENV GOOS=linux
ENV GOARCH=amd64
ENV GOPATH=/go
ADD . /go/src/github.com/cosmo0920/fluent-bit-go-loki
WORKDIR /go/src/github.com/cosmo0920/fluent-bit-go-loki
RUN go build -buildmode=c-shared -o out_loki.so .

out_loki.soの作成に必要なCGO_ENABLED=1, GOOS=linux, GOARCH=amd64, GOPATH=/goの設定が完了しました。
後の三行はout_loki.soをビルドするのに必要なディレクトリ構造を整える行と、ワーキングディレクトリの設定と、実際にビルドを行う行です。

ここで、FROM golang:1.12.7-stretch AS build-envとして、このout_loki.soをビルドするイメージをbuild-envという別名をつけている事に注意してください。この名前はビルドパイプラインの後で使用する事になります。

前段のパイプラインの成果物のout_loki.soをただコピーしてfluent/fluent-bit:1.2イメージ*3から派生させています。

作成したout_loki.soのシステムの共有ライブラリの依存関係を見ると、

% ldd out_loki.so
	linux-vdso.so.1 (0x00007ffc58381000)
	libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007fd36a51d000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fd36a35c000)
	/lib64/ld-linux-x86-64.so.2 (0x00007fd36bc46000)

となるため、libc相当の共有ライブラリがシステムに居れば十分に動作させることができそうです。
ここでは、fluent/fluent-bit:1.2がgcr.io/distroless/ccイメージを利用しており、このイメージ内に必要なライブラリが揃っていると見做すことで問題なさそうです。

FROM fluent/fluent-bit:1.2
COPY --from=build-env /go/src/github.com/cosmo0920/fluent-bit-go-loki/out_loki.so /usr/lib/x86_64-linux-gnu/
# ...

これらと、fluent-bit-go-lokiプラグインの実行に必要なエントリポイントについてはfluent-bit-go-lokiプラグインのDockerfileを参照してください。

まとめ

このようなイメージの作成の仕方をする事でGolangの実行バイナリや共有オブジェクトのみを載せたサイズが軽量となるイメージを作成することができます。
Golang製のツールをDockerイメージに載せるこの方法は始めはなんてまどろっこしい方法を取るんだ!と思っていたら、実行ファイルとその実行に必要な共有オブジェクトのみにしておける利点がある事がわかり腑に落ちました。
Dockerfileを書く事ができればDocker上にデプロイするのは非常に簡単なのでDocker上でもガンガンFluentdファミリーでログを収集してみてください。

*1 https://golang.org/doc/faq#Why_is_my_trivial_program_such_a_large_binary

*2 LokiはGrafanaの新しく開発されたデータソースです。

*3 fluent-bitのイメージはlibc, libgcc程度しか入っていない軽量イメージから派生しています。

タグ: Fluentd
2019-07-30

winevt_c gemを開発をした話

はじめに

Windows EventLogをRubyで取得するには元々win32-eventlog gemがありました。
このgemはWindows EventLogへ読み込みと書き込みができるものです。
ただし、文字コードに関してはCP_ACP(Windowsの初期コードページ)を使っていたため、日本語版WindowsではCP932(Windows-31J)の範囲内の文字列しか読み込むことができませんでした。

エンコーディングをより正確に扱うには?

Windows APIではchar *wchar *を相互に変換するAPIを提供しています。

どの関数も先ず変換先の文字列を長さを取得してから再度取得した変換先の文字列の長さを用いてchar *wchar *の変換を行います。

winevt_c gemでは、wchar *からRubyのUTF8の文字列を作成する関数を多用しました。

VALUE
wstr_to_rb_str(UINT cp, const WCHAR *wstr, int clen)
{
    VALUE vstr;
    CHAR *ptr;
    int len = WideCharToMultiByte(cp, 0, wstr, clen, nullptr, 0, nullptr, nullptr);
    ptr = ALLOCV_N(CHAR, vstr, len);
    WideCharToMultiByte(cp, 0, wstr, clen, ptr, len, nullptr, nullptr);
    VALUE str = rb_utf8_str_new_cstr(ptr);
    ALLOCV_END(vstr);

    return str;
}

この関数は任意のコードページを指定できるようにしていますが、winevt_cでは基本的にCP_UTF8(UTF8のコードページ)を指定して呼び出しています。

VALUE utf8str;
// some stuff.
utf8str = wstr_to_rb_str(CP_UTF8, <Wide Char Result>, -1);

Windows EventLogの新しいAPI

Windows Vistaより、Windows EventLogを読み取る新しいAPIが提供されています。
winevt_c gemではこのAPIを用いてRubyからWindows EventLogを読み込めるように実装しました。

EvtFormatMessageを用いたEventLogのメッセージの取得方法

EvtFormatMessageを用いると、EventLogのメッセージがWindowsの組み込みの管理ツールであるイベントビューアで取得できるメッセージと同じものが取得できます。winevt_cでは、get_message関数内でEvtFormatMessageを使用しています。このEvtFormatMessage関数の引数のFlagsEvtFormatMessageEventを指定しているところがポイントです。

winevt_cの現状

winevt_cはfluent-plugin-windows-eventlogに新しく実装したin_windows_eventlog2のWindows EventLogを取得するメソッドを中心に実装しています。そのため、現状では以下の機能は実装されていません。

  • リモートにあるWindowsの認証情報を作成し、リモートのWindowsにあるWindows EventLogを取得する
  • 指定したWindows EventLogのチャンネルからevtxファイルとしてエクスポートする
  • チャンネルのメタデータや設定をRubyから取得する関数

まとめ

fluent-plugin-windows-eventlogのin_windows_eventlog2を実装するにあたって作成したwinevt_c gemについて簡単に解説しました。この記事はFluentd meetup 2019でWindows EventLogに関するプラグイン回りの発表した話では軽くしか触れられていないwinevt_cの役割と、現状を解説する目的で執筆しました。winevt_c gem単体ではWindows EventLogはXMLで取得され、中身を見るためにはXMLのパースが現状では必要になってしまいますが、RubyでWindows EventLogを扱う方法の一つとして検討してみてください。

タグ: Fluentd
2019-07-25

Fluent-bit-go-s3とFluent BitのGo Pluginプロキシの話

はじめに

Fluent BitはFluentdファミリーを構成するソフトウェアの一つです。
Fleunt BitのWindows対応はプラグインの対応だけではなく、Go Pluginプロキシについても対応を行っています。

Fluent BitのGo Pluginプロキシはv1.1.0からWindowsでも利用可能です。

Fluent BitのGo Pluginプロキシとは

Fluent Bitは共有オブジェクトファイルを読み込んでOutputプラグインとして振る舞わせることができます。

例えば、Golangを使って共有オブジェクトファイルを作成する場合のコードは以下のようになります。

package main

import "github.com/fluent/fluent-bit-go/output"

//export FLBPluginRegister
func FLBPluginRegister(def unsafe.Pointer) int {
    // Gets called only once when the plugin.so is loaded
	return output.FLBPluginRegister(ctx, "gskeleton", "Hey GO!")
}

//export FLBPluginInit
func FLBPluginInit(plugin unsafe.Pointer) int {
    // Gets called only once for each instance you have configured.
    return output.FLB_OK
}

//export FLBPluginFlushCtx
func FLBPluginFlushCtx(ctx, data unsafe.Pointer, length C.int, tag *C.char) int {
    // Gets called with a batch of records to be written to an instance.
    return output.FLB_OK
}

//export FLBPluginExit
func FLBPluginExit() int {
	return output.FLB_OK
}

func main() {
}

このコードを用いて以下のようにするとGolangで共有オブジェクトを作成することができます。

$ go build -buildmode=c-shared -o out_skeleton.so .

GolangをOutputプラグインに使う利点

GolangでFluent BitのOutputプラグインに使う利点はC言語向けには提供されていないライブラリであり、Golang向けには提供されているライブラリが使用できることです。
例えば、Golang向けにはAWS SDKが提供されています。

このSDKを用いてFluent BitからAWSのサービスへ直接接続することができます。

Golang製Fluent Bitプラグインの例

Golang製のFluent Bitプラグインの例として、AWS S3へFluent Bitから直接接続できるようにするプラグインを実際に作成しました。
GolangはWindowsではdllを作成することが可能です。Windows向けのDLLに関してはリリースページに置いてあります。

まとめ

Fluent BitのGo Pluginプロキシとその実例を紹介しました。Golang製であれば共有オブジェクトとしてもさほど依存が含まれません。
例として、Debian 10上でGo 1.12.7でビルドした場合には以下の参照が共有オブジェクトへ含まれます。

% ldd out_s3.so
	linux-vdso.so.1 (0x00007ffd615ca000)
	libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f6e182a3000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f6e180e2000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f6e19933000)

Fluent BitのGolang製のプラグインという選択肢が増えたことにより、AWS SDKを使ってAWSのサービスへ直接接続するプラグインを開発することが出来るようになりました。
依存ライブラリもGolangのみであれば一つのソースでWindowsで動くDLLの作成やLinuxで動作する共有オブジェクトの作成や、macOSでの動的ロードライブラリの作成ができます。そしてそれらをFluent Bit読み込ませて動かすことができます。ぜひ試してみてください。

タグ: Fluentd
2019-07-24

タグ:
年・日ごとに見る
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|07|08|09|10|11|12|
2018|01|02|03|04|05|06|07|08|09|10|11|12|
2019|01|02|03|04|05|06|07|08|09|10|