基于 debootstrap 和 busybox 构建 mini ubuntu
基于 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 工具做这样的小系统会更加简单,有以下的优点:
- debootstrap 生成的小 ubuntu 能方便使用 apt 安装额外的工具
- 可直接把驱动模块拷贝到小镜像内使用
- 定制脚本非常简单容易
整个小系统在不安装额外的软件和内核模块的情况下,为 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的更多相关文章
- 构建 ARM Linux 4.7.3 嵌入式开发环境 —— BusyBox 构建 RootFS
上一篇我们已经成功将 ARM Linux 4.7.3 的内核利用 U-BOOT 引导了起来.但是细心的你会发现,引导到后面,系统无法启动,出现内核恐慌 (Kernel Panic). 原因是找不到文件 ...
- [Android] 基于 Linux 命令行构建 Android 应用(七):自动化构建
本章将演示如何基于 Linux 命令行构建 Android 应用,在开始本章之前,希望你已经阅读之前几章内容. 本文环境为 RHEL Sandiego 32-bits,要基于 Linux CLI 构建 ...
- 使用busybox构建根文件系统
当我们在Qemu上运行起来自己编译的内核之后,需要使用busybox构建一个文件系统,将此文件系统挂载上去就可以使用busybox提供的各种命令了. 1.编译安装busybox 源码下载地址:http ...
- 【Hades】ades是一个开源库,基于JPA和Spring构建,通过减少开发工作量显著的改进了数据访问层的实现
几乎每个应用系统都需要通过访问数据来完成工作.要想使用领域设计方法,你就需要为实体类定义和构建资源库来实现领域对象的持久化.目前开发人员经常使用JPA来实现持久化库.JPA让持久化变得非常容易,但是仍 ...
- 10个基于 Ruby on Rails 构建的顶级站点
本文系国内 ITOM 行业领军企业 OneAPM 工程师翻译整理自 Raviraj Hegde 的文章 Top Sites Built with Ruby on Rails. 就其本身而言,Ruby ...
- 基于Jmeter+maven+Jenkins构建性能自动化测试平台
一.目的: 为能够将相关系统性能测试做为常规化测试任务执行,且可自动无人值守定时执行并输出性能测试结果报告及统计数据,因此基于Jmeter+maven+Jenkins构建了一套性能自动化测试平台 ...
- 基于 Webhooks gitlab 自动化构建
基于gitlab webhooks 自动构建流程 1.服务器安装 git 服务 安装成功 配置 PHP 脚本: <?php // 接受头部信息 if (!isset($_GET['youpara ...
- 移植busybox构建最小根文件系统
Busybox:瑞士军刀,里面装有很多小命令. STEP 1:构建目录结构 创建根文件系统目录,主要包括以下目录/dev /etc /lib /usr /var /proc /tmp /hom ...
- 运用busybox构建最小根文件系统
平台:vmware下ubuntu14.04前期准备:安装交叉编译环境arm-linux-gcc-4.5.1;下载完成BusyBox 1.23.2一.busybox构建1.make menuconfig ...
随机推荐
- FluentData(微型ORM)
using FluentData; using System; using System.Collections.Generic; using System.Linq; using System.Te ...
- 分布式追踪系统dapper
http://www.cnblogs.com/LBSer/p/3390852.html 最近单位需要做自己的分布式监控系统,因此看了一些资料,其中就有google的分布式追踪系统dapper的论文:h ...
- Spring Web MVC 多viewResolver视图解析器解决方案
viewResolver的定义如下: public interface ViewResolver { View resolveViewName(String viewName, Locale loca ...
- CozyRSS开发记录7-了解RSS
CozyRSS开发记录7-了解RSS 1.初窥RSS 多找几个RSS源就会发现,有的源是用Atom协议提供的,有的源是RSS协议提供的.RSS协议有过几个版本,0.9.1.0和2.0等,理论上支持2. ...
- Listview的使用
最近一个多月忙着使用新的技术来做项目,现在项目上线了,嗯,发现android有些生疏了,所以今天特地写了这一篇博客来相信的讲解一些基础知识,同事呢,也可以让我温故知新一下.进入正题. 什么是listv ...
- JAVA Day9
1.StringBuffer类 优点: 内存的管理! StringBuffer: String 增强版 StringBuffer sb = new StringBuffer(); StringBuff ...
- [开源]用MQL4实现MD5加密
本文转载自博客园:混沌的世界 原文地址:http://www.cnblogs.com/niniwzw/archive/2009/12/05/1617685.html 在用MQL4进行金融交易的时候,经 ...
- Codeforces558E A Simple Task(线段树)
题目 Source http://codeforces.com/problemset/problem/558/E Description This task is very simple. Given ...
- 从零开始山寨Caffe·肆:线程系统
不精通多线程优化的程序员,不是好程序员,连码农都不是. ——并行计算时代掌握多线程的重要性 线程与操作系统 用户线程与内核线程 广义上线程分为用户线程和内核线程. 前者已经绝迹,它一般只存在于早期不支 ...
- tornado 学习笔记5 构建Tornado网站应用
一个Tornado 网站应用通常由一个或多个RequestHanlde的子类.一个负责将请求路由至handlers的Application以及一个启动服务器的main()函数等组成. 一个最小的“he ...