DockerとQEMUでRaspbianイメージをカスタマイズする

## TL;DR
dockerとqemuでRaspbianのイメージファイル`.img`にchrootし、直接カスタマイズする

## 何がうれしい
* 設定がコード化できる
* 同じ環境をたくさん作れる
* 完成物が小さい
* PCだけで作業できる(シリアルケーブル不要)

## 作業用Dockerイメージ

```dockerfile
FROM ubuntu:18.04
RUN apt update &&\
    apt install -y kpartx libarchive-tools wget qemu-user-static binfmt-support --no-install-recommends &&\
    apt clean &&\
    rm -rf /var/lib/apt/lists/*
```

適当に作業用ディレクトリを作成し、マウントする

```console
$ mkdir workspace
$ docker build -t nullsnet/raspbian_customizer .
$ docker run --rm -v ${PWD}/workspace:/workspace -it nullsnet/raspbian_customizer bash
```

## Ubuntu in Docker
ここからdockerのUbuntu内での作業

### マウント
作業用ディレクトリに移動した後、Raspbianのイメージファイルをダウンロード&展開する

```console
$ cd workspace
$ wget https://downloads.raspberrypi.org/raspbian_lite_latest -O-|bsdtar -xvf
$ image=$(ls -1tr *.img)
$ echo $image
2019-09-26-raspbian-buster-lite.img
```

`kpartx`でマッピングして、適当なディレクトリにマウントする
`loop[0-9]p1`が`boot`パーティション、`loop[0-9]p2`が`rootfs`パーティションになる

```console
$ kpartx -av $image
$ add map loop8p1 (253:16): 0 524288 linear 7:8 8192
$ add map loop8p2 (253:17): 0 3858432 linear 7:8 532480
$ mkdir rootfs boot
$ mount $(ls /dev/mapper/loop*p1) boot
$ mount $(ls /dev/mapper/loop*p2) rootfs
```

この時点で`boot`と`rootfs`の中身が触れるようになる
ファイルを改変するだけの設定で良いなら、ここまででよい

```console
$ ls rootfs
bin  boot  dev  etc  home  lib  lost+found  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var
$ ls raspbian-boot/
COPYING.linux           bcm2708-rpi-cm.dtb      bcm2710-rpi-2-b.dtb       bcm2711-rpi-4-b.dtb  fixup.dat     fixup4x.dat   issue.txt     kernel8.img  start4cd.elf  start_db.elf
LICENCE.broadcom        bcm2708-rpi-zero-w.dtb  bcm2710-rpi-3-b-plus.dtb  bootcode.bin         fixup4.dat    fixup_cd.dat  kernel.img    overlays     start4db.elf  start_x.elf
bcm2708-rpi-b-plus.dtb  bcm2708-rpi-zero.dtb    bcm2710-rpi-3-b.dtb       cmdline.txt          fixup4cd.dat  fixup_db.dat  kernel7.img   start.elf    start4x.elf
bcm2708-rpi-b.dtb       bcm2709-rpi-2-b.dtb     bcm2710-rpi-cm3.dtb       config.txt           fixup4db.dat  fixup_x.dat   kernel7l.img  start4.elf   start_cd.elf
```

### chroot

`qemu-arm-static`をコピーする

```console
$ cp /usr/bin/qemu-arm-static rootfs/usr/bin/
```

`ld.so.preload`を書き換える
chroot時は`$PLATFORM`が定義されないため，これをやっておかないとバイナリ実行時にエラーが出てしまう

```console
$ sed -i 's/${PLATFORM}/v7l/' rootfs/etc/ld.so.preload
```

`rootfs`にchrootしてbashを起動する

```console
$ chroot rootfs /bin/bash
```
これであとは好きなように設定できる

#### raspi-config

`raspi-config`による設定も可能

```console
$ raspi-config nonint do_hostname raspi
$ raspi-config nonint do_wifi_country JP
$ raspi-config nonint do_change_timezone 'Asia/Tokyo'
$ raspi-config nonint do_change_locale ja_JP.UTF-8
```

#### apt

`apt`も当然可能

```console
$ apt update
$ apt upgrade -y
$ apt install -y git
```

#### systemd

`ssh`を有効にしておき、実機起動直後にすぐ接続できるようにもできる

```console
$ systemctl enable ssh
```

#### docker

dockerのインストールだってできる

```console
$ curl -sSL https://get.docker.com/ | sh
$ systemctl enable docker
```

#### Shell Script

設定をシェルスクリプトにしておき、chroot時に呼び出すこともできる

```console
$ chroot rootfs /bin/bash -C YOUR_SCRIPT.sh
```

### 後片付け

chrootを抜けた後、`qemu-arm-static`を削除して`ld.so.preload`を元に戻す

```console
$ exit
$ rm rootfs/usr/bin/qemu-arm-static
$ sed -i 's/v7l/${PLATFORM}/' rootfs/etc/ld.so.preload
```

イメージファイルをアンマウントしてマッピングを削除する

```console
$ umount boot rootfs
$ rmdir boot rootfs
$ kpartx -d $image
$ rm /dev/mapper/loop*
```

## SDカードの作成
作成した`*.img`ファイルを、普通に`dd`等でSDカードへ書き込めばよい