Installer Docker sur un Orange Pi PC

toolchain

On travaille sur une architecture amd64 et l’Orange Pi PC a une architecture ARM v7 avec fpu hardware.

Sous Debian, une première solution consiste à installer les packages :

  • binutils-arm-none-eabi
  • gcc-arm-none-eabi

Une autre solution consiste à utilise les packages toolchain Linaro. On télécharge l’archive binaire sur le site, par exemple gcc-linaro-5.3-2016.02-x86_64_arm-linux-gnueabihf.tar.xz

On l’extrait sous $HOME, ce qui crée le répertoire gcc-linaro-5.3-2016.02-x86_64_arm-linux-gnueabihf/, que l’on ajoute au PATH

export PATH=~/gcc-linaro-5.3-2016.02-x86_64_arm-linux-gnueabihf/bin:$PATH

Les exécutables sont préfixés par arm-linux-gnueabihf- qu’il faudra indiquer pour les cross-compilations.

u-boot

Dans un premier temps, cloner le dépôt git

git clone git://git.denx.de/u-boot.git

On se positionne sur la dernière release stable. Par exemple

git co v2016.11

Puis on lance la compilation. La variable CROSS_COMPILE contient le préfixe correspondant aux outils de la toolchain installés précédemment

make  ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- orangepi_pc_defconfig
make  ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-

A la fin de la compilation, on obtient, entre autres, un ficher u-boot-sunxi-with-spl.bin qui est celui qui nous intéresse.

Pour l’installer sur la carte SD et permettre de booter le noyau, on passe la commande

dd if=u-boot-sunxi-with-spl.bin of=/dev/sdX bs=1024 seek=8

Dans la commande ci-dessus, il faut remplacer /dev/sdX par le device correspondant à la carte SD (/dev/sdb ou /dev/mmcblk0 par exemple).

Noyau Linux

Actuellement, Orange Pi PC (Opi PC) tourne avec un noyau 3.4 qui est le seul patché par sunxi pour supporter le matériel. Or ce noyau commence à dater sérieusement, a de nombreuses failles de sécurité , et n’est pas compatible avec Docker (il manque les cgroups en particulier) ou F2FS qui est un système de fichiers adapté aux mémoires flash comme les cartes SD.

Si le support matériel de l’Opi PC s’améliore dans le noyau Linux vanilla, il y a encore des manques importants, en particulier le port Ethernet et l’USB.

Docker étant plutôt orienté serveur, on peut se passer de l’USB mais pas de l’Ethernet. Heureusement, le support de l’Ethernet de l’Opi PC est en cours de développement et semble fonctionnel. Il existe un dépot Github qui fait un fork du noyau Linux pour ajouter le support emac de l’Opi PC.

Comme on va rebaser sur une version stable du noyau, on a besoin du dépot git de la branche stable de Linux. On récupère également la branche de Linus (version en développement qui sert de base au patch).

Allons-y

git clone -o stable git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git
cd linux-stable
git remote add emac https://github.com/montjoie/linux.git
git remote add linus   git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
git fetch linus
git fetch emac

# on se place sur la v4 du patch emac
git co emac/sun8i-emac-wip-v4
# on le rebase sur la version 4.8.6 du noyau
git rebase v4.8.9
# on crée un tag pour garder la trace
git br emac_wip4_4.8.9

A ce stade, on a donc les sources d’un noyau Linux stable 4.8.9 avec le patch permettant le support Ethernet.

J’ai toujours eu l’habitude de séparer les sources du répertoire de construction (dans l’exemple ~/build/stable) avec la commande suivante

make O=~/build/stable ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- sunxi_defconfig

On repart de la config sunxi par défaut mais celle-ci est loin de convenir. Pour l’adapter

make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig

Voici la config que j’utilise.

Pour le support Ethernet, ne pas oublier les options

CONFIG_ETHERNET=y
CONFIG_NET_VENDOR_ALLWINNER=y
CONFIG_SUN4I_EMAC=y
CONFIG_SUN8I_EMAC=y
CONFIG_PHYLIB=y
CONFIG_SWPHY=y

Reste à (cross-)compiler le noyau. Le script qui suit compile le noyau, les modules et les device trees dans le répertoire ~/build/install

#!/bin/sh
INSTALL=~/build/install
rm -r $INSTALL
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- oldconfig
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j8 zImage modules
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- dtbs
make INSTALL_MOD_PATH=$INSTALL ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- modules_install
cp -r arch/arm/boot/zImage arch/arm/boot/dts $INSTALL

Les différents composants utiles sont donc tous présents dans le répertoire install. Pour ceux qui veulent éviter toutes les étapes précédentes, on peut récupérer l’archive résultante.

Partitionnement de la carte SD

On considère que la carte SD est le device /dev/sdX. On va créer une partition de boot en format FAT, un swap et la partition root.

La parition boot va contenir le noyau, les device trees et le script de configuration.

La partition root va être constitué d’un fichier en F2FS, a priori plus adapté à une carte SD que le format EXT4. C’est une des raisons de l’existence de la partition FAT : u-boot saurait se contenter d’une partition unique EXT4 mais, à ma connaissance, pas en F2FS. De plus, ce n’est pas très génant de séparer boot et root.

Device           Start      End  Sectors  Size Id Type
/dev/sdX1         2048   264191   262144  128M  c W95 FAT32 (LBA)
/dev/sdX2       264192  2361343  2097152    1G 82 Linux swap / Solaris
/dev/sdX3      2361344 31116287 28754944 13,7G 83 Linux

Création des systèmes de fichiers

::

sudo mkfs.vfat /dev/sdX1 sudo mkswap /dev/sdX2 sudo mkfs.f2fs /dev/sdb3

sudo mount /dev/sdX1 /mnt/tmp1 sudo mount /dev/sdX3 /mnt/tmp3

cd /mnt/tmp1 cp -r install .

sudo vi boot.cmd

Partition boot

Depuis le répertoire install, on copie le fichier zImage et le répertoire dts. Les modules devront être copiés plus tard dans le rootfs mais la config retenue fait qu’ils ne sont pas indispensables pour booter car les éléments importants sont compilés dans le noyau et non pas en modules.

Reste le fichier de configuration de boot. On édite le fichier boot.cmd avec le contenu suivant

setenv bootargs console=ttyS0,115200 noinitrd root=/dev/mmcblk0p3 rootwait panic=10
fatload mmc 0 ${kernel_addr_r} zImage
fatload mmc 0 ${fdt_addr_r} dts/sun8i-h3-orangepi-pc.dtb
bootz ${kernel_addr_r} - ${fdt_addr_r}

Un peu de détail ligne par ligne :

  • la première ligne active la console sur le port UART : celà permet d’avoir le log dès le boot du noyau. Comme on a pas l’USB actif, il est quasiment indispensable d’avoir un cable série-USB pour suivre ce qui se passe au boot (et après). On précise également la partition (pour l’Opi PC) qui contient la partition root (ici /dev/mmcblk0p3).
  • la deuxième ligne charge le noyau (depuis une partition FAT)
  • la troisième ligne charge le device tree de l’Opi PC (depuis une partition FAT)
  • et on boot (le - en second paramètre signifie qu’on a pas d’initrd)

Il faut ensuite compiler ce fichier par la commande suivante

mkimage -C none -A arm -T script -d boot.cmd boot.scr

Si la commande mkimage n’existe pas, il faut installer le package u-boot-tools.

A ce stade, on doit pouvoir vérifier que l’Opi PC boote sur la carte SD et lance le noyau. Par contre, on va échouer à monter la racine qui n’existe pas.

Partition rootfs

On a de quoi booter un noyau mais il faut maintenant créer un système complet dans la partition root. Le choix fait ici est de créer un système Debian en utilisant la commande multistrap. On aura également besoin de qemu-user-static pour émuler un processeur ARM et chrooter dans le rootfs.

On commence donc par installer les packages

apt install multistrap qemu-user-static

Il faut ensuite un fichier de configuration. Voici celui que j’utilise et qui peut être adapté. Fichier armhf1.conf

[General]
arch=armhf
directory=jessie-armhf1
cleanup=true
noauth=true
unpack=true
debootstrap=Debian Net Utils
aptsources=Debian

[Debian]
packages=apt kmod lsof
source=http://cdn.debian.net/debian/
keyring=debian-archive-keyring
suite=jessie
components=main contrib non-free

[Net]
packages=netbase net-tools ethtool udev iproute iputils-ping ifupdown
isc-dhcp-client ssh
source=http://cdn.debian.net/debian/

[Utils]
packages=locales adduser vim zsh less wget dialog usbutils rsync tmux git
source=http://cdn.debian.net/debian/

Pour utiliser ce fichier, il suffit ensuite de passer la commande

multistrap -f armhf1.conf

Il faut ensuite copier /usr/bin/qemu-arm-static dans le répertoire /usr/bin du rootfs et chrooter pour exécuter dpkg --configure -a.

L’image obtenue n’est toutefois pas parfaite car plusieurs fichiers de configuartion ne sont pas renseignés, par exemple /etc/fstab ou /etc/network/interfaces.

Pour simplifier l’installation, on peut regrouper les différentes opérations dans un script

ROOTFS_DIR="jessie-armhf1"
HOSTNAME="opi"
ROOTDEV="/dev/mmcblk0p3"
BOOTDEV="/dev/mmcblk0p1"
ID_RSA_PUB="ssh-rsa AAAAB3...JglcE61D36Xs1aWF37Q9DKP54L philippe@example"

multistrap -f armhf1.conf

cp /usr/bin/qemu-arm-static $ROOTFS_DIR/usr/bin
mount -o bind /dev/ $ROOTFS_DIR/dev/
export DEBIAN_FRONTEND=noninteractive DEBCONF_NONINTERACTIVE_SEEN=true
export LC_ALL=C LANGUAGE=C LANG=C
chroot $ROOTFS_DIR dpkg --configure -a

#hostname
filename=$ROOTFS_DIR/etc/hostname
echo $HOSTNAME > $filename

#name servers
filename=$ROOTFS_DIR/etc/resolv.conf
echo nameserver 8.8.8.8 > $filename
echo nameserver 8.8.4.4 >> $filename

#network interfaces
filename=$ROOTFS_DIR/etc/network/interfaces
echo auto eth0 >> $filename
echo iface eth0 inet dhcp >> $filename

#fstab
filename=$ROOTFS_DIR/etc/fstab
echo $BOOTDEV /boot vfat noatime 0 1 > $filename
echo $ROOTDEV /     ext4 noatime 0 1 >> $filename

#apt sources
filename=$ROOTFS_DIR/etc/apt/sources.list
echo deb http://ftp.fr.debian.org/debian jessie main > $filename
echo deb http://security.debian.org/     jessie/updates main >> $filename
echo deb http://ftp.fr.debian.org/debian jessie-backports main >> $filename

# ssh
mkdir -p $ROOTFS_DIR/root/.ssh
chmod 700 $ROOTFS_DIR/root/.ssh
echo $ID_RSA_PUB >> $ROOTFS_DIR/root/.ssh/authorized_keys

Il suffit ensuite de recopier ce rootfs sur la partition root, par exemple en utilisant la commande rsync et en créant un fichier tar.

On peut également recopier les modules du noyau dans le répertoire /lib/modules du rootfs.

# crée système de fichier F2FS
mkfs.f2fs /dev/sdX3
# monte sous /mnt
mount /dev/sdX3 /mnt
cd /mnt
# copie le rootfs crée ci-dessus
rsync -av ~/rootfs/jessie-armhf1/ .
# copie les modules du noyau
rsync -av ~/build/install/lib  .
# démonte la carte SD
umount /mnt

Premier boot

Théoriquement, il y a tout ce qu’il faut pour booter et obtenir un debian pur avec un noyau vanilla (à part le patch emac).

Ne reste plus qu’à installer docker

apt install docker.io thin-provisioning-tools