
2026/01/07 18:37
私のサーバーにもうSSHで接続できません(それでも問題ありません)
RSS: https://news.ycombinator.com/rss
要約▶
Japanese Translation:
著者は高価な「moana」VPSと月額100ドル超のホスティングサービスを廃止し、費用対効果に優れた自己管理型スタック「Tinkerbell」に切り替えました。Moana は自前でサービスをホストするために使用されていましたが、その高い月額コストが縮小のきっかけとなりました。
Tinkerbell には SSH でログインできず、更新は新しいコンテナイメージをレジストリへプッシュし、Tinkerbell がそれを自動的に取得・デプロイすることで行われます。スタックは Fedora CoreOS(FCOS)上で稼働し、Ignition を用いて一度だけのプロビジョニング、Podman Quadlets で宣言型コンテナオーケストレーション、Terraform で Vultr 上のインフラをコード化します。
著者のウェブサイトは Nginx/TLS 経由で配信される静的バイナリです。新しい構成では、Caddy(TLS 用)とスクラッチイメージに包まれたウェブサイトバイナリという 2 つのコンテナが稼働します。Ignition の設定は Butane を使って YAML から生成され、JSON(.ign)ファイルとして FCOS に最初の起動時に適用されます。Terraform は Ignition ファイルへのユーザーデータを指す Vultr インスタンスを定義し、計画を適用または破棄することで VM を再作成します。
当初は systemd のユニットファイルで Podman コンテナを起動していましたが、これが面倒だったため Quadlets(.container と .pod ファイル)へ切り替えました。コンテナに
io.containers.autoupdate=registry ラベルを付与し、タイマーを日次から毎時に変更することで自動更新を有効化しました。
著者は SSH アクセスを完全に削除し、デプロイはイメージプッシュのみで行う予定です。今後の作業としては観測スタックの構築、TLS 証明書をブロックストレージへ移動する可能性、および OpenTelemetry を使った監視検討が挙げられます。イミュータビリティモデルでは変更時に VM を再作成する必要がありますが、ウェブサイトの更新頻度が低いことから受け入れ可能です。
本文
I would like to thank Yann Régis‑Gianas, Sylvain Ribstein and Paul Laforgue for their feedback and careful review.
2026年のスタートに向けて
私は moana(信頼できる100ドル超/月のVPS)を退役させ、代わりに tinkerbell を導入するという明確な目標を持っていました。
moana は新しいサービスを試すには便利でしたが、1か月に100ドルは当然高額で、2025年の利用実績を振り返るとあまり有効活用できていませんでした。縮小する時期だと感じました。
tinkerbell が稼働している現在、SSH で接続さえもできません――それが必要なのはもうありません。
ホストされているサービスを更新したい場合は、該当レジストリに新しいコンテナイメージを正しいタグでプッシュすれば、tinkerbell が自動的に取得してデプロイします。
この記事では、Fedora CoreOS、Ignition、Podman Quadlets を主役に据え、Terraform を不可欠なサポートキャラクターとして活用した「煙と鏡」の旅路を解説します。
このスタックは私が重視する全ての要件を満たしています。
興味がある方は、GitHub に tinkerbell の完全セットアップを公開しています。
本記事は実験ログとして書かれていますので、最終結果だけに興味がある場合はぜひご覧ください。
コンテナ中心・宣言型・低メンテナンス
moana の完全手動構成を再現したくはありませんでした。
結局のところ、すべては
nspawn コンテナをその場で相互接続できる小さなスクリプトに依存していました。サーバー内部を細かく操作する時間とモチベーションがなくなっていたのです。
最初のアイデアは、moana で唯一保持したいサービスであるこのウェブサイトから始めることでした。
既にそのウェブサイト用コンテナイメージを作成していたので、将来的に他プロジェクトも展開できるような最も単純かつ長期的に安全な本番デプロイ方法が必要でした。
静的ウェブサイトのホスティングだけなら、このセットアップは過剰設計で逆効果になる可能性があります。
tinkerbell は「インターネット上の小さなハンドラボ」―クラウド型ホームラボを実現するための足場です。
- Docker Compose 単体では適合しませんでした。Compose ファイルは好きですが、VM をプロビジョニングして管理する必要があります。
- Ansible で VM をプロビジョンできますが、プレイブック作成は思ったより難しく、既存デプロイを信頼性高く更新することも同時に求められます。
- Kubernetes は紙上では魅力的ですが、我々が直面しない課題の妥協から生まれる非常に複雑なスタックです。マネージドクラスタは便利かもしれませんが、費用対効果が低く moana の退役という動機を損ねます。
コンテナ実行専用 OS として設計された CoreOS が際立ちました。Ignition から調査を始めたのです。Ignition は初回起動時に VM を一度だけプロビジョンし、以後変更が必要な場合は VM を捨て新しく作るというモデルです。
直感的には逆らいそうですが、Ansible の代替を探していた主な理由を排除します。
systemd ユニットファイルで podman CLI コマンドを使ってコンテナを起動する方法は面倒だったため、Docker Compose 風のオーケストレーション――Podman Quadlets と自動更新に進みました。
これで全てが繋がり、何をすべきかが明確になりました。非常にワクワクしました。
tinkerbell の構築
1年余りにわたり、私のウェブサイトは OCaml でビルドされたスタンドアロン静的バイナリを RAM 上で提供し、Nginx が TLS 終端、certbot が証明書更新を担当していました。
moana のセットアップが手動だったと正直に言っているのは事実です。デプロイを自動化したいだけでした。
tinkerbell で運用するコンテナは二つ想定しました:
- リバースプロキシ – 長年 Nginx を愛用してきましたが、Caddy が自動的に TLS 証明書を取得・更新できると聞いて購入決定。
- ウェブサイト本体 – 静的バイナリは Scratch ベースイメージでほぼオーバーヘッドゼロのコンテナ化が可能です。
ウェブサイトを Vultr がホストする無料プラン公開レジストリに公開しました。
オフラインコピー取得も次のコマンド一つで完了します:
docker pull ams.vultrcr.com/lthms/www/soap.coffee:live
宣言型 …
この時点でアーキテクチャは明確になりました。次に機械が実行できる形へ変換するステップとして、Ignition 設定を作成し、CoreOS VM を起動します。
Ignition / Butane
Ignition 設定(
.ign)は主にマシン向けに消費される JSON ファイルです。YAML から Butane を使って生成します。例:
variant: fcos version: 1.5.0 passwd: users: - name: lthms ssh_authorized_keys: - ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKajIx3VWRjhqIrza4ZnVnnI1g2q6NfMfMOcnSciP1Ws lthms@vanellope
重要: Ignition は初回起動時に一度だけ実行されます。意味のある変更はマシンを置き換える必要があります。
butane main.bu > main.ign で .ign を生成し、Terraform で Vultr VM を定義します:
resource "vultr_instance" "tinkerbell" { region = "cdg" plan = "vc2-1c-1gb" os_id = "391" label = "tinkerbell" hostname = "tinkerbell" user_data = file("main.ign") }
terraform apply を実行し、新ユーザーで SSH してすべてが機能することを確認しました。
MVP
Terraform が完了したら、Butane 設定でコンテナを起動します。最初は podman を呼び出す systemd サービスを使いました:
systemd: units: - name: soap.coffee.service enabled: true contents: | [Unit] Description=Web Service After=network-online.target Wants=network-online.target [Service] ExecStart=/usr/bin/podman run \ --name soap.coffee \ -p 8901:8901 \ --restart=always \ ams.vultrcr.com/lthms/www/soap.coffee:latest ExecStop=/usr/bin/podman stop soap.coffee [Install] WantedBy=multi-user.target
これはシェルスクリプト風の手法でした。Podman Compose(Quadlet)が存在し、上流で統合されていることを思い出しました。
Quadlets
Quadlet では systemd 設定に似たフォーマットでコンテナを記述します;実際の systemd ユニットファイルは
systemd ジェネレーターによって自動生成されます。ウェブサイト用 .container ファイル例:
[Container] ContainerName=soap.coffee Image=ams.vultrcr.com/lthms/www/soap.coffee:live [Service] Restart=always [Install] WantedBy=multi-user.target
Caddy の設定も同様に
.container ファイル化。二つのコンテナが通信できるよう pod を導入:
# soap.pod [Pod] Name=webapp
そして各コンテナで
PodName=webapp を参照。
tinkerbell の再デプロイで全て正常に動作しました。
自動更新
最後の仕上げは Podman の自動更新です。
コンテナを
io.containers.autoupdate=registry でラベル付けし、podman-auto-update.timer を有効化すると、VM が定期的にレジストリをチェックして新しいイメージがあれば必要なコンテナを更新します。
デフォルトのタイマー(1日1回)を 1 時間ごとに変更:
sudo systemctl enable podman-auto-update.timer sudo systemctl start podman-auto-update.timer
これで新しいタグ(例:
www/soap.coffee:live)をプッシュすれば、サイトは最大 1 時間以内に更新されます。SSH はデプロイに不要です。
今後の展望
tinkerbell は数日間稼働しており、システムには満足しています。
業界標準への近似を目指した結果であり、何か新しいものを発明したわけではありません。
ただし SSH で可視化できないため、システムはブラックボックスになりがちです。
将来的にメトリクス・ログ・トレーシングの観測スタックを構築し、TLS 証明書をブロックストレージへ移行して耐久性を高める計画です。
結局 tinkerbell は「イミュータブルインフラ」の実験であり、1 回デプロイすれば VM が更新を処理し、シンプルさを保ちます。