はじめに
性能そこそこの小さいPCが流行っていると聞いて衝動買いしてから約一年、購入してすぐにUbuntuを入れて満足した後放ったらかしにしていたが、そいつを使った遊びを思いついた。これまで各媒体に書き散らかしていたブログ記事をまとめ、このPCにホスティングしてみる。
ホストするブログは単なる静的サイトなのだが、普段触らない技術にも触れたいがために色々と盛り込んだら結構多機能になっていた。色々楽しめて学びもあったので詳細をまとめておく。
完成したブログはこちら: https://boom-pei.com/
アーキテクチャ
登場するコンポーネントは以下の通り。基本的に必要なサービスはDocker上で動かし、クライアントからのアクセスはCloudflare Tunnelを通して受け付け、アドミン (制作者の私) はTailscaleを通してマシンに接続する。SSL証明書はLet's Encryptから取得。監視はPrometheusとGrafanaを中心にLoki, Grafana Alloy, cAdvisor, Node Exporterを組み合わせて実現。
- Mini PC: ブログをホストするLinuxマシン
- なんか安かったこちらを使っている: https://jp.bmaxit.com/MaxMini-B1-Pro-pd752582388.html
- OSはUbuntu Server 22.04 LTSに切り返済み
- Dockerコンテナ上のサービス
- Nginx: リバースプロキシ
- Next.js: ブログアプリケーション
- Loki: ログ集約
- Grafana Alloy: ログ転送
- Node Exporter: ホスト監視
- cAdvisor: コンテナ監視
- Prometheus: メトリクス収集
- Grafana: 可視化ダッシュボード
- クライアントアクセス制御
- Cloudflare DNS
- cloudflared: Cloudflare Tunnelクライアント
- アドミンアクセス制御
- Tailscaleネットワーク
- tailscaled: Tailscaleクライアント
- SSL証明書管理
- Let's Encrypt
- Certbot: Let's Encryptクライアント
詳細は以下の項にまとめていく。全体図は次の通り。
アプリケーション構成、アクセス制御
アプリケーション自体はいたってシンプル。前段にNginxをリバースプロキシとして配置し、Next.jsアプリケーションにリクエストを転送する。NginxはSSL終端も担当。ブログアプリケーションはNext.jsで構築されており、静的なページを返すだけ。
ドメイン管理とDNSはCloudflareを使用。Cloudflare Tunnelを使用して、クライアントからのアクセスをNginxに転送する。これにより、ミニPCのポートを直接インターネットに公開せずに済んでいる。
SSL/TLS証明書の管理はCertbotが対応。Let's Encryptから証明書を取得し、Nginxに設定する。自動更新もこれに任せている。
Cloudflareが提供する仕組みを使えば証明書を自前で管理しなくてもいいのですが、ちょっとやってみたかったのでこのようになりました。
監視構成
ここは楽しくオーバーエンジニアリングに勤しんだ。モダンな技術スタックを用いてログ、メトリクス、あと簡単にだがトレース情報も収集。Grafanaで可視化ダッシュボードを構築している。
メトリクス
ホストマシンのデータはNode Exporterが収集し、DockerコンテナのデータはcAdvisorが収集する。これらのメトリクスはPrometheusが定期的にスクレイピングして保存する。
保存されたデータを用いて、Grafanaのダッシュボードではコンテナごとのメモリ使用量やCPU使用率、ホストのディスク使用量やホストマシンの温度なんかを見れるようにしている。
ログ
NginxとNext.jsアプリケーションのログはGrafana Alloyが収集し、Lokiに転送する。ログに含まれるHTTPステータスやバージョン (デプロイされているアプリケーションのgitコミットハッシュ値) など、カーディナリティが低くフィルタリングに便利な情報はAlloyによってラベルとして付与される。
Nginx、Next.jsそれぞれでどのような情報がログに含むべきか、ラベリングすべきかと考えるのは非常に楽しかった。以下ログの例を示す (Googleのクローラーからのアクセスログを少し修正)。
Nginxのログ:
{
"time": "2025-09-14T09:15:43+00:00",
"remote_addr": "xxx.xxx.xxx.xxx",
"x_forwarded_for": "66.249.64.225",
"request_method": "GET",
"request_uri": "/introducing-testing-rule-smoothly-en",
"status": 200,
"body_bytes_sent": 8438,
"request_time": 0.184,
"upstream_addr": "xxx.xxx.xxx.xxx:3000",
"upstream_status": "200",
"upstream_response_time": "0.185",
"upstream_connect_time": "0.001",
"http_referer": "",
"http_user_agent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 5X Build/MMB29P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.7339.127 Mobile Safari/537.36 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)",
"trace_id": "f1cb24a1cd911c3330d9a01346b331a9",
"container_id": "xxxxxxxx"
}
Next.jsアプリケーションのログ (上記と同じトレース情報を保持):
{
"level": "info",
"time": "2025-09-14T09:15:43.697Z",
"version": "xxxxxxxx",
"container_id": "xxxxxxxx",
"trace_id": "f1cb24a1cd911c3330d9a01346b331a9",
"path": "/introducing-testing-rule-smoothly-en",
"status": 200,
"msg": "Post Accessed"
}
トレース
簡単だがトレース情報も付与している。Nginxが発番する trace_id をNext.jsアプリケーションに渡し、アプリケーション側でも同じIDをログに含めるようにしている。これにより、NginxのログとNext.jsのログをこのIDで紐付けて参照できる。
アドミンアクセス制御
アドミン、というか私がミニPCにアクセスする際はTailscaleを使用している。TailscaleはWireGuardベースのVPNソリューションで、セットアップが非常に簡単。これにより、ミニPCにSSH接続したり、Grafanaのダッシュボードにアクセスしたりすることができる。
感想・今後の展望
自分のLinuxマシンを好きにいじって活用するのはなかなか楽しいですね。
実運用に耐える程度の仕組みは構築できたのでブログとしてちゃんと運用していこうと思う。機能面についてはまだまだオーバーエンジニアリングしていきたい。次はKubernetesを導入するか、OpenTelemetryに準拠してテレメトリを収集出来るようにするかで迷っている。まずは前者かな。
以上。

