ククログ
https://www.clear-code.com/blog/
クリアコードのログ
クリアコード
ククログ
https://www.clear-code.com/images/icon.png
https://www.clear-code.com/blog/
-
Redmineのプラグインの作り方 - UNCプラグインを例にして
https://www.clear-code.com/blog/2024/3/26/implement-redmine-plugin-from-scratch.html
2024-03-26T00:00:00+09:00
阿部です。
UNC形式のパスをクリップボードにコピーするRedmineプラグイン
を開発しました。
それを通してRedmineのプラグインを一から作る方法について学んだので、その内容を紹介します。
作ったときの私の知識レベルが以下なので、そのくらいの方であれば参考になる内容だと思います。
Redmineは普段利用している
RedmineはRuby on Railsで開発されているらしい
コードは読んだことがない
Ruby on Rails自体は初心者
Redmineのプラグイン開発の経験もなし
はじめに
今回開発したプラグインはUNC形式のパスをクリックでクリップボードにコピーできるようにするものです。
そのようにすることでUNC形式のファイルパスを開くまでのステップを少なくすることが狙いです。
近年はセキリティ上の理由からブラウザで file:// URLへのアクセスに制限があるため、この補助機能でファイルが開きやすくなります。
もう少し具体的に機能の説明をすると、
\\server\path\to\file といった文字列のUNC形式のパスを、
以下のキャプチャにあるクリックでパスをコピーするリンクに自動で変換して表示します。
このRedmineのプラグインではそのような表示文字列の自動変換以外にもできることはいろいろありますが、
本記事では「Redmineの投稿内容を加工して表示」するプラグインを一から作る方法について紹介します。
流れ
Redmine本体を動かす
プラグインの雛形生成
プラグイン情報の更新
プラグインの実装
1. Redmine本体を動かす
今回の本題ではありませんので、Redmineを起動するまでのコマンドの列挙に留めます。
以下の通り実行することでRedmineが起動しブラウザで確認できます。
git clone --depth 1 https://github.com/redmine/redmine.git
cd redmine
cat <<EOF > config/database.yml
development:
adapter: sqlite3
database: db/redmine.sqlite3
EOF
bundle install
bin/rails generate_secret_token
bin/rails db:migrate
REDMINE_LANG=en bin/rails redmine:load_default_data
bin/rails server
2. プラグインの雛形生成
以下のコマンドでプラグインの雛形を生成します。
bin/rails generate redmine_plugin <plugin_name>
Redmine本体を起動したディレクトリで実行します。
bin/rails generate redmine_plugin UNC
create plugins/unc/app
create plugins/unc/app/controllers
create plugins/unc/app/helpers
create plugins/unc/app/models
create plugins/unc/app/views
create plugins/unc/db/migrate
create plugins/unc/lib/tasks
create plugins/unc/assets/images
create plugins/unc/assets/javascripts
create plugins/unc/assets/stylesheets
create plugins/unc/config/locales
create plugins/unc/test
create plugins/unc/test/fixtures
create plugins/unc/test/unit
create plugins/unc/test/functional
create plugins/unc/test/integration
create plugins/unc/test/system
create plugins/unc/README.rdoc
create plugins/unc/init.rb
create plugins/unc/config/routes.rb
create plugins/unc/config/locales/en.yml
create plugins/unc/test/test_helper.rb
plugins ディレクトリ以下に生成されます。
3. プラグイン情報の更新
init.rb を開発するプラグインに合わせて更新します。
以下の内容に更新しました。
Redmine::Plugin.register :unc do
name 'UNC plugin'
author 'ClearCode Inc.'
description 'Improve UNC format path accessibility'
version '0.0.1'
url 'https://gitlab.com/redmine-plugin-unc/redmine-plugin-unc'
author_url 'https://gitlab.com/clear-code'
end
4. プラグインの実装
すべての投稿内容にあるUNC形式のパスを変換したいので、ApplicationHelper#parse_non_pre_blocks を拡張して対応することにしました。
ApplicationHelper#parse_non_pre_blocks は、表示するHTMLのうち
<pre> タグ以外の要素(= マークアップ言語としてMarkdownを使っている場合は ```、 ` で囲まれたところ以外)に対して
ブロックで受け取った変換処理を行うメソッドです。
そこにUNC形式のパスを変換する処理を追加する方針です。
このメソッドを Module#prepend を使って拡張します。
(Redmineのドキュメントにある
Plugin Internals > Extending the Redmine Core
に相当する方法です。)
実際のコードが30行もないので掲載して簡単に解説します。
module Unc
module ApplicationHelperMixin
def link_unc_path(path)
link_to_function(
"(#{l(:copy_unc_path)}) #{path}",
'copyTextToClipboard(this)',
class: 'icon icon-copy-link',
data: {'clipboard-text' => path}
)
end
def parse_unc_paths(text)
text.gsub!(/"(\\\\.+?)"|\\\\[^\s<]+/) do |matched|
link_unc_path($1 || matched)
end
end
def parse_non_pre_blocks(text, obj, macros, options={})
super do |txt|
yield txt
parse_unc_paths(txt)
end
end
end
end
ApplicationHelper.prepend(Unc::ApplicationHelperMixin)
link_unc_path
クリップボードへコピーするリンクのHTMLを生成するメソッド
クリップボードへコピーするJavaScriptがRedmineに含まれていたので活用
Redmineにある類似のメソッドを参考にしました ApplicationHelper#copy_object_url_link
parse_unc_paths
投稿テキストに含まれるUNC形式のパスを link_unc_path が返すHTMLに置換
parse_non_pre_blocks
super が受け取ったブロックを実行し、その後で parse_unc_paths の処理を実行
ApplicationHelper.prepend(Unc::ApplicationHelperMixin)
ApplicationHelper に Unc::ApplicationHelperMixin を追加して、parse_non_pre_blocks を上書き
最後に Unc::ApplicationHelperMixin の読み込みを init.rb へ追加して完了です。
追加後の init.rb は以下の通りです。
Redmine::Plugin.register :unc do
name 'UNC plugin'
author 'ClearCode Inc.'
description 'Improve UNC format path accessibility'
version '0.0.1'
url 'https://gitlab.com/redmine-plugin-unc/redmine-plugin-unc'
author_url 'https://gitlab.com/clear-code'
end
# Load.
Unc::ApplicationHelperMixin
最後の1行がポイントで Unc::ApplicationHelperMixin により
lib/unc/application_helper_mixin.rb が読み込まれ、
その中で ApplicationHelper.prepend(Unc::ApplicationHelperMixin) が実行されます。
その結果、このプラグインで変更した処理がRedmineに組み込まれます。
まとめ
UNC形式のパスをクリップボードにコピーするRedmineプラグインを一から作る方法を紹介しました。
「Redmineの投稿内容を加工して表示」するプラグインを開発するときに役立つと思います。
違う機能のプラグインを開発する場合は方法が変わりますので、
Plugin Tutorial を参考にご対応ください。
なお、この機能はクリアコードが提供しているRedmineサポート契約の中で開発しました。
クリアコードが提供するRedmineサポートではお客さんが必要としている機能を理解した上で
汎用的なプラグインとして対応することが多いです。
その際、プラグインは自由なソフトウェアとして開発し広く公開します。
汎用的な機能のため、お客さんの機密情報などが含まれることがなく、
お客さんにとってデメリットはありません。
また他のお客さんでも使うことになったら、メンテナンス費用を
按分できるコストメリットもあります。
プラグインではなくRedmine本体を改良した方がよさそうな場合もあります。
その時はお客さんが使っているRedmineにパッチを当ててもらうのではなく、
Redmine本体に機能を提案してRedmine本体に取り込んでもらえるようにします。
自分たちでパッチを当てるよりもRedmine本体に入っていた方がアップデートが楽になり、
メンテナンスコストが下がるからです。
ただ、Redmine本体の開発をコントロールすることはできないので、
一時的にプラグインで対処したりパッチを当ててもらうということはあります。
そのようなRedmineサポートがよい方はお問い合わせフォームよりご連絡ください。
参考: Redmineのドキュメント
Redmine本体のインストール
Installing Redmine
プラグイン開発のチュートリアル
Plugin Tutorial
Plugin Internals
<p>阿部です。</p>
<p><a href="https://gitlab.com/redmine-plugin-unc/redmine-plugin-unc">UNC形式のパスをクリップボードにコピーするRedmineプラグイン</a>
を開発しました。
それを通してRedmineのプラグインを一から作る方法について学んだので、その内容を紹介します。</p>
<p>作ったときの私の知識レベルが以下なので、そのくらいの方であれば参考になる内容だと思います。</p>
<ul>
<li>Redmineは普段利用している</li>
<li>RedmineはRuby on Railsで開発されているらしい
<ul>
<li>コードは読んだことがない</li>
<li>Ruby on Rails自体は初心者</li>
</ul>
</li>
<li>Redmineのプラグイン開発の経験もなし</li>
</ul>
<!--more-->
<h2 id="はじめに">はじめに</h2>
<p>今回開発したプラグインはUNC形式のパスをクリックでクリップボードにコピーできるようにするものです。
そのようにすることでUNC形式のファイルパスを開くまでのステップを少なくすることが狙いです。</p>
<p>近年はセキリティ上の理由からブラウザで <code>file://</code> URLへのアクセスに制限があるため、この補助機能でファイルが開きやすくなります。</p>
<p>もう少し具体的に機能の説明をすると、
<code>\\server\path\to\file</code> といった文字列のUNC形式のパスを、
以下のキャプチャにあるクリックでパスをコピーするリンクに自動で変換して表示します。</p>
<p><img src="/images/blog/implement-redmine-plugin-from-scratch/copy_path_exmaple.png" alt="スクリーンショット:表示例" /></p>
<p>このRedmineのプラグインではそのような表示文字列の自動変換以外にもできることはいろいろありますが、
本記事では「Redmineの投稿内容を加工して表示」するプラグインを一から作る方法について紹介します。</p>
<h2 id="流れ">流れ</h2>
<ol>
<li>Redmine本体を動かす</li>
<li>プラグインの雛形生成</li>
<li>プラグイン情報の更新</li>
<li>プラグインの実装</li>
</ol>
<h3 id="1.-redmine本体を動かす">1. Redmine本体を動かす</h3>
<p>今回の本題ではありませんので、Redmineを起動するまでのコマンドの列挙に留めます。
以下の通り実行することでRedmineが起動しブラウザで確認できます。</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git clone <span class="nt">--depth</span> 1 https://github.com/redmine/redmine.git
<span class="nb">cd </span>redmine
<span class="nb">cat</span> <span class="o"><<</span><span class="no">EOF</span><span class="sh"> > config/database.yml
development:
adapter: sqlite3
database: db/redmine.sqlite3
</span><span class="no">EOF
</span>bundle <span class="nb">install
</span>bin/rails generate_secret_token
bin/rails db:migrate
<span class="nv">REDMINE_LANG</span><span class="o">=</span>en bin/rails redmine:load_default_data
bin/rails server
</code></pre></div></div>
<h3 id="2.-プラグインの雛形生成">2. プラグインの雛形生成</h3>
<p>以下のコマンドでプラグインの雛形を生成します。</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>bin/rails generate redmine_plugin <plugin_name>
</code></pre></div></div>
<p>Redmine本体を起動したディレクトリで実行します。</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>bin/rails generate redmine_plugin UNC
create plugins/unc/app
create plugins/unc/app/controllers
create plugins/unc/app/helpers
create plugins/unc/app/models
create plugins/unc/app/views
create plugins/unc/db/migrate
create plugins/unc/lib/tasks
create plugins/unc/assets/images
create plugins/unc/assets/javascripts
create plugins/unc/assets/stylesheets
create plugins/unc/config/locales
create plugins/unc/test
create plugins/unc/test/fixtures
create plugins/unc/test/unit
create plugins/unc/test/functional
create plugins/unc/test/integration
create plugins/unc/test/system
create plugins/unc/README.rdoc
create plugins/unc/init.rb
create plugins/unc/config/routes.rb
create plugins/unc/config/locales/en.yml
create plugins/unc/test/test_helper.rb
</code></pre></div></div>
<p><code>plugins</code> ディレクトリ以下に生成されます。</p>
<h3 id="3.-プラグイン情報の更新">3. プラグイン情報の更新</h3>
<p><code>init.rb</code> を開発するプラグインに合わせて更新します。
以下の内容に更新しました。</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="no">Redmine</span><span class="o">::</span><span class="no">Plugin</span><span class="p">.</span><span class="nf">register</span> <span class="ss">:unc</span> <span class="k">do</span>
<span class="nb">name</span> <span class="s1">'UNC plugin'</span>
<span class="n">author</span> <span class="s1">'ClearCode Inc.'</span>
<span class="n">description</span> <span class="s1">'Improve UNC format path accessibility'</span>
<span class="n">version</span> <span class="s1">'0.0.1'</span>
<span class="n">url</span> <span class="s1">'https://gitlab.com/redmine-plugin-unc/redmine-plugin-unc'</span>
<span class="n">author_url</span> <span class="s1">'https://gitlab.com/clear-code'</span>
<span class="k">end</span>
</code></pre></div></div>
<h3 id="4.-プラグインの実装">4. プラグインの実装</h3>
<p>すべての投稿内容にあるUNC形式のパスを変換したいので、<code>ApplicationHelper#parse_non_pre_blocks</code> を拡張して対応することにしました。</p>
<p><code>ApplicationHelper#parse_non_pre_blocks</code> は、表示するHTMLのうち
<code><pre></code> タグ以外の要素(= マークアップ言語としてMarkdownを使っている場合は <code>```</code>、 <code>`</code> で囲まれたところ以外)に対して
ブロックで受け取った変換処理を行うメソッドです。
そこにUNC形式のパスを変換する処理を追加する方針です。</p>
<p>このメソッドを <a href="https://rurema.clear-code.com/3.3.0/method/Module/i/prepend.html"><code>Module#prepend</code></a> を使って拡張します。
(Redmineのドキュメントにある
<a href="https://www.redmine.org/projects/redmine/wiki/Plugin_Internals#Extending-the-Redmine-Core">Plugin Internals > Extending the Redmine Core</a>
に相当する方法です。)</p>
<p>実際のコードが30行もないので掲載して簡単に解説します。</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">module</span> <span class="nn">Unc</span>
<span class="k">module</span> <span class="nn">ApplicationHelperMixin</span>
<span class="k">def</span> <span class="nf">link_unc_path</span><span class="p">(</span><span class="n">path</span><span class="p">)</span>
<span class="n">link_to_function</span><span class="p">(</span>
<span class="s2">"(</span><span class="si">#{</span><span class="n">l</span><span class="p">(</span><span class="ss">:copy_unc_path</span><span class="p">)</span><span class="si">}</span><span class="s2">) </span><span class="si">#{</span><span class="n">path</span><span class="si">}</span><span class="s2">"</span><span class="p">,</span>
<span class="s1">'copyTextToClipboard(this)'</span><span class="p">,</span>
<span class="ss">class: </span><span class="s1">'icon icon-copy-link'</span><span class="p">,</span>
<span class="ss">data: </span><span class="p">{</span><span class="s1">'clipboard-text'</span> <span class="o">=></span> <span class="n">path</span><span class="p">}</span>
<span class="p">)</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">parse_unc_paths</span><span class="p">(</span><span class="n">text</span><span class="p">)</span>
<span class="n">text</span><span class="p">.</span><span class="nf">gsub!</span><span class="p">(</span><span class="sr">/"(\\\\.+?)"|\\\\[^\s<]+/</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">matched</span><span class="o">|</span>
<span class="n">link_unc_path</span><span class="p">(</span><span class="vg">$1</span> <span class="o">||</span> <span class="n">matched</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">parse_non_pre_blocks</span><span class="p">(</span><span class="n">text</span><span class="p">,</span> <span class="n">obj</span><span class="p">,</span> <span class="n">macros</span><span class="p">,</span> <span class="n">options</span><span class="o">=</span><span class="p">{})</span>
<span class="k">super</span> <span class="k">do</span> <span class="o">|</span><span class="n">txt</span><span class="o">|</span>
<span class="k">yield</span> <span class="n">txt</span>
<span class="n">parse_unc_paths</span><span class="p">(</span><span class="n">txt</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="no">ApplicationHelper</span><span class="p">.</span><span class="nf">prepend</span><span class="p">(</span><span class="no">Unc</span><span class="o">::</span><span class="no">ApplicationHelperMixin</span><span class="p">)</span>
</code></pre></div></div>
<ul>
<li><code>link_unc_path</code>
<ul>
<li>クリップボードへコピーするリンクのHTMLを生成するメソッド</li>
<li>クリップボードへコピーするJavaScriptがRedmineに含まれていたので活用</li>
<li>Redmineにある類似のメソッドを参考にしました <a href="https://github.com/redmine/redmine/blob/5.1.1/app/helpers/application_helper.rb#L1864-L1870"><code>ApplicationHelper#copy_object_url_link</code></a></li>
</ul>
</li>
<li><code>parse_unc_paths</code>
<ul>
<li>投稿テキストに含まれるUNC形式のパスを <code>link_unc_path</code> が返すHTMLに置換</li>
</ul>
</li>
<li><code>parse_non_pre_blocks</code>
<ul>
<li><code>super</code> が受け取ったブロックを実行し、その後で <code>parse_unc_paths</code> の処理を実行</li>
</ul>
</li>
<li><code>ApplicationHelper.prepend(Unc::ApplicationHelperMixin)</code>
<ul>
<li><code>ApplicationHelper</code> に <code>Unc::ApplicationHelperMixin</code> を追加して、<code>parse_non_pre_blocks</code> を上書き</li>
</ul>
</li>
</ul>
<p>最後に <code>Unc::ApplicationHelperMixin</code> の読み込みを <code>init.rb</code> へ追加して完了です。</p>
<p>追加後の <code>init.rb</code> は以下の通りです。</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="no">Redmine</span><span class="o">::</span><span class="no">Plugin</span><span class="p">.</span><span class="nf">register</span> <span class="ss">:unc</span> <span class="k">do</span>
<span class="nb">name</span> <span class="s1">'UNC plugin'</span>
<span class="n">author</span> <span class="s1">'ClearCode Inc.'</span>
<span class="n">description</span> <span class="s1">'Improve UNC format path accessibility'</span>
<span class="n">version</span> <span class="s1">'0.0.1'</span>
<span class="n">url</span> <span class="s1">'https://gitlab.com/redmine-plugin-unc/redmine-plugin-unc'</span>
<span class="n">author_url</span> <span class="s1">'https://gitlab.com/clear-code'</span>
<span class="k">end</span>
<span class="c1"># Load.</span>
<span class="no">Unc</span><span class="o">::</span><span class="no">ApplicationHelperMixin</span>
</code></pre></div></div>
<p>最後の1行がポイントで <code>Unc::ApplicationHelperMixin</code> により
<code>lib/unc/application_helper_mixin.rb</code> が読み込まれ、
その中で <code>ApplicationHelper.prepend(Unc::ApplicationHelperMixin)</code> が実行されます。
その結果、このプラグインで変更した処理がRedmineに組み込まれます。</p>
<h2 id="まとめ">まとめ</h2>
<p>UNC形式のパスをクリップボードにコピーするRedmineプラグインを一から作る方法を紹介しました。
「Redmineの投稿内容を加工して表示」するプラグインを開発するときに役立つと思います。</p>
<p>違う機能のプラグインを開発する場合は方法が変わりますので、
<a href="https://www.redmine.org/projects/redmine/wiki/Plugin_Tutorial">Plugin Tutorial</a> を参考にご対応ください。</p>
<p>なお、この機能はクリアコードが提供しているRedmineサポート契約の中で開発しました。
クリアコードが提供するRedmineサポートではお客さんが必要としている機能を理解した上で
汎用的なプラグインとして対応することが多いです。
その際、プラグインは自由なソフトウェアとして開発し広く公開します。
汎用的な機能のため、お客さんの機密情報などが含まれることがなく、
お客さんにとってデメリットはありません。
また他のお客さんでも使うことになったら、メンテナンス費用を
按分できるコストメリットもあります。</p>
<p>プラグインではなくRedmine本体を改良した方がよさそうな場合もあります。
その時はお客さんが使っているRedmineにパッチを当ててもらうのではなく、
Redmine本体に機能を提案してRedmine本体に取り込んでもらえるようにします。
自分たちでパッチを当てるよりもRedmine本体に入っていた方がアップデートが楽になり、
メンテナンスコストが下がるからです。
ただ、Redmine本体の開発をコントロールすることはできないので、
一時的にプラグインで対処したりパッチを当ててもらうということはあります。</p>
<p>そのようなRedmineサポートがよい方は<a href="/contact/">お問い合わせフォーム</a>よりご連絡ください。</p>
<h2 id="参考:-redmineのドキュメント">参考: Redmineのドキュメント</h2>
<ul>
<li>Redmine本体のインストール
<ul>
<li><a href="https://www.redmine.org/projects/redmine/wiki/RedmineInstall">Installing Redmine</a></li>
</ul>
</li>
<li>プラグイン開発のチュートリアル
<ul>
<li><a href="https://www.redmine.org/projects/redmine/wiki/Plugin_Tutorial">Plugin Tutorial</a></li>
<li><a href="https://www.redmine.org/projects/redmine/wiki/Plugin_Internals">Plugin Internals</a></li>
</ul>
</li>
</ul>
-
Fluentd: in_tailプラグインの基本的な使い方をメンテナーが解説
https://www.clear-code.com/blog/2024/3/14/fluentd-how-to-use-in_tail.html
2024-03-14T00:00:00+09:00
こんにちは。Fluentdのメンテナーの福田です。
Fluentdは、様々なデータソースからデータを収集し、様々な出力先へ転送することができる便利なフリーソフトウェアです!
Fluentdでは、プラグインを組み合わせることで様々な用途を実現できます。
その中でも、ファイルからログを収集したい場合にはin_tailプラグインがよく使われます。
しかし、ファイルと一言で言っても、その更新のされ方やログローテートのされ方は様々です。
加えて、in_tailプラグインにはとても多くの設定項目があります。
本記事では、
まずはこれを把握すれば、大体のケースでin_tailプラグインを問題なく使える!
という点をFluentdメンテナー視点でいくつか解説します。
Fluentdを使ってみたいけど設定がよく分からず使えていない、とか、使っているけど設定にあまり自信がない、という方は、ぜひご覧ください。
(以下の説明は Fluentd v1.16.3 時点のものになります)
最初に注意!
Fluentd v1.16.3 (fluent-package v5.0.2, td-agent v4.5.2)以降のバージョンを使用してください。
in_tailプラグインでは、エラーを出さずに収集が停止してしまう、といった大きな不具合が過去にありました。
Fluentd v1.16.3 (fluent-package v5.0.2, td-agent v4.5.2)時点でそのような大きな不具合を修正しています。
詳しくは Fluent Package v5.0.2リリース - in_tailプラグインの重大な不具合の修正 の記事で紹介しているので、ご覧ください。
動作確認の仕方
Fluentdは簡単に手元で動作を確認することができます。
Fluentdの簡単な動かし方については、以前の記事 Fluentdを動かしてみよう! で解説しているので、ぜひご覧ください。
ぜひ今回も実際にin_tailの動きを確認してみてください。
Fluentdを動かしてみよう! では、次の設定でFluentdが実際にデータを処理する様子を確認しました。
<source>
@type sample
tag test
</source>
<match test.**>
@type stdout
</match>
このsource設定の部分を、in_sampleプラグインからin_tailプラグインの設定に置き換えてください。
<source>
@type tail
tag test
path /tmp/test.log
pos_file /tmp/tail-test.pos
<parse>
@type none
</parse>
</source>
<match test.**>
@type stdout
</match>
そうすれば、 Fluentdを動かしてみよう! と同じように、Fluentdが出力するログメッセージとしてin_tailの収集ログを確認することができます。
最も基本的な使い方
まずは最も基本的な使い方を説明します。
設定
in_tailはInputプラグインなので、sourceセクションを足して次の5つの設定をします。
@type
使いたいプラグインの名前を指定します。名前はそのプラグインのドキュメントを確認しましょう。今回は"tail"です1。
tag
後続のFilterプラグインやOutputプラグインへのルーティング等に使うタグを指定します。今回はテスト用にtestとします。
詳しく知りたい方は Config File Syntax - Interlude: Routing や How do the match patterns work? をご覧ください。
path
収集したいファイルのファイルパスを指定します。
pos_file
どこまで収集したかの記録を残すためのファイルパスを指定します。
in_tail設定を複数行う場合は、それぞれ別々のパスを指定してください。
parseセクション
収集したログのパース設定を行います。好きなParserプラグインを使えます。そのまま出力したい場合は、parser_noneプラグインを設定すればよいです(つまり@type noneを指定します)。
以上を設定すると、例えば次のようなsource設定になります。
<source>
@type tail
tag test
path /tmp/test.log
pos_file /tmp/tail-test.pos
<parse>
@type none
</parse>
</source>
in_tailの動作">in_tailの動作
この設定を動かしてみると、in_tailは/tmp/test.logファイルの末尾を監視して、末尾に追記されるログをどんどん収集します。
一度Fluentdを停止しても、pos_fileにどこまで収集したかの記録が残るため、次回起動時には前回の続きから漏れなく収集してくれます。
前の章の「動作確認の仕方」で説明しているように、out_stdoutプラグインと組み合わせて動作を確認できます。
例えば次のようにファイルにログを追記すると、
echo "test log" >> /tmp/test.log
次のようなログをFluentdが出力するので、in_tailがログを収集したことが分かります。
2024-03-13 14:49:02.480691808 +0900 test: {"message":"test log"}
補足: in_tailがファイルを発見して監視している状態であれば、新規に追記したログをすばやく(基本的に数秒以内に)収集します。
ただし、もし新しくファイルを作成した場合、refresh_interval(デフォルト60秒)の間隔で監視ファイルリストを更新するため、1分程度待つ必要があります。
補足: 権限について
pathやpos_fileに指定するファイルパスにFluentdがアクセスできる十分な権限がないと、うまく動かないので注意が必要です。
権限が足りない場合、Permission denied @ rb_sysopen - /tmp/test.logのようなエラーが出ます。
例: pathに対する読み込み権限が足りない2024-03-14 11:53:42 +0900 [warn]: #0 Permission denied @ rb_sysopen - /tmp/test.log
例: pos_fileに対する書き込み権限が足りない2024-03-14 11:45:24 +0900 [error]: #0 unexpected error error_class=Errno::EACCES error="Permission denied @ rb_sysopen - /tmp/tail-test.pos"
例えば、Unix系OSにおいてこのように権限が足りない場合は、chmodやchownコマンドを使って権限を調整したり、ユーザーやグループ設定を調整したりします(パッケージ「Fluent Package」は、デフォルトでfluentdユーザーおよびグループでサービスを実行します)。
pos_fileは読み込みと書き込みの双方の権限が必要です。
パッケージ「Fluent Package」の場合、デフォルトで/var/log/fluentd/が書き込み可能なパスとして作成されているので、pos_fileに/var/log/fluentd/pos/tail-test.posのようなパスを指定するのもよいでしょう。
pathは読み込み権限が必要です。
例えば、syslogが出力する一部のファイルには読み込み権限がないことがあります。
この場合、単にchmodやchownコマンドを使ってカレントファイルの権限を調整するだけでなく、今後ログローテート時に新規作成されるファイルの権限も調整する必要があります。
本記事では詳しい説明を省きますが、必要に応じてsyslog側の設定の調整も検討してください。
前半まとめ
ここまでで基本的なin_tailの設定と動きを確認しました。
以下では、in_tailを使う際に考慮する必要がある主な点として、以下の4点をそれぞれ説明します。
read_from_head: (初回起動時に)新規ファイルを先頭から読ませる
ログローテートへの対応
カレントファイルのファイル名が変化する出力形式への対応
in_tailで収集するべきファイルであるかどうかの確認
まずはこれらの点を把握すれば、大体のケースでin_tailを問題なく使えるはずです!
より高度な使い方をしたい場合は、公式ドキュメントでどんな設定があるのかを確認してみてください。
本記事では紹介しきれない様々な機能をin_tailは持っています!
read_from_head:-(初回起動時に)新規ファイルを先頭から読ませる">read_from_head: (初回起動時に)新規ファイルを先頭から読ませる
in_tailは、ファイル末尾への追記を収集し続ける性質のため、ファイル内容を全て読み込むとは限りません。
一度そのファイルの収集を開始した後は、pos_fileの記録を元にそれ以降の追記を漏れなく収集します(一時的にFluentdが停止したとしても)。
しかし、最初にファイルの収集を開始するときには、デフォルトではファイルの末尾から開始するため、既存のファイル内容は収集されません。
そのため、基本的には初回起動時のみ気にするべき話になります。
もし初回起動時にファイルの既存の内容を収集してほしい場合は、read_from_headをtrueに設定します。
read_from_head設定が推奨されていた経緯">過去にread_from_head設定が推奨されていた経緯
過去のバージョンで、read_from_head設定が一部の設定に実質必須であった、という経緯がありました。
そのため、とりあえずread_from_head trueを設定しておいた方が安心、という話を今でも聞くことがあります。
しかし、最近のバージョンでは状況が異なります。
Fluentd v1.14.3 (td-agent v4.3.0) 以降のバージョンでは、このパラメターはFluentd起動時の動作にしか影響しません。
そのため、これ以降のバージョンでは、
Fluentd起動時に新規ファイルの内容を全部収集したい
という場合のみread_from_head trueを設定する、という考え方で問題ありません。
補足: Fluentd v1.14.3 (td-agent v4.3.0) よりも前のバージョンでは、pathに日付フォーマット(%Y%m%dなど)を使う場合やfollow_inodes機能を使う場合にはread_from_head trueが実質必須でした。
それ以降のバージョンでは、こういった動的に新規ファイルを発見する機能においてはread_from_head設定に依らずにファイルの内容を全て読み込むように修正されています。
ログローテートへの対応
多くの場合、末尾への追記形式のログファイルはログローテートします。
in_tailはログローテートにうまく対応しますが、形式によっては注意が必要なケースもあります。
in_tailがログローテートに対応する仕組み">in_tailがログローテートに対応する仕組み
in_tailは対象ファイルを監視していますが、一定の条件を満たすと対象ファイルにログローテートが発生したと検知します。
ログローテート発生を検出すると、対象ファイルの先頭から収集を再開します。
これによって、ログローテートが発生しても、漏れなく新しいログファイルの収集を継続します。
ログローテート検知の条件は、次の2つの少なくともどちらか一方を満たすことです。
A: inode番号(ファイル固有のid)が変わる
B: ファイルサイズが小さくなる
Aを満たすようなログローテート形式であれば、全く気にすることはありません。
安定してログローテートを検知できます。
一方で、Aを満たさないようなログローテート形式の場合は注意が必要です。
Bのファイルサイズでしか判定できないからです。
次の章で詳しく説明します。
対象ファイルのログローテート形式
前章の「A: inode番号(ファイル固有のid)が変わる」を満たすようなログローテート形式は、次のような動きになります。
カレントファイルをリネームする: test.log -> test.log.1
新規カレントファイルを作成する: test.logを新規作成
Linux系のシステムでよく見られるのがこの形式です。
一方で、次のようなログローテート形式も存在します。
カレントファイルの内容をコピーする: test.logの内容をtest.log.1にコピー
カレントファイルの内容をクリアする: test.logのサイズが0になる
結果としては最初の形式と同じに見えます。
しかし、カレントファイルの中身は初期化されていますが、ファイル自体は以前と同じファイルのままです。
そのため、ファイル固有のidであるinode番号が変化しておらず、前章の「A: inode番号(ファイル固有のid)が変わる」の条件を満たしません。
この他にも、世の中にはいくつかのログローテート形式が存在します。
in_tailを使う際には、inode番号が変わる(カレントファイルが新規生成される)ようなログローテート形式なのかどうかを確認してください。
もし、そうでない場合は、前章の「B: ファイルサイズが小さくなる」という条件を満たさないと、ログローテートを検知できないことに注意してください。
事前に、ログローテートを検知して漏れなく収集できるかどうか、十分に検証することを推奨します。
カレントファイルのファイル名が変化する出力形式への対応
ログローテートの亜種として、次のようにカレントファイルのファイル名が変化する形式があります。
例えば、次のように日毎に生成された新規ファイルが、その日の間カレントファイルになる、という場合があります。
/tmp/test_20240306.log
/tmp/test_20240307.log <- 昨日のカレントファイル
/tmp/test_20240308.log <- 本日のカレントファイル
これを簡単に収集する2つの方法を紹介します。
strftime()のフォーマット文字列で、当日のファイルパスを収集する
ワイルドカードでまとめて収集する
以下それぞれの紹介です。
strftime()のフォーマット文字列で、当日のファイルパスを収集する">strftime()のフォーマット文字列で、当日のファイルパスを収集する
path設定には %Y, %m, %d, といったstrftime()のフォーマット文字列を利用できます。
例えば、/tmp/test_%Y%m%d.logのように設定すると、次のようにその日時に応じて動的に収集対象とするファイルパスを変更できます。
2024年3月7日時点: /tmp/test_20240307.logを収集
2024年3月8日時点: /tmp/test_20240308.logを収集
これによって、カレントファイル名が定期的に変化するようなケースに対応できます。
in_tail全体の設定例は次のようになります。
<source>
@type tail
tag test
path /tmp/test_%Y%m%d.log
pos_file /tmp/tail-test.pos
<parse>
@type none
</parse>
</source>
ワイルドカードでまとめて収集する
path設定にはワイルドカード*を利用できます。
これによって、複数のファイルをまとめて収集対象とすることができます。
例えば、/tmp/test_*.logのように設定することで、以下のファイルをまとめて収集対象にできます。
/tmp/test_20240306.log
/tmp/test_20240307.log
/tmp/test_20240308.log
これによって、カレントファイル名が定期的に変化するようなケースにも対応できます。
しかし、今回のように前日以前のファイルにはもうログが出力されなくて、当日に相当するファイルだけ収集対象にできれば十分、というケースもあります。
その場合に、このように全部まとめて収集対象とするのはリソースの無駄使いであり、特にファイル数が多い場合はファイルハンドルなどのリソース値を圧迫する懸念があります。
そのような場合は、limit_recently_modified設定を併用して、ファイルの更新日時がある程度最近のファイルだけに収集対象を絞るとよいです。
例えば、limit_recently_modified 3dのように設定することで、3日以内に更新されたファイルのみに収集対象を絞り込むことができます。
(当日のファイルのみ収集すればよい場合は1dでもよいのですが、念のために少し余裕を持たせてもよいでしょう)。
以上に基づくと、in_tail全体の設定例は次のようになります。
<source>
@type tail
tag test
path /tmp/test_*.log
pos_file /tmp/tail-test.pos
limit_recently_modified 3d
<parse>
@type none
</parse>
</source>
in_tailで収集するべきファイルであるかどうかの確認">in_tailで収集するべきファイルであるかどうかの確認
in_tailは、ファイルからデータを収集するプラグインとしてFluentdに標準で組み込まれているため、ファイルからの収集によく使われます。
しかし、データソースがファイルだからといって、必ずしもin_tailが適切なプラグインとは限りません。
in_tailは、末尾への追記形式であるテキストファイルの収集を担うプラグインである、という点に注意してください。
もしそうではないファイルの収集を行いたい場合は、適切な収集方法を広く(Fluentd以外の機能を使う選択肢も含めて)検討してください。
以下、in_tailが適さない主な事例です。
バイナリファイル
上書き形式で更新されるファイル
まとめ
本記事では、in_tailプラグインの基本的な使い方について、メンテナー視点で解説しました。
より高度な使い方をしたい場合は、公式ドキュメントでどんな設定があるのかを確認してみてください。
本記事では紹介しきれなかった様々な機能をin_tailは持っています!
また、Fluentdコミュニティーでは、日本語用のQ&Aも用意しています。
何か疑問や困ったことがあれば、こちらで質問してみてください!
https://github.com/fluent/fluentd/discussions/categories/q-a-japanese
最後に、クリアコードはFluentdのサポートサービスを行っています。
Fluent Packageへのアップデート支援や影響のあるバグ・脆弱性のレポートなどのサポートをいたしますので、詳しくはFluentdのサポートサービスをご覧いただき、お問い合わせフォームよりお気軽にお問い合わせください。
慣習的にInputプラグインはin_というプレフィックスを付けて呼ぶことが多いですが、@typeを指定するときはプレフィックスを付けないことが一般的です。なぜなら、sourceセクション内に設定していることでInputプラグインであることが自明になっているからです。 ↩
<p>こんにちは。<a href="http://www.fluentd.org">Fluentd</a>のメンテナーの福田です。</p>
<p>Fluentdは、様々なデータソースからデータを収集し、様々な出力先へ転送することができる便利なフリーソフトウェアです!</p>
<p>Fluentdでは、プラグインを組み合わせることで様々な用途を実現できます。
その中でも、ファイルからログを収集したい場合には<a href="https://docs.fluentd.org/input/tail">in_tailプラグイン</a>がよく使われます。</p>
<p>しかし、ファイルと一言で言っても、その更新のされ方やログローテートのされ方は様々です。
加えて、<code>in_tail</code>プラグインにはとても多くの設定項目があります。</p>
<p>本記事では、</p>
<ul>
<li>まずはこれを把握すれば、大体のケースで<code>in_tail</code>プラグインを問題なく使える!</li>
</ul>
<p>という点をFluentdメンテナー視点でいくつか解説します。</p>
<p>Fluentdを使ってみたいけど設定がよく分からず使えていない、とか、使っているけど設定にあまり自信がない、という方は、ぜひご覧ください。</p>
<!--more-->
<p>(以下の説明は Fluentd v1.16.3 時点のものになります)</p>
<h2 id="最初に注意!">最初に注意!</h2>
<p>Fluentd v1.16.3 (fluent-package v5.0.2, td-agent v4.5.2)以降のバージョンを使用してください。</p>
<p><code>in_tail</code>プラグインでは、エラーを出さずに収集が停止してしまう、といった大きな不具合が過去にありました。
Fluentd v1.16.3 (fluent-package v5.0.2, td-agent v4.5.2)時点でそのような大きな不具合を修正しています。</p>
<p>詳しくは <a href="/blog/2023/12/15/fluent-package-v5.0.2.html">Fluent Package v5.0.2リリース - in_tailプラグインの重大な不具合の修正</a> の記事で紹介しているので、ご覧ください。</p>
<h2 id="動作確認の仕方">動作確認の仕方</h2>
<p>Fluentdは簡単に手元で動作を確認することができます。
Fluentdの簡単な動かし方については、以前の記事 <a href="/blog/2024/2/16/fluentd-tutorial-run-fluentd.html">Fluentdを動かしてみよう!</a> で解説しているので、ぜひご覧ください。</p>
<p>ぜひ今回も実際に<code>in_tail</code>の動きを確認してみてください。</p>
<p><a href="/blog/2024/2/16/fluentd-tutorial-run-fluentd.html">Fluentdを動かしてみよう!</a> では、次の設定でFluentdが実際にデータを処理する様子を確認しました。</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><source></span>
@type sample
tag test
<span class="nt"></source></span>
<span class="nt"><match</span> <span class="err">test.**</span><span class="nt">></span>
@type stdout
<span class="nt"></match></span>
</code></pre></div></div>
<p>この<code>source</code>設定の部分を、<a href="https://docs.fluentd.org/input/sample">in_sampleプラグイン</a>から<a href="https://docs.fluentd.org/input/tail">in_tailプラグイン</a>の設定に置き換えてください。</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><source></span>
@type tail
tag test
path /tmp/test.log
pos_file /tmp/tail-test.pos
<span class="nt"><parse></span>
@type none
<span class="nt"></parse></span>
<span class="nt"></source></span>
<span class="nt"><match</span> <span class="err">test.**</span><span class="nt">></span>
@type stdout
<span class="nt"></match></span>
</code></pre></div></div>
<p>そうすれば、 <a href="/blog/2024/2/16/fluentd-tutorial-run-fluentd.html">Fluentdを動かしてみよう!</a> と同じように、Fluentdが出力するログメッセージとして<code>in_tail</code>の収集ログを確認することができます。</p>
<h2 id="最も基本的な使い方">最も基本的な使い方</h2>
<p>まずは最も基本的な使い方を説明します。</p>
<h3 id="設定">設定</h3>
<p><code>in_tail</code>は<a href="https://docs.fluentd.org/input">Inputプラグイン</a>なので、<a href="https://docs.fluentd.org/configuration/config-file#id-1.-source-where-all-the-data-comes-from">sourceセクション</a>を足して次の5つの設定をします。</p>
<ul>
<li><code>@type</code>
<ul>
<li>使いたいプラグインの名前を指定します。名前はそのプラグインのドキュメントを確認しましょう。<a href="https://docs.fluentd.org/input/tail#example-configuration">今回は"tail"です</a><sup class="footnote-ref"><a href="#fn1" id="fnref1">1</a></sup>。</li>
</ul>
</li>
<li><code>tag</code>
<ul>
<li>後続の<a href="https://docs.fluentd.org/filter">Filterプラグイン</a>や<a href="https://docs.fluentd.org/output">Outputプラグイン</a>へのルーティング等に使うタグを指定します。今回はテスト用に<code>test</code>とします。</li>
<li>詳しく知りたい方は <a href="https://docs.fluentd.org/configuration/config-file#interlude-routing">Config File Syntax - Interlude: Routing</a> や <a href="https://docs.fluentd.org/configuration/config-file#how-do-the-match-patterns-work">How do the match patterns work?</a> をご覧ください。</li>
</ul>
</li>
<li><a href="https://docs.fluentd.org/input/tail#path">path</a>
<ul>
<li>収集したいファイルのファイルパスを指定します。</li>
</ul>
</li>
<li><a href="https://docs.fluentd.org/input/tail#pos_file-highly-recommended">pos_file</a>
<ul>
<li>どこまで収集したかの記録を残すためのファイルパスを指定します。</li>
<li><code>in_tail</code>設定を複数行う場合は、それぞれ別々のパスを指定してください。</li>
</ul>
</li>
<li><a href="https://docs.fluentd.org/input/tail#less-than-parse-greater-than-directive-required">parseセクション</a>
<ul>
<li>収集したログのパース設定を行います。好きな<a href="https://docs.fluentd.org/parser">Parserプラグイン</a>を使えます。そのまま出力したい場合は、<a href="https://docs.fluentd.org/parser/none">parser_noneプラグイン</a>を設定すればよいです(つまり<code>@type none</code>を指定します)。</li>
</ul>
</li>
</ul>
<p>以上を設定すると、例えば次のような<code>source</code>設定になります。</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><source></span>
@type tail
tag test
path /tmp/test.log
pos_file /tmp/tail-test.pos
<span class="nt"><parse></span>
@type none
<span class="nt"></parse></span>
<span class="nt"></source></span>
</code></pre></div></div>
<h3 id="<code>in_tail</code>の動作"><code>in_tail</code>の動作</h3>
<p>この設定を動かしてみると、<code>in_tail</code>は<code>/tmp/test.log</code>ファイルの末尾を監視して、末尾に追記されるログをどんどん収集します。
一度Fluentdを停止しても、<code>pos_file</code>にどこまで収集したかの記録が残るため、次回起動時には前回の続きから漏れなく収集してくれます。</p>
<p>前の章の「動作確認の仕方」で説明しているように、<a href="https://docs.fluentd.org/output/stdout">out_stdoutプラグイン</a>と組み合わせて動作を確認できます。
例えば次のようにファイルにログを追記すると、</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">echo</span> <span class="s2">"test log"</span> <span class="o">>></span> /tmp/test.log
</code></pre></div></div>
<p>次のようなログをFluentdが出力するので、<code>in_tail</code>がログを収集したことが分かります。</p>
<pre><code>2024-03-13 14:49:02.480691808 +0900 test: {"message":"test log"}
</code></pre>
<p>補足: <code>in_tail</code>がファイルを発見して監視している状態であれば、新規に追記したログをすばやく(基本的に数秒以内に)収集します。
ただし、もし新しくファイルを作成した場合、<a href="https://docs.fluentd.org/input/tail#refresh_interval">refresh_interval</a>(デフォルト60秒)の間隔で監視ファイルリストを更新するため、1分程度待つ必要があります。</p>
<h3 id="補足:-権限について">補足: 権限について</h3>
<p><code>path</code>や<code>pos_file</code>に指定するファイルパスにFluentdがアクセスできる十分な権限がないと、うまく動かないので注意が必要です。
権限が足りない場合、<code>Permission denied @ rb_sysopen - /tmp/test.log</code>のようなエラーが出ます。</p>
<ul>
<li>例: <code>path</code>に対する読み込み権限が足りない<pre><code>2024-03-14 11:53:42 +0900 [warn]: #0 Permission denied @ rb_sysopen - /tmp/test.log
</code></pre></li>
<li>例: <code>pos_file</code>に対する書き込み権限が足りない<pre><code>2024-03-14 11:45:24 +0900 [error]: #0 unexpected error error_class=Errno::EACCES error="Permission denied @ rb_sysopen - /tmp/tail-test.pos"
</code></pre></li>
</ul>
<p>例えば、Unix系OSにおいてこのように権限が足りない場合は、<code>chmod</code>や<code>chown</code>コマンドを使って権限を調整したり、ユーザーやグループ設定を調整したりします(パッケージ「Fluent Package」は、デフォルトで<code>fluentd</code>ユーザーおよびグループでサービスを実行します)。</p>
<p><code>pos_file</code>は読み込みと書き込みの双方の権限が必要です。
パッケージ「Fluent Package」の場合、デフォルトで<code>/var/log/fluentd/</code>が書き込み可能なパスとして作成されているので、<code>pos_file</code>に<code>/var/log/fluentd/pos/tail-test.pos</code>のようなパスを指定するのもよいでしょう。</p>
<p><code>path</code>は読み込み権限が必要です。
例えば、syslogが出力する一部のファイルには読み込み権限がないことがあります。
この場合、単に<code>chmod</code>や<code>chown</code>コマンドを使ってカレントファイルの権限を調整するだけでなく、今後ログローテート時に新規作成されるファイルの権限も調整する必要があります。
本記事では詳しい説明を省きますが、必要に応じてsyslog側の設定の調整も検討してください。</p>
<h3 id="前半まとめ">前半まとめ</h3>
<p>ここまでで基本的な<code>in_tail</code>の設定と動きを確認しました。</p>
<p>以下では、<code>in_tail</code>を使う際に考慮する必要がある主な点として、以下の4点をそれぞれ説明します。</p>
<ul>
<li><code>read_from_head</code>: (初回起動時に)新規ファイルを先頭から読ませる</li>
<li>ログローテートへの対応</li>
<li>カレントファイルのファイル名が変化する出力形式への対応</li>
<li><code>in_tail</code>で収集するべきファイルであるかどうかの確認</li>
</ul>
<p>まずはこれらの点を把握すれば、大体のケースで<code>in_tail</code>を問題なく使えるはずです!
より高度な使い方をしたい場合は、<a href="https://docs.fluentd.org/input/tail">公式ドキュメント</a>でどんな設定があるのかを確認してみてください。
本記事では紹介しきれない様々な機能を<code>in_tail</code>は持っています!</p>
<h2 id="<code>read_from_head</code>:-(初回起動時に)新規ファイルを先頭から読ませる"><code>read_from_head</code>: (初回起動時に)新規ファイルを先頭から読ませる</h2>
<p><code>in_tail</code>は、ファイル末尾への追記を収集し続ける性質のため、ファイル内容を全て読み込むとは限りません。</p>
<p>一度そのファイルの収集を開始した後は、<code>pos_file</code>の記録を元にそれ以降の追記を漏れなく収集します(一時的にFluentdが停止したとしても)。
しかし、最初にファイルの収集を開始するときには、デフォルトではファイルの末尾から開始するため、既存のファイル内容は収集されません。</p>
<p>そのため、基本的には初回起動時のみ気にするべき話になります。
もし初回起動時にファイルの既存の内容を収集してほしい場合は、<a href="https://docs.fluentd.org/input/tail#read_from_head">read_from_head</a>を<code>true</code>に設定します。</p>
<h3 id="過去に<code>read_from_head</code>設定が推奨されていた経緯">過去に<code>read_from_head</code>設定が推奨されていた経緯</h3>
<p>過去のバージョンで、<code>read_from_head</code>設定が一部の設定に実質必須であった、という経緯がありました。
そのため、とりあえず<code>read_from_head true</code>を設定しておいた方が安心、という話を今でも聞くことがあります。
しかし、最近のバージョンでは状況が異なります。</p>
<p>Fluentd v1.14.3 (td-agent v4.3.0) 以降のバージョンでは、このパラメターはFluentd起動時の動作にしか影響しません。
そのため、これ以降のバージョンでは、</p>
<ul>
<li>Fluentd起動時に新規ファイルの内容を全部収集したい</li>
</ul>
<p>という場合のみ<code>read_from_head true</code>を設定する、という考え方で問題ありません。</p>
<p>補足: Fluentd v1.14.3 (td-agent v4.3.0) よりも前のバージョンでは、<code>path</code>に日付フォーマット(<code>%Y%m%d</code>など)を使う場合や<a href="https://docs.fluentd.org/input/tail#follow_inodes">follow_inodes</a>機能を使う場合には<code>read_from_head true</code>が実質必須でした。
それ以降のバージョンでは、こういった動的に新規ファイルを発見する機能においては<code>read_from_head</code>設定に依らずにファイルの内容を全て読み込むように修正されています。</p>
<h2 id="ログローテートへの対応">ログローテートへの対応</h2>
<p>多くの場合、末尾への追記形式のログファイルはログローテートします。
<code>in_tail</code>はログローテートにうまく対応しますが、形式によっては注意が必要なケースもあります。</p>
<h3 id="<code>in_tail</code>がログローテートに対応する仕組み"><code>in_tail</code>がログローテートに対応する仕組み</h3>
<p><code>in_tail</code>は対象ファイルを監視していますが、一定の条件を満たすと対象ファイルにログローテートが発生したと検知します。
ログローテート発生を検出すると、対象ファイルの先頭から収集を再開します。
これによって、ログローテートが発生しても、漏れなく新しいログファイルの収集を継続します。</p>
<p>ログローテート検知の条件は、次の2つの少なくともどちらか一方を満たすことです。</p>
<ul>
<li>A: inode番号(ファイル固有のid)が変わる</li>
<li>B: ファイルサイズが小さくなる</li>
</ul>
<p>Aを満たすようなログローテート形式であれば、全く気にすることはありません。
安定してログローテートを検知できます。</p>
<p>一方で、Aを満たさないようなログローテート形式の場合は注意が必要です。
Bのファイルサイズでしか判定できないからです。</p>
<p>次の章で詳しく説明します。</p>
<h3 id="対象ファイルのログローテート形式">対象ファイルのログローテート形式</h3>
<p>前章の「A: inode番号(ファイル固有のid)が変わる」を満たすようなログローテート形式は、次のような動きになります。</p>
<ol>
<li>カレントファイルをリネームする: <code>test.log</code> -> <code>test.log.1</code></li>
<li>新規カレントファイルを作成する: <code>test.log</code>を新規作成</li>
</ol>
<p>Linux系のシステムでよく見られるのがこの形式です。</p>
<p>一方で、次のようなログローテート形式も存在します。</p>
<ol>
<li>カレントファイルの内容をコピーする: <code>test.log</code>の内容を<code>test.log.1</code>にコピー</li>
<li>カレントファイルの内容をクリアする: <code>test.log</code>のサイズが0になる</li>
</ol>
<p>結果としては最初の形式と同じに見えます。
しかし、カレントファイルの中身は初期化されていますが、ファイル自体は以前と同じファイルのままです。
そのため、ファイル固有のidであるinode番号が変化しておらず、前章の「A: inode番号(ファイル固有のid)が変わる」の条件を満たしません。</p>
<p>この他にも、世の中にはいくつかのログローテート形式が存在します。
<code>in_tail</code>を使う際には、inode番号が変わる(カレントファイルが新規生成される)ようなログローテート形式なのかどうかを確認してください。</p>
<p>もし、そうでない場合は、前章の「B: ファイルサイズが小さくなる」という条件を満たさないと、ログローテートを検知できないことに注意してください。
事前に、ログローテートを検知して漏れなく収集できるかどうか、十分に検証することを推奨します。</p>
<h2 id="カレントファイルのファイル名が変化する出力形式への対応">カレントファイルのファイル名が変化する出力形式への対応</h2>
<p>ログローテートの亜種として、次のようにカレントファイルのファイル名が変化する形式があります。
例えば、次のように日毎に生成された新規ファイルが、その日の間カレントファイルになる、という場合があります。</p>
<pre><code>/tmp/test_20240306.log
/tmp/test_20240307.log <- 昨日のカレントファイル
/tmp/test_20240308.log <- 本日のカレントファイル
</code></pre>
<p>これを簡単に収集する2つの方法を紹介します。</p>
<ul>
<li><code>strftime()</code>のフォーマット文字列で、当日のファイルパスを収集する</li>
<li>ワイルドカードでまとめて収集する</li>
</ul>
<p>以下それぞれの紹介です。</p>
<h3 id="<code>strftime()</code>のフォーマット文字列で、当日のファイルパスを収集する"><code>strftime()</code>のフォーマット文字列で、当日のファイルパスを収集する</h3>
<p><a href="https://docs.fluentd.org/input/tail#path">path</a>設定には <code>%Y</code>, <code>%m</code>, <code>%d</code>, といった<a href="https://docs.ruby-lang.org/ja/latest/method/Time/i/strftime.html">strftime()のフォーマット文字列</a>を利用できます。
例えば、<code>/tmp/test_%Y%m%d.log</code>のように設定すると、次のようにその日時に応じて動的に収集対象とするファイルパスを変更できます。</p>
<ul>
<li>2024年3月7日時点: <code>/tmp/test_20240307.log</code>を収集</li>
<li>2024年3月8日時点: <code>/tmp/test_20240308.log</code>を収集</li>
</ul>
<p>これによって、カレントファイル名が定期的に変化するようなケースに対応できます。
<code>in_tail</code>全体の設定例は次のようになります。</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><source></span>
@type tail
tag test
path /tmp/test_%Y%m%d.log
pos_file /tmp/tail-test.pos
<span class="nt"><parse></span>
@type none
<span class="nt"></parse></span>
<span class="nt"></source></span>
</code></pre></div></div>
<h3 id="ワイルドカードでまとめて収集する">ワイルドカードでまとめて収集する</h3>
<p><a href="https://docs.fluentd.org/input/tail#path">path</a>設定にはワイルドカード<code>*</code>を利用できます。
これによって、複数のファイルをまとめて収集対象とすることができます。
例えば、<code>/tmp/test_*.log</code>のように設定することで、以下のファイルをまとめて収集対象にできます。</p>
<pre><code>/tmp/test_20240306.log
/tmp/test_20240307.log
/tmp/test_20240308.log
</code></pre>
<p>これによって、カレントファイル名が定期的に変化するようなケースにも対応できます。</p>
<p>しかし、今回のように前日以前のファイルにはもうログが出力されなくて、当日に相当するファイルだけ収集対象にできれば十分、というケースもあります。
その場合に、このように全部まとめて収集対象とするのはリソースの無駄使いであり、特にファイル数が多い場合はファイルハンドルなどのリソース値を圧迫する懸念があります。</p>
<p>そのような場合は、<a href="https://docs.fluentd.org/input/tail#limit_recently_modified">limit_recently_modified</a>設定を併用して、ファイルの更新日時がある程度最近のファイルだけに収集対象を絞るとよいです。
例えば、<code>limit_recently_modified 3d</code>のように設定することで、3日以内に更新されたファイルのみに収集対象を絞り込むことができます。
(当日のファイルのみ収集すればよい場合は<code>1d</code>でもよいのですが、念のために少し余裕を持たせてもよいでしょう)。</p>
<p>以上に基づくと、<code>in_tail</code>全体の設定例は次のようになります。</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><source></span>
@type tail
tag test
path /tmp/test_*.log
pos_file /tmp/tail-test.pos
limit_recently_modified 3d
<span class="nt"><parse></span>
@type none
<span class="nt"></parse></span>
<span class="nt"></source></span>
</code></pre></div></div>
<h2 id="<code>in_tail</code>で収集するべきファイルであるかどうかの確認"><code>in_tail</code>で収集するべきファイルであるかどうかの確認</h2>
<p><code>in_tail</code>は、ファイルからデータを収集するプラグインとしてFluentdに標準で組み込まれているため、ファイルからの収集によく使われます。
しかし、データソースがファイルだからといって、必ずしも<code>in_tail</code>が適切なプラグインとは限りません。</p>
<p><code>in_tail</code>は、<strong>末尾への追記</strong>形式である<strong>テキスト</strong>ファイルの収集を担うプラグインである、という点に注意してください。
もしそうではないファイルの収集を行いたい場合は、適切な収集方法を広く(Fluentd以外の機能を使う選択肢も含めて)検討してください。</p>
<p>以下、<code>in_tail</code>が適さない主な事例です。</p>
<ul>
<li>バイナリファイル</li>
<li>上書き形式で更新されるファイル</li>
</ul>
<h2 id="まとめ">まとめ</h2>
<p>本記事では、<code>in_tail</code>プラグインの基本的な使い方について、メンテナー視点で解説しました。</p>
<p>より高度な使い方をしたい場合は、<a href="https://docs.fluentd.org/input/tail">公式ドキュメント</a>でどんな設定があるのかを確認してみてください。
本記事では紹介しきれなかった様々な機能を<code>in_tail</code>は持っています!</p>
<p>また、Fluentdコミュニティーでは、日本語用のQ&Aも用意しています。
何か疑問や困ったことがあれば、こちらで質問してみてください!</p>
<ul>
<li><a href="https://github.com/fluent/fluentd/discussions/categories/q-a-japanese">https://github.com/fluent/fluentd/discussions/categories/q-a-japanese</a></li>
</ul>
<p>最後に、クリアコードは<a href="/services/fluentd-service.html">Fluentdのサポートサービス</a>を行っています。
Fluent Packageへのアップデート支援や影響のあるバグ・脆弱性のレポートなどのサポートをいたしますので、詳しくは<a href="/services/fluentd-service.html">Fluentdのサポートサービス</a>をご覧いただき、<a href="/contact/">お問い合わせフォーム</a>よりお気軽にお問い合わせください。</p>
<section class="footnotes">
<ol>
<li id="fn1">
<p>慣習的にInputプラグインは<code>in_</code>というプレフィックスを付けて呼ぶことが多いですが、<code>@type</code>を指定するときはプレフィックスを付けないことが一般的です。なぜなら、<code>source</code>セクション内に設定していることでInputプラグインであることが自明になっているからです。 <a href="#fnref1" class="footnote-backref">↩</a></p>
</li>
</ol>
</section>
-
公開のOSS開発プロジェクトの業務での開発事例:Waterfoxのサイドバー開発依頼(契約編)
https://www.clear-code.com/blog/2024/3/12/collaboration-of-waterfox-and-tst-contract.html
2024-03-12T00:00:00+09:00
結城です。
先だってFirefoxのフォーク版の一つとして知られるWaterfoxプロジェクトのブログにてアナウンスがあった、Waterfoxの次期バージョンにおける「タブのプレビュー画像を伴った縦置き型のタブバー」の開発を、当社業務の一環として筆者が担当させて頂きました。
先方のブログでも言及されている「仮想スクロール」の技術的な詳細については、筆者個人のブログにて、解説記事を別途公開しております。
ここでは、それとは別の切り口として、「個人の開発者宛に頂いた連絡を、どのようにして企業の案件として請け負い、成果をオープンソース開発プロジェクトに還元したか」に焦点を当て、前後編に分けて、前編(この記事)では初期の交渉と仕様検討段階で行ったこと、後編では実装段階で行ったことをご紹介します。
初期の交渉
発端となる依頼
始まりは、筆者の個人活動のメールアドレス宛に届いた、Waterfoxプロジェクト主催のAlexさんからのご相談でした。
メールには、Waterfoxプロジェクトで縦置き型のタブバーを正式な新機能として実装しようとしていて、同様の趣旨の機能を提供するFirefox用アドオンであるTree Style Tab(以下、TST)の作者の筆者に、仕事として助力をお求めである旨が綴られていました。
アドオン作者にブラウザー本体の改造について相談を?と疑問に思われるかもしれませんが、Firefoxはバージョン57の時点でアドオンの仕様が大きく変わっており、Firefox 56以前のアドオンは「XULアドオン」と呼ばれ、その開発にはFirefox本体の開発と共通する知識が必要でした。
ですので、その時代を知る古株の拡張機能作者である筆者にご相談が寄せられたのは納得のいくところです。
会社への相談
ただ、どのような形で関わるにせよ、作業量はかなりの大きさになりそうです。
諸事情により本件に投じられる筆者のプライベートの時間は限られたものとなりそうだったため、開発を個人で責任を持ってお請けするのは難しいと思えました。
幸い、当社ではオープンソース開発を事業として行っており、公開プロジェクトの開発への業務での参加1も、海外企業をスポンサーとした業務2も、どちらも先例があります。
そこで、本件も社の業務時間で行えないかどうか、先方と当社のブラウザーサポート事業チームの両方に相談したところ、「双方のスケジュール感・予算規模・筆者の案件へのアサイン状況などの問題がなければ、社の仕事として請けるのは可能である」との判断を頂けました。
正直なところ、本件のような事例の再現性で最重要キーになるのはこの点でしょう。
雇用主と依頼主のどちらについても「オープンソース開発のマネタイズ」に肯定的だったのは、幸運でした。
(そうでなければそもそも、TSTが生まれてすらいなかったかもしれません。)
要件定義と見積もり
ヒアリング
スケジュール(どのくらいの期間がかかるか)と工数(どのくらいの予算が必要か、相手の想定予算に収まるのかどうか)を見積もるためには、要件定義が必要です。
具体的にどのようなゴールを目指すのか先方に尋ね、要件は以下の通りと分かりました。
パワーユーザー向けに、Waterfoxにおいてシームレスに利用できる縦型タブバーを実現する。
実装方法は問わない。
TSTを下敷きにする場合、開発の成果はTST側にフィードバックして公開しても問題無い。
機能としては、以下を必須の要件とする。
縦置きタブバーであること。
タブ内埋め込みのプレビュー(サムネイル)があること。
ブラウザー本体の設定画面に設定画面を組み込むこと。
設定画面へ遷移するためのボタンがあること。
開発期間は2ヵ月ほどを目処とする。
これを踏まえ、これらの要件を満たす具体的な方法として、以下の表の3つの方向性を検討してみました。
案
メリット
デメリット
素案1: Waterfox自体のタブ実装を改造する。
開発の自由度が高い。
開発結果の検証コストが大きい。将来のバージョンのFirefoxへの追従コストが非常に大きくなる恐れがある。
素案2: TSTをWaterfox向けに改造する。
既にある物が出発点となるため、必要工数は小さくなる(はず)。開発結果の検証コストが小さい。将来のバージョンのFirefoxへの追従コストが小さくなる。TSTへ成果が還元される。
Manifest V2の先行きが不透明。WebExtensions APIの制約により実現できない機能があり、Experiments APIの実装コストが必要。タブの数が多いときの性能に難がある。
素案3: Waterfoxネイティブのサイドバーパネルとして実装する。
開発の自由度が高い。将来のバージョンのFirefoxへの追従コストが小さくなる。
開発結果の検証コストが大きい。すべてを実装し直すためのコストがかかる。
以下、それぞれの検討内容を述べていきます。
素案1:Waterfox自体のタブ実装を改造する
Waterfox自身がすでにFirefoxを改造したものなので、この選択肢はWaterfoxプロジェクトとしては順当なものです。
このやり方では、技術的にはほぼ無制限に自由な開発が可能なのがメリットですが、それとひきかえのデメリットもあります。
まず、開発結果を検証するためには「Waterfox本体のビルド」の手間がかかります。
CIの定義ファイルを見ると、Windows版のビルドは複数のステージに分けて行われており、それなりの手間がかかる様子が窺えます。
いくらか簡略化はできたとしても、アドオン開発の手軽さに比べると重たい作業なのは変わりません。
また、筆者が最も大きなデメリットと考えるのは、開発の成果がベースバージョンのFirefoxに対して密結合になるため、Firefoxの変更の影響を強く受ける点です。
Firefox 56以前は、Firefoxの開発チームも既存のXULアドオンとの互換性をある程度意識していたそうですが、XULアドオンが廃止されて互換性維持の制約がなくなった現在のFirefoxでは、大胆な変更が度々行われており、追従コストは以前よりも大きくなっています。
WaterfoxはFirefox ESR3を元にしているため、更新の頻度は1年に1回程度で済みますし、ベースバージョンとなるFirefoxも単一のバージョンに限られるので、複数バージョンのFirefoxを想定した工夫が不要な分、XULアドオン開発よりも工数は小さくなりそうです。
それでも、毎回のメジャーアップデートへの追従では1人月程度の工数は発生しかねません。
会社の業務としては継続的な収入を見込める案件は歓迎ですが、この件が今回のみの単発作業にとどまった場合、以後の追従作業に支障が生じる恐れがあり、Waterfoxプロジェクトとしてはデメリットとなり得ます4。
素案2: TSTをWaterfox向けに改造する
すでにあるFirefox用アドオンのTSTを要件に合うように改造する場合は、全体的に必要な工数が小さくなるのが大きなメリットです。
前項での懸念の1つめの「ビルドの手間」は、アドオンであれば、変更の度にブラウザーのUI上で再読み込みするだけで済みます5。
また、Firefox本体の更新に対する今のアドオンの追従の容易さは、現在のTST自身がその証明となっています。
この場合のデメリットの1つ目は、TSTが準拠しているアドオンの仕様である「Manifest V2」の将来性の不透明さです。
FirefoxではManifest V2は当面存続する旨が公表されていますが、将来に渡って安心して使える確約はありません。
この懸念を払拭するには、Manifest V3へ移行するほかありませんが、この時点で今回の要件をManifest V3で満たせるかどうかは不明でした。
というのも、Manifest V3ではTSTの重要コンポーネントの1つである、タブのツリー構造に矛盾が生じるのを防ぐための「タブの変更を常時監視する、永続的なバックグラウンドページ」を使用できない6からです。
このような制約があっても必要な動作を実現できるのかは、PoC7を用いての検証が必要です。
デメリットの2つ目は、WebExtensions APIでは実現できない機能や、WebExtensions固有の制約があることです。
WebExtensions APIの機能的な制約により、TSTには「実装したいができない」機能がいくつかあります(詳しくは後述)。
今回の要件の中にも、ブラウザー本体の設定パネルへの機能追加など、WebExtensions APIでは実現できない機能が含まれています。
そのため、この選択肢においてはExperiments APIの使用が必須となります。
Experiments APIは、WebExtensions APIベースのアドオンにXULアドオンと同等の自由度をもたらす技術です。
リリース版のFirefoxでは機能が封印されていますが、Waterfoxでは機能が有効化されているため、「Waterfox専用のアドオン」の開発では使用を前提にできます。
前述したManifest V3での制約も、極端な話、「永続的なバックグラウンドページ」を代替する物をExperiments APIで実装する形で制限を回避できます。
ただ、Experiments APIの実装は前項同様に、ベースバージョンとなるFirefoxの変更の影響を強く受けます。
Experiments APIに依存する箇所が増えれば増えるほど、Firefoxの更新への追従コストは大きくなる8リスクがあります。
また、WebExtensionsアドオンが別プロセスで実行されることに由来する既知の不具合がある9、という問題もあります。
問題の深刻度次第では「Waterfoxにパッチを当てて問題を解消する」対処も有り得ますが、実際にどこまでできるかは予算のかけ方次第です。
「パワーユーザー向けの機能として提供する」という前提からは、タブが多いときの性能問題も懸念されます。
本件開始時点でのTSTバージョン3.9.22では、数千個単位でタブを開いている複数のユーザーから、動作速度の遅さやメモリー消費量の多さが指摘されていました。
Waterfoxの想定ユーザー層を考慮すると、この点は無視できません。
性能面の懸念を払拭するためには、タブの数が多くなっても性能が劣化しにくい実装方式である仮想スクロールの導入も必須と考えられました。
Manifest V3への移行や仮想スクロールの導入は、TSTで前々から課題と認識していた点で、もしうまく実現できれば、成果をそのまま公開版のTSTにフィードバックできます。
先方は「TST開発プロジェクト自体への支援もしたい」とお考えだったので、この点は非機能要件としてのメリットと言えます。
素案3: Waterfoxネイティブのサイドバーパネルとして実装する
これは、WebExtensions APIの制約に囚われない自由な実装を行いつつ、その実装を「Waterfox専用のサイドバーパネル」内に閉じることによりFirefox由来の部分との接続を弱めて、将来のバージョンのFirefoxへの追従コストを小さくする、という「いいとこ取り」を狙った案です。
技術的な障壁は特になく、最も高機能・高性能かつ長期的に有用な成果を得られると期待できるのがメリットです。
この選択肢のデメリットは、開発規模が大きくなることに尽きます。
WebExtensionsベースではないので、現行のTSTのコードはそのままでは流用できず、多くの部分をゼロから作る必要があります。
開発の成果を検証するためにWaterfoxのビルドが必要であるという点や、成果物がWaterfoxプロジェクトの負債になりかねない点も、素案1と同様です。
提案とフィードバック、作業計画の具体化
Waterfoxプロジェクトが何を優先するかによって、最適な選択肢は異なります。
そのため、この3つの素案を先方に提示し、意見を求めてみました。
そうしたところ、この中では素案2の方向性(WebExtensionsアドオンとして開発)が最も有力そうだとの回答を頂けたため、ここから先はその方向での詳細を詰めるべく、技術的な議論や情報のすり合わせを行いました。
その過程で、先方がすでに得ておられた技術的な知見を共有していただくこともでき、こちらで懸念していた点のいくつかについては、解消の筋道が立ってきました。
また、素案検討段階で未知数の部分が多かった「Manifest V3での実現可能性」についても調査を進め、PoCの設計方針が見えてきました。
ここまでの情報を踏まえて整理した作業内容と計画は、以下のようになりました。
機能はWebExtensionsアドオンとして実装する。
開発するアドオンは、TSTの実装を一部流用する。
開発は以下の2段階に分けて行う。
第1段階:現行のTSTと同等の基本機能の実装。
まずPoCを作成し、Manifest V3で素直に実装した場合の実用性を検証する。
PoCでの実証結果が芳しければ、そのままManifest V3で開発を進める。(この成果はTSTに還元する)
PoCでの実証結果が芳しくなければ、Manifest V2での開発(TSTの改造)に舵を切る。
Manifest V2とManifest V3のどちらで実装するとしても、UIは仮想スクロールを用いて、ほぼ新規に開発する。
第2段階:Waterfox固有の機能拡張部分の実装。
サムネイル、設定画面の組み込みなど、WebExtensions APIでは実現不可能な部分をExperiments APIで実装する。
これらの作業は2ヵ月程度の作業期間内で可能そうである。
こうして、ある程度の精度での見積もりができたことで、晴れて本件を社の案件として受注できるとの判断が下り、筆者の担当業務として取り組める運びとなりました。
開発内容にはまだ未知数の部分があり、詳細な作業の内容は変動する恐れがあるため、契約は請負契約ではなく準委任契約とし、期待される開発の成果と開発の期間のみをお約束する形です。
以降は、契約の話を会社間で進めてもらう一方で、筆者は引き続き、先方との間で実装方針検討のための技術的な議論を進めていきました。
方針の見直し
議論のさなか、先の素案2での「永続的なバックグラウンドページをExperiments APIで代替する」方向性の検討過程で、「そもそも、Manifest V3における制約を迂回してバックグラウンドページを永続化できれば、短命のバックグラウンドページへの設計変更もExperiments APIでの代替実装も必要ないのでは?」という疑問が浮上してきました。
そこで、この方向性について双方で調査を進めたところ、バックグラウンドページの永続化の可否は一定時間経過後にバックグラウンドページをアンロードする処理の中で判定されており、現状でも、隠し設定のextensions.background.idle.enabledをfalseに設定すれば「Manifest V3でバックグラウンドページを永続化」できると分かりました。
この隠し設定は全てのアドオンに影響が及ぶため、正式リリースには反映しづらいのですが、「どこを変更すればバックグラウンドページを永続化できるか」が分かったことの意義は大きいです。
調査と検証の結果、Experiments APIでもこの箇所への介入はできないと分かりましたが、Waterfox側で小改修を行えば、今回開発するアドオンに限定してのバックグラウンドページの永続化は可能です。
また、将来のバージョンのFirefoxに追従する際にも、「バックグラウンドページのアンロード処理に介入する」改修自体はそれほど困難な物とはならないと予想できます。
以上の事を踏まえ、最終的な作業内容と計画を以下の通り変更・決定しました。
機能はWebExtensionsアドオンとして実装する。(仮称「Waterfox Sidebar」)
開発するアドオンは、TSTの実装を一部流用する。
アドオンの仕様はManifest V3とする。
ただし、バックグラウンドページは何らかの方法で永続化できる前提とする。
つまり、アドオンの全体的な設計はManifest V2からほぼ変えない。
バックグランド永続化の具体的な方法は、Waterfox側での特別な対応を想定する。
UIは仮想スクロールを用いて、ほぼ新規に開発する。
サムネイル、設定画面の組み込みなど、WebExtensions APIでは実現不可能な部分をExperiments APIで実装する。
これを踏まえて、作業用のリポジトリーを先方の管理下に用意して頂き、ここからようやく実装作業に入ることになります。
まとめ、次回予告
以上、個人開発のオープンソースソフトウェアに寄せられた連絡を企業としての開発案件に繋げた事例の紹介の前編として、交渉の経緯と見積もり段階での検討内容をご紹介しました。
ここまでの流れは、クライアントがWaterfoxというオープンソース開発プロジェクト10である点を除けば、一般的な「ソフトウェアの受託開発」業務の話と言えます。
マネタイズに苦労するプロジェクトが多い中で、Firefoxの成果をベースにしているとはいえ、外部の開発者に依頼ができるくらいの資金力をWaterfoxプロジェクトが持てているのは、すごいことだと筆者には感じられます。
ところで、冒頭に上げたWaterfoxプロジェクトのブログ記事や筆者個人のブログ記事をご覧になられた方はお気付きかと思いますが、実はこの件の最終的な着地点は「Waterfoxのサイドバー開発」というよりは、「オープンソース開発プロジェクトであるTSTとWaterfoxのコラボレーション」「TSTの開発をWaterfoxが支援」という見え方となっています。
さて、一体この後何があってそうなったのか。
後編では、計画に基づいて開発を始めて序盤で起こった方針の再検討と、「TSTプロジェクトの都合」と「Waterfoxプロジェクトの都合」をどのように両立させたかの工夫についてご紹介します。
Groonga、Fluentdなど。 ↩
Fluentd開発・サポートなど。 ↩
Extended Support Release。セキュリティアップデートが約1年間提供され続ける法人向けバージョン。 ↩
メリットとデメリットを考えるときは「誰にとってなのか」が重要です。自分と相手のメリットとデメリットが一致しているときは何も悩まず済みますが、このように「自分達にとってはメリットになるが、相手にとってはデメリットになり得る」事柄は、判断が難しい所です。筆者は基本的には、「自分と相手の共同プロジェクトとして末永く運用していく上でどうか」という観点で整理するようにしています。 ↩
web-extを使用すれば、ファイルの編集を検知しての自動再読み込みすら可能です。 ↩
Manifest V3では、バックグラウンド動作は、何らかのイベントを契機に実行される短命なスクリプトのみ使用が許されています。 ↩
Proof of Concept。概念実証のための実装。 ↩
それでも、ブラウザーそのものの改造に比べれば追従コストは小さいのですが。 ↩
具体的には、Bug 1595883やBug 1773886など、TSTにおいてFirefox本体側の修正を待っている問題がいくつかあります。 ↩
正確には、その主催であるAlex氏が運営するBrowserWorks社。 ↩
<p>結城です。</p>
<p>先だって<a href="https://www.waterfox.net/blog/waterfox-x-treestyletab/">Firefoxのフォーク版の一つとして知られるWaterfoxプロジェクトのブログにてアナウンスがあった</a>、Waterfoxの次期バージョンにおける「タブのプレビュー画像を伴った縦置き型のタブバー」の開発を、当社業務の一環として筆者が担当させて頂きました。</p>
<p>先方のブログでも言及されている「仮想スクロール」の技術的な詳細については、<a href="https://piro.sakura.ne.jp/latest/blosxom/mozilla/xul/2024-03-08_tst4.htm">筆者個人のブログにて、解説記事を別途公開しております</a>。
ここでは、それとは別の切り口として、「個人の開発者宛に頂いた連絡を、どのようにして企業の案件として請け負い、成果をオープンソース開発プロジェクトに還元したか」に焦点を当て、前後編に分けて、前編(この記事)では初期の交渉と仕様検討段階で行ったこと、後編では実装段階で行ったことをご紹介します。</p>
<!--more-->
<h3 id="初期の交渉">初期の交渉</h3>
<h4 id="発端となる依頼">発端となる依頼</h4>
<p>始まりは、筆者の個人活動のメールアドレス宛に届いた、Waterfoxプロジェクト主催のAlexさんからのご相談でした。
メールには、Waterfoxプロジェクトで縦置き型のタブバーを正式な新機能として実装しようとしていて、同様の趣旨の機能を提供するFirefox用アドオンであるTree Style Tab(以下、TST)の作者の筆者に、仕事として助力をお求めである旨が綴られていました。</p>
<p>アドオン作者にブラウザー本体の改造について相談を?と疑問に思われるかもしれませんが、Firefoxはバージョン57の時点でアドオンの仕様が大きく変わっており、Firefox 56以前のアドオンは「XULアドオン」と呼ばれ、その開発にはFirefox本体の開発と共通する知識が必要でした。
ですので、その時代を知る古株の拡張機能作者である筆者にご相談が寄せられたのは納得のいくところです。</p>
<h4 id="会社への相談">会社への相談</h4>
<p>ただ、どのような形で関わるにせよ、作業量はかなりの大きさになりそうです。
諸事情により本件に投じられる筆者のプライベートの時間は限られたものとなりそうだったため、開発を個人で責任を持ってお請けするのは難しいと思えました。</p>
<p>幸い、当社ではオープンソース開発を事業として行っており、公開プロジェクトの開発への業務での参加<sup class="footnote-ref"><a href="#fn1" id="fnref1">1</a></sup>も、海外企業をスポンサーとした業務<sup class="footnote-ref"><a href="#fn2" id="fnref2">2</a></sup>も、どちらも先例があります。
そこで、本件も社の業務時間で行えないかどうか、先方と当社のブラウザーサポート事業チームの両方に相談したところ、「双方のスケジュール感・予算規模・筆者の案件へのアサイン状況などの問題がなければ、社の仕事として請けるのは可能である」との判断を頂けました。</p>
<p>正直なところ、本件のような事例の再現性で最重要キーになるのはこの点でしょう。
雇用主と依頼主のどちらについても「オープンソース開発のマネタイズ」に肯定的だったのは、幸運でした。
(そうでなければそもそも、TSTが生まれてすらいなかったかもしれません。)</p>
<h3 id="要件定義と見積もり">要件定義と見積もり</h3>
<h4 id="ヒアリング">ヒアリング</h4>
<p>スケジュール(どのくらいの期間がかかるか)と工数(どのくらいの予算が必要か、相手の想定予算に収まるのかどうか)を見積もるためには、要件定義が必要です。
具体的にどのようなゴールを目指すのか先方に尋ね、要件は以下の通りと分かりました。</p>
<ul>
<li>パワーユーザー向けに、Waterfoxにおいてシームレスに利用できる縦型タブバーを実現する。</li>
<li>実装方法は問わない。
<ul>
<li>TSTを下敷きにする場合、開発の成果はTST側にフィードバックして公開しても問題無い。</li>
</ul>
</li>
<li>機能としては、以下を必須の要件とする。
<ul>
<li>縦置きタブバーであること。</li>
<li>タブ内埋め込みのプレビュー(サムネイル)があること。</li>
<li>ブラウザー本体の設定画面に設定画面を組み込むこと。</li>
<li>設定画面へ遷移するためのボタンがあること。</li>
</ul>
</li>
<li>開発期間は2ヵ月ほどを目処とする。</li>
</ul>
<p>これを踏まえ、これらの要件を満たす具体的な方法として、以下の表の3つの方向性を検討してみました。</p>
<table>
<thead>
<tr>
<th>案</th>
<th>メリット</th>
<th>デメリット</th>
</tr>
</thead>
<tbody>
<tr>
<td>素案1: Waterfox自体のタブ実装を改造する。</td>
<td><ul><li>開発の自由度が高い。</li></ul></td>
<td><ul><li>開発結果の検証コストが大きい。</li><li>将来のバージョンのFirefoxへの追従コストが非常に大きくなる恐れがある。</li></ul></td>
</tr>
<tr>
<td>素案2: TSTをWaterfox向けに改造する。</td>
<td><ul><li>既にある物が出発点となるため、必要工数は小さくなる(はず)。</li><li>開発結果の検証コストが小さい。</li><li>将来のバージョンのFirefoxへの追従コストが小さくなる。</li><li>TSTへ成果が還元される。</li></ul></td>
<td><ul><li>Manifest V2の先行きが不透明。</li><li>WebExtensions APIの制約により実現できない機能があり、Experiments APIの実装コストが必要。</li><li>タブの数が多いときの性能に難がある。</li></ul></td>
</tr>
<tr>
<td>素案3: Waterfoxネイティブのサイドバーパネルとして実装する。</td>
<td><ul><li>開発の自由度が高い。</li><li>将来のバージョンのFirefoxへの追従コストが小さくなる。</li></ul></td>
<td><ul><li>開発結果の検証コストが大きい。</li><li>すべてを実装し直すためのコストがかかる。</li></ul></td>
</tr>
</tbody>
</table>
<p>以下、それぞれの検討内容を述べていきます。</p>
<h4 id="素案1:waterfox自体のタブ実装を改造する">素案1:Waterfox自体のタブ実装を改造する</h4>
<p>Waterfox自身がすでにFirefoxを改造したものなので、この選択肢はWaterfoxプロジェクトとしては順当なものです。
このやり方では、<em>技術的にはほぼ無制限に自由な開発が可能</em>なのがメリットですが、それとひきかえのデメリットもあります。</p>
<p>まず、開発結果を検証するためには「Waterfox本体のビルド」の手間がかかります。
<a href="https://github.com/WaterfoxCo/Waterfox/blob/current/.github/workflows/build.yml">CIの定義ファイル</a>を見ると、Windows版のビルドは複数のステージに分けて行われており、それなりの手間がかかる様子が窺えます。
いくらか簡略化はできたとしても、アドオン開発の手軽さに比べると重たい作業なのは変わりません。</p>
<p>また、筆者が最も大きなデメリットと考えるのは、<em>開発の成果がベースバージョンのFirefoxに対して密結合になるため、Firefoxの変更の影響を強く受ける</em>点です。</p>
<p>Firefox 56以前は、Firefoxの開発チームも既存のXULアドオンとの互換性をある程度意識していたそうですが、XULアドオンが廃止されて互換性維持の制約がなくなった現在のFirefoxでは、大胆な変更が度々行われており、追従コストは以前よりも大きくなっています。
WaterfoxはFirefox ESR<sup class="footnote-ref"><a href="#fn3" id="fnref3">3</a></sup>を元にしているため、更新の頻度は1年に1回程度で済みますし、ベースバージョンとなるFirefoxも単一のバージョンに限られるので、複数バージョンのFirefoxを想定した工夫が不要な分、XULアドオン開発よりも工数は小さくなりそうです。
それでも、毎回のメジャーアップデートへの追従では1人月程度の工数は発生しかねません。</p>
<p>会社の業務としては継続的な収入を見込める案件は歓迎ですが、この件が今回のみの単発作業にとどまった場合、以後の追従作業に支障が生じる恐れがあり、Waterfoxプロジェクトとしてはデメリットとなり得ます<sup class="footnote-ref"><a href="#fn4" id="fnref4">4</a></sup>。</p>
<h4 id="素案2:-tstをwaterfox向けに改造する">素案2: TSTをWaterfox向けに改造する</h4>
<p>すでにあるFirefox用アドオンのTSTを要件に合うように改造する場合は、<em>全体的に必要な工数が小さくなる</em>のが大きなメリットです。</p>
<p>前項での懸念の1つめの「ビルドの手間」は、アドオンであれば、変更の度にブラウザーのUI上で再読み込みするだけで済みます<sup class="footnote-ref"><a href="#fn5" id="fnref5">5</a></sup>。
また、Firefox本体の更新に対する今のアドオンの追従の容易さは、現在のTST自身がその証明となっています。</p>
<p> </p>
<p>この場合のデメリットの1つ目は、TSTが準拠しているアドオンの仕様である「Manifest V2」の将来性の不透明さです。
FirefoxではManifest V2は当面存続する旨が公表されていますが、将来に渡って安心して使える確約はありません。</p>
<p>この懸念を払拭するには、Manifest V3へ移行するほかありませんが、この時点で<em>今回の要件をManifest V3で満たせるかどうかは不明</em>でした。
というのも、Manifest V3ではTSTの重要コンポーネントの1つである、タブのツリー構造に矛盾が生じるのを防ぐための「タブの変更を常時監視する、永続的なバックグラウンドページ」を使用できない<sup class="footnote-ref"><a href="#fn6" id="fnref6">6</a></sup>からです。
このような制約があっても必要な動作を実現できるのかは、PoC<sup class="footnote-ref"><a href="#fn7" id="fnref7">7</a></sup>を用いての検証が必要です。</p>
<p> </p>
<p>デメリットの2つ目は、WebExtensions APIでは実現できない機能や、WebExtensions固有の制約があることです。
WebExtensions APIの機能的な制約により、TSTには「実装したいができない」機能がいくつかあります(詳しくは後述)。
今回の要件の中にも、ブラウザー本体の設定パネルへの機能追加など、WebExtensions APIでは実現できない機能が含まれています。
そのため、この選択肢においては<em>Experiments API</em>の使用が必須となります。</p>
<p>Experiments APIは、WebExtensions APIベースのアドオンにXULアドオンと同等の自由度をもたらす技術です。
リリース版のFirefoxでは機能が封印されていますが、Waterfoxでは機能が有効化されているため、「Waterfox専用のアドオン」の開発では使用を前提にできます。
前述したManifest V3での制約も、極端な話、「永続的なバックグラウンドページ」を代替する物をExperiments APIで実装する形で制限を回避できます。</p>
<p>ただ、Experiments APIの実装は前項同様に、ベースバージョンとなるFirefoxの変更の影響を強く受けます。
Experiments APIに依存する箇所が増えれば増えるほど、Firefoxの更新への追従コストは大きくなる<sup class="footnote-ref"><a href="#fn8" id="fnref8">8</a></sup>リスクがあります。</p>
<p>また、WebExtensionsアドオンが別プロセスで実行されることに由来する既知の不具合がある<sup class="footnote-ref"><a href="#fn9" id="fnref9">9</a></sup>、という問題もあります。
問題の深刻度次第では「Waterfoxにパッチを当てて問題を解消する」対処も有り得ますが、実際にどこまでできるかは予算のかけ方次第です。</p>
<p> </p>
<p>「パワーユーザー向けの機能として提供する」という前提からは、タブが多いときの性能問題も懸念されます。
本件開始時点でのTSTバージョン3.9.22では、数千個単位でタブを開いている複数のユーザーから、動作速度の遅さやメモリー消費量の多さが指摘されていました。
Waterfoxの想定ユーザー層を考慮すると、この点は無視できません。
性能面の懸念を払拭するためには、タブの数が多くなっても性能が劣化しにくい実装方式である<em>仮想スクロール</em>の導入も必須と考えられました。</p>
<p>Manifest V3への移行や仮想スクロールの導入は、TSTで前々から課題と認識していた点で、もしうまく実現できれば、成果をそのまま公開版のTSTにフィードバックできます。
先方は「TST開発プロジェクト自体への支援もしたい」とお考えだったので、この点は非機能要件としてのメリットと言えます。</p>
<h4 id="素案3:-waterfoxネイティブのサイドバーパネルとして実装する">素案3: Waterfoxネイティブのサイドバーパネルとして実装する</h4>
<p>これは、<em>WebExtensions APIの制約に囚われない自由な実装</em>を行いつつ、その実装を「Waterfox専用のサイドバーパネル」内に閉じることによりFirefox由来の部分との接続を弱めて、<em>将来のバージョンのFirefoxへの追従コストを小さくする</em>、という「いいとこ取り」を狙った案です。
技術的な障壁は特になく、最も高機能・高性能かつ長期的に有用な成果を得られると期待できるのがメリットです。</p>
<p>この選択肢のデメリットは、<em>開発規模が大きくなる</em>ことに尽きます。
WebExtensionsベースではないので、現行のTSTのコードはそのままでは流用できず、多くの部分をゼロから作る必要があります。
開発の成果を検証するためにWaterfoxのビルドが必要であるという点や、成果物がWaterfoxプロジェクトの負債になりかねない点も、素案1と同様です。</p>
<h4 id="提案とフィードバック、作業計画の具体化">提案とフィードバック、作業計画の具体化</h4>
<p>Waterfoxプロジェクトが何を優先するかによって、最適な選択肢は異なります。
そのため、この3つの素案を先方に提示し、意見を求めてみました。</p>
<p>そうしたところ、この中では素案2の方向性(WebExtensionsアドオンとして開発)が最も有力そうだとの回答を頂けたため、ここから先はその方向での詳細を詰めるべく、技術的な議論や情報のすり合わせを行いました。</p>
<p>その過程で、先方がすでに得ておられた技術的な知見を共有していただくこともでき、こちらで懸念していた点のいくつかについては、解消の筋道が立ってきました。
また、素案検討段階で未知数の部分が多かった「Manifest V3での実現可能性」についても調査を進め、PoCの設計方針が見えてきました。</p>
<p>ここまでの情報を踏まえて整理した作業内容と計画は、以下のようになりました。</p>
<ul>
<li>機能はWebExtensionsアドオンとして実装する。
<ul>
<li>開発するアドオンは、TSTの実装を一部流用する。</li>
</ul>
</li>
<li>開発は以下の2段階に分けて行う。
<ul>
<li>第1段階:現行のTSTと同等の基本機能の実装。
<ul>
<li>まずPoCを作成し、Manifest V3で素直に実装した場合の実用性を検証する。
<ul>
<li>PoCでの実証結果が芳しければ、そのままManifest V3で開発を進める。(この成果はTSTに還元する)</li>
<li>PoCでの実証結果が芳しくなければ、Manifest V2での開発(TSTの改造)に舵を切る。</li>
</ul>
</li>
<li>Manifest V2とManifest V3のどちらで実装するとしても、UIは仮想スクロールを用いて、ほぼ新規に開発する。</li>
</ul>
</li>
<li>第2段階:Waterfox固有の機能拡張部分の実装。
<ul>
<li>サムネイル、設定画面の組み込みなど、WebExtensions APIでは実現不可能な部分をExperiments APIで実装する。</li>
</ul>
</li>
</ul>
</li>
<li>これらの作業は2ヵ月程度の作業期間内で可能そうである。</li>
</ul>
<p>こうして、ある程度の精度での見積もりができたことで、晴れて本件を社の案件として受注できるとの判断が下り、筆者の担当業務として取り組める運びとなりました。
開発内容にはまだ未知数の部分があり、詳細な作業の内容は変動する恐れがあるため、契約は請負契約ではなく準委任契約とし、期待される開発の成果と開発の期間のみをお約束する形です。
以降は、契約の話を会社間で進めてもらう一方で、筆者は引き続き、先方との間で実装方針検討のための技術的な議論を進めていきました。</p>
<h4 id="方針の見直し">方針の見直し</h4>
<p>議論のさなか、先の素案2での「永続的なバックグラウンドページをExperiments APIで代替する」方向性の検討過程で、「そもそも、Manifest V3における制約を迂回してバックグラウンドページを永続化できれば、短命のバックグラウンドページへの設計変更もExperiments APIでの代替実装も必要ないのでは?」という疑問が浮上してきました。</p>
<p>そこで、この方向性について双方で調査を進めたところ、バックグラウンドページの永続化の可否は<a href="https://searchfox.org/mozilla-esr115/rev/1b386c499cf6292d66b28df018b53124009bbf7d/toolkit/components/extensions/parent/ext-backgroundPage.js#393-451">一定時間経過後にバックグラウンドページをアンロードする処理</a>の中で判定されており、現状でも、隠し設定の<code>extensions.background.idle.enabled</code>を<code>false</code>に設定すれば「Manifest V3でバックグラウンドページを永続化」できると分かりました。</p>
<p>この隠し設定は全てのアドオンに影響が及ぶため、正式リリースには反映しづらいのですが、「どこを変更すればバックグラウンドページを永続化できるか」が分かったことの意義は大きいです。
調査と検証の結果、Experiments APIでもこの箇所への介入はできないと分かりましたが、Waterfox側で小改修を行えば、今回開発するアドオンに限定してのバックグラウンドページの永続化は可能です。
また、将来のバージョンのFirefoxに追従する際にも、「バックグラウンドページのアンロード処理に介入する」改修自体はそれほど困難な物とはならないと予想できます。</p>
<p>以上の事を踏まえ、最終的な作業内容と計画を以下の通り変更・決定しました。</p>
<ul>
<li>機能はWebExtensionsアドオンとして実装する。(仮称「Waterfox Sidebar」)
<ul>
<li>開発するアドオンは、TSTの実装を一部流用する。</li>
</ul>
</li>
<li>アドオンの仕様はManifest V3とする。
<ul>
<li>ただし、バックグラウンドページは何らかの方法で永続化できる前提とする。
<ul>
<li>つまり、アドオンの全体的な設計はManifest V2からほぼ変えない。</li>
<li>バックグランド永続化の具体的な方法は、Waterfox側での特別な対応を想定する。</li>
</ul>
</li>
</ul>
</li>
<li>UIは仮想スクロールを用いて、ほぼ新規に開発する。</li>
<li>サムネイル、設定画面の組み込みなど、WebExtensions APIでは実現不可能な部分をExperiments APIで実装する。</li>
</ul>
<p>これを踏まえて、作業用のリポジトリーを先方の管理下に用意して頂き、ここからようやく実装作業に入ることになります。</p>
<h3 id="まとめ、次回予告">まとめ、次回予告</h3>
<p>以上、個人開発のオープンソースソフトウェアに寄せられた連絡を企業としての開発案件に繋げた事例の紹介の前編として、交渉の経緯と見積もり段階での検討内容をご紹介しました。</p>
<p>ここまでの流れは、クライアントがWaterfoxというオープンソース開発プロジェクト<sup class="footnote-ref"><a href="#fn10" id="fnref10">10</a></sup>である点を除けば、一般的な「ソフトウェアの受託開発」業務の話と言えます。
マネタイズに苦労するプロジェクトが多い中で、Firefoxの成果をベースにしているとはいえ、外部の開発者に依頼ができるくらいの資金力をWaterfoxプロジェクトが持てているのは、すごいことだと筆者には感じられます。</p>
<p>ところで、<a href="https://www.waterfox.net/blog/waterfox-x-treestyletab/">冒頭に上げたWaterfoxプロジェクトのブログ記事</a>や<a href="https://piro.sakura.ne.jp/latest/blosxom/mozilla/xul/2024-03-08_tst4.htm">筆者個人のブログ記事</a>をご覧になられた方はお気付きかと思いますが、実はこの件の最終的な着地点は「Waterfoxのサイドバー開発」というよりは、「オープンソース開発プロジェクトであるTSTとWaterfoxのコラボレーション」「TSTの開発をWaterfoxが支援」という見え方となっています。</p>
<p>さて、一体この後何があってそうなったのか。
後編では、計画に基づいて開発を始めて序盤で起こった方針の再検討と、「TSTプロジェクトの都合」と「Waterfoxプロジェクトの都合」をどのように両立させたかの工夫についてご紹介します。</p>
<section class="footnotes">
<ol>
<li id="fn1">
<p>Groonga、Fluentdなど。 <a href="#fnref1" class="footnote-backref">↩</a></p>
</li>
<li id="fn2">
<p>Fluentd開発・サポートなど。 <a href="#fnref2" class="footnote-backref">↩</a></p>
</li>
<li id="fn3">
<p>Extended Support Release。セキュリティアップデートが約1年間提供され続ける法人向けバージョン。 <a href="#fnref3" class="footnote-backref">↩</a></p>
</li>
<li id="fn4">
<p>メリットとデメリットを考えるときは「誰にとってなのか」が重要です。自分と相手のメリットとデメリットが一致しているときは何も悩まず済みますが、このように「自分達にとってはメリットになるが、相手にとってはデメリットになり得る」事柄は、判断が難しい所です。筆者は基本的には、「自分と相手の共同プロジェクトとして末永く運用していく上でどうか」という観点で整理するようにしています。 <a href="#fnref4" class="footnote-backref">↩</a></p>
</li>
<li id="fn5">
<p>web-extを使用すれば、ファイルの編集を検知しての自動再読み込みすら可能です。 <a href="#fnref5" class="footnote-backref">↩</a></p>
</li>
<li id="fn6">
<p>Manifest V3では、バックグラウンド動作は、何らかのイベントを契機に実行される短命なスクリプトのみ使用が許されています。 <a href="#fnref6" class="footnote-backref">↩</a></p>
</li>
<li id="fn7">
<p>Proof of Concept。概念実証のための実装。 <a href="#fnref7" class="footnote-backref">↩</a></p>
</li>
<li id="fn8">
<p>それでも、ブラウザーそのものの改造に比べれば追従コストは小さいのですが。 <a href="#fnref8" class="footnote-backref">↩</a></p>
</li>
<li id="fn9">
<p>具体的には、<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1595883" title="1595883 - Immediate drag-and-drop across the sidebar and Firefox UI produces a zombie drag session and breaks subsequent drag-and-drops on Windows">Bug 1595883</a>や<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1773886" title="1773886 - Intermittent wrong coordinates of dragend event">Bug 1773886</a>など、TSTにおいてFirefox本体側の修正を待っている問題がいくつかあります。 <a href="#fnref9" class="footnote-backref">↩</a></p>
</li>
<li id="fn10">
<p>正確には、その主催であるAlex氏が運営するBrowserWorks社。 <a href="#fnref10" class="footnote-backref">↩</a></p>
</li>
</ol>
</section>
-
UbuntuのAPTを使用して、.debパッケージでサードパーティリポジトリを登録しパッケージをインストールする仕組み
https://www.clear-code.com/blog/2024/3/7/blog-how-to-apt-install-3rd-packages.html
2024-03-07T00:00:00+09:00
最近、Groongaチームに加わると同時に普段使いのmacOSからUbuntuに乗り換え、まだ不慣れな思いをしている児玉です。
Groongaの開発環境を整えることになり、Groongaの最新版をソースコードからビルドするためにサードパーティのリポジトリからパッケージをインストールする場面に直面しました。パッケージの公式サイトを見ながら手順通りにインストールできたものの、その背後にある仕組みが理解できていない状態でした。この状態に少々モヤモヤし、気になっていた気持ちを社内でつぶやいたところ、Debian Developerの林先輩が.debパッケージでサードパーティリポジトリを登録しパッケージをインストールする仕組みを教えてくれました。今回は、その貴重な学びを共有します。
今回は、実際にインストールする必要があったApache Arrowのパッケージであるlibarrow-devを例にとって見ていきます。Apache Arrow公式のインストールページを見てみると下記のようなコマンドが書かれています。(今回の説明に必要な部分だけ抜粋しています。)
$ wget https://apache.jfrog.io/artifactory/arrow/$(lsb_release --id --short | tr 'A-Z' 'a-z')/apache-arrow-apt-source-latest-$(lsb_release --codename --short).deb
$ sudo apt install -y -V ./apache-arrow-apt-source-latest-$(lsb_release --codename --short).deb
$ sudo apt update
$ sudo apt install -y -V libarrow-dev
全体的な流れとしては下記のようになっていますが、具体的に何をおこなっているのか一つ一つ見ていきたいと思います。
サードパーティのAPTリポジトリ情報を取得する
取得したAPTリポジトリ情報を反映する
APTリポジトリにアクセスしパッケージ情報を更新をする
パッケージをインストール
サードパーティのAPTリポジトリ情報を取得する
最初に下記のコマンドでは、LinuxのディストリビューションIDとリリースコードネームを使用して、サードパーティリポジトリであるApache ArrowのAPTリポジトリの情報を含んだファイルをダウンロードしてきています。と言われても少なくとも私は分からなかったので一つずつ見ていきます。
$ wget https://apache.jfrog.io/artifactory/arrow/$(lsb_release --id --short | tr 'A-Z' 'a-z')/apache-arrow-apt-source-latest-$(lsb_release --codename --short).deb
まず、実際にどんなURLにリクエストを飛ばしているのかを確認します。
まずは、$(lsb_release --id --short | tr 'A-Z' 'a-z')の部分を確認していきましょう。
大きくlsb_release --id --shortとtr 'A-Z' 'a-z'の部分に分かれていて、前者のコマンドの結果をパイプで後者のコマンドに渡しています。
lsb_release --id --shortの結果をみてみると下記のようになります。
下記のコマンドは、lsb_releaseを使って現在利用中のLinuxディストリビューションの情報を取得しています。--idオプションを使ってディストリビューションのIDを取得し、合わせて--shortオプションを指定することで、ディストリビューション名を簡潔にUbuntuと取得できています。コマンドに関して詳しくは、man lsb_releaseを参照してください。
$ lsb_release --id
Distributor ID: Ubuntu
$ lsb_release --id --short
Ubuntu
次に、Ubuntuと取得できた結果をtr 'A-Z' 'a-z'に渡すことで文字列内の大文字の部分を小文字のアルファベットに変換しています。
よって、lsb_release --id --short | tr 'A-Z' 'a-z'の結果は、ubuntuになることがわかります。コマンドに関して詳しくはman trで確認してみてください。
$ lsb_release --id --short | tr 'A-Z' 'a-z'
ubuntu
更に、lsb_release --codename --shortの結果を見てみましょう。
こちらも先程みたlsb_releaseコマンドを利用しており、--codenameオプションでリリースコードネームを取得し、さらに--shortオプションを指定することで、リリースコードネーム名を簡潔にjammyと取得しています。
$ lsb_release --codename --short
jammy
なので最終的に利用されるリクエスト先URLは下記のようになります。
$ echo "https://apache.jfrog.io/artifactory/arrow/$(lsb_release --id --short | tr 'A-Z' 'a-z')/apache-arrow-apt-source-latest-$(lsb_release --codename --short).deb"
https://apache.jfrog.io/artifactory/arrow/ubuntu/apache-arrow-apt-source-latest-jammy.deb
よって、このコマンドは利用者のディストリビューションとコードネームにあった.debファイルをダウンロードしています。なお、つぎのセクションでふれますが、この.debファイルはサードパーティリポジトリであるApache ArrowのAPTリポジトリの情報を含んでいます。
$ wget https://apache.jfrog.io/artifactory/arrow/ubuntu/apache-arrow-apt-source-latest-jammy.deb
つぎは、ダウンロードしてきたAPTリポジトリの情報を含んだ.debファイルをどのように利用していくのかをみていきます。
取得したAPTリポジトリ情報を反映する
先ほどダウンロードした.debファイルをインストールします。これにより、サードパーティリポジトリであるApache ArrowのAPTリポジトリの情報がシステムのリポジトリリストに反映されます。と言われてもここでも私は分からなかったので一つずつ見ていきます。
$ sudo apt install -y -V ./apache-arrow-apt-source-latest-$(lsb_release --codename --short).deb
実際にどんな情報がインストールし反映されたのかを確認してみます。次の2点が特に重要です。1点目は、apache-arrow.sourcesというファイルに記載されているリポジトリ情報です。このファイルは/etc/apt/sources.list.dに置かれています。2点目は、公開鍵apache-arrow-apt-source.gpgで、これは/usr/share/keyrings/に配置されています。
$ dpkg -L apache-arrow-apt-source
/.
/etc
/etc/apt
/etc/apt/sources.list.d
/etc/apt/sources.list.d/apache-arrow.sources
/usr
/usr/share
/usr/share/doc
/usr/share/doc/apache-arrow-apt-source
/usr/share/doc/apache-arrow-apt-source/changelog.Debian.gz
/usr/share/doc/apache-arrow-apt-source/copyright
/usr/share/keyrings
/usr/share/keyrings/apache-arrow-apt-source.gpg
実際にapache-arrow.sourcesファイルの中身をみてみましょう。ファイルの中身を見ていくと下記のような内容になっています。
Typesには、取得可能なパッケージのタイプが指定されています
URIsには、サードパーティリポジトリがホストしているURLが指定されています
パッケージを取得する際は、https://apache.jfrog.io/artifactory/arrow/ubuntu/にリクエストすることを示しています
Suitesには、リポジトリが対象とするUbuntuのリリースバージョンのコードネームが指定されています
Componentsには、パッケージのカテゴリーが指定されています
mainが指定されているので、このパッケージがフリーソフトウェアであることを示しています
コンポーネントに関して詳しくは、こちらを参照してください
Signed-Byには、インストール時にパッケージが正当なものかを確認するのに利用される公開鍵のパスが指定されています
公開鍵であるapache-arrow-apt-source.gpgをパッケージの正当性を確認する際に利用することを示しています
$ cat /etc/apt/sources.list.d/apache-arrow.sources
Types: deb deb-src
URIs: https://apache.jfrog.io/artifactory/arrow/ubuntu/
Suites: jammy
Components: main
Signed-By: /usr/share/keyrings/apache-arrow-apt-source.gpg
ここまでで、サードパーティリポジトリであるApache ArrowのAPTリポジトリのどんな情報が反映されたのかが分かったと同時にパッケージをインストールしたいサードパーティリポジトリ情報も分かりました。
次は、その情報を利用して実際にサードパーティリポジトリが提供しているパッケージの情報を取得するのを見ていきます。
APTリポジトリにアクセスしパッケージ情報を更新をする
おなじみのapt updateコマンドでパッケージ情報を更新してみると、ログから先程取得したサードパーティリポジトリであるApache ArrowのAPTリポジトリにパッケージ情報を取得して無事に情報を取得できていることがわかります。
$ sudo apt update
...
Hit:8 http://jp.archive.ubuntu.com/ubuntu jammy-backports InRelease
Get:9 http://jp.archive.ubuntu.com/ubuntu jammy-updates/main amd64 Packages [1,421 kB]
Get:10 http://jp.archive.ubuntu.com/ubuntu jammy-updates/main i386 Packages [579 kB]
Get:11 https://apache.jfrog.io/artifactory/arrow/ubuntu jammy InRelease [7,291 B]
Get:12 https://apache.jfrog.io/artifactory/arrow/ubuntu jammy/main Sources [19.0 kB]
Get:13 https://apache.jfrog.io/artifactory/arrow/ubuntu jammy/main amd64 Packages [112 kB]
Fetched 2,422 kB in 7s (350 kB/s)
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
2 packages can be upgraded. Run 'apt list --upgradable' to see them.
試しに、今回インストールしたいlibarrow-devの情報が更新され取得されているのかを確認してみると無事にパッケージ情報を取得できています。
$ sudo apt show libarrow-dev
Package: libarrow-dev
...
せっかくなので、もう少しく詳しくみてみましょう。特にAPT-Sourcesに注目してみると、apache-arrow.sourcesファイル内でリクエスト先であったhttps://apache.jfrog.io/artifactory/arrow/ubuntuが指定されていることがわかります。事前に取得していたサードパーティリポジトリであるApache ArrowのAPTリポジトリのURLと合致しているのがわかります。
$ sudo apt show libarrow-dev
Package: libarrow-dev
Version: 15.0.0-1
Priority: optional
Section: libdevel
Source: apache-arrow
Maintainer: Apache Arrow Developers <dev@arrow.apache.org>
Installed-Size: 116 MB
Depends: libarrow1500 (= 15.0.0-1), libbrotli-dev, libbz2-dev, libcurl4-openssl-dev, liblz4-dev, libc-ares-dev, libre2-dev, libsnappy-dev, libssl-dev, libutf8proc-dev, libzstd-dev, nlohmann-json-dev | nlohmann-json3-dev, protobuf-compiler-grpc, zlib1g-dev
Homepage: https://arrow.apache.org/
Download-Size: 14.0 MB
APT-Sources: https://apache.jfrog.io/artifactory/arrow/ubuntu jammy/main amd64 Packages
Description: Apache Arrow is a data processing library for analysis
.
This package provides C++ header files.
では、最後に目的であったlibarrow-devをインストールしていきます。
パッケージをインストール
無事にlibarrow-devがインストールされていることが確認できました。
$ sudo apt install libarrow-dev
$ sudo apt list --installed libarrow-dev
Listing... Done
libarrow-dev/jammy,now 15.0.0-1 amd64 [installed]
まとめ
最後にあらためて全体の流れをおさらいすると下記のようになります。特に公式のUbuntuリポジトリからパッケージをダウンロードする時との差異は、サードバーティリポジトリの情報を取得し反映するというステップが必要になる部分です。
(追加で必要)サードパーティのAPTリポジトリ情報を取得する
(追加で必要)取得したAPTリポジトリ情報を反映する
(追加で必要)APTリポジトリにアクセスしパッケージ情報を更新をする
パッケージをインストール
おわりに
今回は、.debパッケージを利用してUbuntuのAPTでサードパーティのリポジトリからパッケージをインストールする仕組みに関して紹介しました。
他にも、PPA(Personal Package Archives) を利用してサードパーテイリポジトリを追加してパッケージをインストールする方法などもあるようですが、今回はここまでの紹介にします。
<p>最近、<a href="https://groonga.org/ja/">Groonga</a>チームに加わると同時に普段使いのmacOSからUbuntuに乗り換え、まだ不慣れな思いをしている児玉です。</p>
<p>Groongaの開発環境を整えることになり、Groongaの最新版をソースコードからビルドするためにサードパーティのリポジトリからパッケージをインストールする場面に直面しました。パッケージの公式サイトを見ながら手順通りにインストールできたものの、その背後にある仕組みが理解できていない状態でした。この状態に少々モヤモヤし、気になっていた気持ちを社内でつぶやいたところ、<a href="/blog/2020/9/28.html">Debian Developerの林先輩</a>が.debパッケージでサードパーティリポジトリを登録しパッケージをインストールする仕組みを教えてくれました。今回は、その貴重な学びを共有します。</p>
<!--more-->
<p>今回は、実際にインストールする必要があったApache Arrowのパッケージである<code>libarrow-dev</code>を例にとって見ていきます。<a href="https://arrow.apache.org/install/">Apache Arrow</a>公式のインストールページを見てみると下記のようなコマンドが書かれています。(今回の説明に必要な部分だけ抜粋しています。)</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">$</span><span class="w"> </span>wget https://apache.jfrog.io/artifactory/arrow/<span class="si">$(</span>lsb_release <span class="nt">--id</span> <span class="nt">--short</span> | <span class="nb">tr</span> <span class="s1">'A-Z'</span> <span class="s1">'a-z'</span><span class="si">)</span>/apache-arrow-apt-source-latest-<span class="si">$(</span>lsb_release <span class="nt">--codename</span> <span class="nt">--short</span><span class="si">)</span>.deb
<span class="gp">$</span><span class="w"> </span><span class="nb">sudo </span>apt <span class="nb">install</span> <span class="nt">-y</span> <span class="nt">-V</span> ./apache-arrow-apt-source-latest-<span class="si">$(</span>lsb_release <span class="nt">--codename</span> <span class="nt">--short</span><span class="si">)</span>.deb
<span class="gp">$</span><span class="w"> </span><span class="nb">sudo </span>apt update
<span class="gp">$</span><span class="w"> </span><span class="nb">sudo </span>apt <span class="nb">install</span> <span class="nt">-y</span> <span class="nt">-V</span> libarrow-dev
</code></pre></div></div>
<p>全体的な流れとしては下記のようになっていますが、具体的に何をおこなっているのか一つ一つ見ていきたいと思います。</p>
<ul>
<li>サードパーティのAPTリポジトリ情報を取得する</li>
<li>取得したAPTリポジトリ情報を反映する</li>
<li>APTリポジトリにアクセスしパッケージ情報を更新をする</li>
<li>パッケージをインストール</li>
</ul>
<h2 id="サードパーティのaptリポジトリ情報を取得する">サードパーティのAPTリポジトリ情報を取得する</h2>
<p>最初に下記のコマンドでは、LinuxのディストリビューションIDとリリースコードネームを使用して、サードパーティリポジトリであるApache ArrowのAPTリポジトリの情報を含んだファイルをダウンロードしてきています。と言われても少なくとも私は分からなかったので一つずつ見ていきます。</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">$</span><span class="w"> </span>wget https://apache.jfrog.io/artifactory/arrow/<span class="si">$(</span>lsb_release <span class="nt">--id</span> <span class="nt">--short</span> | <span class="nb">tr</span> <span class="s1">'A-Z'</span> <span class="s1">'a-z'</span><span class="si">)</span>/apache-arrow-apt-source-latest-<span class="si">$(</span>lsb_release <span class="nt">--codename</span> <span class="nt">--short</span><span class="si">)</span>.deb
</code></pre></div></div>
<p>まず、実際にどんなURLにリクエストを飛ばしているのかを確認します。
まずは、<code>$(lsb_release --id --short | tr 'A-Z' 'a-z')</code>の部分を確認していきましょう。
大きく<code>lsb_release --id --short</code>と<code>tr 'A-Z' 'a-z'</code>の部分に分かれていて、前者のコマンドの結果をパイプで後者のコマンドに渡しています。</p>
<p><code>lsb_release --id --short</code>の結果をみてみると下記のようになります。
下記のコマンドは、<code>lsb_release</code>を使って現在利用中のLinuxディストリビューションの情報を取得しています。<code>--id</code>オプションを使ってディストリビューションのIDを取得し、合わせて<code>--short</code>オプションを指定することで、ディストリビューション名を簡潔に<code>Ubuntu</code>と取得できています。コマンドに関して詳しくは、<code>man lsb_release</code>を参照してください。</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">$</span><span class="w"> </span>lsb_release <span class="nt">--id</span>
<span class="go">Distributor ID: Ubuntu
</span><span class="gp">$</span><span class="w"> </span>lsb_release <span class="nt">--id</span> <span class="nt">--short</span>
<span class="go">Ubuntu
</span></code></pre></div></div>
<p>次に、<code>Ubuntu</code>と取得できた結果を<code>tr 'A-Z' 'a-z'</code>に渡すことで文字列内の大文字の部分を小文字のアルファベットに変換しています。
よって、<code>lsb_release --id --short | tr 'A-Z' 'a-z'</code>の結果は、<code>ubuntu</code>になることがわかります。コマンドに関して詳しくは<code>man tr</code>で確認してみてください。</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">$</span><span class="w"> </span>lsb_release <span class="nt">--id</span> <span class="nt">--short</span> | <span class="nb">tr</span> <span class="s1">'A-Z'</span> <span class="s1">'a-z'</span>
<span class="go">ubuntu
</span></code></pre></div></div>
<p>更に、<code>lsb_release --codename --short</code>の結果を見てみましょう。
こちらも先程みた<code>lsb_release</code>コマンドを利用しており、<code>--codename</code>オプションでリリースコードネームを取得し、さらに<code>--short</code>オプションを指定することで、リリースコードネーム名を簡潔に<code>jammy</code>と取得しています。</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">$</span><span class="w"> </span>lsb_release <span class="nt">--codename</span> <span class="nt">--short</span>
<span class="go">jammy
</span></code></pre></div></div>
<p>なので最終的に利用されるリクエスト先URLは下記のようになります。</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">$</span><span class="w"> </span><span class="nb">echo</span> <span class="s2">"https://apache.jfrog.io/artifactory/arrow/</span><span class="si">$(</span>lsb_release <span class="nt">--id</span> <span class="nt">--short</span> | <span class="nb">tr</span> <span class="s1">'A-Z'</span> <span class="s1">'a-z'</span><span class="si">)</span><span class="s2">/apache-arrow-apt-source-latest-</span><span class="si">$(</span>lsb_release <span class="nt">--codename</span> <span class="nt">--short</span><span class="si">)</span><span class="s2">.deb"</span>
<span class="go">https://apache.jfrog.io/artifactory/arrow/ubuntu/apache-arrow-apt-source-latest-jammy.deb
</span></code></pre></div></div>
<p>よって、このコマンドは利用者のディストリビューションとコードネームにあった.debファイルをダウンロードしています。なお、つぎのセクションでふれますが、この.debファイルはサードパーティリポジトリであるApache ArrowのAPTリポジトリの情報を含んでいます。</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">$</span><span class="w"> </span>wget https://apache.jfrog.io/artifactory/arrow/ubuntu/apache-arrow-apt-source-latest-jammy.deb
</code></pre></div></div>
<p>つぎは、ダウンロードしてきたAPTリポジトリの情報を含んだ.debファイルをどのように利用していくのかをみていきます。</p>
<h2 id="取得したaptリポジトリ情報を反映する">取得したAPTリポジトリ情報を反映する</h2>
<p>先ほどダウンロードした.debファイルをインストールします。これにより、サードパーティリポジトリであるApache ArrowのAPTリポジトリの情報がシステムのリポジトリリストに反映されます。と言われてもここでも私は分からなかったので一つずつ見ていきます。</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">$</span><span class="w"> </span><span class="nb">sudo </span>apt <span class="nb">install</span> <span class="nt">-y</span> <span class="nt">-V</span> ./apache-arrow-apt-source-latest-<span class="si">$(</span>lsb_release <span class="nt">--codename</span> <span class="nt">--short</span><span class="si">)</span>.deb
</code></pre></div></div>
<p>実際にどんな情報がインストールし反映されたのかを確認してみます。次の2点が特に重要です。1点目は、<code>apache-arrow.sources</code>というファイルに記載されているリポジトリ情報です。このファイルは<code>/etc/apt/sources.list.d</code>に置かれています。2点目は、公開鍵<code>apache-arrow-apt-source.gpg</code>で、これは<code>/usr/share/keyrings/</code>に配置されています。</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">$</span><span class="w"> </span>dpkg <span class="nt">-L</span> apache-arrow-apt-source
<span class="go">/.
/etc
/etc/apt
/etc/apt/sources.list.d
/etc/apt/sources.list.d/apache-arrow.sources
/usr
/usr/share
/usr/share/doc
/usr/share/doc/apache-arrow-apt-source
/usr/share/doc/apache-arrow-apt-source/changelog.Debian.gz
/usr/share/doc/apache-arrow-apt-source/copyright
/usr/share/keyrings
/usr/share/keyrings/apache-arrow-apt-source.gpg
</span></code></pre></div></div>
<p>実際に<code>apache-arrow.sources</code>ファイルの中身をみてみましょう。ファイルの中身を見ていくと下記のような内容になっています。</p>
<ul>
<li>Typesには、取得可能なパッケージのタイプが指定されています</li>
<li>URIsには、サードパーティリポジトリがホストしているURLが指定されています
<ul>
<li>パッケージを取得する際は、<code>https://apache.jfrog.io/artifactory/arrow/ubuntu/</code>にリクエストすることを示しています</li>
</ul>
</li>
<li>Suitesには、リポジトリが対象とするUbuntuのリリースバージョンのコードネームが指定されています</li>
<li>Componentsには、パッケージのカテゴリーが指定されています
<ul>
<li><code>main</code>が指定されているので、このパッケージがフリーソフトウェアであることを示しています</li>
<li>コンポーネントに関して詳しくは、<a href="https://help.ubuntu.com/community/Repositories/">こちら</a>を参照してください</li>
</ul>
</li>
<li>Signed-Byには、インストール時にパッケージが正当なものかを確認するのに利用される公開鍵のパスが指定されています
<ul>
<li>公開鍵である<code>apache-arrow-apt-source.gpg</code>をパッケージの正当性を確認する際に利用することを示しています</li>
</ul>
</li>
</ul>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">$</span><span class="w"> </span><span class="nb">cat</span> /etc/apt/sources.list.d/apache-arrow.sources
<span class="go">Types: deb deb-src
URIs: https://apache.jfrog.io/artifactory/arrow/ubuntu/
Suites: jammy
Components: main
Signed-By: /usr/share/keyrings/apache-arrow-apt-source.gpg
</span></code></pre></div></div>
<p>ここまでで、サードパーティリポジトリであるApache ArrowのAPTリポジトリのどんな情報が反映されたのかが分かったと同時にパッケージをインストールしたいサードパーティリポジトリ情報も分かりました。
次は、その情報を利用して実際にサードパーティリポジトリが提供しているパッケージの情報を取得するのを見ていきます。</p>
<h2 id="aptリポジトリにアクセスしパッケージ情報を更新をする">APTリポジトリにアクセスしパッケージ情報を更新をする</h2>
<p>おなじみの<code>apt update</code>コマンドでパッケージ情報を更新してみると、ログから先程取得したサードパーティリポジトリであるApache ArrowのAPTリポジトリにパッケージ情報を取得して無事に情報を取得できていることがわかります。</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">$</span><span class="w"> </span><span class="nb">sudo </span>apt update
<span class="c">...
</span><span class="go">Hit:8 http://jp.archive.ubuntu.com/ubuntu jammy-backports InRelease
Get:9 http://jp.archive.ubuntu.com/ubuntu jammy-updates/main amd64 Packages [1,421 kB]
Get:10 http://jp.archive.ubuntu.com/ubuntu jammy-updates/main i386 Packages [579 kB]
Get:11 https://apache.jfrog.io/artifactory/arrow/ubuntu jammy InRelease [7,291 B]
Get:12 https://apache.jfrog.io/artifactory/arrow/ubuntu jammy/main Sources [19.0 kB]
Get:13 https://apache.jfrog.io/artifactory/arrow/ubuntu jammy/main amd64 Packages [112 kB]
Fetched 2,422 kB in 7s (350 kB/s)
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
2 packages can be upgraded. Run 'apt list --upgradable' to see them.
</span></code></pre></div></div>
<p>試しに、今回インストールしたい<code>libarrow-dev</code>の情報が更新され取得されているのかを確認してみると無事にパッケージ情報を取得できています。</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">$</span><span class="w"> </span><span class="nb">sudo </span>apt show libarrow-dev
<span class="go">Package: libarrow-dev
</span><span class="c">...
</span></code></pre></div></div>
<p>せっかくなので、もう少しく詳しくみてみましょう。特に<code>APT-Sources</code>に注目してみると、<code>apache-arrow.sources</code>ファイル内でリクエスト先であった<code>https://apache.jfrog.io/artifactory/arrow/ubuntu</code>が指定されていることがわかります。事前に取得していたサードパーティリポジトリであるApache ArrowのAPTリポジトリのURLと合致しているのがわかります。</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">$</span><span class="w"> </span><span class="nb">sudo </span>apt show libarrow-dev
<span class="go">Package: libarrow-dev
Version: 15.0.0-1
Priority: optional
Section: libdevel
Source: apache-arrow
</span><span class="gp">Maintainer: Apache Arrow Developers <dev@arrow.apache.org></span><span class="w">
</span><span class="go">Installed-Size: 116 MB
Depends: libarrow1500 (= 15.0.0-1), libbrotli-dev, libbz2-dev, libcurl4-openssl-dev, liblz4-dev, libc-ares-dev, libre2-dev, libsnappy-dev, libssl-dev, libutf8proc-dev, libzstd-dev, nlohmann-json-dev | nlohmann-json3-dev, protobuf-compiler-grpc, zlib1g-dev
Homepage: https://arrow.apache.org/
Download-Size: 14.0 MB
APT-Sources: https://apache.jfrog.io/artifactory/arrow/ubuntu jammy/main amd64 Packages
Description: Apache Arrow is a data processing library for analysis
</span><span class="c"> .
</span><span class="go"> This package provides C++ header files.
</span></code></pre></div></div>
<p>では、最後に目的であった<code>libarrow-dev</code>をインストールしていきます。</p>
<h2 id="パッケージをインストール">パッケージをインストール</h2>
<p>無事に<code>libarrow-dev</code>がインストールされていることが確認できました。</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">$</span><span class="w"> </span><span class="nb">sudo </span>apt <span class="nb">install </span>libarrow-dev
<span class="gp">$</span><span class="w"> </span><span class="nb">sudo </span>apt list <span class="nt">--installed</span> libarrow-dev
<span class="go">Listing... Done
libarrow-dev/jammy,now 15.0.0-1 amd64 [installed]
</span></code></pre></div></div>
<h2 id="まとめ">まとめ</h2>
<p>最後にあらためて全体の流れをおさらいすると下記のようになります。特に公式のUbuntuリポジトリからパッケージをダウンロードする時との差異は、サードバーティリポジトリの情報を取得し反映するというステップが必要になる部分です。</p>
<ul>
<li>(追加で必要)サードパーティのAPTリポジトリ情報を取得する</li>
<li>(追加で必要)取得したAPTリポジトリ情報を反映する</li>
<li>(追加で必要)APTリポジトリにアクセスしパッケージ情報を更新をする</li>
<li>パッケージをインストール</li>
</ul>
<h2 id="おわりに">おわりに</h2>
<p>今回は、.debパッケージを利用してUbuntuのAPTでサードパーティのリポジトリからパッケージをインストールする仕組みに関して紹介しました。
他にも、PPA(Personal Package Archives) を利用してサードパーテイリポジトリを追加してパッケージをインストールする方法などもあるようですが、今回はここまでの紹介にします。</p>
-
Fluentd Update - Fluentdとパッケージの最新動向についてOSC 2024 Online/Springで発表しました
https://www.clear-code.com/blog/2024/3/5/osc-2024-spring-fluentd.html
2024-03-05T00:00:00+09:00
林です。
2024年3月1日・2日に開催されたOpen Source Conference 2024 Online/Springにおいて、「Fluentd Update - Fluentdとパッケージの最新動向について」と題した発表を林・福田の両名で行いました。
1日目のC会場にて実施した発表内容を紹介します。
発表資料について
Fluentd Update – Fluentdとパッケージの最新動向について
スライド(Rabbit Slide Show)
リポジトリー
発表内容について
Fluentdプロジェクトのメンバーとして、以前OSC 2022にて次のような発表を行いました。
OSSを継続的にメンテナンスしていく仕組みづくり – Fluentdの事例とその最新情報についてOSC 2022 Online Springで発表しました
それから2年になるので、Fluentdに関連する最新情報を日本語で提供すべく、登壇しました。
発表においては、次の4つのトピックを話しました。
Fluentdとは
OSC2022 Online/Spring以降のできごと
Fluent Package LTSとは
Fluentd開発の最新トピック
昨年td-agentの後継となるFluent PackageのLTS版の提供をはじめたのが大きな変化です。
また、直近でリリースする予定のFluent Packageの新バージョンに盛り込む変更点を先行して紹介しました。
当日の発表の内容はYoutubeのOSPN.jpチャンネルのアーカイブで視聴できるようになっています。
さいごに
「Fluentd Update - Fluentdとパッケージの最新動向について」でもお話しましたが、継続的にLTS版をリリースしたり、より活発なコミュニティーサポートにつなげるのが今後の課題としてあります。
OSCでは、あくまでコミュニティーとしての活動内容の話をすることが目的なので言及していませんでしたが、クリアコードでは、Fluentdのサポートサービスを提供しています。サポートサービスでは障害発生時の調査や回避策の提案、パッチの提供などを行います。
また、既存のtd-agent v4からFluent Packageへのアップグレードの支援もサポートサービスの一環として行っています。
Fluentdに関するトラブルを抱えて困っている方は、ぜひこちらのお問い合わせフォームからご連絡ください。
<p>林です。</p>
<p>2024年3月1日・2日に開催された<a href="https://event.ospn.jp/osc2024-online-spring/">Open Source Conference 2024 Online/Spring</a>において、「Fluentd Update - Fluentdとパッケージの最新動向について」と題した発表を林・福田の両名で行いました。
1日目のC会場にて実施した発表内容を紹介します。</p>
<!--more-->
<h2 id="発表資料について">発表資料について</h2>
<div class="rabbit-slide">
<iframe src="https://slide.rabbit-shocker.org/authors/kenhys/osc2024-online-spring-fluentd/viewer.html"
width="640" height="404"
frameborder="0"
marginwidth="0"
marginheight="0"
scrolling="no"
style="border: 1px solid #ccc; border-width: 1px 1px 0; box-sizing: content-box; margin-bottom: 5px"
allowfullscreen> </iframe>
<div style="margin-bottom: 5px">
<a href="https://slide.rabbit-shocker.org/authors/kenhys/osc2024-online-spring-fluentd/" title="Fluentd Update – Fluentdとパッケージの最新動向について">Fluentd Update – Fluentdとパッケージの最新動向について</a>
</div>
</div>
<ul>
<li><a href="https://slide.rabbit-shocker.org/authors/kenhys/osc2024-online-spring-fluentd/">スライド(Rabbit Slide Show)</a></li>
<li><a href="https://gitlab.com/clear-code/fluentd-support-common/-/tree/main/open-source-conference-2024-online-spring?ref_type=heads">リポジトリー</a></li>
</ul>
<h2 id="発表内容について">発表内容について</h2>
<p>Fluentdプロジェクトのメンバーとして、以前OSC 2022にて次のような発表を行いました。</p>
<ul>
<li><a href="/blog/2022/3/12/osc-2022-spring-fluentd.html">OSSを継続的にメンテナンスしていく仕組みづくり – Fluentdの事例とその最新情報についてOSC 2022 Online Springで発表しました</a></li>
</ul>
<p>それから2年になるので、Fluentdに関連する最新情報を日本語で提供すべく、登壇しました。
発表においては、次の4つのトピックを話しました。</p>
<ul>
<li>Fluentdとは</li>
<li>OSC2022 Online/Spring以降のできごと</li>
<li>Fluent Package LTSとは</li>
<li>Fluentd開発の最新トピック</li>
</ul>
<p>昨年td-agentの後継となるFluent PackageのLTS版の提供をはじめたのが大きな変化です。
また、直近でリリースする予定のFluent Packageの新バージョンに盛り込む変更点を先行して紹介しました。</p>
<p>当日の発表の内容はYoutubeのOSPN.jpチャンネルのアーカイブで視聴できるようになっています。</p>
<iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/T0TZ47HLQ1k?si=wqoLWKdJf0B0Gnja" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen>
</iframe>
<h2 id="さいごに">さいごに</h2>
<p>「Fluentd Update - Fluentdとパッケージの最新動向について」でもお話しましたが、継続的にLTS版をリリースしたり、より活発なコミュニティーサポートにつなげるのが今後の課題としてあります。</p>
<p>OSCでは、あくまでコミュニティーとしての活動内容の話をすることが目的なので言及していませんでしたが、クリアコードでは、<a href="/services/fluentd.html">Fluentdのサポートサービス</a>を提供しています。サポートサービスでは障害発生時の調査や回避策の提案、パッチの提供などを行います。
また、既存のtd-agent v4からFluent Packageへのアップグレードの支援もサポートサービスの一環として行っています。
Fluentdに関するトラブルを抱えて困っている方は、ぜひこちらの<a href="/contact/">お問い合わせフォーム</a>からご連絡ください。</p>
-
fluent-package不具合情報 - Windows版で環境によってはFluentdワーカープロセスの起動ができないことがある
https://www.clear-code.com/blog/2024/2/26/fluent-package-windows-crash-on-launch.html
2024-02-26T00:00:00+09:00
クリアコードはFluentdを利用する法人様に向けてFluentdのサポートサービスを提供しています。そのサービス内容の一つとして、Fluentdで何らかの不具合があってお客様に重大な影響が懸念される場合に、(契約形態にも依りますが)プッシュ型で情報をお知らせするサービスも提供しています。
今回、fluent-packageやtd-agentが環境によってはワーカープロセスを起動できない問題があることが発覚しましたので、その情報をククログでも紹介します。
本件の概要
fluent-packageやtd-agentのWindows版において、レジストリHKLM(HKEY_LOCAL_MACHINE)やHKCU(HKEY_CURRENT_USER)のSOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\以下に非ASCII文字を含むサブキーが存在すると、Fluentdのワーカープロセスが起動できないという問題があることが発覚しました。
上記のレジストリはWindowsのプログラムの追加と削除機能のために、各アプリケーションが情報を書き込む場所です。通常、各アプリケーションは上記レジストリキー直下にGUIDをサブキーとして書き込みますが、中には漢字等を含んだサブキーを作成するアプリケーションが存在します。
上記のようなレジストリキーが存在する場合でもFluentdの動作に影響を与えないというのが期待される動作ですが、fluent-packageやtd-agentが同梱するRubyInstallerの問題により、特定バージョンのfluent-packageやtd-agentではプロセスを起動できないという問題が発生します。
影響範囲
td-agent v4.5.0 以降、fluent-package v5.0.2以前のバージョンがこの影響を受けます。
前述のような問題のあるアプリケーションがインストールされている場合にのみ影響を受けます。
以下レジストリキーのサブキーとして非ASCII文字を含むものがあるかどうかを確認することで、影響を受けるかどうかを判断できます
\HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\
\HKEY_CURRENT_USER\WOW6432Node\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\
\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\
\HKEY_LOCAL_MACHINE\WOW6432Node\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\
問題なくFluentdを起動できている場合は、必ずしも対処する必要はありません。
ただし、Fluentd起動後に追加で上記のような問題のあるアプリケーションをインストールした場合は、次回Fluentd再起動時に影響を受けます。
影響
この問題に該当する環境では、以下のようなエラーが発生してFluentdのワーカープロセスの起動に失敗し、ログ収集が行われません。
Traceback (most recent call last):
17: from <internal:gem_prelude>:1:in `<internal:gem_prelude>'
16: from <internal:gem_prelude>:1:in `require'
15: from C:/opt/td-agent/lib/ruby/2.7.0/rubygems.rb:1427:in `<top (required)>'
14: from C:/opt/td-agent/lib/ruby/2.7.0/rubygems.rb:1427:in `require'
13: from C:/opt/td-agent/lib/ruby/2.7.0/rubygems/defaults/operating_system.rb:24:in `<top (required)>'
12: from C:/opt/td-agent/lib/ruby/site_ruby/2.7.0/ruby_installer/runtime/singleton.rb:27:in `enable_dll_search_paths'
11: from C:/opt/td-agent/lib/ruby/site_ruby/2.7.0/ruby_installer/runtime/msys2_installation.rb:125:in `enable_dll_search_paths'
10: from C:/opt/td-agent/lib/ruby/site_ruby/2.7.0/ruby_installer/runtime/msys2_installation.rb:115:in `mingw_bin_path'
9: from C:/opt/td-agent/lib/ruby/site_ruby/2.7.0/ruby_installer/runtime/msys2_installation.rb:102:in `msys_path'
8: from C:/opt/td-agent/lib/ruby/site_ruby/2.7.0/ruby_installer/runtime/msys2_installation.rb:68:in `iterate_msys_paths'
7: from C:/opt/td-agent/lib/ruby/site_ruby/2.7.0/ruby_installer/runtime/msys2_installation.rb:68:in `each'
6: from C:/opt/td-agent/lib/ruby/site_ruby/2.7.0/ruby_installer/runtime/msys2_installation.rb:70:in `block in iterate_msys_paths'
5: from C:/opt/td-agent/lib/ruby/2.7.0/win32/registry.rb:542:in `open'
4: from C:/opt/td-agent/lib/ruby/2.7.0/win32/registry.rb:435:in `open'
3: from C:/opt/td-agent/lib/ruby/site_ruby/2.7.0/ruby_installer/runtime/msys2_installation.rb:71:in `block (2 levels) in iterate_msys_paths'
2: from C:/opt/td-agent/lib/ruby/2.7.0/win32/registry.rb:611:in `each_key'
1: from C:/opt/td-agent/lib/ruby/2.7.0/win32/registry.rb:910:in `export_string'
C:/opt/td-agent/lib/ruby/2.7.0/win32/registry.rb:910:in `encode': U+767E to ASCII-8BIT in conversion from UTF-16LE to UTF-8 to ASCII-8BIT (Encoding::UndefinedConversionError)
2023-06-14 16:40:41 +0800 [error]: Worker 0 exited unexpectedly with status 1
対応方法
影響を受ける場合は、以下の方法でこの問題を回避することが可能です
td-agentの場合: C:\opt\td-agent\msys64\usr\bin\msys-2.0.dllというパスで空のファイルを作成する
fluent-packageの場合: C:\opt\fluent\msys64\usr\bin\msys-2.0.dllというパスで空のファイルを作成する
上記ファイル作成後、自動で復旧する場合もありますが、念のためfluentdwinsvcサービスを再起動することをおすすめします。
fluent-package v5.0.3 にてこの問題を暫定対処予定です。リリース時期については調整中です。
RubyInstallerのアップストリームにも問題を報告し、抜本的な対応方法を検討しています。
原因詳細
Fluentdが使用するRubyは、起動時にDLLのサーチパスをリストアップします。
この際、MSYS2がインストールされているパスをDLLサーチパスの一つとして追加しようとします。
いくつか固定のデフォルトパスを検索してMYS2がインストールされていない場合は、上記のレジストリ値を参照してMSYS2がインストールされているかを確認します。
Fluentdのワーカープロセスは、サービスとして起動する場合、Rubyの内部エンコーディングをASCII-8BIT(コマンドラインオプション-Eacsii-8bit:ascii-8bit)として起動しようとするため、レジストリキーに非ASCII文字が含まれる場合、変換に失敗しクラッシュします。
サービスではなくコマンドラインで起動する場合、通常はUTF-8(コマンドラインオプション-Eutf8)で起動しようとするため、この問題の影響を受けません。
C:\opt\fluent\msys64\usr\bin\msys-2.0.dllは上記で説明している固定のデフォルトパスの一つであるため、このファイルが存在する場合は、本不具合を発生させているコードの実行をスキップさせることができます。
なお、MSYS2がインストールされていなくてもFluentdは問題なく使用することができます。fluent-gemコマンドで新たなプラグインをインストールする場合に一時的にMSYS2が必要となることはありますが、通常利用では必要とされません。
厳密には td-agent v4.4.2以前の全てのv4系バージョンにも同様の問題があります。ただし、v4.4.2以前ではHKCU(HKEY_CURRENT_USER)のレジストリのみを参照し、HKLM(HKEY_LOCAL_MACHINE)のレジストリは参照しないため、サービスを起動するユーザーで影響を受けることは現実的にはほぼ無いと言えます。td-agent v4.5.0以降ではRubyInstallerの以下の変更の影響を受けてHKLMのレジストリも参照するようになったため、問題が発生するようになりました。
さいごに
最初にも紹介した通り、この情報は当社の法人向けサポートサービスのお客様向けに提供しているものを少しアレンジして公開したものになります。
当社の法人向けサポートサービスは、お客様の問題について原因や回避方法を調査してその解決を図ると同時に、その問題がフリーソフトウェアの問題であった場合はアップストリームへのフィードバックも行う事を心がけています。本件についても、関連するフリーソフトウェアのリポジトリ上で問題を報告・管理していたものをまとめたに過ぎず、アップストリームのサイトでは既に公開されている情報となります。
ただ、各リポジトリ上ではすべて英語でやりとりしていますし、問題が複数のフリーソフトウェアの組み合わせによって起こる場合には情報が分散してしまいますので、特に日本のユーザーにとっては問題に気がつきにくかったり、全体像を捉えにくいといったことがあったかもしれません。これからもこういった情報があれば随時ククログでも公開していこうと考えていますが、もしこういった情報を迅速に受け取りたい・問題解決をサポートして欲しいといったご要望があれば、ぜひ弊社のFluentdサポートサービスのご利用をご検討下さい。
<p>クリアコードはFluentdを利用する法人様に向けて<a href="/services/fluentd-service.html">Fluentdのサポートサービス</a>を提供しています。そのサービス内容の一つとして、Fluentdで何らかの不具合があってお客様に重大な影響が懸念される場合に、(契約形態にも依りますが)プッシュ型で情報をお知らせするサービスも提供しています。</p>
<p>今回、fluent-packageやtd-agentが環境によってはワーカープロセスを起動できない問題があることが発覚しましたので、その情報をククログでも紹介します。</p>
<!--more-->
<h2 id="本件の概要">本件の概要</h2>
<ul>
<li>fluent-packageやtd-agentのWindows版において、レジストリ<code>HKLM</code>(<code>HKEY_LOCAL_MACHINE</code>)や<code>HKCU</code>(<code>HKEY_CURRENT_USER</code>)の<code>SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\</code>以下に非ASCII文字を含むサブキーが存在すると、Fluentdのワーカープロセスが起動できないという問題があることが発覚しました。</li>
<li>上記のレジストリはWindowsの<code>プログラムの追加と削除</code>機能のために、各アプリケーションが情報を書き込む場所です。通常、各アプリケーションは上記レジストリキー直下にGUIDをサブキーとして書き込みますが、中には漢字等を含んだサブキーを作成するアプリケーションが存在します。</li>
<li>上記のようなレジストリキーが存在する場合でもFluentdの動作に影響を与えないというのが期待される動作ですが、fluent-packageやtd-agentが同梱するRubyInstallerの問題により、特定バージョンのfluent-packageやtd-agentではプロセスを起動できないという問題が発生します。</li>
</ul>
<h2 id="影響範囲">影響範囲</h2>
<ul>
<li>td-agent v4.5.0 以降、fluent-package v5.0.2以前のバージョンがこの影響を受けます。</li>
<li>前述のような問題のあるアプリケーションがインストールされている場合にのみ影響を受けます。</li>
<li>以下レジストリキーのサブキーとして非ASCII文字を含むものがあるかどうかを確認することで、影響を受けるかどうかを判断できます
<ul>
<li><code>\HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\</code></li>
<li><code>\HKEY_CURRENT_USER\WOW6432Node\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\</code></li>
<li><code>\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\</code></li>
<li><code>\HKEY_LOCAL_MACHINE\WOW6432Node\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\</code></li>
</ul>
</li>
<li>問題なくFluentdを起動できている場合は、必ずしも対処する必要はありません。
<ul>
<li>ただし、Fluentd起動後に追加で上記のような問題のあるアプリケーションをインストールした場合は、次回Fluentd再起動時に影響を受けます。</li>
</ul>
</li>
</ul>
<h2 id="影響">影響</h2>
<ul>
<li>この問題に該当する環境では、以下のようなエラーが発生してFluentdのワーカープロセスの起動に失敗し、ログ収集が行われません。</li>
</ul>
<pre lang="txt"><code>Traceback (most recent call last):
17: from <internal:gem_prelude>:1:in `<internal:gem_prelude>'
16: from <internal:gem_prelude>:1:in `require'
15: from C:/opt/td-agent/lib/ruby/2.7.0/rubygems.rb:1427:in `<top (required)>'
14: from C:/opt/td-agent/lib/ruby/2.7.0/rubygems.rb:1427:in `require'
13: from C:/opt/td-agent/lib/ruby/2.7.0/rubygems/defaults/operating_system.rb:24:in `<top (required)>'
12: from C:/opt/td-agent/lib/ruby/site_ruby/2.7.0/ruby_installer/runtime/singleton.rb:27:in `enable_dll_search_paths'
11: from C:/opt/td-agent/lib/ruby/site_ruby/2.7.0/ruby_installer/runtime/msys2_installation.rb:125:in `enable_dll_search_paths'
10: from C:/opt/td-agent/lib/ruby/site_ruby/2.7.0/ruby_installer/runtime/msys2_installation.rb:115:in `mingw_bin_path'
9: from C:/opt/td-agent/lib/ruby/site_ruby/2.7.0/ruby_installer/runtime/msys2_installation.rb:102:in `msys_path'
8: from C:/opt/td-agent/lib/ruby/site_ruby/2.7.0/ruby_installer/runtime/msys2_installation.rb:68:in `iterate_msys_paths'
7: from C:/opt/td-agent/lib/ruby/site_ruby/2.7.0/ruby_installer/runtime/msys2_installation.rb:68:in `each'
6: from C:/opt/td-agent/lib/ruby/site_ruby/2.7.0/ruby_installer/runtime/msys2_installation.rb:70:in `block in iterate_msys_paths'
5: from C:/opt/td-agent/lib/ruby/2.7.0/win32/registry.rb:542:in `open'
4: from C:/opt/td-agent/lib/ruby/2.7.0/win32/registry.rb:435:in `open'
3: from C:/opt/td-agent/lib/ruby/site_ruby/2.7.0/ruby_installer/runtime/msys2_installation.rb:71:in `block (2 levels) in iterate_msys_paths'
2: from C:/opt/td-agent/lib/ruby/2.7.0/win32/registry.rb:611:in `each_key'
1: from C:/opt/td-agent/lib/ruby/2.7.0/win32/registry.rb:910:in `export_string'
C:/opt/td-agent/lib/ruby/2.7.0/win32/registry.rb:910:in `encode': U+767E to ASCII-8BIT in conversion from UTF-16LE to UTF-8 to ASCII-8BIT (Encoding::UndefinedConversionError)
2023-06-14 16:40:41 +0800 [error]: Worker 0 exited unexpectedly with status 1
</code></pre>
<h2 id="対応方法">対応方法</h2>
<ul>
<li>影響を受ける場合は、以下の方法でこの問題を回避することが可能です
<ul>
<li>td-agentの場合: <code>C:\opt\td-agent\msys64\usr\bin\msys-2.0.dll</code>というパスで空のファイルを作成する</li>
<li>fluent-packageの場合: <code>C:\opt\fluent\msys64\usr\bin\msys-2.0.dll</code>というパスで空のファイルを作成する</li>
<li>上記ファイル作成後、自動で復旧する場合もありますが、念のためfluentdwinsvcサービスを再起動することをおすすめします。</li>
</ul>
</li>
<li>fluent-package v5.0.3 にてこの問題を<a href="https://github.com/fluent/fluent-package-builder/pull/620">暫定対処</a>予定です。リリース時期については調整中です。</li>
<li>RubyInstallerのアップストリームにも<a href="https://github.com/oneclick/rubyinstaller2/issues/372">問題を報告</a>し、抜本的な対応方法を検討しています。</li>
</ul>
<h2 id="原因詳細">原因詳細</h2>
<ul>
<li>Fluentdが使用するRubyは、起動時にDLLのサーチパスをリストアップします。</li>
<li>この際、MSYS2がインストールされているパスをDLLサーチパスの一つとして追加しようとします。</li>
<li>いくつか固定のデフォルトパスを検索してMYS2がインストールされていない場合は、上記のレジストリ値を参照してMSYS2がインストールされているかを確認します。</li>
<li>Fluentdのワーカープロセスは、サービスとして起動する場合、Rubyの内部エンコーディングを<code>ASCII-8BIT</code>(コマンドラインオプション<code>-Eacsii-8bit:ascii-8bit</code>)として起動しようとするため、レジストリキーに非ASCII文字が含まれる場合、変換に失敗しクラッシュします。
<ul>
<li>サービスではなくコマンドラインで起動する場合、通常は<code>UTF-8</code>(コマンドラインオプション<code>-Eutf8</code>)で起動しようとするため、この問題の影響を受けません。</li>
</ul>
</li>
<li><code>C:\opt\fluent\msys64\usr\bin\msys-2.0.dll</code>は上記で説明している<code>固定のデフォルトパス</code>の一つであるため、このファイルが存在する場合は、本不具合を発生させているコードの実行をスキップさせることができます。</li>
<li>なお、MSYS2がインストールされていなくてもFluentdは問題なく使用することができます。<code>fluent-gem</code>コマンドで新たなプラグインをインストールする場合に一時的にMSYS2が必要となることはありますが、通常利用では必要とされません。</li>
<li>厳密には td-agent v4.4.2以前の全てのv4系バージョンにも同様の問題があります。ただし、v4.4.2以前では<code>HKCU</code>(<code>HKEY_CURRENT_USER</code>)のレジストリのみを参照し、<code>HKLM</code>(<code>HKEY_LOCAL_MACHINE</code>)のレジストリは参照しないため、サービスを起動するユーザーで影響を受けることは現実的にはほぼ無いと言えます。td-agent v4.5.0以降ではRubyInstallerの以下の変更の影響を受けて<a href="https://github.com/oneclick/rubyinstaller2/commit/4382f99bf21608a3e37b6eabe861dca5e1f015f5"><code>HKLM</code>のレジストリも参照するようになった</a>ため、問題が発生するようになりました。</li>
</ul>
<h2 id="さいごに">さいごに</h2>
<p>最初にも紹介した通り、この情報は当社の法人向けサポートサービスのお客様向けに提供しているものを少しアレンジして公開したものになります。</p>
<p>当社の法人向けサポートサービスは、お客様の問題について原因や回避方法を調査してその解決を図ると同時に、その問題がフリーソフトウェアの問題であった場合はアップストリームへのフィードバックも行う事を心がけています。本件についても、関連するフリーソフトウェアのリポジトリ上で問題を報告・管理していたものをまとめたに過ぎず、アップストリームのサイトでは既に公開されている情報となります。</p>
<p>ただ、各リポジトリ上ではすべて英語でやりとりしていますし、問題が複数のフリーソフトウェアの組み合わせによって起こる場合には情報が分散してしまいますので、特に日本のユーザーにとっては問題に気がつきにくかったり、全体像を捉えにくいといったことがあったかもしれません。これからもこういった情報があれば随時ククログでも公開していこうと考えていますが、もしこういった情報を迅速に受け取りたい・問題解決をサポートして欲しいといったご要望があれば、ぜひ弊社の<a href="/services/fluentd-service.html">Fluentdサポートサービス</a>のご利用をご検討下さい。</p>
-
Fluentdを動かしてみよう!
https://www.clear-code.com/blog/2024/2/16/fluentd-tutorial-run-fluentd.html
2024-02-16T00:00:00+09:00
こんにちは。Fluentdのメンテナーの福田です。
Fluentdは、様々なデータソースからデータを読み込み、様々な出力先へ転送することができる便利なフリーソフトウェアです!
今回は、Fluentdに興味がある、触ってみたい、という方向けに、Fluentdを手元で動かす方法を紹介します。
RPMパッケージ、DEBパッケージ、MSI(Microsoft Windows Installer)を使ってパッケージ(Fluent Package)をインストールする方法と、ソースコードから起動する方法の2種類を説明します。
ユーザーとして機能を確かめてみたい、という方や、Fluentdやそのプラグインの開発に興味がある、という方は、ぜひご覧ください。
パッケージをインストールして動かす
Fluentdを便利に使うため、Fluentdプロジェクトはパッケージ「Fluent Package」を提供しています。
以前は「td-agent」という名前でした。
このパッケージをインストールすることで、すぐにFluentdを動かすことができます1。
今回は次の3種類を紹介します。
RPMパッケージ (Red Hat / CentOS / Rocky Linux / AlmaLinux)
DEBパッケージ (Debian / Ubuntu)
MSI (Windows)
もしmacOSで使いたいという人は、この後の「ソースコードから動かす」方法にするか、もしくは古いパッケージになりますがInstall by .dmg Package v4 (macOS)を参考にしてください。
また、ダウンロードして利用できるものについては、次の公式サイトをご覧ください。
Dockerイメージなどもあります。
https://www.fluentd.org/download
RPMパッケージ (Red Hat / CentOS / Rocky Linux / AlmaLinux)
コマンド一発でインストールできます!
$ curl -fsSL https://toolbelt.treasuredata.com/sh/install-redhat-fluent-package5-lts.sh | sh
インストールすると、systemdのユニットfluentdとして登録されます。
次のようにsystemctlコマンドで確認できます。
$ systemctl status fluentd
● fluentd.service - fluentd: All in one package of Fluentd
Loaded: loaded (/usr/lib/systemd/system/fluentd.service; disabled; vendor preset: disabled)
Active: inactive (dead)
Docs: https://docs.fluentd.org/
早速起動してみましょう。
$ sudo systemctl start fluentd
$ systemctl status fluentd
● fluentd.service - fluentd: All in one package of Fluentd
Loaded: loaded (/usr/lib/systemd/system/fluentd.service; disabled; vendor preset: disabled)
Active: active (running) since Thu 2024-02-01 14:35:25 UTC; 7s ago
Docs: https://docs.fluentd.org/
Process: ...
running状態になりました!
Fluent Packageでは、Fluentdの動作ログ2がデフォルトで/var/log/fluent/fluentd.logに出力されます。
ログ内容を確認してみましょう。
$ cat /var/log/fluent/fluentd.log
2024-02-01 14:35:25 +0000 [info]: init supervisor logger path="/var/log/fluent/fluentd.log" rotate_age=nil rotate_size=nil
2024-02-01 14:35:25 +0000 [info]: parsing config file is succeeded path="/etc/fluent/fluentd.conf"
2024-02-01 14:35:25 +0000 [info]: gem 'fluentd' version '1.16.3'
...
何やら動いていそうですね!
次に設定を確認してみましょう。
Fluent Packageでは、設定ファイルはデフォルトで/etc/fluent/fluentd.confになります。
$ cat /etc/fluent/fluentd.conf
####
## Output descriptions:
...
何やら色々設定がありますね!
せっかくなので、もうちょっと遊んでみたいですよね。
それについてはインストール方法が関係ないので、最後の「設定を変更してデータを処理させる」で説明しています!
そちらをご覧ください!
またアンインストールは、dnfコマンドでfluent-packageをアンインストールする形になります。
$ sudo dnf remove fluent-package
参考: 公式ドキュメント https://docs.fluentd.org/installation/install-by-rpm
DEBパッケージ (Debian / Ubuntu)
コマンド一発でインストールできます!
ディストリビューションによってコマンドが異なります。
Ubuntu Jammy
$ curl -fsSL https://toolbelt.treasuredata.com/sh/install-ubuntu-jammy-fluent-package5-lts.sh | sh
Ubuntu Focal
$ curl -fsSL https://toolbelt.treasuredata.com/sh/install-ubuntu-focal-fluent-package5-lts.sh | sh
Debian Bookworm
$ curl -fsSL https://toolbelt.treasuredata.com/sh/install-debian-bookworm-fluent-package5-lts.sh | sh
Debian Bullseye
$ curl -fsSL https://toolbelt.treasuredata.com/sh/install-debian-bullseye-fluent-package5-lts.sh | sh
インストールすると、systemdのユニットfluentdとして登録されます。
次のようにsystemctlコマンドで確認できます。
$ systemctl status fluentd
systemctl status fluentd
● fluentd.service - fluentd: All in one package of Fluentd
Loaded: loaded (/lib/systemd/system/fluentd.service; enabled; vendor preset: enabled)
Active: active (running) since Thu 2024-02-01 15:01:46 UTC; 17s ago
Docs: https://docs.fluentd.org/
Process: ...
自動で動き始めていますね!
(今後のバージョンで変わるかもしれません。もし動いていなかったら$ sudo systemctl start fluentdコマンドを実行して起動してください。)
Fluent Packageでは、Fluentdの動作ログ2がデフォルトで/var/log/fluent/fluentd.logに出力されます。
ログ内容を確認してみましょう。
$ cat /var/log/fluent/fluentd.log
2024-02-01 14:35:25 +0000 [info]: init supervisor logger path="/var/log/fluent/fluentd.log" rotate_age=nil rotate_size=nil
2024-02-01 14:35:25 +0000 [info]: parsing config file is succeeded path="/etc/fluent/fluentd.conf"
2024-02-01 14:35:25 +0000 [info]: gem 'fluentd' version '1.16.3'
...
何やら動いていそうですね!
次に設定を確認してみましょう。
Fluent Packageでは、設定ファイルはデフォルトで/etc/fluent/fluentd.confになります。
$ cat /etc/fluent/fluentd.conf
####
## Output descriptions:
...
何やら色々設定がありますね!
せっかくなので、もうちょっと遊んでみたいですよね。
それについてはインストール方法が関係ないので、最後の「設定を変更してデータを処理させる」で説明しています!
そちらをご覧ください!
またアンインストールは、aptコマンドでfluent-packageをアンインストールする形になります。
$ sudo apt purge fluent-package
参考: 公式ドキュメント https://docs.fluentd.org/installation/install-by-deb
MSI (Microsoft Windows Installer)
公式サイトの「Download Fluent Package」ページに、MSIファイルの一覧へのリンクがあります。
ここからMSIファイルをダウンロードして、実行することでインストール可能です。
インストーラーでは、特に何も変更せずにデフォルトでインストールして問題ありません。
インストールすると、fluentdwinsvcという名前のWindows Serviceとして登録されます。
管理者権限のPowerShellを立ち上げて、確認してみましょう。
$ Get-Service fluentdwinsvc
Status Name DisplayName
------ ---- -----------
Stopped fluentdwinsvc Fluentd Windows Service
早速起動してみましょう。
$ Start-Service fluentdwinsvc
$ Get-Service fluentdwinsvc
Status Name DisplayName
------ ---- -----------
Running fluentdwinsvc Fluentd Windows Service
Running状態になりました!
Fluent Packageでは、Fluentdの動作ログ2がデフォルトで次のファイルに出力されます3。
C:\opt\fluent\fluentd-supervisor-0.log
Supervisorプロセス(Fluentdの大元のプロセス)のログです
C:\opt\fluent\fluentd-0.log
Workerプロセス(実際に作業を行う子分のプロセス)のログです
これらのファイルの中身を確認すると、何やらFluentdが動いていそうなことが分かります。
次に設定を確認してみましょう。
Fluent Packageでは、設定ファイルはデフォルトでC:\opt\fluent\etc\fluent\fluentd.confになります。
このファイルの中身を確認すると、何やら色々設定があることが分かります。
せっかくなので、もうちょっと遊んでみたいですよね。
それについてはインストール方法が関係ないので、最後の「設定を変更してデータを処理させる」で説明しています!
そちらをご覧ください!
またアンインストールは、「設定」の「アプリと機能」や、「コントロールパネル」の「プログラムと機能」から、Fluent Package v...をアンインストールすることで可能です。
C:\optのフォルダーも不要であれば消してください。
参考: 公式ドキュメント https://docs.fluentd.org/installation/install-by-msi
ソースコードから動かす
FluentdはRubyで動いています。
そのため、GitとRubyの実行環境があれば、ソースコードから簡単に動かすことが可能です。
GitとRubyの実行環境がない場合は、事前に整えてください。
GitHubのFluentdリポジトリーをクローンします。
$ git clone https://github.com/fluent/fluentd.git
Cloning ...
...
クローンしたディレクトリーに移動します。
$ cd fluentd
依存関係をダウンロードします。
$ bundle
Fetching gem metadata from ...
...
以上で、Fluentdを動かす用意ができました。
いくつかやり方がありますが、ここでは$ bundle exec fluentd ...という形でFluentdを実行していきます。
まずはヘルプを見てみましょう。
$ bundle exec fluentd --help
Usage: fluentd [options]
-s, --setup [DIR=/etc/fluent] install sample configuration file to the directory
-c, --config PATH config file path (default: /etc/fluent/fluent.conf)
...
fluentdコマンドのヘルプが表示されますね。
次にバージョンを確認してみましょう。
$ bundle exec fluentd --version
fluentd 1.16.2
バージョンが表示されました。
次の章で、実際にFluentdの設定を用意してデータを処理させてみましょう。
参考: 公式ドキュメント https://docs.fluentd.org/installation/install-from-source
設定を変更してデータを処理させる
ここまで色々な方法でFluentdを動かす方法を見てきました。
最後に、実際にFluentdの設定をして、データを処理させてみましょう。
Fluentdは、「プラグイン」という機能を組み合わせることで様々な動作を実現できます。
このプラグインには、標準で搭載されているものもありますし、有志で作成されて公開されており、インストールすることで使えるものもあります。
Fluentdの設定の大半は、利用する各プラグインの設定になります。
今回は、in_sampleプラグインとout_stdoutプラグインを使って、簡単にFluentdがデータを処理する様子を見てみましょう。
in_sampleプラグインは、Inputプラグイン4の1種です。
通常のInputプラグインは、何かの外部データソースからデータを収集するものですが、in_sampleは単にサンプルデータを1秒ごとにFluentdに流し込むだけです。
主にFluentdの動作チェックのために使います。
今回は簡単にFluentdの動作を確認するために、このInputプラグインを使ってみましょう。
out_stdoutプラグインは、Outputプラグイン5の1種です。
out_stdoutは、標準出力にデータを出力します。
パッケージなどを利用してFluentd自身の動作ログをログファイルに出力している場合は、out_stdoutも同じログファイルに出力することになります。
コマンド実行などで、Fluentd自身の動作ログをコンソールに出力している場合は、out_stdoutもコンソールに出力することになります。
今回は、in_sampleが流し込むサンプルデータを簡単に確認するため、このOutputプラグインを使ってみましょう。
設定は次の通りです。
<source>
@type sample
tag test
</source>
<match test.**>
@type stdout
</match>
パッケージで動かしている場合は、既存の設定ファイルを上書きしてください。
設定が完了したら、Fluentdをリスタートしましょう。
$ sudo systemctl restart fluentd
リスタート後、ログファイルを確認してみてください。
サンプルログが出力される様子を確認できます。
ソースコードから動かしている場合は、適当な場所に設定ファイルを新規作成してください。
設定ファイルを作成したら、-cオプションで設定ファイルのパスを指定してFluentdを起動しましょう。
$ bundle exec fluentd -c {設定ファイルパス}
このようにログファイルパスを指定せずにコマンドでFluentdを起動すると、Fluentdはコンソールに自身の動作ログを出力します。
動作ログとともに、サンプルログが出力される様子を確認できます。
確認したら、Ctrl + Cで停止します。
どちらの方法でも、次のように動作ログと一緒にサンプルログが出力されるのを確認できましたね!
2024-02-02 10:01:47 +0900 [info]: init supervisor logger path=nil rotate_age=nil rotate_size=nil
2024-02-02 10:01:47 +0900 [info]: parsing config file is succeeded path="../fluentd-conf/sample.conf"
2024-02-02 10:01:47 +0900 [info]: gem 'fluentd' version '1.16.2'
2024-02-02 10:01:47 +0900 [warn]: both of Plugin @id and path for <storage> are not specified. Using on-memory store.
2024-02-02 10:01:47 +0900 [warn]: both of Plugin @id and path for <storage> are not specified. Using on-memory store.
2024-02-02 10:01:47 +0900 [info]: using configuration file: <ROOT>
<source>
@type sample
tag "test"
</source>
<match test.**>
@type stdout
</match>
</ROOT>
2024-02-02 10:01:47 +0900 [info]: starting fluentd-1.16.2 pid=618044 ruby="3.2.2"
2024-02-02 10:01:47 +0900 [info]: spawn command to main: cmdline=["/home/daipom/.rbenv/versions/3.2.2/bin/ruby", "-r/home/daipom/.rbenv/versions/3.2.2/lib/ruby/site_ruby/3.2.0/bundler/setup", "-Eascii-8bit:ascii-8bit", "/home/daipom/.rbenv/versions/3.2.2/lib/ruby/gems/3.2.0/bin/fluentd", "-c", "../fluentd-conf/sample.conf", "--under-supervisor"]
2024-02-02 10:01:47 +0900 [info]: #0 init worker0 logger path=nil rotate_age=nil rotate_size=nil
2024-02-02 10:01:47 +0900 [info]: adding match pattern="test.**" type="stdout"
2024-02-02 10:01:47 +0900 [info]: adding source type="sample"
2024-02-02 10:01:47 +0900 [warn]: #0 both of Plugin @id and path for <storage> are not specified. Using on-memory store.
2024-02-02 10:01:47 +0900 [warn]: #0 both of Plugin @id and path for <storage> are not specified. Using on-memory store.
2024-02-02 10:01:47 +0900 [info]: #0 starting fluentd worker pid=618064 ppid=618044 worker=0
2024-02-02 10:01:47 +0900 [info]: #0 fluentd worker is now running worker=0
2024-02-02 10:01:48.048965556 +0900 test: {"message":"sample"}
2024-02-02 10:01:49.050802278 +0900 test: {"message":"sample"}
2024-02-02 10:01:50.052568659 +0900 test: {"message":"sample"}
...
この最後の部分が、in_sampleが流し込んだサンプルデータを、out_stdoutが出力したものになっています!
2024-02-02 10:01:48.048965556 +0900 test: {"message":"sample"}
2024-02-02 10:01:49.050802278 +0900 test: {"message":"sample"}
2024-02-02 10:01:50.052568659 +0900 test: {"message":"sample"}
...
まとめ
今回は、Fluentdを手元で動かしてみる方法について、RPMパッケージ、DEBパッケージ、MSI(Microsoft Windows Installer)を使ってパッケージ(Fluent Package)をインストールする方法と、ソースコードから起動する方法の2種類を説明しました。
Fluentdに興味がある方は、ぜひ実際に手元で動かしてみてください!
また、Fluentdコミュニティーでは、日本語用のQ&Aも用意しています。
何か疑問や困ったことがあれば、こちらで質問してみてください!
https://github.com/fluent/fluentd/discussions/categories/q-a-japanese
最後に、クリアコードはFluentdのサポートサービスを行っています。
Fluent Packageへのアップデート支援や影響のあるバグ・脆弱性のレポートなどのサポートをいたしますので、詳しくはFluentdのサポートサービスをご覧いただき、お問い合わせフォームよりお気軽にお問い合わせください。
パッケージには実行に必要なRubyなどが組み込まれているため、インストールするだけですぐに動かすことができます。GitやRuby環境を用意するのが大変、という方はパッケージで動かすのがおすすめです。一方で、Fluentdやそのプラグインの開発にも興味がある方は、「ソースコードから動かす」の方がおすすめです。 ↩
Fluentd自身の動作ログ: Fluentdがログを扱うソフトウェアなのでややこしいですが、Fluentd自身も動作ログを出力します。Fluentdが収集したり転送したりするログと、Fluentd自身の動作ログとを混同しないように気をつけてください! ↩
Windowsにおけるログファイル: Windowsではプロセス毎に別のログファイルに出力する形になります。 ↩
Inputプラグイン: あるデータソースからデータを収集する処理を行うプラグインです。慣習としてin_というプレフィックスを付けて呼ぶことが多いです。source設定として設定します。 ↩
Outputプラグイン: Inputプラグインが収集したデータなどを他のデータソースへ出力するプラグインです。慣習としてout_といプレフィックスを付けて呼ぶことが多いです。match設定として設定します。 ↩
<p>こんにちは。<a href="http://www.fluentd.org">Fluentd</a>のメンテナーの福田です。</p>
<p>Fluentdは、様々なデータソースからデータを読み込み、様々な出力先へ転送することができる便利なフリーソフトウェアです!</p>
<p>今回は、Fluentdに興味がある、触ってみたい、という方向けに、Fluentdを手元で動かす方法を紹介します。
RPMパッケージ、DEBパッケージ、MSI(Microsoft Windows Installer)を使ってパッケージ(Fluent Package)をインストールする方法と、ソースコードから起動する方法の2種類を説明します。</p>
<p>ユーザーとして機能を確かめてみたい、という方や、Fluentdやそのプラグインの開発に興味がある、という方は、ぜひご覧ください。</p>
<!--more-->
<h2 id="パッケージをインストールして動かす">パッケージをインストールして動かす</h2>
<p>Fluentdを便利に使うため、Fluentdプロジェクトはパッケージ「Fluent Package」を提供しています。
以前は「td-agent」という名前でした。
このパッケージをインストールすることで、すぐにFluentdを動かすことができます<sup class="footnote-ref"><a href="#fn1" id="fnref1">1</a></sup>。</p>
<p>今回は次の3種類を紹介します。</p>
<ul>
<li>RPMパッケージ (Red Hat / CentOS / Rocky Linux / AlmaLinux)</li>
<li>DEBパッケージ (Debian / Ubuntu)</li>
<li>MSI (Windows)</li>
</ul>
<p>もしmacOSで使いたいという人は、この後の「ソースコードから動かす」方法にするか、もしくは古いパッケージになりますが<a href="https://docs.fluentd.org/installation/obsolete-installation/treasure-agent-v4-installation/install-by-dmg-td-agent-v4">Install by .dmg Package v4 (macOS)</a>を参考にしてください。</p>
<p>また、ダウンロードして利用できるものについては、次の公式サイトをご覧ください。
Dockerイメージなどもあります。</p>
<ul>
<li><a href="https://www.fluentd.org/download">https://www.fluentd.org/download</a></li>
</ul>
<h3 id="rpmパッケージ-(red-hat-/-centos-/-rocky-linux-/-almalinux)">RPMパッケージ (Red Hat / CentOS / Rocky Linux / AlmaLinux)</h3>
<p>コマンド一発でインストールできます!</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">$</span><span class="w"> </span>curl <span class="nt">-fsSL</span> https://toolbelt.treasuredata.com/sh/install-redhat-fluent-package5-lts.sh | sh
</code></pre></div></div>
<p>インストールすると、systemdのユニット<code>fluentd</code>として登録されます。
次のように<code>systemctl</code>コマンドで確認できます。</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">$</span><span class="w"> </span>systemctl status fluentd
<span class="go">● fluentd.service - fluentd: All in one package of Fluentd
</span><span class="gp"> Loaded: loaded (/usr/lib/systemd/system/fluentd.service;</span><span class="w"> </span>disabled<span class="p">;</span> vendor preset: disabled<span class="o">)</span>
<span class="go"> Active: inactive (dead)
Docs: https://docs.fluentd.org/
</span></code></pre></div></div>
<p>早速起動してみましょう。</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">$</span><span class="w"> </span><span class="nb">sudo </span>systemctl start fluentd
<span class="gp">$</span><span class="w"> </span>systemctl status fluentd
<span class="go">● fluentd.service - fluentd: All in one package of Fluentd
</span><span class="gp"> Loaded: loaded (/usr/lib/systemd/system/fluentd.service;</span><span class="w"> </span>disabled<span class="p">;</span> vendor preset: disabled<span class="o">)</span>
<span class="gp"> Active: active (running) since Thu 2024-02-01 14:35:25 UTC;</span><span class="w"> </span>7s ago
<span class="go"> Docs: https://docs.fluentd.org/
Process: ...
</span></code></pre></div></div>
<p><code>running</code>状態になりました!</p>
<p>Fluent Packageでは、Fluentdの動作ログ<sup class="footnote-ref"><a href="#fn2" id="fnref2">2</a></sup>がデフォルトで<code>/var/log/fluent/fluentd.log</code>に出力されます。
ログ内容を確認してみましょう。</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">$</span><span class="w"> </span><span class="nb">cat</span> /var/log/fluent/fluentd.log
<span class="go">2024-02-01 14:35:25 +0000 [info]: init supervisor logger path="/var/log/fluent/fluentd.log" rotate_age=nil rotate_size=nil
2024-02-01 14:35:25 +0000 [info]: parsing config file is succeeded path="/etc/fluent/fluentd.conf"
2024-02-01 14:35:25 +0000 [info]: gem 'fluentd' version '1.16.3'
</span><span class="c">...
</span></code></pre></div></div>
<p>何やら動いていそうですね!</p>
<p>次に設定を確認してみましょう。
Fluent Packageでは、設定ファイルはデフォルトで<code>/etc/fluent/fluentd.conf</code>になります。</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">$</span><span class="w"> </span><span class="nb">cat</span> /etc/fluent/fluentd.conf
<span class="gp">#</span><span class="c">###</span>
<span class="gp">#</span><span class="c"># Output descriptions:</span>
<span class="c">...
</span></code></pre></div></div>
<p>何やら色々設定がありますね!</p>
<p>せっかくなので、もうちょっと遊んでみたいですよね。
それについてはインストール方法が関係ないので、最後の「設定を変更してデータを処理させる」で説明しています!
そちらをご覧ください!</p>
<p>またアンインストールは、<code>dnf</code>コマンドで<code>fluent-package</code>をアンインストールする形になります。</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">$</span><span class="w"> </span><span class="nb">sudo </span>dnf remove fluent-package
</code></pre></div></div>
<p>参考: 公式ドキュメント <a href="https://docs.fluentd.org/installation/install-by-rpm">https://docs.fluentd.org/installation/install-by-rpm</a></p>
<h3 id="debパッケージ-(debian-/-ubuntu)">DEBパッケージ (Debian / Ubuntu)</h3>
<p>コマンド一発でインストールできます!</p>
<p>ディストリビューションによってコマンドが異なります。</p>
<ul>
<li><code>Ubuntu Jammy</code>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">$</span><span class="w"> </span>curl <span class="nt">-fsSL</span> https://toolbelt.treasuredata.com/sh/install-ubuntu-jammy-fluent-package5-lts.sh | sh
</code></pre></div></div>
</li>
<li><code>Ubuntu Focal</code>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">$</span><span class="w"> </span>curl <span class="nt">-fsSL</span> https://toolbelt.treasuredata.com/sh/install-ubuntu-focal-fluent-package5-lts.sh | sh
</code></pre></div></div>
</li>
<li><code>Debian Bookworm</code>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">$</span><span class="w"> </span>curl <span class="nt">-fsSL</span> https://toolbelt.treasuredata.com/sh/install-debian-bookworm-fluent-package5-lts.sh | sh
</code></pre></div></div>
</li>
<li><code>Debian Bullseye</code>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">$</span><span class="w"> </span>curl <span class="nt">-fsSL</span> https://toolbelt.treasuredata.com/sh/install-debian-bullseye-fluent-package5-lts.sh | sh
</code></pre></div></div>
</li>
</ul>
<p>インストールすると、systemdのユニット<code>fluentd</code>として登録されます。
次のように<code>systemctl</code>コマンドで確認できます。</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">$</span><span class="w"> </span>systemctl status fluentd
<span class="go">systemctl status fluentd
● fluentd.service - fluentd: All in one package of Fluentd
</span><span class="gp"> Loaded: loaded (/lib/systemd/system/fluentd.service;</span><span class="w"> </span>enabled<span class="p">;</span> vendor preset: enabled<span class="o">)</span>
<span class="gp"> Active: active (running) since Thu 2024-02-01 15:01:46 UTC;</span><span class="w"> </span>17s ago
<span class="go"> Docs: https://docs.fluentd.org/
Process: ...
</span></code></pre></div></div>
<p>自動で動き始めていますね!
(今後のバージョンで変わるかもしれません。もし動いていなかったら<code>$ sudo systemctl start fluentd</code>コマンドを実行して起動してください。)</p>
<p>Fluent Packageでは、Fluentdの動作ログ<sup class="footnote-ref"><a href="#fn2" id="fnref2">2</a></sup>がデフォルトで<code>/var/log/fluent/fluentd.log</code>に出力されます。
ログ内容を確認してみましょう。</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">$</span><span class="w"> </span><span class="nb">cat</span> /var/log/fluent/fluentd.log
<span class="go">2024-02-01 14:35:25 +0000 [info]: init supervisor logger path="/var/log/fluent/fluentd.log" rotate_age=nil rotate_size=nil
2024-02-01 14:35:25 +0000 [info]: parsing config file is succeeded path="/etc/fluent/fluentd.conf"
2024-02-01 14:35:25 +0000 [info]: gem 'fluentd' version '1.16.3'
</span><span class="c">...
</span></code></pre></div></div>
<p>何やら動いていそうですね!</p>
<p>次に設定を確認してみましょう。
Fluent Packageでは、設定ファイルはデフォルトで<code>/etc/fluent/fluentd.conf</code>になります。</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">$</span><span class="w"> </span><span class="nb">cat</span> /etc/fluent/fluentd.conf
<span class="gp">#</span><span class="c">###</span>
<span class="gp">#</span><span class="c"># Output descriptions:</span>
<span class="c">...
</span></code></pre></div></div>
<p>何やら色々設定がありますね!</p>
<p>せっかくなので、もうちょっと遊んでみたいですよね。
それについてはインストール方法が関係ないので、最後の「設定を変更してデータを処理させる」で説明しています!
そちらをご覧ください!</p>
<p>またアンインストールは、<code>apt</code>コマンドで<code>fluent-package</code>をアンインストールする形になります。</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">$</span><span class="w"> </span><span class="nb">sudo </span>apt purge fluent-package
</code></pre></div></div>
<p>参考: 公式ドキュメント <a href="https://docs.fluentd.org/installation/install-by-deb">https://docs.fluentd.org/installation/install-by-deb</a></p>
<h3 id="msi-(microsoft-windows-installer)">MSI (Microsoft Windows Installer)</h3>
<p><a href="https://www.fluentd.org/download/fluent_package">公式サイトの「Download Fluent Package」ページ</a>に、<a href="https://td-agent-package-browser.herokuapp.com/lts/5/windows">MSIファイルの一覧</a>へのリンクがあります。
ここからMSIファイルをダウンロードして、実行することでインストール可能です。
インストーラーでは、特に何も変更せずにデフォルトでインストールして問題ありません。</p>
<p>インストールすると、<code>fluentdwinsvc</code>という名前のWindows Serviceとして登録されます。
管理者権限のPowerShellを立ち上げて、確認してみましょう。</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">$</span><span class="w"> </span><span class="n">Get-Service</span><span class="w"> </span><span class="nx">fluentdwinsvc</span><span class="w">
</span><span class="n">Status</span><span class="w"> </span><span class="nx">Name</span><span class="w"> </span><span class="nx">DisplayName</span><span class="w">
</span><span class="o">------</span><span class="w"> </span><span class="o">----</span><span class="w"> </span><span class="o">-----------</span><span class="w">
</span><span class="n">Stopped</span><span class="w"> </span><span class="nx">fluentdwinsvc</span><span class="w"> </span><span class="nx">Fluentd</span><span class="w"> </span><span class="nx">Windows</span><span class="w"> </span><span class="nx">Service</span><span class="w">
</span></code></pre></div></div>
<p>早速起動してみましょう。</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">$</span><span class="w"> </span><span class="n">Start-Service</span><span class="w"> </span><span class="nx">fluentdwinsvc</span><span class="w">
</span><span class="err">$</span><span class="w"> </span><span class="n">Get-Service</span><span class="w"> </span><span class="nx">fluentdwinsvc</span><span class="w">
</span><span class="n">Status</span><span class="w"> </span><span class="nx">Name</span><span class="w"> </span><span class="nx">DisplayName</span><span class="w">
</span><span class="o">------</span><span class="w"> </span><span class="o">----</span><span class="w"> </span><span class="o">-----------</span><span class="w">
</span><span class="n">Running</span><span class="w"> </span><span class="nx">fluentdwinsvc</span><span class="w"> </span><span class="nx">Fluentd</span><span class="w"> </span><span class="nx">Windows</span><span class="w"> </span><span class="nx">Service</span><span class="w">
</span></code></pre></div></div>
<p><code>Running</code>状態になりました!</p>
<p>Fluent Packageでは、Fluentdの動作ログ<sup class="footnote-ref"><a href="#fn2" id="fnref2">2</a></sup>がデフォルトで次のファイルに出力されます<sup class="footnote-ref"><a href="#fn3" id="fnref3">3</a></sup>。</p>
<ul>
<li><code>C:\opt\fluent\fluentd-supervisor-0.log</code>
<ul>
<li>Supervisorプロセス(Fluentdの大元のプロセス)のログです</li>
</ul>
</li>
<li><code>C:\opt\fluent\fluentd-0.log</code>
<ul>
<li>Workerプロセス(実際に作業を行う子分のプロセス)のログです</li>
</ul>
</li>
</ul>
<p>これらのファイルの中身を確認すると、何やらFluentdが動いていそうなことが分かります。</p>
<p>次に設定を確認してみましょう。
Fluent Packageでは、設定ファイルはデフォルトで<code>C:\opt\fluent\etc\fluent\fluentd.conf</code>になります。
このファイルの中身を確認すると、何やら色々設定があることが分かります。</p>
<p>せっかくなので、もうちょっと遊んでみたいですよね。
それについてはインストール方法が関係ないので、最後の「設定を変更してデータを処理させる」で説明しています!
そちらをご覧ください!</p>
<p>またアンインストールは、「設定」の「アプリと機能」や、「コントロールパネル」の「プログラムと機能」から、<code>Fluent Package v...</code>をアンインストールすることで可能です。
<code>C:\opt</code>のフォルダーも不要であれば消してください。</p>
<p>参考: 公式ドキュメント <a href="https://docs.fluentd.org/installation/install-by-msi">https://docs.fluentd.org/installation/install-by-msi</a></p>
<h2 id="ソースコードから動かす">ソースコードから動かす</h2>
<p>FluentdはRubyで動いています。
そのため、GitとRubyの実行環境があれば、ソースコードから簡単に動かすことが可能です。</p>
<p>GitとRubyの実行環境がない場合は、事前に整えてください。</p>
<p><a href="https://github.com/fluent/fluentd">GitHubのFluentdリポジトリー</a>をクローンします。</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">$</span><span class="w"> </span>git clone https://github.com/fluent/fluentd.git
<span class="go">Cloning ...
</span><span class="c">...
</span></code></pre></div></div>
<p>クローンしたディレクトリーに移動します。</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">$</span><span class="w"> </span><span class="nb">cd </span>fluentd
</code></pre></div></div>
<p>依存関係をダウンロードします。</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">$</span><span class="w"> </span>bundle
<span class="go">Fetching gem metadata from ...
</span><span class="c">...
</span></code></pre></div></div>
<p>以上で、Fluentdを動かす用意ができました。
いくつかやり方がありますが、ここでは<code>$ bundle exec fluentd ...</code>という形でFluentdを実行していきます。</p>
<p>まずはヘルプを見てみましょう。</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">$</span><span class="w"> </span>bundle <span class="nb">exec </span>fluentd <span class="nt">--help</span>
<span class="go">Usage: fluentd [options]
-s, --setup [DIR=/etc/fluent] install sample configuration file to the directory
-c, --config PATH config file path (default: /etc/fluent/fluent.conf)
</span><span class="c">...
</span></code></pre></div></div>
<p><code>fluentd</code>コマンドのヘルプが表示されますね。</p>
<p>次にバージョンを確認してみましょう。</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">$</span><span class="w"> </span>bundle <span class="nb">exec </span>fluentd <span class="nt">--version</span>
<span class="go">fluentd 1.16.2
</span></code></pre></div></div>
<p>バージョンが表示されました。</p>
<p>次の章で、実際にFluentdの設定を用意してデータを処理させてみましょう。</p>
<p>参考: 公式ドキュメント <a href="https://docs.fluentd.org/installation/install-from-source">https://docs.fluentd.org/installation/install-from-source</a></p>
<h2 id="設定を変更してデータを処理させる">設定を変更してデータを処理させる</h2>
<p>ここまで色々な方法でFluentdを動かす方法を見てきました。
最後に、実際にFluentdの設定をして、データを処理させてみましょう。</p>
<p>Fluentdは、「プラグイン」という機能を組み合わせることで様々な動作を実現できます。
このプラグインには、標準で搭載されているものもありますし、有志で作成されて公開されており、インストールすることで使えるものもあります。
Fluentdの設定の大半は、利用する各プラグインの設定になります。</p>
<p>今回は、<a href="https://docs.fluentd.org/input/sample">in_sampleプラグイン</a>と<a href="https://docs.fluentd.org/output/stdout">out_stdoutプラグイン</a>を使って、簡単にFluentdがデータを処理する様子を見てみましょう。</p>
<p><a href="https://docs.fluentd.org/input/sample">in_sampleプラグイン</a>は、<a href="https://docs.fluentd.org/input">Inputプラグイン</a><sup class="footnote-ref"><a href="#fn4" id="fnref4">4</a></sup>の1種です。
通常のInputプラグインは、何かの外部データソースからデータを収集するものですが、<code>in_sample</code>は単にサンプルデータを1秒ごとにFluentdに流し込むだけです。
主にFluentdの動作チェックのために使います。
今回は簡単にFluentdの動作を確認するために、このInputプラグインを使ってみましょう。</p>
<p><a href="https://docs.fluentd.org/output/stdout">out_stdoutプラグイン</a>は、<a href="https://docs.fluentd.org/output">Outputプラグイン</a><sup class="footnote-ref"><a href="#fn5" id="fnref5">5</a></sup>の1種です。
<code>out_stdout</code>は、標準出力にデータを出力します。
パッケージなどを利用してFluentd自身の動作ログをログファイルに出力している場合は、<code>out_stdout</code>も同じログファイルに出力することになります。
コマンド実行などで、Fluentd自身の動作ログをコンソールに出力している場合は、<code>out_stdout</code>もコンソールに出力することになります。
今回は、<code>in_sample</code>が流し込むサンプルデータを簡単に確認するため、このOutputプラグインを使ってみましょう。</p>
<p>設定は次の通りです。</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><source></span>
@type sample
tag test
<span class="nt"></source></span>
<span class="nt"><match</span> <span class="err">test.**</span><span class="nt">></span>
@type stdout
<span class="nt"></match></span>
</code></pre></div></div>
<p>パッケージで動かしている場合は、既存の設定ファイルを上書きしてください。
設定が完了したら、Fluentdをリスタートしましょう。</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">$</span><span class="w"> </span><span class="nb">sudo </span>systemctl restart fluentd
</code></pre></div></div>
<p>リスタート後、ログファイルを確認してみてください。
サンプルログが出力される様子を確認できます。</p>
<p>ソースコードから動かしている場合は、適当な場所に設定ファイルを新規作成してください。
設定ファイルを作成したら、<code>-c</code>オプションで設定ファイルのパスを指定してFluentdを起動しましょう。</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">$</span><span class="w"> </span>bundle <span class="nb">exec </span>fluentd <span class="nt">-c</span> <span class="o">{</span>設定ファイルパス<span class="o">}</span>
</code></pre></div></div>
<p>このようにログファイルパスを指定せずにコマンドでFluentdを起動すると、Fluentdはコンソールに自身の動作ログを出力します。
動作ログとともに、サンプルログが出力される様子を確認できます。
確認したら、<code>Ctrl</code> + <code>C</code>で停止します。</p>
<p>どちらの方法でも、次のように動作ログと一緒にサンプルログが出力されるのを確認できましたね!</p>
<pre><code>2024-02-02 10:01:47 +0900 [info]: init supervisor logger path=nil rotate_age=nil rotate_size=nil
2024-02-02 10:01:47 +0900 [info]: parsing config file is succeeded path="../fluentd-conf/sample.conf"
2024-02-02 10:01:47 +0900 [info]: gem 'fluentd' version '1.16.2'
2024-02-02 10:01:47 +0900 [warn]: both of Plugin @id and path for <storage> are not specified. Using on-memory store.
2024-02-02 10:01:47 +0900 [warn]: both of Plugin @id and path for <storage> are not specified. Using on-memory store.
2024-02-02 10:01:47 +0900 [info]: using configuration file: <ROOT>
<source>
@type sample
tag "test"
</source>
<match test.**>
@type stdout
</match>
</ROOT>
2024-02-02 10:01:47 +0900 [info]: starting fluentd-1.16.2 pid=618044 ruby="3.2.2"
2024-02-02 10:01:47 +0900 [info]: spawn command to main: cmdline=["/home/daipom/.rbenv/versions/3.2.2/bin/ruby", "-r/home/daipom/.rbenv/versions/3.2.2/lib/ruby/site_ruby/3.2.0/bundler/setup", "-Eascii-8bit:ascii-8bit", "/home/daipom/.rbenv/versions/3.2.2/lib/ruby/gems/3.2.0/bin/fluentd", "-c", "../fluentd-conf/sample.conf", "--under-supervisor"]
2024-02-02 10:01:47 +0900 [info]: #0 init worker0 logger path=nil rotate_age=nil rotate_size=nil
2024-02-02 10:01:47 +0900 [info]: adding match pattern="test.**" type="stdout"
2024-02-02 10:01:47 +0900 [info]: adding source type="sample"
2024-02-02 10:01:47 +0900 [warn]: #0 both of Plugin @id and path for <storage> are not specified. Using on-memory store.
2024-02-02 10:01:47 +0900 [warn]: #0 both of Plugin @id and path for <storage> are not specified. Using on-memory store.
2024-02-02 10:01:47 +0900 [info]: #0 starting fluentd worker pid=618064 ppid=618044 worker=0
2024-02-02 10:01:47 +0900 [info]: #0 fluentd worker is now running worker=0
2024-02-02 10:01:48.048965556 +0900 test: {"message":"sample"}
2024-02-02 10:01:49.050802278 +0900 test: {"message":"sample"}
2024-02-02 10:01:50.052568659 +0900 test: {"message":"sample"}
...
</code></pre>
<p>この最後の部分が、<code>in_sample</code>が流し込んだサンプルデータを、<code>out_stdout</code>が出力したものになっています!</p>
<pre><code>2024-02-02 10:01:48.048965556 +0900 test: {"message":"sample"}
2024-02-02 10:01:49.050802278 +0900 test: {"message":"sample"}
2024-02-02 10:01:50.052568659 +0900 test: {"message":"sample"}
...
</code></pre>
<h2 id="まとめ">まとめ</h2>
<p>今回は、Fluentdを手元で動かしてみる方法について、RPMパッケージ、DEBパッケージ、MSI(Microsoft Windows Installer)を使ってパッケージ(Fluent Package)をインストールする方法と、ソースコードから起動する方法の2種類を説明しました。
Fluentdに興味がある方は、ぜひ実際に手元で動かしてみてください!</p>
<p>また、Fluentdコミュニティーでは、日本語用のQ&Aも用意しています。
何か疑問や困ったことがあれば、こちらで質問してみてください!</p>
<ul>
<li><a href="https://github.com/fluent/fluentd/discussions/categories/q-a-japanese">https://github.com/fluent/fluentd/discussions/categories/q-a-japanese</a></li>
</ul>
<p>最後に、クリアコードは<a href="/services/fluentd-service.html">Fluentdのサポートサービス</a>を行っています。
Fluent Packageへのアップデート支援や影響のあるバグ・脆弱性のレポートなどのサポートをいたしますので、詳しくは<a href="/services/fluentd-service.html">Fluentdのサポートサービス</a>をご覧いただき、<a href="/contact/">お問い合わせフォーム</a>よりお気軽にお問い合わせください。</p>
<section class="footnotes">
<ol>
<li id="fn1">
<p>パッケージには実行に必要なRubyなどが組み込まれているため、インストールするだけですぐに動かすことができます。GitやRuby環境を用意するのが大変、という方はパッケージで動かすのがおすすめです。一方で、Fluentdやそのプラグインの開発にも興味がある方は、「ソースコードから動かす」の方がおすすめです。 <a href="#fnref1" class="footnote-backref">↩</a></p>
</li>
<li id="fn2">
<p>Fluentd自身の動作ログ: Fluentdがログを扱うソフトウェアなのでややこしいですが、Fluentd自身も動作ログを出力します。Fluentdが収集したり転送したりするログと、Fluentd自身の動作ログとを混同しないように気をつけてください! <a href="#fnref2" class="footnote-backref">↩</a></p>
</li>
<li id="fn3">
<p>Windowsにおけるログファイル: Windowsではプロセス毎に別のログファイルに出力する形になります。 <a href="#fnref3" class="footnote-backref">↩</a></p>
</li>
<li id="fn4">
<p>Inputプラグイン: あるデータソースからデータを収集する処理を行うプラグインです。慣習として<code>in_</code>というプレフィックスを付けて呼ぶことが多いです。<code>source</code>設定として設定します。 <a href="#fnref4" class="footnote-backref">↩</a></p>
</li>
<li id="fn5">
<p>Outputプラグイン: Inputプラグインが収集したデータなどを他のデータソースへ出力するプラグインです。慣習として<code>out_</code>といプレフィックスを付けて呼ぶことが多いです。<code>match</code>設定として設定します。 <a href="#fnref5" class="footnote-backref">↩</a></p>
</li>
</ol>
</section>
-
dbus-sendを利用して既存のFirefoxプロセスでサイトを開く方法
https://www.clear-code.com/blog/2024/2/7/firefox-openurl-via-dbus-send.html
2024-02-07T00:00:00+09:00
Firefoxのプロセスが既に起動している場合、新たにFirefoxを起動しようとすると、既に起動している方のプロセスにてコンテンツが表示されます。
その一方で、同一のプロファイルを指定してFirefoxを追加で起動しようと試みた場合など、そのままでは既に起動しているプロセスにてコンテンツを開かせることができない場合もあります。
すでにFirefoxが起動中だが、応答しない旨のエラーメッセージが表示され、Firefoxを終了し別プロファイルを利用するようにうながされます。
このような挙動になるのは、プロファイルを保護するためにロックがかけられている状態になっているためです。
今回は、GNU/Linux環境下においてそのような場合でも既存のプロセスでタブを開けるように、dbus-sendをどのように利用するとよいかを説明します。
FirefoxのDBusインターフェースについて
Firefoxでは、DBus経由でアクセスできるインターフェースとしてOpenURLが公開されています。
static const char* introspect_template =
"<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection "
"1.0//EN\"\n"
"\"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">\n"
"<node>\n"
" <interface name=\"org.mozilla.%s\">\n"
" <method name=\"OpenURL\">\n"
" <arg name=\"url\" direction=\"in\" type=\"ay\"/>\n"
" </method>\n"
" </interface>\n"
"</node>\n";
上記はインターフェースに関するIntrospection結果を返すテンプレートの定義ですが、OpenURLは引数にtype=ay - つまりバイト列を受け取る仕様であることがわかります。
では、バイト列はどのように渡せばよいのでしょうか。
それについては、渡されたバイト列をコマンドライン引数として処理するnsUnixRemoteServer::HandleCommandLineにて次のように言及されています。
// the commandline property is constructed as an array of int32_t
// followed by a series of null-terminated strings:
//
// [argc][offsetargv0][offsetargv1...]<workingdir>\0<argv[0]>\0argv[1]...\0
// (offset is from the beginning of the buffer)
ざっくり説明すると次のとおりです。
argc 引数の個数を指定する (4byte)
offsetargv0 argv0へのオフセットを指定する (4byte)
offsetargvN argvNへのオフセットを指定する (4byte)
workingdir 作業ディレクトリのパスを指定する
argv[0] 0番目の引数を指定する
argv[N] N番目の引数を指定する
argcで指定した個数だけオフセットが並び、NULL終端の作業ディレクトリと後続の引数が続く仕様であることがわかります。
なお、argv[0]は無視されるので、与えるURLはargv[1]以降に指定しなければなりません。
URLを1つだけ渡す次のような例を考えてみましょう。
workingdirをNULLとする(\0)
argv[0]をNULLとする(\0)
URLに https://www.clear-code.com を指定する
この場合のargcとそのオフセット値は次のとおりです。
argc: 2
offsetargv0: 13 (argcとoffsetargv0,offsetargv1で4*3バイト、workingdirの\0を加味するのでargv0へのオフセットは先頭から13バイト)
offsetargv1: 14 (offsetargv0に加えて、argv0の\0を加味するのでargv1へのオフセットは14バイト)
最終的なバイト列は次のようになります。(途中で改行していますが実際には1行)
0x02,0x00,0x00,0x00,
0x0d,0x00,0x00,0x00,
0x0e,0x00,0x00,0x00,
0x00,
0x00,
0x68,0x74,0x74,0x70,0x73,0x3a,0x2f,0x2f,0x77,0x77,0x77,0x2e,0x63,0x6c,0x65,0x61,0x72,0x2d,0x63,0x6f,0x64,0x65,0x2e,0x63,0x6f,0x6d,0x00
FirefoxのDBusインターフェースの宛先を知るには
どのようなバイト列を送れば受け付けてもらえるかがわかりましたが、宛先がわかっていません。
この宛先は、次のようにして調べることができます。
Firefoxのセッションバスを指定したうえで、次のコマンドを実行します。
dbus-send --session --dest=org.freedesktop.DBus --type=method_call \
--print-reply /org/freedesktop/DBus org.freedesktop.DBus.ListNames
すると、次のような応答が含まれていることがわかります。
...
string ":1.8"
string "org.freedesktop.portal.Desktop"
string ":1.9"
string "org.mozilla.firefox.ZGVmYXVsdC1yZWxlYXNl"
string "org.gtk.vfs.UDisks2VolumeMonitor"
string "org.gtk.vfs.mountpoint_4104"
string "org.a11y.Bus"
...
したがって、dbus-sendを利用してOpenURLを実行するには次のようなコマンドを実行1します。
dbus-send --session \
--dest=org.mozilla.firefox.cHJvZmlsZS1lc3IxMjI_ \
--type=method_call \
--print-reply \
/org/mozilla/firefox/Remote \
org.mozilla.firefox.OpenURL array:byte:(バイト列)
これで、既存のFirefoxプロセスでタブを開かせることができるようになります。
ここまでの内容をスクリプトとしてまとめると次のようになります。
引数に開きたいURLを指定して実行することを想定しています。
#!/bin/bash
# 古いPID(親プロセスIDを取得する)
PID=$(pidof firefox | rev | cut -d' ' -f1 | rev)
# PIDをもとにしてBUSアドレスを取得する
eval `strings /proc/$PID/environ | grep DBUS_SESSION_BUS_ADDRESS`
export DBUS_SESSION_BUS_ADDRESS=$DBUS_SESSION_BUS_ADDRESS
env | grep DBUS
# string "org.mozilla.firefox.xxx"からBUSアドレス部分を抜き出す
DEST=$(dbus-send --session --dest=org.freedesktop.DBus --type=method_call --print-reply /org/freedesktop/DBus org.freedesktop.DBus.ListNames | grep firefox | sed -e 's/^ *\| *$//' | cut -d' ' -f2 | sed -e 's/"//g')
echo $DEST
# 引数として与えられたURLをバイト列に変換する
BYTES="0x02,0x00,0x00,0x00" # argc
BYTES="$BYTES,0x0d,0x00,0x00,0x00" # offset to argv[0] (4 + 4 * 2 + 1) = 13 (0x0d)
BYTES="$BYTES,0x0e,0x00,0x00,0x00" # offset to argv[1] (4 + 4 * 2 + 1 + 1) = 14 (0x0e)
BYTES="$BYTES,0x00" # empty workingdir = \0
BYTES="$BYTES,0x00" # empty argv[0] = \0
# 例) odで1バイトずつ1行で表示できる。
# echo -n https://www.clear-code.com | od --format x1 --width=2048
# 0000000 68 74 74 70 73 3a 2f 2f 77 77 77 2e 63 6c 65 61 72 2d 63 6f 64 65 2e 63 6f 6d
# 0000032
# あとはこの出力から左端のオフセット表示を除去する
for b in `echo -n $1 | od --format x1 --width=2048 | head -n 1 | cut -d' ' -f2-`; do
BYTES="$BYTES,0x$b"
done
BYTES="$BYTES,0x00" # \0
echo $BYTES
dbus-send --session \
--dest=$DEST \
--type=method_call \
--print-reply \
/org/mozilla/firefox/Remote \
org.mozilla.firefox.OpenURL array:byte:$BYTES
dbus-sendではarray:byte:と型指定をすることでバイト列を送信できます。
これをシェルスクリプトとして保存しておけば、任意のサイトを指定して実行することで、dbus-sendを利用してコンテンツを既存のFirefoxプロセスで開けます。
おわりに
今回は、dbus-sendを利用して起動済みのFirefoxのプロセスで特定のサイトを開く方法を紹介しました。
クリアコードでは、お客さまからの技術的なご質問・ご依頼に有償にて対応するFirefoxサポートサービスを提
供しています。企業内でのFirefoxの運用でお困りの情シスご担当者さまやベンダーさまは、お問い合わせフォームよりお
問い合わせください。
--destオプションに渡す値はFirefoxを起動するごとに変化します。 ↩
<p>Firefoxのプロセスが既に起動している場合、新たにFirefoxを起動しようとすると、既に起動している方のプロセスにてコンテンツが表示されます。</p>
<p>その一方で、同一のプロファイルを指定してFirefoxを追加で起動しようと試みた場合など、そのままでは既に起動しているプロセスにてコンテンツを開かせることができない場合もあります。
すでにFirefoxが起動中だが、応答しない旨のエラーメッセージが表示され、Firefoxを終了し別プロファイルを利用するようにうながされます。
このような挙動になるのは、プロファイルを保護するためにロックがかけられている状態になっているためです。</p>
<p>今回は、GNU/Linux環境下においてそのような場合でも既存のプロセスでタブを開けるように、<code>dbus-send</code>をどのように利用するとよいかを説明します。</p>
<!--more-->
<h2 id="firefoxのdbusインターフェースについて">FirefoxのDBusインターフェースについて</h2>
<p>Firefoxでは、DBus経由でアクセスできるインターフェースとして<a href="https://searchfox.org/mozilla-central/rev/76cb3efe3b19e649bf675bb6ec5d4af8109b9771/toolkit/components/remote/nsDBusRemoteServer.cpp#26-37">OpenURLが公開</a>されています。</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">static</span> <span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">introspect_template</span> <span class="o">=</span>
<span class="s">"<!DOCTYPE node PUBLIC </span><span class="se">\"</span><span class="s">-//freedesktop//DTD D-BUS Object Introspection "</span>
<span class="s">"1.0//EN</span><span class="se">\"\n</span><span class="s">"</span>
<span class="s">"</span><span class="se">\"</span><span class="s">http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd</span><span class="se">\"</span><span class="s">></span><span class="se">\n</span><span class="s">"</span>
<span class="s">"<node></span><span class="se">\n</span><span class="s">"</span>
<span class="s">" <interface name=</span><span class="se">\"</span><span class="s">org.mozilla.%s</span><span class="se">\"</span><span class="s">></span><span class="se">\n</span><span class="s">"</span>
<span class="s">" <method name=</span><span class="se">\"</span><span class="s">OpenURL</span><span class="se">\"</span><span class="s">></span><span class="se">\n</span><span class="s">"</span>
<span class="s">" <arg name=</span><span class="se">\"</span><span class="s">url</span><span class="se">\"</span><span class="s"> direction=</span><span class="se">\"</span><span class="s">in</span><span class="se">\"</span><span class="s"> type=</span><span class="se">\"</span><span class="s">ay</span><span class="se">\"</span><span class="s">/></span><span class="se">\n</span><span class="s">"</span>
<span class="s">" </method></span><span class="se">\n</span><span class="s">"</span>
<span class="s">" </interface></span><span class="se">\n</span><span class="s">"</span>
<span class="s">"</node></span><span class="se">\n</span><span class="s">"</span><span class="p">;</span>
</code></pre></div></div>
<p>上記はインターフェースに関するIntrospection結果を返すテンプレートの定義ですが、OpenURLは引数に<code>type=ay</code> - つまりバイト列を受け取る仕様であることがわかります。</p>
<p>では、バイト列はどのように渡せばよいのでしょうか。
それについては、渡されたバイト列をコマンドライン引数として処理する<a href="https://searchfox.org/mozilla-central/rev/76cb3efe3b19e649bf675bb6ec5d4af8109b9771/toolkit/components/remote/nsUnixRemoteServer.cpp#66-70">nsUnixRemoteServer::HandleCommandLine</a>にて次のように言及されています。</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="c1">// the commandline property is constructed as an array of int32_t</span>
<span class="c1">// followed by a series of null-terminated strings:</span>
<span class="c1">//</span>
<span class="c1">// [argc][offsetargv0][offsetargv1...]<workingdir>\0<argv[0]>\0argv[1]...\0</span>
<span class="c1">// (offset is from the beginning of the buffer)</span>
</code></pre></div></div>
<p>ざっくり説明すると次のとおりです。</p>
<ul>
<li>argc 引数の個数を指定する (4byte)</li>
<li>offsetargv0 argv0へのオフセットを指定する (4byte)</li>
<li>offsetargvN argvNへのオフセットを指定する (4byte)</li>
<li>workingdir 作業ディレクトリのパスを指定する</li>
<li>argv[0] 0番目の引数を指定する</li>
<li>argv[N] N番目の引数を指定する</li>
</ul>
<p>argcで指定した個数だけオフセットが並び、NULL終端の作業ディレクトリと後続の引数が続く仕様であることがわかります。</p>
<p>なお、<a href="https://searchfox.org/mozilla-central/rev/76cb3efe3b19e649bf675bb6ec5d4af8109b9771/toolkit/components/commandlines/nsCommandLine.cpp#372">argv[0]は無視される</a>ので、与えるURLはargv[1]以降に指定しなければなりません。</p>
<p>URLを1つだけ渡す次のような例を考えてみましょう。</p>
<ul>
<li>workingdirをNULLとする(\0)</li>
<li>argv[0]をNULLとする(\0)</li>
<li>URLに <a href="https://www.clear-code.com">https://www.clear-code.com</a> を指定する</li>
</ul>
<p>この場合のargcとそのオフセット値は次のとおりです。</p>
<ul>
<li>argc: 2</li>
<li>offsetargv0: 13 (argcとoffsetargv0,offsetargv1で4*3バイト、workingdirの\0を加味するのでargv0へのオフセットは先頭から13バイト)</li>
<li>offsetargv1: 14 (offsetargv0に加えて、argv0の\0を加味するのでargv1へのオフセットは14バイト)</li>
</ul>
<p>最終的なバイト列は次のようになります。(途中で改行していますが実際には1行)</p>
<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>0x02,0x00,0x00,0x00,
0x0d,0x00,0x00,0x00,
0x0e,0x00,0x00,0x00,
0x00,
0x00,
0x68,0x74,0x74,0x70,0x73,0x3a,0x2f,0x2f,0x77,0x77,0x77,0x2e,0x63,0x6c,0x65,0x61,0x72,0x2d,0x63,0x6f,0x64,0x65,0x2e,0x63,0x6f,0x6d,0x00
</code></pre></div></div>
<h2 id="firefoxのdbusインターフェースの宛先を知るには">FirefoxのDBusインターフェースの宛先を知るには</h2>
<p>どのようなバイト列を送れば受け付けてもらえるかがわかりましたが、宛先がわかっていません。
この宛先は、次のようにして調べることができます。</p>
<p>Firefoxのセッションバスを指定したうえで、次のコマンドを実行します。</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>dbus-send <span class="nt">--session</span> <span class="nt">--dest</span><span class="o">=</span>org.freedesktop.DBus <span class="nt">--type</span><span class="o">=</span>method_call <span class="se">\</span>
<span class="nt">--print-reply</span> /org/freedesktop/DBus org.freedesktop.DBus.ListNames
</code></pre></div></div>
<p>すると、次のような応答が含まれていることがわかります。</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">...
</span><span class="go">string ":1.8"
string "org.freedesktop.portal.Desktop"
string ":1.9"
string "org.mozilla.firefox.ZGVmYXVsdC1yZWxlYXNl"
string "org.gtk.vfs.UDisks2VolumeMonitor"
string "org.gtk.vfs.mountpoint_4104"
string "org.a11y.Bus"
</span><span class="c">...
</span></code></pre></div></div>
<p>したがって、dbus-sendを利用してOpenURLを実行するには次のようなコマンドを実行<sup class="footnote-ref"><a href="#fn1" id="fnref1">1</a></sup>します。</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>dbus-send <span class="nt">--session</span> <span class="se">\</span>
<span class="nt">--dest</span><span class="o">=</span>org.mozilla.firefox.cHJvZmlsZS1lc3IxMjI_ <span class="se">\</span>
<span class="nt">--type</span><span class="o">=</span>method_call <span class="se">\</span>
<span class="nt">--print-reply</span> <span class="se">\</span>
/org/mozilla/firefox/Remote <span class="se">\</span>
org.mozilla.firefox.OpenURL array:byte:<span class="o">(</span>バイト列<span class="o">)</span>
</code></pre></div></div>
<p>これで、既存のFirefoxプロセスでタブを開かせることができるようになります。</p>
<p>ここまでの内容をスクリプトとしてまとめると次のようになります。
引数に開きたいURLを指定して実行することを想定しています。</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/bin/bash</span>
<span class="c"># 古いPID(親プロセスIDを取得する)</span>
<span class="nv">PID</span><span class="o">=</span><span class="si">$(</span>pidof firefox | rev | <span class="nb">cut</span> <span class="nt">-d</span><span class="s1">' '</span> <span class="nt">-f1</span> | rev<span class="si">)</span>
<span class="c"># PIDをもとにしてBUSアドレスを取得する</span>
<span class="nb">eval</span> <span class="sb">`</span>strings /proc/<span class="nv">$PID</span>/environ | <span class="nb">grep </span>DBUS_SESSION_BUS_ADDRESS<span class="sb">`</span>
<span class="nb">export </span><span class="nv">DBUS_SESSION_BUS_ADDRESS</span><span class="o">=</span><span class="nv">$DBUS_SESSION_BUS_ADDRESS</span>
<span class="nb">env</span> | <span class="nb">grep </span>DBUS
<span class="c"># string "org.mozilla.firefox.xxx"からBUSアドレス部分を抜き出す</span>
<span class="nv">DEST</span><span class="o">=</span><span class="si">$(</span>dbus-send <span class="nt">--session</span> <span class="nt">--dest</span><span class="o">=</span>org.freedesktop.DBus <span class="nt">--type</span><span class="o">=</span>method_call <span class="nt">--print-reply</span> /org/freedesktop/DBus org.freedesktop.DBus.ListNames | <span class="nb">grep </span>firefox | <span class="nb">sed</span> <span class="nt">-e</span> <span class="s1">'s/^ *\| *$//'</span> | <span class="nb">cut</span> <span class="nt">-d</span><span class="s1">' '</span> <span class="nt">-f2</span> | <span class="nb">sed</span> <span class="nt">-e</span> <span class="s1">'s/"//g'</span><span class="si">)</span>
<span class="nb">echo</span> <span class="nv">$DEST</span>
<span class="c"># 引数として与えられたURLをバイト列に変換する</span>
<span class="nv">BYTES</span><span class="o">=</span><span class="s2">"0x02,0x00,0x00,0x00"</span> <span class="c"># argc</span>
<span class="nv">BYTES</span><span class="o">=</span><span class="s2">"</span><span class="nv">$BYTES</span><span class="s2">,0x0d,0x00,0x00,0x00"</span> <span class="c"># offset to argv[0] (4 + 4 * 2 + 1) = 13 (0x0d)</span>
<span class="nv">BYTES</span><span class="o">=</span><span class="s2">"</span><span class="nv">$BYTES</span><span class="s2">,0x0e,0x00,0x00,0x00"</span> <span class="c"># offset to argv[1] (4 + 4 * 2 + 1 + 1) = 14 (0x0e)</span>
<span class="nv">BYTES</span><span class="o">=</span><span class="s2">"</span><span class="nv">$BYTES</span><span class="s2">,0x00"</span> <span class="c"># empty workingdir = \0</span>
<span class="nv">BYTES</span><span class="o">=</span><span class="s2">"</span><span class="nv">$BYTES</span><span class="s2">,0x00"</span> <span class="c"># empty argv[0] = \0</span>
<span class="c"># 例) odで1バイトずつ1行で表示できる。</span>
<span class="c"># echo -n https://www.clear-code.com | od --format x1 --width=2048</span>
<span class="c"># 0000000 68 74 74 70 73 3a 2f 2f 77 77 77 2e 63 6c 65 61 72 2d 63 6f 64 65 2e 63 6f 6d</span>
<span class="c"># 0000032</span>
<span class="c"># あとはこの出力から左端のオフセット表示を除去する</span>
<span class="k">for </span>b <span class="k">in</span> <span class="sb">`</span><span class="nb">echo</span> <span class="nt">-n</span> <span class="nv">$1</span> | <span class="nb">od</span> <span class="nt">--format</span> x1 <span class="nt">--width</span><span class="o">=</span>2048 | <span class="nb">head</span> <span class="nt">-n</span> 1 | <span class="nb">cut</span> <span class="nt">-d</span><span class="s1">' '</span> <span class="nt">-f2-</span><span class="sb">`</span><span class="p">;</span> <span class="k">do
</span><span class="nv">BYTES</span><span class="o">=</span><span class="s2">"</span><span class="nv">$BYTES</span><span class="s2">,0x</span><span class="nv">$b</span><span class="s2">"</span>
<span class="k">done
</span><span class="nv">BYTES</span><span class="o">=</span><span class="s2">"</span><span class="nv">$BYTES</span><span class="s2">,0x00"</span> <span class="c"># \0</span>
<span class="nb">echo</span> <span class="nv">$BYTES</span>
dbus-send <span class="nt">--session</span> <span class="se">\</span>
<span class="nt">--dest</span><span class="o">=</span><span class="nv">$DEST</span> <span class="se">\</span>
<span class="nt">--type</span><span class="o">=</span>method_call <span class="se">\</span>
<span class="nt">--print-reply</span> <span class="se">\</span>
/org/mozilla/firefox/Remote <span class="se">\</span>
org.mozilla.firefox.OpenURL array:byte:<span class="nv">$BYTES</span>
</code></pre></div></div>
<p><code>dbus-send</code>では<code>array:byte:</code>と型指定をすることでバイト列を送信できます。
これをシェルスクリプトとして保存しておけば、任意のサイトを指定して実行することで、dbus-sendを利用してコンテンツを既存のFirefoxプロセスで開けます。</p>
<h3 id="おわりに">おわりに</h3>
<p>今回は、<code>dbus-send</code>を利用して起動済みのFirefoxのプロセスで特定のサイトを開く方法を紹介しました。</p>
<p>クリアコードでは、お客さまからの技術的なご質問・ご依頼に有償にて対応する<a href="/services/mozilla/menu.html">Firefoxサポートサービス</a>を提
供しています。企業内でのFirefoxの運用でお困りの情シスご担当者さまやベンダーさまは、<a href="/contact/">お問い合わせフォーム</a>よりお
問い合わせください。</p>
<section class="footnotes">
<ol>
<li id="fn1">
<p>--destオプションに渡す値はFirefoxを起動するごとに変化します。 <a href="#fnref1" class="footnote-backref">↩</a></p>
</li>
</ol>
</section>
-
フィヨルドブートキャンプ様とOSS Gateワークショップをコラボ実施しました!
https://www.clear-code.com/blog/2024/2/2/ossgate-workshop-with-fjord-boot-camp.html
2024-02-02T00:00:00+09:00
OSS Gateのワークショップの企画・実施をここ1年ほど担当している福田です。
OSS Gateは、OSS(オープンソースソフトウェア)開発に参加する「入り口」を提供する取り組みです。
フリーソフトウェアを推進する活動の一貫として、クリアコードはOSS Gateの活動に参加しています。
普段から定期的にワークショップを開催して、参加する方々にOSS開発を体験してもらっています。
今回、フィヨルドブートキャンプ様から素敵なコラボ企画をいただき、OSS Gateオンラインワークショップ - フィヨルドブートキャンプ特別版を2023年9月30日に実施しました。
この記事では、このコラボ企画について紹介します。
OSS Gateとクリアコード
OSS Gateは、OSS(オープンソースソフトウェア)開発に参加する「入り口」を提供する取り組みです。
普段から定期的にワークショップを開催しています。
ワークショップでは、OSS開発を体験したい参加者(「ビギナー」)を、参加したことのある参加者(「サポーター」)がサポートしつつ、OSS開発を体験してもらいます。
例えば、GitHub上で公開されているOSSについて、issueやPull Requestを作ったりします。
実際にどのようなことをするのかは、ワークショップレポートにまとまっている感想やレポートをご覧ください。
フリーソフトウェア1を推進する活動の一貫として、クリアコードはOSS Gateの活動に参加したり、OSS開発支援サービスを行ったりしています。
フィヨルドブートキャンプ様からいただいたコラボ企画
このコラボ企画については、次のプレスリリースでも紹介しているのでそちらもご覧ください。
OSS Gateオンラインワークショップを1月27日に開催、特別編動画を公開
受講生がOSSに関わる機会を提供する
フィヨルドブートキャンプ様は、プログラミングスクールとして受講生がOSSと関わる機会を提供したいと考えていらっしゃいました。
その目的は次の通りです。
よりプログラミングを楽しむため
OSSプロジェクト、OSS開発者との関わりを持つ
自身もOSSにコントリビュート2している喜びを感じる
レベルの高い広い世界を知るため
社内など閉じた世界ではない、文字通り世界クラスのプログラマーの技術力を知る
しかし、現実的にはOSS開発に関わるような機会に遭遇することが少ない、という課題がありました。
そこで、全ての受講生がOSSやOSSへのコントリビュート方法を学び、体験できる機会として、OSS Gateワークショップを活用することを考えてくださいました。
OSS Gateを盛り上げる
フィヨルドブートキャンプ様は、OSS Gateワークショップをただ利用するのではなく、より盛り上げることも含めて考えてくださいました。
「ただ利用するだけでなく、コントリビュートをする」という姿勢は、OSSにとって本質的なものです。
何か足りないところがあったら自分で改善する、すると自分だけでなく他の人も嬉しい、それがOSSの素晴らしいところの1つです。
OSS Gateワークショップも、多くのOSSと同じように有志の方々の協力で成り立っています。
ワークショップでは、OSS開発を体験したい参加者(「ビギナー」)を、参加したことのある参加者(「サポーター」)がサポートしつつ、OSS開発を体験してもらう形になります。
つまり、ワークショップを行うには、ビギナーだけでなくサポーターとして参加される方々が必要不可欠なわけです。
実際に、参加するサポーターの人数に応じて、参加可能なビギナー数が決まるような仕組みにしています。
多くのサポーターが参加するほど、多くのビギナーが参加できるわけです。
フィヨルドブートキャンプ様は、OSS Gateワークショップを通してOSSに関わる機会として、次の2種類を考えてくださいました。
OSSへのコントリビュートの方法を知る
OSSへのコントリビュートの方法を教える
「1. OSSへのコントリビュートの方法を知る」では、受講生がワークショップにビギナーとして参加し、OSS開発を体験します。
1を終えた受講生は、次に「2. OSSへのコントリビュートの方法を教える」としてワークショップにサポーターとして参加し、ビギナーのサポートを通してOSSと関わります。
2によってサポーターが増えれば、1で参加できるビギナーの人数も増えるわけです。
フィヨルドブートキャンプ様とOSS Gateの双方にとって、とっても嬉しいアイデアです!
コラボ企画: フィヨルドブートキャンプ特別版ワークショップ
以上の内容をスタートするにあたり、そのスタートダッシュとしてOSS Gateオンラインワークショップ - フィヨルドブートキャンプ特別版を企画、実施しました。
この特別版ワークショップでは、多くのフィヨルドブートキャンプ様の受講生の方々にワークショップにご参加いただくことで、その後の継続的な参加の流れを作ることを狙いとしました。
受講生の方々は全国にいらっしゃるので、オンライン形式にしました。
受講生の方々の参加の敷居を下げるため、ビギナー参加者をフィヨルドブートキャンプ様の受講生限定としました。
また、Firefoxやそのプラグイン開発に関わってきたクリアコードの結城洋志さんによる講演「今日から参加できる!OSS開発」をワークショップの前に行いました。
OSSの基本的な事柄やOSS開発に参加することのメリットや楽しさを知ってもらった上で、ワークショップに参加してもらえるようにするためです。
フィヨルドブートキャンプ特別版ワークショップの様子
以上の企画に基づき、OSS Gateオンラインワークショップ - フィヨルドブートキャンプ特別版を2023年9月30日に実施しました。
サポーターとビギナー合わせて20名以上の方々にご参加いただいて、みんなでワイワイとOSSに触れ合うことができました!
講演: 「今日から参加できる!OSS開発」
Firefoxやそのプラグイン開発に関わってきたクリアコードの結城洋志さんによる講演「今日から参加できる!OSS開発」からスタートしました!
講演の動画やスライドを公開しているのでぜひご覧ください!
動画URL https://youtu.be/F2Ey3kRZfQoq
スライド
今日から参加できる!OSS開発
ワークショップ
講演が終わったら、いよいよワークショップ本編の開始です。
最近のオンラインワークショップはOSSであるElementを使っていましたが、今回はフィヨルドブートキャンプ様が普段使われているRemoを使わせていただきました。
バーチャルに配置されている各テーブルにサポーターとビギナーがバランスよく座ってワークショップを進めました。
ワークショップの内容は普段のワークショップと同じです。
身近にあるOSSについて、改めて公式ドキュメントを見ながら触ってみると、コントリビュートできそうなポイントが意外とすぐに見つかる、ということを体験していただけたと思います。
多くのビギナーの方がワークショップの時間内にコントリビュートすることができていました(例えばGitHub上でissueやPull Requestを作る、など)。
ワークショップ中にやりきれなかった方も、後日OSS Gateのチャットで相談したりしつつコントリビュートすることができていました。
みなさんのコントリビュート内容は、 Workshopの成果物2023‐09‐30 にまとまっています。
興味のある方はぜひご覧ください。
また、フィヨルドブートキャンプ様のブログもぜひご覧ください。
OSS Gate と FBC のコラボ企画を開催しました
まとめ
その後の通常のワークショップも、多くの方に参加いただいて盛況です。
OSS Gateオンラインワークショップ2023-12-09
OSS Gateオンラインワークショップ2024-01-27
今年はオフラインのワークショップも混ぜていきたいなと思っています。
興味のある方はぜひ今後のワークショップに参加してみてください!
OSS Gateが今後もフィヨルドブートキャンプ様の受講生にとってOSSに関わる機会となれれば嬉しいです。
また、このようなOSS Gateとのコラボ企画は大歓迎ですので、興味のある方はぜひご連絡をください。
OSS Gateのチャットにご参加いただき、そこでご連絡をいただければと思います。
詳しくはスポンサー募集ページもご覧ください。
また、クリアコードはOSS開発支援サービスも行っていますので、お問い合わせフォームよりお気軽にお問い合わせください。
フリーソフトウェアとは、ユーザーが自由に使えるソフトウェアのことで、自由(な)ソフトウェア、などとも呼ばれます(自由ソフトウェアとは?)。フリーソフトウェアとOSSはほぼ同じソフトウェアを指しますが、考え方が違います(なぜ、オープンソースは自由ソフトウェアの的を外すのか)。クリアコードはフリーソフトウェアの考え方を大事にしていますが、本記事では分かりやすさのために基本的にOSSという言葉を使います。 ↩
コントリビュート: 「貢献」。OSSにおいては、issueやPull Requestを作ったりしてそのOSSの開発に参加(貢献)することを指します。 ↩
<p><a href="https://oss-gate.github.io/">OSS Gate</a>のワークショップの企画・実施をここ1年ほど担当している福田です。</p>
<p>OSS Gateは、OSS(オープンソースソフトウェア)開発に参加する「入り口」を提供する取り組みです。
フリーソフトウェアを推進する活動の一貫として、クリアコードはOSS Gateの活動に参加しています。
普段から定期的にワークショップを開催して、参加する方々にOSS開発を体験してもらっています。</p>
<p>今回、フィヨルドブートキャンプ様から素敵なコラボ企画をいただき、<a href="https://oss-gate.doorkeeper.jp/events/160648">OSS Gateオンラインワークショップ - フィヨルドブートキャンプ特別版</a>を2023年9月30日に実施しました。
この記事では、このコラボ企画について紹介します。</p>
<!--more-->
<h2 id="oss-gateとクリアコード">OSS Gateとクリアコード</h2>
<p><a href="https://oss-gate.github.io/">OSS Gate</a>は、OSS(オープンソースソフトウェア)開発に参加する「入り口」を提供する取り組みです。
普段から定期的にワークショップを開催しています。
ワークショップでは、OSS開発を体験したい参加者(「ビギナー」)を、参加したことのある参加者(「サポーター」)がサポートしつつ、OSS開発を体験してもらいます。
例えば、GitHub上で公開されているOSSについて、issueやPull Requestを作ったりします。
実際にどのようなことをするのかは、<a href="https://oss-gate.github.io/workshop/report.html">ワークショップレポート</a>にまとまっている感想やレポートをご覧ください。</p>
<p>フリーソフトウェア<sup class="footnote-ref"><a href="#fn1" id="fnref1">1</a></sup>を推進する活動の一貫として、クリアコードはOSS Gateの活動に参加したり、<a href="/services/oss-development-support.html">OSS開発支援サービス</a>を行ったりしています。</p>
<h2 id="フィヨルドブートキャンプ様からいただいたコラボ企画">フィヨルドブートキャンプ様からいただいたコラボ企画</h2>
<p>このコラボ企画については、次のプレスリリースでも紹介しているのでそちらもご覧ください。</p>
<ul>
<li><a href="/press-releases/20231226-oss-gate-workshop-video.html">OSS Gateオンラインワークショップを1月27日に開催、特別編動画を公開</a></li>
</ul>
<h3 id="受講生がossに関わる機会を提供する">受講生がOSSに関わる機会を提供する</h3>
<p>フィヨルドブートキャンプ様は、プログラミングスクールとして受講生がOSSと関わる機会を提供したいと考えていらっしゃいました。
その目的は次の通りです。</p>
<ul>
<li>よりプログラミングを楽しむため
<ul>
<li>OSSプロジェクト、OSS開発者との関わりを持つ</li>
<li>自身もOSSにコントリビュート<sup class="footnote-ref"><a href="#fn2" id="fnref2">2</a></sup>している喜びを感じる</li>
</ul>
</li>
<li>レベルの高い広い世界を知るため
<ul>
<li>社内など閉じた世界ではない、文字通り世界クラスのプログラマーの技術力を知る</li>
</ul>
</li>
</ul>
<p>しかし、現実的にはOSS開発に関わるような機会に遭遇することが少ない、という課題がありました。
そこで、全ての受講生がOSSやOSSへのコントリビュート方法を学び、体験できる機会として、OSS Gateワークショップを活用することを考えてくださいました。</p>
<h3 id="oss-gateを盛り上げる">OSS Gateを盛り上げる</h3>
<p>フィヨルドブートキャンプ様は、OSS Gateワークショップをただ利用するのではなく、より盛り上げることも含めて考えてくださいました。</p>
<p>「ただ利用するだけでなく、コントリビュートをする」という姿勢は、OSSにとって本質的なものです。
何か足りないところがあったら自分で改善する、すると自分だけでなく他の人も嬉しい、それがOSSの素晴らしいところの1つです。</p>
<p>OSS Gateワークショップも、多くのOSSと同じように有志の方々の協力で成り立っています。
ワークショップでは、OSS開発を体験したい参加者(「ビギナー」)を、参加したことのある参加者(「サポーター」)がサポートしつつ、OSS開発を体験してもらう形になります。
つまり、ワークショップを行うには、ビギナーだけでなくサポーターとして参加される方々が必要不可欠なわけです。
実際に、参加するサポーターの人数に応じて、参加可能なビギナー数が決まるような仕組みにしています。
多くのサポーターが参加するほど、多くのビギナーが参加できるわけです。</p>
<p>フィヨルドブートキャンプ様は、OSS Gateワークショップを通してOSSに関わる機会として、次の2種類を考えてくださいました。</p>
<ol>
<li>OSSへのコントリビュートの方法を知る</li>
<li>OSSへのコントリビュートの方法を教える</li>
</ol>
<p>「1. OSSへのコントリビュートの方法を知る」では、受講生がワークショップにビギナーとして参加し、OSS開発を体験します。
1を終えた受講生は、次に「2. OSSへのコントリビュートの方法を教える」としてワークショップにサポーターとして参加し、ビギナーのサポートを通してOSSと関わります。</p>
<p>2によってサポーターが増えれば、1で参加できるビギナーの人数も増えるわけです。
フィヨルドブートキャンプ様とOSS Gateの双方にとって、とっても嬉しいアイデアです!</p>
<h3 id="コラボ企画:-フィヨルドブートキャンプ特別版ワークショップ">コラボ企画: フィヨルドブートキャンプ特別版ワークショップ</h3>
<p>以上の内容をスタートするにあたり、そのスタートダッシュとして<a href="https://oss-gate.doorkeeper.jp/events/160648">OSS Gateオンラインワークショップ - フィヨルドブートキャンプ特別版</a>を企画、実施しました。
この特別版ワークショップでは、多くのフィヨルドブートキャンプ様の受講生の方々にワークショップにご参加いただくことで、その後の継続的な参加の流れを作ることを狙いとしました。</p>
<p>受講生の方々は全国にいらっしゃるので、オンライン形式にしました。
受講生の方々の参加の敷居を下げるため、ビギナー参加者をフィヨルドブートキャンプ様の受講生限定としました。
また、Firefoxやそのプラグイン開発に関わってきたクリアコードの結城洋志さんによる講演「今日から参加できる!OSS開発」をワークショップの前に行いました。
OSSの基本的な事柄やOSS開発に参加することのメリットや楽しさを知ってもらった上で、ワークショップに参加してもらえるようにするためです。</p>
<h2 id="フィヨルドブートキャンプ特別版ワークショップの様子">フィヨルドブートキャンプ特別版ワークショップの様子</h2>
<p>以上の企画に基づき、<a href="https://oss-gate.doorkeeper.jp/events/160648">OSS Gateオンラインワークショップ - フィヨルドブートキャンプ特別版</a>を2023年9月30日に実施しました。
サポーターとビギナー合わせて20名以上の方々にご参加いただいて、みんなでワイワイとOSSに触れ合うことができました!</p>
<h3 id="講演:-「今日から参加できる!oss開発」">講演: 「今日から参加できる!OSS開発」</h3>
<p>Firefoxやそのプラグイン開発に関わってきたクリアコードの結城洋志さんによる講演「今日から参加できる!OSS開発」からスタートしました!
講演の動画やスライドを公開しているのでぜひご覧ください!</p>
<p>動画URL <a href="https://youtu.be/F2Ey3kRZfQoq">https://youtu.be/F2Ey3kRZfQoq</a></p>
<div class="youtube">
<iframe width="560"
height="315"
src="https://www.youtube.com/embed/F2Ey3kRZfQo?si=Zkf9j131hF3DkXqU"
title="YouTube video player"
frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
allowfullscreen></iframe>
</div>
<p>スライド</p>
<div class="rabbit-slide">
<iframe src="https://slide.rabbit-shocker.org/authors/Piro/presentation-oss-gate-workshop-fjord-bootcamp-introduction/viewer.html"
width="640" height="524"
frameborder="0"
marginwidth="0"
marginheight="0"
scrolling="no"
style="border: 1px solid #ccc; border-width: 1px 1px 0; margin-bottom: 5px"
allowfullscreen> </iframe>
<div style="margin-bottom: 5px">
<a href="https://slide.rabbit-shocker.org/authors/Piro/presentation-oss-gate-workshop-fjord-bootcamp-introduction/" title="今日から参加できる!OSS開発">今日から参加できる!OSS開発</a>
</div>
</div>
<h3 id="ワークショップ">ワークショップ</h3>
<p>講演が終わったら、いよいよワークショップ本編の開始です。</p>
<p>最近のオンラインワークショップはOSSである<a href="https://element.io/">Element</a>を使っていましたが、今回はフィヨルドブートキャンプ様が普段使われている<a href="https://remo.co">Remo</a>を使わせていただきました。
バーチャルに配置されている各テーブルにサポーターとビギナーがバランスよく座ってワークショップを進めました。</p>
<p>ワークショップの内容は普段のワークショップと同じです。
身近にあるOSSについて、改めて公式ドキュメントを見ながら触ってみると、コントリビュートできそうなポイントが意外とすぐに見つかる、ということを体験していただけたと思います。
多くのビギナーの方がワークショップの時間内にコントリビュートすることができていました(例えばGitHub上でissueやPull Requestを作る、など)。
ワークショップ中にやりきれなかった方も、後日OSS Gateのチャットで相談したりしつつコントリビュートすることができていました。</p>
<p>みなさんのコントリビュート内容は、 <a href="https://github.com/oss-gate/workshop/wiki/Workshop%E3%81%AE%E6%88%90%E6%9E%9C%E7%89%A92023%E2%80%9009%E2%80%9030">Workshopの成果物2023‐09‐30</a> にまとまっています。
興味のある方はぜひご覧ください。</p>
<p>また、フィヨルドブートキャンプ様のブログもぜひご覧ください。</p>
<ul>
<li><a href="https://bootcamp.fjord.jp/articles/91">OSS Gate と FBC のコラボ企画を開催しました</a></li>
</ul>
<h2 id="まとめ">まとめ</h2>
<p>その後の通常のワークショップも、多くの方に参加いただいて盛況です。</p>
<ul>
<li><a href="https://oss-gate.doorkeeper.jp/events/161080">OSS Gateオンラインワークショップ2023-12-09</a></li>
<li><a href="https://oss-gate.doorkeeper.jp/events/161082">OSS Gateオンラインワークショップ2024-01-27</a></li>
</ul>
<p>今年はオフラインのワークショップも混ぜていきたいなと思っています。
興味のある方はぜひ<a href="https://oss-gate.doorkeeper.jp/events/upcoming">今後のワークショップ</a>に参加してみてください!</p>
<p>OSS Gateが今後もフィヨルドブートキャンプ様の受講生にとってOSSに関わる機会となれれば嬉しいです。
また、このようなOSS Gateとのコラボ企画は大歓迎ですので、興味のある方はぜひご連絡をください。
<a href="https://matrix.to/#/#oss-gate:matrix.org">OSS Gateのチャット</a>にご参加いただき、そこでご連絡をいただければと思います。
詳しくは<a href="https://oss-gate.github.io/sponsors/wanted.html">スポンサー募集ページ</a>もご覧ください。</p>
<p>また、クリアコードは<a href="/services/oss-development-support.html">OSS開発支援サービス</a>も行っていますので、<a href="/contact/">お問い合わせフォーム</a>よりお気軽にお問い合わせください。</p>
<section class="footnotes">
<ol>
<li id="fn1">
<p>フリーソフトウェアとは、ユーザーが自由に使えるソフトウェアのことで、自由(な)ソフトウェア、などとも呼ばれます(<a href="https://www.gnu.org/philosophy/free-sw.ja.html">自由ソフトウェアとは?</a>)。フリーソフトウェアとOSSはほぼ同じソフトウェアを指しますが、考え方が違います(<a href="https://www.gnu.org/philosophy/open-source-misses-the-point.html">なぜ、オープンソースは自由ソフトウェアの的を外すのか</a>)。クリアコードはフリーソフトウェアの考え方を大事にしていますが、本記事では分かりやすさのために基本的にOSSという言葉を使います。 <a href="#fnref1" class="footnote-backref">↩</a></p>
</li>
<li id="fn2">
<p>コントリビュート: 「貢献」。OSSにおいては、issueやPull Requestを作ったりしてそのOSSの開発に参加(貢献)することを指します。 <a href="#fnref2" class="footnote-backref">↩</a></p>
</li>
</ol>
</section>
-
Win32 app isolationでアプリケーションを分離する方法
https://www.clear-code.com/blog/2024/1/29/win32-app-isolation.html
2024-01-29T00:00:00+09:00
はじめに
ChronosというChromiumベースの業務ブラウザの開発をしている橋田です。
2023年6月14日、MicrosoftからWin32 app isolationという環境分離技術が発表されました。
今回このWin32 app isolationについて調査したので、使い方について紹介していきたいと思います。
なお、2023年12月現在、Win32 app isolationはOpen Previewであり、使用方法について今後変更される可能性があります。
Win32 app isolationとは
Win32 app isolationは、AppContainerというコンテナ技術を利用し、アプリケーションの権限を制限し、アプリケーションのゼロデイ脆弱性を利用した攻撃などからユーザーを保護することを目的としています。
今回はWin32 app isolationの具体的な使い方について紹介していきます。
Win32 app isolationの実態は、マニフェストによりアクセス権限の制限を行ったMSIXパッケージファイルです。
Win32 app isolationのドキュメントを参照すると、以下の手順でアプリケーションのWin32 app isolation化が可能です。
それぞれの手順について紹介していきます。
環境
Windows 11
Windows insider version >= 25357
Windowsインサイダープログラムに参加
Canaryチャネルを受信
手順
専用のMSIXパッケージツールの準備
MSIXパッケージの作成
署名ファイルの作成
Win32アプリケーションのMSIXパッケージ化
MSIXパッケージのIsolated Win32アプリケーション化
Application Capability Profilerを使用して、必要な権限の設定
デベロッパーモードの設定
プロファイリングの実行
作成されたマニフェストの適用
Windows insider version >= 25357 にアップデート
Windows Insider Programに登録します。
登録にはMicrosoftアカウントが必要なので、Microsoftアカウントを登録しておきます。
Windows Insider Programに登録後、Windowsの[設定] -> [Windows Update] -> [Windows Insider Program]からCanaryチャネルを受信します。
その後Windows Updateを実行するとCanaryチャネルのビルドが適用され、Windows insider version >= 25357になります。
現在のバージョンはwinverコマンドなどで確認できます。
専用のMSIXパッケージツールの準備
Win32 app isolationから、最新のACP-MPT-{version}.zipをダウンロードします。
Win32 app isolation向けにカスタマイズされたMSIX Packaging Toolもその中に含まれているため、そちらをインストールします。
ここで、このMSIX Packaging Toolのバージョンが古い場合があり、その場合、Windows Updateが実行されると自動的に最新バージョンにアップデートされる場合があります。
最新バージョンにアップデートされると、カスタマイズされていない状態になってしまうため、実行時にはMSIX Packaging Toolのバージョンを確認してください。
作業中は一時的にWindows Updateを停止する方が良いかもしれません。
ここでは、ダウンロードしたZIPファイルの展開先をC:\test\ACP-MPT-v0.1.1とします。
MSIXパッケージの作成
証明書の作成、インストール
MSIXパッケージを実行するには、原則として証明書が必要です。
パッケージに署名するのに必要な証明書を作成します。
ここでは、テスト用に自己署名証明書を作成します。
https://learn.microsoft.com/ja-jp/windows/msix/package/create-certificate-package-signing
上記内容に沿ってNew-SelfSignedCertificateを使って証明書を作成し、Export-PfxCertificateで.pfxファイルにエクスポートします。
次に.cerファイルを書き出し、信頼されたユーザーとして証明書をインストールします。
Export-Certificate -cert “Cert:\CurrentUser\My\<証明書番号>” -FilePath “C:\<証明書名>.cer”
書き出した.cerファイルをダブルクリックし、証明書ストアの[証明書をすべての次のストアに配置する]->[参照]->[信頼されたユーザー]を選択し、信頼されたユーザーとして証明書をインポートします。
Win32アプリケーションのMSIXパッケージ化
MSIXパッケージの作成方法に従い、MSIXパッケージを作成します。
OSSのテキストエディターであるNotepad++を例に考えます。
ダウンロードページから最新のインストーラーをダウンロードします。
ここでは、ダウンロードしたインストーラーをC:\test\npp.8.6.Installer.x64.exeに配置します。
MSIXパッケージツールを起動し、アプリケーションパッケージを選択し、新しいパッケージを作成します。
ここでは、「このコンピューターにパッケージを作成する」でパッケージを作成します。
準備の確認が実行されるので、実行完了を待ちます。
パッケージを作成するインストーラーでC:\test\npp.8.6.Installer.x64.exeを指定します。
署名の基本設定で「証明書(.pfx)を使用して署名する」を指定し、上記の「証明書の作成、インストール」で作成した.pfxファイルを指定します。
パッケージ情報を入力します。
インストール先は空で構いません。
次へボタンをクリックすると、インストーラーが実行されます。
動作確認のためにインストールしているため、ここではminimalインストールとしています。
インストーラーが実行されると、初回起動時のタスクの管理画面となります。
ここでアプリケーションのエントリポイント(直接起動可能なアプリケーション)を指定します。
また、実際に起動し、起動後のタスクをキャプチャすることができます。
エントリポイントの指定、キャプチャが完了したら、MSIXパッケージを作成します。
これで、通常のMSIXパッケージが作成できました。
MSIXパッケージのIsolated Win32アプリケーション化
上記のMSIXパッケージのマニフェストを編集し、アプリケーション実行環境を分離(Isolate)します。
Win32 app isolationのマニフェストの編集手順にしたがって編集します。
Packageエレメントにxmlns:previewsecurity2="http://schemas.microsoft.com/appx/manifest/preview/windows10/security/2"を追加
追加した場合、PackageエレメントのIgnorableNamespacesにpreviewsecurity2を追加
Packageエレメントにxmlns:uap10="http://schemas.microsoft.com/appx/manifest/uap/windows10/10"を追加
追加した場合、PackageエレメントのIgnorableNamespacesにuap10を追加
Dependenciesエレメント配下のTargetDeviceFamilyエレメントを<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.25357.0" MaxVersionTested="10.0.25357.0" />に変更
Applicationエレメントのentrypoint、trustlevel、runtimebehaviorをuap10:TrustLevel="appContainer" previewsecurity2:RuntimeBehavior="appSilo"に変更
Capabilitiesエレメント配下の<rescap:Capability name="runFullTrust">が不要なら削除
comServerやFirewallRulesなど、runFullTrust権限が必要なextensionsが存在する場合などに必要
今回のマニフェストを修正すると、以下のようになります。
このマニフェストは、最小の権限の状態になっています。
<?xml version="1.0" encoding="utf-8"?>
<Package xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10" xmlns:previewsecurity2="http://schemas.microsoft.com/appx/manifest/preview/windows10/security/2" xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10" xmlns:uap10="http://schemas.microsoft.com/appx/manifest/uap/windows10/10" xmlns:desktop7="http://schemas.microsoft.com/appx/manifest/desktop/windows10/7" xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities" IgnorableNamespaces="uap uap10 desktop7 rescap previewsecurity2">
<!--Package created by MSIX Packaging Tool version: 1.2023.517.0-->
<Identity Name="MyNotepadPP" Publisher="CN=Contoso Software, O=Contoso Corporation, C=US" Version="1.0.1.0" ProcessorArchitecture="x64" />
<Properties>
<DisplayName>MyNotepadPP</DisplayName>
<PublisherDisplayName>Contoso</PublisherDisplayName>
<Description>None</Description>
<Logo>Assets\StoreLogo.png</Logo>
<uap10:PackageIntegrity>
<uap10:Content Enforcement="on" />
</uap10:PackageIntegrity>
</Properties>
<Resources>
<Resource Language="en-us" />
</Resources>
<Dependencies>
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.25357.0" MaxVersionTested="10.0.25357.0" />
</Dependencies>
<Applications>
<Application Id="NOTEPAD" Executable="VFS\ProgramFilesX64\Notepad++\notepad++.exe" uap10:TrustLevel="appContainer" previewsecurity2:RuntimeBehavior="appSilo">
<uap:VisualElements BackgroundColor="transparent" DisplayName="Notepad++" Square150x150Logo="Assets\NOTEPAD-Square150x150Logo.png" Square44x44Logo="Assets\NOTEPAD-Square44x44Logo.png" Description="Notepad++">
<uap:DefaultTile Wide310x150Logo="Assets\NOTEPAD-Wide310x150Logo.png" Square310x310Logo="Assets\NOTEPAD-Square310x310Logo.png" Square71x71Logo="Assets\NOTEPAD-Square71x71Logo.png" />
</uap:VisualElements>
<Extensions>
<desktop7:Extension Category="windows.shortcut">
<desktop7:Shortcut File="[{Common Programs}]\Notepad++.lnk" Icon="[{Package}]\VFS\ProgramFilesX64\Notepad++\notepad++.exe" />
</desktop7:Extension>
</Extensions>
</Application>
</Applications>
<Capabilities>
</Capabilities>
</Package>
このマニフェストを適用して、MSIXパッケージを再作成します。
※パッケージの作成時、証明書を再度指定する必要があるのに注意してください。
パッケージをインストールし起動すると、設定ファイルが読み込めない、ファイルが保存できないといったファイルアクセス制限がかかっていることがわかります。
この状態から、マニフェストの<Capabilities>に適切な<Capability>を追加して、このアプリケーションに権限を付与していきます。
必要な権限はApplication Capability Profilerを使用して設定します。
Application Capability Profilerを使用して、必要な権限の設定
https://github.com/microsoft/win32-app-isolation/blob/main/docs/profiler/application-capability-profiler.md
上記の手順に従い、作成したMSIXパッケージ(Isolated Win32アプリケーション)に必要な権限を取得します。
デベロッパーモードの設定
Windowsの[設定]->[プライバシーとセキュリティ]->[デベロッパーモード]をONにします。
https://learn.microsoft.com/ja-jp/windows/apps/get-started/enable-your-device-for-development
レジストリーのHKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\AppModelUnlockに1を指定することでもデベロッパーモードにすることができます。
一例として、Powershellを管理者権限で実行し、以下のコマンドを実行します。
PS C:\WINDOWS\system32> reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\AppModelUnlock" /t REG_DWORD /f /v "AllowAllTrustedApps" /d "1"
https://learn.microsoft.com/ja-jp/windows/apps/get-started/developer-mode-features-and-debugging#use-regedit-to-enable-your-device
また、Windows 11 Pro/Enterprise/Educationであれば、グループポリシーから有効にすることも可能です。
https://learn.microsoft.com/ja-jp/windows/apps/get-started/developer-mode-features-and-debugging#use-gpedit-to-enable-your-device
Powershell 7のインストール
プロファイリングにはPowershell 7が必要です。
現在使用しているPowershellのバージョンが7未満の場合、Powershell 7をインストールします。
現在使用しているPowershellのバージョンはGet-Hostコマンドなどで確認できます。
PS C:> Get-Host
Name : ConsoleHost
Version : 7.4.0
InstanceId : 52a98369-bc7b-4543-8b12-014b2c1111f8
UI : System.Management.Automation.Internal.Host.InternalHostUserInterface
CurrentCulture : ja-JP
CurrentUICulture : ja-JP
PrivateData : Microsoft.PowerShell.ConsoleHost+ConsoleColorProxy
DebuggerEnabled : True
IsRunspacePushed : False
Runspace : System.Management.Automation.Runspaces.LocalRunspace
wprコマンドの取得
プロファイリングに必要なため、Windows パフォーマンス レコーダーをインストールします。
Get-Command wpr
プロファイリングの実行
https://github.com/microsoft/win32-app-isolation/blob/main/docs/profiler/application-capability-profiler.md#profiling-step-2-start-profiling
上記の手順に従い、プロファイリングを実行します。
まず、作成済みのパッケージファイルをMSIX パッケージツールで開きます。
マニフェスト開き、内容をローカルのXMLファイルにコピーします。
ここでは、コピー先のファイル名をC:\test\MyNotepadPPManifest.xmlとします。
ACP-MPT-{version}.zipの展開先をC:\test\ACP-MPT-v0.1.1とします。
以下のコマンドを実行し、プロファイリングを開始します。
以下、実行するコマンドの詳細は上記のURLを参照してください。
PS C:\test> cd C:\test\ACP-MPT-v0.1.1\ACP
PS C:\test\ACP-MPT-v0.1.1\ACP> Import-Module .\Microsoft.Windows.Win32Isolation.ApplicationCapabilityProfiler.dll
PS C:\test\ACP-MPT-v0.1.1\ACP> cd C:\test
PS C:\test> Start-Profiling -ManifestPath MyNotepadPPManifest.xml
プロファイリング開始後、MSIXパッケージからインストールしたアプリケーションを起動します。
プロファイリング中はアクセス制限がない状態でアプリケーションが動作します。
この状態でファイルを開いて保存といった、想定される動作シナリオを実行します。
プロファイリング中にアプリケーションで実行されたファイルアクセスなどがキャプチャーされ、プロファイル終了時に結果ファイルに出力されます。
シナリオを実行し終えたらプロファイリングを終了します。
PS C:\test> Stop-Profiling
Press Ctrl+C to cancel the stop operation.
The trace was successfully saved.
Is Developer Mode enabled? Yes.
The request was completed successfully!
The following event trace log file was collected. It can be parsed by Get-ProfilingResults to identify useful capability access information.
C:\test\trace.etl
生のプロファイル結果であるC:\test\trace.etlを利用可能なプロファイル結果に変換します。
PS C:\test> Get-ProfilingResults -EtlFilePaths trace.etl -ManifestPath .\MyNotepadPPManifest.xml
C:\test\trace.etl: parsing file and searching for capabilities...
C:\test\trace.etl: done parsing!
Identifying capabilities that grant the desired access...
Done identifying capabilities!
Writing C:\test\MyNotepadPPManifest.xml...
Done writing C:\test\MyNotepadPPManifest.xml.
Writing C:\test\AccessAttemptRecords.csv...
Done writing C:\test\AccessAttemptRecords.csv.
Writing C:\test\summary.txt...
Done writing C:\test\summary.txt.
2023/12/18 10:31:05
Microsoft.Windows.Win32Isolation.CapabilityAccessParser
Output Guide
Input parsed: C:\test\trace.etl
Target(s): ...
The following files were output.
- C:\test\summary.txt:
Summary of identified capabilities and/or failures for the given execution.
- C:\test\AccessAttemptRecords.csv:
Comma-separated values file containing detailed information on the parsed access attempt events.
The following files contain the capabilities to be manifested by each application package profiled. Please declare them in their corresponding package manifest and reinstall the package.
- C:\test\MyNotepadPPManifest.xml
Troubleshooting Guide
If the application still experiences access and/or compatibility issues after it's packaged and installed with the capabilities identified, consider whether the traces parsed cover all critical application scenarios. If not, consider retrying with traces for all critical application scenarios. Otherwise, please submit the following files to Microsoft for analysis and extension of support.
Troubleshooting files to submit:
C:\test\AccessAttemptRecords.csv
C:\test\trace.etl
The request was completed successfully!
MyNotepadPPManifest.xmlのCapabilitiesがキャプチャーされた結果をもとに更新されています。
この更新後のMyNotepadPPManifest.xmlの内容をMSIXパッケージツールから再適用します。
Win32 app isolationの動作環境について
Win32 app isolationにより分離されたアプリケーションは、アプリケーションコンテナ(AppContainer)で仮想化されたパッケージ上で動作します。
アプリケーションコンテナ上では、ファイルシステムやレジストリが仮想化されています。
https://learn.microsoft.com/ja-jp/windows/msix/desktop/desktop-to-uwp-behind-the-scenes
C:\Program Files\WindowsApps\<package_full_name>\VFSに仮想ファイルシステムフォルダーが作成されます
システム系の
例えばC:\Windows\System32の読み取りはC:\Program Files\WindowsApps\<package_full_name>\VFS\SystemX86とC:\Windows\System32の内容がマージされた内容が読み込まれます
AppDataへの書き込みはC:\Users\<ユーザー名>\AppData配下のパッケージごとの場所に行われます
例えば、今回作成したMyNotepadPPパッケージであればC:\Users\<ユーザー名>\AppData\Local\Packages\MyNotepadPP_<パブリッシャーID>に作成されます。
パッケージ内(C:\Program Files\WindowsApps\<パッケージ>)への書き込みは禁止されています
変更を加えられるのは基本的にはパッケージインストール、更新、アンインストール時のみです
パッケージ外へのアクセスはアクセス許可がある場合のみ許可されます
パッケージの仮想レジストリはregistry.datに記録されています
仮想レジストリはMSIXパッケージ作成時に指定できます
HKLM\Softwareの読み取りはregistry.datの内容とマージされます
HKCUへの書き込みは、C:\Users\<ユーザー名>\AppData配下のパッケージごとの場所に行われます
パッケージ内のレジストリへの書き込みは禁止されています
パッケージ外への書き込みはアクセス権がある場合のみ許可されます
これに加え、Capabilitiesでアプリ機能の宣言をすることで、使用可能な機能を制限できます。
https://learn.microsoft.com/ja-jp/windows/uwp/packaging/app-capability-declarations
Win32 app isolationでは、更に以下のような権限が追加されています。
https://github.com/microsoft/win32-app-isolation/blob/main/docs/packaging/msix-packaging-tool.md#msix---isolated-win32
例えば以下の権限があります。
isolatedWin32-print
印刷の許可
isolatedWin32-sysTrayIcon
システムトレイに通知を表示することを許可
isolatedWin32-promptForAccess
ファイルアクセスをしようとした場合に、ユーザーにアクセスを許可するかどうか尋ねる
isolatedWin32-accessToPublisherDirectory
以下のディレクトリへのアクセスを許可
パブリッシャーIDで終わるネットワーク共有ディレクトリ
\Device\BootDevice\ProgramData(C:\ProgramData)配下にある、パブリッシャーIDで終わるディレクトリ
https://github.com/microsoft/win32-app-isolation/blob/main/docs/fundamentals/consent.md
先の手順の通り、ACPにより必要な権限は自動でプロファイルできます。
例えば、プロファイル中に印刷を実行し、プロファイル結果を作成すれば、isolatedWin32-printが付与されたマニフェストが生成されます。
おわりに
今回、Win32 app isolationの使用方法について紹介しました。
本記事が少しでも皆さんの役に立てば幸いです。
また、クリアコードではこのように、業務で得た知見や成果を公開することを重視しています。
クリアコードで働くことに興味を持たれた方は、クリアコードの採用情報をぜひご覧ください。
<h3 id="はじめに">はじめに</h3>
<p><a href="https://github.com/ThinBridge/Chronos">Chronos</a>というChromiumベースの業務ブラウザの開発をしている橋田です。
2023年6月14日、Microsoftから<a href="https://blogs.windows.com/windowsdeveloper/2023/06/14/public-preview-improve-win32-app-security-via-app-isolation/">Win32 app isolation</a>という環境分離技術が発表されました。</p>
<p>今回このWin32 app isolationについて調査したので、使い方について紹介していきたいと思います。</p>
<p>なお、2023年12月現在、Win32 app isolationはOpen Previewであり、使用方法について今後変更される可能性があります。</p>
<!--more-->
<h3 id="win32-app-isolationとは">Win32 app isolationとは</h3>
<p>Win32 app isolationは、<a href="https://learn.microsoft.com/ja-jp/windows/win32/secauthz/appcontainer-for-legacy-applications-">AppContainer</a>というコンテナ技術を利用し、アプリケーションの権限を制限し、アプリケーションのゼロデイ脆弱性を利用した攻撃などからユーザーを保護することを目的としています。
今回はWin32 app isolationの具体的な使い方について紹介していきます。</p>
<p>Win32 app isolationの実態は、マニフェストによりアクセス権限の制限を行ったMSIXパッケージファイルです。
<a href="https://github.com/microsoft/win32-app-isolation/tree/main/docs">Win32 app isolationのドキュメント</a>を参照すると、以下の手順でアプリケーションのWin32 app isolation化が可能です。
それぞれの手順について紹介していきます。</p>
<ul>
<li>環境
<ul>
<li>Windows 11</li>
<li>Windows insider version >= 25357
<ul>
<li>Windowsインサイダープログラムに参加</li>
<li>Canaryチャネルを受信</li>
</ul>
</li>
</ul>
</li>
<li>手順
<ul>
<li>専用のMSIXパッケージツールの準備</li>
<li>MSIXパッケージの作成
<ul>
<li>署名ファイルの作成</li>
<li>Win32アプリケーションのMSIXパッケージ化</li>
</ul>
</li>
<li>MSIXパッケージのIsolated Win32アプリケーション化</li>
<li>Application Capability Profilerを使用して、必要な権限の設定
<ul>
<li>デベロッパーモードの設定</li>
<li>プロファイリングの実行</li>
<li>作成されたマニフェストの適用</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 id="windows-insider-version->=-25357-にアップデート">Windows insider version >= 25357 にアップデート</h3>
<p><a href="https://www.microsoft.com/ja-jp/windowsinsider/">Windows Insider Program</a>に登録します。
登録にはMicrosoftアカウントが必要なので、Microsoftアカウントを登録しておきます。</p>
<p>Windows Insider Programに登録後、Windowsの[設定] -> [Windows Update] -> [Windows Insider Program]からCanaryチャネルを受信します。
その後Windows Updateを実行するとCanaryチャネルのビルドが適用され、Windows insider version >= 25357になります。
現在のバージョンはwinverコマンドなどで確認できます。</p>
<h3 id="専用のmsixパッケージツールの準備">専用のMSIXパッケージツールの準備</h3>
<p><a href="https://github.com/microsoft/win32-app-isolation/releases">Win32 app isolation</a>から、最新の<code>ACP-MPT-{version}.zip</code>をダウンロードします。
Win32 app isolation向けにカスタマイズされたMSIX Packaging Toolもその中に含まれているため、そちらをインストールします。
ここで、このMSIX Packaging Toolのバージョンが古い場合があり、その場合、Windows Updateが実行されると自動的に最新バージョンにアップデートされる場合があります。
最新バージョンにアップデートされると、カスタマイズされていない状態になってしまうため、実行時にはMSIX Packaging Toolのバージョンを確認してください。</p>
<p>作業中は一時的にWindows Updateを停止する方が良いかもしれません。</p>
<p>ここでは、ダウンロードしたZIPファイルの展開先を<code>C:\test\ACP-MPT-v0.1.1</code>とします。</p>
<h3 id="msixパッケージの作成">MSIXパッケージの作成</h3>
<h4 id="証明書の作成、インストール">証明書の作成、インストール</h4>
<p>MSIXパッケージを実行するには、原則として証明書が必要です。</p>
<p>パッケージに署名するのに必要な証明書を作成します。
ここでは、テスト用に自己署名証明書を作成します。</p>
<p><a href="https://learn.microsoft.com/ja-jp/windows/msix/package/create-certificate-package-signing">https://learn.microsoft.com/ja-jp/windows/msix/package/create-certificate-package-signing</a></p>
<p>上記内容に沿ってNew-SelfSignedCertificateを使って証明書を作成し、Export-PfxCertificateで.pfxファイルにエクスポートします。</p>
<p>次に.cerファイルを書き出し、信頼されたユーザーとして証明書をインストールします。</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Export-Certificate</span><span class="w"> </span><span class="nt">-cert</span><span class="w"> </span><span class="err">“</span><span class="nx">Cert:\CurrentUser\My\</span><span class="err"><証明書番号>”</span><span class="w"> </span><span class="nt">-FilePath</span><span class="w"> </span><span class="err">“</span><span class="nx">C:\</span><span class="err"><証明書名></span><span class="o">.</span><span class="nf">cer</span><span class="err">”</span><span class="w">
</span></code></pre></div></div>
<p>書き出した.cerファイルをダブルクリックし、証明書ストアの[証明書をすべての次のストアに配置する]->[参照]->[信頼されたユーザー]を選択し、信頼されたユーザーとして証明書をインポートします。</p>
<h4 id="win32アプリケーションのmsixパッケージ化">Win32アプリケーションのMSIXパッケージ化</h4>
<p><a href="https://github.com/microsoft/win32-app-isolation/blob/main/docs/packaging/msix-packaging-tool.md#win32---msix">MSIXパッケージの作成方法</a>に従い、MSIXパッケージを作成します。</p>
<p>OSSのテキストエディターである<a href="https://notepad-plus-plus.org/">Notepad++</a>を例に考えます。
<a href="https://notepad-plus-plus.org/downloads/">ダウンロードページ</a>から最新のインストーラーをダウンロードします。</p>
<p>ここでは、ダウンロードしたインストーラーを<code>C:\test\npp.8.6.Installer.x64.exe</code>に配置します。</p>
<p>MSIXパッケージツールを起動し、アプリケーションパッケージを選択し、新しいパッケージを作成します。</p>
<p>ここでは、「このコンピューターにパッケージを作成する」でパッケージを作成します。</p>
<p><img src="/images/blog/win32-app-isolation/msix-environment.png" alt="スクリーンショット:環境" /></p>
<p>準備の確認が実行されるので、実行完了を待ちます。</p>
<p><img src="/images/blog/win32-app-isolation/msix-setup-computer.png" alt="スクリーンショット:コンピュータの準備" /></p>
<p>パッケージを作成するインストーラーで<code>C:\test\npp.8.6.Installer.x64.exe</code>を指定します。</p>
<p>署名の基本設定で「証明書(.pfx)を使用して署名する」を指定し、上記の「証明書の作成、インストール」で作成した.pfxファイルを指定します。</p>
<p><img src="/images/blog/win32-app-isolation/msix-installer.png" alt="スクリーンショット:インストーラーの選択" /></p>
<p>パッケージ情報を入力します。</p>
<p>インストール先は空で構いません。</p>
<p><img src="/images/blog/win32-app-isolation/msix-package-info.png" alt="スクリーンショット:パッケージ情報" /></p>
<p>次へボタンをクリックすると、インストーラーが実行されます。</p>
<p><img src="/images/blog/win32-app-isolation/msix-install-notepadpp.png" alt="スクリーンショット:インストーラー起動" /></p>
<p>動作確認のためにインストールしているため、ここではminimalインストールとしています。</p>
<p><img src="/images/blog/win32-app-isolation/notepadpp-install-minimal.png" alt="スクリーンショット:minimal" /></p>
<p>インストーラーが実行されると、初回起動時のタスクの管理画面となります。
ここでアプリケーションのエントリポイント(直接起動可能なアプリケーション)を指定します。
また、実際に起動し、起動後のタスクをキャプチャすることができます。</p>
<p><img src="/images/blog/win32-app-isolation/msix-entry-point.png" alt="スクリーンショット:エントリーポイント" /></p>
<p>エントリポイントの指定、キャプチャが完了したら、MSIXパッケージを作成します。</p>
<p><img src="/images/blog/win32-app-isolation/msix-create-package.png" alt="スクリーンショット:パッケージの作成" /></p>
<p>これで、通常のMSIXパッケージが作成できました。</p>
<h3 id="msixパッケージのisolated-win32アプリケーション化">MSIXパッケージのIsolated Win32アプリケーション化</h3>
<p>上記のMSIXパッケージのマニフェストを編集し、アプリケーション実行環境を分離(Isolate)します。</p>
<p><a href="https://github.com/microsoft/win32-app-isolation/blob/main/docs/packaging/msix-packaging-tool.md#msix---isolated-win32">Win32 app isolationのマニフェストの編集手順</a>にしたがって編集します。</p>
<ul>
<li><code>Package</code>エレメントに<code>xmlns:previewsecurity2="http://schemas.microsoft.com/appx/manifest/preview/windows10/security/2"</code>を追加
<ul>
<li>追加した場合、<code>Package</code>エレメントの<code>IgnorableNamespaces</code>に<code>previewsecurity2</code>を追加</li>
</ul>
</li>
<li><code>Package</code>エレメントに<code>xmlns:uap10="http://schemas.microsoft.com/appx/manifest/uap/windows10/10"</code>を追加
<ul>
<li>追加した場合、<code>Package</code>エレメントの<code>IgnorableNamespaces</code>に<code>uap10</code>を追加</li>
</ul>
</li>
<li><code>Dependencies</code>エレメント配下の<code>TargetDeviceFamily</code>エレメントを<code><TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.25357.0" MaxVersionTested="10.0.25357.0" /></code>に変更</li>
<li><code>Application</code>エレメントの<code>entrypoint</code>、<code>trustlevel</code>、<code>runtimebehavior</code>を<code>uap10:TrustLevel="appContainer" previewsecurity2:RuntimeBehavior="appSilo"</code>に変更</li>
<li><code>Capabilities</code>エレメント配下の<code><rescap:Capability name="runFullTrust"></code>が不要なら削除
<ul>
<li><code>comServer</code>や<code>FirewallRules</code>など、<code>runFullTrust</code>権限が必要な<code>extensions</code>が存在する場合などに必要</li>
</ul>
</li>
</ul>
<p>今回のマニフェストを修正すると、以下のようになります。
このマニフェストは、最小の権限の状態になっています。</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp"><?xml version="1.0" encoding="utf-8"?></span>
<span class="nt"><Package</span> <span class="na">xmlns=</span><span class="s">"http://schemas.microsoft.com/appx/manifest/foundation/windows10"</span> <span class="na">xmlns:previewsecurity2=</span><span class="s">"http://schemas.microsoft.com/appx/manifest/preview/windows10/security/2"</span> <span class="na">xmlns:uap=</span><span class="s">"http://schemas.microsoft.com/appx/manifest/uap/windows10"</span> <span class="na">xmlns:uap10=</span><span class="s">"http://schemas.microsoft.com/appx/manifest/uap/windows10/10"</span> <span class="na">xmlns:desktop7=</span><span class="s">"http://schemas.microsoft.com/appx/manifest/desktop/windows10/7"</span> <span class="na">xmlns:rescap=</span><span class="s">"http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"</span> <span class="na">IgnorableNamespaces=</span><span class="s">"uap uap10 desktop7 rescap previewsecurity2"</span><span class="nt">></span>
<span class="c"><!--Package created by MSIX Packaging Tool version: 1.2023.517.0--></span>
<span class="nt"><Identity</span> <span class="na">Name=</span><span class="s">"MyNotepadPP"</span> <span class="na">Publisher=</span><span class="s">"CN=Contoso Software, O=Contoso Corporation, C=US"</span> <span class="na">Version=</span><span class="s">"1.0.1.0"</span> <span class="na">ProcessorArchitecture=</span><span class="s">"x64"</span> <span class="nt">/></span>
<span class="nt"><Properties></span>
<span class="nt"><DisplayName></span>MyNotepadPP<span class="nt"></DisplayName></span>
<span class="nt"><PublisherDisplayName></span>Contoso<span class="nt"></PublisherDisplayName></span>
<span class="nt"><Description></span>None<span class="nt"></Description></span>
<span class="nt"><Logo></span>Assets\StoreLogo.png<span class="nt"></Logo></span>
<span class="nt"><uap10:PackageIntegrity></span>
<span class="nt"><uap10:Content</span> <span class="na">Enforcement=</span><span class="s">"on"</span> <span class="nt">/></span>
<span class="nt"></uap10:PackageIntegrity></span>
<span class="nt"></Properties></span>
<span class="nt"><Resources></span>
<span class="nt"><Resource</span> <span class="na">Language=</span><span class="s">"en-us"</span> <span class="nt">/></span>
<span class="nt"></Resources></span>
<span class="nt"><Dependencies></span>
<span class="nt"><TargetDeviceFamily</span> <span class="na">Name=</span><span class="s">"Windows.Desktop"</span> <span class="na">MinVersion=</span><span class="s">"10.0.25357.0"</span> <span class="na">MaxVersionTested=</span><span class="s">"10.0.25357.0"</span> <span class="nt">/></span>
<span class="nt"></Dependencies></span>
<span class="nt"><Applications></span>
<span class="nt"><Application</span> <span class="na">Id=</span><span class="s">"NOTEPAD"</span> <span class="na">Executable=</span><span class="s">"VFS\ProgramFilesX64\Notepad++\notepad++.exe"</span> <span class="na">uap10:TrustLevel=</span><span class="s">"appContainer"</span> <span class="na">previewsecurity2:RuntimeBehavior=</span><span class="s">"appSilo"</span><span class="nt">></span>
<span class="nt"><uap:VisualElements</span> <span class="na">BackgroundColor=</span><span class="s">"transparent"</span> <span class="na">DisplayName=</span><span class="s">"Notepad++"</span> <span class="na">Square150x150Logo=</span><span class="s">"Assets\NOTEPAD-Square150x150Logo.png"</span> <span class="na">Square44x44Logo=</span><span class="s">"Assets\NOTEPAD-Square44x44Logo.png"</span> <span class="na">Description=</span><span class="s">"Notepad++"</span><span class="nt">></span>
<span class="nt"><uap:DefaultTile</span> <span class="na">Wide310x150Logo=</span><span class="s">"Assets\NOTEPAD-Wide310x150Logo.png"</span> <span class="na">Square310x310Logo=</span><span class="s">"Assets\NOTEPAD-Square310x310Logo.png"</span> <span class="na">Square71x71Logo=</span><span class="s">"Assets\NOTEPAD-Square71x71Logo.png"</span> <span class="nt">/></span>
<span class="nt"></uap:VisualElements></span>
<span class="nt"><Extensions></span>
<span class="nt"><desktop7:Extension</span> <span class="na">Category=</span><span class="s">"windows.shortcut"</span><span class="nt">></span>
<span class="nt"><desktop7:Shortcut</span> <span class="na">File=</span><span class="s">"[{Common Programs}]\Notepad++.lnk"</span> <span class="na">Icon=</span><span class="s">"[{Package}]\VFS\ProgramFilesX64\Notepad++\notepad++.exe"</span> <span class="nt">/></span>
<span class="nt"></desktop7:Extension></span>
<span class="nt"></Extensions></span>
<span class="nt"></Application></span>
<span class="nt"></Applications></span>
<span class="nt"><Capabilities></span>
<span class="nt"></Capabilities></span>
<span class="nt"></Package></span>
</code></pre></div></div>
<p>このマニフェストを適用して、MSIXパッケージを再作成します。</p>
<p>※パッケージの作成時、証明書を再度指定する必要があるのに注意してください。</p>
<p>パッケージをインストールし起動すると、設定ファイルが読み込めない、ファイルが保存できないといったファイルアクセス制限がかかっていることがわかります。</p>
<p><img src="/images/blog/win32-app-isolation/notepadpp-load-error.png" alt="スクリーンショット:読み込みエラー" /></p>
<p>この状態から、マニフェストの<code><Capabilities></code>に適切な<code><Capability></code>を追加して、このアプリケーションに権限を付与していきます。
必要な権限はApplication Capability Profilerを使用して設定します。</p>
<h3 id="application-capability-profilerを使用して、必要な権限の設定">Application Capability Profilerを使用して、必要な権限の設定</h3>
<p><a href="https://github.com/microsoft/win32-app-isolation/blob/main/docs/profiler/application-capability-profiler.md">https://github.com/microsoft/win32-app-isolation/blob/main/docs/profiler/application-capability-profiler.md</a></p>
<p>上記の手順に従い、作成したMSIXパッケージ(Isolated Win32アプリケーション)に必要な権限を取得します。</p>
<h4 id="デベロッパーモードの設定">デベロッパーモードの設定</h4>
<p>Windowsの[設定]->[プライバシーとセキュリティ]->[デベロッパーモード]をONにします。</p>
<p><a href="https://learn.microsoft.com/ja-jp/windows/apps/get-started/enable-your-device-for-development">https://learn.microsoft.com/ja-jp/windows/apps/get-started/enable-your-device-for-development</a></p>
<p>レジストリーの<code>HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\AppModelUnlock</code>に1を指定することでもデベロッパーモードにすることができます。
一例として、Powershellを管理者権限で実行し、以下のコマンドを実行します。</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">PS</span><span class="w"> </span><span class="nx">C:\WINDOWS\system32</span><span class="err">></span><span class="w"> </span><span class="nx">reg</span><span class="w"> </span><span class="nx">add</span><span class="w"> </span><span class="s2">"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\AppModelUnlock"</span><span class="w"> </span><span class="nx">/t</span><span class="w"> </span><span class="nx">REG_DWORD</span><span class="w"> </span><span class="nx">/f</span><span class="w"> </span><span class="nx">/v</span><span class="w"> </span><span class="s2">"AllowAllTrustedApps"</span><span class="w"> </span><span class="nx">/d</span><span class="w"> </span><span class="s2">"1"</span><span class="w">
</span></code></pre></div></div>
<p><a href="https://learn.microsoft.com/ja-jp/windows/apps/get-started/developer-mode-features-and-debugging#use-regedit-to-enable-your-device">https://learn.microsoft.com/ja-jp/windows/apps/get-started/developer-mode-features-and-debugging#use-regedit-to-enable-your-device</a></p>
<p>また、Windows 11 Pro/Enterprise/Educationであれば、グループポリシーから有効にすることも可能です。</p>
<p><a href="https://learn.microsoft.com/ja-jp/windows/apps/get-started/developer-mode-features-and-debugging#use-gpedit-to-enable-your-device">https://learn.microsoft.com/ja-jp/windows/apps/get-started/developer-mode-features-and-debugging#use-gpedit-to-enable-your-device</a></p>
<h4 id="powershell-7のインストール">Powershell 7のインストール</h4>
<p>プロファイリングには<a href="https://learn.microsoft.com/ja-jp/powershell/scripting/install/installing-powershell-on-windows?view=powershell-7.4">Powershell 7</a>が必要です。
現在使用しているPowershellのバージョンが7未満の場合、Powershell 7をインストールします。
現在使用しているPowershellのバージョンは<code>Get-Host</code>コマンドなどで確認できます。</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">PS</span><span class="w"> </span><span class="nx">C:</span><span class="err">></span><span class="w"> </span><span class="nx">Get-Host</span><span class="w">
</span><span class="n">Name</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="nx">ConsoleHost</span><span class="w">
</span><span class="n">Version</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="nx">7.4.0</span><span class="w">
</span><span class="n">InstanceId</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="nx">52a98369-bc7b-4543-8b12-014b2c1111f8</span><span class="w">
</span><span class="n">UI</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="nx">System.Management.Automation.Internal.Host.InternalHostUserInterface</span><span class="w">
</span><span class="n">CurrentCulture</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="nx">ja-JP</span><span class="w">
</span><span class="n">CurrentUICulture</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="nx">ja-JP</span><span class="w">
</span><span class="n">PrivateData</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="nx">Microsoft.PowerShell.ConsoleHost</span><span class="o">+</span><span class="nx">ConsoleColorProxy</span><span class="w">
</span><span class="n">DebuggerEnabled</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="nx">True</span><span class="w">
</span><span class="n">IsRunspacePushed</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="nx">False</span><span class="w">
</span><span class="n">Runspace</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="nx">System.Management.Automation.Runspaces.LocalRunspace</span><span class="w">
</span></code></pre></div></div>
<h4 id="wprコマンドの取得">wprコマンドの取得</h4>
<p>プロファイリングに必要なため、<a href="https://learn.microsoft.com/ja-jp/windows-hardware/test/wpt/introduction-to-wpr">Windows パフォーマンス レコーダー</a>をインストールします。</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Get-Command</span><span class="w"> </span><span class="nx">wpr</span><span class="w">
</span></code></pre></div></div>
<h4 id="プロファイリングの実行">プロファイリングの実行</h4>
<p><a href="https://github.com/microsoft/win32-app-isolation/blob/main/docs/profiler/application-capability-profiler.md#profiling-step-2-start-profiling">https://github.com/microsoft/win32-app-isolation/blob/main/docs/profiler/application-capability-profiler.md#profiling-step-2-start-profiling</a></p>
<p>上記の手順に従い、プロファイリングを実行します。</p>
<p>まず、作成済みのパッケージファイルをMSIX パッケージツールで開きます。
マニフェスト開き、内容をローカルのXMLファイルにコピーします。</p>
<p>ここでは、コピー先のファイル名を<code>C:\test\MyNotepadPPManifest.xml</code>とします。</p>
<p><code>ACP-MPT-{version}.zip</code>の展開先を<code>C:\test\ACP-MPT-v0.1.1</code>とします。</p>
<p>以下のコマンドを実行し、プロファイリングを開始します。
以下、実行するコマンドの詳細は上記のURLを参照してください。</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">PS</span><span class="w"> </span><span class="nx">C:\test</span><span class="err">></span><span class="w"> </span><span class="nx">cd</span><span class="w"> </span><span class="nx">C:\test\ACP-MPT-v0.1.1\ACP</span><span class="w">
</span><span class="n">PS</span><span class="w"> </span><span class="nx">C:\test\ACP-MPT-v0.1.1\ACP</span><span class="err">></span><span class="w"> </span><span class="nx">Import-Module</span><span class="w"> </span><span class="o">.</span><span class="nx">\Microsoft.Windows.Win32Isolation.ApplicationCapabilityProfiler.dll</span><span class="w">
</span><span class="n">PS</span><span class="w"> </span><span class="nx">C:\test\ACP-MPT-v0.1.1\ACP</span><span class="err">></span><span class="w"> </span><span class="nx">cd</span><span class="w"> </span><span class="nx">C:\test</span><span class="w">
</span><span class="n">PS</span><span class="w"> </span><span class="nx">C:\test</span><span class="err">></span><span class="w"> </span><span class="nx">Start-Profiling</span><span class="w"> </span><span class="nt">-ManifestPath</span><span class="w"> </span><span class="nx">MyNotepadPPManifest.xml</span><span class="w">
</span></code></pre></div></div>
<p>プロファイリング開始後、MSIXパッケージからインストールしたアプリケーションを起動します。
プロファイリング中はアクセス制限がない状態でアプリケーションが動作します。
この状態でファイルを開いて保存といった、想定される動作シナリオを実行します。
プロファイリング中にアプリケーションで実行されたファイルアクセスなどがキャプチャーされ、プロファイル終了時に結果ファイルに出力されます。</p>
<p>シナリオを実行し終えたらプロファイリングを終了します。</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">PS</span><span class="w"> </span><span class="nx">C:\test</span><span class="err">></span><span class="w"> </span><span class="nx">Stop-Profiling</span><span class="w">
</span><span class="n">Press</span><span class="w"> </span><span class="nx">Ctrl</span><span class="o">+</span><span class="nx">C</span><span class="w"> </span><span class="nx">to</span><span class="w"> </span><span class="nx">cancel</span><span class="w"> </span><span class="nx">the</span><span class="w"> </span><span class="nx">stop</span><span class="w"> </span><span class="nx">operation.</span><span class="w">
</span><span class="n">The</span><span class="w"> </span><span class="nx">trace</span><span class="w"> </span><span class="nx">was</span><span class="w"> </span><span class="nx">successfully</span><span class="w"> </span><span class="nx">saved.</span><span class="w">
</span><span class="n">Is</span><span class="w"> </span><span class="nx">Developer</span><span class="w"> </span><span class="nx">Mode</span><span class="w"> </span><span class="nx">enabled</span><span class="nf">?</span><span class="w"> </span><span class="nx">Yes.</span><span class="w">
</span><span class="nx">The</span><span class="w"> </span><span class="nx">request</span><span class="w"> </span><span class="nx">was</span><span class="w"> </span><span class="nx">completed</span><span class="w"> </span><span class="nx">successfully</span><span class="o">!</span><span class="w">
</span><span class="n">The</span><span class="w"> </span><span class="nx">following</span><span class="w"> </span><span class="nx">event</span><span class="w"> </span><span class="nx">trace</span><span class="w"> </span><span class="nx">log</span><span class="w"> </span><span class="nx">file</span><span class="w"> </span><span class="nx">was</span><span class="w"> </span><span class="nx">collected.</span><span class="w"> </span><span class="nx">It</span><span class="w"> </span><span class="nx">can</span><span class="w"> </span><span class="nx">be</span><span class="w"> </span><span class="nx">parsed</span><span class="w"> </span><span class="nx">by</span><span class="w"> </span><span class="nx">Get-ProfilingResults</span><span class="w"> </span><span class="nx">to</span><span class="w"> </span><span class="nx">identify</span><span class="w"> </span><span class="nx">useful</span><span class="w"> </span><span class="nx">capability</span><span class="w"> </span><span class="nx">access</span><span class="w"> </span><span class="nx">information.</span><span class="w">
</span><span class="n">C:\test\trace.etl</span><span class="w">
</span></code></pre></div></div>
<p>生のプロファイル結果である<code>C:\test\trace.etl</code>を利用可能なプロファイル結果に変換します。</p>
<pre><code>PS C:\test> Get-ProfilingResults -EtlFilePaths trace.etl -ManifestPath .\MyNotepadPPManifest.xml
C:\test\trace.etl: parsing file and searching for capabilities...
C:\test\trace.etl: done parsing!
Identifying capabilities that grant the desired access...
Done identifying capabilities!
Writing C:\test\MyNotepadPPManifest.xml...
Done writing C:\test\MyNotepadPPManifest.xml.
Writing C:\test\AccessAttemptRecords.csv...
Done writing C:\test\AccessAttemptRecords.csv.
Writing C:\test\summary.txt...
Done writing C:\test\summary.txt.
2023/12/18 10:31:05
Microsoft.Windows.Win32Isolation.CapabilityAccessParser
Output Guide
Input parsed: C:\test\trace.etl
Target(s): ...
The following files were output.
- C:\test\summary.txt:
Summary of identified capabilities and/or failures for the given execution.
- C:\test\AccessAttemptRecords.csv:
Comma-separated values file containing detailed information on the parsed access attempt events.
The following files contain the capabilities to be manifested by each application package profiled. Please declare them in their corresponding package manifest and reinstall the package.
- C:\test\MyNotepadPPManifest.xml
Troubleshooting Guide
If the application still experiences access and/or compatibility issues after it's packaged and installed with the capabilities identified, consider whether the traces parsed cover all critical application scenarios. If not, consider retrying with traces for all critical application scenarios. Otherwise, please submit the following files to Microsoft for analysis and extension of support.
Troubleshooting files to submit:
C:\test\AccessAttemptRecords.csv
C:\test\trace.etl
The request was completed successfully!
</code></pre>
<p>MyNotepadPPManifest.xmlの<code>Capabilities</code>がキャプチャーされた結果をもとに更新されています。
この更新後の<code>MyNotepadPPManifest.xml</code>の内容をMSIXパッケージツールから再適用します。</p>
<h3 id="win32-app-isolationの動作環境について">Win32 app isolationの動作環境について</h3>
<p>Win32 app isolationにより分離されたアプリケーションは、アプリケーションコンテナ(<code>AppContainer</code>)で仮想化されたパッケージ上で動作します。
アプリケーションコンテナ上では、ファイルシステムやレジストリが仮想化されています。</p>
<p><a href="https://learn.microsoft.com/ja-jp/windows/msix/desktop/desktop-to-uwp-behind-the-scenes">https://learn.microsoft.com/ja-jp/windows/msix/desktop/desktop-to-uwp-behind-the-scenes</a></p>
<ul>
<li><code>C:\Program Files\WindowsApps\<package_full_name>\VFS</code>に仮想ファイルシステムフォルダーが作成されます
<ul>
<li>システム系の</li>
<li>例えば<code>C:\Windows\System32</code>の読み取りは<code>C:\Program Files\WindowsApps\<package_full_name>\VFS\SystemX86</code>と<code>C:\Windows\System32</code>の内容がマージされた内容が読み込まれます</li>
</ul>
</li>
<li>AppDataへの書き込みは<code>C:\Users\<ユーザー名>\AppData</code>配下のパッケージごとの場所に行われます
<ul>
<li>例えば、今回作成したMyNotepadPPパッケージであれば<code>C:\Users\<ユーザー名>\AppData\Local\Packages\MyNotepadPP_<パブリッシャーID></code>に作成されます。</li>
</ul>
</li>
<li>パッケージ内(<code>C:\Program Files\WindowsApps\<パッケージ></code>)への書き込みは禁止されています
<ul>
<li>変更を加えられるのは基本的にはパッケージインストール、更新、アンインストール時のみです</li>
</ul>
</li>
<li>パッケージ外へのアクセスはアクセス許可がある場合のみ許可されます</li>
<li>パッケージの仮想レジストリは<code>registry.dat</code>に記録されています
<ul>
<li>仮想レジストリはMSIXパッケージ作成時に指定できます</li>
<li><code>HKLM\Software</code>の読み取りは<code>registry.dat</code>の内容とマージされます</li>
<li><code>HKCU</code>への書き込みは、<code>C:\Users\<ユーザー名>\AppData</code>配下のパッケージごとの場所に行われます</li>
<li>パッケージ内のレジストリへの書き込みは禁止されています</li>
<li>パッケージ外への書き込みはアクセス権がある場合のみ許可されます</li>
</ul>
</li>
</ul>
<p>これに加え、<code>Capabilities</code>でアプリ機能の宣言をすることで、使用可能な機能を制限できます。</p>
<p><a href="https://learn.microsoft.com/ja-jp/windows/uwp/packaging/app-capability-declarations">https://learn.microsoft.com/ja-jp/windows/uwp/packaging/app-capability-declarations</a></p>
<p>Win32 app isolationでは、更に以下のような権限が追加されています。</p>
<p><a href="https://github.com/microsoft/win32-app-isolation/blob/main/docs/packaging/msix-packaging-tool.md#msix---isolated-win32">https://github.com/microsoft/win32-app-isolation/blob/main/docs/packaging/msix-packaging-tool.md#msix---isolated-win32</a></p>
<p>例えば以下の権限があります。</p>
<ul>
<li><code>isolatedWin32-print</code>
<ul>
<li>印刷の許可</li>
</ul>
</li>
<li><code>isolatedWin32-sysTrayIcon</code>
<ul>
<li>システムトレイに通知を表示することを許可</li>
</ul>
</li>
<li><code>isolatedWin32-promptForAccess</code>
<ul>
<li>ファイルアクセスをしようとした場合に、ユーザーにアクセスを許可するかどうか尋ねる</li>
</ul>
</li>
<li><code>isolatedWin32-accessToPublisherDirectory</code>
<ul>
<li>以下のディレクトリへのアクセスを許可
<ul>
<li>パブリッシャーIDで終わるネットワーク共有ディレクトリ</li>
<li><code>\Device\BootDevice\ProgramData(C:\ProgramData)</code>配下にある、パブリッシャーIDで終わるディレクトリ</li>
</ul>
</li>
<li><a href="https://github.com/microsoft/win32-app-isolation/blob/main/docs/fundamentals/consent.md">https://github.com/microsoft/win32-app-isolation/blob/main/docs/fundamentals/consent.md</a></li>
</ul>
</li>
</ul>
<p>先の手順の通り、ACPにより必要な権限は自動でプロファイルできます。
例えば、プロファイル中に印刷を実行し、プロファイル結果を作成すれば、<code>isolatedWin32-print</code>が付与されたマニフェストが生成されます。</p>
<h3 id="おわりに">おわりに</h3>
<p>今回、Win32 app isolationの使用方法について紹介しました。
本記事が少しでも皆さんの役に立てば幸いです。</p>
<p>また、クリアコードではこのように、業務で得た知見や成果を公開することを重視しています。
クリアコードで働くことに興味を持たれた方は、<a href="/recruitment/">クリアコードの採用情報</a>をぜひご覧ください。</p>
-
fluent-package v5への更新【Fluentd.org記事翻訳】
https://www.clear-code.com/blog/2024/1/24/upgrade-to-fluent-package-v5-from-v4.html
2024-01-24T00:00:00+09:00
今回の記事は、2023年7月に英語でリリースされたUpgrade to fluent-package v5 1の翻訳記事になります。
2023年8月にリリースされたFluent Package及びFluent Package LTS(Long Term Support)の説明
更新の手順
が含まれています
前段
fluent-package"v5"は、2023年8月から利用可能になっています。fluent-packageは td-agent"v4"の後継として作られました。
この投稿では、開発段階でテストした手順を共有します。これがv4からv5への更新の実践に役立つことを願っています。
パッケージ名称の変更理由
fluent-packageは、以前td-agentとして知られていたものです。昔は、Treasure Data社が、中心となってパッケージの提供を行っていました。現在は、Fluentdのコミュニティでパッケージ提供を行っています。この開発体制の変更が今回のパッケージ名の変更の理由です。
「Fluentdと関連するgemパッケージを含むFluentdのオールインワンパッケージ」を表現するために、パッケージ名がfluent-packageに変更されました。
パッケージの名称は変更されましたが、現在でもTreasure Data社が、引き続きパッケージの配信リソースのスポンサーをしています。
どのチャンネルを使うべきか
fluent-packageには、2種類のチャンネルがあります。
Normal release version = 通常リリース版
Long Term Support version (LTS) = 長期サポート版
一つは、定期的に更新される通常リリースバージョンです。つまり、ラピッドリリース開発スタイルで提供されます。 (td-agent v4 は、ほぼ四半期ごとにこのようにリリースされました)。このバージョンでは、同梱されているfluentdは最終的に新しいマイナーバージョン (例: 1.17.xなど)に更新されます。(そのため、新しい機能が含まれることになります。)
もう一つは、新しい機能が導入されない、より保守的なメンテナンスの行われるバージョン(長期サポート)です。セキュリティ修正やバグ修正などの小さなアップデートのみが適用されます。 v5のLTSは2025年3月までサポートされる予定です。
通常リリース版とLTS版の違いについては、「Fluent Packageに関するサポートライフサイクル予定のお知らせ(※英語)」で詳しく説明しています。
td-agent v4とfluent-package v5の違い
Fluent-package v5では、ruby (2.7.8 -> 3.2.2) や OpenSSL (1.1.1 -> 3.1.0 (Windows の場合)、3.0.8 (macOS の場合)) などのコアコンポーネントが更新されました。
主な変更点は以下の通りです。
コマンド名td-agentがfluentdに変更
$ td-agent --version -> $ fluentd --version
コマンド名td-agent-gemがfluent-gemに変更
$ td-agent-gem list -> $ fluent-gem list
Windows以外のサービス名称td-agentがfluentdに変更
$ systemctl status td-agent -> $ systemctl status fluentd
パッケージ名の変更に伴い、インストールパス、サービス名(/opt/fluent、fluentd.serviceなど)等も変更されました。基本的に、td-agent v4ユーザーに対しては、古いファイルをコピーしたり、シンボリックリンクを提供したりして移行プロセスを実行することで、可能な限り互換性を維持することを目指しました。
アップグレードされたコンポーネントの詳細を知りたい場合は、CHANGELOG.mdを参照してください。
注: プラットフォーム固有の問題については、「v4 ユーザー向けの追加ヒント」セクションで説明します。
アップグレード手順
td-agentにバンドルされているプラグインは、fluent packageにアップグレートを行う際に、自動的にアップグレードされます。ただし、自分で追加した他のプラグインは自動的にアップグレードされません。 v4およびv5のディレクトリ構造の一部が変更されているため、プラグインを別にアップグレードする必要があるかどうかを確認する必要があります。
ここでは、「fluent-plugin-concat」などのプラグインを独自に追加した手順を示します。以下は、手順で使用したサンプル構成ファイルです。
<filter docker.log>
@type concat
key message
multiline_start_regexp /^Start/
</filter>
1. td-agentにどのプラグインが一緒にインストールされているかをレビューする
$ td-agent-gem list | grep fluent-plugin*
fluent-plugin-calyptia-monitoring (0.1.3)
fluent-plugin-concat (2.5.0)
fluent-plugin-elasticsearch (5.3.0)
fluent-plugin-flowcounter-simple (0.1.0)
fluent-plugin-kafka (0.19.0)
fluent-plugin-metrics-cmetrics (0.1.2)
fluent-plugin-opensearch (1.1.0)
fluent-plugin-prometheus (2.0.3)
fluent-plugin-prometheus_pushgateway (0.1.0)
fluent-plugin-record-modifier (2.1.1)
fluent-plugin-rewrite-tag-filter (2.4.0)
fluent-plugin-s3 (1.7.2)
fluent-plugin-sd-dns (0.1.0)
fluent-plugin-systemd (1.0.5)
fluent-plugin-td (1.2.0)
fluent-plugin-utmpx (0.5.0)
fluent-plugin-webhdfs (1.5.0)
/opt/td-agent/lib/ruby/gems/2.7.0/gems/ディレクトリー以下でもインストールされているプラグインを見つけることができます。
$ ls -l /opt/td-agent/lib/ruby/gems/2.7.0/gems |grep fluent-plugin*
drwxr-xr-x. 5 root root 175 7月 14 03:01 fluent-plugin-calyptia-monitoring-0.1.3
drwxr-xr-x. 4 root root 206 7月 14 03:03 fluent-plugin-concat-2.5.0
drwxr-xr-x. 5 root root 4096 7月 14 03:01 fluent-plugin-elasticsearch-5.3.0
drwxr-xr-x. 4 root root 205 7月 14 03:01 fluent-plugin-flowcounter-simple-0.1.0
drwxr-xr-x. 6 root root 191 7月 14 03:01 fluent-plugin-kafka-0.19.0
drwxr-xr-x. 5 root root 190 7月 14 03:01 fluent-plugin-metrics-cmetrics-0.1.2
drwxr-xr-x. 5 root root 4096 7月 14 03:01 fluent-plugin-opensearch-1.1.0
drwxr-xr-x. 5 root root 215 7月 14 03:01 fluent-plugin-prometheus-2.0.3
drwxr-xr-x. 6 root root 238 7月 14 03:01 fluent-plugin-prometheus_pushgateway-0.1.0
drwxr-xr-x. 4 root root 176 7月 14 03:01 fluent-plugin-record-modifier-2.1.1
drwxr-xr-x. 3 root root 210 7月 14 03:01 fluent-plugin-rewrite-tag-filter-2.4.0
drwxr-xr-x. 5 root root 230 7月 14 03:01 fluent-plugin-s3-1.7.2
drwxr-xr-x. 3 root root 170 7月 14 03:01 fluent-plugin-sd-dns-0.1.0
drwxr-xr-x. 3 root root 49 7月 14 03:01 fluent-plugin-systemd-1.0.5
drwxr-xr-x. 5 root root 221 7月 14 03:01 fluent-plugin-td-1.2.0
drwxr-xr-x. 5 root root 166 7月 14 03:01 fluent-plugin-utmpx-0.5.0
drwxr-xr-x. 4 root root 191 7月 14 03:01 fluent-plugin-webhdfs-1.5.0
2. td-agent v4のデーモンを止める
$ sudo systemctl stop td-agent
fluent-packageは、サービスを止めないままの更新をサポートしていますが、ここでは、明確に止めることをおすすめします。
3. fluent-package v5のインストールスクリプトを実行する
RedHat 及び派生のディストリビューションを使っている場合で、fluent-package通常版をインストールしたい場合は以下のスクリプトを実行してください。
# curl -L https://toolbelt.treasuredata.com/sh/install-redhat-fluent-package5.sh | sh
RedHat 及び派生のディストリビューションを使っている場合で、fluent-packageLTS版をインストールしたいなら以下のスクリプトを実行してください。
# curl -L https://toolbelt.treasuredata.com/sh/install-redhat-fluent-package5-lts.sh | sh
もっと細かいインストールについての情報は公式ドキュメントFluend Doc - Installationを参照してください。
4. fluent-package v5がきちんとインストールされていることの確認
$ LANG=C yum info fluent-package
Loaded plugins: fastestmirror
Loading mirror speeds from cached hostfile
* base: ftp.riken.jp
* extras: ftp.riken.jp
* updates: ftp.riken.jp
Installed Packages
Name : fluent-package
Arch : x86_64
Version : 5.0.0
Release : 1.el7
Size : 64 M
Repo : installed
From repo : /fluent-package-5.0.0-1.el7.x86_64
Summary : The stable distribution of Fluentd
URL : https://www.treasuredata.com/
License : ASL 2.0
Description : The stable distribution of Fluentd, called td-agent.
5. fluent-packageのデーモンを再読み込み
$ sudo systemctl daemon-reload
$ sudo systemctl enable --now fluentd
6. インストールされているプラグインの確認
$ fluent-gem list |grep fluent-plugin*
fluent-plugin-calyptia-monitoring (0.1.3)
fluent-plugin-elasticsearch (5.3.0)
fluent-plugin-flowcounter-simple (0.1.0)
fluent-plugin-kafka (0.19.0)
fluent-plugin-metrics-cmetrics (0.1.2)
fluent-plugin-opensearch (1.1.0)
fluent-plugin-prometheus (2.0.3)
fluent-plugin-prometheus_pushgateway (0.1.0)
fluent-plugin-record-modifier (2.1.1)
fluent-plugin-rewrite-tag-filter (2.4.0)
fluent-plugin-s3 (1.7.2)
fluent-plugin-sd-dns (0.1.0)
fluent-plugin-systemd (1.0.5)
fluent-plugin-td (1.2.0)
fluent-plugin-utmpx (0.5.0)
fluent-plugin-webhdfs (1.5.0)
バンドルされているプラグインもアップグレードされていることがわかりますが、独自に追加されたプラグインは見つかりません。今回の例では、"fluent-plugin-concat" が追加されていますが、インストールされているプラグインリストには表示されていません。
7. 個別に追加したプラグインをインストールする
$ sudo fluent-gem install fluent-plugin-concat
$ fluent-gem list | grep fluent-plugin*
fluent-plugin-calyptia-monitoring (0.1.3)
fluent-plugin-concat (2.5.0)
fluent-plugin-elasticsearch (5.3.0)
fluent-plugin-flowcounter-simple (0.1.0)
fluent-plugin-kafka (0.19.0)
fluent-plugin-metrics-cmetrics (0.1.2)
fluent-plugin-opensearch (1.1.0)
fluent-plugin-prometheus (2.0.3)
fluent-plugin-prometheus_pushgateway (0.1.0)
fluent-plugin-record-modifier (2.1.1)
fluent-plugin-rewrite-tag-filter (2.4.0)
fluent-plugin-s3 (1.7.2)
fluent-plugin-sd-dns (0.1.0)
fluent-plugin-systemd (1.0.5)
fluent-plugin-td (1.2.0)
fluent-plugin-utmpx (0.5.0)
fluent-plugin-webhdfs (1.5.0)
fluent-package v5のために、"/opt/fluent/lib/ruby/gems/3.2.0/gems/" ディレクトリー下に"fluent-plugin-concat" がインストールされました。
$ ls -l /opt/fluent/lib/ruby/gems/3.2.0/gems/ |grep fluent-plugin*
drwxr-xr-x. 5 root root 175 7月 14 03:14 fluent-plugin-calyptia-monitoring-0.1.3
drwxr-xr-x. 4 root root 206 7月 14 03:16 fluent-plugin-concat-2.5.0
drwxr-xr-x. 5 root root 4096 7月 14 03:14 fluent-plugin-elasticsearch-5.3.0
drwxr-xr-x. 4 root root 205 7月 14 03:14 fluent-plugin-flowcounter-simple-0.1.0
drwxr-xr-x. 6 root root 191 7月 14 03:14 fluent-plugin-kafka-0.19.0
drwxr-xr-x. 5 root root 190 7月 14 03:14 fluent-plugin-metrics-cmetrics-0.1.2
drwxr-xr-x. 5 root root 4096 7月 14 03:14 fluent-plugin-opensearch-1.1.0
drwxr-xr-x. 5 root root 215 7月 14 03:14 fluent-plugin-prometheus-2.0.3
drwxr-xr-x. 6 root root 238 7月 14 03:14 fluent-plugin-prometheus_pushgateway-0.1.0
drwxr-xr-x. 4 root root 176 7月 14 03:14 fluent-plugin-record-modifier-2.1.1
drwxr-xr-x. 3 root root 210 7月 14 03:14 fluent-plugin-rewrite-tag-filter-2.4.0
drwxr-xr-x. 5 root root 230 7月 14 03:14 fluent-plugin-s3-1.7.2
drwxr-xr-x. 3 root root 170 7月 14 03:14 fluent-plugin-sd-dns-0.1.0
drwxr-xr-x. 3 root root 49 7月 14 03:14 fluent-plugin-systemd-1.0.5
drwxr-xr-x. 5 root root 221 7月 14 03:14 fluent-plugin-td-1.2.0
drwxr-xr-x. 5 root root 166 7月 14 03:14 fluent-plugin-utmpx-0.5.0
drwxr-xr-x. 4 root root 191 7月 14 03:14 fluent-plugin-webhdfs-1.5.0
8. fluent-package v5のデーモンを開始する
$ sudo systemctl start fluentd
9. fluentdのログにエラーメッセージがないことを確認する
$ tail -100f /var/log/fluent/fluentd.log
これで、更新手順は完了です。ログ収集を楽しんでください!
v4ユーザーへの追加ヒント
For Debian/Ubuntu
fluentd-apt-sourceパッケージは、移行用のパッケージとしてマークされます。sudo apt purge fluentd-apt-sourceを実行することで、安全に削除することができます。
td-agent.serviceを有効にしたい場合は、明示的に以下のコマンドを実行しなくてはなりません。
$ sudo systemctl unmask td-agent
$ sudo systemctl enable fluentd
For RHEL
td-agent.serviceを有効にしたい場合は、明示的に以下のコマンドを実行しなくてはなりません。
$ sudo systemctl enable fluentd
For Windows
fluentd-packageインストーラーはデフォルトでサービスを起動しなくなりました。fluentdをサービスとして開始したい場合はアドミニストレーター権限で明示的に下記のコマンドを実行してください。
c:\opt\fluent> net start fluentdwinsvc
For macOS
現在のところ、fluent-packageのdmgバージョンを正式にサポートする予定はまだありません。最小限の構築が可能な状態になるように変更されているだけであり、テスト目的のみでしか使えません。
まとめ
クリアコードがコミュニティにも関わっているFluentdの新しいパッケージについての本家の英語記事を翻訳しました。必要としている方にとって便利だと思ってもらえると嬉しいです。移行などで問題があった場合はサポートサービスにてサポート可能ですので、お気軽にお問合せください。
新パッケージが出た際に「リリース自慢会」を開催したのですが、その中でも移行に関する部分の報切り抜いた動画もあるので、あわせてごらんください。
Written with Apache Licence 2.0 http://www.apache.org/licenses/LICENSE-2.0. Copy rights 2023 Fluentd project. 内容を日本語に翻訳。 ↩
<p>今回の記事は、2023年7月に英語でリリースされた<a href="https://www.fluentd.org/blog/upgrade-td-agent-v4-to-v5">Upgrade to fluent-package v5</a> <sup class="footnote-ref"><a href="#fn1" id="fnref1">1</a></sup>の翻訳記事になります。</p>
<div class="callout secondary">
<ul>
<li>2023年8月にリリースされたFluent Package及びFluent Package LTS(Long Term Support)の説明</li>
<li>更新の手順</li>
</ul>
が含まれています
</div>
<!--more-->
<h3 id="前段">前段</h3>
<p>fluent-package"v5"は、2023年8月から利用可能になっています。fluent-packageは td-agent"v4"の後継として作られました。
この投稿では、開発段階でテストした手順を共有します。これがv4からv5への更新の実践に役立つことを願っています。</p>
<h2 id="パッケージ名称の変更理由">パッケージ名称の変更理由</h2>
<p><code>fluent-package</code>は、以前<code>td-agent</code>として知られていたものです。昔は、<a href="https://www.treasuredata.com/">Treasure Data社</a>が、中心となってパッケージの提供を行っていました。現在は、Fluentdのコミュニティでパッケージ提供を行っています。この開発体制の変更が今回のパッケージ名の変更の理由です。</p>
<p>「Fluentdと関連するgemパッケージを含むFluentdのオールインワンパッケージ」を表現するために、パッケージ名が<code>fluent-package</code>に変更されました。</p>
<p>パッケージの名称は変更されましたが、現在でもTreasure Data社が、引き続きパッケージの配信リソースのスポンサーをしています。</p>
<h2 id="どのチャンネルを使うべきか">どのチャンネルを使うべきか</h2>
<p><code>fluent-package</code>には、2種類のチャンネルがあります。</p>
<ul>
<li>Normal release version = 通常リリース版</li>
<li>Long Term Support version (LTS) = 長期サポート版</li>
</ul>
<p>一つは、定期的に更新される通常リリースバージョンです。つまり、ラピッドリリース開発スタイルで提供されます。 (td-agent v4 は、ほぼ四半期ごとにこのようにリリースされました)。このバージョンでは、同梱されているfluentdは最終的に新しいマイナーバージョン (例: 1.17.xなど)に更新されます。(そのため、新しい機能が含まれることになります。)</p>
<p>もう一つは、新しい機能が導入されない、より保守的なメンテナンスの行われるバージョン(長期サポート)です。セキュリティ修正やバグ修正などの小さなアップデートのみが適用されます。 v5のLTSは2025年3月までサポートされる予定です。</p>
<p>通常リリース版とLTS版の違いについては、<a href="https://www.fluentd.org/blog/fluent-package-scheduled-lifecycle">「Fluent Packageに関するサポートライフサイクル予定のお知らせ(※英語)」</a>で詳しく説明しています。</p>
<h2 id="td-agent-v4とfluent-package-v5の違い">td-agent v4とfluent-package v5の違い</h2>
<p>Fluent-package v5では、ruby (2.7.8 -> 3.2.2) や OpenSSL (1.1.1 -> 3.1.0 (Windows の場合)、3.0.8 (macOS の場合)) などのコアコンポーネントが更新されました。</p>
<p>主な変更点は以下の通りです。</p>
<ul>
<li>コマンド名<code>td-agent</code>が<code>fluentd</code>に変更
<ul>
<li><code>$ td-agent --version</code> -> <code>$ fluentd --version</code><!--ここはfluentではない?--></li>
</ul>
</li>
<li>コマンド名<code>td-agent-gem</code>が<code>fluent-gem</code>に変更
<ul>
<li><code>$ td-agent-gem list</code> -> <code>$ fluent-gem list</code></li>
</ul>
</li>
<li>Windows以外のサービス名称<code>td-agent</code>が<code>fluentd</code>に変更
<ul>
<li><code>$ systemctl status td-agent</code> -> <code>$ systemctl status fluentd</code></li>
</ul>
</li>
</ul>
<p>パッケージ名の変更に伴い、インストールパス、サービス名(/opt/fluent、fluentd.serviceなど)等も変更されました。基本的に、td-agent v4ユーザーに対しては、古いファイルをコピーしたり、シンボリックリンクを提供したりして移行プロセスを実行することで、可能な限り互換性を維持することを目指しました。</p>
<p>アップグレードされたコンポーネントの詳細を知りたい場合は、<a href="https://github.com/fluent/fluent-package-builder/blob/master/CHANGELOG.md#release-v500---20230729">CHANGELOG.md</a>を参照してください。</p>
<p>注: プラットフォーム固有の問題については、「v4 ユーザー向けの追加ヒント」セクションで説明します。</p>
<h2 id="アップグレード手順">アップグレード手順</h2>
<p>td-agentにバンドルされているプラグインは、fluent packageにアップグレートを行う際に、自動的にアップグレードされます。ただし、自分で追加した他のプラグインは自動的にアップグレードされません。 v4およびv5のディレクトリ構造の一部が変更されているため、プラグインを別にアップグレードする必要があるかどうかを確認する必要があります。</p>
<p>ここでは、「fluent-plugin-concat」などのプラグインを独自に追加した手順を示します。以下は、手順で使用したサンプル構成ファイルです。</p>
<pre><code><filter docker.log>
@type concat
key message
multiline_start_regexp /^Start/
</filter>
</code></pre>
<h3 id="1.-td-agentにどのプラグインが一緒にインストールされているかをレビューする">1. td-agentにどのプラグインが一緒にインストールされているかをレビューする</h3>
<pre><code>$ td-agent-gem list | grep fluent-plugin*
fluent-plugin-calyptia-monitoring (0.1.3)
fluent-plugin-concat (2.5.0)
fluent-plugin-elasticsearch (5.3.0)
fluent-plugin-flowcounter-simple (0.1.0)
fluent-plugin-kafka (0.19.0)
fluent-plugin-metrics-cmetrics (0.1.2)
fluent-plugin-opensearch (1.1.0)
fluent-plugin-prometheus (2.0.3)
fluent-plugin-prometheus_pushgateway (0.1.0)
fluent-plugin-record-modifier (2.1.1)
fluent-plugin-rewrite-tag-filter (2.4.0)
fluent-plugin-s3 (1.7.2)
fluent-plugin-sd-dns (0.1.0)
fluent-plugin-systemd (1.0.5)
fluent-plugin-td (1.2.0)
fluent-plugin-utmpx (0.5.0)
fluent-plugin-webhdfs (1.5.0)
</code></pre>
<p><code>/opt/td-agent/lib/ruby/gems/2.7.0/gems/</code>ディレクトリー以下でもインストールされているプラグインを見つけることができます。</p>
<pre><code>$ ls -l /opt/td-agent/lib/ruby/gems/2.7.0/gems |grep fluent-plugin*
drwxr-xr-x. 5 root root 175 7月 14 03:01 fluent-plugin-calyptia-monitoring-0.1.3
drwxr-xr-x. 4 root root 206 7月 14 03:03 fluent-plugin-concat-2.5.0
drwxr-xr-x. 5 root root 4096 7月 14 03:01 fluent-plugin-elasticsearch-5.3.0
drwxr-xr-x. 4 root root 205 7月 14 03:01 fluent-plugin-flowcounter-simple-0.1.0
drwxr-xr-x. 6 root root 191 7月 14 03:01 fluent-plugin-kafka-0.19.0
drwxr-xr-x. 5 root root 190 7月 14 03:01 fluent-plugin-metrics-cmetrics-0.1.2
drwxr-xr-x. 5 root root 4096 7月 14 03:01 fluent-plugin-opensearch-1.1.0
drwxr-xr-x. 5 root root 215 7月 14 03:01 fluent-plugin-prometheus-2.0.3
drwxr-xr-x. 6 root root 238 7月 14 03:01 fluent-plugin-prometheus_pushgateway-0.1.0
drwxr-xr-x. 4 root root 176 7月 14 03:01 fluent-plugin-record-modifier-2.1.1
drwxr-xr-x. 3 root root 210 7月 14 03:01 fluent-plugin-rewrite-tag-filter-2.4.0
drwxr-xr-x. 5 root root 230 7月 14 03:01 fluent-plugin-s3-1.7.2
drwxr-xr-x. 3 root root 170 7月 14 03:01 fluent-plugin-sd-dns-0.1.0
drwxr-xr-x. 3 root root 49 7月 14 03:01 fluent-plugin-systemd-1.0.5
drwxr-xr-x. 5 root root 221 7月 14 03:01 fluent-plugin-td-1.2.0
drwxr-xr-x. 5 root root 166 7月 14 03:01 fluent-plugin-utmpx-0.5.0
drwxr-xr-x. 4 root root 191 7月 14 03:01 fluent-plugin-webhdfs-1.5.0
</code></pre>
<h3 id="2.-td-agent-v4のデーモンを止める">2. td-agent v4のデーモンを止める</h3>
<pre><code>$ sudo systemctl stop td-agent
</code></pre>
<p><code>fluent-package</code>は、サービスを止めないままの更新をサポートしていますが、ここでは、明確に止めることをおすすめします。</p>
<h3 id="3.-fluent-package-v5のインストールスクリプトを実行する">3. fluent-package v5のインストールスクリプトを実行する</h3>
<p>RedHat 及び派生のディストリビューションを使っている場合で、fluent-package通常版をインストールしたい場合は以下のスクリプトを実行してください。</p>
<pre><code># curl -L https://toolbelt.treasuredata.com/sh/install-redhat-fluent-package5.sh | sh
</code></pre>
<p>RedHat 及び派生のディストリビューションを使っている場合で、fluent-packageLTS版をインストールしたいなら以下のスクリプトを実行してください。</p>
<pre><code># curl -L https://toolbelt.treasuredata.com/sh/install-redhat-fluent-package5-lts.sh | sh
</code></pre>
<p>もっと細かいインストールについての情報は公式ドキュメント<a href="https://docs.fluentd.org/installation">Fluend Doc - Installation</a>を参照してください。</p>
<h3 id="4.-fluent-package-v5がきちんとインストールされていることの確認">4. fluent-package v5がきちんとインストールされていることの確認</h3>
<pre><code>$ LANG=C yum info fluent-package
Loaded plugins: fastestmirror
Loading mirror speeds from cached hostfile
* base: ftp.riken.jp
* extras: ftp.riken.jp
* updates: ftp.riken.jp
Installed Packages
Name : fluent-package
Arch : x86_64
Version : 5.0.0
Release : 1.el7
Size : 64 M
Repo : installed
From repo : /fluent-package-5.0.0-1.el7.x86_64
Summary : The stable distribution of Fluentd
URL : https://www.treasuredata.com/
License : ASL 2.0
Description : The stable distribution of Fluentd, called td-agent.
</code></pre>
<h3 id="5.-fluent-packageのデーモンを再読み込み">5. fluent-packageのデーモンを再読み込み</h3>
<pre><code>$ sudo systemctl daemon-reload
$ sudo systemctl enable --now fluentd
</code></pre>
<h3 id="6.-インストールされているプラグインの確認">6. インストールされているプラグインの確認</h3>
<pre><code>$ fluent-gem list |grep fluent-plugin*
fluent-plugin-calyptia-monitoring (0.1.3)
fluent-plugin-elasticsearch (5.3.0)
fluent-plugin-flowcounter-simple (0.1.0)
fluent-plugin-kafka (0.19.0)
fluent-plugin-metrics-cmetrics (0.1.2)
fluent-plugin-opensearch (1.1.0)
fluent-plugin-prometheus (2.0.3)
fluent-plugin-prometheus_pushgateway (0.1.0)
fluent-plugin-record-modifier (2.1.1)
fluent-plugin-rewrite-tag-filter (2.4.0)
fluent-plugin-s3 (1.7.2)
fluent-plugin-sd-dns (0.1.0)
fluent-plugin-systemd (1.0.5)
fluent-plugin-td (1.2.0)
fluent-plugin-utmpx (0.5.0)
fluent-plugin-webhdfs (1.5.0)
</code></pre>
<p>バンドルされているプラグインもアップグレードされていることがわかりますが、独自に追加されたプラグインは見つかりません。今回の例では、"fluent-plugin-concat" が追加されていますが、インストールされているプラグインリストには表示されていません。</p>
<h3 id="7.-個別に追加したプラグインをインストールする">7. 個別に追加したプラグインをインストールする</h3>
<pre><code>$ sudo fluent-gem install fluent-plugin-concat
</code></pre><pre><code>$ fluent-gem list | grep fluent-plugin*
fluent-plugin-calyptia-monitoring (0.1.3)
fluent-plugin-concat (2.5.0)
fluent-plugin-elasticsearch (5.3.0)
fluent-plugin-flowcounter-simple (0.1.0)
fluent-plugin-kafka (0.19.0)
fluent-plugin-metrics-cmetrics (0.1.2)
fluent-plugin-opensearch (1.1.0)
fluent-plugin-prometheus (2.0.3)
fluent-plugin-prometheus_pushgateway (0.1.0)
fluent-plugin-record-modifier (2.1.1)
fluent-plugin-rewrite-tag-filter (2.4.0)
fluent-plugin-s3 (1.7.2)
fluent-plugin-sd-dns (0.1.0)
fluent-plugin-systemd (1.0.5)
fluent-plugin-td (1.2.0)
fluent-plugin-utmpx (0.5.0)
fluent-plugin-webhdfs (1.5.0)
</code></pre>
<p>fluent-package v5のために、"/opt/fluent/lib/ruby/gems/3.2.0/gems/" ディレクトリー下に"fluent-plugin-concat" がインストールされました。</p>
<pre><code>$ ls -l /opt/fluent/lib/ruby/gems/3.2.0/gems/ |grep fluent-plugin*
drwxr-xr-x. 5 root root 175 7月 14 03:14 fluent-plugin-calyptia-monitoring-0.1.3
drwxr-xr-x. 4 root root 206 7月 14 03:16 fluent-plugin-concat-2.5.0
drwxr-xr-x. 5 root root 4096 7月 14 03:14 fluent-plugin-elasticsearch-5.3.0
drwxr-xr-x. 4 root root 205 7月 14 03:14 fluent-plugin-flowcounter-simple-0.1.0
drwxr-xr-x. 6 root root 191 7月 14 03:14 fluent-plugin-kafka-0.19.0
drwxr-xr-x. 5 root root 190 7月 14 03:14 fluent-plugin-metrics-cmetrics-0.1.2
drwxr-xr-x. 5 root root 4096 7月 14 03:14 fluent-plugin-opensearch-1.1.0
drwxr-xr-x. 5 root root 215 7月 14 03:14 fluent-plugin-prometheus-2.0.3
drwxr-xr-x. 6 root root 238 7月 14 03:14 fluent-plugin-prometheus_pushgateway-0.1.0
drwxr-xr-x. 4 root root 176 7月 14 03:14 fluent-plugin-record-modifier-2.1.1
drwxr-xr-x. 3 root root 210 7月 14 03:14 fluent-plugin-rewrite-tag-filter-2.4.0
drwxr-xr-x. 5 root root 230 7月 14 03:14 fluent-plugin-s3-1.7.2
drwxr-xr-x. 3 root root 170 7月 14 03:14 fluent-plugin-sd-dns-0.1.0
drwxr-xr-x. 3 root root 49 7月 14 03:14 fluent-plugin-systemd-1.0.5
drwxr-xr-x. 5 root root 221 7月 14 03:14 fluent-plugin-td-1.2.0
drwxr-xr-x. 5 root root 166 7月 14 03:14 fluent-plugin-utmpx-0.5.0
drwxr-xr-x. 4 root root 191 7月 14 03:14 fluent-plugin-webhdfs-1.5.0
</code></pre>
<h3 id="8.-fluent-package-v5のデーモンを開始する">8. fluent-package v5のデーモンを開始する</h3>
<pre><code>$ sudo systemctl start fluentd
</code></pre>
<h3 id="9.-fluentdのログにエラーメッセージがないことを確認する">9. fluentdのログにエラーメッセージがないことを確認する</h3>
<pre><code>$ tail -100f /var/log/fluent/fluentd.log
</code></pre>
<p>これで、更新手順は完了です。ログ収集を楽しんでください!</p>
<h2 id="v4ユーザーへの追加ヒント">v4ユーザーへの追加ヒント</h2>
<h3 id="for-debian/ubuntu">For Debian/Ubuntu</h3>
<ul>
<li><code>fluentd-apt-source</code>パッケージは、移行用のパッケージとしてマークされます。<code>sudo apt purge fluentd-apt-source</code>を実行することで、安全に削除することができます。</li>
<li><code>td-agent.service</code>を有効にしたい場合は、明示的に以下のコマンドを実行しなくてはなりません。</li>
</ul>
<pre><code>$ sudo systemctl unmask td-agent
$ sudo systemctl enable fluentd
</code></pre>
<h3 id="for-rhel">For RHEL</h3>
<ul>
<li><code>td-agent.service</code>を有効にしたい場合は、明示的に以下のコマンドを実行しなくてはなりません。</li>
</ul>
<pre><code>$ sudo systemctl enable fluentd
</code></pre>
<h3 id="for-windows">For Windows</h3>
<p><code>fluentd-package</code>インストーラーはデフォルトでサービスを起動しなくなりました。<code>fluentd</code>をサービスとして開始したい場合はアドミニストレーター権限で明示的に下記のコマンドを実行してください。</p>
<pre><code>c:\opt\fluent> net start fluentdwinsvc
</code></pre>
<h3 id="for-macos">For macOS</h3>
<p>現在のところ、fluent-packageのdmgバージョンを正式にサポートする予定はまだありません。最小限の構築が可能な状態になるように変更されているだけであり、テスト目的のみでしか使えません。</p>
<h1 id="まとめ">まとめ</h1>
<p>クリアコードがコミュニティにも関わっているFluentdの新しいパッケージについての本家の英語記事を翻訳しました。必要としている方にとって便利だと思ってもらえると嬉しいです。移行などで問題があった場合は<a href="/services/fluentd-service.html">サポートサービス</a>にてサポート可能ですので、お気軽にお問合せください。</p>
<p>新パッケージが出た際に<a href="https://youtube.com/live/CrsTNHqWHTc">「リリース自慢会」</a>を開催したのですが、その中でも移行に関する部分の報切り抜いた動画もあるので、あわせてごらんください。</p>
<div class="youtube">
<iframe width="560" height="315" src="https://www.youtube.com/embed/e5Lfnze092A?si=23tehPhvOuq6RM0u" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>
</div>
<section class="footnotes">
<ol>
<li id="fn1">
<p>Written with Apache Licence 2.0 <a href="http://www.apache.org/licenses/LICENSE-2.0">http://www.apache.org/licenses/LICENSE-2.0</a>. Copy rights 2023 Fluentd project. 内容を日本語に翻訳。 <a href="#fnref1" class="footnote-backref">↩</a></p>
</li>
</ol>
</section>
-
PostgreSQL Conference Japan 2023 - Apache Arrow Flight SQLでPostgreSQLをもっと速く! #pgcon23j
https://www.clear-code.com/blog/2024/1/24/postgresql-conference-japan-2023.html
2024-01-24T00:00:00+09:00
なんともう2ヶ月前になってしまったのですが、PostgreSQL Conference Japan 2023でApache Arrow Flight SQLでPostgreSQLをもっと速く!という話をしてきた須藤です。
Apache Arrow Flight SQLでPostgreSQLをもっと速く!
関連リンク:
スライド(Rabbit Slide Show)
リポジトリー
内容
2023年はPostgreSQLをApache Arrow Flight SQLに対応させるApache Arrow Flight SQL adapter for PostgreSQLというプロダクトを開発していました。ということで、このプロダクトがどんな問題をどのように解決するプロダクトなのかというユーザー目線での説明と、このプロダクトの実装はどうなっているのかという開発者目線での説明をしました。
このプロダクトがどんな問題を解決したいかというと次の2つです。
PostgreSQLからの大量データの読み込みが遅い
PostgreSQLへの大量データの書き込みが遅い
これらの問題をApache ArrowベースのプロトコルであるApache Arrow Flight SQLで解決するのがこのプロダクトです。
Apache Arrowはデータ交換コストをほぼ0にするデータフォーマットです。PostgreSQLのプロトコルが使っているデータフォーマットはデータ交換コストがほぼ0ではないのでApache Arrowフォーマットを使うことで高速化できないかという狙いです。
現在のPostgreSQLではPostgreSQLプロトコルでApache Arrowフォーマットを使えないので、プロトコルごと変えてApache Arrowフォーマットを使おうというのがこのプロダクトです。プロトコルにはApache ArrowとSQLを使った高速なプロトコルApache Arrow Flight SQLを使います。
この問題を解決できたかどうかは高速になったかどうかがポイントなのでいくつかのパターンで検証しました。しかし、残念ながら思ったほど速くなっていませんでした。。。いくつかのパターンでは速かったのですがそれ以外のパターンはむしろ遅くなっていました。高速化の案はあるので引き続き高速化に取り組んでいきます。
開発者目線では次のことを説明しました。
認証まわりの実装方法
PostgreSQLに新しくプロトコルを追加する方法
このプロダクトの開発に興味がある人は https://github.com/apache/arrow-flight-sql-postgresql に来てください。一緒に開発しましょう!
オフラインカンファレンスのメリット
PostgreSQL Conference Japanは例年オフラインで開催されています。私は数年ぶりに参加しましたが、やはりオフラインカンファレンスはいいですね!
私は聞いてくれている人たちの様子を見ながら話す方が楽しいのでオフラインの方が楽しく話せます。今回は特に楽しく話せました。今回はスタッフの方から「コミュニティーっぽい話し方でいいですね」というようなコメントをもらったのですが、そういう感じ(どういう感じ?)の話し方をしているんでしょう。ただ、私の話し方はちょっとクセがあるので苦手な人もいるとは思います。実際、アンケートで「良くなかった」(5段階評価で最低評価)がついていた数少ない講演でした。でも、私はこの話し方以外はできないんですよねぇ。違う話し方だと私が楽しくなくなってしまいそうです。すみませんねぇ。
話しているときだけではなく、話していないときもオフラインでのいいことがあります。RubyKaigiもそうなのですが、開発者がそこらへんを歩いているのがいいです。今回の話に関連するところでは、PostgreSQLのCOPYを拡張できないかなぁというアイディアがありました。COPYでApache Arrowフォーマットを扱うことができればPostgreSQLプロトコルでも高速にできそうだからです。ということで、そこらへんにいた@masahiko_sawadaや@fujii_masaoや@michaelpqが相談にのってくれました。とても助かりました。ありがとうございました!
その結果、すでにそういう議論がされていてCOPYを拡張できるようにしようよ、というところまで話が進んでいることがわかりました。ただ、誰も実装していなくて話がそこで止まっていました。後は仕様を詰めて実装するだけだったので、PostgreSQL Conference Japan 2023が終わってからはpgsql-hackersでそこらへんのことをやっていました。Make COPY format extendable: Extract COPY TO format implementationsのスレッドでやりとりしたりパッチを投げたりしているので、興味がある人はレビューしたり動作確認したりしてね。
まとめ
なんともう2ヶ月前になってしまったPostgreSQL Conference Japan 2023で話したことについて書きました。
久しぶりに参加したのですがいいカンファレンスでした!今年は「COPYを拡張可能にしたぞ!」という話をしに行けるといいな。
<p>なんともう2ヶ月前になってしまったのですが、<a href="https://www.postgresql.jp/jpug-pgcon2023">PostgreSQL Conference Japan 2023</a>で<a href="https://slide.rabbit-shocker.org/authors/kou/postgresql-conference-japan-2023/">Apache Arrow Flight SQLでPostgreSQLをもっと速く!</a>という話をしてきた須藤です。</p>
<!--more-->
<div class="rabbit-slide rabbit-slide-wide">
<iframe src="https://slide.rabbit-shocker.org/authors/kou/postgresql-conference-japan-2023/viewer.html"
width="640" height="404"
frameborder="0"
marginwidth="0"
marginheight="0"
scrolling="no"
style="border: 1px solid #ccc; border-width: 1px 1px 0; box-sizing: content-box; margin-bottom: 5px"
allowfullscreen> </iframe>
<div style="margin-bottom: 5px">
<a href="https://slide.rabbit-shocker.org/authors/kou/postgresql-conference-japan-2023/" title="Apache Arrow Flight SQLでPostgreSQLをもっと速く!">Apache Arrow Flight SQLでPostgreSQLをもっと速く!</a>
</div>
</div>
<p>関連リンク:</p>
<ul>
<li>
<p><a href="https://slide.rabbit-shocker.org/authors/kou/postgresql-conference-japan-2023/">スライド(Rabbit Slide Show)</a></p>
</li>
<li>
<p><a href="https://gitlab.com/ktou/rabbit-slide-kou-postgresql-conference-japan-2023">リポジトリー</a></p>
</li>
</ul>
<h3 id="内容">内容</h3>
<p>2023年はPostgreSQLを<a href="https://arrow.apache.org/blog/2022/02/16/introducing-arrow-flight-sql/">Apache Arrow Flight SQL</a>に対応させる<a href="https://arrow.apache.org/flight-sql-postgresql/current/">Apache Arrow Flight SQL adapter for PostgreSQL</a>というプロダクトを開発していました。ということで、このプロダクトがどんな問題をどのように解決するプロダクトなのかというユーザー目線での説明と、このプロダクトの実装はどうなっているのかという開発者目線での説明をしました。</p>
<p>このプロダクトがどんな問題を解決したいかというと次の2つです。</p>
<ul>
<li>PostgreSQL<em>から</em>の大量データの<em>読み</em>込みが遅い</li>
<li>PostgreSQL<em>へ</em>の大量データの<em>書き</em>込みが遅い</li>
</ul>
<p>これらの問題をApache ArrowベースのプロトコルであるApache Arrow Flight SQLで解決するのがこのプロダクトです。</p>
<p>Apache Arrowはデータ交換コストをほぼ0にするデータフォーマットです。PostgreSQLのプロトコルが使っているデータフォーマットはデータ交換コストがほぼ0ではないのでApache Arrowフォーマットを使うことで高速化できないかという狙いです。</p>
<p>現在のPostgreSQLではPostgreSQLプロトコルでApache Arrowフォーマットを使えないので、プロトコルごと変えてApache Arrowフォーマットを使おうというのがこのプロダクトです。プロトコルにはApache ArrowとSQLを使った高速なプロトコルApache Arrow Flight SQLを使います。</p>
<p>この問題を解決できたかどうかは高速になったかどうかがポイントなのでいくつかのパターンで検証しました。しかし、残念ながら思ったほど速くなっていませんでした。。。いくつかのパターンでは速かったのですがそれ以外のパターンはむしろ遅くなっていました。高速化の案はあるので引き続き高速化に取り組んでいきます。</p>
<p>開発者目線では次のことを説明しました。</p>
<ul>
<li>認証まわりの実装方法</li>
<li>PostgreSQLに新しくプロトコルを追加する方法</li>
</ul>
<p>このプロダクトの開発に興味がある人は <a href="https://github.com/apache/arrow-flight-sql-postgresql">https://github.com/apache/arrow-flight-sql-postgresql</a> に来てください。一緒に開発しましょう!</p>
<h3 id="オフラインカンファレンスのメリット">オフラインカンファレンスのメリット</h3>
<p>PostgreSQL Conference Japanは例年オフラインで開催されています。私は数年ぶりに参加しましたが、やはりオフラインカンファレンスはいいですね!</p>
<p>私は聞いてくれている人たちの様子を見ながら話す方が楽しいのでオフラインの方が楽しく話せます。今回は特に楽しく話せました。今回はスタッフの方から「コミュニティーっぽい話し方でいいですね」というようなコメントをもらったのですが、そういう感じ(どういう感じ?)の話し方をしているんでしょう。ただ、私の話し方はちょっとクセがあるので苦手な人もいるとは思います。実際、アンケートで「良くなかった」(5段階評価で最低評価)がついていた数少ない講演でした。でも、私はこの話し方以外はできないんですよねぇ。違う話し方だと私が楽しくなくなってしまいそうです。すみませんねぇ。</p>
<p>話しているときだけではなく、話していないときもオフラインでのいいことがあります。RubyKaigiもそうなのですが、開発者がそこらへんを歩いているのがいいです。今回の話に関連するところでは、PostgreSQLの<code>COPY</code>を拡張できないかなぁというアイディアがありました。<code>COPY</code>でApache Arrowフォーマットを扱うことができればPostgreSQLプロトコルでも高速にできそうだからです。ということで、そこらへんにいた<a href="https://twitter.com/masahiko_sawada">@masahiko_sawada</a>や<a href="https://twitter.com/fujii_masao">@fujii_masao</a>や<a href="https://twitter.com/michaelpq">@michaelpq</a>が相談にのってくれました。とても助かりました。ありがとうございました!</p>
<p>その結果、すでにそういう議論がされていて<code>COPY</code>を拡張できるようにしようよ、というところまで話が進んでいることがわかりました。ただ、誰も実装していなくて話がそこで止まっていました。後は仕様を詰めて実装するだけだったので、PostgreSQL Conference Japan 2023が終わってからはpgsql-hackersでそこらへんのことをやっていました。<a href="https://www.postgresql.org/message-id/flat/20231204.153548.2126325458835528809.kou%40clear-code.com">Make COPY format extendable: Extract COPY TO format implementations</a>のスレッドでやりとりしたりパッチを投げたりしているので、興味がある人はレビューしたり動作確認したりしてね。</p>
<h3 id="まとめ">まとめ</h3>
<p>なんともう2ヶ月前になってしまったPostgreSQL Conference Japan 2023で話したことについて書きました。</p>
<p>久しぶりに参加したのですがいいカンファレンスでした!今年は「<code>COPY</code>を拡張可能にしたぞ!」という話をしに行けるといいな。</p>
-
WebExtensionsのNative Messaging Hostが動作しなくなる問題と、その予防方法
https://www.clear-code.com/blog/2024/1/22/webextensions-native-messaging-host.html
2024-01-22T00:00:00+09:00
結城です。
先日、当社製ThunderbirdアドオンのFlexConfirmMailとList Addons in Windows' Programs(以下、LAWP)について、「これらを同時に使用するとFlexConfirmMailが動作しなくなる」というお問い合わせを頂きました。
調査の結果、この現象はアドオン開発時に使用するNative Messagingという機能の使い方に起因するものであったことが分かりました。
この記事では、主に開発者向けの情報として、トラブルシューティング事例としてこの不具合の原因調査の過程を紹介しつつ、FirefoxおよびThunderbirdのアドオンの開発時のNative Messaging使用時の注意点を説明します。
原因の調査
「FlexConfirmMailが動作しない」状態の確認
調査のため手元の検証環境で現象の再現を試みたところ、FlexConfirmMailとLAWPの両方をインストールした状態において、Thunderbirdの起動直後の状態で何度かに1回程度の割合で、FlexConfirmMailが動作しない現象が再現しました。
具体的には、メールの送信直前に割り込んで宛先確認を行う動作のうち「宛先確認を行う」部分が機能せず、「メールの送信処理の中断」だけが行われている(その結果、メールを送信できなくなっている)ように見えました。
処理が停止している箇所の特定
現象発生時の状況をデバッグログで詳しく追ったところ、メール送信を検知して行うFlexConfirmMailの設定の読み込み処理の中で処理が停止してしまっている(その直前のログは記録されている一方で、その直後に出るはずのログが記録されていなかった)様子が窺えました。
ただ、該当箇所をtry-catchで囲ってみても例外は発生していませんでした。
そこで、より深く掘り下げてみたところ、処理が停止しているのはWebExtensionsの非同期APIであるbrowser.runtime.sendNativeMessage()が返したPromiseの解決をawaitで待っている箇所であることが分かりました。
これはつまり、APIが返したPromiseがいつまで待っても解決されていない状況だということです。
browser.runtime.sendNativeMessage()は、FirefoxやThunderbirdのアドオンがWebExtensionsのAPIが用意されていないことを行うために、外部のネイティブアプリケーション(Native Messaging Host)を呼び出して実行する、Native Messagingという仕組みのためのAPIです。
Thunderbird版FlexConfirmMailは、Outlook版FlexConfirmMail用のGPOの設定を読み込むためにNative Messagingを使っており、この一行を実行する過程で
FlexConfirmMailがbrowser.runtime.sendNativeMessage()を呼ぶ。
事前にFlexConfirmMail Native Messaging Hostのインストーラが登録した情報に基づいて、ThunderbirdがFlexConfirmMail Native Messaging Host(事前に登録された小型のローカルアプリ)の情報を把握する。
ThunderbirdがNative Messaging Hostを起動する。
Native Messaging Hostが起動し、レジストリ上からGPOの設定内容を読み込んでThunderbirdに返却する。
ThunderbirdがNative Messaging Hostから受け取った情報をアドオンに返却する。
FlexConfirmMailがbrowser.runtime.sendNativeMessage()の戻り値として、Native Messaging Hostから返された情報を受け取る。
という一連の処理がThunderbirdの内部で行われます。
このどこかで処理が躓いてしまっているようですが、どこで止まっているのかまではまだ分かりません。
処理が停止している状況で起こっていることの把握
Native Messaging Hostの呼び出し時に起こっていることは前述の通りですが、これを「処理が停止する原因」という観点で捉えると、以下のような原因が考えられます。
LAWPのNative Messaging Hostの登録情報が間違っていて、ThunderbirdがFlexConfirmMailのNative Messaging Hostを起動しようとしても起動できなかった。
各Native Messaging Hostの登録情報は正しいが、Thunderbirdがその情報通りにFlexConfirmMailのNative Messaging Hostを起動しようとしても起動できなかった。
Native Messaging Hostは起動されたが、Native Messaging Hostが異常終了した。
Native Messaging Hostは正常に終了したが、結果をFlexConfirmMailに返せなかった。
まず、1の可能性を疑って調査を行いました。
当社では複数のNative Messaging Hostを開発していますが、コード流用時の識別子の更新忘れなどによって「LAWPのNative Messaging Hostをインストールしたら、誤ってFlexConfirmMailのNative Messaging Hostとして登録されてしまった」といったことが起こる可能性はあります。
具体的には、本来であればWindowsのレジストリー上のキー
HKEY_LOCAL_MACHINE\SOFTWARE\Mozilla\NativeMessagingHosts\com.clear_code.list_addons_in_win_programs_we_host
に値を設定するべき所で、誤って
HKEY_LOCAL_MACHINE\SOFTWARE\Mozilla\NativeMessagingHosts\com.clear_code.flexible_confirm_mail_we_host
などのキーに値を設定してしまっている可能性が疑われます。
そこで、検証環境でそれぞれのNative Messaging Hostのインストールを試行し、これらのレジストリキーの状態をレジストリエディターで確認しましたが、登録情報が混ざるということはなく、すべて期待通りの情報が登録される結果を得られました。
次に、2の可能性を疑って調査を行いました。
当社では「Native Messaging Hostの登録情報が正しいのにプロセスを起動できない」過去事例はありませんでしたが、「アドオンが読み込まれた順番によって、片方がもう片方の動作に意図しない影響を与えた」事例はありました。
そこで、FlexConfirmMailとLAWPのそれぞれの有効・無効状態を切り替えてリロードしたり、アドオン自体をアンインストール・再インストールしてみたりして、状況に変化が見られるかを確認したところ、以下の事が分かりました。
LAWPを先に読み込んだ場合でもFlexConfirmMailを先に読み込んだ場合でも、どちらの場合も現象が再現する。
よって、「LAWPが有効であること」だけが必須の再現条件だと言える。
現象が発生した後、FlexConfirmMailをリロードしても、現象は発生し続ける。
よって、アドオンのリロードで解放・再確保される種類のリソース(JavaScriptの名前空間など)以外の部分で何かが起こっていると考えられる。
Thunderbirdを再起動すると、現象が発生しなくなる。
よって、問題はThunderbirdというアプリケーションの制御下の範囲で起こっていて、Windowsのレベルで起こっているわけではないように思われる。
これらのことから「LAWPの初期化処理の過程で何かが起こっているようだ」と検討をつけて、LAWPのデバッグログ出力を有効にした状態でLAWPのリロードを繰り返しながらログを採取してみたところ、「LAWPの正常動作時に出力されるはずの一連のログが、途中までしか出力されなくなる場合がある」「そのような状態が発生すると、それ以後FlexConfirmMailのNative Messaging Hostを呼び出せなくなる」ことが分かりました。
このとき、出力されなくなったのは「LAWPがLAWP自身のNative Messaging Hostへの問い合わせ結果を受けて出力する」ログでした。
このことからようやく、起こっていた現象は実は「FlexConfirmMailのNative Messaging Host呼び出しだけが失敗している」のではなく、「FlexConfirmMailも含めた複数の(恐らく、すべての)アドオンのNative Messaging Host呼び出しが失敗している」というものであったと分かりました。
LAWPは「アドオンが動作すると、その時点でThunderbirdにインストールされているアドオンの一覧をWindowsのレジストリーに登録する」アドオンなので、インストールされているアドオンの一覧に変化がなければ、LAWPが動作していてもいなくても表面上の「動作結果」は変わりません。
そのため、「実はLAWP自身のNative Messaging Host呼び出しも失敗していた」とは気付きにくい状況だったのでした。
Native Messaging Hostを呼び出せなくなっている時に起こっていることの詳細
Native Messaging Host呼び出しが失敗している箇所として前項で1から4までを仮定し、1については可能性を否定できましたが、2から4までのどれが実際の原因箇所かはまだ分かりません。
そこで、Native Messaging Host呼び出しに失敗している時に何が起こっているのかをより詳細に調べてみました。
当社製アドオンのNative Messaging Hostは、それ自身もまたデバッグ用のログを出力する機能を持っています。
そこでNative Messaging Host側のログを確認してみたところ、現象の発生時には、そもそも全くログが出力されていないことが分かりました。
このことから、現象の発生原因となっている箇所は、前項で述べた2、すなわち「ThunderbirdがNative Messaging Hostのプロセスを起動できていない」という点であると判断できました。
この時の様子をWindowsのタスクマネージャーで確認し、正常動作時と比較したところ、以下の事が分かりました。
Thunderbirdは直接Native Messaging Hostのプロセスを起動するのではなく、conhost.exeというプロセスを経由して起動している。
これは、WindowsアプリケーションがGUIを持たないプロセスを起動する時に共通して使う、Windowsの機能である。
正常は、LAWPの初期化処理中にconhost.exeのプロセスが大量に起動され、その後しばらくして、それらのconhost.exeのプロセスが一斉に終了する。
現象発生時は、LAWPの初期化処理中にconhost.exeのプロセスが大量に起動された後、それらのconhost.exeのプロセスが終了せずにそのまま残留している。
Google Chrome拡張機能のNative Messagingの仕様では、Native Messaging Hostの動作として「1つのプロセスを起動して、ソケット通信で継続的にデータをやり取りする」モードと「要求の度にプロセスを起動して、処理が終わったらプロセスが終了する」モードの2つがあることになっていますが、Thunderbirdはそのうちの後者にのみ対応しています。
LAWPのNative Messaging Hostは「このアドオンの情報を1つだけWindowsのレジストリーに登録せよ・レジストリーから削除せよ」というごく単純な指示を受け取って動作するよう設計されているため、インストールされているアドオンの数だけNative Messaging Hostが起動されることになります。
conhost.exeのプロセスが大量に起動するのは、そのためです。
このとき使用するAPIのbrowser.runtime.sendNativeMessage()はPromiseを返すため、戻り値を受け取るためには、一般的には以下の例のように、awaitで解決を待つことになります。
const addons = await browser.management.getAll();
for (const addon of addons) {
await browser.runtime.sendNativeMessage(...);
}
ただ、この例のようにそれぞれの呼び出しで毎回awaitで解決を待つと、無駄な待ち時間が生じてしまいます。
そのため、こういった非同期APIのそれぞれの呼び出し同士に依存関係が無い場合は、以下のようにPromise.all()を使うのがセオリーとなっています。
const addons = await browser.management.getAll();
await Promise.all(
addons.map(
async addon => browser.runtime.sendNativeMessage(...)
)
);
後者のような実装の仕方では、ごく短時間(数マイクロ秒~数ミリ秒)の間にconhost.exeが大量に起動されることになります。
JavaScriptは基本的にシングルスレッドのため、このような実装であってもメモリ破壊などは原則として発生し得ませんが、JavaScriptのコードの先にいるconhost.exeやその呼び出し箇所(C++で実装されていると思われる)はその限りではありません。
もしconhost.exeやその呼び出し箇所に、ごく短時間の間に複数回実行されることで問題が発生する何らかの不具合があるならば、後者のような実装を前者のように改めることで、処理に要する時間が増大する代わりに安定した動作を得られるようになる可能性があります。
そこで、後者のような効率重視の実装になっていた箇所を、前者のようなベタな実装に変更して動作を検証してみたところ、これがまさに大当たりで、当社検証環境においては現象が再現しなくなりました。
Thunderbirdとconhost.exe(Windows)のどちらのに責任があるのかは現時点では不明ですが、状況から見て、「Thunderbirdがconhost.exeのプロセスを一度に大量に起動すると、conhost.exeが正常に動作・終了しなくなる」という根本の問題があり、LAWPが前述のような実装の仕方になっているためにその問題が表面化した、というのがこの現象の正体である模様です。
以上の調査はあくまで当社の検証環境で行っており、実際の環境で起こっている現象は全く別の原因によるものである可能性もあります。
そこでお客さまには、現象発生時のconhost.exeのプロセスの状態(conhost.exeのプロセスが大量に残留するかどうか)の確認と、前述の改修を行ったバージョンのLAWPの動作テストをお願いしました。
果たして、お客さま環境においても現象発生時にはconhost.exeのプロセスが大量に残留していること、改修版のLAWPでは現象が発生しなくなったことをご確認頂けました。
問題の解決
とりあえずの対応
お客様環境での検証結果を承け、改修を反映したLAWPをバージョン2.1としてリリースしました。
今回の改修範囲はアドオン本体のみに留まっており、Native Messaging Host側には変更が無いため、アドオンの自動更新を有効にしている環境ではすでに更新が反映されているものと思われます。
企業内などでアドオンの自動更新を無効化している場合は、配布ページからXPIパッケージを入手して各端末にインストールして頂く必要があります。
今回の改修方法は、改修範囲がアドオン側のみに留まるメリットがありますが、インストールされているアドオンの数が増えるほど待ち時間が増えて、処理の完了までにかかる時間が長くなるというデメリットもあります。
今回は以下の理由から、この改修方法のままリリースすることとしました。
お客さまからのお問い合わせに基づく改修で、なるべく早く対応することが望ましい。改修範囲が広がると、それだけ検証にも時間がかかるので、なるべく小規模の改修で済ませたかった。
LAWPというアドオン(Thunderbirdのアドオンマネージャーが認識しているアドオンを、資産管理ソフト等で管理しやすくするようにWindowsのレジストリーに登録する)の性質上、それほど高い性能が要求されるわけではない。
ただ、これはあくまで、すでにある実装をそのまま使った方が改修コストを削減できるという前提があっての判断です。
新たにこのような機能を実装する場面では、より望ましい実装方法を模索するべきなのは間違い無いです。
アドオン開発においてNative Messagingの使用時に気をつけるべきこと
一般的に、メンテナンス性・開発の継続性の維持を重視する観点からは、ソフトウェアのモジュール同士は疎結合にしておくことが望ましいです。
LAWPにおける「1回のNative Messaging Host呼び出しで1件の情報をレジストリへ書き込む」設計は、モジュール同士を疎結合にする観点で取った選択でした。
しかしながら、今回の調査の過程で、Native Messaging Hostへの大量の呼び出しを一度に行う事には思わぬリスクがあることが分かりました。
Native Messagingを使用する場合は、安全のために、Native Messaging Hostの呼び出し回数がなるべく少なく済むような設計とすることが望ましいと言えるでしょう。
例えば、今回取り上げたLAWPのようなアドオンであれば、browser.runtime.sendNativeMessage()の引数で指定してNative Messaging Hostに渡すメッセージや、Native Messaging Hostが返却するメッセージは、「複数の処理対象を配列でまとめて指定する」「処理結果を配列で返す」といった形で一括処理を行える設計にするといったやり方1が考えられます。
ただ、そのような設計がそぐわないケースもあります。
例えば、当社開発のBrowserSelectorのFirefox用アドオンでは、「Firefox上で遷移しようとしているWebページのURLがWindowsのセキュリティゾーン設定においてどのゾーンと判定されるかを、Windowsのネイティブアプリケーション向けAPIを用いて判定する」必要があるために、ページ遷移の要求が生じる度に必ずNative Messaging Hostを呼ぶようになっています。
読み込み要求は都度処理しなくてはならないため、「読み込み要求を一定数溜め込んでからバッチ処理する」ことは、残念ながらできません。
BrowserSelectorの事例では、Windowsのセキュリティゾーンに基づく判定が必要ないのであれば、個々の読み込み要求に対して必ずしもNative Messaging Hostを呼び出す必要はありません。
そのため、判定をアドオン側で行えるならそれで済ませて、Native Messaging Hostの呼び出しを減らす改修を検討しています。
まとめ
Thunderbird用のアドオンのFlexConfirmMailとList Addons in Windows' Programs(以下、LAWP)について、これらを併用しているお客さまから寄せられたお問い合わせに基づいて行った調査の経緯と解決までの顛末をご紹介しました。
また、その際に得られた知見に基づいて、Firefox・Thunderbird用アドオン開発時においてNative Messagingを安全に使うための注意点もご紹介しました。
当社では、FirefoxやThunderbirdの法人運用におけるトラブルの原因究明や回避方法の調査、社内事情に合わせるためのアドオンの改修、アドオンの新規開発のご依頼など、様々なお問い合わせへの対応を有償にて承っております。
こういった事柄についてお悩みを抱えている企業のご担当者さまは、お問い合わせフォームよりご相談ください。
なお、仕様上も実装上も、アドオンからNative Messaging Hostへ送信できるメッセージの最大サイズは1回あたり4GBまで、Native Messaging Hostからアドオンへ返却できるメッセージの最大サイズは1回あたり1MBまでという制約があるので、これを超えるサイズのメッセージをやり取りする場合は、複数個のメッセージへの分割が必要となります。 ↩
<p>結城です。</p>
<p>先日、当社製Thunderbirdアドオンの<a href="https://addons.thunderbird.net/thunderbird/addon/flex-confirm-mail/">FlexConfirmMail</a>と<a href="https://addons.thunderbird.net/thunderbird/addon/list-addons-in-win-programs/">List Addons in Windows' Programs(以下、LAWP)</a>について、「これらを同時に使用するとFlexConfirmMailが動作しなくなる」というお問い合わせを頂きました。
調査の結果、この現象はアドオン開発時に使用する<a href="https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Native_messaging">Native Messaging</a>という機能の使い方に起因するものであったことが分かりました。</p>
<p>この記事では、主に開発者向けの情報として、トラブルシューティング事例としてこの不具合の原因調査の過程を紹介しつつ、FirefoxおよびThunderbirdのアドオンの開発時のNative Messaging使用時の注意点を説明します。</p>
<!--more-->
<h3 id="原因の調査">原因の調査</h3>
<h4 id="「flexconfirmmailが動作しない」状態の確認">「FlexConfirmMailが動作しない」状態の確認</h4>
<p>調査のため手元の検証環境で現象の再現を試みたところ、FlexConfirmMailとLAWPの両方をインストールした状態において、Thunderbirdの起動直後の状態で何度かに1回程度の割合で、FlexConfirmMailが動作しない現象が再現しました。
具体的には、メールの送信直前に割り込んで宛先確認を行う動作のうち「宛先確認を行う」部分が機能せず、「メールの送信処理の中断」だけが行われている(その結果、メールを送信できなくなっている)ように見えました。</p>
<h4 id="処理が停止している箇所の特定">処理が停止している箇所の特定</h4>
<p>現象発生時の状況をデバッグログで詳しく追ったところ、<a href="https://github.com/FlexConfirmMail/Thunderbird/blob/9530ca7b12bf1ea635316458bc897f6a58e75fb1/webextensions/background/background.js#L346">メール送信を検知して行うFlexConfirmMailの設定の読み込み処理</a>の中で処理が停止してしまっている(その直前のログは記録されている一方で、その直後に出るはずのログが記録されていなかった)様子が窺えました。
ただ、該当箇所をtry-catchで囲ってみても例外は発生していませんでした。
そこで、より深く掘り下げてみたところ、処理が停止しているのは<a href="https://github.com/FlexConfirmMail/Thunderbird/blob/9530ca7b12bf1ea635316458bc897f6a58e75fb1/webextensions/common/common.js#L402">WebExtensionsの非同期APIである<code>browser.runtime.sendNativeMessage()</code>が返したPromiseの解決を<code>await</code>で待っている箇所</a>であることが分かりました。
これはつまり、APIが返したPromiseがいつまで待っても解決されていない状況だということです。</p>
<p><code>browser.runtime.sendNativeMessage()</code>は、FirefoxやThunderbirdのアドオンが<a href="https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions">WebExtensions</a>のAPIが用意されていないことを行うために、外部のネイティブアプリケーション(Native Messaging Host)を呼び出して実行する、<a href="https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Native_messaging">Native Messaging</a>という仕組みのためのAPIです。
Thunderbird版FlexConfirmMailは、Outlook版FlexConfirmMail用のGPOの設定を読み込むためにNative Messagingを使っており、この一行を実行する過程で</p>
<ol>
<li>FlexConfirmMailが<code>browser.runtime.sendNativeMessage()</code>を呼ぶ。</li>
<li>事前にFlexConfirmMail Native Messaging Hostのインストーラが登録した情報に基づいて、ThunderbirdがFlexConfirmMail Native Messaging Host(事前に登録された小型のローカルアプリ)の情報を把握する。</li>
<li>ThunderbirdがNative Messaging Hostを起動する。</li>
<li>Native Messaging Hostが起動し、レジストリ上からGPOの設定内容を読み込んでThunderbirdに返却する。</li>
<li>ThunderbirdがNative Messaging Hostから受け取った情報をアドオンに返却する。</li>
<li>FlexConfirmMailが<code>browser.runtime.sendNativeMessage()</code>の戻り値として、Native Messaging Hostから返された情報を受け取る。</li>
</ol>
<p>という一連の処理がThunderbirdの内部で行われます。
このどこかで処理が躓いてしまっているようですが、どこで止まっているのかまではまだ分かりません。</p>
<h4 id="処理が停止している状況で起こっていることの把握">処理が停止している状況で起こっていることの把握</h4>
<p>Native Messaging Hostの呼び出し時に起こっていることは前述の通りですが、これを「処理が停止する原因」という観点で捉えると、以下のような原因が考えられます。</p>
<ol>
<li>LAWPのNative Messaging Hostの登録情報が間違っていて、ThunderbirdがFlexConfirmMailのNative Messaging Hostを起動しようとしても起動できなかった。</li>
<li>各Native Messaging Hostの登録情報は正しいが、Thunderbirdがその情報通りにFlexConfirmMailのNative Messaging Hostを起動しようとしても起動できなかった。</li>
<li>Native Messaging Hostは起動されたが、Native Messaging Hostが異常終了した。</li>
<li>Native Messaging Hostは正常に終了したが、結果をFlexConfirmMailに返せなかった。</li>
</ol>
<p>まず、1の可能性を疑って調査を行いました。</p>
<p>当社では複数のNative Messaging Hostを開発していますが、コード流用時の識別子の更新忘れなどによって「LAWPのNative Messaging Hostをインストールしたら、誤ってFlexConfirmMailのNative Messaging Hostとして登録されてしまった」といったことが起こる可能性はあります。
具体的には、本来であればWindowsのレジストリー上のキー
<code>HKEY_LOCAL_MACHINE\SOFTWARE\Mozilla\NativeMessagingHosts\com.clear_code.list_addons_in_win_programs_we_host</code>
に値を設定するべき所で、誤って
<code>HKEY_LOCAL_MACHINE\SOFTWARE\Mozilla\NativeMessagingHosts\com.clear_code.flexible_confirm_mail_we_host</code>
などのキーに値を設定してしまっている可能性が疑われます。
そこで、検証環境でそれぞれのNative Messaging Hostのインストールを試行し、これらのレジストリキーの状態をレジストリエディターで確認しましたが、登録情報が混ざるということはなく、すべて期待通りの情報が登録される結果を得られました。</p>
<p>次に、2の可能性を疑って調査を行いました。</p>
<p>当社では「Native Messaging Hostの登録情報が正しいのにプロセスを起動できない」過去事例はありませんでしたが、「アドオンが読み込まれた順番によって、片方がもう片方の動作に意図しない影響を与えた」事例はありました。
そこで、FlexConfirmMailとLAWPのそれぞれの有効・無効状態を切り替えてリロードしたり、アドオン自体をアンインストール・再インストールしてみたりして、状況に変化が見られるかを確認したところ、以下の事が分かりました。</p>
<ul>
<li>LAWPを先に読み込んだ場合でもFlexConfirmMailを先に読み込んだ場合でも、どちらの場合も現象が再現する。
よって、「LAWPが有効であること」だけが必須の再現条件だと言える。</li>
<li>現象が発生した後、FlexConfirmMailをリロードしても、現象は発生し続ける。
よって、アドオンのリロードで解放・再確保される種類のリソース(JavaScriptの名前空間など)以外の部分で何かが起こっていると考えられる。</li>
<li>Thunderbirdを再起動すると、現象が発生しなくなる。
よって、問題はThunderbirdというアプリケーションの制御下の範囲で起こっていて、Windowsのレベルで起こっているわけではないように思われる。</li>
</ul>
<p>これらのことから「LAWPの初期化処理の過程で何かが起こっているようだ」と検討をつけて、LAWPのデバッグログ出力を有効にした状態でLAWPのリロードを繰り返しながらログを採取してみたところ、「LAWPの正常動作時に出力されるはずの一連のログが、途中までしか出力されなくなる場合がある」「そのような状態が発生すると、それ以後FlexConfirmMailのNative Messaging Hostを呼び出せなくなる」ことが分かりました。</p>
<p>このとき、出力されなくなったのは「LAWPがLAWP自身のNative Messaging Hostへの問い合わせ結果を受けて出力する」ログでした。
このことからようやく、<em>起こっていた現象は実は「FlexConfirmMailのNative Messaging Host呼び出しだけが失敗している」のではなく、「FlexConfirmMailも含めた複数の(恐らく、すべての)アドオンのNative Messaging Host呼び出しが失敗している」というものであった</em>と分かりました。
LAWPは「アドオンが動作すると、その時点でThunderbirdにインストールされているアドオンの一覧をWindowsのレジストリーに登録する」アドオンなので、インストールされているアドオンの一覧に変化がなければ、LAWPが動作していてもいなくても表面上の「動作結果」は変わりません。
そのため、「実はLAWP自身のNative Messaging Host呼び出しも失敗していた」とは気付きにくい状況だったのでした。</p>
<h4 id="native-messaging-hostを呼び出せなくなっている時に起こっていることの詳細">Native Messaging Hostを呼び出せなくなっている時に起こっていることの詳細</h4>
<p>Native Messaging Host呼び出しが失敗している箇所として前項で1から4までを仮定し、1については可能性を否定できましたが、2から4までのどれが実際の原因箇所かはまだ分かりません。
そこで、Native Messaging Host呼び出しに失敗している時に何が起こっているのかをより詳細に調べてみました。</p>
<p>当社製アドオンのNative Messaging Hostは、それ自身もまたデバッグ用のログを出力する機能を持っています。
そこでNative Messaging Host側のログを確認してみたところ、現象の発生時には、そもそも全くログが出力されていないことが分かりました。
このことから、現象の発生原因となっている箇所は、前項で述べた2、すなわち「ThunderbirdがNative Messaging Hostのプロセスを起動できていない」という点であると判断できました。</p>
<p>この時の様子をWindowsのタスクマネージャーで確認し、正常動作時と比較したところ、以下の事が分かりました。</p>
<ul>
<li>Thunderbirdは直接Native Messaging Hostのプロセスを起動するのではなく、conhost.exeというプロセスを経由して起動している。
これは、WindowsアプリケーションがGUIを持たないプロセスを起動する時に共通して使う、Windowsの機能である。</li>
<li>正常は、LAWPの初期化処理中にconhost.exeのプロセスが大量に起動され、その後しばらくして、それらのconhost.exeのプロセスが一斉に終了する。</li>
<li>現象発生時は、LAWPの初期化処理中にconhost.exeのプロセスが大量に起動された後、<em>それらのconhost.exeのプロセスが終了せずにそのまま残留している</em>。</li>
</ul>
<p>Google Chrome拡張機能のNative Messagingの仕様では、Native Messaging Hostの動作として「1つのプロセスを起動して、ソケット通信で継続的にデータをやり取りする」モードと「要求の度にプロセスを起動して、処理が終わったらプロセスが終了する」モードの2つがあることになっていますが、Thunderbirdはそのうちの後者にのみ対応しています。
LAWPのNative Messaging Hostは「このアドオンの情報を1つだけWindowsのレジストリーに登録せよ・レジストリーから削除せよ」というごく単純な指示を受け取って動作するよう設計されているため、インストールされているアドオンの数だけNative Messaging Hostが起動されることになります。
conhost.exeのプロセスが大量に起動するのは、そのためです。</p>
<p>このとき使用するAPIの<code>browser.runtime.sendNativeMessage()</code>はPromiseを返すため、戻り値を受け取るためには、一般的には以下の例のように、<code>await</code>で解決を待つことになります。</p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">addons</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">browser</span><span class="p">.</span><span class="nx">management</span><span class="p">.</span><span class="nx">getAll</span><span class="p">();</span>
<span class="k">for</span> <span class="p">(</span><span class="kd">const</span> <span class="nx">addon</span> <span class="k">of</span> <span class="nx">addons</span><span class="p">)</span> <span class="p">{</span>
<span class="k">await</span> <span class="nx">browser</span><span class="p">.</span><span class="nx">runtime</span><span class="p">.</span><span class="nx">sendNativeMessage</span><span class="p">(...);</span>
<span class="p">}</span>
</code></pre></div></div>
<p>ただ、この例のようにそれぞれの呼び出しで毎回<code>await</code>で解決を待つと、無駄な待ち時間が生じてしまいます。
そのため、こういった非同期APIのそれぞれの呼び出し同士に依存関係が無い場合は、以下のように<code>Promise.all()</code>を使うのがセオリーとなっています。</p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">addons</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">browser</span><span class="p">.</span><span class="nx">management</span><span class="p">.</span><span class="nx">getAll</span><span class="p">();</span>
<span class="k">await</span> <span class="nb">Promise</span><span class="p">.</span><span class="nx">all</span><span class="p">(</span>
<span class="nx">addons</span><span class="p">.</span><span class="nx">map</span><span class="p">(</span>
<span class="k">async</span> <span class="nx">addon</span> <span class="o">=></span> <span class="nx">browser</span><span class="p">.</span><span class="nx">runtime</span><span class="p">.</span><span class="nx">sendNativeMessage</span><span class="p">(...)</span>
<span class="p">)</span>
<span class="p">);</span>
</code></pre></div></div>
<p>後者のような実装の仕方では、ごく短時間(数マイクロ秒~数ミリ秒)の間にconhost.exeが大量に起動されることになります。
JavaScriptは基本的にシングルスレッドのため、このような実装であってもメモリ破壊などは原則として発生し得ませんが、JavaScriptのコードの先にいるconhost.exeやその呼び出し箇所(C++で実装されていると思われる)はその限りではありません。
もしconhost.exeやその呼び出し箇所に、ごく短時間の間に複数回実行されることで問題が発生する何らかの不具合があるならば、後者のような実装を前者のように改めることで、処理に要する時間が増大する代わりに安定した動作を得られるようになる可能性があります。</p>
<p>そこで、後者のような効率重視の実装になっていた箇所を、前者のようなベタな実装に変更して動作を検証してみたところ、これがまさに大当たりで、当社検証環境においては現象が再現しなくなりました。
Thunderbirdとconhost.exe(Windows)のどちらのに責任があるのかは現時点では不明ですが、状況から見て、<em>「Thunderbirdがconhost.exeのプロセスを一度に大量に起動すると、conhost.exeが正常に動作・終了しなくなる」という根本の問題があり、LAWPが前述のような実装の仕方になっているためにその問題が表面化した</em>、というのがこの現象の正体である模様です。</p>
<p>以上の調査はあくまで当社の検証環境で行っており、実際の環境で起こっている現象は全く別の原因によるものである可能性もあります。
そこでお客さまには、現象発生時のconhost.exeのプロセスの状態(conhost.exeのプロセスが大量に残留するかどうか)の確認と、前述の改修を行ったバージョンのLAWPの動作テストをお願いしました。
果たして、お客さま環境においても現象発生時にはconhost.exeのプロセスが大量に残留していること、改修版のLAWPでは現象が発生しなくなったことをご確認頂けました。</p>
<h3 id="問題の解決">問題の解決</h3>
<h4 id="とりあえずの対応">とりあえずの対応</h4>
<p>お客様環境での検証結果を承け、改修を反映したLAWPをバージョン2.1としてリリースしました。
今回の改修範囲はアドオン本体のみに留まっており、Native Messaging Host側には変更が無いため、アドオンの自動更新を有効にしている環境ではすでに更新が反映されているものと思われます。
企業内などでアドオンの自動更新を無効化している場合は、<a href="https://addons.thunderbird.net/thunderbird/addon/list-addons-in-win-programs/">配布ページ</a>からXPIパッケージを入手して各端末にインストールして頂く必要があります。</p>
<p>今回の改修方法は、改修範囲がアドオン側のみに留まるメリットがありますが、インストールされているアドオンの数が増えるほど待ち時間が増えて、処理の完了までにかかる時間が長くなるというデメリットもあります。
今回は以下の理由から、この改修方法のままリリースすることとしました。</p>
<ul>
<li>お客さまからのお問い合わせに基づく改修で、なるべく早く対応することが望ましい。改修範囲が広がると、それだけ検証にも時間がかかるので、なるべく小規模の改修で済ませたかった。</li>
<li>LAWPというアドオン(Thunderbirdのアドオンマネージャーが認識しているアドオンを、資産管理ソフト等で管理しやすくするようにWindowsのレジストリーに登録する)の性質上、それほど高い性能が要求されるわけではない。</li>
</ul>
<p>ただ、これはあくまで、すでにある実装をそのまま使った方が改修コストを削減できるという前提があっての判断です。
新たにこのような機能を実装する場面では、より望ましい実装方法を模索するべきなのは間違い無いです。</p>
<h4 id="アドオン開発においてnative-messagingの使用時に気をつけるべきこと">アドオン開発においてNative Messagingの使用時に気をつけるべきこと</h4>
<p>一般的に、メンテナンス性・開発の継続性の維持を重視する観点からは、ソフトウェアのモジュール同士は疎結合にしておくことが望ましいです。
LAWPにおける「1回のNative Messaging Host呼び出しで1件の情報をレジストリへ書き込む」設計は、モジュール同士を疎結合にする観点で取った選択でした。</p>
<p>しかしながら、今回の調査の過程で、Native Messaging Hostへの大量の呼び出しを一度に行う事には思わぬリスクがあることが分かりました。
Native Messagingを使用する場合は、<em>安全のために、Native Messaging Hostの呼び出し回数がなるべく少なく済むような設計とすることが望ましい</em>と言えるでしょう。
例えば、今回取り上げたLAWPのようなアドオンであれば、<em><code>browser.runtime.sendNativeMessage()</code>の引数で指定してNative Messaging Hostに渡すメッセージや、Native Messaging Hostが返却するメッセージは、「複数の処理対象を配列でまとめて指定する」「処理結果を配列で返す」といった形で一括処理を行える設計にする</em>といったやり方<sup class="footnote-ref"><a href="#fn1" id="fnref1">1</a></sup>が考えられます。</p>
<p>ただ、そのような設計がそぐわないケースもあります。
例えば、当社開発の<a href="https://gitlab.com/clear-code/browserselector">BrowserSelector</a>の<a href="https://addons.mozilla.org/firefox/addon/browserselector/">Firefox用アドオン</a>では、「Firefox上で遷移しようとしているWebページのURLがWindowsのセキュリティゾーン設定においてどのゾーンと判定されるかを、Windowsのネイティブアプリケーション向けAPIを用いて判定する」必要があるために、ページ遷移の要求が生じる度に必ずNative Messaging Hostを呼ぶようになっています。
読み込み要求は都度処理しなくてはならないため、「読み込み要求を一定数溜め込んでからバッチ処理する」ことは、残念ながらできません。</p>
<p>BrowserSelectorの事例では、Windowsのセキュリティゾーンに基づく判定が必要ないのであれば、個々の読み込み要求に対して必ずしもNative Messaging Hostを呼び出す必要はありません。
そのため、<a href="https://gitlab.com/clear-code/browserselector/-/merge_requests/64">判定をアドオン側で行えるならそれで済ませて、Native Messaging Hostの呼び出しを減らす改修</a>を検討しています。</p>
<h3 id="まとめ">まとめ</h3>
<p>Thunderbird用のアドオンの<a href="https://addons.thunderbird.net/thunderbird/addon/flex-confirm-mail/">FlexConfirmMail</a>と<a href="https://addons.thunderbird.net/thunderbird/addon/list-addons-in-win-programs/">List Addons in Windows' Programs(以下、LAWP)</a>について、これらを併用しているお客さまから寄せられたお問い合わせに基づいて行った調査の経緯と解決までの顛末をご紹介しました。
また、その際に得られた知見に基づいて、Firefox・Thunderbird用アドオン開発時においてNative Messagingを安全に使うための注意点もご紹介しました。</p>
<p>当社では、FirefoxやThunderbirdの法人運用におけるトラブルの原因究明や回避方法の調査、社内事情に合わせるためのアドオンの改修、アドオンの新規開発のご依頼など、様々なお問い合わせへの対応を有償にて承っております。
こういった事柄についてお悩みを抱えている企業のご担当者さまは、<a href="/contact/">お問い合わせフォームよりご相談ください</a>。</p>
<section class="footnotes">
<ol>
<li id="fn1">
<p>なお、<a href="https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Native_messaging#app_side">仕様</a>上も<a href="https://searchfox.org/mozilla-central/rev/9d88be0557a5bc39d3da288f9aff71414baa303e/toolkit/components/extensions/NativeMessaging.sys.mjs#27-35">実装</a>上も、アドオンからNative Messaging Hostへ送信できるメッセージの最大サイズは1回あたり4GBまで、Native Messaging Hostからアドオンへ返却できるメッセージの最大サイズは1回あたり1MBまでという制約があるので、これを超えるサイズのメッセージをやり取りする場合は、複数個のメッセージへの分割が必要となります。 <a href="#fnref1" class="footnote-backref">↩</a></p>
</li>
</ol>
</section>
-
利用しているgemと互換性のあるRubyのバージョンを調べる方法
https://www.clear-code.com/blog/2024/1/15/check-gem-with-compatible-ruby-version.html
2024-01-15T00:00:00+09:00
はじめに
パッケージにRubyおよび必要なgemがバンドルされている場合、メジャーアップグレードにともない、バンドルされているRubyの処理系のバージョンも更新されていることがあります。
パッケージにバンドルされているgemのみ使用している場合には問題になりませんが、追加で必要なgemをインストールしている場合には、採用しているgemが新しいパッケージにバンドルされている処理系で動作するのかを知りたいことがあります。
今回は利用しているgemと互換性のあるRubyのバージョンを事前に調べる方法を紹介します。
互換性の有無の確認方法
gemではどのバージョンのRubyで動作するのか、メタ情報として指定します。
具体的には.gemspecのGem::Specificationにおけるrequired_ruby_versionが該当します。
このメタ情報は、rubygems.orgの各gemのページを参照すると「必要なRubyのバージョン」として参照できます。
パッケージに同梱されているRubyのバージョンが更新されている場合には、「必要なRubyのバージョン」を満たせているかを確認できればよいわけです。
自分で追加したgemの数が少ないうちは各gemのページを確認するのでもよいのですが、数が増えてくるとやや大変です。
APIを使って必要なRubyのバージョンを確認してみる
追加したgemのリストをもとに次のようにして調べられます。
gemの対象バージョンを調べる(たいていは最新のバージョン)
gemの特定バージョンにおける必要なRubyのバージョンを調べる
どちらもrubygems.orgにて提供されているGEM VERSION METHODS APIを利用して取得できます。
例えば、fluent-plugin-elasticsearchの最新バージョンは次のコマンドで取得できます。
$ curl --silent https://rubygems.org/api/v1/versions/fluent-plugin-elasticsearch/latest.json | jq --raw-output ".version"
5.3.0
これをもとに、必要なRubyのバージョンを調べてみましょう。
$ curl --silent https://rubygems.org/api/v2/rubygems/fluent-plugin-elasticsearch/versions/5.3.0 | jq --raw-output ".ruby_version"
>= 2.3
このように、Ruby 2.3以降であればよいことがわかります。 1
必要なRubyバージョンの状況
ではより具体的な例で確認してみましょう。
Fluentdのディストリビューションであるtd-agentからfluent-packageに移行する場合に問題になりそうなのは、どれくらいありそうかという題材で調べてみます。
例えば、Fluentdのpluginにおける対応状況は次のようになっています。2
バージョンの制限の有無
プラグイン数
指定あり
163
制限なし(>=0)
870
不明
122
全件が1155なので、制限のないものがほとんどです。しかし、明示的に指定のあるもの、不明なものがあることがわかります。
td-agentからfluent-packageに移行する場合に問題になりそうなのは、次のバージョンです。
td-agentはRuby 2.7.8 (一部例外あり)
fluent-packageはRuby 3.2.2
したがって、Ruby 3.2.2以降に対応しているかどうかが分岐点となります。
必要なバージョンのパターンはこれだけありました。
< 2.5.0, >= 2.1.0
> 2.1
>= 1.9.1
>= 1.9.2
>= 1.9.3
>= 2.0
>= 2.0.0
>= 2.1
>= 2.1, < 4
>= 2.1.0
>= 2.1.2
>= 2.2
>= 2.2.0
>= 2.3
>= 2.3.0
>= 2.4
>= 2.4.0
>= 2.5
>= 2.5.0
>= 2.6
>= 2.6.0
>= 2.6.3
>= 2.7
>= 2.7.0
>= 3.1
>= 3.1.2
~> 2
~> 2.0
~> 2.1
~> 3.1
3.2.2に対応しているかどうかという観点だと、次の制限がひっかかります。
< 2.5.0, >= 2.1.0
~> 2
~> 2.0
~> 2.1
これらは、3.2.2では動作しないということになります。
具体的には、以下のgemが制限にひっかかることがわかっています。(バージョンによる制限が不明なものを除きます。)
fluent-plugin-postgresql_csv (< 2.5.0, >= 2.1.0)
2018年リリース 0.0.7が最新。
https://github.com/masterlee998/ は404。gemのみ登録されている。
fluent-plugin-s3in (~>2)
2015年リリース0.1.2.2が最新。
https://github.com/shii-take/fluent-plugin-s3in
fluent-plugin-keyvalue-parser (~> 2.0)
2017年リリース 0.1.4 (pre-release)が最新。
https://github.com/nom3ad/fluent-plugin-keyvalue-parser
fluent-plugin-parser_cef (~> 2.1)
https://github.com/lunardial/fluent-plugin-parser_cef
2017年リリース 1.0.0が最新。
fluent-plugin-tail-ex-rotate (~> 2.1)
2017年リリース 0.1.1が最新。
https://github.com/ymizushi/fluent-plugin-tail-ex-rotate
制限が不明なgemについては、個別にみていくしかありません。
おわりに
今回は、利用しているgemと互換性のあるRubyのバージョンを調べる方法を紹介しました。
実用上は、さらに依存先のgemとの互換性などもチェックする必要がでてきたりします。
そちらについては割愛します。
必要なRubyバージョンが「なし」もしくは「>= 0」となっていることがあります。その場合はリポジトリのドキュメントを参照するか、CIがRubyのバージョンごとに整備されていればそちらを確認してもよいでしょう。 ↩
fluent-plugin-と命名されているものが対象となっています。 ↩
<h3 id="はじめに">はじめに</h3>
<p>パッケージにRubyおよび必要なgemがバンドルされている場合、メジャーアップグレードにともない、バンドルされているRubyの処理系のバージョンも更新されていることがあります。</p>
<p>パッケージにバンドルされているgemのみ使用している場合には問題になりませんが、追加で必要なgemをインストールしている場合には、採用しているgemが新しいパッケージにバンドルされている処理系で動作するのかを知りたいことがあります。</p>
<p>今回は利用しているgemと互換性のあるRubyのバージョンを事前に調べる方法を紹介します。</p>
<!--more-->
<h3 id="互換性の有無の確認方法">互換性の有無の確認方法</h3>
<p>gemではどのバージョンのRubyで動作するのか、メタ情報として指定します。</p>
<p>具体的には.gemspecの<code>Gem::Specification</code>における<code>required_ruby_version</code>が該当します。</p>
<p>このメタ情報は、rubygems.orgの各gemのページを参照すると「必要なRubyのバージョン」として参照できます。</p>
<p>パッケージに同梱されているRubyのバージョンが更新されている場合には、「必要なRubyのバージョン」を満たせているかを確認できればよいわけです。</p>
<p>自分で追加したgemの数が少ないうちは各gemのページを確認するのでもよいのですが、数が増えてくるとやや大変です。</p>
<h3 id="apiを使って必要なrubyのバージョンを確認してみる">APIを使って必要なRubyのバージョンを確認してみる</h3>
<p>追加したgemのリストをもとに次のようにして調べられます。</p>
<ul>
<li>gemの対象バージョンを調べる(たいていは最新のバージョン)</li>
<li>gemの特定バージョンにおける必要なRubyのバージョンを調べる</li>
</ul>
<p>どちらもrubygems.orgにて提供されている<a href="https://guides.rubygems.org/rubygems-org-api/#gem-version-methods">GEM VERSION METHODS API</a>を利用して取得できます。</p>
<p>例えば、fluent-plugin-elasticsearchの最新バージョンは次のコマンドで取得できます。</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>curl <span class="nt">--silent</span> https://rubygems.org/api/v1/versions/fluent-plugin-elasticsearch/latest.json | jq <span class="nt">--raw-output</span> <span class="s2">".version"</span>
5.3.0
</code></pre></div></div>
<p>これをもとに、必要なRubyのバージョンを調べてみましょう。</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>curl <span class="nt">--silent</span> https://rubygems.org/api/v2/rubygems/fluent-plugin-elasticsearch/versions/5.3.0 | jq <span class="nt">--raw-output</span> <span class="s2">".ruby_version"</span>
<span class="o">>=</span> 2.3
</code></pre></div></div>
<p>このように、Ruby 2.3以降であればよいことがわかります。 <sup class="footnote-ref"><a href="#fn1" id="fnref1">1</a></sup></p>
<h3 id="必要なrubyバージョンの状況">必要なRubyバージョンの状況</h3>
<p>ではより具体的な例で確認してみましょう。
Fluentdのディストリビューションである<code>td-agent</code>から<code>fluent-package</code>に移行する場合に問題になりそうなのは、どれくらいありそうかという題材で調べてみます。</p>
<p>例えば、Fluentdのpluginにおける対応状況は次のようになっています。<sup class="footnote-ref"><a href="#fn2" id="fnref2">2</a></sup></p>
<table>
<thead>
<tr>
<th>バージョンの制限の有無</th>
<th>プラグイン数</th>
</tr>
</thead>
<tbody>
<tr>
<td>指定あり</td>
<td>163</td>
</tr>
<tr>
<td>制限なし(>=0)</td>
<td>870</td>
</tr>
<tr>
<td>不明</td>
<td>122</td>
</tr>
</tbody>
</table>
<p>全件が1155なので、制限のないものがほとんどです。しかし、明示的に指定のあるもの、不明なものがあることがわかります。</p>
<p><code>td-agent</code>から<code>fluent-package</code>に移行する場合に問題になりそうなのは、次のバージョンです。</p>
<ul>
<li>td-agentはRuby 2.7.8 (一部例外あり)</li>
<li>fluent-packageはRuby 3.2.2</li>
</ul>
<p>したがって、Ruby 3.2.2以降に対応しているかどうかが分岐点となります。
必要なバージョンのパターンはこれだけありました。</p>
<ul>
<li><code>< 2.5.0, >= 2.1.0</code></li>
<li><code>> 2.1</code></li>
<li><code>>= 1.9.1</code></li>
<li><code>>= 1.9.2</code></li>
<li><code>>= 1.9.3</code></li>
<li><code>>= 2.0</code></li>
<li><code>>= 2.0.0</code></li>
<li><code>>= 2.1</code></li>
<li><code>>= 2.1, < 4</code></li>
<li><code>>= 2.1.0</code></li>
<li><code>>= 2.1.2</code></li>
<li><code>>= 2.2</code></li>
<li><code>>= 2.2.0</code></li>
<li><code>>= 2.3</code></li>
<li><code>>= 2.3.0</code></li>
<li><code>>= 2.4</code></li>
<li><code>>= 2.4.0</code></li>
<li><code>>= 2.5</code></li>
<li><code>>= 2.5.0</code></li>
<li><code>>= 2.6</code></li>
<li><code>>= 2.6.0</code></li>
<li><code>>= 2.6.3</code></li>
<li><code>>= 2.7</code></li>
<li><code>>= 2.7.0</code></li>
<li><code>>= 3.1</code></li>
<li><code>>= 3.1.2</code></li>
<li><code>~> 2</code></li>
<li><code>~> 2.0</code></li>
<li><code>~> 2.1</code></li>
<li><code>~> 3.1</code></li>
</ul>
<p>3.2.2に対応しているかどうかという観点だと、次の制限がひっかかります。</p>
<ul>
<li><code>< 2.5.0, >= 2.1.0</code></li>
<li><code>~> 2</code></li>
<li><code>~> 2.0</code></li>
<li><code>~> 2.1</code></li>
</ul>
<p>これらは、3.2.2では動作しないということになります。</p>
<p>具体的には、以下のgemが制限にひっかかることがわかっています。(バージョンによる制限が不明なものを除きます。)</p>
<ul>
<li>fluent-plugin-postgresql_csv (<code>< 2.5.0, >= 2.1.0</code>)
<ul>
<li>2018年リリース 0.0.7が最新。</li>
<li><a href="https://github.com/masterlee998/">https://github.com/masterlee998/</a> は404。gemのみ登録されている。</li>
</ul>
</li>
<li>fluent-plugin-s3in (<code>~>2</code>)
<ul>
<li>2015年リリース0.1.2.2が最新。</li>
<li><a href="https://github.com/shii-take/fluent-plugin-s3in">https://github.com/shii-take/fluent-plugin-s3in</a></li>
</ul>
</li>
<li>fluent-plugin-keyvalue-parser (<code>~> 2.0</code>)
<ul>
<li>2017年リリース 0.1.4 (pre-release)が最新。</li>
<li><a href="https://github.com/nom3ad/fluent-plugin-keyvalue-parser">https://github.com/nom3ad/fluent-plugin-keyvalue-parser</a></li>
</ul>
</li>
<li>fluent-plugin-parser_cef (<code>~> 2.1</code>)
<ul>
<li><a href="https://github.com/lunardial/fluent-plugin-parser_cef">https://github.com/lunardial/fluent-plugin-parser_cef</a></li>
<li>2017年リリース 1.0.0が最新。</li>
</ul>
</li>
<li>fluent-plugin-tail-ex-rotate (<code>~> 2.1</code>)
<ul>
<li>2017年リリース 0.1.1が最新。</li>
<li><a href="https://github.com/ymizushi/fluent-plugin-tail-ex-rotate">https://github.com/ymizushi/fluent-plugin-tail-ex-rotate</a></li>
</ul>
</li>
</ul>
<p>制限が不明なgemについては、個別にみていくしかありません。</p>
<h3 id="おわりに">おわりに</h3>
<p>今回は、利用しているgemと互換性のあるRubyのバージョンを調べる方法を紹介しました。
実用上は、さらに依存先のgemとの互換性などもチェックする必要がでてきたりします。
そちらについては割愛します。</p>
<section class="footnotes">
<ol>
<li id="fn1">
<p>必要なRubyバージョンが「なし」もしくは「>= 0」となっていることがあります。その場合はリポジトリのドキュメントを参照するか、CIがRubyのバージョンごとに整備されていればそちらを確認してもよいでしょう。 <a href="#fnref1" class="footnote-backref">↩</a></p>
</li>
<li id="fn2">
<p><code>fluent-plugin-</code>と命名されているものが対象となっています。 <a href="#fnref2" class="footnote-backref">↩</a></p>
</li>
</ol>
</section>
-
Open Source Summit Japan 2023に参加してきました!
https://www.clear-code.com/blog/2023/12/18/attending-os-summit-japan-2023.html
2023-12-18T00:00:00+09:00
2023年12月4日~6日にかけて開催された、Open Source Summit Japan 2023に、クリアコードメンバーが「New Chapter of Fluentd, Rebranding and New Release Cycle (LTS)」と題して Fluentdの長期サポートを提供するためのパッケージに関する発表をすることになったので、応援&勉強を兼ねて参加してきました。
コードを書く以外の仕事をしている吉本です。
これまで、ソフトウェア開発以外の業界で長く働いており、各業界でイベントやカンファレンスに主催者、登壇者、参加者、運営者など様々な形で参加してきましたが、今回がクリアコードに入って初のオフラインイベントでした。
とても興味深く学びが多かったです。
まず、プレゼンテーションは基本的にすべて英語で行われていて、国外参加者が4~5割くらいいそうな、国際カンファレンス感がとてもありました。キーセッション以外は、司会がいなかったので、時間になったときに登壇者の人がうまく開始して、終わりもうまく終わらせる形式で新鮮でした。
オープンソースの文脈でよく見かけるLinux Foundationについていろいろ知れたことも勉強になりました。エンジニアよりも、プロジェクトをマネージメントや運用をサポートするためのエンジニアではないスタッフが多いことを聞いて、私ももっといろいろやれることが有るのかなと思いました。
基調講演の中に、Linuxの生みの親のLinus Torvalds氏とVerizon社のOpen Source Program OfficeのDirk Hohndel氏の対談登壇があり、二人が登壇すると会場中のスマホが向けられていて、アイドル感を感じました。二人の話から、今でも新しい技術を取り入れてよりよく進化させていく事の意味と、30年以上も同じプロジェクトに関わる人がいるくらいオープンソースの開発が魅了されるものなんだなぁと感じました。また、OSS Gateの活動で目指している「新しくOSS開発に関わる人を増やしたい」に通じる悩みである「開発者の高齢化」「レビュワー育成の難しさ」みたいなものをやっぱり持っている事を知れるような話が聞けたことも良かったです。
そのほかいくつかのプレゼンテーションに参加しましたが、やっぱり海外の人のプレゼンテーションは、動きが多いなと思いました。それを想定しているのか、ピンマイクだったのが今まで参加したことのあるイベントとはちょっと違うなぁと感じました。
あと、重要でない気づきポイントとして、予想はできていたけれど予想以上にみんなスーツ着ないんだなって思いました。そして、海外の参加者は知り合い同士っぽい雰囲気がありました。
今回参加したイベントの概要
開催期間:2023年12月4日(月)~6日(木)
会場:有明セントラルタワーホール&カンファレンス
公式ページ:https://events.linuxfoundation.org/open-source-summit-japan/
Open Source Summit Japanは、日本で開催される大規模なカンファレンスで、オープンソースエコシステムが一堂に会します。技術者やオープンソースリーダー企業が、コラボレーションと情報共有のために、そして最新のオープンソース技術を学ぶために、あるいは革新的なオープンソリューションを使った競争力の付け方を見つけるために集結します。
Open Source Summitは、今日のオープンソースに影響を与える最も重要な技術、トピック、および問題をカバーするイベントの集まりで構成される、カンファレンスアンブレラです。(公式ページより抜粋)
イベント会場の様子を少しだけ
キーノート開催時の盛況な様子
名札は自由にカスタマイズするしくみを採用していました。ソーシャルディスタンスの表示だったり、関連する分野を示すリボンがあって、自分好みに修正できていました。これだけ多くの分野のカンファレンスが同時にできるのは面白いなぁと思いました。
求人ボードがありました。こういったカンファレンスでの縁から仕事になる話はよく聞くので、大事なアクションなんだろうなと思いました。
登壇した二人と記念撮影しました。撮影場所を探してうろうろしているときのスタッフさんに親切にしてもらいました。
まとめ
コロナ禍中入社だったため、久しぶり&慣れないイベント参加でちょっと緊張しましたが、やっぱり楽しいなぁと感じました。登壇をした二人はもっと大変だったと思うのですが、英語の発表という事で発表の準備お手伝いもしたので、無事に終わってほっとする気持ちになりました。
まだまだ上手にお話はできませんでしたが、お世話になっている会社の方にご挨拶できたり、海外のスタンダードや最新の傾向を知れたり、オープンソースに関する知見が増えたりしたので、参加できてよかったです。
クリアコードでは、こういったイベント参加を後押ししてもらえるので、こういった機会があればまた参加してみたいです。どこかの会場でククログを読んでいる人に出会ったらご挨拶できたらすごいなって思っています。
<p>2023年12月4日~6日にかけて開催された、Open Source Summit Japan 2023に、クリアコードメンバーが<a href="/blog/2023/12/7/ossjapan2023-fluent-package-lts.html">「New Chapter of Fluentd, Rebranding and New Release Cycle (LTS)」と題して Fluentdの長期サポートを提供するためのパッケージに関する発表をする</a>ことになったので、応援&勉強を兼ねて参加してきました。</p>
<!--more-->
<p>コードを書く以外の仕事をしている吉本です。</p>
<p>これまで、ソフトウェア開発以外の業界で長く働いており、各業界でイベントやカンファレンスに主催者、登壇者、参加者、運営者など様々な形で参加してきましたが、今回がクリアコードに入って初のオフラインイベントでした。</p>
<p>とても興味深く学びが多かったです。</p>
<p>まず、プレゼンテーションは基本的にすべて英語で行われていて、国外参加者が4~5割くらいいそうな、国際カンファレンス感がとてもありました。キーセッション以外は、司会がいなかったので、時間になったときに登壇者の人がうまく開始して、終わりもうまく終わらせる形式で新鮮でした。</p>
<p>オープンソースの文脈でよく見かけるLinux Foundationについていろいろ知れたことも勉強になりました。エンジニアよりも、プロジェクトをマネージメントや運用をサポートするためのエンジニアではないスタッフが多いことを聞いて、私ももっといろいろやれることが有るのかなと思いました。</p>
<p>基調講演の中に、Linuxの生みの親のLinus Torvalds氏とVerizon社のOpen Source Program OfficeのDirk Hohndel氏の対談登壇があり、二人が登壇すると会場中のスマホが向けられていて、アイドル感を感じました。二人の話から、今でも新しい技術を取り入れてよりよく進化させていく事の意味と、30年以上も同じプロジェクトに関わる人がいるくらいオープンソースの開発が魅了されるものなんだなぁと感じました。また、OSS Gateの活動で目指している「新しくOSS開発に関わる人を増やしたい」に通じる悩みである「開発者の高齢化」「レビュワー育成の難しさ」みたいなものをやっぱり持っている事を知れるような話が聞けたことも良かったです。</p>
<p>そのほかいくつかのプレゼンテーションに参加しましたが、やっぱり海外の人のプレゼンテーションは、動きが多いなと思いました。それを想定しているのか、ピンマイクだったのが今まで参加したことのあるイベントとはちょっと違うなぁと感じました。</p>
<p>あと、重要でない気づきポイントとして、予想はできていたけれど予想以上にみんなスーツ着ないんだなって思いました。そして、海外の参加者は知り合い同士っぽい雰囲気がありました。</p>
<h2 id="今回参加したイベントの概要">今回参加したイベントの概要</h2>
<ul>
<li>開催期間:2023年12月4日(月)~6日(木)</li>
<li>会場:有明セントラルタワーホール&カンファレンス</li>
<li>公式ページ:<a href="https://events.linuxfoundation.org/open-source-summit-japan/">https://events.linuxfoundation.org/open-source-summit-japan/</a></li>
</ul>
<blockquote>
<p>Open Source Summit Japanは、日本で開催される大規模なカンファレンスで、オープンソースエコシステムが一堂に会します。技術者やオープンソースリーダー企業が、コラボレーションと情報共有のために、そして最新のオープンソース技術を学ぶために、あるいは革新的なオープンソリューションを使った競争力の付け方を見つけるために集結します。
Open Source Summitは、今日のオープンソースに影響を与える最も重要な技術、トピック、および問題をカバーするイベントの集まりで構成される、カンファレンスアンブレラです。(公式ページより抜粋)</p>
</blockquote>
<h3 id="イベント会場の様子を少しだけ">イベント会場の様子を少しだけ</h3>
<p><img src="/images/blog/ossummit-japan-2023/ossummit-keysession.jpg" alt="キーノート開催時の盛況な様子" /></p>
<p>キーノート開催時の盛況な様子</p>
<p><img src="/images/blog/ossummit-japan-2023/ossummit-budges.jpg" alt="名札は自由にスタマイズ。ソーシャルディスタンスの表示だったり、関連する分野を示すリボンがあって、自分好みに修正できていました。これだけ多くの分野のカンファレンスが同時にできるのは面白いなぁと思いました。" /></p>
<p>名札は自由にカスタマイズするしくみを採用していました。ソーシャルディスタンスの表示だったり、関連する分野を示すリボンがあって、自分好みに修正できていました。これだけ多くの分野のカンファレンスが同時にできるのは面白いなぁと思いました。</p>
<p><img src="/images/blog/ossummit-japan-2023/ossummit-jobhunting.jpg" alt="求人ボードがありました。こういったところの縁から仕事になる話はよく聞くので、大事なアクションなんだろうなと思いました。" /></p>
<p>求人ボードがありました。こういったカンファレンスでの縁から仕事になる話はよく聞くので、大事なアクションなんだろうなと思いました。</p>
<p><img src="/images/blog/ossummit-japan-2023/osssummit-japan-3shots.jpg" alt="登壇した二人と記念撮影しました。撮影場所を探してうろうろしてしまいました。" /></p>
<p>登壇した二人と記念撮影しました。撮影場所を探してうろうろしているときのスタッフさんに親切にしてもらいました。</p>
<h2 id="まとめ">まとめ</h2>
<p>コロナ禍中入社だったため、久しぶり&慣れないイベント参加でちょっと緊張しましたが、やっぱり楽しいなぁと感じました。登壇をした二人はもっと大変だったと思うのですが、英語の発表という事で発表の準備お手伝いもしたので、無事に終わってほっとする気持ちになりました。</p>
<p>まだまだ上手にお話はできませんでしたが、お世話になっている会社の方にご挨拶できたり、海外のスタンダードや最新の傾向を知れたり、オープンソースに関する知見が増えたりしたので、参加できてよかったです。</p>
<p>クリアコードでは、こういったイベント参加を後押ししてもらえるので、こういった機会があればまた参加してみたいです。どこかの会場でククログを読んでいる人に出会ったらご挨拶できたらすごいなって思っています。</p>