扣丁學堂淺談如何在紅帽Linux企業版中將系統服務容器化

在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來跟蹤容器。

Advertisements

[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&&\

Advertisements

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

你的區域文件沒有更新嗎?可能是因為你的編輯器,而不是序列號。

我們已經達成了我們打算完成的目標,從容器中為DNS請求和區域文件提供服務。我們已經得到一個持久化的位置來管理更新和配置,並且更新后該配置不變。

注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。

Advertisements

你可能會喜歡