GPTの文章がきらい
Claudeはそんなでもないんだけど、ChatGPT (OpenAI GPT)の作る文章がきらい。
分かりやすい例があったので (記事の内容とは関係ないけれど)、引用してコメントしていく。
了解。まずはメモから「暗黙の前提・制約・判断基準」を仮説化しつつ、設計の分岐点だけを突く質問を最大3つ投げます。あなたの回答に合わせて次の3問を出し、十分になったらトレードオフ表にまとめます。
では最初の3問です。どれも「方式選定(同期/キュー/バッチ)」の分岐を決める前提を炙り出すための質問です。
取りこぼし最小化の定義を、重複との関係で決めたいです。
「1件も落とさない」優先なのか、「重複は絶対NG」なのか、どちらに寄せたいですか?(例:Pushは重複OKだがメールは重複NG、などチャネル別でも)通知は主にどっちですか?(設計が大きく変わります)
- 単一ユーザー起点のトランザクション通知(例:決済完了、コメント返信)
- 多数ユーザーへの配信/キャンペーン型(例:お知らせ一斉送信、セグメント配信)
※後者が混ざるなら「1イベント→大量fan-out」をどこでやるかが重要になります。ユーザー体験として許容できる 遅延の上限 と 再送の期限 はどれくらいですか?
例:「基本は数秒〜数分、最悪でも30分以内」「翌日まで届けばよい」「24時間過ぎたら捨てる」など。
(ここが決まると、同期/キュー/バッチの“勝ち筋”がほぼ決まります)まずはこの3つに答えてください。返答をもとに、次の3問で「段階導入の切り方」「監視を最小化するための観測点」「失敗時の安全策(永続化/再試行/冪等)」に踏み込みます。
AI に指示するのではなく、まずは AI に質問させた方が捗る
了解。
全体がですます調なので、了解です、ね。短い方がいいなら、はい、でいいんじゃない?
設計の分岐点だけを突く質問を最大3つ投げます
- だけ、最大: 不要な修飾しがち。
- 分岐点、突く、投げる: 自分イケてるって酔ってる人みたいだからいやだ。もっと普通の言い方あるでしょう。
たとえばこう書いてほしい: 大まかな設計方針を決めるために、3つ質問をします。
では最初の3問です。どれも「方式選定(同期/キュー/バッチ)」の分岐を決める前提を炙り出すための質問です。
- 炙り出す: 自分イケてるって(略)
- 通知は主にどっちですか?(設計が大きく変わります)
- どっち: どちら
- ユーザー体験として許容できる 遅延の上限 と 再送の期限 はどれくらいですか?
例:「基本は数秒〜数分、最悪でも30分以内」「翌日まで届けばよい」「24時間過ぎたら捨てる」など。
(ここが決まると、同期/キュー/バッチの“勝ち筋”がほぼ決まります)
- 最悪: 最大
- "勝ち筋": 自分イケてるって(略)
まずはこの3つに答えてください。返答をもとに、次の3問で「段階導入の切り方」「監視を最小化するための観測点」「失敗時の安全策(永続化/再試行/冪等)」に踏み込みます。
- 切り方、観測点、踏み込む: 自分イケてるって(略)
Windows 11 Setup
プライベートも仕事もMacなんだけど、ひょんなことでプライベート用にWindowsラップトップが手に入ったので、手になじむようにセットアップする。
Windows 11 Pro (24H2)
Windows Settings
Remove browser tab from Alt-Tab menu
System > Multitasking > Show tabs from apps when snapping or pressing Alt+Tab > Don't show tabs
Desktop separation
System > Multitasking > Desktops > task bar > On all desktops System > Multitasking > Desktops > Alt+Tab > On all desktops Desktopまたいで移動したいことがよくあるので。
Key repeat
Control Panel > Keyboard > Repeat delay > shortest
Control Panel > Keyboard > Repeat rate> fastest
Default input method
画面ロックから復帰するとUIの言語のIMに変わるのが不便。UIの言語は英語にしておきたいが、入力はCorvusSKKを使いたい。
Time & Language > Typing > Advanced keyboard settings > Override for default input method > CorvusSKK (デフォルトは Use language list)
Keyboard
Dvorak
最初AutoHotKeyでやろうとしたがちょいちょいリマップが効かないことがあった。以下の設定で立ち上げて jk キーを連打していると hththtjhtjtht みたいにたまにjが混ざる。連打とかでなくて普通に文章を入力するときにも起こる。
#Requires AutoHotkey v2.0 j::h k::t
解決できなかったので、おとなしく?レジストリで。英語はSettingsから United States-Dvorak を選ぶだけだが、日本語はレジストリから設定する必要がある。
Computer\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00000411 Layout File -> KBDDV.dll
ただし、WSLgはQWERTYになってしまう。US-Dvorakの方はdvorakのままなのになー。
そのほかキーリマップ
レジストリでCapsLockをControlに置き換える。
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Keyboard Layout Scancode Map -> hex:00,00,00,00,00,00,00,00,02,00,00,00,1d,00,3a,00,00,00,00,00
スペースはSandS (Space and Shift)に、Enterも同じようにControlとしても機能するようにする(Enter and Control?)。AutoHotKeyで設定する。
#Requires AutoHotkey v2.0 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Enter to Control while holding ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; https://www.autohotkey.com/boards/viewtopic.php?p=482892&sid=34ec48f6d1e7e9f8fce513d07c8737c6#p482892 *Enter:: { Send "{Blind}{RCtrl down}" KeyWait "Enter" ; waits for Enter to be released } *Enter Up:: { Send "{Blind}{RCtrl Up}" If (A_PriorKey = "Enter" AND A_TimeSincePriorHotkey < 300) { Send "{Blind}{Enter}" } } ;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; SandS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;; *Space:: { Send "{Blind}{Shift down}" KeyWait "Space" ; waits for Space to be released } *Space Up:: { Send "{Blind}{Shift Up}" If (A_PriorKey = "Space" AND A_TimeSincePriorHotkey < 300) { Send "{Blind}{Space}" } }
参考にしたフォーラムのコードだと修飾キーとして動作してほしいときにもスペースやEnterが入力されてしまったのだが、 A_PriorHotKey のところを A_PriorKey にして調整すると、期待する動作になった。タイマーは、修飾キーのつもりで押したあと何もせずにやめたいときのため。Space/Enterを押して少し待ってからはなすと何も入力されない。
Emacs風キーバインド。よく使う移動用のもののみ。TerminalとかEmacsなどは直接オリジナルの機能を使う。C-aとかC-fはWindowsアプリでもよく使うのでとりあえずC-M prefixで代替。ただよく間違えるので、AltキーをMacのCommand相当にしてしまってMacのふりをするのもありかもしれない。
;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Emacs ;;;;;;;;;;;;;;;;;;;;;;;;;;;;; excludedApps() { if WinActive("ahk_class RAIL_WINDOW ahk_exe msrdc.exe") return 1 if WinActive("ahk_class CASCADIA_HOSTING_WINDOW_CLASS ahk_exe WindowsTerminal.exe") return 1 if WinActive("ahk_class Emacs ahk_exe emacs.exe") return 1 return 0 } #HotIf !excludedApps() $^h::Send "{Backspace}" $^d::Send "{Delete}" $^a::Send "{Home}" $^e::Send "{End}" $^f::Send "{Right}" $^b::Send "{Left}" $^n::Send "{Down}" $^p::Send "{Up}" $^k::Send "+{End}^x" $^y::Send "^v" ;; Evacuate freq used Ctrl+X to Alt+Ctrl+X !^a::Send "^a" ;; Select all !^f::Send "^f" ;; Find !^n::Send "^n" ;; New #HotIf
SKK
CorvusSKKをインストールして、ACT の設定を入れる。CorvusSKKのGitHubリポジトリにACTのサンプルがある。選択、キー1、位置、仮名、あたりがACT(Dvorak)固有の設定になる。
Font
メイリオは長い文章が読みづらいので、Noto Sansに置き換える設定をWebブラウザに入れる。
Stylusプラグインで以下CSSをすべてのサイトに適用させる。
@font-face { font-family: Meiryo; src: local("Noto Sans JP"); } @font-face { font-family: メイリオ; src: local("Noto Sans JP"); }
CSSでメイリオを指定しているサイトも、指定していなくてデフォルトフォントに落ちる場合でも、Noto Sansで表示される。すばらしい。
WSL
開発環境の整備はこれから。
おまけ
前職場で使っていたWindowsはWin 7だったので、進歩を感じる。以前は以下のような設定、アプリを使っていた。
- MacType
- アンチエイリアス依存症なので。HiDPIディスプレイのおかげでフォントがきれいになって本当にうれしい。
- QTTabBar
- 仕事でたくさんフォルダを開く必要あったので。今回のマシンはプライベート用だけど、Explorerだけで仕事できるんじゃないかな。
- Cygwin/Mintty
SpacesとかExposeみたいなのもあるし、だいぶ使いやすくなっていていい感じ。Macじゃなくてもいいかもなーというふうに感じた。
mDNSでサブドメインに応答したい
mDNSにサブドメインというものはなく、 abc.def.local の場合 "abc.def" がホスト名に相当する。しかし便宜上、この記事ではサブドメインと呼ぶ。
2020年にサブドメインをmDNS avahi でホストしようとしたときに記事にした。このときは、使いたいサブドメインごとに avahi-publish を実行するsystemdサービスを追加した。
2025年になりあらためて探してみたところ、あいかわらず avahi は対応していない (https://github.com/avahi/avahi/issues/40)。まあ mDNS の想定された使い方ではないからとか、そんなところなのだろうか。issueの中身は追っていない。
avahi は変わらなかったが go-avahi-cname といういいかんじのプログラムが公開されていたので、それを使ってみる。ホスト名が serv.local だったとすると、 *.serv.local にCNAMEを応答してくれる。サブドメインごとの対応が不要なので大変べんり。
インストール
バイナリをgithubのリリースページから落としてくる。
curl -LO curl -LO https://github.com/grishy/go-avahi-cname/releases/download/v2.1.1/go-avahi-cname_v2.1.1_linux_amd64.tar.gz tar xf go-avahi-cname_v2.1.1_linux_amd64.tar.gz go-avahi-cname sudo mv go-avahi-cname /usr/local/bin
サービス化
適当にsystemdのサービスファイルを作る
# /etc/systemd/system/go-avahi-cname.service [Unit] Description=Go-avahi-cname Service After=avahi-daemon.service Requires=avahi-daemon.service [Service] ExecStart=/usr/local/bin/go-avahi-cname subdomain Restart=always User=daemon Group=daemon Environment=PATH=/usr/local/bin:/usr/bin:/bin WorkingDirectory=/usr/local/bin [Install] WantedBy=multi-user.target
enable &&start
sudo systemctl daemon-reload
sudo systemctl enable --now go-avahi-cname.service
systemctl status go-avahi-cname.service
● go-avahi-cname.service - Go-avahi-cname Service
Loaded: loaded (/etc/systemd/system/go-avahi-cname.service; enabled; preset: enabled)
Active: active (running) since Sat 2025-01-18 19:32:33 EST; 28min ago
Main PID: 336826 (go-avahi-cname)
Tasks: 8 (limit: 18797)
Memory: 2.8M (peak: 3.4M)
CPU: 167ms
CGroup: /system.slice/go-avahi-cname.service
└─336826 /usr/local/bin/go-avahi-cname subdomain
Jan 18 19:32:33 serv systemd[1]: Started go-avahi-cname.service - Go-avahi-cname Service.
Jan 18 19:32:33 serv go-avahi-cname[336826]: 19:32:33 INF creating publisher
Jan 18 19:32:33 serv go-avahi-cname[336826]: 19:32:33 INF getting FQDN from Avahi
Jan 18 19:32:33 serv go-avahi-cname[336826]: 19:32:33 INF running subdomain publisher fqdn=serv.local.
Jan 18 19:32:33 serv go-avahi-cname[336826]: 19:32:33 INF creating connection to multicast
Jan 18 19:32:33 serv go-avahi-cname[336826]: 19:32:33 INF start listening
確認
[20:07:31 nao@macmini] ~ % dns-sd -Q serv.local DATE: ---Sat 18 Jan 2025--- 20:08:04.057 ...STARTING... Timestamp A/R Flags IF Name Type Class Rdata 20:08:05.552 Add 2 12 serv.local. Addr IN 192.168.1.32 ^C [20:08:17 nao@macmini] ~ % dns-sd -Q cat.serv.local DATE: ---Sat 18 Jan 2025--- 20:08:19.776 ...STARTING... Timestamp A/R Flags IF Name Type Class Rdata 20:08:20.070 Add 3 12 cat.serv.local. CNAME IN serv.local. 20:08:20.070 Add 2 12 serv.local. Addr IN 192.168.1.32 ^C [20:08:25 nao@macmini] ~ % dns-sd -Q dog.serv.local DATE: ---Sat 18 Jan 2025--- 20:08:49.552 ...STARTING... Timestamp A/R Flags IF Name Type Class Rdata 20:08:49.999 Add 3 12 dog.serv.local. CNAME IN serv.local. 20:08:49.999 Add 2 12 serv.local. Addr IN 192.168.1.32 ^C
Emacs 29.1 の What's New ピックアップ
Emacs 29.1のWhat'sの紹介記事を見て、自分的に気になったものを並べる。
split-root-window-below と split-root-window-right
ウィンドウ分割は C-x 2 で今のウィンドウを上下に分割、 C-x 3 で左右に分割なんだけど、例えばすでに左右に分割された状態で、その2つのウィンドウの下にウィンドウを入れようとしたときのコマンドがなかった。この例の場合についていえば、右のウィンドウを一度消してからc-x 2(上下分割), c-x 3(左右分割) とかなんとかしたりして、面倒だなと思っていた。必要になるのはたまにだけど。
そういうときに、今回追加された split-root-window-below (C-x w 2) とか split-root-window-right (C-x w 3) が使える。便利。
Emoji
emoji-list とか emoji-search とかで絵文字を挿入できるようになった。たぶん使わないけど、ためしておーとなった。
tree-sitter
使ってみたい。あとで時間あるときにためす。
pixel-scroll-precision-mode
普通のアプリケーションのように?なめらかなスクロールができるようになる。ためしてみたけどちょっとスクロール重くなったり固まったりして、そもそもマウスでスクロールする習慣ないからデフォルトで有効にはしないかな。
キーバインド、キーマップ設定関数
Use 'keymap-set' instead of 'define-key'. Use 'keymap-global-set' instead of 'global-set-key'. Use 'keymap-local-set' instead of 'local-set-key'. Use 'keymap-global-unset' instead of 'global-unset-key'. Use 'keymap-local-unset' instead of 'local-unset-key'. Use 'keymap-substitute' instead of 'substitute-key-definition'. Use 'keymap-set-after' instead of 'define-key-after'. Use 'keymap-lookup' instead of 'lookup-key' and 'key-binding'. Use 'keymap-local-lookup' instead of 'local-key-binding'. Use 'keymap-global-lookup' instead of 'global-key-binding'.
define-key や global-set-key などに対応するあたらしい関数。今までの関数は使えなくなる訳ではないけれど、新しい方を推奨していくとのこと。参照記事にもあるけど、古い名前に馴染んでしまっているよなあ。まあleafとかuse-packageとか使っているとあまり気にする必要ないか。
Proced
proced-modeというのがあるのを今回知った。ためしてみてpsっぽい一覧が見えておーとなったけど、使わないかな..
Tramp
/docker:<コンテナ名> で動いているコンテナの中のファイルにアクセスできるようになった。コンテナ内のファイルをemacsでじっくり編集したくなることはあまりないけど、覚えておいてもいいかな。
Project
project-find-file や project-kill-buffers など。project-find-file は Emacs 25のころからあったらしい。projectile を入れて projectile-find-file とか projectile-kill-buffers をよく使っているけど、それの代わりになるのかな。ivyとかその他パッケージがprojectile利用していそうなのでprojectileは消せないかな。それなら今のままでいいかな?
scratch-buffer
scratch buffer がなければ作るコマンド。これ欲しかった。たまにscratch buffer消しちゃったときに、やっぱり手頃なバッファーが欲しくなって scratchバッファーの作りかたを何度か検索した(そしてない事に驚いた)。Lisp評価できる必要ないし、適当な名前でバッファー作ればいいんだけども。initial-major-mode でscratch bufferのモード指定できたのか。fundamentalとかにしておくかな。
package-upgrade-all
大抵 list-package -> U -> x -> y と流れでやっているのを、ショートカットできる。何がアップデートされるのか分からないのはちょっと微妙。
M-SPC (cycle-spacing)
知らなかった。たくさんのスペースを1つにしたいときは多々あるので、覚えよう。記事にある M-^ (delete-indentation) も知らなかったけど、これも覚えよう。
restart-emacs
あるなら使うかもしれない。パッケージアップデートしたときとかゴミみたいなバッファーたくさんあって掃除したいときとか、再起動はたまにする。
duplicate-line と duplicate-dwim
C-a C-SPC C-n M-w C-yが染みついているのでいまさらという感じもあるが、よく使うものでもあるのでどこかに割り当てようかな。kill-ringが汚れないのもよい。
Global font size change (C-M + スクロールもしくは C-x C-M-=, C-x C-M--)
フォントサイズを変えたいときは大抵バッファーごとではなくてすべてのバッファーで変えたいのでうれしい。
ctrl + スクロール もしくは C-x C-=, C-x C-- でバッファー単位のフォントサイズ変更をしたときに、行番号とかtab-barのタブとかmode-lineとかの大きさが変わらなくて気持ち悪かったんだけど、こっちのフォントサイズ変更だと全部変わるのもうれしい。
Emacs is now capable of editing files with very long lines
たまにながーい行のあるファイルを扱うときにEmacsがおっっっそくなって心を無にすることがあるので、改善はうれしい。
Ubuntu 20.04 を Wireguard サーバー(ゲートウェイ)にする
Ubuntu 20.04をWireguard サーバーにし、さらにGWとして使えるように設定する。IPv6は設定しない。
OSの設定をする
ルーティングしてもらうために /etc/sysctl.conf の以下の行のコメントを外す。
net.ipv4.ip_forward=1
Wireguardをインストールする
$ sudo apt update $ sudo apt install -y wiregaurd $ sudo reboot
Wireguardを設定する
Wireguardの設定は、wg-quickでOS起動/停止時に有効化/無効化する方針で。
鍵ペアを作る (/etc/wireguard/{privatekey,publickey})。
$ sudo sh -c 'cd /etc/wireguard; wg genkey | tee privatekey | wg pubkey > publickey' $ sudo chmod 600 /etc/wireguard/privatekey
wg-quick 用の設定ファイルを作成する。 xxx.conf の xxx がWireguardのインターフェース名として使われる。ここでは wg0 を使う。PrivateKeyには作成した privatekey の内容を転記する。IPアドレスやポート番号は適当に。PostUp/PreDownの第1引数 %i はWireguardのインターフェース名に置換される。第2引数は listen port、第3引数は wireguard用のセグメントを指定する。PeerにIPを手動で割り当てるのちょっとめんどい。
$ sudo vi /etc/wireguard/wg0.conf [Interface] SaveConfig = false Address = 10.0.1.1/24 ListenPort = 47474 PrivateKey = xxxxxx PostUp = /etc/wireguard/postup.sh %i 47474 10.0.1.0/24 PreDown = /etc/wireguard/predown.sh %i 47474 10.0.1.0/24 [Peer] # hogehoge PC PublicKey = abcabc AllowedIPs = 10.0.1.2/32 [Peer] # fugafuga PC PublicKey = xyzxyz AllowedIPs = 10.0.1.3/32
下はPeer側の設定例。PeerのPeerはサーバー。AllowedIPsを 0.0.0.0/0 としデフォルトゲートウェイをWireguardサーバーに向ける。ロケーションによってDNSレスポンスが変わる事もあるらしいので、Wireguardサーバーに近いDNSサーバーを使いたい場合は指定する。
[Interface] PrivateKey = zzzzzz Address = 10.0.1.2/32 DNS = xx.xx.xx.xx [Peer] PublicKey = abcabc AllowedIPs = 0.0.0.0/0 Endpoint = xx.xx.xx.xx:47474 $ sudo chmod 600 /etc/wireguard/wg0.conf
wireguardを有効/無効にするときにfirewallの設定を追加/削除したいので、 postup.sh/predown.sh に書いておく。ポートを開く他に、FORWARDチェインの設定(ufw route allow ...) とIPマスカレードの設定(iptables -t nat ...)が必須。
[/etc/wireguard/postup.sh] WG_IF=$1 PORT=$2 TUN_SEG=$3 GW_IF=$(ip -j route list default | python3 -c 'import sys,json; print(json.load(sys.stdin)[0]["dev"])') ufw allow in on $GW_IF to any port $PORT proto udp ufw allow in on $WG_IF ufw route allow in on $WG_IF -s $TUN_SEG out on $GW_IF iptables -t nat -I POSTROUTING -o $GW_IF -s $TUN_SEG -j MASQUERADE ufw status verbose
[/etc/wireguard/predown.sh] WG_IF=$1 PORT=$2 TUN_SEG=$3 GW_IF=$(ip -j route list default | python3 -c 'import sys,json; print(json.load(sys.stdin)[0]["dev"])') ufw delete allow in on $GW_IF to any port $PORT proto udp ufw delete allow in on $WG_IF ufw route delete allow in on $WG_IF -s $TUN_SEG out on $GW_IF iptables -t nat -D POSTROUTING -o $GW_IF -s $TUN_SEG -j MASQUERADE ufw status verbose
$ sudo chmod 755 /etc/wireguard/post{up,down}.sh
Wireguardを有効化する
$ sudo systemctl enable --now wg-quick@wg0.service
Wireguard、他の状態を確認する
$ systemctl status wg-quick@wg0.service $ journalctl -u wg-quick@wg0.service $ sudo wg show wg0
ついでに他の設定も確認する
$ sudo ufw status verbose $ sudo iptables -t nat -nvL $ sudo sysctl -a | grep ip_forward
補足
上では混ぜて書いているが、Wireguardの設定の他に、Linuxをルーター(GW)として動作させるためには、以下の設定を忘れないように。
kpartx -a xxx で追加したデバイスのソースイメージを探す
kpartx -a *.img でイメージファイルの各パーティションをアタッチした時に、デファイスファイルのどれがどれに対応しているか調べた。環境はUbuntu 20.04。
ためしに kpartx -a してみる。
### ディスクを作る $ dd if=/dev/zero of=/tmp/test.img bs=1 count=0 seek=1G ### 適当にパーティションを作る $ sgdisk -a1 -n1:34:2047 -t1:EF02 -n2:2048:+256M -t2:EF00 -n3:0:0:+100% -t3:8300 /tmp/test.img $ sudo sgdisk -p /tmp/test.img Disk /tmp/test.img: 2097152 sectors, 1024.0 MiB Sector size (logical): 512 bytes Disk identifier (GUID): BAC9F0D5-63A5-411B-9F48-E06CBB18E58B Partition table holds up to 128 entries Main partition table begins at sector 2 and ends at sector 33 First usable sector is 34, last usable sector is 2097118 Partitions will be aligned on 2-sector boundaries Total free space is 0 sectors (0 bytes) Number Start (sector) End (sector) Size Code Name 1 34 2047 1007.0 KiB EF02 2 2048 526335 256.0 MiB EF00 3 526336 2097118 767.0 MiB 8300 $ sudo kpartx -av /tmp/test.img add map loop0p1 (253:0): 0 2014 linear 7:0 34 add map loop0p2 (253:1): 0 524288 linear 7:0 2048 add map loop0p3 (253:2): 0 1570783 linear 7:0 526336 $ ll /dev/mapper/ total 0 drwxr-xr-x 2 root root 120 Jul 22 17:28 ./ drwxr-xr-x 16 root root 4200 Jul 22 17:28 ../ crw------- 1 root root 10, 236 Apr 23 14:53 control lrwxrwxrwx 1 root root 7 Jul 22 17:28 loop0p1 -> ../dm-0 lrwxrwxrwx 1 root root 7 Jul 22 17:28 loop0p2 -> ../dm-1 lrwxrwxrwx 1 root root 7 Jul 22 17:28 loop0p3 -> ../dm-2
名前からして /dev/mapper/loop0p{1..3} の親は /dev/loop0 であると推測できるが、一応確認してみる。
kpartx で接続された各パーティションは、device mapper によってマップされている。マッピングの様子は、たとえば dmsetup ls --tree で表示できる。
$ sudo dmsetup ls --tree loop0p3 (253:2) └─ (7:0) loop0p2 (253:1) └─ (7:0) loop0p1 (253:0) └─ (7:0)
253:x や 7:0 はデバイスナンバーといい、major:minor の組で表わされる。 dev 下にあるデバイスファイルを ls -l で表示すると、オーナー/グループと日付の間に major, minor の形で表示される。たとえば dmsetup ls --tree の結果の loop0p3 を見ると、loop0p3 (dm-2) 自体は 253:2 で、親は 7:0 であるという。これは下の ls -l の結果と照合できる。
$ ls -l /dev/loop* /dev/mapper/* /dev/dm-* brw-rw---- 1 root disk 253, 0 Jul 22 17:28 /dev/dm-0 brw-rw---- 1 root disk 253, 1 Jul 22 17:28 /dev/dm-1 brw-rw---- 1 root disk 253, 2 Jul 22 17:28 /dev/dm-2 crw-rw---- 1 root disk 10, 237 Jul 22 06:10 /dev/loop-control brw-rw---- 1 root disk 7, 0 Jul 22 17:28 /dev/loop0 brw-rw---- 1 root disk 7, 1 Jul 22 07:57 /dev/loop1 brw-rw---- 1 root disk 7, 10 Jul 20 15:34 /dev/loop10 brw-rw---- 1 root disk 7, 11 Jul 21 06:45 /dev/loop11 brw-rw---- 1 root disk 7, 2 Jul 22 07:57 /dev/loop2 brw-rw---- 1 root disk 7, 3 Jul 22 07:56 /dev/loop3 brw-rw---- 1 root disk 7, 4 May 17 16:33 /dev/loop4 brw-rw---- 1 root disk 7, 5 Jun 21 20:28 /dev/loop5 brw-rw---- 1 root disk 7, 6 May 6 09:28 /dev/loop6 brw-rw---- 1 root disk 7, 7 Jun 14 15:28 /dev/loop7 brw-rw---- 1 root disk 7, 8 Jun 29 23:38 /dev/loop8 brw-rw---- 1 root disk 7, 9 Jul 22 07:57 /dev/loop9 crw------- 1 root root 10, 236 Apr 23 14:53 /dev/mapper/control lrwxrwxrwx 1 root root 7 Jul 22 17:28 /dev/mapper/loop0p1 -> ../dm-0 lrwxrwxrwx 1 root root 7 Jul 22 17:28 /dev/mapper/loop0p2 -> ../dm-1 lrwxrwxrwx 1 root root 7 Jul 22 17:28 /dev/mapper/loop0p3 -> ../dm-2
ループデバイスにアタッチされているものは、losetup --list コマンドで確認できる。
$ losetup --list /dev/loop0 NAME SIZELIMIT OFFSET AUTOCLEAR RO BACK-FILE DIO LOG-SEC /dev/loop0 0 0 0 0 /tmp/test.img 0 512
まとめると、今回の例では、 /tmp/test.img は /dev/loop0 (7:0) にアタッチされており、その中のパーティションが /dev/mapper/loop0p{1..3} にアタッチされている事がわかる。
kpartx -a でアタッチしたパーティションは kpartx -d でデタッチできる
$ sudo kpartx -dv /tmp/test.img del devmap : loop0p3 del devmap : loop0p2 del devmap : loop0p1 loop deleted : /dev/loop0
万が一、kpartx -a を何度もしたりアタッチしたファイルが何だったか忘れたなどの場合には、dmsetup, losetup を使って状況を確認、個別に削除できる。
補足
デバイスのmajor番号は、/proc/device に一覧になっている。又、デバイス種ごとに番号は重複してもよいらしい。
$ egrep '^([A-C]| *[7] )' /proc/devices Character devices: 7 vcs Block devices: 7 loop
参考URL
- デバイスのmajor, minor numberについて https://www.ibm.com/docs/en/linux-on-systems?topic=hdaa-names-nodes-numbers
- Device mapperのわかりやすい解説 http://lc.linux.or.jp/lc2009/slide/T-02-slide.pdf
vmdkをdockerコンテナ内でマウントする
Ubuntu cloud imageの初期状態のディスク内容を見たかったので、vmdkを直接マウントすることにした。ホストにいろいろインストールするのは避けたいので、dockerコンテナ内でやることにした。guestmount コマンドを使う。ホストはUbuntu 18.04。
Ubuntu cloud-imageダウンロード
$ wget https://cloud-images.ubuntu.com/focal/current/focal-server-cloudimg-amd64.ova $ tar tvf focal-server-cloudimg-amd64.ova -rw-r--r-- root/root 10760 2021-05-03 17:59 ubuntu-focal-20.04-cloudimg.ovf -rw-r--r-- root/root 213 2021-05-03 17:59 ubuntu-focal-20.04-cloudimg.mf -rw-r--r-- root/root 539142656 2021-05-03 17:59 ubuntu-focal-20.04-cloudimg.vmdk $ tar xf focal-server-cloudimg-amd64.ova ubuntu-focal-20.04-cloudimg.vmdkコンテナ起動
fuseが必要になるので、/dev/fuseをマウントすると共に、必要な権限を追加する(cap-add, security-opt) (ref)$ docker run -it --rm -v $(pwd)/ubuntu-focal-20.04-cloudimg.vmdk:/tmp/ubuntu-focal-20.04-cloudimg.vmdk \ --device /dev/fuse --cap-add SYS_ADMIN --security-opt apparmor:unconfined \ ubuntu:20.04 bashパッケージインストール
ref# apt update && \ DEBIAN_FRONTEND=noninteractive apt install \ --no-install-recommends -y \ libguestfs-tools qemu-utils linux-image-genericマウント
guestmountは少し時間かかる。# guestmount -a /tmp/ubuntu-focal-20.04-cloudimg.vmdk -i --ro /mnt # ll /mnt total 88 drwxr-xr-x 19 root root 4096 May 3 21:45 ./ drwxr-xr-x 1 root root 4096 Jun 17 15:32 ../ lrwxrwxrwx 1 root root 7 May 3 21:39 bin -> usr/bin/ drwxr-xr-x 4 root root 4096 May 3 21:45 boot/ drwxr-xr-x 5 root root 4096 May 3 21:42 dev/ drwxr-xr-x 91 root root 4096 May 3 21:45 etc/ drwxr-xr-x 2 root root 4096 Apr 15 2020 home/ lrwxrwxrwx 1 root root 7 May 3 21:39 lib -> usr/lib/ lrwxrwxrwx 1 root root 9 May 3 21:39 lib32 -> usr/lib32/ lrwxrwxrwx 1 root root 9 May 3 21:39 lib64 -> usr/lib64/ lrwxrwxrwx 1 root root 10 May 3 21:39 libx32 -> usr/libx32/ drwx------ 2 root root 16384 May 3 21:45 lost+found/ drwxr-xr-x 2 root root 4096 May 3 21:39 media/ drwxr-xr-x 2 root root 4096 May 3 21:39 mnt/ drwxr-xr-x 2 root root 4096 May 3 21:39 opt/ drwxr-xr-x 2 root root 4096 Apr 15 2020 proc/ drwx------ 2 root root 4096 May 3 21:42 root/ drwxr-xr-x 3 root root 4096 May 3 21:42 run/ lrwxrwxrwx 1 root root 8 May 3 21:39 sbin -> usr/sbin/ drwxr-xr-x 6 root root 4096 May 3 21:42 snap/ drwxr-xr-x 2 root root 4096 May 3 21:39 srv/ drwxr-xr-x 2 root root 4096 Apr 15 2020 sys/ drwxrwxrwt 2 root root 4096 May 3 21:42 tmp/ drwxr-xr-x 15 root root 4096 May 3 21:41 usr/ drwxr-xr-x 13 root root 4096 May 3 21:42 var/
見れた!