IdeaVim が意外とよく出来ている

最近はコーディングに JetBrains の IntelliJ IDEA を使う事が多い。元々は Scala を書くために導入したものだが、プラグインを導入すれば TypeScript や Go、Ruby、その他様々な言語もサポートしてくれるようになるので広く使うようになった。

IntelliJ IDEA には Vim のようにエディタを操作できる IdeaVim というプラグインを導入して使用している。Vim のモードやカーソル移動がサポートされていれば十分と思っていたが、調べていくと意外と高機能であった。

設定は .ideavimrc に記述できる。

IdeaVim の設定は .vimrc と同様に .ideavimrc に記述することができる。

source コマンドで .ideavimrc から既存の .vimrc を読み込んだりすることができる。

source ~/.vimrc

set multiple-cursors
set clipboard=unnamed
set scrolloff=10
set sidescrolloff=16

Vim プラグインのサポート

驚くべきことに Vim プラグインにも対応している。 ドキュメントを見る限り対応プラグインは限定的だが、 vim-plug と同様に .ideavimrc に Plug 'foo/bar' と記述するだけで使用可能になる。

対応プラグインの一覧を見ると NERDTree にも対応していた。IDE のプロジェクトツールウィンドウと連動して動作する。

Plug 'preservim/nerdtree'
set NERDTree

キーマップ

キーマップにも対応している。Vim を使うときは;をリーダーとして設定し様々なアクションを設定していたので、こちらも同じように設定してみる。

let mapleader = ";"
nnoremap <Leader>n :NERDTreeToggle<CR>
nnoremap <Leader>f :action GotoFile<CR>

IDE 側のアクションも呼び出せるようになっているので、Vimfzf を呼び出すように、ファイル検索のランチャーが起動するように設定している。用意されているアクション一覧は :actionlist で確認することができた。

こちらによるとキーマップは IDE のエディタ部分にフォーカスが当たっているときだけ有効になるらしい。:NERDTreeToggle でプロジェクトツールウインドウに切り替わったあと、そのまま :NERDTreeToggle で閉じたりできないのは少し残念。

Rails で OpenTelemetry の Tracing を試す

OpenTelemetry の Ruby 実装である opentelemetry-ruby も開発が進んでおり、Tracing に関しては Stable となっていたので試しに Rails アプリケーションに導入して触ってみた。

データの送り先として Jaeger を準備する

まずは Tracing データの送り先として Jaeger を起動しておく。Getting Started を読みながら、All in One のイメージを実行する。

docker run --rm --name jaeger \
  -p 14268:14268 \
  -p 16686:16686 \
  jaegertracing/all-in-one:1.53

公開するポートとしては、データの投げ込み先として jaeger.thrift の 14268、 WebUI を表示するための 16686 を指定しておいた。

Rails に opentelemetry-ruby を組み込む

こちらも Getting Started を読みながら、Rails アプリケーションに opentelemetry-ruby を仕込んでいく。

まずは必要な gem を Gemfile に追記し、 bundle install する。 今回は Jaeger に飛ばしたいので、opentelemetry-exporter-jaegerも忘れずに指定しておく。

gem 'opentelemetry-sdk'
gem 'opentelemetry-instrumentation-all'
gem 'opentelemetry-exporter-jaeger'

次に config/initializers に設定ファイルを配置する。

# config/initializers/otel.rb

OpenTelemetry::SDK.configure do |c|
  c.service_name = 'rails_test'
  c.service_version = '0.1.0'

  c.resource = OpenTelemetry::SDK::Resources::Resource.create(
    OpenTelemetry::SemanticConventions::Resource::DEPLOYMENT_ENVIRONMENT => Rails.env.to_s,
    OpenTelemetry::SemanticConventions::Resource::HOST_NAME => "localhost",
  )

  c.add_span_processor(
    OpenTelemetry::SDK::Trace::Export::BatchSpanProcessor.new(
      OpenTelemetry::Exporter::Jaeger::CollectorExporter.new(endpoint: 'http://localhost:14268/api/traces')
    )
  )

  c.use_all
end

resource にはホスト名や環境名をセットしておいた。独自に resource 定義していくよりはなるべく Semantic Conventions として定義されているものを使うと良いのだろう。opentelemetry-ruby でも定数が用意されていたのでそれを使った。

exporter の設定には先程起動しておいた Jaeger のエンドポイントを指定しておく。

最後に c.use_all を設定してあるが、これで利用可能な instrumentation を自動でセットアップしてくれるらしい。

Rails アプリケーションを起動して送信されたデータを確認してみる

Rails アプリケーションを起動して適当なエンドポイントを数回叩いたあと、Jaeger が動いている http://localhost:16686/search にアクセスして確認してみる。 設定した service 名で無事データが送信されており、エンドポイントごとに検索できるようになっていた。

結果を選択して詳細を見てみると、データベースの SELECT にかかった時間なども確認できるようになっていた。

use_all するとどのような instrumentation が動くのか

見よう見まねで use_all を指定していたが、この設定でどのような instrumentation が動くのか気になるところである。 まず use_all した状態でサーバを起動すると、ログに大量のメッセージが出力されていることに気付く。

WARN -- : Instrumentation: OpenTelemetry::Instrumentation::Gruf failed to install
WARN -- : Instrumentation: OpenTelemetry::Instrumentation::Trilogy failed to install
INFO -- : Instrumentation: OpenTelemetry::Instrumentation::ActiveSupport was successfully installed with the following options {}
--- これが延々と続くので以下略 ---

基本的にはインストールされている instrumentation の gem を全て読み込みセットアップしようとするらしい。 今回だと opentelemetry-instrumentation-all という gem をインストールしていたので、opentelemetry-ruby-contrib にある全てをセットアップしようと試行している様子だった。

failed to install と表示されているように、当然アプリケーションに導入していないライブラリの instrumentation セットアップには失敗している。導入済みライブラリの検出にはクラス定義の有無を見ているようだ。(Sidekiq の 例)

動作に支障は無さそうなものの failed to install と大量に出力されるのはあまり気持ち良くないので、インストールする gem を絞るか、以下のように use_all をやめて個別に instrumentation を指定していくのが良いのかもしれない。

  # c.use_all
  c.use 'OpenTelemetry::Instrumentation::Rails'
  c.use 'OpenTelemetry::Instrumentation::PG'
  c.use 'OpenTelemetry::Instrumentation::Sidekiq'

これで使い方はなんとなく分かってきたので、ちゃんとした環境に仕込んで動かしてみたいところだ。

OpenTelemetry で PostgreSQL のメトリックを収集する


Mackerel チームで Web アプリケーションエンジニアをやっている inommm です。
この記事は Mackerel Advent Calendar 2023 12/13 の記事になります。



Mackerel では現在 OpenTelemetry の対応を進めており、 OpenTelemetry Protocol をサポートした「ラベル付きメトリック機能」はオープンベータテスト中です。 この記事では OpenTelemetry Collector で PostgreSQL のメトリックを収集し、Mackerel で閲覧できるようにする手順を紹介します。

docs.google.com

Collector の準備をする

OpenTelemetry Collector を使って、メトリックの収集と送信を行えるようにしていきます。 今回は PostgreSQL のメトリックを収集するための PostgreSQL Receiver と、収集したメトリックを Mackerel に送信するための OTLP gRPC Exporter を使用するための設定をしていきます。

Collector の設定

Collector の設定を記述する YAML ファイルを作成します。

vim config.yaml
receivers:
  postgresql:
    endpoint: 192.0.2.254:5432
    transport: tcp
    username: example
    password: ****
    databases:
      - example
    collection_interval: 10s
    tls:
      insecure: true

processors:
  batch:
    timeout: 1m

exporters:
  otlp:
    endpoint: otlp.mackerelio.com:4317
    compression: gzip
    headers:
      Mackerel-Api-Key: ****

service:
  pipelines:
    metrics:
      receivers: [postgresql]
      processors: [batch]
      exporters: [otlp]

receivers の部分で PostgreSQL に接続するための設定を書いていきます。
PostgreSQL Receiver は DB 接続時もTLS がデフォルトで有効になっているようでした。手元で動かしている PostgreSQL では TLS が無効になっているので、insecure: true を設定する必要がありました。

exporters の部分では Mackerel のエンドポイントと API キーの設定をしておきました。

Collector の起動

作成した設定ファイルを指定しつつ、Docker で collector を起動します。 イメージには PostgreSQL Receiver も含まれている otel/opentelemetry-collector-contrib を使用しました。

docker run -v $(pwd)/config.yaml:/etc/otelcol-contrib/config.yaml otel/opentelemetry-collector-contrib:latest

こちらが起動している様子です。しばらく動かしてみて、エラーがないか確認します。

2023-12-12T23:08:16.825Z        info    service/telemetry.go:90 Setting up own telemetry...
2023-12-12T23:08:16.826Z        info    service/telemetry.go:116        Serving Prometheus metrics      {"address": ":8888", "level": "Basic"}
2023-12-12T23:08:16.826Z        info    service/service.go:140  Starting otelcol-contrib...     {"Version": "0.72.0", "NumCPU": 2}
2023-12-12T23:08:16.827Z        info    extensions/extensions.go:41     Starting extensions...
2023-12-12T23:08:16.827Z        info    service/service.go:157  Everything is ready. Begin running and processing data.

問題がなければ mackerel 側で送信されたメトリックを確認してみましょう。

Mackerel で収集したメトリックの確認をする

ダッシュボードのウィジェットでクエリグラフを選択します。

検索ボックスに postgresql と入力すると、メトリックの候補がでてきます。

メトリックを選択し、保存します。

グラフが作成され、指定したデータベースのテーブル毎の行数が確認できるようになりました。

最後に

思ったよりもサクッと動かすことができてしまいました。
ひとまず「手元で試しに動かしてみた。」という感じですが、自宅で稼働している様々なミドルウェア等にも仕込んでいこうかなと思っています。

Asuswrt-Merlin を導入して RT-AX86U を簡易 DNS サーバとして動かす

家庭向けのルータを探してみても DNS サーバ相当の機能をもった機種は皆無に近い。 私が現在使用している ASUS の RT-AX86U も同様だが、 Asuswrt-Merlin というサードパーティファームウェアを導入することで可能になるようなので試してみた。

www.asuswrt-merlin.net

当然ながらメーカーのサポート外となるため、自己責任で実施する必要がある。

Asuswrt-Merlinの導入インストール

ダウンロードページからダウンロードサイトを選択し、使用している機種のファームウェアをダウンロードして zip ファイルを展開しておく。

執筆時点でのRT-AX86Uの Asuswrt-Merlin ファームウェアバージョンは388.1であった。

ルータ管理画面の /Advanced_FirmwareUpgrade_Content.asp にアクセスし、「ファームウェア手動更新」の項目からファームウェアファイルをアップロードする。

公式ファームウェアに戻したい場合も同様の手順で戻すことができる。 RT-AX86U の公式ファームウェアここから入手することができた。

SSH と カスタム設定ファイルを有効にする

ルータ設定画面の /Advanced_System_Content.asp にアクセスし、以下のように変更しておく

カスタム設定の有効化

SSH の有効化 LAN からしかアクセスしないので LAN Only にしておく

設定変更後、画面下部で変更内容を適用する。

カスタム設定ファイルについて

Asus-Merlin では SSH でログインし、/jffs/configs/ にカスタム設定ファイルを配置することで、ルータ内で動作しているデーモンの設定をオーバーライドすることができ、オーバーライドに対応したファイルの一覧はドキュメントで確認するできる。

今回は dnsmasq の設定をオーバーライドして LAN 内のドメインの名前解決をできるようにしたい。

dnsmasq の設定を変更する

/jffs/configs/dnsmasq.conf を配置し、設定ファイルをまるごと記述していってもいいが、名前解決の設定を追加したいだけであれば dnsmasq.conf.add で必要な設定だけを追記していくこともできる。

foo.example.com の名前を 192.168.0.254 に解決できるようにしたい場合、以下のように dnsmasq.conf.add を作成すれば良い。

# cat > /jffs/configs/dnsmasq.conf.add
address=/foo.example.com/192.168.0.254

作成後、dnsmasq を再起動することで設定が反映される

# service restart_dnsmasq

これで RT-AX86U が簡素な DNS サーバとして動作するようになる。

Mac のスケーリング解像度別 GPU 使用状況を Mackerel で確認する


Mackerel チームで Web アプリケーションエンジニアをやっている inommm です。
この記事は Mackerel Advent Calendar 2022 12月13日の記事になります。



macOS には複数のピクセルを使って1ドットを表現するための機能が備わっています。 高解像度のモニタを使ってなめらかな表示を行うための機能ですが、どの程度スケーリングさせるか調整できるようになっています。

作業領域を増やすためになめらかさを犠牲にしてスケーリング解像度を上げてみたりしたいわけですが、Apple の Web サイト によるとスケーリング解像度を上げていくとパフォーマンス低下が起きるようです。

解像度を調整するとパフォーマンスに影響する場合があります。

(以前は設定画面にも同様の注意書きがでていましたが Ventura では表示されなくなっていました)

実際のところこれがどのくらいパフォーマンスに影響するのか気になったため、解像度別の GPU 使用状況を Mackerel に投稿して確認してみることにしました。

スクリプトを書いて GPU 使用率を Mackerel へ投稿する

macOSGPU 使用率は powermetrics コマンドで取得することができ、現在の解像度の取得には system_profiler コマンドが使えます。 今回は以下のようなスクリプトを用意し、メトリック名に解像度を含めつつ、GPU 使用率を値として送信するようにしています。

#!/usr/bin/env ruby

require "time"
require "mackerel/client"

API_KEY = "******"
@mackerel = Mackerel::Client.new(mackerel_api_key: API_KEY)

def get_gpu_usage
  command = "powermetrics --samplers gpu_power -n1"
  `#{command}`.split("\n")
    .find { |l| l.match(/GPU Busy/) }
    .split(":")
    .last
    .sub(/%/, "")
    .strip
    .to_f
end

def get_current_resolution
  resolution_pattern = /([0-9]+\s+x\s+[0-9]+)/

  base_resolution_command = "system_profiler SPDisplaysDataType |grep 'Resolution'"
  base_resolution = `#{base_resolution_command}`.split(":")
    .last.match(resolution_pattern)[1]
    .gsub(/\s+/, "")

  scaled_resolution_command = "system_profiler SPDisplaysDataType |grep 'UI Looks like'"
  scaled_resolution = `#{scaled_resolution_command}`.split(":")
    .last
    .match(resolution_pattern)[1]
    .gsub(/\s+/, "")

  # base_resolution と scaled_resolution が同じであればモニタネイティブの解像度として扱う
  if base_resolution == scaled_resolution 
    base_resolution
  else
    scaled_resolution + "-scaled"
  end
end

loop do
  service_name = "scaled-resolution-test"
  metrics = [
    {
      "name" => "mbp.gpu.usage.#{get_current_resolution}",
      "time" => Time.now.to_i, "value" => get_gpu_usage
    }
  ]
  pp metrics
  @mackerel.post_service_metrics(service_name, metrics)
  sleep 60
end

このスクリプトを実行し、解像度を変えながら Mackerel へサービスメトリックとして投稿したのが以下のグラフになります。

デフォルトのスケーリング解像度 (1920x1080) から一段回ずつ解像度を上げていくにつれて、GPU の使用率が少しずつ増えていっている様子が分かります。最後に試した 3840x2160 はモニタのネイティブ解像度となり、スケーリングされていないものになるので負荷が最も低くなっています。

スケーリングはどのように実行されているか

スケーリングがどのように macOS 上で実行されているか公式な情報は見つけられませんでしたが、Stack Exchange のこの回答によると、モニタのネイティブ解像度の整数倍にならないスケーリング解像度では一旦倍の解像度で出力してからスケーリングしているようです。

4K (3840x2160) のモニタで 1920x1080 のスケーリング解像度を選択するとそのまま整数スケーリングされますが、2560x1440 のスケーリング解像度を選択すると一旦 5120x2880 で出力してから 2560x1440 へスケーリングされるようです。 余分な解像度で出力する分パフォーマンスが落ちるということでしょうか。

さらにこのブログによると、非整数スケーリングを行った際にはスクロール時のチラつきなどパフォーマンス低下以外にも様々なデメリットが生じるようです。結局この方は整数スケーリングで 3008x1692 のスケーリング解像度を実現できる 6K の Pro Display XDR を購入したようです。。。

最後に

Mackerel ではホスト監視以外のメトリックもサービスメトリックとして投稿することができます。今回のように思いつきでスクリプトを書いてデータ収集する用途で使っても便利なので、ぜひ試してみてください。

FPS の振り向き計算機を作った

inommm.github.io

FPS をやる上で欠かせない「振り向き」を計算するためのアプリケーションを作った。自身がよくプレイする ValorantRainbow Six: Siege における振り向き(cm)をそれぞれ算出することができる。

振り向きとは

振り向きとは FPS ゲーム内で真後ろを向く(180°回転する)ためにマウスをどのくらい動かす必要があるかを表す。この距離は使用しているマウスの DPI やゲーム内の設定値により変わっていく。

振り向きは他人がどのくらいの感度設定でゲームをプレイしているか感覚的に分かりやすいため、「あのプロゲーマーは振り向き20cmでプレイしている」といったように使われることもあれば、他の FPS タイトルでも同じ感覚でプレイするための基準の一つにもなる。*1

振り向きの計算方法

振り向きはゲームを起動し実際にマウスを動かしてその距離を測定する方法もあるが、各設定値が分かっていれば簡単に計算することができる。

海外では振り向きよりも 360° Distance (視点が一周する距離) が用いられることが多いが、この値を半分にしたものが振り向きである。360° Distance の計算方法は以下の通り。

  360° \text { Distance }(\mathrm{cm})=\frac{360*2.54}{D P I * \text { Yaw } * \text { Sensitivity }}

マウスの DPI やゲーム内感度設定は簡単に分かるが、Yaw はタイトルごとに異なる上、値を公開しているタイトルも少ないため、個別に調べていく必要がある。

各タイトルの Yaw を調べる

Yaw はタイトルによって値が公表されているものや、ゲーム内コンソール等にアクセスして値を確認できるものもある。今回対象とした Valorant と Rainbow Six: Siege の値は Sensitivity Matcher のプリセットに入っていたため、今回はこの値をそのまま使用する。

www.kovaak.com

タイトル Yaw
Valorant 0.07
Rainbow Six: Siege 0.005729577951308232087679815481411

アプリケーションの実装

ソースコードこちら

アプリケーションは React で実装した。create-react-app した後 bootstrap を導入し、値を受け取るための簡単なフォームと計算処理を追加しただけなので、 SPA として面白い部分はほとんどない。実際に計算を行っているのはこのあたり

今後は View Speed や Monitor Distance の計算もできるように拡張していきたいところである。

*1:ゲーム間の感度変換の場合は振り向きよりも View Speed や Monitor Distance を用いたほうが良いとされる