88171.net


RAMが足りない

このサーバはさくらのVPSの1Gプランで稼動している。 1GB RAMのマシンでGentooを使うとどうなるかといえば、一部のパッケージをコンパイルするたびに怒涛のページングが発生する。

Gentoo界隈では「コンパイルジョブ並列数 × 2GB」のRAMが必要と言われている。

The --jobs N argument [...] Values for N should be no more than 2GB of ram per processor core.

EMERGE_DEFAULT_OPTS - Gentoo Wiki

Calculate 2 GB RAM for every compile job !

User:Pietinger/Tutorials/Optimize compile times - Gentoo Wiki

健全とはいえないのだけど、盛大にページングしても数時間程度で終わるなら待てばいい、と思ってこれまでやってきた。

しかし、状況が変わった。 Node.jsが22になったころからコンパイルに10時間以上かかるようになってしまった。 これはさすがにどうにかしたい。

# ちなみに、Rustはこのスペックだと最早コンパイルできず、コンパイル済みパッケージが提供されているのでそれのお世話になっている。


RAMを増やそうと思ったらVPSをスケールアップするしかない。

2GB RAMのプランにスケールアップすると料金がほぼ倍になる。 まあ許容できる額ではあるのだけど、このスペックが必要になるのは月あたり高々数時間程度というのがひっかかる。 平時に2GBなんて有効活用できないので、費用対効果で考えるとすごく悪い。

さらにいえば2GBは最低ラインであって、2GBあればなんでもできるわけでもない。 もう一つ上の4GBにすると料金がさらに倍になり、そこまではさすがにちょっと払えない。

あと、一度上げてしまったらもう二度とこっち側に戻って来られなくなりそう、という不安もある。


クラウドで必要なときだけマッチョなインスタンスを立ち上げてdistcc、というのも考えたのだけど、チャレンジする気にはならなかった。 調べてみるとコンパイラのバージョンなど制約が意外に多く、単発で気軽に使い捨てるようなことはできそうにない。 環境の維持・運用にかかる負担で精神的に赤字になりそう。

他のアプローチとして、指定した一部のパッケージだけバイナリ版を使う仕組みがGentooにあれば助かるのだけど、見通しは立っていない模様。

There is currently no way to specify that the binary version of a particular package should be preferred to the source version. The functionality is planned, but there is no specific timeline for implementation at this stage. Refer to bug 463964 and bug 924772.

Gentoo Binary Host Quickstart - Gentoo Wiki


目下、現実に発生している問題は「コンパイルに時間がかかる」ことだけで、突き詰めればそこに実害はない。 将来予見されるのは「ページングしすぎてリソース制限を賜る」というリスクなのだけど、それが実際に発生するか/発生した場合にどのくらいの悪影響があるかはわからない。

結局、今回は現状を維持することに決めた。

ひとまずリソース制限を賜ったらすぐ気づけるようAPIで監視するようにした。 監視の動向次第では「インフラに変な負荷かけるのも悪いから‥‥」という新たな動機が生まれる、かもしれない。

昔読んだエッセイか何かに「食べるものがないなら歯を磨くと空腹感が紛れる」という主旨の一節があったのを思い出す。


後日談。

この件の発端だったNode.jsは結局、Portageで管理することを諦めて公式のStandalone Binaryを使わせてもらうことにした。 そもそもnpmでコマンドラインツールをいくつか使っている程度だったので、PATHを通すだけで特にトラブルもなく移行できた。

これだけでもうすっかり平和になってしまったので、スケールアップはとうぶん俎上に載らない。


NASからDASへ

XigmaNASを動かしているHP MicroServer N54Lを使いはじめてから丸10年がたち、ハードウェアの寿命が気になりだした。 いざ壊れてからバタバタするのも嫌なのでリプレースを検討しはじめたのだけど、さっそく問題にぶち当たった。

とにかく、NASに適したリーズナブルなハードウェアがない。 N54Lは型落ちだったこともあり当時RACまでついて3万円未満だったが、今日日そんな値段では最低限妥協できるものすら手に入らない。 普通に見繕うと20万円コース。 嗚呼、物価高。

いっそのことNASアプライアンスという選択肢もなくはない。 なくはないのだけど、ロックインは極力避けたい。

そもそも、現状のクライアントがMac mini 1台だけであることを考えればNASである必要性が(完全にとは言わないけれど)ほとんどない。 なのでいろいろ割り切ってDASを作ることにした。


エンクロージャは、後述のとおりSATAポートマルチプライヤとして機能すればいいので正直なんでもいい。 どうせHDDを使うので接続方式やI/Oスピードにもさしてこだわりはない。 とにかく物理的なウィークポイントが少ない、頑丈な構造のものがいい。

という観点で選んで、OWCのMercury Elite Pro Dualにした。 巷にはもっと安い選択肢もあるのだけど、PCに比べたらぜんぜんお手頃なので納得できるものに。

実際に使ってみて大きな不満はないのだけど、強いてひとつ挙げるとすれば、Macのスリープとうまく連動してくれない。 どうもOWCに限らず外付けストレージにありがちな問題らしく、これといった解決法も見当たらないので、スリープを使わないことにした。 特に不便は感じていないのでよしとする。


HDDを2本積んでJBODモードにし、Time Machine用にAPFSと、雑多なアーカイブ用にZFS mirrorを載せる。

Time MachineバックアップをZFSに載せることも不可能ではなさそうなのだけど、構成が複雑になっていざリストアするときに困る気がすごくする。 あとHDD容量の個体差を吸収するバッファとしても都合がいいので、それぞれのHDDの一部をAPFSで使っている。 模式図でも描こうと思ったけれど面倒になったので、後述のコマンドラインから察してほしい。

ZFS実装にはOpenZFS on OS Xのお世話になる。 HomebrewにCaskがあるのでインストール自体は簡単なのだけど、カーネル拡張や各種ツールを動かすためにセキュリティ機構を調整する必要があって少し面倒。 行き詰まったらとにかくO3XWikiのFAQを見るのがオススメ。


構築に使ったコマンドラインを載せておく。 主にHDDを交換する将来の自分に向けたヒントなので、意味がわからないところもあるかもしれない。

パーティションの作成。 3TBのHDDを積んでいるので、それぞれ2TBをZFSに、残りをAPFSにする。 EFIパーティションは不要。 partitionDisk でいきなりAPFSを作ることはできないので、一旦JHFS+を作って変換する。 APFSはこのあと2つともTime Machineに設定しておく。

$ diskutil partitionDisk -noEFI disk${disk1_number} 2 \
    GPT %6A898CC3-1DD2-11B2-99A6-080020736631% %noformat% 2.0T \
    JHFS+ "Time Capsule ${disk1_serial}" 0
$ diskutil apfs convert disk${disk1_number}s2

ZFSストレージプールの作成。 当初XigmaNASの旧データセット(正確にはその前のFreeNASで作ったもの)をsend/recvしたらUnicode Normalizationの関係でまともに使えないことがわかったので、NFDにする。

$ sudo zpool create \
    -o ashift=12 \
    -O casesensitivity=insensitive \
    -O normalization=formD \
    ${pool_name} \
    mirror disk${disk1_number}s1 disk${disk2_number}s1

ZFSデータセットの作成。 今回Native Encryptionを使うので、あらかじめキーファイルを作成しておいてそれを読ませる。

$ openssl rand -hex 32 > ${keyfile_abspath}
$ sudo zfs create \
    -o encryption=on \
    -o keyformat=hex \
    -o keylocation=file://${keyfile_abspath} \
    ${pool_name}/${dataset_name}

あとは、旧データセットからデータを移行する。 Unicode Normalizationを変えた影響でsend/recvで移行できないので、普通にrsyncでファイルコピーした。


OpenZFSはオープンで柔軟性もあるし、半端なハードウェアRAIDなんかと比べたら信頼性も高いと思うのだけど、macOS向け実装の将来性には若干不安もある。 メンテナのJorgen Lundman氏がたしかRedditで「Appleが将来macOSのkextサポートを廃止する可能性がプロジェクトにとって最大のリスク」という主旨のコメントをしていた気がして(改めて探しなおしたけど見つけられなかった…)、エコシステム全体で見たときの不安はどうしても拭えない。 まあ現時点で他にベターな構成も思い付かないし、いざ使えなくなったらそのときに考えればいいことなので、当面お世話になります。


最後に白状すると、DASに移行したのは5月末のことで、この半年くらい草稿をこねくり回していた。 いろいろと書き足りていないことはあるのだけど、このままだと年を越えてしまいそうなので適当なところでケリをつけた。


自分が困る書き忘れに気付いたので追記。

Native Encryptionを使っていて zpool import のときにキーを自動でロードさせたい場合、-l オプションが必要になる。

-l

Indicates that this command will request encryption keys for all encrypted datasets it attempts to mount as it is bringing the pool online. Note that if any datasets have a keylocation of prompt this command will block waiting for the keys to be entered. Without this flag encrypted datasets will be left unavailable until the keys are loaded.

ZPOOL-IMPORT(8)

OpenZFS on OS Xでは /Library/LaunchDaemons/org.openzfsonosx.zpool-import.plistProgramArguments に追加する。


OpenSSH公開鍵の制限設定

OpenSSHサーバに公開鍵認証でログインしたユーザができることを制限したい場合、authorized_keys の公開鍵にオプションを書き連ねる。

no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty,command="..." ssh-rsa ...

これらのうち no-* は、restrict オプションひとつで置き換えることができる。

restrict,command="..." ssh-rsa ...

より正確には、restrict は以下の設定と等価になる。

さらに将来同様のオプションが新設された場合も restrict でカバーされることが期待できる。 必要なオプションがあれば一部を明示的に有効化することもできるので、restrict をベースに設定するのがいい。

詳しくはsshd(8)や、ソースコードの auth-options.c を参照。


自分の中では新発見だったのだけど、restrict オプション自体はにリリースされたOpenSSH 7.2(リリースノート)で新設されたもので、まったくもって目新しいものではない。

Gitのpull/pushにしか使わない公開鍵を持っていて、できることを制限しようと思い立った。 git-shell を使えばいいことはすぐにわかったのだけど、他にどんなオプションがあったかと調べていたらたまたま見つけた。