carloscn / blog

My blog
Apache License 2.0
119 stars 34 forks source link

[LS104x] ostree的部署 #195

Open carloscn opened 1 year ago

carloscn commented 1 year ago

[LS104x] 使用ostree更新rootfs 中我们在ramdisk中使用ostree的checkout功能还原出一个完整版本rootfs,但是这个方法并没有完全彻底的使用ostree的部署功能。本节将要总结和整理一个ostree的部署功能。类似于该demo:YOUTUBE - Designing OSTree based embedded Linux systems with the Yocto Project

从ubuntu更新为busybox,更新原理如图所示:

Note,这些ostree的部署的逻辑被包含在了meta-updater里面, https://github.com/advancedtelematic/meta-updater/tree/master 。但metaupdater集成度很高,而且该仓库已经很久没有更新了,就导致和新版的yocto集成有很多问题,需要不少的工作量。因此,本文手动部署ostree,并且参考meta-updater里面的逻辑手动补充一些脚本来完成ostree的部署和ostree部署的rootfs的使用。

1. 服务器端配置

本文使用HTTP而不是HTTPS,并且服务器使用本机HOST局域网更新。

1.1 仓库配置

创建初始化仓库:

mkdir -p repo && ostree --repo=repo init --mode=archive-z2 && mkdir -p rootfs

1.1.1 rootfs改造

需要把rootfs下面根目录的etc转移到/usr/下面:

The deployment should not have a traditional UNIX /etc; instead, it should include /usr/etc. This is the “default configuration”. When OSTree creates a deployment, it performs a 3-way merge using the old default configuration, the active system’s /etc, and the new default configuration. In the final filesystem tree for a deployment then, /etc is a regular writable directory. https://ostreedev.github.io/ostree/deployment/

1.1.2 kernel文件

除此之外,还需要创建kernel、devicetree和initramfs的文件到下面的文件路径:

命名规则如下所示:

Note,不要使用软链接符号。

没有以上两步,ostree无法在板子上部署

1.2 仓库上传

可能有的需要sudo命令:

ostree --repo=repo commit --branch=master --subject="image v1 (ls104x) rootfs)" rootfs

不需要sudo:

ostree log master --repo=repo

ostree summary --repo=repo ./repo/branch.summary -u

使用服务器监听repo:

python3 -m http.server 8000 --bind 10.10.192.121 --directory repo

2. 客户端配置(板级)

2.1 初始化版本的rootfs

我们需要准备一个能够正常启动,包括ostree和网络功能的的rootfs,作为基本的rootfs。然后我们依赖于这个rootfs,进入Linux runtime对ostree进行部署。

在我的demo中使用的是ubuntu:(当然ubuntu会很大,可以采用一些轻量的rootfs)

使用这个版本进入正常的Linux Runtime,接下来我们要在板子上部署ostree。

2.2 部署ostree

部署之前请检查你的板子:

2.2.1 创建标准的sysroot

进入板子的根目录cd / 并且创建一个sysroot文件夹 mkdir sysroot

初始化一个标准的sysroot文件夹:

ostree admin init-fs sysroot

你可以得到一个:

2.2.2 增加远程repo

给仓库增加

ostree remote add --repo=/sysroot/ostree/repo --no-gpg-verify origin http://10.10.192.121:8000/

从远程仓库拉去数据:

ostree pull --repo=/sysroot/ostree/repo origin:master

记得要sync一下:

sync

ostree log --repo=/sysroot/ostree/repo origin:master

2.2.3 部署仓库

新建一个tcu的系统:

ostree admin os-init tcu --sysroot=sysroot

部署tcu系统:

ostree admin --sysroot=sysroot --os=tcu deploy origin:master

查看部署情况: ostree admin --sysroot=sysroot status

部署之后的文件:

/sysroot/ostree/deploy/tcu/deploy目录就可以看到:

upgrade:

ostree admin --sysroot=sysroot --os=tcu upgrade

通过这个命令,可以看到,ostree把最新的部署rootfs中的etc从/usr/etc拿了出来:

之后可以通过一些脚本,在部署完成之后,拿到rootfs的地址,然后在ramdisk里面配合一些脚本解析进行切换。

2.2.4 ramdisk脚本

#!/bin/sh
#
# Copyright 2018 NXP
#
# SPDX-License-Identifier:      BSD-3-Clause
#

# mount the /proc and /sys filesystems.
/bin/mount -n -t proc none /proc
/bin/mount -n -t sysfs none /sys

a=`/bin/cat /proc/cmdline`

# Mount the root filesystem.
if ! echo $a | /bin/grep -q 'mount=' ; then
    # set default mount device if mountdev is not set in othbootargs env
    mountdev=mmcblk0p3
    # echo Using default mountdev: $mountdev
else
    mountdev=`echo $a | /bin/sed -r 's/.*(mount=[^ ]+) .*/\1/'`
    echo Using specified mountdev: $mountdev
fi

partnum=`echo $mountdev | /usr/bin/awk '{print substr($0,length())}'`
echo partnum: $partnum

MOUNT_P=/new_root
MOUNT_R=/mnt
mkdir ${MOUNT_P}

/bin/mknod /dev/$mountdev b 179 $partnum && /bin/mount -o rw /dev/$mountdev ${MOUNT_P}
if [ $? -ne 0 ];then
    echo "[INFO] mount sd card failed! reboot device!"
    reboot
    exit 0
fi

ls ${MOUNT_P} && ls ${MOUNT_P}/etc > /dev/null
if [ $? -ne 0 ];then
    echo "[INFO] No ${MOUNT_P} or ${MOUNT_P}/etc directory!! reboot device!"
    reboot
    exit 0
fi

ls ${MOUNT_P}/update
if [ $? -ne 0 ];then
    echo "[INFO] current is NXP busybox (primary image), switch to ubuntu (recovery)"
    ROOTFS_DIR="${MOUNT_P}"
else
    echo "[INFO] current is ubuntu (recovery image), update and switch to primary!"
    cd ${MOUNT_P}
    ROOTFS_PATH="${MOUNT_P}/sysroot/ostree/deploy/tcu/deploy"
    OSTREE_NEWEST_COMMIT=`ostree admin --sysroot=sysroot status | grep tcu | head -n1 | sed 's/tcu //g' | tr -d ' '`
    ROOTFS_DIR="${ROOTFS_PATH}/${OSTREE_NEWEST_COMMIT}"
    echo "[INFO] The rootfs real directory is : ${ROOTFS_DIR}"
    ls ${ROOTFS_DIR}
    if [ $? -ne 0 ];then
        echo "[INFO] no rootfs! Go to recovery!"
        ROOTFS_DIR="${MOUNT_P}"
    else
        echo "[INFO] found a new rootfs!"

    fi
fi

# switch_root will fail to function if newroot is not the root of a
# mount. If you want to switch root into a directory that does not
# meet this requirement then you can first use a bind-mounting
# trick to turn any directory into a mount point:
#       mount --bind $DIR $DIR
/bin/mount --bind ${ROOTFS_DIR} ${MOUNT_R}
echo "[INFO] mount move proc sys directories."
/bin/mount -o move /proc ${MOUNT_R}/proc
/bin/mount -o move /sys ${MOUNT_R}/sys

echo "[INFO] exec /bin/busybox switch_root ${MOUNT_R} /sbin/init"
sleep 1
exec /bin/busybox switch_root ${MOUNT_R} /sbin/init </dev/console >dev/console 2>&1

需要注意的是,这里有个非常关键的一点,就是使用switch_root来切换rootfs路径问题。由于ostree部署的路径是ramdisk挂载sd卡中子目录,并没有挂载到root(根)上面的路径。这个在switch_root的新版本中是不被允许的。参考:https://man7.org/linux/man-pages/man8/switch_root.8.html 中的note,

switch_root will fail to function if newroot is not the root of a mount. If you want to switch root into a directory that does not meet this requirement then you can first use a bind-mounting trick to turn any directory into a mount point.

如果用了sd卡基于挂载点的子目录,就会有如下错误:

[    4.978901] Kernel panic - not syncing: Attempted to kill init! exitcode=0x00000100
[    4.986556] CPU: 2 PID: 1 Comm: busybox Not tainted 5.10.35 #1
[    4.992381] Hardware name: LS1046A RDB Board (DT)
[    4.997077] Call trace:
[    4.999522]  dump_backtrace+0x0/0x1a8
[    5.003176]  show_stack+0x18/0x68
[    5.006485]  dump_stack+0xd0/0x12c
[    5.009879]  panic+0x16c/0x334
[    5.012924]  do_exit+0x9ec/0xa08
[    5.016143]  do_group_exit+0x44/0xa0
[    5.019709]  __wake_up_parent+0x0/0x30
[    5.023451]  el0_svc_common.constprop.0+0x78/0x1a0
[    5.028233]  do_el0_svc+0x24/0x90
[    5.031540]  el0_svc+0x14/0x20
[    5.034584]  el0_sync_handler+0xb0/0xb8
[    5.038411]  el0_sync+0x178/0x180
[    5.041719] SMP: stopping secondary CPUs
[    5.045635] Kernel Offset: disabled
[    5.049114] CPU features: 0x0240022,21002000
[    5.053374] Memory Limit: none
[    5.056423] ---[ end Kernel panic - not syncing: Attempted to kill init! exitcode=0x00000100 ]---