@Tmacy
2016-08-23T07:00:19.000000Z
字数 18492
阅读 1654
linux
systemd
以下是systemd系列博客的翻译,博文为systemd的作者Lennart Poettering 所写,本译文仅供学习使用。
附录中有其他学习资料供参考
现代计算技术一个很重要的方面是对资源的管理。如果里在单一设备上运行超过一个程序,你就会需要给他们一个资源分配的策略。这一般在小型的嵌入式系统上用的很多,这些设备的资源是主要制约因素。而那些大的设备,例如云架构系统,虽然有很多资源,但是在单节点上运行的程序/服务/容器已经大幅提高了。
Linux系统上一直都只有一个分配资源的策略:所有的进程获得同样的CPU时间,或者IO带宽。只通过优先级机制进行少量的调度。这个方法很简单,在Linux上使用了相当长的时间。然而,它的缺点是:不是所有的进程都需要这些资源,有的服务也会涉及很多进程(考虑以下Apache,带有很多CGI进程)。这种方式会让进程得到过多的资源,而服务却很少(例如syslog)
当为systemd考虑关于服务的管理时,我们很快意识到资源的管理是一个核心功能。现代世界上--无论是服务器还是嵌入式--控制CPU,内存以及IO资源的各种服务不能被忽视,一定是重中之重。 传统的优先级一定是要为每个服务而不是每个进程,或者如POSIX资源限制。
在这里,我们想分享一下对systemd服务的资源管理策略。在systemd中,资源管理已经以各种方式存在了一段时间,这里我们为大家做个统一的介绍。
在前面的博文中,我强调了使用标记与层级分组机制的Linux Control Cgroups
与作为资源控制子系统的Linux cgroups的不同。然而systemd依赖的是前者,后者是可选的。并且这个可选的后者现在被我们用来管理每个服务的资源(这里最好先看一下cgroups的内容,了解其最基本的概念以及如何实现的。下面介绍的会比较深,了解一点背景知识会很有帮助)
Linux Cgroup 控制资源管理主要是针对cpu,内存以及blkio。与大部分发行版一样,这个功能需要在内核中激活。systemd暴露出一些高级的服务设置来使用这些控制器,而不需要学习内核的细节。
默认的情况下,如果cpu控制器在内核中默认激活,systemd会在每个服务启动时为其创建一个cgroup。无需配置它就具备了一个很好的效果:在systemd系统中每个系统服务会平均分配CPU,无论该服务有多少进程。换句话说,在你的web服务器MYSQL会得到与apache相同的CPU数量,即使后者会包含1000各CGI进程,而前者仅有几个工作任务。(这个行为可以关闭,查看在/etc/systemd/system.conf中的DefaultControllers=)
在这种情况下,可以利用CPUShares=的设置来精确配置CPU对于一个服务的资源。默认的值时1024,如果你增长这个值,就会为这个服务分配更多的cpu,如果减少,则分配更少的cpu。
让我们看下细节,如何来利用这个。我们想声明Apache 1500 CPU而不是默认的1024。为了实现这个,我们要在/etc/systemd/system/httpd.service创建一个新的服务,来取代/usr/lib/systemd/system/httpd.service,我们可以修改配置:
.include /usr/lib/systemd/system/httpd.service
[Service]
CPUShares=1500
在第一行会加载服务文件。现在让我们重新加载systemd的配置,重启Apache的服务:
systemctl daemon-reload
systemctl restart httpd.service
恩,现在已经完成。
(注意设置CPUSares=会使指定的服务在cpu层次上获得属于自己的cgroup,即使cpu不包括在DefaultControllers=里面)
当然,修改资源分配而不了解服务对资源的使用情况就像是无头苍蝇。为了帮助你了解所有服务的资源使用,我们创建了systemd-cgtop工具。它会列举系统中所有的cgroup,查明他们的资源使用情况(CPU,内存和IO)并且将他们按照top的方式来显示。由于systemd的服务是通过cgroups来管理的,这个工具可以告诉你那些服务正为你工作。
不幸的是,默认情况下cgtop只会列出CPU的使用情况,IO和内存只能显示整个系统的信息。原因很简单,默认是没有每个服务的cgroups在blkio与内存控制器上的层级。最好的方式是为所有的服务加上内存与blkio控制器,在system.conf的DefaultControllers=设置。
为了强制限制内存,systemd 提供了MemoryLimit=和MemorySoftLimit=来控制所有进程的内存。这个设置限制了服务的全部内存,单位是bytes。这个设置可以使用常见的K,M,G,T等单位符号:
.include /usr/lib/systemd/system/httpd.service
[Service]
MemoryLimit=1
(类似之前提到的CPUShares=,这个选项会影响服务获得其cgroup的内存cgroup层级)
为了控制块IO,systemd提供了多重设置。首先 BlockIOWeight=会为指定的服务分配一个IO权重。这个权重的行为与CPU的资源控制有所不同。默认的权重是1000,范围是从10到1000:
.include /usr/lib/systemd/system/httpd.service
[Service]
BlockIOWeight=500
可以选择指定设备:
.include /usr/lib/systemd/system/httpd.service
[Service]
BlockIOWeight=/dev/disk/by-id/ata-SAMSUNG_MMCRE28G8MXP-0VBL1_DC06K01009SE009B5252 750
除了指定一个具体的设备节点,你也可以指定文件系统中的任意路径:
.include /usr/lib/systemd/system/httpd.service
[Service]
BlockIOWeight=/home/lennart 750
如果指定的路径没有引用到一个设备节点,systemd会查明块设备/home/lennart挂载何处,并且为其分配权重。
另外,可以利用BlockIOReadBandwidth=和BlockIOWriteBandwidth=设置来限制带宽。这些设置控制一个对设备节点与带宽速率或者一对文件路径与带宽速率:
.include /usr/lib/systemd/system/httpd.service
[Service]
BlockIOReadBandwith=/var/log 5M
以上设置了块设备的从/var/log读的最大读带宽是5Mb/s。
以上描述的只是资源控制的一小部分。我们选择这些并增加了高级选项,是因为我们任务对大部分人来说,真的需要一个好的界面来妥善处理单元以及块设备的名字。
在很多例子中,这些选项可能并不满足你的需求,可能一个低级的内核cgroup设置会很好用。而使用低级设置也是很简单的。例如有时候可能需要为一个服务设置swappiness。内核通过内存的.swappiness cgroup属性设置,但是在systemd中,没有提供一个高级的选项,你可以使用低级设置ControlGroupAttribute= :
.include /usr/lib/systemd/system/httpd.service
[Service]
ControlGroupAttribute=memory.swappiness 70
(与其他例子一样,这会导致服务增加了内存层级)
注意:使用资源控制会在系统运行时产成作用。限制资源时要付出代价的。如果你使用他们,请慎重考虑。尤其是在内存方面的限制。
当我们开始启动systemd这个项目时,我们已经仔细审视过各种Linux的启动脚本所作的事情。除了其他我们已经注意到的内容外,我们发现有一些脚本会检查是否运行在一个虚拟环境中。有些启动脚本会禁用这些,有些不会。经常性的,更好的方式时检查其他条件而不是虚拟化,但看到这些后,我们得到的结论是,在很多情况下基于检测虚拟化的限制条件对服务来说是有效的。最后我们增加了一个新的配置项来设置服务的限制性条件:ConditionVirtualization,我们还有一个小的工具可以用在shell脚本中来检测虚拟化:systemd-detect-virt,最后,我们增加了一个迷你总线接口从其他应用中查询这个。
测试你的代码是否运行在一个虚拟环境中实际上并不难。根据你像检测什么,不只是简单运行CPUID指令,并且可能会检查一些在/sys和/proc的文件。其复杂性大部分只是要查找字符串,以及保持这个列表最新。当前,systemd的虚拟化的检测代码可以检测到一下环境:
增加ConditionVirtualization到[Unit]中就可以在一个服务中判断是否使用了虚拟化:
[Unit]
Name=My Foobar Service (runs only only on guests)
ConditionVirtualization=yes
[Service]
ExecStart=/usr/bin/foobard
如果指定的参数不是yes或no,可以指定一个特定的虚拟化方案名称(例如"kvm","vmware",)或者“container”,"vm"来检查内核是否启用容器虚拟化或者硬件虚拟化。并且,参数可以加前缀“!”来禁止某些选项。更多详细内容查看手册。
在shell脚本中很容易就可以检查虚拟化系统。使用systemd-detect-virt工具,如下:
if systemd-detect-virt -q ; then
echo "Virtualization is used:" `systemd-detect-virt`
else
echo "No virtualization is used."
fi
如果这个工具的返回代码是0(成功),说明找到了虚拟化方案,不是0则相反。使用参数-q,它会打印一个简单的虚拟化方案的标识。使用-c与-v参数,它只会检测内核虚拟化或者硬件虚拟化。更多细节查看用户手册。
是否启用了虚拟化,可以通过systemd暴露给系统总线的接口得知:
gdbus call --system --dest org.freedesktop.systemd1 --object-path /org/freedesktop/systemd1 --method org.freedesktop.DBus.Properties.Get org.freedesktop.systemd1.Manager Virtualization
(<'systemd-nspawn'>,)
如果没有虚拟化方案检测到,会返回一个空字符串。注意一些容器环境无法被w无权限的代码直接检测到。这就是为啥我们暴露这个基于总线的特性,而不是提供一个库——总线会很好的处理权限问题。
注意,所有的方法只会检测并返回最深层的虚拟化方案。如果你有多层虚拟化,这个接口只会暴露最直接接触的那一层。这意味着,如果一个虚拟机里运行了一个容器,那么只有容器会被检测到。
套接字激活是systemd中一个重要的特性。当我们第一次推出systemd时,我们已经把重点放在如何使用套接字激活来增加socket服务的并行能力以及稳定性,并且要简化引导的逻辑复杂度。这一节我会解释一下为什么套接字激活对极大提高在单个系统中运行大量服务甚至容器的能力有很大作用。换句话说,就是如何提高用户网站的数量而不增加新的硬件。
第一,我们先看下什么是套接字激活?基本上来说,套接字激活表示systemd设置监听套接字(IP或其他)代表你的服务(无需启动),并且会在第一次连入时启动(activates)服务。根据这个技术,服务可能会在获得处理连接后空闲一会,并且在他们自己退出前有后续的连接。所以systemd会再次监听套接字,并且当下次连接时再次启动服务。对于客户端是无法知道服务当前是否在运行。服务的IP套接字保持连续的连接,没有连接就尝试报告失败,有连接就及时处理。
一个设置低资源的方法:设置服务在需要时运行,设置在需要的时候加载资源。这个对很多互联网站点以及服务会有很大帮助。例如,网站维护者会注意到在众多的上线网站中只有一小部分的请求需要持续连接,大部分的网站总是等待连接,消耗资源极少。使用类似套接字的模式会让你获益。在单一系统上像这样来运行网站,只有服务需要时才会提供足够的资源:这样就可以在系统上运行更多的网站。当然,在高峰期间也需要有能力提供足够多的资源才可以。
套接字激活在systemd里简单易用。很多现代的互联网守护进程已经默认支持套接字激活(还没有的也容易添加)。有systemd的实例化单元的支持,很容易的写出一对服务与套接字模板,然后可能会为每个站点多次实例化。然后(可选)使用一些systemd的安全特性来隔离站点的服务(考虑:每个服务应该只会对当前用户访问)。你现在拥有一个高度可伸缩,可靠的服务系统,用最小的资源来服务大量的安全沙盒服务,所有的这些技术在你的系统中都是内置的。
这种设置已经在一些公司的生产环境中使用。例如Pantheon的great flolks正运行他们的可伸缩即时Drupal系统。(实际上是Pantheon的David Strauss开创了这个模式)
上面提到的已经在systemd中加入了。如果你使用基于systemd的发行版,你可以立刻使用。除了这个,我们来更进一步。在systemd 197上,我们增加了对整个系统的容器支持套接字激活的特性。而且我确实想强调一下,这个特性我是一直很期待的。
基本上,使用套接字激活的系统容器,主机的systemd实例会监听代表容器的一系列端口。例如一个套接字监听ssh,一个监听web以及一个监听数据库。一旦第一次连接建立,就会孵化一个容器,把这三个套接字都传递给这个容器。在容器内部,另一个systemd在运行着,接收套接字并且进一步进行分发,运行在容器内的服务会使用普通的套接字激活。SSH,web以及数据库服务只会看到内部的容器,即使他们已经被主机的套接字激活!对于客户端,这些都是不可见的。通过简单的网络连接生成与触发一个完整的系统容器对与客户端是完全透明的。
系统容器包含一整个操作系统,可能与在主机上的系统有所不同。例如,你可能会在主机上运行Fedora,但是会在其中运行几个Debian容器。系统容器会有自己的systemd,自己的SSH接口,自己的进程树等等,但是会与主机共享其他的工具(例如内存管理)。
现在,systemd自己有比较微不足道的容器管理器,systemd-nspawn已经升级支持套接字激活。我们希望Libvirt-lxc会很快有类似的功能。这里,我们一起看看使用nspawn的一些细节:
首先,请使用一个类似debootstrap或这yum的--installroot的工具来设置容器OS树。这个细节与本博客关系不大,有很多的文档介绍如何去做。当然,要确保你已经在容器里安装了systemd v197。为了通过命令行进入容器,可使用systemd-nspawn。在你配置完成后,尝试使用systemd-nspawn 的-b选项。
假设你现在已经有一个可以用的容器了,让我们来写一个服务文件来启动或关闭它。
创建/etc/systemd/system/mycontainer.service
[Unit]
Description=My little container
[Service]
ExecStart=/usr/bin/systemd-nspawn -jbD /srv/mycontainer 3
KillMode=process
这个服务可以通过systemd start和systemctl stop来启动和停止。然而没有好的方式在容器内获得一个命令提示符。所以我们会加入一个SSH。让我们配置一下SSH,一但连接到容器的SSH端口,就会套接字激活整个容器。第一步,我们告知主机现在会监听容器的SSH。创建/etc/systemd/system/mycontainer.socket,在主机上:
[Unit]
Description=The SSH socket of my little container
[Socket]
ListenStream=23
我们可以在主机上使用systemctl start来启动单元,它会监听23端口,一旦连接进入就会激活我们之前定义的容器。我们这里选择23端口而不是通用的22,主要是因为主机已经在监听22了。nspawn会虚拟化进程列表以及文件系统树,但是不能虚拟化网络栈,因此我们只能选择与主机不同的端口。
当然,在容器内的系统还不知道传递来的套接字要做什么。如果你现在尝试连接端口,容器会启动,但是立刻又关闭了。我们需要修改一下配置。
很有必要教会在容器中的SSH如何处理。让我们来写一对简单的套接字与服务单元,创建/etc/systemd/system/sshd.socket在容器内:
[Unit]
Description=SSH Socket for Per-Connection Servers
[Socket]
ListenStream=23
Accept=yes
增加匹配SSH的服务文件:/etc/systemd/system/ssh@.service,在容器内:
[Unit]
Description=SSH Per-Connection Server for %I
[Service]
ExecStart=-/usr/sbin/sshd -i
StandardInput=socket
然后,确保吧ssdd.socket挂到sockets.target上,以便该单元在容器启动时可以自动启动:
ln -s /etc/systemd/system/sshd.socket /etc/systemd/system/sockets.target.wants/
如果我们激活了mycontainer.socket,主机的systemd会绑定套接字供我们连接。连接时,主机的systemd会激活容器,传进去套接字。容器中的systemd会接收这个套接字,与容器内的sshd.socket相匹配。我们接入的连接是排队的,并且会直接触发启动一个ssh@.service的实例,我们就可以登陆了。
这里就是全部的内容了,你可以很简单的添加额外的套接字来监听mycontainer.socket。它们都会被传递到激活的容器中,所有的套接字单元在容器内配置,并尽可能更好的被匹配。那些没有匹配到的套接字会被关闭,而那些没有传递到容器中但已经被监听的套接字会在容器内绑定systemd的实例。
那么我们来回顾一下。我们在这个过程中得到了什么?基本上,我们现在可以在单一主机上提供一些完整的系统容器,容器不用持续运行就可提供服务。主机上的系统容器数量会大量增加。
当然,这个只能工作在基于内核的虚拟化,不是硬件虚拟化。只能用libvit-lxc或nspawn在系统中实现,而不能使用qemu/kvm。
如果你建立了一些这样的容器,日志提供一个很酷的东西。如果你在主机上使用journalctl -m ,会自动发现所有的本地容器并显示出来。
在systemd 197上你可以建立你自己的套接字激活的系统容器。然而,我们会尽快加入一些改进:例如:如果所有在容器中的服务在空闲时立刻退出,容器仍然会保留。我们也希望容器也在服务关闭时退出。我们已经针对这个的措施:我们可以为笔记本增加自动挂起的恢复功能:检测笔记本空闲状态并挂起,与检测一个容器空闲后关闭它是类似的。
这个博客已经很长了,很感谢能读到这里。我们已经谈论了虚拟化,套接字,服务,不同的系统等等,希望这个博客可以为建立强大灵活的服务系统。如果你想知道更多,查阅文档或者在IRC上交流。谢谢!
容器已经成了Linux中的一个热门话题。类似libvirt-lxc ,LXC或Docker这样的容器管理器已经广为人知。本文我想阐明systemd集成的容器管理器,允许在容器之间有类似服务的管理机制。
我们会关注系统容器。容器中运行了一个初始化系统,并且容器很大程度上类似一个独立的系统。我所描述的大部分内容在这里可以看到,包括libvirt-lxc。简单来看,我们只关注systemd-nspawn,集成在systemd里的迷你容器管理器。systemd-nspawn使用了与其他容器管理器相同的内核接口。然而它用起来并不是很灵活,只是为了尽可能的简单使用,而不是为了成为可以十分细致配置的通用工具。我们在开发sytemd过程中经常使用systemd-nspawn。
好吧,我们一起看一下如何使用,在Fedora中创建一个容器:
# yum -y --releasever=20 --nogpg --installroot=/srv/mycontainer --disablerepo='*' --enablerepo=fedora install systemd passwd yum fedora-release vim-minimal
这个会下载一个迷你的Fedora系统,并安装到/srv/mycontainer中。这个命令是Fedora特有的,不过大部分的发行版提供类似的功能。在sytemd-nspawn的用户手中包含了各种发行版的命令。
我们现在有一个已经安装好的容器了,让我们设置一个root密码:
# systemd-nspawn -D /srv/mycontainer
Spawning container mycontainer on /srv/mycontainer
Press ^] three times within 1s to kill container.
-bash-4.2# passwd
Changing password for user root.
New password:
Retype new password:
passwd: all authentication tokens updated successfully.
-bash-4.2# ^D
Container mycontainer exited successfully.
我们使用systemd-nspawn获取容器的shell,并且使用passwd来设置root密码。完成后,我们可以利用新的密码来登陆
$ systemd-nspawn -D /srv/mycontainer -b
Spawning container mycontainer on /srv/mycontainer.
Press ^] three times within 1s to kill container.
systemd 208 running in system mode. (+PAM +LIBWRAP +AUDIT +SELINUX +IMA +SYSVINIT +LIBCRYPTSETUP +GCRYPT +ACL +XZ)
Detected virtualization 'systemd-nspawn'.
Welcome to Fedora 20 (Heisenbug)!
[ OK ] Reached target Remote File Systems.
[ OK ] Created slice Root Slice.
[ OK ] Created slice User and Session Slice.
[ OK ] Created slice System Slice.
[ OK ] Created slice system-getty.slice.
[ OK ] Reached target Slices.
[ OK ] Listening on Delayed Shutdown Socket.
[ OK ] Listening on /dev/initctl Compatibility Named Pipe.
[ OK ] Listening on Journal Socket.
Starting Journal Service...
[ OK ] Started Journal Service.
[ OK ] Reached target Paths.
Mounting Debug File System...
Mounting Configuration File System...
Mounting FUSE Control File System...
Starting Create static device nodes in /dev...
Mounting POSIX Message Queue File System...
Mounting Huge Pages File System...
[ OK ] Reached target Encrypted Volumes.
[ OK ] Reached target Swap.
Mounting Temporary Directory...
Starting Load/Save Random Seed...
[ OK ] Mounted Configuration File System.
[ OK ] Mounted FUSE Control File System.
[ OK ] Mounted Temporary Directory.
[ OK ] Mounted POSIX Message Queue File System.
[ OK ] Mounted Debug File System.
[ OK ] Mounted Huge Pages File System.
[ OK ] Started Load/Save Random Seed.
[ OK ] Started Create static device nodes in /dev.
[ OK ] Reached target Local File Systems (Pre).
[ OK ] Reached target Local File Systems.
Starting Trigger Flushing of Journal to Persistent Storage...
Starting Recreate Volatile Files and Directories...
[ OK ] Started Recreate Volatile Files and Directories.
Starting Update UTMP about System Reboot/Shutdown...
[ OK ] Started Trigger Flushing of Journal to Persistent Storage.
[ OK ] Started Update UTMP about System Reboot/Shutdown.
[ OK ] Reached target System Initialization.
[ OK ] Reached target Timers.
[ OK ] Listening on D-Bus System Message Bus Socket.
[ OK ] Reached target Sockets.
[ OK ] Reached target Basic System.
Starting Login Service...
Starting Permit User Sessions...
Starting D-Bus System Message Bus...
[ OK ] Started D-Bus System Message Bus.
Starting Cleanup of Temporary Directories...
[ OK ] Started Cleanup of Temporary Directories.
[ OK ] Started Permit User Sessions.
Starting Console Getty...
[ OK ] Started Console Getty.
[ OK ] Reached target Login Prompts.
[ OK ] Started Login Service.
[ OK ] Reached target Multi-User System.
[ OK ] Reached target Graphical Interface.
Fedora release 20 (Heisenbug)
Kernel 3.18.0-0.rc4.git0.1.fc22.x86_64 on an x86_64 (console)
mycontainer login: root
Password:
-bash-4.2#
现在,我们已经准备好使用systemd内置的容器了。让我们先看下machinectl工具。当没有额外参数时,会列出本地运行的容器:
$ machinectl
MACHINE CONTAINER SERVICE
mycontainer container nspawn
1 machines listed.
status参数会显示关于容器的更多细节:
$ machinectl status mycontainer
mycontainer:
Since: Mi 2014-11-12 16:47:19 CET; 51s ago
Leader: 5374 (systemd)
Service: nspawn; class container
Root: /srv/mycontainer
Address: 192.168.178.38
10.36.6.162
fd00::523f:56ff:fe00:4994
fe80::523f:56ff:fe00:4994
OS: Fedora 20 (Heisenbug)
Unit: machine-mycontainer.scope
├─5374 /usr/lib/systemd/systemd
└─system.slice
├─dbus.service
│ └─5414 /bin/dbus-daemon --system --address=systemd: --nofork --nopidfile --systemd-act...
├─systemd-journald.service
│ └─5383 /usr/lib/systemd/systemd-journald
├─systemd-logind.service
│ └─5411 /usr/lib/systemd/systemd-logind
└─console-getty.service
└─5416 /sbin/agetty --noclear -s console 115200 38400 9600
我们可以看到一些关于容器的信息,包括它的cgroup树(包括进程),IP地址以及root目录。
login参数会让我们获得容器的一个新的登陆shell
# machinectl login mycontainer
Connected to container mycontainer. Press ^] three times within 1s to exit session.
Fedora release 20 (Heisenbug)
Kernel 3.18.0-0.rc4.git0.1.fc22.x86_64 on an x86_64 (pts/0)
mycontainer login:
reboot 参数会重启容器:
#machinectl reboot mycontainer
poweroff 参数会关闭容器:
#machinectl poweroff mycontainer
这么多的machinectl工具。可以查看用户手册来了解细节。再次注意,尽管我们使用使用systemd-nspawn作为容器管理器,仍然可以使用其他的容器管理器,包括libvirt-lxc,具体的描述参考这里。
machinectl不是唯一的工具。如今更新了很多支持容器的工具!我们来尝试一下(启动容器的方法可以参考上面的介绍)
#hostnamectl -M mycontainer set-hostname "wuff"
在本地容器中使用hostnamectl来设置它的主机名
类似的,很多其他的工具也为本地容器做了升级。这里的systemctl -M:
# systemctl -M mycontainer
UNIT LOAD ACTIVE SUB DESCRIPTION
-.mount loaded active mounted /
dev-hugepages.mount loaded active mounted Huge Pages File System
dev-mqueue.mount loaded active mounted POSIX Message Queue File System
proc-sys-kernel-random-boot_id.mount loaded active mounted /proc/sys/kernel/random/boot_id
[...]
time-sync.target loaded active active System Time Synchronized
timers.target loaded active active Timers
systemd-tmpfiles-clean.timer loaded active waiting Daily Cleanup of Temporary Directories
LOAD = Reflects whether the unit definition was properly loaded.
ACTIVE = The high-level unit activation state, i.e. generalization of SUB.
SUB = The low-level unit activation state, values depend on unit type.
49 loaded units listed. Pass --all to see loaded but inactive units, too.
To show all installed unit files use 'systemctl list-unit-files'.
这个会显示指定容器中已经激活的单元列表,而不是主机中的。
可以使用命令重启容器中的服务:
#systemctl -M mycontainer restart systemd-resolved.service
systemctl利用-M选项提供了很多对容器的支持。使用-r选项,会列出所有运行在主机上的单元,以及所有本地运行的容器中的单元。
# systemctl -r
UNIT LOAD ACTIVE SUB DESCRIPTION
boot.automount loaded active waiting EFI System Partition Automount
proc-sys-fs-binfmt_misc.automount loaded active waiting Arbitrary Executable File Formats File Syst
sys-devices-pci0000:00-0000:00:02.0-drm-card0-card0\x2dLVDS\x2d1-intel_backlight.device loaded active plugged /sys/devices/pci0000:00/0000:00:02.0/drm/ca
[...]
timers.target loaded active active Timers
mandb.timer loaded active waiting Daily man-db cache update
systemd-tmpfiles-clean.timer loaded active waiting Daily Cleanup of Temporary Directories
mycontainer:-.mount loaded active mounted /
mycontainer:dev-hugepages.mount loaded active mounted Huge Pages File System
mycontainer:dev-mqueue.mount loaded active mounted POSIX Message Queue File System
[...]
mycontainer:time-sync.target loaded active active System Time Synchronized
mycontainer:timers.target loaded active active Timers
mycontainer:systemd-tmpfiles-clean.timer loaded active waiting Daily Cleanup of Temporary Directories
LOAD = Reflects whether the unit definition was properly loaded.
ACTIVE = The high-level unit activation state, i.e. generalization of SUB.
SUB = The low-level unit activation state, values depend on unit type.
191 loaded units listed. Pass --all to see loaded but inactive units, too.
To show all installed unit files use 'systemctl list-unit-files'.
我们首先可以看到主机的单元,接下来是当前运行的一个容器的单元。容器的单元前会加上容器名作为前缀。
systemctl 的list-machines 选项会列出所有运行的容器。并向容器管理器查询容器系统的状态。会显示容器是否启动,或有任何失败的服务:
# systemctl list-machines
NAME STATE FAILED JOBS
delta (host) running 0 0
mycontainer running 0 0
miau degraded 1 0
waldi running 0 0
4 machines listed.
为了显示的效果,我已经并行开启了几个容器。其中一个有个失败的服务,其状态显示为degraded。
接下来看一下journalctl。它也支持-M来查看特定容器的日志:
# journalctl -M mycontainer -n 8
Nov 12 16:51:13 wuff systemd[1]: Starting Graphical Interface.
Nov 12 16:51:13 wuff systemd[1]: Reached target Graphical Interface.
Nov 12 16:51:13 wuff systemd[1]: Starting Update UTMP about System Runlevel Changes...
Nov 12 16:51:13 wuff systemd[1]: Started Stop Read-Ahead Data Collection 10s After Completed Startup.
Nov 12 16:51:13 wuff systemd[1]: Started Update UTMP about System Runlevel Changes.
Nov 12 16:51:13 wuff systemd[1]: Startup finished in 399ms.
Nov 12 16:51:13 wuff sshd[35]: Server listening on 0.0.0.0 port 24.
Nov 12 16:51:13 wuff sshd[35]: Server listening on :: port 24.
它也支持-m来显示主机以及所有本地容器的日志:
#journalctl -m -e
但是,不是只有systemd的工具可以支持容器:
# ps -eo pid,machine,args
PID MACHINE COMMAND
1 - /usr/lib/systemd/systemd --switched-root --system --deserialize 20
[...]
2915 - emacs contents/projects/containers.md
3403 - [kworker/u16:7]
3415 - [kworker/u16:9]
4501 - /usr/libexec/nm-vpnc-service
4519 - /usr/sbin/vpnc --non-inter --no-detach --pid-file /var/run/NetworkManager/nm-vpnc-bfda8671-f025-4812-a66b-362eb12e7f13.pid -
4749 - /usr/libexec/dconf-service
4980 - /usr/lib/systemd/systemd-resolved
5006 - /usr/lib64/firefox/firefox
5168 - [kworker/u16:0]
5192 - [kworker/u16:4]
5193 - [kworker/u16:5]
5497 - [kworker/u16:1]
5591 - [kworker/u16:8]
5711 - sudo -s
5715 - /bin/bash
5749 - /home/lennart/projects/systemd/systemd-nspawn -D /srv/mycontainer -b
5750 mycontainer /usr/lib/systemd/systemd
5799 mycontainer /usr/lib/systemd/systemd-journald
5862 mycontainer /usr/lib/systemd/systemd-logind
5863 mycontainer /bin/dbus-daemon --system --address=systemd: --nofork --nopidfile --systemd-activation
5868 mycontainer /sbin/agetty --noclear --keep-baud console 115200 38400 9600 vt102
5871 mycontainer /usr/sbin/sshd -D
6527 mycontainer /usr/lib/systemd/systemd-resolved
[...]
第二列显示了进程所属的容器。前面显示"-"的是表示主机本身。
我们准备在systemd/kdbus上下文中使用的新”sd-bus"D-Bus客户端库也支持容器。当你使用sd_bus_open_system()
来连接你本地主机系统总线时,sd_bus_open_system_container()
可以被用来连接本地的容器,这样你就可以再上面执行总线的方法了。
sd-login.h和machined的总线接口提供了一些API来增加对容器的支持。它们支持容器的枚举以及根据PID等来检索。
systemd-networkd 可以支持容器。当在一个容器内运行时,它会默认在名为host0的虚拟网络接口运行一个DHCP客户端和IPv4LL(这个接口的描述)。当运行在主机networkd上,会默认提供一个DHCP服务和IPv4LL在y以ve-开头的虚拟网络(veth network)接口上,并跟在容器名称后面。
让我们来看下systemd的容器集成最新的部分:与服务名称挂钩的选项。最近的systemd版本包含了一个新的NSS模块nss-mymachines,可以通过gethostbyname()
和getaddrinfo()
来解析本地容器的名字。这个只对使用自己网络命名空间的容器有用。利用之前提到的systemd-nspawn命令,容器与主机共享网络配置,因此,我们重启容器,这次使用一个虚拟的veth网络连接主机与容器:
# machinectl poweroff mycontainer
# systemd-nspawn -D /srv/mycontainer --network-veth -b
现在(假设networkd用在容器和主机上),我们已经可以使用名字来ping容器:
# ping mycontainer
PING mycontainer (10.0.0.2) 56(84) bytes of data.
64 bytes from mycontainer (10.0.0.2): icmp_seq=1 ttl=64 time=0.124 ms
64 bytes from mycontainer (10.0.0.2): icmp_seq=2 ttl=64 time=0.078 ms
当然,名称解析并不只是在ping上使用。它可以工作在所有使用了gethostname()
和getaddrinfo()
的工具。
这里我已近介绍了很多。我们简单接触了几个工具,并且仍然还有很多。我们会提供更多的容器工具,所以请关注每次的systemd发布。
注意:整个machine的概念实际上不只限于容器,还包括了vm。然而,集成工具对于vm来说,并不能像容器那样好用。它通常需要依赖网络转换而不是直接调用系统调用接口。