<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
	<channel>
		<title>運用 on nsys.dev 技術ブログ</title>
		<link>https://blog.nsys.dev/tags/%E9%81%8B%E7%94%A8/</link>
		<description>Recent content in 運用 on nsys.dev 技術ブログ</description>
		<generator>Hugo</generator>
		<language>ja</language>
		
		
		
		
			<lastBuildDate>Mon, 15 Jun 2026 00:00:00 +0000</lastBuildDate>
		
			<atom:link href="https://blog.nsys.dev/tags/%E9%81%8B%E7%94%A8/index.xml" rel="self" type="application/rss+xml" />
			<item>
				<title>書いたものを自分の手元に置く ― Markdown から記事を出す小さな出版ツールを作りました</title>
				<link>https://blog.nsys.dev/posts/own-your-writing/</link>
				<pubDate>Mon, 15 Jun 2026 00:00:00 +0000</pubDate>
				<guid>https://blog.nsys.dev/posts/own-your-writing/</guid>
				<description>&lt;blockquote&gt;&#xA;&lt;p&gt;&lt;strong&gt;シリーズ・第 1 回&lt;/strong&gt; — 「個人で出版ツールを作って配るまで」（全 6 回予定）。各回は単独でも読めますが、まとめて辿るなら &lt;a href=&#34;https://blog.nsys.dev/tags/crofty/&#34;&gt;シリーズの記事一覧&lt;/a&gt; からどうぞ。今回は全体像と前提の共有で、個々の設計判断は次回以降に取り上げます。&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;書いたものは、どこに残るでしょうか。&lt;/p&gt;&#xA;&lt;p&gt;普段、文章を書く場所の多くは、誰かのプラットフォームの上です。読んでもらいやすい一方で、記事の住所（URL）も、見た目も、読者との接点も、その場所の仕様に委ねることになります。サービスが方針を変えたり、畳んだりすれば、書いたものもそれに従うしかありません。&lt;/p&gt;&#xA;&lt;p&gt;それ自体が悪いわけではないのですが、「長く手元に残しておきたい文章」については、もう少し自分の側に置いておきたい。そう思ったのが、小さな出版ツール &lt;strong&gt;crofty&lt;/strong&gt; を作りはじめたきっかけでした。&lt;/p&gt;&#xA;&lt;p&gt;このシリーズでは、その crofty を作って配るまでに考えたこと・つまずいたことを、テーマごとに分けて残していきます。今回はその土台として、「何をしたかったのか」と「全体の流れ」をまとめます。&lt;/p&gt;&#xA;&lt;h2 id=&#34;何をしたかったか&#34;&gt;何をしたかったか&lt;/h2&gt;&#xA;&lt;p&gt;やりたかったことは、ひとことで言うと「自分のコンテンツを所有したまま出版する」ことでした。具体的には、次の 4 つです。&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;自分のドメインで出す&lt;/strong&gt; — 記事の住所（URL）を、自分の側に持っておく&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Markdown で書く&lt;/strong&gt; — プレーンテキストなら、どこにでも移せて、長く読める&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;静的サイトにする&lt;/strong&gt; — データベースを持たず表示が速い。仕組みを入れ替えても記事はそのまま&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;エッジから配信する&lt;/strong&gt; — 世界中どこからでも、軽く開ける&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;平たく言えば、「書いたものが、それを作る道具より長生きする」状態にしておきたかった、ということです。&lt;/p&gt;&#xA;&lt;h2 id=&#34;なぜ既存のツールそのままではないのか&#34;&gt;なぜ既存のツールそのままではないのか&lt;/h2&gt;&#xA;&lt;p&gt;ここで正直な疑問があります。静的サイトを作る道具（いわゆる SSG）はすでに優秀なものが揃っていて、たとえば Hugo はとても速くて安定しています。実際、crofty も内部では Hugo を使っています。では、なぜわざわざ別の道具を作ったのか。&lt;/p&gt;&#xA;&lt;p&gt;理由は、「出版」は書くこと自体だけでは終わらないからです。実際にやってみると、書く前後にこまごました工程がついてきます。&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;記事を書く土台を用意する（最初のセットアップ）&lt;/li&gt;&#xA;&lt;li&gt;体裁が整っているかを確かめる&lt;/li&gt;&#xA;&lt;li&gt;出力が、配信先で正しく開ける形になっているかを確かめる&lt;/li&gt;&#xA;&lt;li&gt;配信する&lt;/li&gt;&#xA;&lt;li&gt;後から見た目や設定を見直す&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;これらを、その都度ばらばらの手順でやるのではなく、&lt;strong&gt;コマンド 1 つずつの薄い手順&lt;/strong&gt;にまとめておきたかった。crofty は、Hugo を置き換えるものではなく、その上に薄くかぶせた小さな道具です。土台は素の Hugo のままなので、いつでも crofty を外して、ただの Hugo プロジェクトとして扱えます。この「いつでも自分の手元に戻せる」感覚も、最初から大事にしていました。&lt;/p&gt;&#xA;&lt;p&gt;なお、「道具を変えても記事は自分のもの」をどう保証するかは、それ自体で 1 本の記事になるテーマなので、次回にゆずります。&lt;/p&gt;&#xA;&lt;h2 id=&#34;全体の流れ&#34;&gt;全体の流れ&lt;/h2&gt;&#xA;&lt;p&gt;実際の流れは、とてもシンプルです。Markdown を書いて、組み立てて、配る。ほぼそれだけです。&lt;/p&gt;&#xA;&lt;figure class=&#34;mermaid&#34;&gt;&#xA;&lt;img src=&#34;pipeline.svg&#34; alt=&#34;Markdown を書き、crofty build で静的サイト（dist）を生成し、crofty deploy でエッジに配信して、読者のブラウザに届くまでの流れ図&#34;&gt;&#xA;&lt;figcaption&gt;書く → 組み立てる → 配る、という一本道&lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;p&gt;登場するコマンドも、役割で並べるとこれだけです。&lt;/p&gt;</description>
			</item>
			<item>
				<title>分散したスクレイピングを、公式nginxのまま国単位のCIDRで止めた話</title>
				<link>https://blog.nsys.dev/posts/nginx-geo-country-block/</link>
				<pubDate>Fri, 12 Jun 2026 00:00:00 +0000</pubDate>
				<guid>https://blog.nsys.dev/posts/nginx-geo-country-block/</guid>
				<description>&lt;p&gt;あるWebサイトで、特定の国のIPからのアクセスが目立って増えていることに気づきました。調べてみると、よくある「1台のサーバーから猛烈に叩いてくる」タイプではなく、&lt;strong&gt;非常に多くのIPに分散して、1IPあたりは控えめなペースでアクセスしてくる&lt;/strong&gt;スクレイピングでした。&lt;/p&gt;&#xA;&lt;p&gt;この記事は、その正体の見立てと、どう塞ぐかを「ブラスト半径（壊れたときの影響範囲）」で選び、稼働中のサーバーを止めずに展開するまでの記録です。同じような分散アクセスに悩む方の手がかりになれば嬉しいです。&lt;/p&gt;&#xA;&lt;h2 id=&#34;何が起きていたか&#34;&gt;何が起きていたか&lt;/h2&gt;&#xA;&lt;p&gt;アクセスログを集計すると、増えていたトラフィックには次のような特徴がありました。&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;ほぼ &lt;strong&gt;100% が &lt;code&gt;GET&lt;/code&gt;&lt;/strong&gt;（ログイン試行やフォーム投稿のような書き込みは無い）&lt;/li&gt;&#xA;&lt;li&gt;リクエスト先は&lt;strong&gt;記事コンテンツのパスに集中&lt;/strong&gt;（脆弱性スキャンのような不審なパスはほぼ無い）&lt;/li&gt;&#xA;&lt;li&gt;User-Agent が複数のブラウザ版を&lt;strong&gt;不自然なほど均等にローテーション&lt;/strong&gt;している&lt;/li&gt;&#xA;&lt;li&gt;送信元は&lt;strong&gt;消費者向けのISP&lt;/strong&gt;が中心（データセンターではない）&lt;/li&gt;&#xA;&lt;li&gt;1.5万を超えるIPに分散し、&lt;strong&gt;1IPあたりのリクエストは十数件程度&lt;/strong&gt;に抑えられている&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;つまり攻撃ではなく、&lt;strong&gt;レート制限を回避するように設計されたコンテンツ収集ボット&lt;/strong&gt;でした。記事をひたすら集めているようです。住宅・モバイル回線のプロキシ網を経由していると見られます。&lt;/p&gt;&#xA;&lt;p&gt;ここで効いてくるのが、この「1IPあたりは控えめ」という点です。「短時間に大量アクセスしてくるIPを検知して個別に弾く」という、送信元ごとの振る舞いを見るやり方が、この相手にはほぼ効きません。&lt;/p&gt;&#xA;&lt;table&gt;&#xA;&#x9;&lt;thead&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th&gt;観点&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th&gt;ありがちな大量アクセス&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th&gt;今回の分散アクセス&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&lt;/thead&gt;&#xA;&#x9;&lt;tbody&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;送信元IP&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;少数に集中&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;&lt;strong&gt;1.5万超に分散&lt;/strong&gt;&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;1IPあたりの頻度&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;高く、突出する&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;&lt;strong&gt;低く、正規利用者と見分けにくい&lt;/strong&gt;&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;個別IP単位で弾けるか&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;◎ 閾値で検知しやすい&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;&lt;strong&gt;✕ 閾値を越えず素通り。厳しくすると正規を巻き込む&lt;/strong&gt;&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;p&gt;個別IPの振る舞いで戦うのが分の悪い相手なら、&lt;strong&gt;送信元の「国／ネットワーク単位」でまとめて塞ぐ&lt;/strong&gt;のが素直な解になります。幸い、このボットは特定の国のISPに集中していました。&lt;/p&gt;&#xA;&lt;h2 id=&#34;なぜipアドレス帯cidrで塞ぐのか&#34;&gt;なぜ「IPアドレス帯（CIDR）」で塞ぐのか&lt;/h2&gt;&#xA;&lt;p&gt;国単位で塞ぐと聞くと、「1.5万個のIPをリストにするのか」と身構えるかもしれません。実際にはその必要はなく、&lt;strong&gt;CIDR&lt;/strong&gt;（IPアドレス帯）で扱います。&lt;/p&gt;&#xA;&lt;p&gt;CIDRは &lt;code&gt;203.0.113.0/24&lt;/code&gt; のような表記で、&lt;strong&gt;1エントリが1つのIPではなく、1つのネットワーク帯&lt;/strong&gt;を表します。帯はかなり広く、数字（プレフィックス長）が小さいほど広大になります。&lt;/p&gt;&#xA;&lt;table&gt;&#xA;&#x9;&lt;thead&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th&gt;CIDR表記&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th&gt;1行でカバーするIP数（目安）&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&lt;/thead&gt;&#xA;&#x9;&lt;tbody&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;&lt;code&gt;/24&lt;/code&gt;&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;約 256&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;&lt;code&gt;/16&lt;/code&gt;&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;約 6.5 万&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;&lt;code&gt;/12&lt;/code&gt;&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;約 104 万&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;&lt;code&gt;/10&lt;/code&gt;&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;約 419 万&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;p&gt;つまり &lt;code&gt;/10&lt;/code&gt; 1行で約419万IPを覆えます。&lt;/p&gt;&#xA;&lt;p&gt;なぜ1行でこれだけ覆えるのか。IPアドレスを2進数で見ると、プレフィックス長までが&lt;strong&gt;ネットワーク部&lt;/strong&gt;（一致を見る範囲）、その先が&lt;strong&gt;ホスト部&lt;/strong&gt;（どんな値でも一致）になるからです。&lt;code&gt;/24&lt;/code&gt; なら下位8ビットが自由になり、1行で256個をまとめて指せます。&lt;/p&gt;&#xA;&lt;p&gt;ある国に割り当てられたアドレス空間は、集約版のCIDRリストにすると&lt;strong&gt;おおよそ5,500行&lt;/strong&gt;で表せます。この5,500行が覆うIPの総数は&lt;strong&gt;3.4億を超えます&lt;/strong&gt;。しかも重要なのは——&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;今観測しているIPだけでなく、&lt;strong&gt;まだ観測していないIP&lt;/strong&gt;も最初から含まれる&lt;/li&gt;&#xA;&lt;li&gt;プロキシ網が明日から使い始める新しいIPも、同じ国の割り当てなら&lt;strong&gt;先回りで&lt;/strong&gt;含まれる&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;個別IPを1つずつ塞ぐのは、3.4億を相手にしたモグラ叩きです。国単位のCIDRリストなら、&lt;strong&gt;約5,500行で先回りして面で塞げる&lt;/strong&gt;。これが本質的な利点でした。&lt;/p&gt;&#xA;&lt;h2 id=&#34;実装方式はブラスト半径で選んだ&#34;&gt;実装方式は「ブラスト半径」で選んだ&lt;/h2&gt;&#xA;&lt;p&gt;国単位のCIDR遮断を実現する方法はいくつかあります。今回は前提として、&lt;strong&gt;前段にロードバランサーやCDNが無く、各サーバーが直接 80/443 を公開している&lt;/strong&gt;構成でした。つまり、最前面のnginxが転ぶと、その1台はWebごとオフラインになります。&lt;/p&gt;&#xA;&lt;p&gt;この前提だと、選定基準は性能よりも「&lt;strong&gt;壊れたときに、どこまで巻き添えになるか&lt;/strong&gt;（ブラスト半径）」が主役になります。3つの候補を並べました。&lt;/p&gt;&#xA;&lt;table&gt;&#xA;&#x9;&lt;thead&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th&gt;方式&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th&gt;仕組み&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th&gt;壊れたときの影響範囲&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&lt;/thead&gt;&#xA;&#x9;&lt;tbody&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;ホストのファイアウォール&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;カーネルで対象IP帯を破棄&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;&lt;strong&gt;ホスト全体&lt;/strong&gt;。設定を誤ると自分のSSHごと締め出す危険。コンテナ宛の通し方も間違えやすい&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;nginx + 外部のGeoIPデータベース&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;専用モジュールで国を判定&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;nginxコンテナ内。ただし&lt;strong&gt;公式イメージにモジュールが無く、自前ビルドのイメージを所有&lt;/strong&gt;することになる&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;&lt;strong&gt;nginx標準のgeoモジュール + CIDRリスト&lt;/strong&gt;&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;標準機能でIP帯を判定&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;&lt;strong&gt;nginxコンテナ内。データファイルが壊れても起動前の検証で弾け、nginx本体は無傷&lt;/strong&gt;&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;p&gt;3つを「効果 × ブラスト半径（壊れたときの影響範囲）」で並べると、選ぶべき位置がはっきりします。&lt;/p&gt;</description>
			</item>
			<item>
				<title>ブログを追加しました</title>
				<link>https://blog.nsys.dev/posts/blog-start/</link>
				<pubDate>Tue, 09 Jun 2026 00:00:00 +0000</pubDate>
				<guid>https://blog.nsys.dev/posts/blog-start/</guid>
				<description>&lt;p&gt;開発の現場で取り組んだことを、備忘録として少しずつ残していく場所を用意しました。肩肘張らず、自分の振り返りも兼ねて、ゆっくり綴っていけたらと思います。&lt;/p&gt;&#xA;&lt;h2 id=&#34;構成の方針&#34;&gt;構成の方針&lt;/h2&gt;&#xA;&lt;p&gt;ホームページ &lt;a href=&#34;https://nsys.dev&#34;&gt;https://nsys.dev&lt;/a&gt; は表示速度を優先しており、PageSpeed Insights で各指標 100 点を維持しています。ブログを足すにあたっても、その方針は崩さないことを前提に設計しました。&lt;/p&gt;&#xA;&lt;style&gt;&#xA;.ps-shots{display:flex;gap:1rem;flex-wrap:wrap;margin:1.5rem 0}&#xA;.ps-shots figure{flex:1 1 280px;margin:0;text-align:center}&#xA;.ps-shots figcaption{font-size:.85rem;margin-top:.4rem}&#xA;.ps-thumb{display:block;cursor:zoom-in}&#xA;.ps-toggle{position:absolute;width:1px;height:1px;margin:-1px;clip:rect(0 0 0 0);overflow:hidden}&#xA;.ps-zoom{display:none}&#xA;#ps-zm:checked ~ .ps-zoom.zm,&#xA;#ps-zp:checked ~ .ps-zoom.zp{display:flex;position:fixed;inset:0;z-index:1000;background:rgba(0,0,0,.85);align-items:center;justify-content:center;padding:1rem;cursor:zoom-out}&#xA;.ps-zoom img{max-width:100%;max-height:100%}&#xA;&lt;/style&gt;&#xA;&lt;div class=&#34;ps-shots&#34;&gt;&#xA;&lt;input type=&#34;checkbox&#34; id=&#34;ps-zm&#34; class=&#34;ps-toggle&#34;&gt;&#xA;&lt;input type=&#34;checkbox&#34; id=&#34;ps-zp&#34; class=&#34;ps-toggle&#34;&gt;&#xA;&lt;figure&gt;&#xA;&lt;label class=&#34;ps-thumb&#34; for=&#34;ps-zm&#34;&gt;&lt;img src=&#34;pagespeed-mobile.avif&#34; alt=&#34;モバイルの PageSpeed Insights 結果（各指標 100 点）&#34; width=&#34;720&#34; height=&#34;556&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34;&gt;&lt;/label&gt;&#xA;&lt;figcaption&gt;モバイル&lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;figure&gt;&#xA;&lt;label class=&#34;ps-thumb&#34; for=&#34;ps-zp&#34;&gt;&lt;img src=&#34;pagespeed-pc.avif&#34; alt=&#34;PC の PageSpeed Insights 結果（各指標 100 点）&#34; width=&#34;720&#34; height=&#34;557&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34;&gt;&lt;/label&gt;&#xA;&lt;figcaption&gt;PC&lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;label class=&#34;ps-zoom zm&#34; for=&#34;ps-zm&#34; aria-label=&#34;閉じる&#34;&gt;&lt;img src=&#34;pagespeed-mobile-full.avif&#34; alt=&#34;モバイルの PageSpeed Insights 結果（フルサイズ）&#34; width=&#34;961&#34; height=&#34;743&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34;&gt;&lt;/label&gt;&#xA;&lt;label class=&#34;ps-zoom zp&#34; for=&#34;ps-zp&#34; aria-label=&#34;閉じる&#34;&gt;&lt;img src=&#34;pagespeed-pc-full.avif&#34; alt=&#34;PC の PageSpeed Insights 結果（フルサイズ）&#34; width=&#34;960&#34; height=&#34;743&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34;&gt;&lt;/label&gt;&#xA;&lt;/div&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;ページは Markdown から静的 HTML を生成&lt;/li&gt;&#xA;&lt;li&gt;CSS はビルド時に HTML へインライン化し、外部リクエストをゼロに&lt;/li&gt;&#xA;&lt;li&gt;クライアント側の JavaScript は使わない（コードのハイライトもビルド時に処理）&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;既存のビルド基盤（Node + Docker）にそのまま乗せる形にしたため、新しいランタイムやデータベースは増えていません。&lt;/p&gt;&#xA;&lt;h2 id=&#34;よくあるブログ構成にしなかった理由&#34;&gt;よくあるブログ構成にしなかった理由&lt;/h2&gt;&#xA;&lt;p&gt;ブログというと、CMS とデータベースを用意し、管理画面から書いて公開する構成が一般的です。多人数での運用や、コメント・検索といった動的な機能が得意で、用途が合えばとても便利な仕組みだと思います。&lt;/p&gt;</description>
			</item>
	</channel>
</rss>
