FreeBSDを9.3から11.0にバージョンアップする際の注意点など

いまさら感もありますが、サポート終了が迫っているFreeBSD 9.3から11.0へバージョンアップする際の注意点などをまとめてみました。

## BIND(DNSサーバ)の移行

FreeBSDではバージョン10以降BINDが標準提供されなくなったので、アップデートの途中で/var/named(通常/etc/namedbは/var/named/etc/namedbへのシンボリックリンクになっているはずです)以下を消そうとします。クリーンインストールの状態から変更されているファイルは残るようですが、named.rootなど標準のまま使うファイルも消されて復旧作業が面倒なので、DNSサーバを使っている場合は設定ファイルのバックアップを先に取っておいてから戻す方が楽です。

0. /var/namedのバックアップをとる

    ```text
# cd /var
# tar -zc --one-file-system -f /some/where/named.tgz named
```
0. OSをアップグレード
0. bindパッケージをインストール

    ```text
# pkg install dns/bind99
```
0. 設定ファイルを戻す

     ```text
# cd /var
# tar -jxpf /some/where/named.txz
```
0. /etc/rc.confに以下を追記

    ```test
named_conf="/etc/namedb/named.conf"
named_chrootdir="/var/named"
```
だいたいこういう流れで9.3からのBIND移行は完了です。

## srcを入れているとfreebsd-updateがこける不具合への対処

システムにソースコードをインストールしていると、freebsd-updateの最初のステップで

```text
# freebsd-update upgrade -r 11.0-RELEASE
  :
The following components of FreeBSD seem to be installed:
kernel/generic src/src world/base world/doc world/lib32

The following components of FreeBSD do not seem to be installed:
world/games

Does this look reasonable (y/n)? y
  :
The update metadata is correctly signed, but
failed an integrity check.
Cowardly refusing to proceed any further.
#
```
などとエラーが出ていきなり失敗します。原因は11.0-RELEASEのソースにファイル名にカンマを含む物が存在しているため。以下のパッチを当てる（P=の行にカンマ追加する）と回避できます。

```text:freebsd-update.patch
--- /usr/sbin/freebsd-update.orig	2016-12-13 17:22:02.000000000 +0900
+++ /usr/sbin/freebsd-update	2016-12-13 17:22:06.000000000 +0900
@@ -1223,7 +1223,7 @@
 	# Some aliases to save space later: ${P} is a character which can
 	# appear in a path; ${M} is the four numeric metadata fields; and
 	# ${H} is a sha256 hash.
-	P="[-+./:=%@_[~[:alnum:]]"
+	P="[-+,./:=%@_[~[:alnum:]]"
 	M="[0-9]+\|[0-9]+\|[0-9]+\|[0-9]+"
 	H="[0-9a-f]{64}"
```

## パスワードDBの更新

/etcのマージをしている時に/etc/master.passwdや/etc/passwdに修正が入ると/etc/pwd.dbとの間に不整合が生じるので、以下のコマンドを実行します。

```text
/usr/sbin/pwd_mkdb -d /etc /etc/master.passwd
/usr/sbin/pwd_mkdb -p -d /etc /etc/master.passwd
```
## VMwareではvmx3f0ドライバがまだない

VMwareのゲストとして使っていて、vmware-toolsからvmx3f0 NICを使っている場合、9.3用のカーネルモジュールは当然ながら動きません。11用のvmx3f0ドライバはまだ無いようなので、OS標準のvmx0ドライバを使う必要があります。

具体的には1回目のfreebsd-update installを実行して再起動する前に以下の手順で設定変更します。
まず、/boot/loader.confを編集してモジュールの設定を

```text
#vmxnet_load="YES"
#vmxnet3_load="YES"
```
のようにコメントアウトするか、行を削除します。

そして/etc/rc.confの

```text
ifconfig_vmx3f0="..."
```
という行を

```text
ifconfig_vmx0="..."
```
に修正します。

## USB HDDを外付けしているとfsckで止まることが

9.3では、USBの外付けHDDを繋いだマシンで/etc/fstabに

```text
/dev/da0s1d		/mnt/usb	ufs	rw		1	1
```
のように記述していても問題なく起動していたのですが、11.0-RELEASEになってUSB HDDの認識タイミングが遅くなり、

```text
Can't stat /dev/da0s1d: No such file or directory
Automatic file system check failed; help!
ERROR: ABORTING BOOT (sending SIGTERM to parent)!
``` 
といったエラーを出してブートに失敗するケースがありました。

回避するには、/etc/fstabの第6フィールドを

```text
/dev/da0s1d		/mnt/usb	ufs	rw		1	3
```
のように1より大きな値にします。これで

```text
Warning! Some of the devices might not be available; retrying
Waiting 30s for the root mount holders: usbus1 usbus0
```
といったメッセージを表示して起動を継続するようになります。

## freebsd-update install & rebootをスクリプトで自動化

翁： 4.xの頃は/etc/rc.earlyってスクリプトがあってじゃのう、fsck -y && make installworld とかぶいぶい言わせとったもんじゃ。。。

昔話はさておき、この先はアヤシイ世界ですのでお話程度に。

```text
# freebsd-update upgrade -r 11.0-RELEASE
```
が終わった後のOSアップデート手順は、

0. カーネルをインストールして再起動
0. 11.0-RELEASEのライブラリ等をインストール
0. パッケージの更新
0. 9.3-RELEASEのライブラリを削除
0. 再起動

といった感じになりますが、サーバの台数が多かったりするとちょっと大変です。

そこで、pkgを使ってアプリの管理が問題なくできている状態前提ですが、上記一連の作業をrcスクリプトを書いて自動化してしまいます。いちおう作業実績のあるスクリプトから環境固有な処理(同時にやったRuby2.1->2.3でなぜか残ったゴミ掃除とか）を除いたものですが、もしやるにしてもVMwareのスナップショット使って練習するなど十分確認してください。

まず、以下のスクリプトを/etc/rc.d/upgrade11という名前で保存します。

```text:/etc/rc.d/upgrade11
#!/bin/sh
# PROVIDE: upgrade11
# REQUIRE: NETWORKING ldconfig abi devfs
# BEFORE:  SERVERS syslogd

. /etc/rc.subr

name="upgrade11"
start_cmd="upgrade11_start"
stop_cmd=":"

PAGER="/bin/cat"; export PAGER
BATCH="yes"; export BATCH
ASSUME_ALWAYS_YES="yes"; export ASSUME_ALWAYS_YES

TEE="/usr/bin/tee -a"
LOG="/var/tmp/11_upgrade.log"
TEELOG="$TEE $LOG"

upgrade11_start()
{
   echo "########## install base update ##########" >> $LOG
   /usr/sbin/freebsd-update install | $TEELOG

   echo "" >> $LOG
   echo "########## upgrade base packages ##########" >> $LOG
   /usr/local/sbin/pkg-static install -f pkg | $TEELOG
   /usr/sbin/pkg upgrade -y pkg | $TEELOG
   /usr/sbin/pkg upgrade -y | $TEELOG
   /usr/sbin/pkg install -y misc/compat9x | $TEELOG
   /usr/sbin/pkg install -y dns/bind99 | $TEELOG

   echo "########## delete old library ##########" >> $LOG
   /usr/sbin/freebsd-update install | $TEELOG

   echo "########## restore old data/packages ##########" >> $LOG
   /usr/bin/tar -zxp -f /some/where/namedb.txz -C /var/ | $TEELOG
   if [ ! -e /etc/namedb ]; then
      /bin/ln -s /var/named/etc/namedb /etc/
   fi

   echo "" >> $LOG
   echo "########## pwdb ##########" >> $LOG
   /usr/sbin/pwd_mkdb -d /etc /etc/master.passwd
   /usr/sbin/pwd_mkdb -p -d /etc /etc/master.passwd

   echo "" >> $LOG
   echo "########## complete ##########" >> $LOG

   /bin/rm -f /etc/rc.d/upgrade11
   /bin/sleep 5
   /sbin/shutdown -r +1
   /bin/sleep 1
   /bin/sync
   /bin/sync
   /bin/sync

   /bin/sleep 180
   /bin/sync
   /bin/sync
   /bin/sync
   /sbin/reboot
   /bin/sleep 1
}

load_rc_config $name
run_rc_command "$1"
```
保存したら実行ビットを立てます。

```text
# chmod +x /etc/rc.d/upgrade11
```

rcorderコマンドを使ってスクリプトが実行されるタイミングを確認すると、だいたいこんな感じになるはずです。

```text
# rcorder /etc/rc.d/* | less
 :
/etc/rc.d/NETWORKING
/etc/rc.d/netwait
/etc/rc.d/mountcritremote
/etc/rc.d/devfs
/etc/rc.d/ipmon
/etc/rc.d/mdconfig2
/etc/rc.d/archdep
/etc/rc.d/abi
/etc/rc.d/ldconfig
/etc/rc.d/upgrade11
/etc/rc.d/newsyslog
/etc/rc.d/syslogd
 :
```

準備が整ったら、初回のカーネルインストールを実行します。

```text
# /usr/sbin/freebsd-update install
Installing updates...
Kernel updates have been installed.  Please reboot and run
"/usr/sbin/freebsd-update install" again to finish installing updates.
```

深呼吸して、再起動します。

```text
# shutdown -r now
```
あとは祈りながら見守りましょう。
再起動したら念のため/var/tmp/11_upgrade.logを見て、すべての処理が正常に完了していることを確認します。

このスクリプトは、OS再起動時にネットワーク接続まで終わったところで実行され、

- カーネル以外のライブラリ等をインストール
- パッケージを更新し、compat9xとBINDを追加インストール
- BINDの設定を戻す（ファイルのパスに注意！！）
- /etc/pwd.dbを更新
- 自分自身を消して再起動

という処理を行います。再起動のくだりがアヤシさ全開ですが、確実に自分を消しつつ、残りのrcスクリプトに進まないようにするため、おまじない多めになっています。

最後にちょっと怖い話を。
このスクリプトのせいなのか、はたまた手動でも同じ結果だったのか確認のしようがないのですが、再起動後zfsのマウントに失敗してシングルユーザモードで止まったサーバが数台あり、

```text
# zfs import -f [プール名]
```
を実行してさらに再起動が必要なケースがありました。やるにしてもトラブルシュートの体制を整え、監視しながらの作業がよいでしょう。