日記9750
adw という名前でやらせていただいております
趣味
- AtCoder
ちょっとコーディングにハマった - 乗り鉄
乗り鉄ですがなにか
好きな言語
- rust
最近はrust一筋でやらせていただいております
いつも使ってるリポジトリ
https://mirror.hashy0917.net/
※自動変更スクリプトも書いてます。 よければ使ってください。 → change0917
リンクとか
RT専門です。 垢わけようか、迷ってます
ボイスロイド劇場が好きです。 一人称劇場大好き。
ASMR毎日お世話になってたら耳ガアアになったので、ほどほどに癒されてます。
githubアカウントです。
qiitaでも書いてます。- BlueSky
bluesky登録できた(今更)
notepad.mdについて
- url: https://adw39.org
- repo: tam1192/tam1192
mdbookを使用した暫定ブログです。 今後機能つけて正式ブログにするか、wordpress鯖立てるかは迷ってます。
github pagesを使ってるので、そんなに落ちないと思います。 自宅鯖に移行した暁には落とさないようベストを尽くします。
さりげなくご要望受け付けてます。 githubアカウントお持ちの方は(これ見てる人ほとんどたと思うが)、issueにお願いします。
My Parser Project
CommandAPIを用いて生成したプロセスの標準入出力を管理するプログラムを書いていて、 その実験用に登場した「DesperatelyRustyToolBox」が 独自のコマンドラインを持つプログラムであった。
このプログラムをもっと拡張できるよう、高度な構文解析が必要と考えて登場したのがこのプロジェクト。
他にも登場経緯はあるが割愛。
自分用標準構成
自分用とあるが、実際rustで開発するにあたって必要であるカスタム型、便利ツール、開発方法、モデルを定義したもの。
要素は以下の通り
- CI/CDを実現するためにGithubActionsを積極採用
cargo build
とcargo test
を自動化。さらに、cargo doc
も自動化。
mainにマージされた時はクリーンなコードを保てる。 - テスト駆動開発で開発を行う
せっかくCI/CDでテストの自動化を行ったんだから、書かないわけにはいかない。
公式にもある通り、テスト駆動開発で目標の見える化を実現。 - エラートレイトを実装したオリジナルのカスタムエラー型を用意
ちゃんとsourceまであるならsourceまで実現。
エラー発生箇所の特定を容易にするだけでなく、
From
の実装で?
シュガーシンタックスに対応、コードがより見やすく。 - 一般的なブランチモデルを採用
いわゆる
git flow
に基づいたブランチモデルを採用する。
また、名前規則やコメント規則等々も今後まとめていきたい。
最終目標
このプロジェクトを使い回していけるように頑張りたい。
参考とか
- Rust でパーサコンビネータを作ってみる/nojima様
パーサーコンビネータの基本のきがここに載ってます。とても参考になります。
当プロジェクトでは部分的に実装を変えたりしてるが、ほとんど参考にして作ってます。 - BNFや構文解析についてのメモ
四則演算パーサー実装時に参考にしました。
commit: first_commit
この記事はgitを用いながらrustでプログラムを書いてみよう っていう記事です。
warning
この記事は途中までです。
使ってる環境
- macOS Sequoia
- vscode
- git 2.39.5 (Apple Git-154)
- rustc 1.83.0 (90b35a623 2024-11-26)
- cargo 1.83.0 (5ffbef321 2024-10-29)
- github
詳しく説明しないこと
- unixのコマンド(もしくはpwsh,cmdのコマンド)
無駄に詳しく説明すること
- rustの知識(ある意味rust布教記事でもある)
commit: cargo initを実行
開発環境をセットアップして、最初のコミットをしよう。
localリポジトリをセットアップ
rustのプロジェクトを作ります。 とりあえずディレクトリを作成してそこに移動
cargo init .
を実行すると、いい感じにセットアップしてくれます。
.
├── Cargo.toml
└── src
└── main.rs
この時点ですでに、gitのローカルリポジトリがセットアップされています。
補足
cargoというのは、rust開発者を支援するツールです。 rust以外だと、cmake(c言語)、gradle(Java,Kotlin)、npm(nodejs)などがあります。
これらのツールは基本的に、外部ライブラリの整理(インストール)、ビルド支援(プログラム間の連携)をしてくれるわけです。
githubにアクセスできるようにする
github上にリポジトリを作りましょう。
git remote add
する方法もあり、学びとしてはそっちの方が深いのですが、
正直こっちの方が楽
https://cli.github.com
一度githubのアカウントを作成しておけば、CLI上でログインするだけでgithubの機能をコマンド上からアクセス可能です。
sshの鍵登録も不要!
インストール後、
gh auth login --web
コマンドでgithubにログインします。
githubにリポジトリを登録
一度ghコマンドを使えるようにすると、以降次の手順でgithubにリポジトリを作成可能です。
gh repo create
対話式で様々な質問が出てきます。
What would you like to do?
一番最初の質問で三択出てきますが
Push an existing local repository to GitHub
(存在しているローカルリポジトリを、githubにpushする) を選択します。
その後はenter連打でいけます。
注意
Visibility をprivateにすると、非公開になります。 公開するのが怖い場合は注意してください。
コミットする
ここまできたらコミットします。
コミットって何?
例えば、blenderでサイコロを作るとき...
上からステップごとに分かれていて、作業ステップが変わる度に
git commmit -m <ここにコミットメッセージ>
というコマンドを入力します。
こうすることで、ファイルをいじった時、ファイルの変更にメッセージをつけて記録しておくことができます。
コミット頻度はどうしよう
例えば、サイコロの形に変更が入った時...
git log
をたとって、あるコミットから分岐させることが可能になります。
(分岐の方法については後で記載します)
そう考えると、次の考え方ができます。
- コミット間に加えた変化が多いほど、戻しづらくなる
- しかし、コミットが多いほど、今度はログが見辛くなる
(例えば、サイコロの穴を開ける時、穴一つ一つ毎にコミットすれば、20コミット増えます)
しかも、git log
した時、得られる情報はメッセージのみです。
そう考えると、
- コミットメッセージは丁寧にした方が良い
となるわけです。
コミットの頻度についてはこれらの記事が参考になると思います。
...参考にしてるのかよく分からないですが、 今回は 「変更が一行で説明できる範囲内で1コミット」 を目標に 頑張ります。
ところで
もちろんblenderでもgitは使えます。(blenderにターミナルついてないから相性は良くないかもだけど) 何なら、パワポ、ワード、エクセルといった3種の仁義、 その他etc...
gitのメリットの一つ バイナリファイルが扱える です。
(この記事も下敷きはgit使ってます)
実際に行ってみる
実際にコミットします。
まずはプロジェクトの一番浅いところで次のコマンドを実行
git add .
意味は次の章で説明
git commit -m "cargo initを実行"
これでコミットができます。
first commit? 邪道です、ちゃんとやったことを書く
(こだわり強く生きる)
リモートにあげる
コミットしただけではリモート(github)に適用されません。
次のコマンドでリモートにアップロードできます。
まず、git branch
を実行。
このコマンドで表示された文字列がmaster
なら
git push --set-upstream origin master
このコマンドで表示された文字列がmain
なら
git push --set-upstream origin main
意味は後述
二回目以降はこちら 、理由は後述
git push
logを確認
> git log
commit 6c57a4c498b7a817265563bb8e1c8b31ee1a3a7d (HEAD -> master, origin/master)
Author: nikki9750 <76221172+tam1192@users.noreply.github.com>
Date: Sat Jan 11 17:42:06 2025 +0900
cargo init を実行
commitの後ろに書いてあるハッシュみたいな文字列はコミットハッシュです。 識別子。 さっきみたいにコミット戻す時に使います。
HEAD
は頭。 今触っているところ。master(main)
はブランチと言います。origin
はリモートの名前です。Author
コミットした人Date
コミットした日付
これで最初の作業は終了です。
ブランチ
図のように、ログを切り分けることができます。
左から右に、ブランチを作成しています。(ブランチを切るという)
右から左の矢印は、切ったブランチから変更を適用しています。
一度作成したブランチは消すまで存在し続けます。 別のブランチで加えた変更は、様々な方法で別のブランチで適用ができます。
ブランチのメリットは、触りながら理解できればいいかなと思います。
mainとmaster(変更は任意)
masterが差別用語に該当するとかで、mainという言い方に切り替わっています。(参考)
masterブランチは、これ以降mainに統一して説明します。
私の環境では、cargoが生成するgitリポジトリのデフォルトブランチが"master"なので、gitに触れるついでに変更しておきます。
今いるブランチをmasterにして、
git branch -m main
で変更。
-m
はブランチ名の変更を意味するそう。
そのままpushしようとすると
fatal: The upstream branch of your current branch does not match
the name of your current branch. To push to the upstream branch
on the remote, use
git push origin HEAD:master
To push to the branch of the same name on the remote, use
git push origin HEAD
To choose either option permanently, see push.default in 'git help config'.
To avoid automatically configuring an upstream branch when its name
won't match the local branch, see option 'simple' of branch.autoSetupMerge
in 'git help config'.
と出るので、その通りに
git push origin HEAD
しかし、リモート側に新規作成されるだけで、変更にはならないので...
githubのSettingsにアクセスし
Default branchを変更
その後、masterブランチを削除する
これで解決...しない ローカルの方でも変更が必要。
> git branch -a
* main
remotes/origin/main
remotes/origin/master
-a
オプションは、隠しブランチ的な存在を表示してくれる。
remote
というのはその名通りリモートのこと。
リモートとローカルは、その性質上ズレが生じる。それをうまいこと解決してくれるブランチなのだが、masterが居残る。
git fetch --prune
を実行すると、masterが消えてくれる
> git branch -a
* main
remotes/origin/main
remotes/origin/master
試してないから知らんが、git branch -d /remotes/origin/master
よりは安全と見た。
branch: ライブラリを試験
日記さんはコマンドラインプログラムを書くことに決めました。
計画を練ろう
計画的に開発する方が、幸せになります。 (ちなみにこの記事は無計画です。)
コマンド名はnanodesu
にします。
nanodesuは、nano
コマンドをめっちゃシンプルにしたテキストエディターです。
大まかな計画はこんな感じ
- 引数処理: 引数の処理をします
- ファイル入出力処理: ファイル読み込みの処理をします
- キー処理: コマンド実行中に受けた入力に応答します
- 画面処理: 入力に対応して画面が動くようになります
- デプロイ: 公開します。
- ドキュメント作成: 説明書を作ります。
使うライブラリを検討
一から書くと時間がかかります。 有志が作ってくれた優秀なライブラリをフル活用しましょう。
- clap: 引数の処理をしてくれます
- ratatui: 画面の処理とキー処理を担当してくれます
- fs: ファイルの処理をしてくれます
この先の計画を練る前に
ライブラリを使う場合、まずは触れてみたくなります。 触れるために新しいプロジェクトを作っても良いのですが、 せっかくgitを使ってるので 、gitで管理しようではありませんか。
ブランチを作る
この図のように、コミットログを切り替えることができます。
左から右に、ブランチを作成しています。(ブランチを切るという)
git checkout -b <ブランチ名>
でブランチを切ることができます。 ここで加えた変更は、mainには適用されません。
Readmeファイルを追加しよう
Githubでは、Readme.md
というマークダウンファイルがある場合
この部分に、その内容が表示されます。
このファイルをブランチを使って追加してみましょう。
git checkout -b Readme追加
ブランチが作成されると
git branch
を実行した時に
> git branch
* Readme追加
main
となるはずです。 *
が今作業しているブランチ。
内容は適当でいいので、Readme.mdを作成して保存。 コミットはまだしないでください。
ブランチを切り替える
git checkout main
で一度mainに戻ります。
ls
コマンドで中身を除くとこうなるはずです。
> ls
Cargo.toml Readme.md src
ブランチをせっかく分けたのに、変更がmainにも同時に適用されているように見えます。
この時点で、Readme.md
がまだgitの管理下にない状態であることを理解する必要があります。 コミットもしてないから当然っちゃ当然?
ステージング
git checkout Readme追加
で先ほどのブランチの戻ります。
git add
は、コミットするファイルをステージングするためのコマンドです。
git add
は、前回のコミットから変更が加えられたファイルのみをステージングします。
git add .
で、カレントディレクトリーより深く変更が加えられたファイル全てをステージングに追加します。
git addの恩恵はこの先で知ることができるはずです。
git add .
もしくは
git add Readme.md
でステージングします。
git checkout main
でもう一度mainに戻ります。
ls
コマンドで中身を除くとこうなるはずです。
> ls
Cargo.toml Readme.md src
ステージングした時点ではまだコミットされていません
コミットするブランチを決める
この状態でgit commit
をすると、mainブランチでコミットされます。
それだと困るので、
git checkout Readme追加
で先ほどのブランチの戻ります。
git commit -m Readme追加
ついでにremoteにあげましょう。
remoteとorigin
git remote -v
を実行すると
> git remote -v
origin https://github.com/tam1192/git_fun.git (fetch)
origin https://github.com/tam1192/git_fun.git (push)
となるはずです。 -v
は`verboseの略で、冗長とかいう意味があります。
originとなってますが、名前を変更できます。
git remote rename origin github
もう一度実行すると
git remote -v
github https://github.com/tam1192/git_fun.git (fetch)
github https://github.com/tam1192/git_fun.git (push)
名前が変わっています。 gitは複数のリモートリポジトリを同時に管理可能なため、remoteを自由に追加できます。 originとは初期設定でつくremoteサーバーの識別名です。
複数のremoteを使えるのは、gitが分散型だからとも言えるのではないでしょうか。 しらんけど
だけど、基本一つのremoteしか使いたくない(複雑になるから)
リモートにブランチを作る
gitの分散というのは何も、複数のリモートを使えるということだけではありません。 リモートリポジトリの内容がローカルリポジトリに丸まるクローンされているというのも大きいです。
だからこそ、今作ったブランチがリモートに存在するとは限らないのです。
git pushとは何者か
git push
を実行すると、ブランチごとにあらかじめ設定しておいたリモートのブランチに、変更を送信します
set-upstreamでリモート
実際にリモートにブランチを作るにはこう設定します
git push --set-upstream <リモートの識別名> <リモートでのブランチ名>
なお、基本的にローカルとリモートのブランチ名は同一です。
git push --set-upstream origin Readme追加
※origin
をgithub
に変えた場合は変更してください。
これで、リモートに変更が加えられます。
一度設定したら再び同じことをする必要はありません。
git push
だけで送信されます。
mainに変更を適用する
コミットが成功するとmainブランチで
> ls
Cargo.toml src
という結果が得られるはずです。
Readme.mdはReadme追加
ブランチで変更を保存されたため、main
には存在しない扱いに切り替わったのです。
gitがまだ一度もコミットしたことがない、未管理のファイルはコミットするまで全てのブランチで表示されます。
gitがファイルを操作していないからです。
一方で、コミットすると、コミット履歴が残っているブランチにのみファイルが存在するようになります。
gitがファイルを操作するようになるからです。
現時点でmainブランチにReadme.mdが作成された履歴がないから、表示されないわけです。
参考URLとか
感想もうっすら書いてます。割と適当なので注意されたし
作図・図とか
この際だからblender触ってみたかった。 最新版でもイケる
git
コミット頻度関係
頻繁にコミットするのが大切らしい
適度なコミットがよいらしい
便利だなぁ。是非ともブックマークしたい記事
ブランチ
-n
ではなく-m
なのか。 変更することないから知らんかった。
追跡ブランチってちょっと複雑だから使わないように気をつけて書いてた。
rust関係
Crate
Vue × WASM × Rust—試行錯誤とこれからの展望
最近、VueやNuxt、Nuxt UIを触れていて、「色々作れそうだな」と思うことが増えてきました。
一方で、JSの型システムがガバガバすぎて、Rustが恋しくなってきました。
そこで、WASMの存在を思い出し、Rustの強みを活かせる仕組みを急遽作ることにしました。
experiments-wasm-vue について
warning
本リポジトリは現在修復作業中です... なぜmainに手を加えたしとか、絶対に言っちゃダメですからね?
このリポジトリでは、RustをWASMとしてコンパイルし、Vueと連携する試みを行いました。
内容としてはシンプルで、BMPパーサーを作る際にお世話になったnomを活用し、四則演算パーサーを作ったものです。
当初、外部ライブラリを使うとWASMのコンパイルが難しいかと思っていましたが、入出力が絡まない処理なら問題なく通ることが分かりました。
LLVMが適切に変換してくれるらしく、「まじか…?」と驚きつつも、その柔軟さに感動しました。
リポジトリのリンク:
Vueの環境構築とWASMの連携
Vueの開発環境はViteを使ってセットアップしました。 そのため、通常のVue開発とは少し異なり、Viteの設定にも気を配る必要がある点がポイントになります。
また、今後はNuxtへの連携も視野に入れているのですが、ここで引っ掛かったのがWASMをNode.jsのモジュールではなく、Webサイトとして使える形式にビルドすることでした。
どーやら通常のHTML同様の紐付け方をすればOKらしい?ということが分かったので、今回はそうしました。
Nuxtのディレクトリ構造とWASMの管理方法
Nuxt4から、主要なコンポーネントのディレクトリが/appに集約されるようになるそう。 なので、同じプロジェクト内で/wasmディレクトリを作り、WASMのコードを管理するのが良さそうだと考えています。
まだ検証の余地はありますが、この方法ならVue/Nuxtのコンポーネントと適切に連携できそうなので、今後試していきたいと思います。
今後の展望—ランタイムの構想
WASMの可能性を探る中で、「Webブラウザ上で動く独自ランタイムを作ってみたい」とも考え始めました。
カーネルレベルの設計とまではいきませんが、例えば:
- ブラウザ内で動く軽量な仮想環境
- WASMの能力をフル活用できる処理オフロード
- Rustの型安全性を生かしたスクリプト実行システム
こういうの作ってみたいなって願望を持ってます。願望です。はい。
まとめ
wasmはいいぞ
参考など
- VueベースなWebツールのロジック部分をRust製Wasmで実装する: pirosuke様
ディレクトリの構造等を参考にしました。 - Rust から WebAssembly にコンパイル/mdn
いつも通りめっちゃわかりやすい
ceph-monってなに?
ceph-mon 私もこの記事を書いている時点ではあまりよくわかってないんですが、 わかる範囲で例えるなら「図書館司書」
ところで、私の好きなゲームの話なのですが、図書館に引きこもりなんですが、本が、とても大好きな子がいるんです。
コミュ障なところがほんと好き。
ちなみに、コスト削減してくれるので結構有能です。
cephの概要
Cephは、複数のサーバーが連携して動作するクラスタシステムで、ストレージを共有・管理できるシステムのこと。 つまり、各サーバーが持つストレージを、一つのストレージとしてみることができます。
オブジェクト
Cephはオブジェクトストレージ。 データ一つ一つがオブジェクトと呼ばれています。
レプリケーション
冗長性を保つため、各サーバーストレージに複製を保存する機能です。 kubernetesでも似た様な技術が見られます。
OSD
Cephでは、ストレージ単体のことをOSD(Object Storage Device)と言います。 OSDはサーバー自体ではなく、サーバー内のストレージデバイスを指すので注意。
Ceph Monitor(ceph-mon)
Ceph Monitor (以下mon) はcephシステムの入口的存在。 役割は以下の通り
- 監視機能:OSDの状態を常に監視
- オブジェクト管理:オブジェクトがどのOSDに記録されているかを記録
ここが消えてしまえば、ストレージにアクセスすることはできません。 そのためmonは冗長化されています。
Map
地図です。データの配置や構造を記録されてます。
- ObjectMap: オブジェクトの位置を記録するMap
- MonMap: 冗長化されているmon同士が、どの様に働いているかを記録するMap
全てのmonは、このMapを同期しあい、ユニークになる様にしています。
図書館で例えると
- オブジェクトは本。
- OSDは書棚。
- monは司書。
書棚には本が置かれています。 そして(優秀な)司書は、本がどこに置かれているのか、 はっきりわかるわけです。 また、司書は仕事として、本の質を確認して回っています。
mapの場合は
Mapは司書が持っているツールです。 ObjectMapは本がどの書棚にあるかを記録 MonMapは司書同志の連絡帳ですね。
Mapの同期が取れてないと、別の司書が本を移した時、 「そこにあると思われていた本がない」といったトラブルにつながるわけです。
MonMapとIP
monmapは図書館で例えている時、連絡帳と書きました。 この連絡帳にはIPが書かれています。
epoch 16
fsid 12345678-abcd-1234-zyxw-9876-abcdefghijklm
last_changed 2025-01-20T12:40:16.621338+0000
created 2024-10-27T05:57:33.339382+0000
min_mon_release 18 (reef)
election_strategy: 1
0: [v2:192.168.1.10:3300/0,v1:192.168.1.10:6789/0] mon.node1
1: [v2:192.168.1.20:3300/0,v1:192.168.1.20:6789/0] mon.node2
dumped monmap epoch 16
これがmonmap。 至ってシンプルです。
epoch <monmapのバージョン>
fsid <cephシステムに使われる固有の識別子, id>
last_changed <最終更新日>
created <作成日>
min_mon_release 18 (reef) <おそらくmonの最低要件バージョン>
election_strategy: 1 <よくわからん...>
0: <monのipと名前>
...
dumped monmap epoch 16
monmapは、内部でバージョン管理されていて、 やろうと思えばロールバックができる構造になってます。 (...やり方知りませんが...)
monmapの取得方法
たくさんあります
ceph
コマンドが生きてる場合
cephコマンドが使える場合は、
ceph mon dump
で取得可能です。
ceph
コマンドが生きてない場合
ceph-mon
コマンドを使います。
そもそもこのコマンドは、monそのものなので
systemctlで立ち上げる場合、内部でコマンドが用いられます。
ceph-mon -i <monの名前> --extract-monmap <出力したい場所, ex: ~/monmap>
次に、monmaptool
を使います。
monmaptool --print <出力した場所, ex: ~/monmap>
その他
ほかにも ceph-monstore-tool
を用いる方法もあります。
ceph-monstore-tool <mon storeの場所> get monmap -r
monstore
monstoreとは、monが持つ小さなデータベースです。 MonMapとObjectMapが保管されています。
場所
- microceph:
/var/snap/microceph/common/data/mon/<monの名前>
- ceph:
/var/lib/ceph/mon/<monの名前>
monの名前を忘れた時、ここをみるとわかるかもしれません。
monmapを手動書き換えする
ceph-mon
コマンドで取得したmonmapは、monmaptool
を用いることで
書き換えることが可能です。
monmap関係でトラブルが発生した時はこの方法を用いることで解決できると思います。
また、書き換えたmonmapを再びmonstoreに戻す時は
ceph-mon -i <monの名前> --inject-monmap <出力した場所, ex: ~/monmap>
とすることで対応可能です。 なお、injectしたmonmapのバージョンは、自動的に引き上げられます。
コマンドリファレンス
参考
で、結局お前何やらかしたの?
ceph mon set-addrs <name> <addrs>
こういうコマンドがあります。 このコマンドはアドレスを変えるコマンドです。 うちでmonが動いているサーバーには複数のipがあります。
二個目のipに対応させるため、いい方法を探してたらこのコマンドを見つけたわけです。
もともと3ノードあったのですが、3ノード目で1ノードと同じipを設定したら、 1,3ノードともに動作しなくなりました。
結局、monmaptoolで3ノード目を消して解決しました。
...二週間。 学生でよかったと思った瞬間でした。
検証するならtmp使えや
ファイル整理中にめっちゃ思ったこと。 tmp使って欲しい。
tmpってメモリに置かれるから(※OSによる)、早い、自動で消してくれる、そして、SSDの寿命を奪わない
まじで人に「ファイルはこうつくります!!!」って見せびらかす時は
tmp使ってくれ...
ふと思ったけど
一時的にgit cloneするならメモリ使うという手もありじゃね?
ご意見はこちら
https://github.com/tam1192/notepad.md/issues/10
このページの生成速度を上げた話
だいぶ早くなりました。
mdbookについて
いまこのページを作るのに使っているツール mdbook
はrust製のソフトウェアで、名前の通りマークダウン形式で本(資料)を書けるのが特徴的です。
加えて、GithubActionsを活用し、ページ生成、GithubPagesへの登録まで一括して行なっています。
そのため、私はこのページを保管しているリポジトリにマークダウンファイルを配置し、PRしつつmain
ブランチにマージすれば、ページが追加されます。
割と便利。
cargo installについて
rust製のソフトウェア、ライブラリは、crates.ioによって登録、管理されています。 cargoというツールを活用することで、プロジェクトにライブラリを追加したり、rust製のアプリケーションをPCにインストールできます。
cargoは、aptなどのパッケージマネージャーと異なり、インストール時にビルドをするため、インストール時間がながくなってしまいがちです。
GithubActionsは、簡単に言えば一時的にlinux環境を借りることができる機能なのですが、処理が終わればその環境はリセットされます。 また、最低限の機能しか内臓されてない(とはいえdockerとかcargoとかは標準である)ので、mdbookをcargoでいちいちインストールする必要があるのです。
生成速度を上げるために
つまり、cargo installを回避すれば、高速化できるということです。
そこで、dockerの力を借りることにしました。
-> tam1192/MyMdbookContainer
あらかじめ、cargo installを済ませたmdbook内臓のdockerイメージを作成しておき、公開しておくことにしたのです。
このコンテナイメージはmdbookと、このページに使うプラグインを追加して、イメージにしております。
イメージの公開先は、ghcr.ioを活用してます。 githubが所有するコンテナリポジトリです。 github Actionsもgithubが所有する機能の一つなので、その繋がりで高速化が期待できそうですね!
結論
ページ生成が速くなりました。
お掃除2506
お掃除がてら数年前の自分を振り返りたいなと思ったので描きました。
命名規則や公開範囲とかについて
副操縦士に聞いたところ、-
区切りで書くのが良いらしい。
面白いですね!遊びで試したプロジェクトやメモを整理するなら、カテゴリごとに名前を決めると管理しやすいかもしれません。例えば:
- sandbox-*:試作や遊びで作ったコード
- prototype-*:アイデアを形にしたもの
- experiments-*:技術検証用
- archive-*:過去のプロジェクトやメモの保存庫
- notes-*:設計やアイデアのメモ
例えば、Tailwindの新しいユーティリティを試したなら
sandbox-tailwind-experiments
、Nuxt×Ollamaのアイデアならprototype-ai-gui
みたいな感じ。あとは用途別に分けるのもいいですね:
fun-projects
small-tools
random-ideas
何か気に入るものはありますか?一緒にいい名前考えましょうか!
そもそも遊びのコードをそのまま放置するのは良くない、それこそtmp使えや!ですよ!
とはいえ、残したいと言う気分があるので、ネタに残せるレベルだったら残す方針で。
思い出話
整理しながら懐かしいrepo触ってるので、せっかくだし思い出話をしてしまおうと思います。
vox-ttsシリーズの開発と試行錯誤
vox-ttsは、VoicevoxのREST APIを活用したDiscord TTSボットで、私が最初に「書き切った!」と感じたプログラムのひとつ。Pythonで始め、のちにJSへ移行しました。この過程で得た知見を振り返ります。
開発のきっかけは、VoicevoxがREST API経由で音声合成できると知った瞬間の衝動。「これはやらねばならぬ」とコードを書き始めました。Python (discord.py
) を使いましたが、後にJS (discord.js
) へ移行。その理由は、ライブラリの不安定さよりも応答速度の改善を期待してた。しかし、最終的にはVoicevoxの生成時間がボトルネックだったため、この変更はそんなに意味がなかったです。
とはいえ、JSを選んだことで、イベント駆動型の設計が活きるようになりました。Discordのような「いつ発生するかわからない処理」に向いていて、非同期処理の管理がしやすい。特にPromise.allが便利で、APIコールの最適化に活用しました。例えば、複数のデータ取得を並列化することで、待機時間を短縮できます。
const [userData, posts, comments] = await Promise.all([
fetch('/api/user').then(res => res.json()),
fetch('/api/posts').then(res => res.json()),
fetch('/api/comments').then(res => res.json()),
]);
console.log(userData, posts, comments);
(Promise.allの一例)
マルチスレッドと比較すると、JSの非同期処理は考えることが少なくて済むのが強み。スレッド管理や共有変数の競合を気にする必要がなく、await
を必要な箇所だけに書けばいいのが直感的です。
vox-ttsの開発を通じて、PythonとJSそれぞれの特性や非同期処理を深く理解できました。イベント駆動型の設計は、リアルタイム処理を必要とするコードには最適です。
リポジトリへのリンク
- vox-tts.py
- vox-tts.js
- node-voxclient
Voicevoxを使う関数群を分離したやつです。 ...全然記憶にない
- node-voxclient
参考など
experiments-discord-bot.js & node-dirtools
vox-tts.jsを作る前に検証用として作ったbotです。
vox-ttsの項で大体の説明をしてしまったため、ここではリポジトリのリンクに留めます。
リポジトリへのリンク
experiments-blockview.rs: Rustとの出会いと試作コード
Rustに触れ始めたきっかけは、ゲームのmod修正でした。
致命的なバグが放置されていて、誰も修正しようとしていなかったので、「ならば自分で直してみよう」と思ったのが始まり。Rustの本を読みつつ、当時流行っていたAIも活用しながらコードを書き進めました。
最初は試行錯誤の連続で、書いたコードも拙いものでした。unsafeを使いまくる方法を取っていたので、PRを出した際に大量の指摘を受けました。しかし、それが結果的にRustの型システムや所有権の概念を深く理解する良い機会になったと思っています。
Rustで書いた試作コード
Rustを学ぶために書いたコードのひとつに、8色程度の色データを配列に入れ、それを標準出力に四角絵文字で表示するプログラムがあります。例えば:
🟥🟦🟨🟩
実装方法としては、色データをenumで管理し、出力時には対応する絵文字を使う形を採用しました。
一見シンプルですが、後から見返すとswapを使ってデータを入れ替えたり、二次元配列で管理すべきデータを一次元配列に縛ったりと、なかなか無茶な設計をしていたことに気付きました。
とはいえ、この試作を通じてRustの型管理や所有権の扱い、さらにはデータ構造の設計について多くの学びが得られました。
今後はbmpに保存できるようにしたい...(現在やってる)
リポジトリへのリンク
experiments-wasm-vue
名前変えました。 そして書いてたら長くなったのでページ分けました
-> Vue × WASM × Rust—試行錯誤とこれからの展望
item_json_maker: Minecraftリソースパック制作のためのツール
Minecraftのリソースパックを作る際に使用するツールとして、item_json_maker を作成しました。 出番はあまりないかもしれませんが、必要になったときに便利なツールになればと思います。
開発のスタイル
このツールは、HTML・CSS・JSのみで作られており、Vueやその他のフレームワークは使用していません。 Bootstrapを活用しており、classを指定するだけでスタイルを整えてくれるので、その点はとても楽でした。 ただし、Vueを知ってしまうと、プレーンなHTMLが整理されていないように見えてしまう問題があるのも事実。 ファイルを適切に分割することで解決できる部分もありますが、シンプルなツールなのでこの形式でまとめています。
余談
中学生の頃はHTMLとJSを活用して遊んでいました。 特にJSは、使用例を載せているサイトを参考にしながら、自分でサイトを拡張していくのが楽しかったです。 こういう技術を学ぶ過程って、ただ知識を増やすだけでなく、「試してみる」「遊んでみる」ことが大事だと思っています。 今でも新しい技術に触れるときは、ショールームを見ているような感覚でワクワクしますね。
今後
今のところ拡張の予定はありませんが、今後こういったツールを作るなら、VueやWASMも活用してみたいと考えています。 フレームワークを使うことで、より整理されたコードが書けるようになるし、WASMを組み合わせることでパフォーマンス面でも強化できそうですね。 新しい技術を活かす場面が出てきたら、またツールを作ってみようと思います!
リポジトリへのリンク
comAccess: Windowsでシリアルポート通信を可能にするツール
WindowsにはWindows Terminalという非常に使いやすいターミナルが備わっていますが、
シリアルポートに直接接続できないという制約があります。
その不便さを解消するために、Windows APIを駆使してcomポートアクセス用のプログラムを作成しました。
開発の経緯—シリアル通信の難しさ
開発を進める中で、シリアルポートの制御が意外とシビアであることを実感しました。
特にWindows環境では、通信の安定性やデバイスの排他制御など、考慮すべき点が多くあります。
最初はCで書きましたが、cisco機の一部は認識しなかったりとまだまだ改善の余地があります。
この過程で、Teratermの偉大さを改めて感じました。
長年シリアル通信の定番ソフトとして君臨している理由がよく分かりますね。
Rustでの挑戦と課題
実は、Rustを使ってcomAccessを作ろうと試みましたが、こちらは更に通信がうまくいかず、実装に苦戦しました。
Rustには環境に依存しないserialportクレートがあり、これを利用しようとしましたが、
実際に通信が来ない問題に直面しました。
さらに、Cの時同様に、Windowsの排他制御も面倒で、ポートの管理に苦戦。
結果として、Rustでの実装を諦めました。
Windows環境の変化—weztermとの併用
最近、使用する環境がMacに変わったことで、シリアル通信の選択肢が増えました。
Macではscreenコマンドを使えばシリアル通信が可能になります。
とはいえ、weztermというRust製ターミナルも並行して使用しており、
試行錯誤しながら快適な環境を模索しています。
weztermの強みとして、
- フォントの柔軟なカスタマイズ
- 画像表示機能
- 高度なタイル型ウィンドウ管理
などがあり、標準のターミナルとは違った使い勝手がありそうです。
今後、使い込んでいく中で特筆したいポイントが出てきたら、記事にまとめたいと思います。
リポジトリへのリンク
MCAutoShutdownPlugin
Minecraftのプラグイン開発は、比較的手軽に始められるものの、
実際に作ってみると様々な技術的な発見があります。
今回は、人がいないときにMinecraftサーバーを自動でシャットダウンするプラグイン、
MCAutoShutdownPlugin を開発した経験をもとに、学んだことを整理してみます。
開発のきっかけと目的
「いつかMinecraft関係のコードを書くぞ」と思い立ち、比較的シンプルな目的のプラグインを作るところから始めました。
Minecraftのプラグインは、基本的にサーバー環境の拡張を目的としており、
既存のAPIを活用しながら機能を追加できるため、比較的簡単に実装できます。
しかし、簡単だからこそ、満足感が薄いと感じる部分もありました。
クライアントで動くmodと異なり、制約も多いというのが実感です。
デザインパターンとの向き合い方
このプラグインを開発するにあたり、デザインパターンの理解が必要だと感じ、少しだけ理解してきました。 特にシングルトンパターンはよく使われ、Minecraftプラグインのコア部分を扱う際にも頻繁に登場します。
ただし、それ以外のデザインパターンはほぼ使いませんでした。
その理由は単純で、この規模のプラグインでは必要なかったからです。
適切な設計を考えることは重要ですが、無理にパターンを適用するのではなく、
「本当に必要かどうか」を見極めることも大切だと学びました。
今後のプラグイン開発について
この経験を通じて、Minecraftのプラグイン開発の流れを理解できたので、今後も新しいプラグインに挑戦する予定です。
次にプラグインを作る際には、もっと複雑な処理やデザインパターンを意識した設計を試してみたいと考えています。
リポジトリへのリンク
お掃除まとめ
色々書きましたが(...AIが書いてくれましたが)
懐かしいものから、続きを作りたいものもたくさん出てきたので、今後もっと深めていけたらと思いましたとさ。
ループバックアドレスでIP節約
(ダークモードだと見づらいのは気のせいです。)
ということで、ループバックアドレス使って極力IP減らしました。
note
この記事は、とある理由でグローバルIPなど特定のIP(以下グローバルIPという)の割り当てを減らしたい人向けに書いております。
自宅など、プライベートアドレスの節約については書いておりません。
プライベートIPとグローバルIP(/32)をうまく併用すれば、グローバルIPを節約しつつネットワークを構成できます。
本記事では、ループバックアドレスや/32, /31の使い方に加え、プライベートIPとの併用によるIP節約テクニックを紹介します。
結論1
/32
と/31
を使うと無駄なIPが減ります!
無駄なIPって?
ここでは、無駄なIPとは、次のアドレスのことを言う。
- ネットワークアドレス
- ブロードキャストアドレス
おいおいまてよ、二つとも本当に不要なのかい?
と思うかもしれません。 まぁぶっちゃけ不要じゃね?知らんけど。
とにかくこの二つは機材にアサインすることもできないし、
機材にアサインできないってことは、サーバーとして使えるアドレスじゃないし
warning
私はたまに適当なこと言います、気になったら調べてほしい
じゃあ消そう!とはいかない
ネットワークを構成するには、RFCというお約束により、 上記二つのアドレスは一部の例外を除いて必ず発生してしまうのです
無駄なIPが消える例外なネットワーク
そもそも例外なネットワークはまず、ネットワークと言っていいのかわかりません。
ネットワークって複数の機材を繋げあう技術でしょう?
例外1: /31 一対一のネットワーク
コンピューター間を一対一で繋げる場合は、/30と/31が用いられます。
このうち、/30はアドレス空間的に4つのアドレス(32-30=2[bit]=4通り)割り振れて、
/31はアドレス空間的に2つのアドレス(32-31=1[bit]=2通り)割り振れます。
しかし、無駄なアドレス二つを割り振ろうとすると、
/30で使える実際に使えるアドレスは2つになり、
/31に至っては0になります。
実はここで例外が発生します。/31のように、無駄なIPによって実際に割り当てられるアドレスが0になる場合、 無駄なアドレスは発生しなくなるのです。
すなわち、/31は二つのアドレスが使用可能となります。
...実は、/31は長年議論されていたようで、古いルーターですと設定できないみたいです。
例外2: /32 一つのアドレス
/32はもはやアドレス一つを指します。 ネットワークを構成できません。 /31の時のように、例外が発動します。
/32の用途はこのあと例に挙げますが、ネットワークを構成する必要がない、自分自身を示す際に使用できます。
例外まとめ
そもそも例外と言ってますけど、二つのアドレスの意味を考えると
- ネットワークアドレス
/32はそもそもネットワークともいえない
/31は設定する余裕がない - ブロードキャストアドレス
/31も/32もブロードキャストするメリットがない
ということで、必要がなくなるというわけです。
結論1まとめ
冒頭にもあるこの図のように、/31と/32を使えば、隅々にまでアドレスを割り当てることが可能となります。
結論2
こっちは簡単です。
冒頭のノートに述べた通り、基本はプライベートIPを使用しつつ、/32で割り当て可能なループバックにグローバルIPアドレスを使うことでIPを節約します。
その、ループバックアドレスで通信する方法ですが、ループバックアドレスを上流ルーターに経路通知すれば、通信が可能になるってことです。
warning
懸念点を挙げるとすれば、ループバックアドレスを設定した機材からは、ループバックアドレスを使って外に出るようにしなければならないことです。 図にも黄色蛍光ペンで強調させてますが、パケットのsrcがloopbackアドレスのものにならなければならないということです。
この設定が可能でないと、この手法はとれないことになります。
netplanの場合
netplanの場合はroutesの中でfromが使えます。 これを設定することにより、loアドレスからパケットが送出されるようになります。
network:
version: 2
ethernets:
lo:
addresses:
- 10.1.1.1/32
enp1s0:
dhcp4: false
dhcp6: false
accept-ra: false
addresses:
- 10.0.0.2/24
routes:
- to: default
via: 10.0.0.1
from: 10.1.1.1
nameservers:
addresses:
- 8.8.8.8
- 8.8.4.4
/31はあまり意味がない
L3スイッチなどのポートごとに1つ、さらに相手側(仮想サーバーなど)にも1つIPを割り当てる必要があり、結局2つ消費します。
一方、/32とプライベートIP(例えば 192.168.x.x など)を併用すれば、プライベートIPは枯渇しにくいので、
L3スイッチ側はプライベートIPを割り当て、相手側のループバックにグローバルな/32を割り当てれば済みます。
要するに、/32+プライベートIPでやらないとIP節約につながらないのです。 (一つのサーバーで複数のグローバルIPを使いたいなら別)
まとめ
ループバックアドレスや/32の活用、そしてプライベートIPとの併用によって、
無駄なグローバルIP消費を最小限に抑えることができます。IP枯渇対策の一案としてご参考まで!
...という今更の話題でした。
参考
- Netplan documentation(routing)
- github copilot
PRとかで意見もらえそうなので使ってみました。
こんな記事に使うな