使用 systemd-nspawn
这个命令我们可以很方便的创建一个 Linux 容器,需要的只是一个使用 systemd 作为 init 的 Linux 发行版的根文件系统。通过创建容器,我们可以获得一个可以随便折腾而不用担心损坏的 Linux 环境。这里用 Ubuntu 16.04 和 CentOS 7 为例,整个过程可以说是非常简单(虽然比起 Docker 还是麻烦了点)
对于 Ubuntu,可以直接从源里下载到它的根文件系统。下载一份,并解压到 /var/lib/machines/ubuntu1604
:
1
2
3
|
sudo mkdir -p /var/lib/machines/ubuntu1604
wget http://mirrors.ustc.edu.cn/ubuntu-cdimage/ubuntu-base/releases/16.04.3/release/ubuntu-base-16.04.1-base-amd64.tar.gz -O /tmp/rootfs.tgz
sudo tar xpzf /tmp/rootfs.tgz -C /var/lib/machines/ubuntu1604
|
OK,到此为止我们就得到了一个可以被 systemd-nspawn
启动的 rootfs,不过我们还需要一些配置,例如修改 root 密码等等:
1
2
|
chroot /var/lib/machines/ubuntu1604 /usr/bin/passwd root
echo ubuntu > /var/lib/machines/ubuntu1604/etc/hostname
|
下面只需要用 systemd-nspawn
来“启动”这个容器:
1
|
systemd-nspawn -b -D /var/lib/machines/ubuntu1604 --bind=/lib/firmware
|
这样就完成了!相当简单吧~输出内容大概是这样:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
|
Spawning container ubuntu1604 on /var/lib/machines/ubuntu1604.
Press ^] three times within 1s to kill container.
systemd 229 running in system mode. (+PAM +AUDIT +SELINUX +IMA +APPARMOR +SMACK +SYSVINIT +UTMP +LIBCRYPTSETUP +GCRYPT +GNUTLS +ACL +XZ -LZ4 +SECCOMP +BLKID +ELFUTILS +KMOD -IDN)
Detected virtualization systemd-nspawn.
Detected architecture x86-64.
Welcome to Ubuntu 16.04.1 LTS!
Set hostname to <ubuntu>.
Failed to install release agent, ignoring: No such file or directory
[ OK ] Listening on Journal Socket (/dev/log).
[ OK ] Started Dispatch Password Requests to Console Directory Watch.
[ OK ] Started Forward Password Requests to Wall Directory Watch.
[ OK ] Reached target Paths.
[ OK ] Reached target Remote File Systems (Pre).
[ OK ] Reached target Remote File Systems.
[ OK ] Listening on /dev/initctl Compatibility Named Pipe.
[ OK ] Created slice System Slice.
[ OK ] Reached target Slices.
[ OK ] Created slice system-getty.slice.
[ OK ] Reached target Swap.
[ OK ] Reached target Encrypted Volumes.
[ OK ] Listening on Journal Socket.
Mounting Huge Pages File System...
[ OK ] Reached target Sockets.
Starting Remount Root and Kernel File Systems...
Mounting POSIX Message Queue File System...
Starting Journal Service...
Mounting FUSE Control File System...
[ OK ] Mounted POSIX Message Queue File System.
[ OK ] Mounted Huge Pages File System.
[ OK ] Mounted FUSE Control File System.
[ OK ] Started Remount Root and Kernel File Systems.
[ OK ] Reached target Local File Systems (Pre).
[ OK ] Reached target Local File Systems.
Starting Load/Save Random Seed...
[ OK ] Started Load/Save Random Seed.
[ OK ] Started Journal Service.
Starting Flush Journal to Persistent Storage...
[ OK ] Started Flush Journal to Persistent Storage.
Starting Create Volatile Files and Directories...
[ OK ] Started Create Volatile Files and Directories.
Starting Update UTMP about System Boot/Shutdown...
[ OK ] Reached target System Time Synchronized.
[ OK ] Started Update UTMP about System Boot/Shutdown.
[ OK ] Reached target System Initialization.
[ OK ] Started Daily Cleanup of Temporary Directories.
[ OK ] Reached target Basic System.
Starting Permit User Sessions...
Starting LSB: Set the CPU Frequency Scaling governor to "ondemand"...
Starting /etc/rc.local Compatibility...
[ OK ] Started Daily apt activities.
[ OK ] Reached target Timers.
[ OK ] Started Permit User Sessions.
[ OK ] Started /etc/rc.local Compatibility.
[ OK ] Started Console Getty.
[ OK ] Reached target Login Prompts.
[ OK ] Started LSB: Set the CPU Frequency Scaling governor to "ondemand".
[ OK ] Reached target Multi-User System.
[ OK ] Reached target Graphical Interface.
Starting Update UTMP about System Runlevel Changes...
[ OK ] Started Update UTMP about System Runlevel Changes.
Ubuntu 16.04.1 LTS ubuntu console
ubuntu login:
|
值得注意的是,这个容器和虽然看起来很像那么一回事儿,但是它的内核和网络等仍然是使用宿主机的。
如果我们想让这个容器拥有独立的网络呢?其实也很简单,只要给它一个桥接的接口就行了。首先我们需要准备一个桥接接口,此处以 netctl
为例,准备好配置文件:
1
2
3
4
5
|
Description="A bridge connection"
Interface=br0
Connection=bridge
BindsToInterfaces=(enp1s0 enp2s0 enp3s0 enp4s0)
IP=dhcp
|
这样就是将 enp1s0 ~ enp4s0 这四张网卡桥接,形成 br0 接口,并通过 DHCP 获得 IP 地址。
然后微调启动命令,增加一个命令 --network-bridge=br0
即可,例如:
1
|
systemd-nspawn -b -D /var/lib/machines/ubuntu1604 --bind=/lib/firmware --network-bridge=br0
|
启动完成后,通过 ip link
命令就能看到接口了,我这里看到的是 host0@if9
。之后再按照对应的发行版修改一下网络配置,就完成了。
如果不想每次都通过这么长的命令来启动容器,我们可以使用 systemd
提供的 machinectl
命令来完成容器的开启与关闭,比如:
1
2
3
|
machinectl start ubuntu1604
machinectl login ubuntu1604
machinectl stop ubuntu1604
|
如果需要添加额外的参数,可以前往 /etc/systemd/nspawn
目录创建一个同名文件,例如 ubuntu1604.nspawn
,在其中写上这个容器的配置,例如:
1
2
3
4
5
6
7
8
9
10
|
[Exec]
Boot=true
[Files]
Bind=/lib/firmware:/lib/firmware
[Network]
VirtualEthernet=yes
Private=yes
Bridge=br0
|
以上就是比较完整的容器创建和使用过程啦~
CentOS 稍微复杂一点,因为他没有直接提供最小的 rootfs,我们要自己从 ISO 中解压安装,整个过程如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
|
# 切换到超级用户
sudo -s
# 建立一些文件夹
mkdir -p /var/lib/machines/centos7
mkdir -p /tmp/iso
mkdir -p /tmp/squashfs
mkdir -p /tmp/rootfs
# 挂载 CentOS 7 的系统盘
mount /mnt/Disk2/OS/Linux/CentOS-7-x86_64-Minimal-1708.iso /tmp/iso
mount /tmp/iso/LiveOS/squashfs.img /tmp/squashfs
mount /tmp/squashfs/LiveOS/rootfs.img /tmp/rootfs
# 复制系统文件,速度看你的硬盘,可能会比较慢_(:з」∠)_
cp -pr /tmp/rootfs/* /var/lib/machines/centos7
# 卸载一些不会再用到的镜像
umount /tmp/{rootfs,squashfs}
# 安装一下 yum
mkdir -p /var/lib/machines/centos7/mnt/iso
mount --bind /tmp/iso /var/lib/machines/centos7/mnt/iso
chroot /var/lib/machines/centos7 /usr/bin/rpm -ivh --nodeps /mnt/iso/Packages/rpm-4.11.3-25.el7.x86_64.rpm
chroot /var/lib/machines/centos7 /usr/bin/rpm -ivh --nodeps /mnt/iso/Packages/yum-3.4.3-154.el7.centos.noarch.rpm
# 配置一下基本系统,执行最小安装
echo "[cdrom]
name=Install CD-ROM
baseurl=file:///mnt/iso
enabled=0
gpgcheck=1
gpgkey=file:///mnt/iso/RPM-GPG-KEY-CentOS-7" > /var/lib/machines/centos7/etc/yum.repos.d/cdrom.repo
chroot /var/lib/machines/centos7 /usr/bin/yum --disablerepo=\* --enablerepo=cdrom -y reinstall yum
chroot /var/lib/machines/centos7 /usr/bin/yum --disablerepo=\* --enablerepo=cdrom -y groupinstall "Minimal Install"
# 删掉 ISO 源
rm /var/lib/machines/centos7/etc/yum.repos.d/cdrom.repo
# 卸载 ISO
umount /var/lib/machines/centos7/mnt/iso /tmp/iso
# 设置一下 root 密码之类的
chroot /var/lib/machines/centos7 /usr/bin/passwd root
# 进入虚拟环境,执行这段脚本
systemd-nspawn -D /var/lib/machines/centos7 --bind=/lib/firmware << _END_POSTINSTALL_
# 换源,先备份
mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup
curl https://lug.ustc.edu.cn/wiki/_export/code/mirrors/help/centos\?codeblock=3 > /etc/yum.repos.d/CentOS-Base.repo
# 更新
yum makecache
# 安装一个缺失的依赖,为什么会缺我也不知道…
yum install lvm2-libs -y
# 这些服务是作为一个容器不需要的,把他们关掉
systemctl disable auditd.service
systemctl disable kdump.service
systemctl disable multipathd.service
systemctl disable network.service
systemctl disable smartd.service
systemctl disable lvm2-monitor.service
systemctl disable sshd.service
# 设置 locale,如果需要中文就用 zh_CN.UTF-8
echo LANG=en_US.UTF-8 > /etc/locale.conf
# 设置主机名
echo CentOS-7 > /etc/hostname
_END_POSTINSTALL_
# 然后就可以开机了(((
systemd-nspawn -b -D /var/lib/machines/centos7 --bind=/lib/firmware
|
参考资料: