LXC-Linux Container 簡介
【編者的話】這是關於Linux容器介紹的第二篇,上一篇文章深入介紹了核心的控制組(cgroup),這個功能讓你可以隔離,限制以及監控選擇的使用者空間的應用,在這篇文章中,將會更深入的介紹程序隔離,即通過LXC(Linux容器,Linux Container)來進行程序隔離。
容器相當於於你運行了一個接近於裸機的虛擬機器。這項技術始於2008年,LXC的大部分功能來自於Solaris容器(又叫做Solaries Zones)以及之前的FreeBSD jails技術。 LXC並不是建立一個成熟的虛擬機器,而是建立了一個擁有自己程序程和網路空間的虛擬環境,使用名稱空間來強制程序隔離並利用核心的控制組(cgroups)功能,該功能可以限制,計算和隔離一個或多個程序的CPU,記憶體,磁碟I / O和網路使用情況。 您可以將這種使用者空間框架想像成是 chroot
的高階形式。
chroot
是一個改變當前執行程序以及其子程序的根目錄的操作。一個執行在這種環境的程式無法訪問根目錄外的檔案和命令。
注意:LXC使用名稱空間來強制程序隔離,同時利用核心的控制組來計算以及限制一個或多個程序的CPU,記憶體,磁碟I / O和網路使用。
但容器究竟是什麼?簡短的答案是容器將軟體應用程式與作業系統分離,為使用者提供乾淨且最小的Linux環境,與此同時在一個或多個隔離的“容器”中執行其他所有內容。容器的目的是啟動一組有限數量的應用程式或服務(通常稱為微服務),並使它們在獨立的沙盒環境中執行。

圖1 對比在傳統環境以及容器環境執行的應用
這種隔離可防止在給定容器內執行的程序監視或影響在另一個容器中執行的程序。此外,這些集裝箱化服務不會影響或干擾主機。能夠將分散在多個物理伺服器上的許多服務合併為一個的想法是資料中心選擇採用該技術的眾多原因之一。
容器有以下幾個特點:
- 安全性:容器裡可以執行網路服務,這可以限制安全漏洞或違規行為造成的損害。那些成功利用那個容器的一個或多個應用的安全漏洞的入侵者將會被限制在只能在那個容器中做一些操作。
- 隔離性:容器允許在同一物理機器上部署一個或多個應用程式,即使這些應用程式必須在不同的域下執行,每個域都需要獨佔訪問其各自的資源。例如,通過將每個容器關聯的不同IP地址,在不同容器中執行的多個應用程式可以繫結到同一物理網路介面。
- 虛擬化和透明性:容器為系統提供虛擬化環境,這個環境可以隱藏或限制系統底層的物理裝置或系統配置的可見性。容器背後的一般原則是避免更改執行應用程式的環境,但解決安全性或隔離問題除外。
使用LXC的工具
對於大多數現代Linux發行版,核心都啟用了控制組,但您很可能仍需要安裝LXC工具。
如果您使用的是Red Hat或CentOS,則需要先安裝EPEL倉庫。對於其他發行版,例如Ubuntu或Debian,只需鍵入:
$ sudo apt-get install lxc
現在,在開始使用這些工具之前,您需要配置您的環境。在此之前,您需要驗證當前使用者是否同時在/ etc / subuid和/ etc / subgid中定義了uid和gid:
$ cat /etc/subuid petros:100000:65536 $ cat /etc/subgid petros:100000:65536
如果 ~/.config/lxc directory
不存在,則建立該目錄,並且把配置檔案 /etc/lxc/default.conf
複製到 ~/.config/lxc/default.conf.
,將以下兩行新增到檔案末尾:
lxc.id_map = u 0 100000 65536 lxc.id_map = g 0 100000 65536
It should look something like this:
結果如下:
$ cat ~/.config/lxc/default.conf lxc.network.type = veth lxc.network.link = lxcbr0 lxc.network.flags = up lxc.network.hwaddr = 00:16:3e:xx:xx:xx lxc.id_map = u 0 100000 65536 lxc.id_map = g 0 100000 65536
將以下命令新增到 /etc/lxc/lxc-usernet
檔案末尾(把第一列換成你的username)
petros veth lxcbr0 10
最快使這些配置生效的方法是重啟節點或者將使用者登出再登入。
重新登入後,請驗證當前是否已載入veth網路驅動程式:
$ lsmod|grep veth veth163840
如果沒有,請輸入:
$ sudo modprobe veth
現在您可以使用LXC工具集來下載,執行,管理Linux容器。
接下來,下載容器映象並將其命名為“example-container”。當您鍵入以下命令時,您將看到一長串許多Linux發行版和版本支援的容器:
$ sudo lxc-create -t download -n example-container
將會有三個彈出框讓您分別選擇發行版名稱(distribution),版本號(release)以及架構(architecture)。請選擇以下三個選項:
Distribution: ubuntu Release: xenial Architecture: amd64
選擇後點擊 Enter
,rootfs將在本地下載並配置。出於安全原因,每個容器不附帶OpenSSH伺服器或使用者帳戶。同時也不會提供預設的root密碼。要更改root密碼並登入,必須在容器目錄路徑中執行lxc-attach或chroot(在啟動之後)。
啟動容器:
$ sudo lxc-start -n example-container -d
-d
選項表示隱藏容器,它會在後臺執行。如果您想要觀察boot的過程,只需要將 -d
換成 -F
。那麼它將在前臺執行,登入框出現時結束。
你可能會遇到如下錯誤:
$ sudo lxc-start -n example-container -d lxc-start: tools/lxc_start.c: main: 366 The container failed to start. lxc-start: tools/lxc_start.c: main: 368 To get more details, run the container in foreground mode. lxc-start: tools/lxc_start.c: main: 370 Additional information can be obtained by setting the --logfile and --logpriority options.
如果你遇到了,您需要通過在前臺執行lxc-start服務來除錯它:
$ sudo lxc-start -n example-container -F lxc-start: conf.c: instantiate_veth: 2685 failed to create veth pair (vethQ4NS0B and vethJMHON2): Operation not supported lxc-start: conf.c: lxc_create_network: 3029 failed to create netdev lxc-start: start.c: lxc_spawn: 1103 Failed to create the network. lxc-start: start.c: __lxc_start: 1358 Failed to spawn container "example-container". lxc-start: tools/lxc_start.c: main: 366 The container failed to start. lxc-start: tools/lxc_start.c: main: 370 Additional information can be obtained by setting the --logfile and --logpriority options.
從以上示例,你可以看到模組 veth
沒有被引入,在引入之後,將會解決這個問題。
之後,開啟第二個terminal視窗,驗證容器的狀態。
$ sudo lxc-info -n example-container Name:example-container State:RUNNING PID:1356 IP:10.0.3.28 CPU use:0.29 seconds BlkIO use:16.80 MiB Memory use:29.02 MiB KMem use:0 bytes Link:vethPRK7YU TX bytes:1.34 KiB RX bytes:2.09 KiB Total bytes:3.43 KiB
也可以通過另一種方式來檢視所有安裝的容器,執行命令:
$ sudo lxc-ls -f NAMESTATEAUTOSTART GROUPS IPV4IPV6 example-container RUNNING 0-10.0.3.28 -
但是問題是你仍然不能登入進去,你只需要直接attach到正在執行的容器,建立你的使用者,使用 passwd
命令改變相關的密碼。
$ sudo lxc-attach -n example-container root@example-container:/# root@example-container:/# useradd petros root@example-container:/# passwd petros Enter new UNIX password: Retype new UNIX password: passwd: password updated successfully
更改密碼後,您將能夠從控制檯直接登入到容器,而無需使用 lxc-attach
命令:
$ sudo lxc-console -n example-container
如果要通過網路連線到此執行容器,請安裝OpenSSH伺服器:
root@example-container:/# apt-get install openssh-server
抓取容器的本地IP地址:
root@example-container:/# ip addr show eth0|grep inet inet 10.0.3.25/24 brd 10.0.3.255 scope global eth0 inet6 fe80::216:3eff:fed8:53b4/64 scope link
然後在主機的新的控制檯視窗中鍵入:
$ ssh 10.0.3.25
瞧!您現在可以ssh到正在執行的容器並鍵入您的使用者名稱和密碼。
在主機系統上,而不是在容器內,可以觀察在啟動容器後啟動和執行的LXC程序:
$ ps aux|grep lxc|grep -v grep root8610.00.0 2347721368 ?Ssl11:01 ↪0:00 /usr/bin/lxcfs /var/lib/lxcfs/ lxc-dns+11550.00.1528682908 ?S11:01 ↪0:00 dnsmasq -u lxc-dnsmasq --strict-order ↪--bind-interfaces --pid-file=/run/lxc/dnsmasq.pid ↪--listen-address 10.0.3.1 --dhcp-range 10.0.3.2,10.0.3.254 ↪--dhcp-lease-max=253 --dhcp-no-override ↪--except-interface=lo --interface=lxcbr0 ↪--dhcp-leasefile=/var/lib/misc/dnsmasq.lxcbr0.leases ↪--dhcp-authoritative root11960.00.1544843928 ?Ss11:01 ↪0:00 [lxc monitor] /var/lib/lxc example-container root16580.00.1547803960 pts/1S+11:02 ↪0:00 sudo lxc-attach -n example-container root16600.00.2544644900 pts/1S+11:02 ↪0:00 lxc-attach -n example-container
要停止容器,請鍵入(在主機):
$ sudo lxc-stop -n example-container
停止後,驗證容器的狀態:
```
$ sudo lxc-ls -f
NAME STATE AUTOSTART GROUPS IPV4 IPV6
example-container STOPPED 0 - - -
$ sudo lxc-info -n example-container
Name: example-container
State: STOPPED
```
要徹底銷燬容器 - 即從主機system—type清除它:
$ sudo lxc-destroy -n example-container
銷燬容器
example-container
銷燬後,可以驗證是否已將其刪除:
```
$ sudo lxc-info -n example-container
example-container doesn't exist
$ sudo lxc-ls -f
$
```
注意:如果您嘗試銷燬正在執行的容器,該命令將失敗並告知您容器仍在執行:
在銷燬容器前必須先停止它
高階配置
有時,可能需要配置一個或多個容器來完成一個或多個任務。 LXC通過讓管理員修改位於/var/lib/ lxc中的容器配置檔案來簡化這一過程:
$ sudo su
cd /var/lib/lxc
ls
example-container
容器的父目錄將包含至少兩個檔案:1)容器配置檔案和2)容器的整個rootfs:
cd example-container/
ls
config rootfs
假設您想要在主機系統啟動時自動啟動名稱為 example-container
的容器。那麼您需要將以下行新增到容器的配置檔案`/ var / lib / lxc / example-container / config`的尾部
Enable autostart
lxc.start.auto = 1
重新啟動容器或重新啟動主機系統後,您應該看到如下內容:
$ sudo lxc-ls -f NAMESTATEAUTOSTART GROUPS IPV4IPV6 example-container RUNNING 1-10.0.3.25 -
注意 AUTOSTART
欄位現在被設定為“1”。
如果在容器啟動時,您希望容器繫結裝載主機上的目錄路徑,請將以下行新增到同一檔案的尾部:
將掛載系統路徑繫結到本地路徑
lxc.mount.entry = /mnt mnt none bind 0 0
通過上面的示例,當容器重新啟動時,您將看到容器本地的 / mnt目錄可訪問的主機/ mnt目錄的內容。
特權與非特權容器
您經常會發現在與LXC相關的內容中討論特權容器和非特權容器的概念。但它們究竟是什麼呢?這個概念非常簡單,並且LXC容器可以在任一配置下執行。
根據設計,無特權容器被認為位元權容器更安全,更保密。無特權容器執行時,容器的root UID對映到主機系統上的非root UID。這使得攻擊者即使破解了容器,也難以獲得對底層主機的root許可權。簡而言之,如果攻擊者設法通過已知的軟體漏洞破壞了您的容器,他們會立即發現自己無法獲取任何主機許可權。
特權與非特權容器
您經常會發現在與LXC相關的內容中討論特權容器和非特權容器的概念。但它們究竟是什麼呢?這個概念非常簡單,並且LXC容器可以在任一配置下執行。
根據設計,無特權容器被認為位元權容器更安全,更保密。無特權容器執行時,容器的root UID對映到主機系統上的非root UID。這使得攻擊者即使破解了容器,也難以獲得對底層主機的root許可權。簡而言之,如果攻擊者設法通過已知的軟體漏洞破壞了您的容器,他們會立即發現自己無法獲取任何主機許可權。
特權容器可能使系統暴露於此類攻擊。這就是為什麼我們最好在特權模式下執行儘量少的容器。確定需要特權訪問的容器,並確保付出額外的努力來定期更新並以其他方式鎖定它們。
然而,Docker又是什麼呢?
我花了相當多的時間談論Linux容器,但是Docker呢?它是生產中部署最多的容器解決方案。自首次推出以來,Docker已經風靡Linux計算世界。 Docker是一種Apache許可的開源容器化技術,旨在自動化在容器內建立和部署微服務這類重複性任務。 Docker將容器視為非常輕量級和模組化的虛擬機器。最初,Docker是在LXC之上構建的,但它已經遠離了這種依賴,從而帶來了更好的開發人員和使用者體驗。與LXC非常相似,Docker繼續使用核心 cgroup
子系統。該技術不僅僅是執行容器,還簡化了建立容器,構建映像,共享構建的映像以及對其進行版本控制的過程。
Docker主要關注於:
- 可移植性:Docker提供基於映象的部署模型。這種型別的可移植性允許更簡單的方式在多個環境中共享應用程式或服務集合(以及它們的所有依賴)。
- 版本控制:單個Docker映象由一系列組合層組成。每當映象被更改時,都會建立一個新層。例如,每次使用者指定命令(例如執行或複製)時,都會建立一個新層。 Docker將重用這些層用於新的容器構建。分層到Docker是它自己的版本控制方法。
- 回滾:再次,每個Docker映象都有很多層。如果您不想使用當前執行的層,則可以回滾到以前的版本。這種敏捷性使軟體開發人員可以更輕鬆地持續整合和部署他們的軟體技術。
-快速部署:配置新硬體通常需要數天時間。並且,安裝和配置它的工作量和開銷是非常繁重的。使用Docker,您可以在幾秒鐘將映象啟動並執行,相比於之前,節省了大量的時間。當你使用完一個容器時,你可以輕鬆地銷燬它。
從本質上說,Docker和LXC都非常相似。它們都是使用者空間和輕量級虛擬化平臺,它們利用cgroup和名稱空間來管理資源隔離。但是,兩者之間也存在許多明顯的差異。
程序管理
Docker將容器限制為單個程序執行。如果您的應用程式包含X個併發程序,Docker將要求您執行X個容器,每個容器都有自己單獨的程序。 LXC不是這樣,LXC執行具有傳統init程序的容器,反過來,可以在同一容器內託管多個程序。例如,如果要託管LAMP(Linux + Apache + MySQL + PHP)伺服器,每個應用程式的每個程序都需要跨越多個Docker容器。
狀態管理
Docker被設計為無狀態,意味著它不支援持久儲存。有很多方法可以解決這個問題,但同樣,只有在程序需要時才需要它。建立Docker映象時,它將包含只讀層。這不會改變。在執行時,如果容器的程序對其內部狀態進行任何更改,則將保持內部狀態和映象的當前狀態之間的差異,直到對Docker映象進行提交(建立新層)或直到容器被刪除,差異也會消失。
可移植性
在討論Docker時,這個詞往往被過度使用 - 因為它是Docker相對於LXC的最重要的優勢。 Docker從應用程式中抽象出網路,儲存和作業系統細節方面做得更好。這樣就形成了一個真正獨立於配置的應用程式,保證應用程式的環境始終保持不變,無論啟用它的機器配置環境如何。
Docker旨在使開發人員和系統管理員都受益。它已成為許多DevOps(開發人員+維護人員)工具鏈中不可或缺的一部分。開發人員可以專注於編寫程式碼,而無需擔心最終託管它的系統是什麼。使用Docker,無需安裝和配置複雜資料庫,也無需擔心在不相容的語言工具鏈版本之間切換。 Docker為維護人員提供了更多的靈活性,通常可以減少託管一些較小和更基本的應用程式所需的物理系統數量。 Docker簡化了軟體交付。新功能和錯誤/安全修復程式可以快速到達客戶,無需任何麻煩,意外或停機。
總結
為了基礎設施安全性和系統穩定性而隔離程序並不像聽起來那麼痛苦。 Linux核心提供了所有必要的工具,使簡單易用的使用者空間應用程式(如LXC(甚至Docker))能夠在隔離的沙盒環境中管理作業系統的微例項及其本地服務。
在本系列的第三部分中,我將講述如何使用Kubernetes的容器編排。