Category Archives: FreeBSD

MastodonからFedibirdに移行する

Mastodon 3.1.2からFedibirdに乗り換えた。(on FreeBSD 13.2-RELEASE)
久しぶりに地獄を味わった気がする。

とりあえずメモを残しておく。きっと、もっとエレガントな方法があるはずだが、それでも記録は記録なのだ。

  • Rubyは2.xである必要あり。3.xでもナントナクは動くけど、時々 **dict の扱い違いが原因のエラーが出るので危険。
  • ruby27はpkgがあるけど、ruby-gemsはpkgがないのでソースコードから ./configure && make install が必要。
  • idn-ruby gemはinclude pathの都合があるので、 bundle install の前に gem install が必要。
  • db:migrateは気合と根性が必要。かもしれない。
  • tootctl cache clearしないとあるはずのデータ(列)が見えなかったりして大混乱する。

追記は末尾にて。

ruby 2.7

https://cache.ruby-lang.org/pub/ruby/2.7/ruby-2.7.7.tar.gz から拾ってきて、 ./configure && make install すればいいだけ。

idn-ruby

$ pkg install libidn2
$ gem install idn-ruby -v 0.1.2 -- --with-idn-dir=/usr/local

db:migrate

ここが一番しんどかったところ。rake db:migrate してもいろいろコケる。
ここはruby 3.xでやっていたのが悪かったかもしれないが、今となっては確かめる気にもならない。
あと、mastodon側も以前migrateが失敗して誤魔化したことがあるので、一部はソレかもしれない。

ざっくり以下の2手順。

  • db:migrateを気合で通す
  • 余分なテーブルと列を葬る

db:migrateを気合いで通す

PG::DuplicateColumn: ERROR:  column "list_id" of relation "keyword_subscribes" already exists
/home/mastodon/db/migrate/20191025190919_add_list_to_keyword_subscribes.rb:5:in `change'

PG::DuplicateColumn: ERROR:  column "list_id" of relation "account_subscribes" already exists
/home/mastodon/db/migrate/20191026110416_add_list_to_account_subscribes.rb:5:in `change'

PG::DuplicateColumn: ERROR:  column "list_id" of relation "follow_tags" already exists
/home/mastodon/db/migrate/20191026110502_add_list_to_follow_tags.rb:5:in `change'

このへんは既に存在する列の追加なので無視。scriptを空にしてスルーさせる。

NameError: uninitialized constant AddIndexURLToStatuses
Did you mean?  AddIndexUrlToStatuses

これは……なんだろうね?動けばよろしいの精神で直す。

db/migrate/20200312020757_add_index_url_to_statuses.rb:
- class AddIndexUrlToStatuses < ActiveRecord::Migration[5.2]
+ class AddIndexURLToStatuses < ActiveRecord::Migration[5.2]

db/migrate/20200827204602_add_inbox_url_to_conversations.rb:
- class AddInboxUrlToConversations < ActiveRecord::Migration[5.2]
+ class AddInboxURLToConversations < ActiveRecord::Migration[5.2]
No such column: custom_filters.whole_word
/home/mastodon/db/migrate/20230106051103_change_default_of_whole_wotd_in_custom_filter.rb:3:in `change'

これはウチの環境が悪いであろうもの。手動で直す。

(echo 'require("./db/migrate/20180707154237_add_whole_word_to_custom_filter.rb")'; echo "AddWholeWordToCustomFilter.new.change" ) | RAILS_ENV=production bundle exec rails runner -
== 20230221031206 AddWidthHeightToCustomEmoji: migrated (0.0017s) =============

rake aborted!
NameError: uninitialized constant CreateNodeInfo

そんなバカな?これも動けばいいので直す。

db/migrate/20230310214919_create_nodeinfo.rb

- class CreateNode < ActiveRecord::Migration[6.1]
+ class CreateNodeInfo < ActiveRecord::Migration[6.1]

記録してあるのはこのへんまで。これで rake db:migrate は成功するが、まだまだおかしい。

余分なテーブルと列を葬る

db/schema.rb の通りにすればいいんだけど、手動でこれをやるのは困難。特にindexとか。

まずは差分をチェックする。
今のデータベースからschema.rbを作って、diffで見ていく。

$ mv db/schema.rb db/schema.rb.fedibird
$ RAILS_ENV=production bundle exec rake db:schema:dump
$ mv db/schema.rb db/schema.rb.old
$ sdiff --width=240 db/schema.rb.old db/schema.rb.fedibird
$ cp db/schema.rb.fedibird db/schema.rb

fedibird側で追加されているものはdefaultに期待してスルー。
逆にfedibird側でないデータはrestore時にコケるので、1つずつDROP TABLEまたはDROP COLUMNしていく。

余計なものがなくなったら、SQL形式でdumpを作る。

$ pg_dump --data -U mastodon mastodon > /でかいよ/mastodon-db-data-only.sql

ここでDBを完全に消して(別途DROP DATABASE)、db:setupで作り直す。先の手順でdb/schema.rbを戻し忘れていると悲しいので注意。

$ RAILS_ENV=production bundle exec rake db:setup

初期用のデータが入るので消す。ただし、schema_migrationは必要なのでとっておく。

$ pg_dump --data --schema=schema_migrations -U mastodon mastodon > /これは大きくない/schema-migrations.sql
$ cat << EOM | psql -U mastodon mastodon
TRUNCATE accounts CASCADE;
TRUNCATE ar_internal_metadata CASCADE;
TRUNCATE oauth_applications CASCADE;
TRUNCATE schema_migrations;
EOM

まっさらチェック。

$ pg_dump -U mastodon --data --insert mastodon | grep INSERT

データを復元する。ここで、循環参照のあるforeign keyを復元させるため、一時的にmastodonユーザにsuperuser権限が必要。終わったら権限は戻しておく。

$ (echo "set session_replication_role = replica;"; cat /でかいね/mastodon-db-data-only.sql) | psql -U mastodon mastodon

schema_migrationsを上書きする。

$ echo "TRUNCATE schema_migrations;" | psql -U mastodon mastodon
$ psql -U mastodon mastodon < /これは大きくない/schema-migrations.sql

途中、何度か時間や手間のかかる処理があるので、適宜バックアップと取らないと辛いことになる。(なった)

追記分

db:migrate関連の失敗で、過去分のmedia_attachments.shortcodeがNULLになっていた。
これがあると、プロフィールのページが開けなかったりする。
対応するのは 20170105224407_add_shortcode_to_media_attachments.rb
SQLを直接実行して解決した。

UPDATE media_attachments SET shortcode = id WHERE shortcode IS NULL AND remote_url = '';

bhyveでPXE Bootしたい

簡単だった。

言うまでもないけど、ff:ff:ff:ff:ff:ff は好きなMACアドレスに書き換えてほしい。ほかのパラメータも、ね。

# pkg install bhyve-firmware
# bhyve -AHP -s 0:0,hostbridge -s 1:0,lpc -s 2:0,virtio-net,tap0,mac=ff:ff:ff:ff:ff:ff -c 1 -m 256M -l bootrom,/usr/local/share/uefi-firmware/BHYVE_UEFI.fd -l com1,stdio guest

参考はこちら。

ポイントは BHYVE_UEFI.fd は放っておけば IPv4 PXE Boot してくれるということ。
なので、PXE Bootしたいからオプションを探して……と悩む必要はない。起動可能なドライブを接続しなければいいのだ。

CP2112をFreeBSDで使う

ArduinoでI2Cをいじって遊んでいたところ、そもそも最初のトリガーをEthernetかなにかで送らねばならず。
結局Raspberry Piにするか、もしくはArduino Yunにするのかと思っていたところ、いいものが見つかった。

サンハヤト: USB・I2C(SMBus)変換モジュール MM-CP2112B

CP2112というチップが載ったちんまいボードで、USBからI2C 1本とGPIO 8本が使える素敵な子。
しかもFreeBSDはCP2112のドライバを標準で積んでいて、kldloadするだけ。

買おうと思ったら、なぜか押し入れから発掘されたので、早速試してみた。(いつ買ったんだろうね??)

Continue reading CP2112をFreeBSDで使う

ng_bridge+CARPは危ない(かもしれない)

注意: ng_bridgeを使った場合の話です。ngctlする人向けです。ifconfigでbridgeする人には関係ない(はず)の内容です。

CARPを設定したら60秒おきに切れたり繋ったりする。
対策としては、CARPをGRE/VXLANなどに通すか、ng_bridgeのループ判定を無効化する。

なんで「かも」かというと、これを書いた前日に散々発生したくせに、調査を始めたら全然起きなくなったから。
でも起きると60秒間通信不能になるので、危険ではあるはず。

なにが問題か

ng_bridgeは簡易ループ判定を持っている。
この判定はパケット受信時に「記憶しているMACアドレスとポートの対が異なる」現象がminStableAge(デフォルト1秒)より短い間隔で発生すると、ループありと判定するもの。
参考: ng_bridgeの判定部分

carp(ucarp)は、送信元MACアドレスを 00:00:5E:00:00:<vhid> にして送るため、ng_bridgeからすると同じMACアドレスからのパケットがいろんなポートから出入りすることになって、上記ループ判定に引っかかることがある。

解決策その1

ng_bridgeにバレなければいいので、encapsuleすればいい。GREとかVXLANとか。
GREだとpeer-to-peerしかできないので、3台以上でcarpしたい場合には向かないが、multicast(=ethernetからすればbroadcast)を減らせるというメリットがある。

解決策その2

encapsuleすると障害の原因が増えかねない。ループは自己責任で、というならこちら。
setconfigでloopTimeout=0にする。set後にgetconfigするとloopTimeoutがいなくなるが、効いている模様。

# ngctl msg br0: getconfig
Rec'd response "getconfig" (2) from "[5]:":                                                                                          │     performing TSO on the inner frames in hardware: cxgbe(4).
Args:   { debugLevel=1 loopTimeout=60 maxStaleness=900 minStableAge=1 }
# ngctl msg br0: setconfig '{ debugLevel=1 loopTimeout=0 maxStaleness=900 minStableAge=1 }'
# ngctl msg br0: getconfig
Rec'd response "getconfig" (2) from "[5]:":
Args:   { debugLevel=1 maxStaleness=900 minStableAge=1 }

ところで……

CARPはMASTERがadvertise中にBACKUPはadvertiseしないため、この事象はあまり発生しないように思える。
しかし、CARPが動作するような状況、つまり中間にある機器(switch, netif)の一時障害が発生すると、両者がMASTERになり、この一時障害の解消時に問題を引き起こす可能性がある。

ちなみに、CARPのパスワードを間違えると相互にadvertiseする状態になるので、すぐにこの現象を引き起こすことになる。

起動用zpoolでzpool removeしてはいけない

死ぬので。(@FreeBSD 13.0-RELEASE, 2021/07/13時点)

そもそも何故zpool removeしたのか

まずディスクを交換したかった。
大体の場合は、mirrorを作って古いディスクを外せばいい。(zpool attach + zpool remove)
ただ今回はboot領域にうっかりでっかいディスクを使ってしまっていて、SSDに交換しようとしたら容量が足りなかった。

調べたところ、最近のzpoolはzpool addしたあとremoveできて、これを使うと、poolのshrinkができる。
てきとーにやってみたら、簡単にできてしまった。(ぎりぎりに縮小することはできなくて、ものすごく余裕を持たせる必要はあったが)

感心&安心していた。数日後にrebootするまでは。

panicする

停電etc.でrebootすることもあるが、そのときに(今回みたいな)障害に遭いたくない。というわけで時間があるときにやる。
rebootしたら、こんなpanicが出てrebootループに入ってしまった。

余談1: その前にbootcodeの書き替え忘れもあった。
余談2: rebootが早すぎて読めなかったので、iPhoneで動画撮影して見る羽目になった。

Root mount waiting for: usbus0
uhub2: 4 ports with 4 removable, self powered
panic: VERIFY(nvlist_lookup_uint64(configs[i], ZPOOL_CONFIG_POOL_TXG, &txg) == 0) failed

困る

ZFS絡みなのが分かったので、すぐに別のマシンに繋いでテストしたりしたが、まったく問題が見つからない。
zpool importは正常だし、zpool scrubしても何も出ない。

解決

実は大分前に、ZIL(log)を取り外したときに同じ現象だったことを(奇跡的に)思い出した。
このときにどうしたかは思い出せなかったが、つまりpoolが悪い。
どうするかは簡単で、zfs sendしてzfs receiveすればいい。

boot用なので5GB程度しかなく、すぐに復旧できた。

余談3: cat ... | zfs receiveは遅い。dd if=... bs=262144 | zfs receiveがいい。

Raspberry PI 4 Model B + FreeBSD 13 BETA3 + ZFS

試してうまくいったのでメモとして残しておく。
後から手順にしているので、余計な手順や、抜けている手順があるかもしれない。(が、参考くらいにはなるはずと思っている)

なお、初期設定ほかではこちらを参考にさせてもらった。
本稿執筆時点(2021/02/23)では、シリアルコンソールなしで、USB+HDMIディスプレイというごく普通の装備だけで動かせている。

FreeBSD on RaspberryPi 4 Model B 発動篇

必要なもの

  • Raspberry PI 4 Model B+ (以下、RPI。なお、2Gモデルで実験している。)
  • FreeBSD-13.0-BETA3-arm64-aarch64-RPI.img
  • microSDカード (4GB以上)
  • USBメモリ (4GB以上、ZFSを認識させるために使用)
  • 適当な作業用FreeBSDマシン + USB-SDカードアダプタ

手順

  1. 適当な方法でイメージをSDカードに焼く。
    # dd if=/tmp/FreeBSD-13.0-BETA3-arm64-aarch64-RPI.img of=/dev/da0 bs=65536
  2. 念の為、この時点でRPIが起動することを確かめておく。
  3. SDカードを作業用PCに接続する。(以下、SDカードはda0になっているとする)
  4. 現在のUFS内データをバックアップする。
    # mkdir /mnt/raspi
    # mount -t ufs /dev/da0s2a /mnt/raspi
    # cd /mnt/raspi
    # tar -cf /tmp/raspi-rootfs.tar .
    # cd /
    # umount /mnt/raspi
  5. UFSを潰してZFSを作る。バックアップから中身を復元する。
    # gpart delete -i 2 da0
    # gpart add -t freebsd -a 1M da0
    # gpart create -s BSD da0s2
    # gpart add -t freebsd-zfs -s 4G da0s2
    # zpool create -t piroot -m none zroot /dev/da0s2a
    # zfs create piroot/ROOT
    # zfs create -o mountpoint=/ -u piroot/ROOT/default
    # zpool set bootfs=piroot/ROOT/default piroot
    # mount -t zfs piroot/ROOT/default /mnt/raspi
    # cd /mnt/raspi
    # tar -xf /tmp/raspi-rootfs.tar
  6. /boot/loader.conf(もちろん /mnt/raspi/boot/loader.conf のことだよ!) を書き変える。hw.usb.template, umodem_load, boot_multicons, boot_serialは無効化した。どれが犯人か分からないが、bcm_dmaでエラーが起きたので無効化して逃げている。
    #hw.usb.template=3
    #umodel_load="YES"
    #boot_multicons="YES"
    #boot_serial="YES"
    beastie_disable="YES"
    loader_color="NO"
    
    zfs_load="YES"
    vfs.root.mountfrom="zfs:zroot/ROOT/default"
  7. /etc/fstabからrootfsをマウントする記述を取り除く。/dev/ufs/rootfsの部分。忘れると酷い目に遭う。遭った。(おまけ参照)
  8. zpool exportする。
    # zpool export piroot
  9. 作業用PCでもう一仕事。USBメモリに起動用イメージを焼く。もっとも、必要なのはrootfs部分だけだが。 USBメモリを作業PCに挿して、SDカードと全く同じようにして書き込む。例えばこう。
    # dd if=/tmp/FreeBSD-13.0-BETA3-arm64-aarch64-RPI.img of=/dev/da1 bs=65536
  10. SDカードとUSBメモリをRPIに挿して、起動させる。kernelが読まれた後、プロンプトに入るためにEnter以外(SPACEとか)のキーを押す。vfs.root.mountfromを書き変えて、一時的にUSBメモリから起動させる。
    Hit [Enter] to boot immediately, or any other key for command prompt.
    Booting [/boot/kernel/kernel] in 10 seconds...
    
    Type '?' for a list of commands, 'help' for more detailed help.
    OK set vfs.root.mountfrom=ufs:/dev/da0s2a
    OK boot
  11. 通常起動する(はず)なので、起動したらzpoolのcacheを作ってコピーして、shutdownする。
    # kldload /boot/kernel/zfs.ko
    # zpool import -f -o cachefile=/tmp/zpool.cache -R /mnt/raspi zroot
    # cp /tmp/zpool.cache /mnt/raspi/boot/zfs/zpool.cache
    # shutdown -p now
  12. USBメモリは抜いて起動する。
  13. おつかれさまでした。

おまけ

ある手順を忘れると、起動中に

Warning: no time-of-day clock registered, system time will not be set accurately

が出て止まる。
このとき、loaderで boot -v すると、start_init: trying /sbin/init で止まる。
(正常起動時は lo0: link state changed to UPgenet0: link state changed to DOWN が出るはず。)

これがまともなエラーを吐かないので調査に手間取った。
途中で作ったUSBメモリを挿していると、これは発生しない。(/dev/ufs/rootfs があるから)
適当なUSBメモリでは代わりにならない。(/dev/ufs/rootfs がないから)
適当に同じようなパーティションを書いたUSBメモリでもダメ。(ラベルを付け忘れたので以下略)