
2025/12/08 17:02
From Azure Functions to FreeBSD
RSS: https://news.ycombinator.com/rss
要約▶
Japanese Translation:
著者は、Rust ベースの Web サービス(EndBASIC、EndTRACKER、および今後リリース予定の ZFS‑auto‑unlock サービス)を Azure Functions から自己ホスト型 FreeBSD 14.x サーバへ移行した理由を説明しています。
Azure の Flex Consumption プランは 2028 年 9 月 30 日に終了し、3 つすべてのアプリで使用されていたフリーティアも廃止予定です。また、カスタム Rust ハンドラをサポートしていませんでした。著者は GitHub Actions CI/CD を利用してサービスを Rust バイナリとして実行していました。
感謝祭の日に発生した Azure Functions ランタイムの障害と、Microsoft SQL Server サーバーレスで TLS がサポートされていない(
sqlx コネクタがアップストリームでパッチできなかった)問題により PostgreSQL への切替えを余儀なくされました。PostgreSQL に移行する際、著者は「開発専用」の $600 超のオプションではなく、月額 $15 のインスタンスを選択しました。新しいデプロイメントは、著者のガレージにある 2×36 コア Xeon E5‑2697 FreeBSD サーバ(64 GB RAM、2 TB NVMe + 4×4 TB HDD)で稼働しています。Rust バイナリは
daemon(8) を使って PID ファイルを管理し、ログローテーション(-H フラグ)、非特権実行、および /usr/local/etc/endbasic.conf での設定を行います。ログローテーションは FreeBSD の newsyslog(8) を利用しており、ログをリネームした後に SIGHUP が必要です。TLS 終端は Cloudflare Tunnels により処理され、CORS ヘッダーはサーバ側ではなく Cloudflare 上で AI 生成の変換ルールを使って設定します。
移行後、著者はパフォーマンスが向上し Azure の課金が発生せず(電気代のみ)、ただし HA、スロットデプロイメント、およびプッシュデプロイ自動化を Azure が提供していたものと同等に再実装する必要があります。
ユーザー側ではサービスの信頼性とコスト削減が期待でき、企業側では自己ホスティングで費用を抑えられることは示されますが、HA、バックアップ、およびデプロイメントツールの管理を自前で行う必要があります。
本文
FreeBSD の「サーバーとしての力」を試す
感謝祭の朝、ウェブサービスの一つが利用不可になったことに気づいた。
HTTP リクエストはすべて 503 Service Unavailable で失敗し、コンソールへログインしても Runtime version: Error とだけ表示され、原因を突き止める手立ても見つからなかった。
多くの時間を費やしたくなく、サポートに連絡する気もない。ダッシュボード上部にある小さな黄色警告の裏側に別のメッセージが隠れていたので注意深く読んだ:
Migrate your app to Flex Consumption as Linux Consumption will reach EOL on September 30 2028 and will no longer be supported.
数週間前から、すべての Azure Functions アプリは「死刑列車」に乗っていると知っていた。
無料プランは廃止される予定で、Rust で書かれたカスタムハンドラをサポートする代替策も見つからず、3年は残っていたが、ある障害が行動を促した。
今では全てのウェブサービスをガレージに置いた FreeBSD サーバー上で稼働させ、コードベースは僅かに変更しただけ。ここが移行物語だ。
どうやってここまで来たか?
2021 年、EndBASIC 言語の開発を続けているうちに、ファイル共有サービスを作りたいと思った。
ユーザー満足度を高めると同時に、ウェブサービスへ踏み込む必要があったからだ。
その頃私は Microsoft で Azure Storage を担当しており、学習用に年間 $300 のクレジットが付与された。
シンプルなアプリならこの額で十分だと判断し、Azure が最適だと思った。
サーバーレスの「無限無料」層を利用するため、Rust 版関数を Linux ランタイムへデプロイする方法を見つけてすぐにベースサービスを構築した。
そこから EndTRACKER(分析サービス)や、最近では暗号化された ZFS ボリュームの自動アンロックを行う新機能を追加していった。
3 年間は Azure で問題なく稼働し、GitHub Actions とデュアルステージング/本番展開で「プッシュオングリーン」を実現した。だが…
クラウドデータベース
計算リソースは十分だった:Azure Functions は低コストで動作し、$300 のクレジットをほぼ使い切らなかった。
しかしウェブサービスにはデータベースも必要だった。
2021 年に唯一の無料層として サーバーレス Microsoft SQL Server (MSSQL) を見つけたが、未経験で PostgreSQL や MySQL 風だと勘違いしていた。
sqlx の TLS 対応を追加するために 2 週間費やしたものの、PR はマイクロソフト側の戦略と衝突し却下された。さらに機能不足やバグが次々現れ、MSSQL 方言に挫折した。
そこで Azure の管理 PostgreSQL を利用することに決めた。オンボーディングウィザードは冗長で高価なインスタンスを勧めてくるが、設定を下げ「リスクを受け入れ」$15/月 で自分のトラフィックに合ったサーバーを手に入れた。
障害と引き金
約 2 ヶ月前に ZFS ボリュームの自動アンロックサービスを作り始めた。
デプロイ時に「選択したプランは 2028 年に廃止予定」という警告が表示された。
書き込みの時点では 3 年後という距離で問題と感じていなかったが、感謝祭の日に分析サービスが動かなくなり、すべての HTTP API が 503 を返した。
新しいビナリをデプロイしても改善しない。ダッシュボードで Runtime version: Error を探しても何も見つからず、結局は運用上のトラブルだった。
速やかに Azure Functions から離れた理由
二年前に中古 ThinkStation(2×36 コア Xeon E5‑2697、64 GB RAM、2 TB NVMe、4 × 4 TB HDD)を購入した。
本来は開発サーバーだったが、最終的には FreeBSD 14.x 上でサービスをホストすることになった。
サーバーレスからセルフホストへ
Azure Functions は
FUNCTIONS_CUSTOMHANDLER_PORT で指定されたポートに HTTP サーバーを起動する Rust(または Go)バイナリを実行する。バイナリとメタデータ JSON を ZIP にまとめて Azure にアップロードすると、ランタイムが TLS 終了処理・マイクロ VM での起動・リクエスト転送を担当してくれる。
Azure Functions ランタイムを削除したので、自前サーバーはスタンドアロンで稼働させる必要があった。
バイナリはすでに HTTP サーバー機能を持っているため、以下の点だけを設定すればよかった。
| 目的 | 実装 |
|---|---|
| 設定変数注入 | の / で PID とログファイルを指定し、 で環境変数を読み込む。 |
| 特権昇格解除 | で非特権ユーザーとして起動。 |
| プロセス管理 | FreeBSD の を利用し、 スクリプトで自動起動・再起動を実装。 |
| ログローテーション | で を呼び出し、ログファイルを安全に回転させる。 |
daemon \ -P /var/run/endbasic.pid \ -o /var/log/endbasic.log \ -H \ -u endbasic \ -t endbasic \ /bin/sh -c ' . /usr/local/etc/endbasic.conf /usr/local/sbin/endbasic'
rc.d スクリプト例:
#! /bin/sh # PROVIDE: endbasic # REQUIRE: NETWORKING postgresql . /etc/rc.subr name="endbasic" command="daemon" rcvar="endbasic_enable" pidfile="/var/run/${name}.pid" start_cmd="endbasic_start" required_files=" /usr/local/etc/endbasic.conf /usr/local/sbin/endbasic" endbasic_start() { [ ! -f /var/log/endbasic.log ] && { touch /var/log/endbasic.log chmod 600 /var/log/endbasic.log chown endbasic /var/log/endbasic.log } echo "Starting ${name}." daemon \ -P "${pidfile}" \ -o /var/log/endbasic.log \ -H \ -u endbasic \ -t endbasic \ /bin/sh -c ' . /usr/local/etc/endbasic.conf /usr/local/sbin/endbasic' } load_rc_config $name run_rc_command "$1"
sysrc endbasic_enable="YES" と service endbasic start でサービスが起動する。
ログローテーション
-H はログ回転を有効にするオプション。Unix ではプロセスがファイルハンドルを保持したまま名前変更や削除を行うと、書き込みは継続し残りファイルが参照できなくなるためディスクがいっぱいになる。
daemon(8) は newsyslog(8) を呼び出してローテーションを実施し、プロセスに SIGHUP を送ってログファイルを再オープンさせる。例として
/usr/local/etc/newsyslog.d/endbasic.conf の設定は次のようになる。
/var/log/endbasic.log endbasic:wheel 600 7 * @T00 JC /var/run/endbasic.pid
数字で保持期間、ローテーション頻度、圧縮方法を制御する(詳細はマニュアル参照)。
TLS 終了
以前は Azure Functions が TLS を終端していた。
ランタイム無しのため自前で設定するかしないかの選択肢が出た。
Cloudflare Tunnels を利用すれば、
cloudflared をインストールし http://localhost:PORT へ転送させるだけで TLS と DDoS 防御を Cloudflare が担う。第三者にトラフィックが見える点はリスクだが、Azure Functions と同様だったので問題ないと判断した。
CORS
Cloudflare をフロントエンドに使ったため CORS の設定が必要になった。
サーバー側で設定しても効果がなく、実際には Cloudflare で Edge Rule を作成し必要なヘッダーを付与した。
AI が提案したルール例:
# Sample Cloudflare transformation rule to set CORS response headers
「クラウド外に出さない」方式で機能することを確認した。
移行後の実感
| 取得できたもの | 捨てたもの |
|---|---|
| 予測可能性 – プライベート環境でアップグレードを自分で管理。 | 可用性/冗長性 – マルチリージョンフェイルオーバーがなく、移行後に 2 時間の停電を経験。 |
| パフォーマンス – 常駐サーバーはサーバーレス起動より高速。 | 運用負荷 – 手動デプロイ・データベース管理・ログ監視が必要。 |
| コスト削減 – Azure の月額約 20 USD がほぼゼロに。既存ハードウェアは他用途でも使える。 | 機能 – Azure のサーバーレスランタイム、オートスケール、統合モニタリングを失う。 |
結論
Azure Functions から FreeBSD サーバーへ移行するには数時間で完了できる。
daemon(8) と newsyslog(8)、Cloudflare Tunnels、Edge Rule を組み合わせれば、TLS 終端と CORS の設定も簡単に実装できる。
手動管理のコストは増えるが、自己ホストなら費用を抑えつつ予測可能な運用が可能になる。
感謝祭の朝に行った移行作業は、自由度と安定性のバランスを再確認する良い機会となった。