在紅帽企業版 Linux 中將系統服務容器化(一)
我們的目的
創建一個可以獨立於任何其它系統服務而更新的網路服務,並且可以從主機端容易地管理和更新。
讓我們來探究一下在容器中建立一個運行在 systemd 之下的 BIND 伺服器。在這一部分,我們將了解到如何建立自己的容器以及管理 BIND 配置和數據文件。
在本系列的第二部分,我們將看到如何整合主機中的 systemd 和容器中的 systemd。我們將探究如何管理容器中的服務,並且使它作為一種主機中的服務。
創建 BIND 容器
為了使 systemd 在一個容器中輕鬆運行,我們首先需要在主機中增加兩個包:oci-register-machine
和 oci-systemd-hook
。oci-systemd-hook
這個鉤子允許我們在一個容器中運行 systemd,而不需要使用特權容器或者手工配置 tmpfs 和 cgroups。oci-register-machine
這個鉤子允許我們使用 systemd 工具如 systemctl
和 machinectl
來跟蹤容器。
[root@rhel7-host ~]# yum install oci-register-machine oci-systemd-hook
回到創建我們的 BIND 容器上。紅帽企業版 Linux 7 基礎鏡像包含了 systemd 作為其初始化系統。我們可以如我們在典型的系統中做的那樣安裝並激活 BIND。你可以從 git 倉庫中下載這份 Dockerfile。
[root@rhel7-host bind]# vi Dockerfile
# Dockerfile for BIND
FROM registry.access.redhat.com/rhel7/rhel
ENV container docker
RUN yum -y install bind &&
yum clean all &&
systemctl enable named
STOPSIGNAL SIGRTMIN+3
EXPOSE 53
EXPOSE 53/udp
CMD [ "/sbin/init" ]
因為我們以 PID 1 來啟動一個初始化系統,當我們告訴容器停止時,需要改變 docker CLI 發送的信號。從 kill
系統調用手冊中 (man 2 kill
):
唯一可以發送給 PID 1 進程(即 init 進程)的信號,是那些初始化系統明確安裝了 信號處理器 的信號。這是為了避免系統被意外破壞。
對於 systemd 信號處理器,SIGRTMIN+3
是對應於 systemd start halt.target
的信號。我們也需要為 BIND 暴露 TCP 和 UDP 埠號,因為這兩種協議可能都要使用。
管理數據
有了一個可以工作的 BIND 服務,我們還需要一種管理配置文件和區域文件的方法。目前這些都放在容器裡面,所以我們任何時候都可以進入容器去更新配置或者改變一個區域文件。從管理的角度來說,這並不是很理想。當要更新 BIND 時,我們將需要重建這個容器,所以鏡像中的改變將會丟失。任何時候我們需要更新一個文件或者重啟服務時,都需要進入這個容器,而這增加了步驟和時間。
相反的,我們將從這個容器中提取出配置文件和數據文件,把它們拷貝到主機上,然後在運行的時候掛載它們。用這種方式我們可以很容易地重啟或者重建容器,而不會丟失所做出的更改。我們也可以使用容器外的編輯器來更改配置和區域文件。因為這個容器的數據看起來像「該系統所提供服務的特定站點數據」,讓我們遵循 Linux 文件系統層次標準 ,並在當前主機上創建 /srv/named
目錄來保持管理權分離。
[root@rhel7-host ~]# mkdir -p /srv/named/etc
[root@rhel7-host ~]# mkdir -p /srv/named/var/named
提示:如果你正在遷移一個已有的配置文件,你可以跳過下面的步驟並且將它直接拷貝到 /srv/named
目錄下。你也許仍然要用一個臨時容器來檢查一下分配給這個容器的 GID。
讓我們建立並運行一個臨時容器來檢查 BIND。在將 init 進程以 PID 1 運行時,我們不能交互地運行這個容器來獲取一個 shell。我們會在容器啟動後執行 shell,並且使用 rpm
命令來檢查重要文件。
[root@rhel7-host ~]# docker build -t named .
[root@rhel7-host ~]# docker exec -it $( docker run -d named ) /bin/bash
[root@0e77ce00405e /]# rpm -ql bind
對於這個例子來說,我們將需要 /etc/named.conf
和 /var/named/
目錄下的任何文件。我們可以使用 machinectl
命令來提取它們。如果註冊了一個以上的容器,我們可以在任一機器上使用 machinectl status
命令來查看運行的是什麼。一旦有了這些配置,我們就可以終止這個臨時容器了。
如果你喜歡,資源庫中也有一個樣例 named.conf
和針對 example.com
的區域文件。
[root@rhel7-host bind]# machinectl list
MACHINE CLASS SERVICE
8824c90294d5a36d396c8ab35167937f container docker
[root@rhel7-host ~]# machinectl copy-from 8824c90294d5a36d396c8ab35167937f /etc/named.conf /srv/named/etc/named.conf
[root@rhel7-host ~]# machinectl copy-from 8824c90294d5a36d396c8ab35167937f /var/named /srv/named/var/named
[root@rhel7-host ~]# docker stop infallible_wescoff
最終的創建
為了創建和運行最終的容器,添加卷選項以掛載:
- 將文件
/srv/named/etc/named.conf
映射為/etc/named.conf
- 將目錄
/srv/named/var/named
映射為/var/named
因為這是我們最終的容器,我們將提供一個有意義的名字,以供我們以後引用。
[root@rhel7-host ~]# docker run -d -p 53:53 -p 53:53/udp -v /srv/named/etc/named.conf:/etc/named.conf:Z -v /srv/named/var/named:/var/named:Z --name named-container named
在最終容器運行時,我們可以更改本機配置來改變這個容器中 BIND 的行為。這個 BIND 伺服器將需要在這個容器分配的任何 IP 上監聽。請確保任何新文件的 GID 與來自這個容器中的其餘的 BIND 文件相匹配。
[root@rhel7-host bind]# cp named.conf /srv/named/etc/named.conf
[root@rhel7-host ~]# cp example.com.zone /srv/named/var/named/example.com.zone
[root@rhel7-host ~]# cp example.com.rr.zone /srv/named/var/named/example.com.rr.zone
很好奇為什麼我不需要在主機目錄中改變 SELinux 上下文? 注1
我們將運行這個容器提供的 rndc
二進位文件重新載入配置。我們可以使用 journald
以同樣的方式檢查 BIND 日誌。如果運行出現錯誤,你可以在主機中編輯該文件,並且重新載入配置。在主機中使用 host
或 dig
,我們可以檢查來自該容器化服務的 example.com 的響應。
[root@rhel7-host ~]# docker exec -it named-container rndc reload
server reload successful
[root@rhel7-host ~]# docker exec -it named-container journalctl -u named -n
-- Logs begin at Fri 2017-05-12 19:15:18 UTC, end at Fri 2017-05-12 19:29:17 UTC. --
May 12 19:29:17 ac1752c314a7 named[27]: automatic empty zone: 9.E.F.IP6.ARPA
May 12 19:29:17 ac1752c314a7 named[27]: automatic empty zone: A.E.F.IP6.ARPA
May 12 19:29:17 ac1752c314a7 named[27]: automatic empty zone: B.E.F.IP6.ARPA
May 12 19:29:17 ac1752c314a7 named[27]: automatic empty zone: 8.B.D.0.1.0.0.2.IP6.ARPA
May 12 19:29:17 ac1752c314a7 named[27]: reloading configuration succeeded
May 12 19:29:17 ac1752c314a7 named[27]: reloading zones succeeded
May 12 19:29:17 ac1752c314a7 named[27]: zone 1.0.10.in-addr.arpa/IN: loaded serial 2001062601
May 12 19:29:17 ac1752c314a7 named[27]: zone 1.0.10.in-addr.arpa/IN: sending notifies (serial 2001062601)
May 12 19:29:17 ac1752c314a7 named[27]: all zones loaded
May 12 19:29:17 ac1752c314a7 named[27]: running
[root@rhel7-host bind]# host www.example.com localhost
Using domain server:
Name: localhost
Address: ::1#53
Aliases:
www.example.com is an alias for server1.example.com.
server1.example.com is an alias for mail
你的區域文件沒有更新嗎?可能是因為你的編輯器,而不是序列號。 注2
終點線
我們已經達成了我們打算完成的目標,從容器中為 DNS 請求和區域文件提供服務。我們已經得到一個持久化的位置來管理更新和配置,並且更新後該配置不變。
在這個系列的第二部分,我們將看到怎樣將一個容器看作為主機中的一個普通服務來運行。
關注 RHEL 博客,通過電子郵件來獲得本系列第二部分和其它新文章的更新。
附加資源
- 所附帶文件的 Github 倉庫: https://github.com/nzwulfin/named-container
- 注1: 通過容器訪問本地文件的 SELinux 上下文
你可能已經注意到當我從容器向本地主機拷貝文件時,我沒有運行 chcon
將主機中的文件類型改變為 svirt_sandbox_file_t
。為什麼它沒有出錯?將一個文件拷貝到 /srv
會將這個文件標記為類型 var_t
。我 setenforce 0
(關閉 SELinux)了嗎?
當然沒有,這將讓 Dan Walsh 大哭(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
不過,運行中我使用了一個卷選項可以使 Dan Walsh 先生高興起來,:Z
。-v /srv/named/etc/named.conf:/etc/named.conf:Z
命令的這部分做了兩件事情:首先它表示這需要使用一個私有卷的 SELiunx 標記來重新標記;其次它表明以讀寫掛載。
啟動容器之後:
[root@rhel7-host ~]# ls -Z /srv/named/etc/named.conf
-rw-r-----. root 25 system_u:object_r:svirt_sandbox_file_t:s0:c821,c956 /srv/named/etc/named.conf
- 注2: VIM 備份行為能改變 inode
如果你在本地主機中使用 vim
來編輯配置文件,而你沒有看到容器中的改變,你可能不經意的創建了容器感知不到的新文件。在編輯時,有三種 vim
設定影響備份副本:backup
、writebackup
和 backupcopy
。
我摘錄了 RHEL 7 中的來自官方 VIM backup_table 中的默認配置。
backup writebackup
off on backup current file, deleted afterwards (default)
所以我們不創建殘留下的 ~
副本,而是創建備份。另外的設定是 backupcopy
,auto
是默認的設置:
"yes" make a copy of the file and overwrite the original one
"no" rename the file and write a new one
"auto" one of the previous, what works best
這種組合設定意味著當你編輯一個文件時,除非 vim
有理由(請查看文檔了解其邏輯),你將會得到一個包含你編輯內容的新文件,當你保存時它會重命名為原先的文件。這意味著這個文件獲得了新的 inode。對於大多數情況,這不是問題,但是這裡容器的 綁定掛載 對 inode 的改變很敏感。為了解決這個問題,你需要改變 backupcopy
的行為。
不管是在 vim
會話中還是在你的 .vimrc
中,請添加 set backupcopy=yes
。這將確保原先的文件被清空並覆寫,維持了 inode 不變並且將該改變傳遞到了容器中。
via: http://rhelblog.redhat.com/2017/07/19/containing-system-services-in-red-hat-enterprise-linux-part-1/
作者:Matt Micene 譯者:liuxinyu123 校對:wxy
本文轉載來自 Linux 中國: https://github.com/Linux-CN/archive