在2017 年红帽峰会上,有人曾经问道:用完整的虚拟机来隔离如DNS 和DHCP等网络服务可以用容器来取而代之吗?答案是可以的,下面是在当前红帽企业版Linux 7系统上创建一个系统容器的例子。
首先创建一个可以独立于任何其它系统服务而更新的网络服务,并且可以从主机端容易地管理和更新,下面我们一起来在容器中建立一个运行在systemd之下的BIND服务器,将了解到如何建立自己的容器以及管理 BIND 配置和数据文件。
创建BIND容器
为了使systemd在一个容器中轻松运行,我们首先需要在主机中增加两个包:oci-register-machine和oci-systemd-hook。oci-systemd-hook这个钩子允许我们在一个容器中运行systemd,而不需要使用特权容器或者手工配置tmpfs和cgroups。oci-register-machine这个钩子允许我们使用systemd工具如systemctl和machinectl来跟踪容器。
[root@rhel7-host~]#yuminstalloci-register-machineoci-systemd-hook
回到创建我们的BIND容器上。红帽企业版Linux7基础镜像包含了systemd作为其初始化系统。我们可以如我们在典型的系统中做的那样安装并激活BIND。你可以从git仓库中下载这份Dockerfile。
[root@rhel7-hostbind]#viDockerfile
#DockerfileforBIND
FROMregistry.access.redhat.com/rhel7/rhel
ENVcontainerdocker
RUNyum-yinstallbind&&\
yumcleanall&&\
systemctlenablenamed
STOPSIGNALSIGRTMIN+3
EXPOSE53
EXPOSE53/udp
CMD["/sbin/init"]
因为我们以PID1来启动一个初始化系统,当我们告诉容器停止时,需要改变dockerCLI发送的信号。从kill系统调用手册中(man2kill):
唯一可以发送给PID1进程(即init进程)的信号,是那些初始化系统明确安装了信号处理器signalhandler的信号。这是为了避免系统被意外破坏。
对于systemd信号处理器,SIGRTMIN+3是对应于systemdstarthalt.target的信号。我们也需要为BIND暴露TCP和UDP端口号,因为这两种协议可能都要使用。
管理数据
有了一个可以工作的BIND服务,我们还需要一种管理配置文件和区域文件的方法。目前这些都放在容器里面,所以我们任何时候都可以进入容器去更新配置或者改变一个区域文件。从管理的角度来说,这并不是很理想。当要更新BIND时,我们将需要重建这个容器,所以镜像中的改变将会丢失。任何时候我们需要更新一个文件或者重启服务时,都需要进入这个容器,而这增加了步骤和时间。
相反的,我们将从这个容器中提取出配置文件和数据文件,把它们拷贝到主机上,然后在运行的时候挂载它们。用这种方式我们可以很容易地重启或者重建容器,而不会丢失所做出的更改。我们也可以使用容器外的编辑器来更改配置和区域文件。因为这个容器的数据看起来像“该系统所提供服务的特定站点数据”,让我们遵循Linux文件系统层次标准FileSystemHierarchy,并在当前主机上创建/srv/named目录来保持管理权分离。
[root@rhel7-host~]#mkdir-p/srv/named/etc
[root@rhel7-host~]#mkdir-p/srv/named/var/named
提示:如果你正在迁移一个已有的配置文件,你可以跳过下面的步骤并且将它直接拷贝到/srv/named目录下。你也许仍然要用一个临时容器来检查一下分配给这个容器的GID。
让我们建立并运行一个临时容器来检查BIND。在将init进程以PID1运行时,我们不能交互地运行这个容器来获取一个shell。我们会在容器启动后执行shell,并且使用rpm命令来检查重要文件。
[root@rhel7-host~]#dockerbuild-tnamed.
[root@rhel7-host~]#dockerexec-it$(dockerrun-dnamed)/bin/bash
[root@0e77ce00405e/]#rpm-qlbind
对于这个例子来说,我们将需要/etc/named.conf和/var/named/目录下的任何文件。我们可以使用machinectl命令来提取它们。如果注册了一个以上的容器,我们可以在任一机器上使用machinectlstatus命令来查看运行的是什么。一旦有了这些配置,我们就可以终止这个临时容器了。
如果你喜欢,资源库中也有一个样例named.conf和针对example.com的区域文件。
[root@rhel7-hostbind]#machinectllist
MACHINECLASSSERVICE
8824c90294d5a36d396c8ab35167937fcontainerdocker
[root@rhel7-host~]#machinectlcopy-from8824c90294d5a36d396c8ab35167937f/etc/named.conf/srv/named/etc/named.conf
[root@rhel7-host~]#machinectlcopy-from8824c90294d5a36d396c8ab35167937f/var/named/srv/named/var/named
[root@rhel7-host~]#dockerstopinfallible_wescoff
最终的创建
为了创建和运行最终的容器,添加卷选项以挂载:
将文件/srv/named/etc/named.conf映射为/etc/named.conf
将目录/srv/named/var/named映射为/var/named
因为这是我们最终的容器,我们将提供一个有意义的名字,以供我们以后引用。
[root@rhel7-host~]#dockerrun-d-p53:53-p53:53/udp-v/srv/named/etc/named.conf:/etc/named.conf:Z-v/srv/named/var/named:/var/named:Z--namenamed-containernamed
在最终容器运行时,我们可以更改本机配置来改变这个容器中BIND的行为。这个BIND服务器将需要在这个容器分配的任何IP上监听。请确保任何新文件的GID与来自这个容器中的其余的BIND文件相匹配。
[root@rhel7-hostbind]#cpnamed.conf/srv/named/etc/named.conf
[root@rhel7-host~]#cpexample.com.zone/srv/named/var/named/example.com.zone
[root@rhel7-host~]#cpexample.com.rr.zone/srv/named/var/named/example.com.rr.zone
很好奇为什么我不需要在主机目录中改变SELinux上下文?注1
我们将运行这个容器提供的rndc二进制文件重新加载配置。我们可以使用journald以同样的方式检查BIND日志。如果运行出现错误,你可以在主机中编辑该文件,并且重新加载配置。在主机中使用host或dig,我们可以检查来自该容器化服务的example.com的响应。
[root@rhel7-host~]#dockerexec-itnamed-containerrndcreload
serverreloadsuccessful
[root@rhel7-host~]#dockerexec-itnamed-containerjournalctl-unamed-n
--LogsbeginatFri2017-05-1219:15:18UTC,endatFri2017-05-1219:29:17UTC.--
May1219:29:17ac1752c314a7named[27]:automaticemptyzone:9.E.F.IP6.ARPA
May1219:29:17ac1752c314a7named[27]:automaticemptyzone:A.E.F.IP6.ARPA
May1219:29:17ac1752c314a7named[27]:automaticemptyzone:B.E.F.IP6.ARPA
May1219:29:17ac1752c314a7named[27]:automaticemptyzone:8.B.D.0.1.0.0.2.IP6.ARPA
May1219:29:17ac1752c314a7named[27]:reloadingconfigurationsucceeded
May1219:29:17ac1752c314a7named[27]:reloadingzonessucceeded
May1219:29:17ac1752c314a7named[27]:zone1.0.10.in-addr.arpa/IN:loadedserial2001062601
May1219:29:17ac1752c314a7named[27]:zone1.0.10.in-addr.arpa/IN:sendingnotifies(serial2001062601)
May1219:29:17ac1752c314a7named[27]:allzonesloaded
May1219:29:17ac1752c314a7named[27]:running
[root@rhel7-hostbind]#hostwww.example.comlocalhost
Usingdomainserver:
Name:localhost
Address:::1#53
Aliases:
www.example.comisanaliasforserver1.example.com.
server1.example.comisanaliasformail
你的区域文件没有更新吗?可能是因为你的编辑器,而不是序列号。注2
终点线
我们已经达成了我们打算完成的目标,从容器中为DNS请求和区域文件提供服务。我们已经得到一个持久化的位置来管理更新和配置,并且更新后该配置不变。
在这个系列的第二部分,我们将看到怎样将一个容器看作为主机中的一个普通服务来运行。
关注RHEL博客,通过电子邮件来获得本系列第二部分和其它新文章的更新。
附加资源
所附带文件的Github仓库:https://github.com/nzwulfin/named-container
注1:通过容器访问本地文件的SELinux上下文
你可能已经注意到当我从容器向本地主机拷贝文件时,我没有运行chcon将主机中的文件类型改变为svirt_sandbox_file_t。为什么它没有出错?将一个文件拷贝到/srv会将这个文件标记为类型var_t。我setenforce0(关闭SELinux)了吗?
当然没有,这将让DanWalsh大哭(LCTT译注:RedHat的SELinux团队负责人,倡议不要禁用SELinux)。是的,machinectl确实将文件标记类型设置为期望的那样,可以看一下:
启动一个容器之前:
[root@rhel7-host~]#ls-Z/srv/named/etc/named.conf
-rw-r-----.unconfined_u:object_r:var_t:s0/srv/named/etc/named.conf
不过,运行中我使用了一个卷选项可以使DanWalsh先生高兴起来,:Z。-v/srv/named/etc/named.conf:/etc/named.conf:Z命令的这部分做了两件事情:首先它表示这需要使用一个私有卷的SELiunx标记来重新标记;其次它表明以读写挂载。
启动容器之后:
[root@rhel7-host~]#ls-Z/srv/named/etc/named.conf
-rw-r-----.root25system_u:object_r:svirt_sandbox_file_t:s0:c821,c956/srv/named/etc/named.conf
注2:VIM备份行为能改变inode
如果你在本地主机中使用vim来编辑配置文件,而你没有看到容器中的改变,你可能不经意的创建了容器感知不到的新文件。在编辑时,有三种vim设定影响备份副本:backup、writebackup和backupcopy。
我摘录了RHEL7中的来自官方VIMbackup_table中的默认配置。
backupwritebackup
offonbackupcurrentfile,deletedafterwards(default)
所以我们不创建残留下的~副本,而是创建备份。另外的设定是backupcopy,auto是默认的设置:
"yes"makeacopyofthefileandoverwritetheoriginalone
"no"renamethefileandwriteanewone
"auto"oneoftheprevious,whatworksbest
这种组合设定意味着当你编辑一个文件时,除非vim有理由(请查看文档了解其逻辑),你将会得到一个包含你编辑内容的新文件,当你保存时它会重命名为原先的文件。这意味着这个文件获得了新的inode。对于大多数情况,这不是问题,但是这里容器的绑定挂载bindmount对inode的改变很敏感。为了解决这个问题,你需要改变backupcopy的行为。
不管是在vim会话中还是在你的.vimrc中,请添加setbackupcopy=yes。这将确保原先的文件被清空并覆写,维持了inode不变并且将该改变传递到了容器中。
最后想要学习Linux开发技术的小伙伴就选择扣丁学堂学习吧,扣丁学堂不仅有专业的老师和与时俱进的课程体系,还有大量的在线Linux培训视频教程供学员观看学习,在这里你一定会学到最前沿最实用的技能,也会结识一群志同道合的朋友,想要进入Linux领域中的你还在等什么,即刻行动吧,我们在扣丁学堂等你!扣丁学堂Linux技术交流群:422345477。
网友评论