本精神病人又来闹妖啦!
自从有了这种容器化技术之后,想要做测试,构建点什么,或者简单的体验下某个Linux Distros的cli就变的无比简单:
-<255:%>- docker run --rm -it ubuntu:20.04 bash Unable to find image 'ubuntu:20.04' locally 20.04: Pulling from library/ubuntu 7b1a6ab2e44d: Already exists Digest: sha256:626ffe58f6e7566e00254b638eb7e0f3b11d4da9675088f4781a50ae288f3322 Status: Downloaded newer image for ubuntu:20.04 root@aa28b038528e:/# lscpu Architecture: x86_64 CPU op-mode(s): 32-bit, 64-bit Byte Order: Little Endian
我们也可以在容器里一顿操作,然后构建出来一个东西拿来用,比如之前在编译LEDE固件时,我就是这么玩的。
某一天,我突然接到这样一个需求:使用pyinstaller把Python脚本编译成不同平台的二进制文件,并且要尽可能跑在容器内。这怎么做?
- 准备Windows macOS和Linux的机器,编译然后拿文件。太麻烦了
- pyinstaller 交叉编译?不好意思pyinstaller不支持交叉编译,更准确的说是1.5版本移除了这个功能
- Linux container跑一个wine,wine里跑Windows版本的Python?逆向工程真的靠谱嘛?会出现很多奇奇怪怪的bug吧
- 在docker里想办法跑个Windows?哎嘿?
当你兴致勃勃的打开Docker Hub,搜索windows发现竟然有Windows的image,Windows base OS images,Windows Server Core,不管了先跑为敬:
-<130:%>- docker run mcr.microsoft.com/windows:20H2 Unable to find image 'mcr.microsoft.com/windows:20H2' locally 20H2: Pulling from windows docker: no matching manifest for linux/amd64 in the manifest list entries. See 'docker run --help'.
docker其实是使用了Linux内核的cgroup等技术来实现的,容器还是使用的宿主机内核,容器的进程其实就是跑在宿主机上的,宿主机还能看见只不过有一定隔离。Windows并不用Linux Kernel,那跑个什么嘛。上面这些image的OS Arch其实也是windows/amd64的。
可能更需要的是这个,确实可以跑Windows container,只不过需要Windows host啦。并且1709的Windows只能跑1709的image
这不就又回到了开头嘛。
突然想起来有一个很神奇的项目Docker-OSX,其实现,简单说是用KVM跑了个macOS的虚拟机,就像黑苹果一样。那么理论上Windows也一定能找到办法做到。那么就上qemu!不行我们就软件模拟!
还有一个有趣的东西叫dind – Docker In Docker。
套娃嘛,我最强。
运行环境检查
无论是VM,还是物理机其实都可以。
KVM支持
如果能有KVM支持的话,虚拟机应该会跑的更流畅。
如果你是VM,CPU支持Intel VT-x/ AMD-v等,那么在虚拟化软件的设置中就可以看到
ESXi是这样的
Fusion是这样的
勾选之后, ls /dev/kvm
就应该有结果啦
容器启用 privileged mode
docker run --privileged xxx
即可,主要是为了能够在容器内访问宿主机的/dev/kvm
。当然也可以用--device
在容器内准备qemu一箩筐
以Ubuntu 20.04为例
apt update && apt install qemu-kvm
准备ISO
以alpine为例,方便测试
wget https://dl-cdn.alpinelinux.org/alpine/v3.15/releases/x86_64/alpine-virt-3.15.0-x86_64.iso
创建vm并启动
为了PoC,我们直接创建vm然后启动试试看,-nographic
因为我们在CLI下,所以不启用图形界面,-enable-kvm
因为宿主机有kvm支持,-cdrom
指定光驱
qemu-system-x86_64 -nographic -enable-kvm -cdrom /root/alpine-virt-3.15.0-x86_64.iso
意料之中,出现了Alpine的启动画面
并且能看到qemu默认配置的内存和CPU。这个QEMU Virtual CPU是不是在某些VPS中看到过呢?
想要退出qemu,先按ctrl a,然后松开,迅速按x
使用VNC
既然无图形界面的alpine可以,那么安装个Ubuntu Desktop也一定可以。
这种情况下就需要我们使用VNC啦。
VNC的默认端口是5900,我们要在启动容器的时候同时publish这个端口,方便我们使用VNC客户端
docker run --rm --privileged -p 5900:5900 -it -v $(pwd):/root/ bennythink/vmid bash
由于Ubuntu Desktop需要比较多的内存和CPU,因此我们这里也多加几个参数
qemu-system-x86_64 -nographic -enable-kvm -m 4096 -cpu host -smp 2 -cdrom /root/ubuntu-20.04.3-desktop-amd64.iso.iso -vnc 0.0.0.0:0
-m
表示内存大小,单位是MiB,也可以 -m 2g
表示2GiB的内存
-cpu
表示CPU类型,之前上面我们看到CPU是QEMU Virtual CPU,在这里我们可以指定CPU,比如 -cpu EPYC
表示虚拟机看到的CPU是AMD EPYC,当然也要审时度势,你一个x86-64的image,就别想着用486了。有几个特殊的值:host
表示使用和宿主机一样的CPU,base的话比较鸡肋顾名思义就是基础的什么指令集都没有,max会把所有的支持的指令集和kvm能提供的指令集都加上。
-smp
就是几核心的CPU啦,这要看宿主机量力而行。就像make时要量力而行一样
-vnc
表示使用VNC,0.0.0.0表示监听0.0.0.0,冒号后的0表示5900,如果用0.0.0.0:1
,那么监听的端口就是5901啦,要注意哦。这种情况下开启的VNC是没有密码的,如果想要密码那就 -vnc 0.0.0.0:0,password
然后设置密码…… 比较复杂,建议放弃。
然后使用VNC客户端连接即可,要注意macOS自带的VNC客户端需要密码,但是我们并没有设置密码,因此是用不了的。使用VNC viewer可破
brew install --cask vnc-viewer
我们已经能够看到Ubuntu的安装界面啦,并且在这台VM中,网络也是通的,QEMU分配了一个10段IP.如果你发现不能ping,那么在host中 sysctl -w net.ipv4.ping_group_range='0 2147483647'
就可以啦。
分配磁盘
像往常使用虚拟机一样,我们也需要为虚拟机分配一个虚拟磁盘,这样才能够安装操作系统。总不能每次都从光盘启动吧。分配虚拟磁盘也有很多讲究,比如10G是一下子都分配了,还是用多少分配多少?
qemu-img create -f qcow2 /root/test.qcow2 16G
-f 表示虚拟磁盘的格式,支持raw和qcow2,raw顾名思义就是原始的,对vm来说就是一个块设备。如果vm文件系统支持空洞,那么raw是一点点填满的,raw性能较好。qcow2是qemu推荐的格式,支持加密快照等等,别想了就这个吧。当然qemu还支持vmdk、vdi之类的。
需要注意的是,别给磁盘撑爆了。
安装Ubuntu Desktop
创建好磁盘之后,使用如下命令即可开始开vm安装Ubuntu
qemu-system-x86_64 -nographic -enable-kvm -m 4096 -cpu host -smp 4 -drive file=/root/test.qcow2 -cdrom /root/ubuntu-20.04.3-desktop-amd64.iso -vnc 0.0.0.0:0
安装过程和普通VM没什么差别,就是…… 慢了点。
启动VM
安装好之后,下次我们可以直接启动VM了,可以删掉-cdrom参数
qemu-system-x86_64 -nographic -enable-kvm -m 4096 -cpu host -smp 4 -drive file=/root/test.qcow2 -vnc 0.0.0.0:0
跑arm……
都知道qemu是个模拟神器,想要跑arm也不是不可能,比如跑个liveCD做个测试什么的:
apt install qemu-system-arm dd if=/dev/zero of=flash0.img bs=1M count=64 dd if=/usr/share/qemu-efi/QEMU_EFI.fd of=flash0.img conv=notrunc dd if=/dev/zero of=flash1.img bs=1M count=64 qemu-system-aarch64 -m 1024 -cpu cortex-a57 -M virt -nographic -pflash flash0.img -pflash flash1.img -drive if=none,file=alpine-virt-3.15.0-aarch64.iso,id=hd0 -device virtio-blk-device,drive=hd0 -net nic -net user
这次启动的速度就会非常慢了,如果启动成功之后没有网络,那么要开下dhcp,以alpine为例
vi /etc/network/interfaces auto lo iface lo inet loopback auto eth0 iface eth0 inet dhcp
然后ifup eth0
即可
如果想要安装到某个磁盘中,拿Ubuntu为例
qemu-img create -f qcow2 ubuntu.qcow2 8G mount -o loop ubuntu-20.04.3-live-server-arm64.iso /mnt cp /mnt/casper/vmlinuz ./ cp /mnt/casper/initrd ./ qemu-system-aarch64 -m 1024 -cpu cortex-a57 -smp cpus=4 -M virt -nographic \ -kernel ./vmlinuz -initrd ./initrd \ -drive file=ubuntu.qcow2,if=none,format=qcow2,id=hd0 -device virtio-blk-device,drive=hd0 \ -drive file=ubuntu-20.04.3-live-server-arm64.iso,if=none,format=raw,id=hd1 -device virtio-blk-device,drive=hd1 \ -net nic -net user
之后开始正常的安装步骤,会比较慢,而且电脑起飞。
安装好就可以去掉iso那行单独启动了。
实际上,Docker 的Multi Arch build也是恰巧利用了QEMU啦。
binfmt
如果只是简单的想要跑下异构的image的话,可以试试binfmt,Windows和Mac的Docker Desktop直接支持,Linux需要先这样:
docker run --rm --privileged docker/binfmt:66f9012c56a8316f9244ffd7622d7c21c1f6f28d
然后跑吧
docker run -it --platform linux/arm64 alpine sh
使用已有的cloud image
我们当然可以选择把OS安装到虚拟磁盘中,但是这样要麻烦一些,安装过程比较繁琐而且很慢。有没有办法用别人已经准备好的qcow2呢?
当然有啦。对于Ubuntu来说,可以去下载cloud image,img结尾的就是 https://cloud-images.ubuntu.com/focal/current/
然后我们需要创建一个user data来初始化我们的密码
cat >user-data <<EOF #cloud-config password: 123456 chpasswd: { expire: False } ssh_pwauth: True EOF cloud-localds user-data.img user-data qemu-system-x86_64 -nographic -enable-kvm -m 2048 -cpu host -smp 4 -drive file=/root/focal-server-cloudimg-amd64.img -drive file=user-data.img,format=raw
等待启动,用户名ubuntu密码123456
之后你的所有更改实际上都会写入到这个img文件中,下次启动的时候就不用user-data啦。
那这个img只有2G,不够用怎么办?qemu-img
扩容!
root@e769eaf9e4ab:~# qemu-img resize focal-server-cloudimg-amd64.img +5G Image resized. root@e769eaf9e4ab:~# qemu-img info focal-server-cloudimg-amd64.img image: focal-server-cloudimg-amd64.img file format: qcow2 virtual size: 7.2 GiB (7730102272 bytes) disk size: 601 MiB cluster_size: 65536 Format specific information: compat: 0.10 refcount bits: 16
然后…… 如果有LVM,那就好办了,如果没LVM,那就liveCD扩容吧。所以建议一开始就qemu-img resize
好。
参考资料
https://github.com/kholia/OSX-KVM
https://blog.ihomura.cn/2020/11/12/%E5%9C%A8qemu-system%E4%B8%8A%E8%B7%91arm-Debian/
https://hub.docker.com/repository/docker/bennythink/vmid