EMLinux 3.1 (202404) にDockerをインストールしてみた

# はじめに
EMLinux 3.1 (202404) にDockerをインストールし、NGINXのコンテナを動かしてみました。

# 構成図
```
+--------------------------------+
| +----------------------------+ |
| | +------------------------+ | |
| | |  EMLinux 3.1 (202404)  | | |
| | +------------------------+ | |
| | QEMU                       | |
| +-+--------------------------+ |
|   |                            |
| +-+----+                       |
| | tap0 |                       |
| +-+----+                       |
|   |                            |
| +-+----------------------+     |
| | virbr0 (192.168.122.1) |     |
| +------------------------+     |
|                                |
| Ubuntu Server 20.04.6 LTS      |
+--------------------------------+
```

# 各ソフトウェアのバージョン
## Ubuntu Server
```
$ cat /etc/lsb-release 
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=20.04
DISTRIB_CODENAME=focal
DISTRIB_DESCRIPTION="Ubuntu 20.04.6 LTS"
```

## QEMU
```
$ qemu-system-aarch64 --version 
QEMU emulator version 4.2.1 (Debian 1:4.2-3ubuntu6.28)
Copyright (c) 2003-2019 Fabrice Bellard and the QEMU Project developers
```
# EMLinuxのビルドに必要なパッケージのインストール
1. 以下の記事を参考に、EMLinuxのビルドに必要なパッケージをインストールしてください。
   - [DockerおよびDocker Comoseのインストール](https://qiita.com/fukunagt/items/eed9fb418e8dd84a5af6#docker%E3%81%8A%E3%82%88%E3%81%B3docker-comose%E3%81%AE%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB)
   - [qemu-user-staticのインストール](https://qiita.com/fukunagt/items/eed9fb418e8dd84a5af6#qemu-user-static%E3%81%AE%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB)

# EMLinuxの展開
1. 以下の記事を参考に、EMLinuxを展開してください。
   - [EMLinuxの展開](https://qiita.com/fukunagt/items/eed9fb418e8dd84a5af6#emlinux%E3%81%AE%E5%B1%95%E9%96%8B)

# カスタムレイヤーの作成
1. EMLinuxを展開したディレクトリに移動してください。/home/emlinuxに展開した場合、以下に移動してください。
   ```
   cd /home/emlinux/EMLinux_3.0-202403/source/emlinux-3.1-202404
   ```
1. 以下のコマンドを実行し、カスタムレイヤーを作成してください。
   ```sh
   ./repos/meta-emlinux-private/scripts/create-custom-layer -p 30 custom
   ```
   ```sh
   mv templates/meta-custom/ repos/
   ```
   ```sh
   ln -s ../repos/meta-custom/scripts/add-layer-custom \
   setup-hooks/70-add-layer-custom
   ```
1. meta-customディレクトリに移動してください。
   ```sh
   cd /home/emlinux/EMLinux_3.0-202403/source/emlinux-3.1-202404/repos/meta-custom
   ```
1. meta-custom配下のディレクトリ構造が以下になるように、ディレクトリの作成、ディレクトリ名の変更、ファイルの作成を行ってください。
   ```
   meta-custom/
   ├── conf
   │   └── layer.conf
   ├── COPYING.MIT
   ├── README
   ├── recipes-docker
   │   ├── configure-docker
   │   │   ├── configure-docker.bb
   │   │   └── files
   │   │       ├── http-proxy.conf
   │   │       └── postinst
   │   └── images
   │       └── emlinux-image-base.bbappend
   └── scripts
       └── add-layer-custom
   ```
   - recipes-docker/configure-docker/configure-docker.bb
     ```
     FILESEXTRAPATHS:prepend := "${FILE_DIRNAME}/files:"

     DESCRIPTION = "Setup script for Docker"

     DEBIAN_DEPENDS = "docker.io"

     inherit dpkg-raw

     SRC_URI = " \
         file://postinst \
         file://http-proxy.conf \
     "

     do_install() {
         install -v -d ${D}/etc/systemd/system/docker.service.d
         install -v -m 644 ${WORKDIR}/http-proxy.conf ${D}/etc/systemd/system/docker.service.d/
     }
     ```
   - recipes-docker/configure-docker/files/http-proxy.conf
     ```
     [Service]
     Environment="HTTP_PROXY=<proxyサーバのIPアドレス>:<ポート番号>"
     Environment="HTTPS_PROXY=<proxyサーバのIPアドレス>:<ポート番号>"
     ```
   - recipes-docker/configure-docker/files/postinst
     ```
     #!/bin/sh
     systemctl enable docker
     update-alternatives --set iptables /usr/sbin/iptables-legacy
     ```
   - recipes-docker/configure-docker/images/emlinux-image-base.bbappend
     ```
     # Add packages for Docker
     IMAGE_PREINSTALL:append = " \
         ca-certificates \
         connman \
         docker.io \
     "
     ```
     - DHCPを有効にするために、connmanを追加しています。
   - 以下は変更不要です。
     - conf/layer.conf
     - COPYING.MIT
     - README
     - scripts/add-layer-custom

# EMLinuxのビルド
1. meta-emlinux/dockerに移動し、run.shを実行してください。
   ```sh
   cd /home/emlinux/EMLinux_3.0-202403/source/emlinux-3.1-202404/repos/meta-emlinux/docker
   ```
   ```sh
   ./run.sh
   ```
1. コンテナにログイン後、環境変数を設定してください。
   ```sh
   source setup-emlinux build
   ```
1. meta-customレイヤーが追加されていることを確認してください。
   ```
   $ bitbake-layers show-layers 
   layer                 path                                      priority
   ==========================================================================
   meta                  /home/build/work/build/../repos/isar/meta  5
   meta-emlinux          /home/build/work/build/../repos/meta-emlinux  12
   isar-cip-core         /home/build/work/build/../repos/isar-cip-core  6
   meta-emlinux-private  /home/build/work/build/../repos/meta-emlinux-private  13
   meta-custom           /home/build/work/build/../repos/meta-custom  30
   ```
1. viでconf/local.confを開き、末尾に以下を追記してください。
   ```
   # ARM64
   MACHINE = "qemu-arm64"
   
   # Setup for Docker
   IMAGE_INSTALL:append = " configure-docker"

   # Extra space for rootfs in MB
   ROOTFS_EXTRA = "10240"   
   ```
1. EMLinuxをビルドしてください。
   ```sh
   bitbake emlinux-image-base
   ```
1. ビルド完了後、exitコマンドでコンテナからログアウトしてください。
   ```
   build@82fa043f8378:~/work$ exit 
   exit
   ```

# tap0の作成
1. 以降の作業はsudo権限があるユーザで行ってください。
1. 以下のコマンドを実行し、tap0を作成してください。
   ```sh
   sudo ip tuntap add tap0 mode tap
   ```
   ```sh
   sudo ip link set tap0 promisc on
   ```
   ```sh
   sudo ip link set dev tap0 master virbr0
   ```
   ```sh
   sudo ip link set dev tap0 up
   ```   

# EMLinuxの起動
1. 以下のディレクトリに移動してください。
   ```sh
   cd /home/emlinux/EMLinux_3.0-202403/source/emlinux-3.1-202404/build
   ```
1. run.shファイルを作成してください。
   ```sh
   touch run.sh
   ```
1. vimでrun.shファイルを以下のように編集してください。
   ```
   qemu-system-aarch64 \
   -net nic \
   -net tap,ifname=tap2,script=no \
   -drive id=disk0,file=./tmp/deploy/images/qemu-arm64/emlinux-image-base-emlinux-bookworm-qemu-arm64.ext4,if=none,format=raw \
   -device virtio-blk-device,drive=disk0 -show-cursor -device VGA,edid=on \
   -device qemu-xhci \
   -device usb-tablet \
   -device usb-kbd \
   -object rng-random,filename=/dev/urandom,id=rng0 \
   -device virtio-rng-pci,rng=rng0 \
   -nographic \
   -machine virt \
   -cpu cortex-a57 \
   -m 2G \
   -serial mon:stdio \
   -serial null \
   -kernel ./tmp/deploy/images/qemu-arm64/emlinux-image-base-emlinux-bookworm-qemu-arm64-vmlinux \
   -initrd ./tmp/deploy/images/qemu-arm64/emlinux-image-base-emlinux-bookworm-qemu-arm64-initrd.img \
   -append 'root=/dev/vda rw highres=off console=ttyS0 mem=2G ip=dhcp console=ttyAMA0'
   ```
1. suコマンドで、sudo権限があるユーザに切り替えてください。
1. run.shを実行してください。
   ```sh
   sudo ./run.sh
   ```
   - パスワードを問われる場合には、sudo権限があるユーザのパスワードを入力してください。
1. rootユーザでログインしてください。既定のパスワードはrootです。
   ```
   EMLinux3 login: root
   Password: 
   ```
1. IPアドレスが割り振られていることを確認してください。
   ```
   root@EMLinux3:~# ip a
   (snip)
   2: enp0s1: <BROADCAST,MULTICAST,DYNAMIC,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 52:54:00:12:34:56 brd ff:ff:ff:ff:ff:ff
    inet 192.168.122.77/24 metric 1024 brd 192.168.122.255 scope global dynamic enp0s1
       valid_lft 3523sec preferred_lft 3523sec
   (snip)
   ```
1. コンテナイメージの検索が行えるか確認してください。
   ```
   root@EMLinux3:~# docker search nginx 
   NAME                                              DESCRIPTION                                     STARS     OFFICIAL   AUTOMATED
   nginx                                             Official build of Nginx.                        19768     [OK]       
   (snip)
   ```
1. 以下のコマンドを実行し、NGINXのコンテナを起動してください。
   ```sh
   docker run -it -d --name nginx-test -p 80:80 nginx:latest
   ```
1. コンテナホストからcurlコマンドで、NGINXにアクセスできることを確認してください。
   ```
   $ curl 192.168.122.77 --noproxy 192.168.122.77
   <!DOCTYPE html>
   <html>
   <head>
   <title>Welcome to nginx!</title>
   (snip)
   ```

# さいごに
EMLinux 3.1のレシピをカスタマイズし、Dockerをインストールしてみました。また、`docker run`でNGINXのコンテナイメージを使い、Webサーバを起動してみました。

ただ、組み込み機器では、わざわざ組み込み機器にログインし、`docker run`などを実行することはないため、Webサーバ経由などで外部から任意のコマンドを実行できるような仕組みが必要と考えていますが、それはまた別の記事で。

# 補足
## Dockerを動作させるために必要なパッケージについて
- Dockerはiptablesに依存しています。Dockerでiptablesを利用するために、`update-alternatives --set iptables /usr/sbin/iptables-legacy`を実行しています。
  - https://gist.github.com/peteristhegreat/4bcbca81ed68605e36cf56bb0fff2960
- `docker pull`など外部との通信を行うために、ca-certificatesをインストールしています。

## ROOTFS_EXTRAについて
- コンテナを保存するために、ディスクサイズを拡張しています。既定では64 MBが指定されています。本記事では、ROOTFS_EXTRA = "10240"をlocal.confに設定しています。
  - https://github.com/ilbers/isar/blob/master/meta/classes/image.bbclass#L85