docker+ansibleで自動設定できるようにしました。https://github.com/ruly-rudel/env
やりたいこと
- USB OTGでハブ経由のUSBのイーサネットとwifiデバイス接続、NAPT
- USB Audio Classデバイス(Babyface)を接続し、shairport-syncでAirPlayサーバー
- 可能な限りbit exactと思われる設定で
ポイント
- BabyfaceはS32_LEフォーマットのみ出力対応のため、ALSAのhw:Babyface...に出力するときはshairport-sync側で出力フォーマットを設定する必要がある
- plughw:Babyface...を利用すればpcmプラグインがよしなに変換してくれるのでこの設定は必要ない
- が、plughw:Babyface...はサンプリングレートも変換できてしまう。サンプリングレートはソフトでは変換したくなかった&ALSAの仕様をイマイチ理解していないので、念のため変換を一切挟まないと思われるhw:Babyface...を利用した
- SDカードへの書き込み回数を減らすため、スワップは停止し/tmp, /var/tmp, /var/logをtmpfsに
- link localアドレス使ってなんとかDHCPつかわんでもavahiだけでいけるかなーと考えたけど、defaultrouterとdns serverをavahiだけで配布するのが大変そうなのであきらめてdnsmasq入れた
- DNSサーバー、nftablesで名前解決リクエストをDNATするだけで動くんだけど、DNSサーバーがNAT先の回線が切れたり繋がったするたびに変わる可能性があるので/etc/resolv.confみてくれるdnsmasqのDNSサーバーを使うことにした
- nft add chain ip nat PREROUTING iifname eth0 udp dport 53 dnat (DNSサーバーのipアドレス)
- eth0に来たUDP53番ポートに来たリクエストを全部DNSサーバーにDNATする設定
- DNSサーバー、nftablesで名前解決リクエストをDNATするだけで動くんだけど、DNSサーバーがNAT先の回線が切れたり繋がったするたびに変わる可能性があるので/etc/resolv.confみてくれるdnsmasqのDNSサーバーを使うことにした
- dnsmasq.confに元から書いてあるおしり二行の謎設定はどうもDHCP配る先がraspberry piの場合のworkaroudみたい。なのでらずぱいに配布しなければ削除しても多分大丈夫
- --dhcp-reply-delay=[tag:,]
Delays sending DHCPOFFER and PROXYDHCP replies for at least the specified number of seconds. This can be used as workaround for bugs in PXE boot firmware that does not function properly when receiving an instant reply. This option takes into account the time already spent waiting (e.g. performing ping check) if any.
- --dhcp-reply-delay=[tag:,]
手順
SD Card作成から起動まで
- Raspberry Pi OS LiteをMicro SDXCに焼いて¥sshをtouch、¥wpa_supplicant.confを下記のように設定
- SDカードをラズパイに挿入してネットワーク上にDHCP serverがある状態で起動
- ssh pi@raspberrypi.localし、初期パスワード「raspberry」でログイン
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1
country=JP
network={
SSID="(SSIDを記載)"
psk="(keyを記載)"
}
network={
...
}
OS upgradeとパッケージ導入
% sudo apt-get update
% sudo apt-get full-upgrade
% sudo apt-get install shairport-sync
% sudo apt-get install nftables
% sudo apt-get install dnsmasq
% sudo raspi-config
(ホスト名変更、パスワード変更)
swap停止
% sudo swapoff --all
% sudo systemctl stop dphys-swapfile
% sudo systemctl disable dphys-swapfile
tmpfs configurations
(ファイル終端に以下を追記)
tmpfs /tmp tmpfs defaults,size=32m,noatime,mode=1777 0 0
tmpfs /var/tmp tmpfs defaults,size=16m,noatime,mode=1777 0 0
tmpfs /var/log tmpfs defaults,size=32m,noatime,mode=0755 0 0
# !/bin/sh
LOGFILES="/var/log/lastlog /var/log/wtmp"
touch $LOGFILES
chmod 664 $LOGFILES
chown root:utmp $LOGFILES
mkdir -p /var/log/fsck
exit 0
[Unit]
Description=tmpfsLogs
RequiresMountsFor=/var/log
[Service]
Type=simple
User=root
WorkingDirectory=/var
ExecStart=/etc/init.d/tmpfs-logs.sh
[Install]
WantedBy=multi-user.target
% sudo rm -rf /tmp
% sudo mkdir -m 1777 /tmp
% sudo chown root:root /tmp
% sudo mount /tmp
% sudo rm -rf /var/tmp
% sudo mkdir -m 1777 /var/tmp
% sudo chown root:root /var/tmp
% sudo mount /var/tmp
% sudo rm -rf /var/log
% sudo mkdir -m 755 /var/log
% sudo chown root:root /var/log
% sudo mount /var/log
% sudo systemctl --system daemon-reload
% sudo systemctl enable tmpfs-logs.service
% sudo systemctl start tmpfs-logs.service
Network configuration (dhcpcd5)
...
interface eth0
static ip_address=xxx.xxx.xxx.xxx
...
wifi workaround
[Match]
Type=wlan
[Link]
Name=wlan0
NAPT using nftables
(ファイル終端に以下を追記)
table ip nat {
chain PREROUTING {
type nat hook prerouting priority 0;
}
chain POSTROUTING {
type nat hook postrouting priority 0;
oifname "wlan0" masquerade
}
}
...
(以下の行をアンコメント)
# Uncomment the next line to enable packet forwarding for IPv4
net.ipv4.ip_forward=1
...
% sudo sysctl -p
% sudo systemctl enable nftables.service
% sudo systemctl start nftables.service
DHCP server and DNS server using dnsmasq
domain-needed
bogus-priv
interface=eth0
no-hosts
dhcp-range=192.168.20.2,192.168.20.127,12h
(おしり二行なんかもとからついてるのでそれはそのまま使ってる)
(ipアドレスくばる*先*がraspberry piでなければおしり二行はたぶん消して大丈夫)
% sudo systemctl enable dnsmasq.service
% sudo systemctl start dnsmasq.service
AirPlay
general =
{
audio_backend_buffer_desired_length_in_seconds = 1.0;
}
alsa =
{
output_device = "hw:1";
output_format = "S32";
}
dsp =
{
convolution = "no";
loudness = "no";
}
% sudo systemctl enable shairport-sync.service
% sudo systemctl start shairport-sync.service
まだできてないこと
- BabyfaceのPhoneアウトボリュームがshairplay-syncから制御・同期できない
- ソフトでボリュームの変更はできるが質が多分悪い
- wifiというかsupplicantがDEAUTH_LEAVINGとかいう理由でなんか切れてるっぽい?のをdmesgで確認したので何の問題なのか一切わからないけどぐぐってなんかの名前を変更?して問題が解決したっぽかったが勘違いだった...まだ修正しきれておらず
おまけ
Ansibleの動作が遅くて激怒していたが、どうもそういうものらしい。
wsl2 dockerコンテナから
- % time ssh raspberrypi echoした場合、real: 0.8s, user: 0.05s, sys: 0.00s
- % time ansible raspberrypi -m ping -i inventoryした場合、real: 6.3s, user: 3.3s, sys: 0.18s
Ansibleのpingモジュール、io待ちとおぼしきものが6.3s - 3.3s = 3.0sくらい。
また、同コンテナから
- % ANSIBLE_KEEP_REMOTE_FILES=1 ansible raspberrypi -m ping -i inventory -vvv
した場合、raspberrypi側にAnsiballZ_ping.pyが残り、raspberry pi側で
- % time python3 ~/.ansible/tmp/ansible-tmp-ほにゃらら/AnsiballZ_ping.pyした場合、real: 2.5s, user: 2.3s, sys: 0.15s
io待ちが3.0sくらいに対してraspi側の実行が2.5sくらいなのでまぁ納得の数字。
ちなみに、time ansible ...したときのtime値は、2回目以降の実行の数字。sshのControlMasterとかいうの利用しているので一回目はもう少し普通に時間がかかるけど2回目以降は早い。
さらにちなみにAnsiballZ_ping.pyなんで遅いの?とか思ったけど中にBASE64とかそういうのでエンコードされたバイナリっぽいのが入っているのでたぶんそれの展開が遅い。たぶん。たしか100kbyteオーダーのファイルサイズあった。たぶん。pipeliningとかいうやつ用かなしらんけど。
参考にしたサイト
- https://qiita.com/homelan/items/62a09aa0d0586d0280a2
- https://unix.stackexchange.com/questions/386925/aborting-authentication-by-local-choice-reason-3-deauth-leaving-when-trying
- https://github.com/raspberrypi/linux/pull/1525
- https://www.angelcurio.com/raspberrypi/?Raspberry%20Pi/%E5%90%84%E7%A8%AE%E8%A8%AD%E5%AE%9A/log%2Ctmp%E3%81%AE%E3%82%AA%E3%83%B3%E3%83%A1%E3%83%A2%E3%83%AA%28tmpfs%29%E5%8C%96
- https://qiita.com/KEINOS/items/f3e6b3064b0cbe35fd03#initd-%E3%81%A7%E8%B5%B7%E5%8B%95%E6%99%82%E3%81%AB%E4%BB%BB%E6%84%8F%E3%81%AE%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%97%E3%83%88%E3%82%92%E3%82%B5%E3%83%BC%E3%83%93%E3%82%B9%E5%AE%9F%E8%A1%8C%E3%81%99%E3%82%8B
- https://www.takosuke.net/systemd%E3%81%A7%E7%89%B9%E5%AE%9A%E3%81%AE%E3%83%9E%E3%82%A6%E3%83%B3%E3%83%88%E3%81%AE%E5%AE%8C%E4%BA%86%E3%82%92%E5%BE%85%E3%81%A3%E3%81%A6%E3%82%B5%E3%83%BC%E3%83%93%E3%82%B9%E3%82%92%E8%B5%B7/
- https://iot-plus.net/make/raspi/extend-sdcard-lifetime-5plus1/
- https://www.angelcurio.com/raspberrypi/?Raspberry%20Pi/%E5%90%84%E7%A8%AE%E8%A8%AD%E5%AE%9A/log%2Ctmp%E3%81%AE%E3%82%AA%E3%83%B3%E3%83%A1%E3%83%A2%E3%83%AA%28tmpfs%29%E5%8C%96