基于 debootstrap 和 busybox 构建 mini ubuntu

最近的工作涉及到服务器自动安装和网络部署操作系统,然后使用 ansible 和 saltsatck 进行配置并安装 openstack 。

难点在于服务器的自动安装,由于不单只是通过 PXE 安装服务器,还需要能够安装时进行分区、配置网卡等工作,因此需要在开始安装前,必须先收集服务器的硬件信息。

调研了一下目前的开源项目中,提供此类功能的有 tinycorelinux 、 puppet razor-el-mk 可做类似的工作。tinycorelinux 是个很好的工具,整个系统在 PXE 之后在内存中执行,可在里面加上简单的 agent 完成任务报告的工作;razor 是 puppet 绑定在一起用的,el-mk 基于 centos ,它在里面装了 razor 的 agent,使用 facter 进行硬件信息收集。

这些方案的基本思路都是相通的,首先通过 PXE 下载 microkernel ,然后直接在内存中执行,启动网卡,运行 agent 并向服务器汇报信息,并接收来自服务器的命令。基本的技术原理都是 PXE + linux initramfs ,根据不同的需要向 initramfs 中加硬件驱动。

仔细研究了一下之后,发现用 debootstrap + busybox 工具做这样的小系统会更加简单,有以下的优点:

  1. debootstrap 生成的小 ubuntu 能方便使用 apt 安装额外的工具
  2. 可直接把驱动模块拷贝到小镜像内使用
  3. 定制脚本非常简单容易

整个小系统在不安装额外的软件和内核模块的情况下,为 100 M 左右,并可加入 busybox 后裁减到 40-50 M(包含完整的基础库)。在安装了 python3 (完整的 python3 ),可裁减到 110 M 左右。

在开始前,可能需要先了解一下 initramfs 的原理,可看 http://www.iteedu.com/os/linux/mklinuxdiary/ch3initrd/index.php 。从本质上看,initramfs 就是一个经过裁减过的 linux 系统的完整文件系统(去掉 kernel 、删掉没有用的软件),然后在内存中展开,并执行程序(/init /sbin/init)。

一个最简单的 mini ubuntu

在 ubuntu 14.04 上,用 debootstrap 生成一个 minibase 的 ubuntu 系统,其中包含了整个基本的文件系统和 apt 工具(不包含 kernel) ::

sudo debootstrap --variant=minbase trusty mini \
http://mirrors.aliyun.com/ubuntu/

这个地方用了 aliyun 的 mirror ,也可换成 163 或其它的 mirror 。可以使用 chroot 切换进去,用 dpkg 查看已安装的软件 ::

sudo chroot mini dpkg -l
# 或
sudo chroot mini /bin/bash

为了让它能作为 initramfs 被 kernel 启动,需要在根目录下放一个 init 文件,在 init 文件中写上启动过程(文件系统挂载、执行 /sbin/init等),以下是从 initramfs-tools 工具里面抄了一部分出来(/usr/share/initramfs-tools/init),由于不挂载硬盘上的 root 分区,因此减少了很多代码。

cat << EOF | sudo tee mini/init
#!/bin/sh [ -d /dev ] || mkdir -m 0755 /dev
[ -d /root ] || mkdir -m 0700 /root
[ -d /sys ] || mkdir /sys
[ -d /proc ] || mkdir /proc
[ -d /tmp ] || mkdir /tmp
mkdir -p /var/lock
mount -t sysfs -o nodev,noexec,nosuid sysfs /sys
mount -t proc -o nodev,noexec,nosuid proc /proc
# Some things don't work properly without /etc/mtab.
ln -sf /proc/mounts /etc/mtab grep -q '\<quiet\>' /proc/cmdline || echo "Loading, please wait..." # Note that this only becomes /dev on the real filesystem if udev's scripts
# are used; which they will be, but it's worth pointing out
if ! mount -t devtmpfs -o mode=0755 udev /dev; then
echo "W: devtmpfs not available, falling back to tmpfs for /dev"
mount -t tmpfs -o mode=0755 udev /dev
[ -e /dev/console ] || mknod -m 0600 /dev/console c 5 1
[ -e /dev/null ] || mknod /dev/null c 1 3
fi
mkdir /dev/pts
mount -t devpts -o noexec,nosuid,gid=5,mode=0620 devpts /dev/pts || true
mount -t tmpfs -o "noexec,nosuid,size=10%,mode=0755" tmpfs /run
mkdir /run/initramfs
# compatibility symlink for the pre-oneiric locations
ln -s /run/initramfs /dev/.initramfs # Set modprobe env
export MODPROBE_OPTIONS="-qb" # mdadm needs hostname to be set. This has to be done before the udev rules are called!
if [ -f "/etc/hostname" ]; then
/bin/hostname -b -F /etc/hostname 2>&1 1>/dev/null
fi exec /sbin/init
EOF sudo chmod +x mini/init

当 GRUB 载入 kernel 和 initramfs 后,kernel 会把 initramfs 在内存中展开,然后执行其根目录下的 init ,也就是上面的脚本。以上的脚本会执行 mount 工作,准备好目录结构,然后执行 /sbin/init 转换入 ubuntu 的初始化过程(system-v init ,upstart , systemd,用 udev 自动创建设备文件等)。

修改 ubuntu 的启动配置,进行自动登录 ::

for i in $(find mini/etc/init -type f -name "tty*"); do
sed -e "s|/sbin/getty -8|/sbin/getty --autologin root -8|" -i $i
done

注意,minibase 中没有包含内核模块和硬件驱动,需要从 kernel 或本机中把模块 copy 进去,这里通过 apt-get 下载一个 linux-image 并解压得到内核模块 ::

sudo apt-get download linux-image-$(uname -r)
sudo rm -rf linux-image-$(uname -r)
sudo dpkg -x $(find . -type f -name "linux-image-$(uname -r)*.deb" | head -n 1) linux-image-$(uname -r)
sudo cp -af linux-image-$(uname -r)/lib mini/

然后生成模块依赖关系 ::

sudo chroot mini depmod

清理一下 ::

sudo chroot mini apt-get clean

现在整个 initramfs 已经准备好,可以打包,并 copy 到 boot 目录 ::

(cd mini; sudo find . | sudo cpio -o -H newc | sudo gzip -9 > ../initramfs-mini.gz)
sudo cp -f initramfs-mini.gz /boot

重启电脑,在 grub 目录处按 c 进入 cmdline ,用以下的命令引导(可用 tab 补全) ::

linux /vmlinux-<xxxxx>
initrd /initramfs-mini.gz
boot

boot 之后,会由 kernel 解压 initramfs-mini.gz ,然后很快进入到熟悉的 ubuntu 命令提示中。这个基本的 linux 能运行常见的操作,除了还缺少像 ping 之类的工具(可通过 apt-get 安装),直接在内存中执行,与平常使用无异(由于所有的文件都在内存中,加载命令的速度应该更加快一点)。

使用 busybox 替换基本命令并裁减

上面生成的小系统已经基本可用了,但如果还想继续减少体积,以供在内存较少的机器上运行,那么还可以继续进行裁减,最重要的步骤就是用 busybox 处理大部分的工作,甚至包括设备驱动的加载和热插拔。

在 minbase 的基础上安装 busybox-staic ::

sudo chroot mini apt-get -y --no-install-recommends install \
busybox-static

定义 apps 和 extra_apps 变量来保存 busybox 所支持的命令,定义函数用于用 bubybox 替换原来的命令 ::

applets=$(mini/bin/busybox --list)
apps=
for i in $applets; do
apps="$apps $(sudo chroot mini which $i)"
done
extra_apps=
for i in $applets; do
if ! sudo chroot mini which $i > /dev/null; then
extra_apps="$extra_apps /bin/$i "
fi
done function fix_missing() {
for i in $apps $extra_apps; do
if ! test -f mini/$i; then
sudo ln -sf /bin/busybox mini/$i
fi
done
}

然后,开始大扫除,清理可被直接删除的包 ::

sudo chroot mini apt-get -y --force-yes purge adduser busybox-initramfs \
cpio ifupdown initramfs-tools initscripts initramfs-tools-bin \
iproute2 locales mountall makedev plymouth procps upstart libprocps3 \
libcgmanager0 libusb-1.0-0 usbutils libdbus-1-3 libnih-dbus1 libdrm2 \
libjson-c2 libjson0 kmod libkmod2 module-init-tools file libmagic1 \
libnih1 libplymouth2 pciutils libudev1 udev
fix_missing

强制去掉不需要的包 ::

sudo chroot mini /bin/sh -c 'echo "Yes, do as I say!" |\
apt-get -y --force-yes purge diffutils findutils hostname'
fix_missing sudo chroot mini /bin/sh -c 'echo "Yes, do as I say!" |\
apt-get -y --force-yes purge -y --force-yes login libmount1 mount\
grep gzip sed mount'
fix_missing sudo chroot mini /bin/sh -c 'echo "Yes, do as I say!" |\
apt-get -y --force-yes purge -y --force-yes sysvinit-utils lsb-base\
e2fsprogs e2fslibs bsdutils libblkid1 libuuid1 passwd tzdata insserv'
fix_missing sudo chroot mini /bin/sh -c 'echo "Yes, do as I say!" |\
apt-get -y --force-yes purge ncurses-base ncurses-bin'
sudo chroot mini /bin/sh -c 'echo "Yes, do as I say!" |\
apt-get purge coreutils'
fix_missing

这里还可以继续清理,把一些不需要的 lib 都去掉。

然后清理 apt 的缓存 ::

sudo chroot mini apt-get clean
sudo rm -rf mini/usr/share/locale/*
sudo rm -rf mini/usr/share/man/*
sudo rm -rf mini/usr/share/doc/*
sudo rm -rf mini/var/log/*
sudo rm -rf mini/var/lib/apt/lists/*
sudo rm -rf mini/var/cache/*
sudo rm -rf mini/etc/rc*

由于在清理的过程中已经去掉了 udev 、 system-v init 、 upstart 等,需要一个支持 busybox 的新 init 脚本。在 ubuntu 里面原来的 udev 本身支持初始化加载驱动和设备插拔,但是 busybox 的 mdev 只在 hotplug 的时候调用,网上的很多资料也没有提到怎样在初始化时加载驱动模块,搜了一下 busybox 的邮件,找到了直接查找 /sys/devices 目录加载驱动模块的方式 http://lists.busybox.net/pipermail/busybox/2009-April/068894.html ::

cat > mini/init << EOF
#!/bin/sh
mount -t proc -o nodev,noexec,nosuid proc /proc
mount -t sysfs -o nodev,noexec,nosuid sysfs /sys
ln -sf /proc/mounts /etc/mtab
mount -t tmpfs -o size=64k,mode=0755 tmpfs /dev
[ -e /dev/console ] || mknod -m 0600 /dev/console c 5 1
[ -e /dev/null ] || mknod /dev/null c 1 3
mkdir -p /dev/pts
mount -t devpts -o noexec,nosuid,gid=5,mode=0620 devpts /dev/pts
mount -t tmpfs -o "noexec,nosuid,size=10%,mode=0755" tmpfs /run
echo "Loading modules..."
# hotplug with mdev
echo /bin/mdev > /proc/sys/kernel/hotplug
mdev -s
# coldplug: load devices supporting modules
find /sys/devices -name modalias | xargs -r cat | xargs -r modprobe -qa
# extra modules
[ -f /etc/modules ] && cat /etc/modules | grep -v "^[[:blank:]]#" |\
xargs -r modprobe -qa
exec /sbin/init
EOF
chmod +x mini/init

修改使用 busybox 的 init ::

ln -sf /bin/busybox mini/bin/sh
ln -sf /bin/busybox mini/sbin/init

busybox 的 init 需要一个 inittab 文件描述初始化过程 ::

cat > mini/etc/inittab << EOF
::sysinit:/etc/init.d/rcS
::ctrlaltdel:/sbin/reboot
::shutdown:/sbin/swapoff -a
::shutdown:/bin/umount -a -r
::restart:/sbin/init
tty1::respawn:/bin/sh
tty2::askfirst:/bin/sh
tty3::askfirst:/bin/sh
tty4::askfirst:/bin/sh
EOF touch mini/etc/init.d/rcS
chmod +x mini/etc/init.d/rcS

同样,加入 kernel 模块 ::

sudo apt-get download linux-image-$(uname -r)
sudo rm -rf linux-image-$(uname -r)
sudo dpkg -x $(find . -type f -name "linux-image-$(uname -r)*.deb" | head -n 1) linux-image-$(uname -r)
sudo cp -af linux-image-$(uname -r)/lib mini/
sudo chroot mini depmod

打包,并 copy 到 boot 目录 ::

(cd mini; sudo find . | sudo cpio -o -H newc | sudo gzip -9 > ../initramfs-mini.gz)
sudo cp -f initramfs-mini.gz /boot

重启,在 grub 目录处按 c 进入 cmdline ::

linux /vmlinux-<xxxxx>
initrd /initramfs-mini.gz
boot

在这一步,得到的整个目录的大小为 80 M 左右(包含 kernel 模块),但是还可以继续进行裁减。

深入裁减

du -hs * mini 查看整个目录,可看到目前占用空间最多的就是内核的模块目录了,看一下内核的模块目录,差不多占了一半的空间。

以下是内核模块的占用空间情况(mini/lib/modules/4.4.0-31-generic) ::

2.6M    arch
1.4M crypto
11M drivers
8.1M fs
1.8M lib
12M net
832K sound
508K ubuntu
20K virt

net 里面包括了 ceph 、netfilter 、openvswitch、atm(如果你只是一个普通程序员,可能你一辈子都不会碰到这货)、x2、appletalk 等等,都是可以删除的。

其它的看情况办,记得删完之后,重新检查依赖关系 ::

chroot mini depmod

mini/usr/lib 目录也很大,里面有些库是可以删的,像 mini/usr/lib/x86_64-linux-gnu/gconv 里面的一大堆编码库,看不过去的删掉一些。libdb-5.3.so 也很大,没有用 man 等是可以删掉的。

这个阶段删东西就不能用 apt-get 了,最多用一下 dpkg ,并且强制清理掉,最后可以把 apt 也清理了。到这个阶段就没有什么技术问题,只要有足够的细心和耐心,就能弄到一个足够小的系统。

其它

还有一些其它方法可进行 linux 的定制,如 LFS http://linuxfromscratch.org/ 、 buildroot https://buildroot.org/ 等。

如果想把这个小 linux 保存到硬盘中,也很好办。整个 mini copy 到一个单独的分区上,加载根目录所在的分区为根分区,在 init 脚本通过 switch_root 切换到 /sbin/init ,假如在 /sda3 ::

# 模块加载完成之后 ...
mount /dev/sda3 /mnt
mkdir -p /mnt/mnt
exec switch_root /mnt /sbin/init

需要注意的是,由于根分区切换, /proc /sys /dev /tmp 等目录需要进行额外的处理,要么把该目录用 mount -o move 到 mnt 下,或者在 mini/fstab 文件中定义挂载点。

基于 debootstrap 和 busybox 构建 mini ubuntu的更多相关文章

  1. 构建 ARM Linux 4.7.3 嵌入式开发环境 —— BusyBox 构建 RootFS

    上一篇我们已经成功将 ARM Linux 4.7.3 的内核利用 U-BOOT 引导了起来.但是细心的你会发现,引导到后面,系统无法启动,出现内核恐慌 (Kernel Panic). 原因是找不到文件 ...

  2. [Android] 基于 Linux 命令行构建 Android 应用(七):自动化构建

    本章将演示如何基于 Linux 命令行构建 Android 应用,在开始本章之前,希望你已经阅读之前几章内容. 本文环境为 RHEL Sandiego 32-bits,要基于 Linux CLI 构建 ...

  3. 使用busybox构建根文件系统

    当我们在Qemu上运行起来自己编译的内核之后,需要使用busybox构建一个文件系统,将此文件系统挂载上去就可以使用busybox提供的各种命令了. 1.编译安装busybox 源码下载地址:http ...

  4. 【Hades】ades是一个开源库,基于JPA和Spring构建,通过减少开发工作量显著的改进了数据访问层的实现

    几乎每个应用系统都需要通过访问数据来完成工作.要想使用领域设计方法,你就需要为实体类定义和构建资源库来实现领域对象的持久化.目前开发人员经常使用JPA来实现持久化库.JPA让持久化变得非常容易,但是仍 ...

  5. 10个基于 Ruby on Rails 构建的顶级站点

    本文系国内 ITOM 行业领军企业 OneAPM 工程师翻译整理自 Raviraj Hegde 的文章 Top Sites Built with Ruby on Rails. 就其本身而言,Ruby ...

  6. 基于Jmeter+maven+Jenkins构建性能自动化测试平台

      一.目的: 为能够将相关系统性能测试做为常规化测试任务执行,且可自动无人值守定时执行并输出性能测试结果报告及统计数据,因此基于Jmeter+maven+Jenkins构建了一套性能自动化测试平台 ...

  7. 基于 Webhooks gitlab 自动化构建

    基于gitlab webhooks 自动构建流程 1.服务器安装 git 服务 安装成功 配置 PHP 脚本: <?php // 接受头部信息 if (!isset($_GET['youpara ...

  8. 移植busybox构建最小根文件系统

    Busybox:瑞士军刀,里面装有很多小命令. STEP 1:构建目录结构  创建根文件系统目录,主要包括以下目录/dev  /etc /lib  /usr  /var /proc /tmp /hom ...

  9. 运用busybox构建最小根文件系统

    平台:vmware下ubuntu14.04前期准备:安装交叉编译环境arm-linux-gcc-4.5.1;下载完成BusyBox 1.23.2一.busybox构建1.make menuconfig ...

随机推荐

  1. FluentData(微型ORM)

    using FluentData; using System; using System.Collections.Generic; using System.Linq; using System.Te ...

  2. 分布式追踪系统dapper

    http://www.cnblogs.com/LBSer/p/3390852.html 最近单位需要做自己的分布式监控系统,因此看了一些资料,其中就有google的分布式追踪系统dapper的论文:h ...

  3. Spring Web MVC 多viewResolver视图解析器解决方案

    viewResolver的定义如下: public interface ViewResolver { View resolveViewName(String viewName, Locale loca ...

  4. CozyRSS开发记录7-了解RSS

    CozyRSS开发记录7-了解RSS 1.初窥RSS 多找几个RSS源就会发现,有的源是用Atom协议提供的,有的源是RSS协议提供的.RSS协议有过几个版本,0.9.1.0和2.0等,理论上支持2. ...

  5. Listview的使用

    最近一个多月忙着使用新的技术来做项目,现在项目上线了,嗯,发现android有些生疏了,所以今天特地写了这一篇博客来相信的讲解一些基础知识,同事呢,也可以让我温故知新一下.进入正题. 什么是listv ...

  6. JAVA Day9

    1.StringBuffer类 优点: 内存的管理! StringBuffer: String 增强版 StringBuffer sb = new StringBuffer(); StringBuff ...

  7. [开源]用MQL4实现MD5加密

    本文转载自博客园:混沌的世界 原文地址:http://www.cnblogs.com/niniwzw/archive/2009/12/05/1617685.html 在用MQL4进行金融交易的时候,经 ...

  8. Codeforces558E A Simple Task(线段树)

    题目 Source http://codeforces.com/problemset/problem/558/E Description This task is very simple. Given ...

  9. 从零开始山寨Caffe·肆:线程系统

    不精通多线程优化的程序员,不是好程序员,连码农都不是. ——并行计算时代掌握多线程的重要性 线程与操作系统 用户线程与内核线程 广义上线程分为用户线程和内核线程. 前者已经绝迹,它一般只存在于早期不支 ...

  10. tornado 学习笔记5 构建Tornado网站应用

    一个Tornado 网站应用通常由一个或多个RequestHanlde的子类.一个负责将请求路由至handlers的Application以及一个启动服务器的main()函数等组成. 一个最小的“he ...