docker buildx bake で高速並列ビルド

Docker ビルド職人の朝は早いーー

毎日コンテナイメージを山ほどビルドしては捨てている皆様、おはようございます。 ビルドの速度はそのまま CI にかかる時間だったりするので、短縮には余念のないことと思います。

レイヤのキャッシュやマルチステージビルドといった基本テクニックについて、ご存じない方は以下の記事がお勧めです。

future-architect.github.io

この記事では、良い Dockerfile をさらに活用できる、かもしれない docker buildx bake について紹介します。

続きを読む

やんないほうがいいかも、GitHub Actions の setup-xxx での依存キャッシュ保存

GitHub Actions で CI している皆様、こんにちは。
GitHub Actions 便利ですよね。使わない日がないというくらい毎日お世話になっています。

さて、CI といえば良く問題になるのが実行時間。 長い待ち時間は開発効率を下げますし、プライベートリポジトリだと Runner の費用も嵩んでしまいます。 時間を短縮する方法は色々ありますが、一手目によく行われるのが依存パッケージのキャッシュじゃないかなと思います。

例えば Go で開発していると、依存パッケージは ~/go/pkg/mod にダウンロードして保存されます。 これを CI 実行のたびにダウンロードしてコンパイルするのは時間とお金の無駄というものです。

幸い、GitHub Actions には CI の実行間でこういった依存パッケージを保存して再利用できるキャッシュ機能があります。 詳しくは以下のドキュメントをご覧ください。

Caching dependencies to speed up workflows - GitHub Docs

とか書いてますが、面倒くさがりな私はこれまでまともにこのキャッシュ機能を調べたことはありませんでした。 なぜって GitHub はとても親切なので、以下のようにするだけでキャッシュ機能で依存関係を保存して再利用できるようにしてくれているからです。

jobs:
  test:
    name: Run tests
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    - uses: actions/setup-go@v5
      with:
        cache: true

注目は cache: true です。こうするだけで Go の依存パッケージをキャッシュしてくれます。 しかもデフォルトが true なので、実は書かなくても大丈夫。えらい!楽!以上!

・・・そうは問屋が卸さなかったのが今回の話です。

続きを読む

AlloyDB で初期ユーザーとさよならする話

AlloyDB, 正式には AlloyDB for PostgreSQL は名前が示す通り PostgreSQL 互換の Google Cloud 上で利用できるマネージドデータベースです。 AWS だと Aurora が類似のサービスで、ストレージとコンピュートのノードが分離したアーキテクチャになっていてスケーラビリティや可用性に優れます。

諸事情で最近 AlloyDB を使っているんですが、通常の PostgreSQL とはちょっと違う点があります。 たぶん最たるものがユーザーと role 周りでしょう。

PostgreSQL の通常のユーザー認証はユーザー名とパスワードで行われます。 AlloyDB のクラスタを作成するときも、postgres という初期ユーザーが平文で指定したパスワードで作られます。

他のマネージドサービスの認証は通常このようなユーザー名パスワード方式ではなく、IAM のユーザーないしサービスアカウントで行われます。 内部的には短期間で expire する認証トークンを発行して OAuth しているのでしょうが、クライアントライブラリ等が自動で処理するのが通常であるため、利用者観点では認証が自動で行われて快適かつセキュアです。 AlloyDB は通常のユーザーに加えて IAM ユーザーで認証して利用することもできます。

さて、そうなると当然平文パスワードで作成した初期ユーザーは捨てて IAM ユーザーだけにしたくなります。 結論から言えばできます。

続きを読む

メンテのいらないソフトウェア

ソフトウェアエンジニアとして働き始めて 20 年以上になります。 元々ソフトウェアでいろいろ作りたくて就いた職業なので、結構な数のプロダクトを開発してきました。

私がメインで開発したもので OSS として出ているものでは、

  • [yrmcds]: memcached クローンで、レプリケーション機能などを持つ
  • [usocksd]: SOCKS4/5 サーバー & ライブラリ
  • [transocks]: アプリのネットワーク通信を透過的に SOCKS サーバーにプロキシする透過プロキシ
  • [coil v2][coil]: Kubernetes の CNI ネットワークドライバ
  • [moco]: MySQL を自動運用する Kubernetes オペレーター
  • [accurate][]: Kubernetes 上で namespace ベースのソフトマルチテナンシーを実現するためのソフトウェア

などがあります。これらのソフトウェアの多くは、現役で使われています。

私の主な仕事はこれらのソフトウェアの開発・メンテナンスではありません。 過去の主要な仕事内容としては cybozu.com のローンチプロジェクトの責任者であったり、開発・運用の本部長であったり、今は新規プロダクトのアーキテクトを務めていたりします。

プロフェッショナルなソフトウェアエンジニアの心がけとして、以下のようなことが良く言われます。

コードを書くことが仕事じゃない。問題を解決できるなら、むしろコードは書かなくて済むほうがいい。

なぜなら、「コードを書くとバグがあったりメンテナンスの工数がついてまわったりする」からです。 そうだよなと思うと同時に、「いや俺、コード書きたくてソフトウェアエンジニアになったんですけど」と本音のところで思ったりしています。

で、本題。コード書きまくってソフトウェアプロダクト作りまくっても、そのバグやメンテナンスに時間を取られない方法はないのか。 あります。

続きを読む

OpenTelemetry 良い感じ

最初に断っておきますと、OpenTelemetry を良く知っていたり真面目に調査しようという人が読むべき内容はここにはありません。 公式ドキュメントなりをご参照ください。これは最近 OpenTelemetry を使いだした一般人の感想記事です。

さて、いけてる Web 開発者、特にバックエンド開発者の方はオブザーバビリティという言葉は聞き及んでいるかと思います。 なかでもオブザーバビリティ三種の神器と言われている(?)ログ、メトリクス、分散トレーシングをどう実装するか頭を悩ませているかもしれません。

頭を悩ませてきた、あるいは頭を悩ませている理由の一つは、これらを実装するときに特定の実装向けになりがちであったためです。 メトリクスであれば最近は Prometheus 向けに /metrics エンドポイントとして提供する実装が多いといった話です。しかしながら、 あらゆる人が Prometheus を使うわけでもないので、汎用的なソフトウェアを書く場合に悩ましかったりしたわけです。

OpenTelemetry は、逆説的に特定の実装を排して、SDK とプロトコルだけを規定することでどんなソフトウェアにも組み込んで良い、 一度組み込んだらメトリクスサーバーやトレーシングサーバーをいつでも変更可能になるよという CNCF のプロジェクトです。まだ 一部の機能、例えば Go SDK でログが未実装といったところはありますが、トレーシングやメトリクスは十分インストルメント可能です。

そんな噂を聞いて、新規のサービス開発でこれからやるなら OpenTelemetry でインストルメントしていくのかなと思いつらつらやってました。 以下、調べたり学んだりしたことと、現状できていることです。正確性は全然担保しないので、使うときはちゃんと調べてくださいね。

続きを読む

Go 1.21 に入る予定の log/slog パッケージの話

ログ出力をどうするかっていうのは、Go 書くときに常にちょっとした悩みの種でした。 標準の log パッケージはあるものの、実用には機能が不足していると見なす人が多いためです。

かくいう私もその一人で、近年は Kubernetes 界隈でよく使われている以下を組み合わせていました。

logr は所謂 structured logging のための共通インタフェースを提供するパッケージで、uber-go/zap のような著名なログライブラリ向けにアダプタも提供してくれます。 debug, info, warn, error といったいわゆるログレベルも備えていますし、context 経由で logger を渡すこともできるので、便利に活用していました。

ただ、logr は結構使われているとはいえ標準パッケージではないため、logr を使わないライブラリ等と組み合わせるのが難しいのが悩みでした。 そんなこんなの声を受けてか、Go 1.21 で log/slog というパッケージが標準で利用できるようになりそうです。 まだリリース前ですが、万一取り除かれたりしない限りは近い将来使えるようになるでしょう。

で、新規にプログラムを作るにあたって、どうせ Go 1.21 以降でプロダクション開発するだろうなと思うと logr + zap よりはこの slog パッケージにしておきたいです。 幸いなことに、Go 本体に取り込まれたのとほぼ同じコードが golang.org/x/exp/slog として今すぐ利用できます。 前置きが長くなりましたが、そんなわけで slog を使ってみたので以下に特徴を紹介します。

  • インタフェースではなく実装なので、他のログライブラリを組み合わせる必要なし
  • structured + leveled logging 可能
    • さらに、ネストした structure も可能
  • slog.SetDefault を呼ぶと標準の log パッケージの出力も slog になる
  • テキスト形式のほか、JSON 形式の出力も手軽にできる
  • APIトークンなどをログに出さないよう型ベースで設定できる ()

最後発だけあり、痒い所に手が届く良い設計だと思います。 context 経由での受け渡しはまだないようですが、実装は簡単なので手元で適当に足して使っています。

以上

PRECIS 知ってますか?

昨今 ID 管理系の業務をやっていて、まあ色々学びがあるんですがそのうちのひとつ、PRECIS について。

PRECIS というのは RFC 8264 で規定される、ユニコード文字列を適正に比較するためのフレームワークです。 PReparation, Enforcement, and Comparison of Internationalized Strings の acronym で PRECIS と呼ばれています。

ユニコードは表示上は同じように見えても UTF-8 のバイト列としては異なる表現になる文字列があったりします。 たとえば「が」は 0xE3 0x81 0x8C もしくは 0xE3 0x81 0x8B 0xE3 0x82 0x99 のどちらかで表現可能です。 当然、単純にバイト列として比較してしまうと同じ文字列なのに異なるという誤判定をしてしまいます。

ここまでは NFC とか NFD といった normalization の話なのですが、PRECIS はそれに加えてユーザー名やパスワードに使えない文字列を除外したりする規則を定めています。 具体的な規則を PRECIS profile と呼んでいて、RFC 8265 では以下の3つが規定されています。

  • UsernameCaseMapped: 大文字小文字を区別しない、ユーザー名用プロファイル。空白文字などは弾かれます。
  • UsernameCasePreserved: 大文字小文字を区別する(以下略)
  • OpaqueString: パスワード用プロファイル。大体の文字は大丈夫だが、コントロールキャラクターとかは弾かれます。

この PRECIS はシステム間でユーザー情報を同期する SCIM などのプロトコルでも採用されているため、新規に ID 管理を実装するならユーザー名やパスワードが PRECIS profile に準拠しているかチェックするようにしておくと、ちょっと相互連携しやすくなるかなと思います。そういうのがなくても、適切に正規化したり不適当な文字を弾いてくれるので使っておくのが安心です。

Go であれば golang.org/x/text/secure/precis パッケージで利用できるようになっています。

以上