Diary of a Perpetual Student

Perpetual Student: A person who remains at university far beyond the normal period

FY2023上半期を振り返る・広く浅く

こんな形で今後も public な振り返りを定期的に書いていきたいですね。

blog.arthur1.dev

ということで、2022年8月〜2023年1月の振り返りを書いていきます。

幅広い技術とコンポーネントに触れた

Mackerel は多くのコンポーネントから構成されています。Web のフロントエンド・バックエンドはもちろんのこと、裏には内製の時系列データベースがいたり外形監視やクラウドインテグレーションを実現するためのクローラーがいたりします。OSS として公開しているアプリケーションもたくさんあります。

サブコンポーネントに詳しいアプリケーションエンジニアや SREs と一緒のチームで働く利点を活かして、これら多くのソフトウェア・インフラに幅広く触れることができました。

今では自分がチームの中で相対的に、より細かい挙動・仕様を知っているコンポーネントや機能もあります。また、可観測性やコスト面を改善するアーキテクチャの提案もできました。最近の話だと、mackerel-agent の Windows での挙動について調査したり改修したりしています。問題を見つけ、取り組める形に分解するところまでは比較的できているので、今後は手早く安全に問題を解決し、価値を届ける力を身につけたいです。

技術面で言うと、Go 言語や Terraform が全く触れない状態から、ある程度のものを作ったりエコシステムを理解できている状態にすることができました。これは普段のチームの仕事だけでなく、開発合宿や個人開発でも積極的に触れることで身につけることができました。

コードレビューできるようになってきた

この半期はコードレビューの量・質を高めることを目指し、エンジニアメンターと目標設定をして取り組みました。上記で示したように幅広い範囲をキャッチアップできたこともあり、目指した通りにレビュー力を上げることができました。

これは index が張られたようなイメージで、「このレビューをするために見なければならない情報はここにある」という知識が脳の引き出しに入っていて、それを取り出してレビューするのにかかる時間を短くすることができました。

「良い設計」に関してはテックリードの考える所にはまだまだ遠く辿り着けていないなあと思っています。「安定したコードが不安定なコードに依存してはならない」と言うのがシンプルに本質を表す金言だなと感じていて、この言葉を噛み締めて開発に取り組んでいきます。

「監視」が分かってきた

入社してすぐの、グラフ定義で選べる単位を追加する*1というタスクに取り組んでいた頃は、監視というドメインへの理解がかなり浅かったなと思います。Mackerel の中の一機能への理解と、それを実現する技術のキャッチアップで精一杯でした。

日々の仕事の中で、自分が開発者として何をどう監視したいかと言うイメージが湧いてくるようになりました。こういったものを可視化したい、監視したい!と思い、メトリック化したりダッシュボードを整備したり、と言う動きもできました。

そうやって Mackerel を利用する主体になって初めて、Mackerel に対する夢・アイデアがどんどん膨らんできました。ユーザー目線に立って、PBI (Product Backlog Item) が生み出す価値について言語化したり、価値を理解した上で実現方法を取捨選択することもできました。

社内/外のアウトプット

社内向けのアウトプットにしては、自分が選んだ選択や仕事で行った創意工夫のベースにある思考を言語化して共有する機会がありました。また、ほたてに小ネタを持ち込んだり、Slack で技術に関する議論を巻き起こしたりなど、アウトプット・情報交換の場を盛り上げることも併せてできたかなと思います。

社外に関しては、Mackerel Advent Calendar 2022 の運営と、参加者としてエントリを投稿したのが一番大きかったです。まとめエントリも書きましたので、Mackerel ユーザの方はぜひお読みください。

blog.arthur1.dev

また、先日開催された Hatena Engineer Seminar に登壇しました。先ほど Mackerel に対する夢について書きましたが、Mackerel チームの夢の膨らませ方とその実現について話したので、よろしければご覧ください。

www.youtube.com

まとめ

総括すると、広く浅くさまざまなことに取り組んだ半期だったなと思います。来期どういう風に生きたいかはまだあんまり考えていないのですが、とにかく価値提供したい、という気持ちが今は強いです。

そしてありがたいことに、半期末の納会で新人賞を頂きました。来期も頑張ります。

*1:cf.) 時間やネットワークトラフィックの単位が追加された Mackerel でネットのスピード監視 - Diary of a Perpetual Student https://blog.arthur1.dev/entry/2022/12/01/000100

パフォーマンスだけを考えると WMI を使わないほうが良いのかもしれない

OS 名は WMI から取得したほうが良いが……

前にこんなエントリを書いた。

blog.arthur1.dev

このエントリを要約すると、Windows の OS 名を取得するとき、レジストリにアクセスするのではなく WMI を使って取得しよう。なぜなら、Win 11 が Win 10 として認識されてしまうから」という内容である。

mackerelio/mackerel-agent には、WMI を利用せずに基盤の情報を取得している箇所がいくつかある。たとえば、レジストリの値を参照していたり、dll を読んで Windows API を呼び出したりしている。こういった場所も WMI に置き換えたほうが良いのだろうか。

それは、現状持ち合わせている情報では分からない、というのが誠実な答えになるだろう。OS 名に関してレジストリの値が信用ならないことは分かったが、それが一般に成り立つとは限らない。

Windows の細かい話は素人にはよく分からないので総合的な良し悪しはあまり語れないが、今回はわかりやすいパフォーマンス面での比較をしようと思う。

パフォーマンス比較実験

実験のために書いたプログラムは以下のリポジトリで公開している。

github.com

実験設定

比較対象は3つの手法である:

  1. dll を読み込み Windows API を呼び出す手法
  2. WMI に WQL クエリを投げる手法
  3. レジストリの値を読み出す手法

この3つの手法それぞれで同等な情報を得られるものを探した結果、プロセッサのアーキテクチャを取得させることにした。

各手法の実装は、mackerelio/mackerel-agent で利用している util パッケージを import する、もしくは同等のコードを書くことによって行った。

ベンチマークには Go 言語に標準で付随する testing パッケージの Benchmark を利用する。

比較対象は、自分所有の PC *1において以下のコマンドで benchmark を実行したときに表示される ns/op (関数1実行あたりにかかったナノ秒)である。

go test .\windows\ -bench .

実験結果

上記コマンドを実行すると、以下のような出力が得られた。

> go test .\windows\ -bench .
goos: windows
goarch: amd64
pkg: github.com/Arthur1/windows-api-benchmark/windows
cpu: 12th Gen Intel(R) Core(TM) i7-12700F
BenchmarkGetProcessorArchitectureFromApi-20     1000000000
--- BENCH: BenchmarkGetProcessorArchitectureFromApi-20
    windows_test.go:9: 9
    windows_test.go:9: 9
    windows_test.go:9: 9
    windows_test.go:9: 9
    windows_test.go:9: 9
    windows_test.go:9: 9
BenchmarkGetProcessorArchitectureFromWmi-20     1000000000               0.01329 ns/op
--- BENCH: BenchmarkGetProcessorArchitectureFromWmi-20
    windows_test.go:14: 9
    windows_test.go:14: 9
    windows_test.go:14: 9
    windows_test.go:14: 9
    windows_test.go:14: 9
    windows_test.go:14: 9
    windows_test.go:14: 9
BenchmarkGetProcessorArchitectureFromReg-20     1000000000
--- BENCH: BenchmarkGetProcessorArchitectureFromReg-20
    windows_test.go:19: AMD64
    windows_test.go:19: AMD64
    windows_test.go:19: AMD64
    windows_test.go:19: AMD64
    windows_test.go:19: AMD64
    windows_test.go:19: AMD64
PASS
ok      github.com/Arthur1/windows-api-benchmark/windows        0.782s

この結果を見ると、Windows APIレジストリを用いる手法では ns/op が表示されないほど短い時間で実行できているが、WMI を用いる手法だと1実行あたり 0.013 ns ほど掛かっているということが分かる。

どこで詰まっているか(詰まっているというほどではないと思うが)気になったので、pprof を使って詳しい様子を観察してみた。概ね、WMI にクエリを投げるのに相当するシステムコールに時間が掛かっている、といったところだろうか。

まとめ

今の mackerel-agent の実装をすべて WMI を使う形に置き換えればいいじゃんと思っていたのだが、パフォーマンスを比較すると WMI が劣っているということを知った。劣っているといっても 0.1 ns にも満たない程度なので、(クエリにもよるだろうが)通常の利用の範囲では問題ないように感じる。

つまり、安定して情報が取得できるか、得た情報が正しいかどうか、などの他の軸に重きをおいて手法を評価したほうが良いということになる。冒頭に紹介したエントリでは得た情報の正しさを鑑みて WMI を選択したが、それに関しては良い選択だったのではないかと思う。

Windows に詳しい人はぜひいろいろ教えてほしい。

*1:Z690 / Core i7-12700F / RTX3080 / DDR5-4800 32GB の自作 PC。OS は Windows 11 Pro

GitHub Actions の ::set-output 脱出 上級編 便利ツールでカバーできないケース

::set-output 脱出 進んでますか?

github.blog

エンジニアで GitHub Actions を利用している人はだいたい知っているであろう、 ::set-output::save-state の非推奨化問題。このコマンドを利用していると2023年5月末にはエラーになってしまう予定となっています。

面倒がっているエンジニアのために、巷には便利なツールがあります。

一つは rhysd/actionlint です。::set-output のような非推奨機能を使っていると actionlint の検査に引っかかります。こちらについては過去記事で紹介したのでご覧ください。

blog.arthur1.dev

もう一つは azu/set-env-to-github_env です。このコマンドをインストールして実行すると、workflow ファイルを新しい書き方に書き換えてくれます。便利。

今日は、残念ながらこれらのツールでは引っ掛からない例とその対応策をご紹介します。

スクリプトファイルを呼び出しているところに注意

mackerelio/mackerel-agentGitHub Actions の結果を見ていると、Annotation で warning が大量に表示されていることに気づきました。利用している action の更新をしたり、actionlint を実行して非推奨な記述を見つけたりして修正したのですが、まだ ::set-output 使っているよ、という warning が表示されていました。

actionlint に引っ掛からなかったのに出るのはおかしいな、と思って、::set-output で文字列検索を掛けたところ、こんなコードが引っ掛かりました

(async function () {
  const version = "1.2.3"; // ここは本当は parse してアレコレしている
  console.log("::set-output name=VERSION::" + version);
})();

そして、この Node.js のスクリプトを Workflow 中で呼び出していました。

- run: node _tools/parse_version.js

なるほど!workflow から呼び出した外部のスクリプトが標準出力に ::set-output と書き込んでいたのに actionlint だと気づけなかった、というわけです。これは先ほど紹介した set-env-to-github_env でも無理だと思います。

解決策: actions/github-script を咬ます

先程のスクリプトを直すのには工夫がいります。新しいやり方では標準出力は利用せず専用のファイルに書き込まなければならないので、 console.log() を用いることはできません。例えば、以下のような方法で新しい方法に対応することができます。

const fs = require('fs/promises')

(async function () {
  const version = "1.2.3";
  const file = process.env.GITHUB_OUTPUT;
  if (file === undefined) {
    process.exit(1);
  }
  await fs.appendFile(file, `VERSION=${version}`);
})();

これは美しくないし、今後さらに変更があったときに見つけづらくなるなあと悩んでいたところ、 id:stefafafan さんが @actions/coresetOutput() という関数があるよ、と教えてくれました。これなら、仮にさらに新たな仕組みになったとしてもライブラリの更新に追従すればよいので良さそうです。

しかし、この npm ライブラリを利用するためだけにちょっとした js ファイルを bundle して生成するのは大袈裟です。少し調べていたところ、actions/github-script という公式の action に行き着きました。この action を経由して Node.js のスクリプトを動かすと、GitHub API や Workflow context を利用するためのライブラリを利用することができるようになります。

Workflow の yml と呼び出しているスクリプトを以下のように書き換えました。

- use: actions/github-script@v6
  with:
    script: |
      const script = require('./_tools/parse_version.js')
      await script({ core })
module.exports = async ({ core }) => {
  const version = "1.2.3";
  core.setOutput("VERSION", version);
};

すると、無事 warning が消え、かつ workflow の output を設定することができました!

まとめ

GitHub Actions の ::set-output 脱出の際には、以下の事項に気をつけましょう。

  • Workflow 定義外のスクリプトファイルを呼び出して ::set-output と標準出力に書き出しているところに気をつける
  • それが Node.js のスクリプトの場合は、actions/github-script 経由で呼び出すようにすると、setOutput() 関数が用意されたライブラリを利用可能

聞き手と共有しているコンテキストを意識して伝え方を選ぶ

自分は以下の2点を理由に、外国語を直訳したような堅い言葉で話すことがある。

  • ボードゲームの翻訳をしている
  • 日常の思考を日本語ベースで行っていないことがある

英語を翻訳していると a, an と言う冠詞に何度も遭遇する。日常会話では何も気にせずに済むのだが、ボードゲームの翻訳となるとそうはいかない。日本語に翻訳した後に、複数なのか単数なのかと言う情報が欠落する可能性があるからだ。ボードゲームに限らず一般にゲームをプレイする上で、ルールの曖昧さ・解釈の不一致はプレイへの集中を妨げる大きな障害となる。

前提とするコンテキスト(文脈を超えて、場や間といった概念も含まれるだろう)の大きさをいかに小さくするか、それはある意味正義なのだが、ある意味では正義ではない。表現として限りなく正確だったとしても、受け手がどう解釈するかは結局受け手次第なのである。

自分はコンビニで煙草を買うとき、レジで「◯◯ひとつください」という風に、目的のモノと数量を明示してお願いする。しかし、結構な確率で「ふたつですか?」と聞き返されてしまう。自分の滑舌が著しく悪いわけでもないのに、だ。子音は同じものの母音は違うので、聞き間違えにくいように思う。

ここには、単数がデフォルトで、いちいち数量を言うやつは何個か欲しいのだろう、と言うバイアスがあるのだと思う。忙しい人が買い物に来るコンビニでは尚更のことである。

5W(When, Where, Who, What, Why)を意識して、適切な物の伝え方 (How) が選べるようになりたいと思う。そして、それはこのブログでも同様である。


余談: なぜこんなエントリを書いたのか

学園祭のWeb開発を語る: 抽象編 自分たちの責務をどう絞り、どう委嘱するか

遠い昔に学園祭の Web 開発をしていた話をするシリーズもの最終回

  1. 具象編1: 講習会と砂場遊びで支える組織
  2. 具象編2: 開発体験・保守性を投げ捨てる
  3. 抽象編: 学園祭の開発部署という組織や、学園祭Webサイトというプロダクトの性質から語る ←イマココ

ここからは、これまでの昔話ではなく、現代の Web 事情を踏まえたり、自分が昔いた組織特有ではない一般の話に寄せたりして書いていく。

もし、自分がまた大学生になってどこかの学園祭の Web サイトを作るとしたら、どういう選択をするだろうか。そんな思考実験をしてみる。

静的なサイトを作ろう

ここ4、5年でフロントエンド界隈は凄まじい進化を遂げたと思っている。TypeScript により保守性と開発体験が向上したし、React や Vue.js など、再利用可能で影響範囲のスコープを絞れる component ベースで物を作れるフレームワークも一般に浸透した。

個人的に一番偉大だと思っているのが、Next.js や Nuxt.js などに実装されている Static Generation(静的サイト生成)である。

サーバ上で HTML を動的に組み立てるのは、パフォーマンスやセキュリティ面での懸念がある。もちろん、プロには知識があるから、キャッシュを活用したり、フレームワークを適切に使って XSS や SQLi を回避したりする。しかし、それを学生に求められるかは難しいところがある。このシリーズエントリの中で講習会をやっていた話をしたが、フロントエンドとサーバサイド両方の話をガッツリやっていたらあっという間に1年が過ぎてしまう。

自分がかつて作っていた学園祭のサイトは、同じアプリケーションでお客さんに見せる部分も、学生に見せる部分(電子申請フォーム)も作っていた。お客さんに見せるところでも、ヘッダメニューなどをテンプレートで作るためにサーバサイドのフレームワークで HTML を組み立てていたのである。しかし、現代でそれを行うのはリッチなフロントエンドフレームワークを使って簡単にできる。電子申請の部分は別のアプリケーションとして分けてあげればよい。

一般的な学園祭サイトの運営において、ある程度技術を身につけて自分たちで作るのであれば、フロントエンド寄りに軸足を置いた方が良い。この状況はこの先しばらくは変わらないんじゃないか、と思っている。

もちろん、SG でサイトを作ればセキュリティ面の心配は全くない、と言っているわけではない。でも、考慮範囲が小さくなるだけで十分儲けものである。

従量課金のクラウドサービスは選択しにくい

アプリケーションの次はそれを載せるインフラの話をしよう。

今の自分が選ぶとしたら、有名なレンタルサーバを月定額で借りて、そこに静的な Web サイトをデプロイする。マネージドなクラウドサービスに載せるのも cool で個人的にはやりたいものの、それを組織が選ぶべき選択肢として提示することはないかもしれない。

一番の問題は料金面である。たかだか学園祭のサイトにマネージドサービスにするほどのトラフィックはない。システムのスケーリングを手動で行うこともないので、それをマネージドサービスが勝手にやってもらっても恩恵が薄い。

特に、従量課金体系の場合は要注意である。ケチって WAF を入れないでいたら DDoS 攻撃喰らってクラウド破産、みたいなことが起こらないように運用しなければならない。そういうところにちゃんと興味があって学園祭のサイトを運用しようとしている人は本当に少ないのではないだろうか。

自分が学生の頃を思えば、監視という業務を本当に疎かにしていたと思う。Availability の目標値どころか Observability も 0 であった。

これは Twitter などで公になっていることなので書くが、自分がいた学園祭実行委員会の Web 担当部署が昨年、所有しているドメインを失効させオークションにかけられてしまう、という事故を起こした。Webサイトは閲覧不可能になり、独自ドメインのメールで学内外の人と連絡していたのもできなくなっただろう。そして、ドメインを再度手元に戻すために多くの負担を強いられた

こういった変化するものを、人とシステムの力で観測できる状態にするという取り組みに手が割けないのであれば、従量課金のサービスなんてとても使っていられないと思う。これは仕方ない面もあって、企業は週5日働いてくれる社員を雇って運用するのだけれど、学生の本分は遊び勉強なので、それほど恒常的に時間を割けない。

料金のトラッキングができないのであれば、定額でサービスを提供できる方が学園祭のサイトとしては嬉しいと思う。これは、サービスの可用性と料金の予測可能性とどちらが大事ですか?というトレードオフの関係になっている。

もちろん、サーバを持つこと自体もセキュリティ面での懸念が大きいだろう。(そのため、権限が大きくないレンタルサーバを候補に上げた。)クラウドサービスを使うことの否定はしていなくて、無料枠で確実に収まり勝手に料金が増えなければ良いと思う。

まとめ

プロダクトそのものの価値にどれぐらい commit したかも大事だが、その裏側でどんな根拠を持ってこういう意思決定をした、と自信を持って言えることも重要だ。自分が新卒採用の面接担当だったらそういう話を学生から聞きたいと思う。

zenn.dev

上のエントリを読んで「めちゃくちゃ良いな」と思って始めたこのシリーズ投稿もこれでおしまいにする。学園祭 Web 界隈の益々の発展を祈っている。

僕は技術エントリを自分のために書いているわけではない

blog.arthur1.dev

おかげさまでたくさんの方に反応していただきました、ありがとうございます。

まだ全然読みきれていないけど、少しずつ丁寧に咀嚼していこうと思います。


技術エントリは自分のために書いている、というコメントが多かったので一つだけ。

僕は技術エントリを自分のために書いているわけではないです。断定は怖いので少し逃げると、主目的ではないです。

自分の理解を整理・収納するのは他の場所でやっていて、チラシの裏にメモ書きしています(これは比喩)。他人に見せなくて良い代物なので、気が楽ですし、スピーディーに書き留められます。後世の自分が見て、記憶の引き出しを開けられる程度の情報もそこにあります。

他人に読んでほしいから、図を用意したり、サンプルコードを用意したり、言葉を丁寧に選んだりするわけです。そして、誰かが一生懸命運用維持してくれるサービスでブログを書くわけです*1

では、なぜ他人に読んでほしいのか。

一つは、Web が記憶拡張装置、ひいては人間の創造を支援するプラットフォームであるからです。Web が好きだから、その存在意義のために自分ができることをしています。ただ、生み出したものが一生誰にも読まれないならば、それはなくても同じことです。いつか読まれるかもしれない、というツッコミはあろうかと思いますが、さて、それはどれぐらいの確率で起こる事象でしょうか。

もう一つは承認欲求という言葉で片付けられるかもしれないし、誰かに必要とされたい、という表現もできるかもしれません。これは人間として比較的高次元な欲求です。自己実現のステージにまで辿り着いている人はすごいですね。

技術エントリを書くことで、副次的に文章力が上がるとか、伝える力が上がって嬉しい、というのはあるかもしれません。ただ、今の自分はそこに問題意識を持ち priority を上げて取り組んでいるからエントリを書いている、というわけではないです。

*1:これははてなブログへの褒め言葉であり、ポジショントークです