タイトルからHugoのSlugを自動生成してくれるCLIツールを作ってみた!

概要 Hugo1は静的サイト生成のためのツールで,マークダウンで書いた記事等をHTML/CSSファイルにしてくれます. しかしHugoは新しい記事のテンプレートを作る際にSlug2を考える必要があり,手間でした. uuidgen3などを使っても同様の省力化はできますが,その場合は生成されるテンプレートファイル名と記事の中身が紐づかず管理が面倒になってしまいます. そこで,軽量なローカルLLMを使うことでタイトルに意味が紐づいたSlugを生成してくれるCLIツール4を作成しました. 仕組み 仕組みはシンプルです. ① ユーザにセクション名と作成予定の記事のタイトルを聞きます. ② LLMに適切なシステムプロンプトとユーザが入力した記事のタイトルを与え,Slugの候補を生成してもらいます. ③ Slugの候補は必要に応じて再生成し,決まり次第hugoのコマンドを呼び出して記事のひな形を作成します. インストール 前提として,Ollama5とHugo1のStandard editionをインストールしておく必要があります. hugo-llslug自体のインストール方法はこちらにまとめました. プラットフォームに合わせてインストールしてください. まとめ このツールによりhugoで記事を書き始める手間が減り,かつローカルLLMなのでネットがない場所でも楽しく記事を書き始めることができます. Hugo Top Page ↩︎ ↩︎ Slug(スラッグ) ↩︎ uuidgenコマンド ↩︎ hugo-llslug GitHub ↩︎ Ollama Top Page ↩︎

2026年6月15日

ラズパイサーバーからGitHub Pagesへ移行

概要 ラズベリーパイからサーバー運用を始め,その後はデスクトップPCでKubernetesを動かすところまで経験しました. この過程でネットワークの仕組みを学ぶことができました. こうした経験は,残しておかなければ忘れてしまうため,本稿にまとめます. サーバー運用の変遷 以下の画像は,直近でGitHubのプロフィールに掲載していたサーバーの運用記録です. 1. 初期:ラズベリーパイ 初期のサーバー構成は以下の通りです. このラズパイサーバーは,空冷ファンとSSDを増設したものです. 可用性を向上させるために増設したのですが,周辺機器を増やした影響で電源供給が不安定になってしまっていました. 突発的にSSDの読み取りに失敗することがあり,頻繁にサーバーが落ちるエラーを抱えていました. 2. 中期:デスクトップPC ラズパイの電源課題を解決するため,余っていたデータ分析用のゲーミングデスクトップPCをUbuntu Server化し,運用を始めることにしました. マシンスペックが向上し,懸念だった電源回りも大幅に改善しました.この時期はコンテナオーケストレーションの学習を兼ねて,MicroK8sを導入.GitHubでビルドしたWebサイトのコンテナイメージを自動でPullし,K8sランタイム上で動作させていました. 以前,環境構築の勉強のために一からKubernetesを構築した経験があったのですが,それに比べるとMicroK8sは簡単に導入でき,個人運用のコンテナランタイムとしては管理コストを抑えられる点が素晴らしいと感じました. 加えて,Prometheusも導入し,Grafanaを利用してサーバーの状態を可視化していました. MicroK8sでは,こうした周辺ツールの導入もアドオンとして用意されており,導入が手軽でした. Grafanaを導入したときのスクショです.きれいすぎてビビりました. しかし,スペックや運用環境は大幅に向上したものの,物理的な単一ノードであることには変わりなく,可用性の低さが依然として課題となりました. 3. 現在:GitHub Pagesへの移行 運用コストを抑えつつ,高い可用性を維持したいと考えた結果,最終的にWebサイトのホスティング先を GitHub Pages へ移行することにしました. これにより,安価かつ高い可用性を実現できました. 学んだ技術 今回のインフラ刷新と運用の変遷を経て,以下の技術への理解が深まりました. 静的サイト生成とCI/CD 現在のWebサイトは,静的サイト生成ツールである Hugoを使って構築しています.記事の内容はMarkdownで執筆し,HugoでHTML/CSSにビルドしたものをGitHub Pagesで公開する仕組みです. デプロイの自動化(CI/CD)を整えるにあたっては,Hugo公式が提供しているGitHub Actions用のテンプレートを利用しました1. MarkdownをGitHubにPushするだけで自動的にサイトが更新される環境が整い,運用の手間が大幅に削減されました. Cloudflare Zero Trustによる公開 勉強のために始めた自宅サーバーですが,外部公開のステップでも大きな学びがありました. 通常ならポート開放が必要なところですが,Cloudflare Tunnel2を利用することで,ポートを閉じたまま安全に外部公開する仕組みを構築できました. まとめ ラズパイの故障対応から始まり,MicroK8sでの本格的なコンテナ運用,そしてGitHub Pagesへの移行と,目的や課題に応じてインフラを変化させていくプロセスが面白かったです. 物理マシンを触りながらインフラ領域について学んだことを,今後の開発に活かしていきたいです. Host on GitHub Pages | Hugo ↩︎ Cloudflare Tunnel ↩︎

2026年6月10日

Leetcode 120をGoで解く

以下の記事で,leetcode精選120問が公開されていたので,解いていきます. アメリカでソフトウェアエンジニアの職を探した 進捗はこちらのリポジトリに記録しております. 2026年4月24日~6月10日 Go言語を使って,いったんEasyの問題は全て解きました.(meeting-roomsは課金しないと閲覧できなかったので省きました) Go言語はシンプルで解きやすいですが,優先度付きキューを実現するための二分ヒープのデータ構造を自前で定義しなければならず,競技プログラミングには使えそうにないなと感じました. 逆に途中Rustに浮気していたのですが,RustはCPUバウンドの処理が得意な言語らしくそういったデータ構造は事前に定義してあるものが使えて便利でした.(kth-largest-element-in-a-streamにて) ドリル形式でわからない問題は解法を頭に入れてからその後理解するようにしました. メンタル面で変化がありました. 解いているうちに,より美しいコードを書きたいと思うようになってきました. また,実務でコードを読むスピードが速くなり,生成AIに書かせるときもより適切な指示ができるようになったと思います. コードを見る目が養われてきて,効率的なコードを書けるときがありました. 例えば,ステータスを扱う時,プログラムで扱っている値をユーザが画面上で見る値に変換するコードが書きたい場面を考えてみます. この程度の例であればswitch文で書いてもいいかもしれませんが,Statusが多くなるとコードが冗長になるかと思います. // statusにはプログラム側で管理されている文字列("Active","Deleted"等)が入っている想定 func convertVisibleStatus(status string) string { switch status { case "Active": return "有効" case "Deleted": return "削除済み" case "Freeze": return "凍結済み" } } func main() { fmt.Println(convertVisibleStatus("Active")) // 出力 : 有効 } しかし,ハッシュマップを使えば,きれいに書くことが可能です. var statusMap := map[string]string{ "Active": "有効" "Deleted": "削除済み" "Freeze": "凍結済み" } func main() { fmt.Println(statusMap["Active"]) // 出力 : 有効 }

2026年6月10日

自宅サーバーにMicroK8s導入

概要 自宅サーバーのリプレイスに伴い,マシンスペックに余裕ができたため,コンテナオーケストレーション環境の構築を検討しました. 運用の手間を減らしつつ,実務で使うことの多いK8s環境を構築し,学びにつなげるため,軽量版パッケージであるMicroK8sを導入しました. 後学のため,導入方法とメトリクス可視化のための設定をまとめておきます. 本質的な難しい部分はMicroK8sが肩代わりしてくれているので,各ステップはとても簡単かなと思います. 前提 OS : Ubuntu Server 24.04 ネットワーク : 外部への公開およびルーティングにはCloudflare Tunnelを利用 MicroK8sはUbuntuの開発元であるCanonicalが開発しており,導入時の障害が少ないかと思います. MicroK8s MicroK8sは,ローカル環境で実行することを想定されたらしいです1. 複雑な設定なしに単一ノードで動くため,今回のケースでは導入が非常に簡単でした. 何はともあれ自宅サーバーは管理しやすい方がいいので,MicroK8sを選んでよかったなと思っています. アプローチ ここからは,MicroK8sを導入する手順を述べます2. 以下のコマンドを実行すると,MicroK8sがインストールできます. sudo snap install microk8s --classic –classicはsnapのサンドボックス機能を無効化するオプションです.詳細は後述する用語集を参照してください. 以上です. これだけでMicroK8sがインストールできてしまいます. DNSとstorageの有効化 次にDNSとstorageというアドオンを入れます. DNSはコンテナ間での名前解決のため,storageはDBを使うときに使います. もしstorageを利用しない場合,PostgreSQLなどのDBコンテナにおいてコンテナが再起動した瞬間にデータが全消去されます. また,データを保持するための設定を書いてもエラーになってアプリが起動しません. これらを入れていないと,サーバー用途としてはまともに使えません. K8sでは,K8s内部で動的にIPアドレスが変化します. そのため,DNSでコンテナの名前解決をしないと,外部公開する際にコンテナを指定できません. sudo microk8s enable dns storage これだけでセットアップ完了です. メトリクス収集ツール サーバーの稼働状況を可視化してくれるツールを導入します. これもMicroK8sであれば簡単に導入できます. sudo microk8s enable observability observabilityアドオンを有効化すると,内部では,kube-prom-stack-grafanaという名称のServiceが作成されるため,それを参照します. service.yaml apiVersion: v1 # api version kind: Service metadata: name: external-grafana namespace: default # cloudflaredのPodが動いているNamespaceと同じにしてください spec: type: ExternalName externalName: kube-prom-stack-grafana.observability.svc.cluster.local ports: - port: 80 # Grafanaが起動するポート番号 targetPort: 80 これをcloudflareのトンネル経由で公開すると,可視化されます. ...

2026年5月17日

坂本研究室Rust勉強会導入

概要 「ゼロから作るDeep Learning」をRustで実装する前に,Rustに慣れるため,leetcodeの問題を解きましょう. 本稿では最も基本的なデータ構造である,配列操作を扱います. 問題はコーディングテストで出題されやすい問題がまとめられている教育用サイトであるleetcodeから最も基本的なデータ構造である配列操作を行う問題のうち難易度Easyの問題を4問選びました. leetcodeの問題は全部で2000問以上あるので,面接で聞かれやすい問題を集めたGrind 75等を解いて対策するのがいいと思います. ルール 問題を解いているとき,AIには問題にそのものについて質問してはいけません. 例えば、問題文をAIに投げて解答コードを得るなど. AIに質問していいこと Rustの文法事項(データ構造や文法の解説等) 概念に関する質問 解答を理解するフェーズでは,Ask AI First(まずAIに聞け).の姿勢でお願いします.(何も見ずに,解答コードを書けるようになれば,いったん,その問題を解けたといってもいいと思います.) ここに記載してある解が最も優れているわけではありません. 問題 問題1 1. Two Sum これより下には解答があります.まずは,15分考えてみましょう. 解答・解説 クリックで展開 (問題に15分向き合っていない場合は展開しないでください) impl Solution { pub fn two_sum(nums: Vec<i32>, target: i32) -> Vec<i32> { // 1. ハッシュマップを用意する let mut map: std::collections::HashMap<i32, usize> = std::collections::HashMap::with_capacity(nums.len()); // capacityを読み込むとプログラムが安定する // 2. hashMapの中にあるかどうかを判定する for (i, &n) in nums.iter().enumerate() { // hashMapの中にあるかどうかを判定して、存在すればそれを返却する if let Some(&prev_index) = map.get(&n) { return vec![prev_index as i32, i as i32]; } // 現在の値の相方が必要な値として、今のインデックスを保存 map.insert(target - n, i); } // ここには到達しないけど返却 vec![] } } 問題2 121. Best Time to Buy and Sell Stock これより下には解答があります.まずは,15分考えてみましょう. ...

2026年5月11日

Rustにおける構造体のデータ格納場所分析

概要 このページで「String構造体もスタックに入りますが、ヒープに入るデータの参照アドレスが一つ入ります。」とあった。 分析することで確認する。 GDBを用いて分析する。 筆者はWindows11においてWSLを用いてUbuntu24.04を使用している。 2025年11月1日現在、筆者の環境ではGDBは以下のコマンドでインストールできる。 sudo apt install gdb 分析 本稿では、前述のページにあるサンプルコードに対して分析を行う。リンク切れをしたときに備えて、対象にするソースコードを示しておく。 以下の文章では、ソースコードといったら、以下のRustコードのことを指すものとする。 struct SeaCreature { animal_type: String, name: String, arms: i32, legs: i32, weapon: String, } fn main() { // SeaCreatureのデータはスタックに入ります。 let ferris = SeaCreature { // String構造体もスタックに入りますが、 // ヒープに入るデータの参照アドレスが一つ入ります。 animal_type: String::from("crab"), name: String::from("Ferris"), arms: 2, legs: 4, weapon: String::from("claw"), }; let sarah = SeaCreature { animal_type: String::from("octopus"), name: String::from("Sarah"), arms: 8, legs: 0, weapon: String::from("none"), }; println!( "{} is a {}. They have {} arms, {} legs, and a {} weapon", ferris.name, ferris.animal_type, ferris.arms, ferris.legs, ferris.weapon ); println!( "{} is a {}. They have {} arms, and {} legs. They have no weapon..", sarah.name, sarah.animal_type, sarah.arms, sarah.legs ); } 構造体のインスタンスとなる変数の場所 ソースコードにおける、main関数の中で定義されているferrisとsarahはSeaCreature構造体のインスタンスである。 これはメモリ上どこにあるのだろうか? ...

2026年5月2日