LeetCode 735: Asteroid Collision - スタックで衝突判定を美しく解く

問題概要 整数で表される小惑星の配列 asteroids が与えられる。各小惑星について: 絶対値:大きさ 符号:方向(正=右、負=左) 全て同じ速度で移動 衝突ルール: 小さい方が爆発 同じ大きさなら両方爆発 同じ方向に移動する小惑星は衝突しない 全ての衝突後の状態を返せ。 失敗した実装 from collections import deque class Solution: def asteroidCollision(self, asteroids: List[int]) -> List[int]: stack = [] for aster in asteroids: if len(stack) <= 0 or (aster <= 0) == (stack[-1] <= 0): stack.append(aster) else: while len(stack) > 0 and (aster <= 0) != (stack[-1] <= 0) and abs(aster) > abs(stack[-1]): stack.pop() if len(stack) <= 0 or (stack[-1] <= 0) == (aster <= 0): stack.append(aster) elif abs(aster) == abs(stack[-1]): stack.pop() return stack 問題点 同じ条件判定 if len(stack) <= 0 or (stack[-1] <= 0) == (aster <= 0) が2箇所に重複 制御フローが複雑で読みにくい (aster <= 0) != (stack[-1] <= 0) は「符号が異なる」を検出するが、衝突しないケースも含む 最適解 class Solution: def asteroidCollision(self, asteroids: List[int]) -> List[int]: stack = [] for asteroid in asteroids: while stack and asteroid < 0 < stack[-1]: # 右向き vs 左向きの衝突が発生 if abs(stack[-1]) < abs(asteroid): # 右向きが小さい → 爆発して次の右向きとも衝突判定 stack.pop() continue elif abs(stack[-1]) == abs(asteroid): # 同じ大きさ → 両方爆発 stack.pop() break else: # 衝突しなかった or 左向きが勝った stack.append(asteroid) return stack わかったこと 1. ループ条件の本質 asteroid < 0 < stack[-1] これは (asteroid < 0) and (0 < stack[-1]) と同じ。つまり: ...

January 13, 2026 · 2 min

Go言語でネットワークプログラミングを学ぶ - 第3章

3.1 プロジェクト構造 go-network-programming/ ├── go.mod ├── go.sum ├── main.go ├── packet.go ├── node.go ├── link.go ├── network_stats.go ├── bandwidth_limiter.go ├── mac_address.go # 新規追加 ├── ethernet_frame.go # 新規追加 └── switch.go # 新規追加 この章では、スイッチを実装して複数のノードを接続できるローカルネットワークを構築します。また、MACアドレスを導入してイーサネットレベルでの通信を実現します。 3.2 MACアドレスの実装 MACアドレス(Media Access Control Address)は、ネットワークインターフェースの物理アドレスです。 ファイル名: ./mac_address.go package main import ( "fmt" "math/rand" "strconv" "strings" ) // MACAddress はMAC(Media Access Control)アドレスを表現する // 実際のイーサネットで使用される6バイトの物理アドレス type MACAddress struct { bytes [6]byte // 6バイトのMACAアドレス(例:aa:bb:cc:dd:ee:ff) } // NewMACAddress は指定されたバイト配列からMACアドレスを作成 func NewMACAddress(bytes [6]byte) MACAddress { return MACAddress{bytes: bytes} } // ParseMACAddress は文字列からMACアドレスを解析 // 例:ParseMACAddress("aa:bb:cc:dd:ee:ff") func ParseMACAddress(s string) (MACAddress, error) { parts := strings.Split(s, ":") if len(parts) != 6 { return MACAddress{}, fmt.Errorf("invalid MAC address format: %s", s) } var mac MACAddress for i, part := range parts { val, err := strconv.ParseUint(part, 16, 8) if err != nil { return MACAddress{}, fmt.Errorf("invalid hex value in MAC address: %s", part) } mac.bytes[i] = byte(val) } return mac, nil } // RandomMACAddress はランダムなMACアドレスを生成 // ユニキャスト、ローカル管理アドレスとして生成 func RandomMACAddress() MACAddress { var mac MACAddress for i := 0; i < 6; i++ { mac.bytes[i] = byte(rand.Intn(256)) } // ユニキャスト(LSBを0に)、ローカル管理(2番目のLSBを1に)に設定 mac.bytes[0] = (mac.bytes[0] & 0xFC) | 0x02 return mac } // String はMACアドレスの文字列表現を返す func (mac MACAddress) String() string { return fmt.Sprintf("%02x:%02x:%02x:%02x:%02x:%02x", mac.bytes[0], mac.bytes[1], mac.bytes[2], mac.bytes[3], mac.bytes[4], mac.bytes[5]) } // Equals は2つのMACアドレスが等しいかチェック func (mac MACAddress) Equals(other MACAddress) bool { return mac.bytes == other.bytes } // IsUnicast はユニキャストアドレスかチェック func (mac MACAddress) IsUnicast() bool { return (mac.bytes[0] & 0x01) == 0 } // IsBroadcast はブロードキャストアドレスかチェック func (mac MACAddress) IsBroadcast() bool { return mac.bytes == [6]byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} } // IsMulticast はマルチキャストアドレスかチェック func (mac MACAddress) IsMulticast() bool { return (mac.bytes[0] & 0x01) == 1 && !mac.IsBroadcast() } // BroadcastMAC はブロードキャストMACアドレスを返す func BroadcastMAC() MACAddress { return MACAddress{bytes: [6]byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}} } 3.3 イーサネットフレームの実装 MACアドレスを含むイーサネットフレーム構造を実装します。 ...

January 11, 2026 · 12 min

Go言語でネットワークプログラミングを学ぶ - 第2章

2.1 プロジェクト構造 go-network-programming/ ├── go.mod ├── go.sum ├── main.go ├── packet.go ├── node.go ├── link.go ├── network_stats.go # 新規追加 └── bandwidth_limiter.go # 新規追加 この章では、ネットワークに時間の概念を本格的に導入します。実際のネットワークのように、帯域幅制限、パケット処理時間、スループット測定を実装し、大きなファイルの送信をシミュレートします。 2.2 ネットワーク統計の追加 ネットワークの性能を測定するための統計機能を追加します。 ファイル名: ./network_stats.go package main import ( "fmt" "sync" "time" ) // NetworkStats はネットワークの統計情報を管理する // 実際のネットワークモニタリングツールのような機能を提供 type NetworkStats struct { mu sync.RWMutex startTime time.Time totalPacketsSent int64 totalPacketsRecv int64 totalBytesSent int64 totalBytesRecv int64 packetLossCount int64 lastUpdateTime time.Time } // NewNetworkStats は新しい統計オブジェクトを作成 func NewNetworkStats() *NetworkStats { return &NetworkStats{ startTime: time.Now(), lastUpdateTime: time.Now(), } } // RecordSentPacket は送信パケットを記録 func (ns *NetworkStats) RecordSentPacket(packet *Packet) { ns.mu.Lock() defer ns.mu.Unlock() ns.totalPacketsSent++ ns.totalBytesSent += int64(packet.Size) ns.lastUpdateTime = time.Now() } // RecordReceivedPacket は受信パケットを記録 func (ns *NetworkStats) RecordReceivedPacket(packet *Packet) { ns.mu.Lock() defer ns.mu.Unlock() ns.totalPacketsRecv++ ns.totalBytesRecv += int64(packet.Size) ns.lastUpdateTime = time.Now() } // RecordPacketLoss はパケット損失を記録 func (ns *NetworkStats) RecordPacketLoss() { ns.mu.Lock() defer ns.mu.Unlock() ns.packetLossCount++ ns.lastUpdateTime = time.Now() } // GetThroughput は現在のスループットを計算(bps: bits per second) func (ns *NetworkStats) GetThroughput() float64 { ns.mu.RLock() defer ns.mu.RUnlock() duration := time.Since(ns.startTime).Seconds() if duration == 0 { return 0 } // バイト数をビット数に変換(1バイト = 8ビット) totalBits := float64(ns.totalBytesSent) * 8 return totalBits / duration } // GetPacketLossRate はパケット損失率を計算(0.0-1.0) func (ns *NetworkStats) GetPacketLossRate() float64 { ns.mu.RLock() defer ns.mu.RUnlock() if ns.totalPacketsSent == 0 { return 0.0 } return float64(ns.packetLossCount) / float64(ns.totalPacketsSent) } // Print は統計情報を表示 func (ns *NetworkStats) Print() { ns.mu.RLock() defer ns.mu.RUnlock() duration := time.Since(ns.startTime) throughputBps := ns.GetThroughput() throughputKbps := throughputBps / 1000 lossRate := ns.GetPacketLossRate() * 100 fmt.Printf("=== Network Statistics ===\n") fmt.Printf("Duration: %v\n", duration.Round(time.Millisecond)) fmt.Printf("Packets Sent: %d\n", ns.totalPacketsSent) fmt.Printf("Packets Received: %d\n", ns.totalPacketsRecv) fmt.Printf("Bytes Sent: %d\n", ns.totalBytesSent) fmt.Printf("Bytes Received: %d\n", ns.totalBytesRecv) fmt.Printf("Throughput: %.2f Kbps\n", throughputKbps) fmt.Printf("Packet Loss Rate: %.2f%%\n", lossRate) fmt.Printf("=========================\n") } 2.3 帯域幅制限機能の実装 実際のネットワークのように、帯域幅制限を実装します。 ...

January 11, 2026 · 7 min

Go言語でネットワークプログラミングを学ぶ - 第1章

第1章:ネットワークの基本要素 - Node、Link、Packet 1.1 プロジェクト構造 go-network-programming/ ├── go.mod ├── go.sum ├── main.go ├── packet.go ├── node.go └── link.go この章では、ネットワークの基本的な構成要素であるノード、リンク、パケットをGo言語で実装します。実際のネットワーク機器と同じように、複数のプロセスが並行して動作し、channelを通じてパケットを送受信する仕組みを構築します。 1.2 パケットの実装 パケットは、ネットワークで送信される情報の基本単位です。送信元、宛先、データ本体、タイムスタンプなどの情報を含みます。 ファイル名: ./packet.go package main import ( "fmt" "time" "github.com/google/uuid" ) // Packet はネットワークで送信される基本単位を表現する // 実際のTCP/IPパケットのように、ヘッダ情報とペイロードを持つ type Packet struct { ID string // パケットの一意識別子 Source string // 送信元ノードの名前 Destination string // 宛先ノードの名前 Data []byte // 実際のデータ(ペイロード) Size int // データサイズ(バイト) Timestamp time.Time // パケット生成時刻 } // NewPacket は新しいパケットを生成する // 実際のネットワークスタックでパケットが生成される処理を模倣 func NewPacket(source, destination string, data []byte) *Packet { return &Packet{ ID: uuid.New().String(), Source: source, Destination: destination, Data: data, Size: len(data), Timestamp: time.Now(), } } // String はパケットの文字列表現を返す(デバッグ用) func (p *Packet) String() string { return fmt.Sprintf("Packet{ID: %s, From: %s, To: %s, Size: %d bytes}", p.ID[:8], p.Source, p.Destination, p.Size) } 1.3 ノードの実装 ノードは、ネットワーク上のデバイス(PC、スマートフォン、ルーターなど)を表現します。パケットの送受信機能を持ち、複数のリンクに接続できます。 ...

January 10, 2026 · 6 min

Go言語でネットワークプログラミングを学ぶ - 第0章

参考・インスピレーション元 この教材は以下のサイトの構成を参考に、Go言語での実装として新たに構築したものです: CoNeCo|コンピュータネットワーク with Colab: https://www.conecolab.com/ 作者:中山悠(東京農工大学准教授) ライセンス:CC-BY-SA Google Colabを使用したPython実装によるネットワーク学習教材 本教材は上記の教育アプローチにインスピレーションを受けつつ、Go言語での独自実装として作成しています。 Go言語でネットワークプログラミングを学ぶ 第0章:環境構築とネットワーク基礎概念 0.1 環境構築 # Go 1.21以上をインストール go version # プロジェクト初期化 mkdir go-network-programming cd go-network-programming go mod init go-network-programming # 必要なパッケージ go get github.com/google/uuid go get gonum.org/v1/gonum/graph 0.2 なぜGo言語なのか? ネットワークプログラミングにおけるGo言語の利点: 並行処理のサポート:goroutineによる軽量な並行処理 型安全性:プロトコルの違いをコンパイル時に検証 シンプルな文法:複雑な仕様を直感的なコードで表現 標準ライブラリ:充実したネットワーク関連パッケージ 0.3 学習対象の基本概念 ノード (Node) ネットワーク上のデバイス(PC、スマートフォン、ルーターなど) パケットを送受信する機能 一意のアドレスを持つ リンク (Link) ノード間の接続 帯域幅、遅延、エラー率などの特性を持つ 双方向または単方向の通信 パケット (Packet) ネットワークで転送される情報の単位 ヘッダとペイロードから構成 プロトコル層によって内容が変化 0.4 基本アーキテクチャの設計 // ネットワークエンティティの基本インターフェース type NetworkEntity interface { ID() string String() string } // パケット処理のインターフェース type PacketHandler interface { Send(packet Packet, destination string) error Receive() <-chan Packet } // アドレス管理のインターフェース type Addressable interface { Address() Address SetAddress(addr Address) } 0.5 学習計画 第1章: 基本要素の実装 (Node, Link, Packet) 第2章: 時間と並行性の導入 第3章: スイッチングとMACアドレス 第4章: MACアドレス学習とループ回避 第5章: IPパケットとルーティング 第6章: 動的ルーティングプロトコル 第7章: レイヤ化とカプセル化 第8章: アドレス解決プロトコル 第9章: 動的IPアドレス設定とNAT 第10章: TCP接続の確立 第11章: 確認応答と再送制御 第12章: 輻輳制御とウィンドウ制御 第13章: QoSと優先制御 第14章: アプリケーション層プロトコル 第15章: セキュリティと暗号化 0.6 評価ポイント 各章で以下の観点から実装を評価します: ...

January 10, 2026 · 1 min

本が読めなかったから、仕事をやめました - 読書メモ

本の情報 タイトル: 本が読めなかったから、仕事をやめました 著者: [著者名不明] 読書期間: 2025年1月9日 読了状況: 大正時代まで(未完) まえがき - 衝撃の一文 本が読めなかったから、仕事をやめました★ ロックすぎる。 著者の状況 読書好き、本を買うために働く 週5勤務、21時まで残業 気づいたら1年間、本を読んでいない 時間があってもスマホを見てしまう 本を開いても目が閉じる、YouTubeに逃げる 3年半後、退職 退職後、ゆっくり読書できるようになった 著者の問題提起 会社で働きながら本を読むことは難しい 本を読む余裕のない社会はおかしい → SNSで多くの同意が集まる → 趣味全般を続けづらい社会への問い → 「あなたの文化は労働に搾取されている」 所感: 共感と違和感 共感ポイント 仕事のために生きている人が多数派でビビる 仕事は微妙、人間関係は辛い これ、私のことだ 読書好きでもこうなるのか…(本当なら) 余裕がない社会 働きながらX(Twitter)をやるのはマジで辛い 違和感 突然「搾取」という言葉が出てきて怖い 自称漫画家、バンドマンのようなゴミは働きながら続けるべき 辛いからこそ、続けるってことは熱量があるってこと 第1章: 労働と文化的生活の両立 著者の姿勢 文句だけ言っても仕方ない 歴史から学ぶアプローチ なぜ今、両立しなくなったのか どうしたら両立できるのか 『花束みたいな恋をした』分析 登場人物: 麦: 地方の花火職人 → 会社員 絹: 金持ち、大企業 展開: 就職後、麦は忙しくなる 漫画が続かない、頭に入らない パズドラしかやる気しない 絹からの本も無視 心が離れていく テーマ: 長時間労働と文化的生活は両立しないという前提の作品 速読・自己啓発ブームの意味 Amazonで速読、情報処理スキル、読書術が人気 趣味ではなく、自己啓発メイン 効率優先 → 労働と読書の両立をみんななんとかしようとした結果 ファスト教養も同じ構造 第2章: 格差と読書 階級格差が読書意欲に影響 麦(労働者)vs 絹(富裕層)の対比 働けど働けど暮らしは楽にならず 本を読む余裕さえなくなる 暮らしの格差が余暇の時間も奪う 『独学大全』の指摘 格差は動機づけの段階から現れる 学ぶ動機づけがない者 → 学問は役に立たない、僻む 意欲から格差が生まれる 第3章: 明治時代 - 長時間労働と読書の始まり 労働環境 この頃から長時間労働 工場労働者: 農民時代より断然長時間 平均残業時間: 2時間 …あれ? 化学工場: 12時間労働 …は? 労働組合はゴミ、割増料金が魅力的 → 今もだいたい同じ 明治時代の感覚 「最近はみんな忙しそうにしてる」 余裕がなくなった感じ、せっかち 近代化 = せっかち 読書革命 句読点と黙読の発明: ...

January 9, 2026 · 2 min

DFS/BFSの本質:深さと幅を支配するデータ構造の選択

DFS, BFSがわかりづらかったので、いくつかの記事を見て個人的に感じた疑問や 「こういうコード例が欲しい」という要望を踏まえて生成AIに生成してもらった。 生成された内容を検証し、コードを実際に動かして確認したところ、 自分の理解が深まる良い記事になったので、このまま公開することにした。 はじめに LeetCodeでMedium問題を解いていると、必ず遭遇するのがDFS(深さ優先探索)とBFS(幅優先探索)だ。 「Dは深さ、Bは幅」というのは知っている。でも、なぜスタックとキューを使い分けるのか?その本質を理解している人は意外と少ない。 今回は、入れ子リストの例を使って、DFS/BFSの動作原理とデータ構造の関係を視覚的に解説する。 問題設定:入れ子リストをフラット化する 以下のような入れ子構造のリストがあるとする。 data = [1, [4, 5, [6, 7, 8], 2], 3] これをフラットな配列にしたい。このとき、「どの順番で要素を取り出すか」がDFS/BFSの違いだ。 ツリー構造として可視化する 入れ子リストは、実はツリー構造として表現できる。 root / | \ 1 [] 3 | /|\ \ 4 5 [] 2 | /|\ 6 7 8 この木をどう巡回するかで、DFSとBFSが決まる。 DFS(深さ優先探索):とにかく深く潜る 動作イメージ 「見つけた枝があれば、まずそこを最後まで探索する」 訪問順序: 1 → [中に入る] → 4 → 5 → [さらに中] → 6 → 7 → 8 → [戻る] → 2 → [戻る] → 3 結果:[1, 4, 5, 6, 7, 8, 2, 3] ...

January 9, 2026 · 3 min

Audibleで学ぶヨーロッパ中世史:雑談メモ

はじめに Audibleでヨーロッパの歴史を聞きながら、メモを取っていたら思いのほか面白い内容になった。バシレイオス2世からカロリング朝の終焉まで、雑談形式で記録してみる。 バシレイオス2世「ブルガリア人殺し」 基本情報 在位: 976-1025年 異名: Βουλγαροκτόνος(ブルガリア人殺し) 業績: ビザンツ帝国最盛期を築く 有名なエピソード:クレイディオンの戦い(1014年) ブルガリア軍15,000人を捕虜に 捕虜全員の目を潰す(100人に1人だけ片目を残して案内役に) ブルガリア皇帝サムイルがショック死 第一次ブルガリア帝国滅亡 個人的感想 「こいつカスだなー、こいつ大帝でいいの?」 確かに現代の倫理観から見れば戦争犯罪者レベル。ただし中世の「大帝」基準では: 領土拡張 ✓ 敵国完全屈服 ✓ 後世まで語り継がれるインパクト ✓ 帝国繁栄 ✓ オットー2世とマラリア 人物像 在位: 973-983年 比較的穏健な統治者 学問保護、教会制度整備 問題: 南イタリア遠征で無茶をした 死因:マラリア 南イタリア遠征中に感染 983年、28歳で死去 当時のマラリアはほぼ死刑宣告 北欧系には特に致命的 感想: 「やっちゃったねぇ」 中世の皇帝は戦争で死ぬか病気で死ぬかの二択。現代医学があれば… リウトプランド・オブ・クレモナ 外交官としての活動 オットー1世の外交使節 ビザンツ皇帝ニケフォロス2世フォカスとの交渉担当 結果: 大失敗 失敗の原因 ビザンツ側が西欧を「野蛮人」として完全に見下し オットー1世の「ローマ皇帝」称号をビザンツが拒否 リウトプランド本人のプライドの高さ 文学的価値 外交官としては無能だったが、『コンスタンティノープル使節記』は貴重な史料。ルポライターとしては一流。 女帝イレーネ・アテネ女 母子の権力闘争 在位: 797-802年 息子コンスタンティノス6世の摂政として実権掌握 息子が独立を図る 797年: クーデターで息子の目を潰して廃位 史上初の女性単独皇帝 歴史的影響 西欧では「東に皇帝がいない」(女性は皇帝と認めない) 800年: カール大帝の「ローマ皇帝」戴冠の口実に 東西ローマ皇帝位問題の発端 最期 802年: ニケフォロスのクーデターで廃位 レスボス島に流刑 803年: 自然死(比較的穏やかな最期) サラセン人とアッシリア人の違い よくある混同 サラセン人: アラブ・イスラム勢力(中世ヨーロッパ人の呼称) アッシリア人: 古代メソポタミア系民族(主にキリスト教徒) 地理的分布(10-11世紀) サラセン人の拠点: ...

January 3, 2026 · 1 min

k3s + Drone CI/CD構築体験記② 手動ビルドでなんとか動いた

前回のハマり話の続編。 今回は実際にCI/CDパイプラインを動かすところまで進めた。結論から言うと、自動化は99%完成したが、最後の1%(Webhook)で詰んだ。 目標設定 理想は当然これ: GitHub push → Drone検知 → Hugo自動ビルド → Dockerイメージ作成 → k3sデプロイ更新 ただし、私の環境には致命的な制約がある。 制約:外部IP持ってない 自宅サーバーはTailscaleでVPN経由でのみアクセス可能。つまりGitHubからのWebhookが届かない。まあ、DuckDNSでドメインは取ってるけど、それでもTailscale依存の構成。 それでも「やれるとこまでやってみよう」精神で進めた。 .drone.yml 設定 最終的にはこんな感じになった: kind: pipeline type: kubernetes name: hugo-pipeline steps: - name: build-hugo image: klakegg/hugo:latest commands: - cd posts - hugo --minify - ls -la public/ - name: create-docker-context image: alpine:latest commands: - cp -r posts/public ./public - ls -la public/ - name: docker-build image: plugins/docker settings: registry: ghcr.io repo: ghcr.io/wasuken/tech_blog username: from_secret: github_username password: from_secret: github_token tags: - latest - "${DRONE_COMMIT_SHA:0:8}" - name: deploy-to-k3s image: bitnami/kubectl environment: KUBECONFIG: from_secret: kubeconfig commands: - kubectl set image deployment/hugo-site hugo=ghcr.io/wasuken/tech_blog:latest - kubectl rollout status deployment/hugo-site - name: deploy-complete image: alpine:latest commands: - echo "Hugo build complete!" - echo "Image pushed successfully" ポイントは、HugoビルドからDockerイメージ作成、GHCR(GitHub Container Registry)へのプッシュ、最終的なk3sデプロイまで全部自動化したこと。 ...

January 1, 2026 · 2 min

ローカル環境を汚さない静的サイト構築 - Hugo Docker Compose環境構築記録

背景 日課でできる範囲の活動として、軽い記事から、疑問を生成AIに出してもらって、それに答えてもらって、深堀や補足、添削をしてもらった内容までを記事にするという習慣を続けていたが、公開するのはどうなのかなと思った。 しかし、後ほど止めるのはもったいないということで妥協案として、ローカルで動くブログには投稿することにした。 なので、ローカルブログを立ち上げることにした。 最初はGitHub Pagesでよく使われているJekyllを試した。しかし、ローカル環境とDocker環境でRubyのバージョン不一致が発生し、プロジェクト初期化の段階で躓いた。 ローカルのRuby 3.4に対してDockerの最新イメージがRuby 3.1で、この差分が原因でSCSS変換周りでエラーが頻発。Jekyllはプロジェクト作成をローカルで行う必要があるため、「Docker使えば環境差を吸収できる」という謳い文句が実質的に機能しなかった。 もっとうまくやればよかっただろうが、そのときは血が登っていて、Hugoにしてしまった。 要件整理 改めて自分の要件を整理した: Markdownファイルのマウントだけで完結 ローカル環境に一切依存しない プロジェクト初期化もDocker内で実行可能 検索機能とファイル一覧が欲しい これを満たすツールを探した結果、Hugoに行き着いた。 なぜHugoなのか Hugoを選んだ理由は明確: 1. バイナリ単体で動作 Go言語で書かれたHugoは単一バイナリで動作する。RubyやNode.js、Pythonのようなランタイム環境が不要。これにより依存関係地獄から解放される。 2. プロジェクト初期化もDocker内で完結 当初は生成AIの言うとおりに以下のコマンドでプロジェクトを作成した。 docker run --rm -v $(pwd)/posts:/src klakegg/hugo:alpine new site . この1コマンドでプロジェクト作成が完了する。ローカルに何もインストールする必要がない。 のだが、後ほどこれがトラブルを産んだ。 3. 高速なビルド Goの並列処理能力により、数千ページ規模のサイトでも秒単位でビルドが完了する。開発時のホットリロードも快適。 構築手順 1. docker-compose.yml作成 services: hugo: image: hugomods/hugo:base container_name: hugo-blog ports: - "7000:7000" volumes: - ./posts:/src command: server --bind 0.0.0.0 --port 7000 --buildDrafts --buildFuture restart: unless-stopped ポイント: hugomods/hugo:base を使用 ポートは7000にマッピング(後述のブラウザ制限回避) --buildDrafts --buildFuture で下書きと未来日付の記事も表示 2. プロジェクト初期化 docker run --rm -v $(pwd)/posts:/src klakegg/hugo:alpine new site . これで posts/ ディレクトリに必要なファイル群が生成される。 ...

December 21, 2025 · 2 min