M1/M2 MacでRaspberry Piのパッケージを管理する(aarch64)

Apple Silicon な MacOS をRaspberry Pi 用の FreeBSD pkg ビルド環境としてセットアップするまでの記録です。公式リポジトリとは異なるバージョンや、パッケージをビルドする時のオプションを変更した独自のリポジトリを管理したい、というのが主な目的になります。

## Mac に FreeBSD をインストール

macOS用の仮想環境として[UTM](https://mac.getutm.app/)を使います。ダウンロードしてインストールするだけなので、特に詰まることもないかと思います。

次に、FreeBSD(13.1)のISOイメージを取得しておきます。https://download.freebsd.org/releases/arm64/aarch64/ISO-IMAGES/13.1/ にアクセスして、FreeBSD-13.1-RELEASE-arm64-aarch64-disc1.iso をダウンロードします。

UTMを起動して、FreeBSD用の仮想マシンを作成します。
1. "+"(Create a New Virtual Machine をクリック
1. Start画面で"Virtualize"を選択
1. Operating System画面で"Other"を選択
1. Other画面で[Browse]ボタンをクリックして13.1のISOイメージを指定
1. Hardware画面で仮想マシンに割り当てるメモリサイズとCPU数を設定。ビルド時間の短縮やzfsを利用することも考え、メモリは8GB（ホストマシン16GB）ぐらい割り当てました。ホストマシンがSSDならスワップアウト時の処理落ちも緩和されますが、そうでなければメモリを増やしましょう。CPUはデフォルトのまま。
1. Storage画面でディスクの割り当て量を設定。64GBもあれば十分でしょう。
1. Shared Directoryはそのまま
1. Summaryで仮想ホストの名前をつけて[Save]で仮想マシンの設定は完了です。

次に作成した仮想マシンを起動して、FreeBSDをインストールします。この手順は通常のインストールと同じですので、詳しくは省略しますが、要点としては、
* PoudriereはZFSを使うので、Partitioningで"Auto(ZFS)"を選択
* パッケージのビルド中にメモリとスワップを使い切ると失敗してしまうので、Swap Sizeをメモリの2倍（16GB）ぐらい確保する。gcc/llvmやtexliveなどの大物をマルチスレッドで構築すると、かなりメモリを使います。
* ネットワークはIPv4をDHCPで設定すると、ホストマシンとは異なるサブネットのIPアドレスが自動的に割り当てられます（共有ネットワーク/Shared Network)。Bridged Networkにしてホストマシンと同じサブネットに直接接続することも可能で、上流のDHCPからIPアドレスを割り当てたり、固定IPを設定することも可能です。

OSのインストールが完了し、再起動したら、いったんBootオプションの選択画面でブートを止め、仮想マシンの電源を落とします。その上で、**UTMの仮想マシンの設定画面から、CD/DVDでISOイメージのマウントを解除してから、再起動**します。

## Poudriereのインストールとセットアップ

rootアカウントでログインしてPoudriereをインストールします。
```shell
# pkg install poudriere
```

Poudriereの設定ファイル(/usr/local/etc/poudriere.conf)を編集します。丁寧なコメントがついているので、詳細はそちらを読んでもらうとして、最低限設定が必要な項目は"ZPOOL"と"FREEBSD_HOST"の2箇所になります。いくつかのパッケージは（標準の）シングルジョブではビルドに時間がかかってボトルネックになるので、ALLOW_MAKE_JOBS_PACKAGESにスペース区切りでパッケージ名を書いてくと、そのパッケージのビルドはマルチジョブで実行されるようになります。様子を見て1つだけ延々とビルドしているパッケージを見つけたら、ここに加えていくとよいでしょう。
```
ZPOOL=zroot
FREEBSD_HOST=https://download.FreeBSD.org

ALLOW_MAKE_JOBS_PACKAGES="pkg ccache py* node* llvm* gcc* ghc* rust"
```

### ports ツリーの初期化

ビルドするパッケージ用のports tree(default)を初期化します。この作業にはgitが必要になるので、インストールします。

```shell
# pkg install git
# poudriere ports -c -p default
````

### jailの作成

ビルド用のjailを作成します。名前は自由につけられますが、ここでは"13x_aarch64"としました。
```shell
# poudriere jail -c -j 13x_aarch64 -v 13.1-RELEASE
```

### make.confの作成

ビルドしたjail用のmake.confファイルを作成します。公式で提供されているものと異なるバージョンやオプションでビルドする場合、必要な設定をmake.confに記述します。jailの名前が"13x-aarch64"の場合、このjailに対応するmake.confファイルは、**/usr/local/etc/poudriere.d/13x_aarch64-make.conf**になります。
以下、MTAとしてSendmailの代わりにPostfixを使い、X ウィンドウは非使用、いくつかのソフトのバージョンを固定する例です（今は使われていないオプションが残っているかもしれません）。
```
NO_SENDMAIL=true
WITH_POSTFIX=YES
WANT_PHP_WEB=yes
WITHOUT_X11=yes
OPTIONS_UNSET+=SENDMAIL X11WANT_OPENLDAP_SASL=YES
WITHOUT_PHP_DEPENDS=YES
DEFAULT_VERSIONS+= apache=2.4 perl5=5.34 php=8.1 ruby=3.1 ssl=openssl bdb=5 python=3.8 python2=2.7 python3=3.8 node=16
APACHE_PORT=www/apache24
APACHE_VERSION=24
MAKE_JOBS_NUMBER=4
```

### ビルドするパッケージのリスト作成

作業用ディレクトリを作って、Raspberry Pi用にビルドするパッケージのリストをテキストに保存します。
```shell
# mkdir ~/work
# cd ~/work
# cat > 13x_aarch64-ports
databases/sqlite3
devel/apr1
devel/git
dns/bind916
dns/bind918
editors/vim
ftp/curl
mail/postfix
misc/lv
net/isc-dhcp44-server
net-mgmt/nagios
net-mgmt/nagios-plugins
net-mgmt/nsca
net-mgmt/nsca-client
ports-mgmt/poudriere
net/rsync
security/ca_root_nss
security/openssl
security/py-fail2ban
security/sudo
shells/fish
sysutils/cronolog
sysutils/screen
www/apache24
security/softether5
```

### ビルドオプションの設定

作成したパッケージのビルドオプションを設定していきます。portsの依存関係もチェックされ、それらのオプションも設定していきます。

```shell
# poudriere options -j 13x_aarch64 -f 13x_aarch64-ports
```

### パッケージのビルド

これで準備完了なので、パッケージをビルドします。

```shell
# poudriere bulk -j 13x_aarch64 -f 13x_aarch64-ports
```

ここで2度目のコーヒータイムです。

### リポジトリーサーバの起動

パッケージのビルドが完了すると、[poudriere.conf:BASEFS]/data/packages/[jail名]-default というディレクトリ以下にリポジトリ用のファイル一式が生成されます。これをWebで公開すればリポジトリーサーバになります。

手っ取り早い方法としては、pythonのワンライナーを使うとよいでしょう。
```shell
# pkg install python
（2022年12月時点ではphtyon3.9がインストールされます）
# cd /usr/local/poudriere/data/packages/13x_aarch64-default
# python3.9 -m http.server 8000
```

## Raspberry Pi で自前リポジトリを使うように設定する

リポジトリーサーバの準備ができたので、そちらのパッケージを使うようにRaspberry Piを設定します。

ちなみに、OSのインストールは、https://download.freebsd.org/releases/arm64/aarch64/ISO-IMAGES/13.1/ からFreeBSD-13.1-RELEASE-arm64-aarch64-RPI.img.xzをダウンロードしてきて解凍し、ddコマンドなどを使ってMicro SDカードにイメージを書き込みます。Macでの手順は以下のようになります。
```shell
(Webブラウザ等でダウンロード)
$ cd ~/Downloads
$ unxz FreeBSD-13.1-RELEASE-arm64-aarch64-RPI.img.xz
(SDカードを挿入)
$ diskutil list
(SDカードのdisk番号を確認、以下では仮に'##'とします)
$ diskutil unmountDisk /dev/disk##
$ sudo dd if=FreeBSD-13.1-RELEASE-arm64-aarch64-RPI.img of=/dev/disk##
$ diskutil eject /dev/disk##
```

### リポジトリーサーバへのルーティング設定

リポジトリーサーバが共有ネットワークモードで動作している場合、Raspberry Piから直接アクセスができないので、ルーティングの設定を行います。

|                     | IPアドレス     |
|:--------------------|:--------------|
| ホストマシン（macOS): | 192.168.0.100 |
| リポジトリサーバ:     | 192.168.64.3 |

という割り当てになっている場合、/etc/rc.confに以下のようにスタティックルーティングの設定を追記します
```
static_routes="vmnet64"
route_vmnet64="-net 192.168.64.0/24 192.168.0.100"
```
Rasplerry Piを再起動するか、以下のコマンドでスタティックルーティングの設定を反映します。
```shell
# /etc/rc.d/routing static
```

### リポジトリの設定

```shell
# cat > /usr/local/etc/pkg/repos/Local.conf 
local: {
url: "http://192.168.64.3:8000/",
mirror_type: "http",
enabled: yes
}
```

公式のリポジトリーと併用しても良いのですが、パッケージの更新タイミングによっては公式リポジトリーのもので上書きされる場合があります。それを回避して、公式リポジトリーは使わなくてもよい場合は、以下の設定も行います。
```shell
# cat > /usr/local/etc/pkg/repos/FreeBSD.conf
FreeBSD: { enabled: no }
```

## 運用フェーズ

ここまで構築ができたら、あとは
* ports ツリーの更新
```shell
# poudriere ports -u -p default
```
* パッケージのビルド
```shell
# cd ~/work
# poudriere bulk -j 13x_aarch64 -f 13x_aarch64-ports
```
* Webサーバの起動（運用フェーズになったらいちいち起動せずにhttpdサーバをインストールしてセットアップするのが良いでしょう）
```shell
# cd /usr/local/poudriere/data/packages/13x_aarch64-default
# python3.9 -m http.server 8000
```

してから、Raspberry Piで、
```shell
# pkg upgrade
```

というルーチンを繰り返すだけになります。