近期公司的服務打算在容器這條路上做一些嘗試,我也趁機試一下容器的水。這篇文章主要是對容器技術的一些概念做個簡單總結。首先我會把容器技術與傳統的虛擬化技術做個簡單的比較,然後再從程式設計的角度來理解一些容器的相關概念。
1. 容器與虛擬機器
a. 傳統的虛擬化技術
在傳統的虛擬化技術裡面,如果想要充分地利用主機資源,人們則需要以虛擬化技術劃分出這部分資源,然後在這部分被劃分出的的硬體資源上安裝一個虛擬的作業系統。而這個作業系統,與我們平日裡在物理機中使用的作業系統並無二致。官方給的架構圖如下
我們可以簡單地把這種技術理解成,在宿主主機上把剩餘的硬體資源以某種方式“切割”出來,進而形成一臺臺小型的“物理機”。接著我們可以在這臺新的“物理機”上安裝作業系統,這臺被分割的機器就會被稱之為虛擬機器。這種技術支援下可選擇的作業系統會比較多,如各種發行版的Linux,Windows,甚至MacOS。
好處: 機器管理起來比較便捷,就相當於多了幾臺真實的主機,作業系統的選擇比較多樣化,如果是Linux的話還可直接通過ssh進行遠端管理。
弊端: 資源的劃分的方式不夠靈活,能夠被劃分出來的機器數量非常有限。
b. 基於容器的虛擬化技術
而像Docker這種基於容器的虛擬化技術,利用起資源來似乎更加靈活些,官網給出它的架構圖如下
如果說傳統的虛擬化技術是以虛擬機器作為基本單元,那麼容器化技術則是以容器來作為基本的虛擬單元了。 容器在我看來它就是一個極度精簡版的Linux,它是基於相關的映象來建立,而容器裡除了你預先設定好的軟體之外(通過映象)幾乎什麼都沒有。為了證明這一點我可以通過以下命令來進入一個正在執行的容器裡看看裡面都有些什麼。
以Redis的映象所建立的容器為例(假設容器已經在後臺執行中)。
// 1. 進入容器>
docker exec -it [redis容器ID] sh // 2. 預設進入了 `/data` 目錄,先切換到根目錄/data # cd /// 3. 當前在`/`目錄,列出根目錄的內容/ # lsbin data dev etc home lib media mnt proc root run sbin srv sys tmp usr var複製程式碼
可見這就是一個不折不扣的Linux作業系統。多執行幾條命令之後你會發現該容器裡似乎只有一些基礎的檔案系統管理命令,某些映象甚至連編輯器都沒有,如果我們需要修改映象中的檔案,則通過apt-get
(一般是Debian系的作業系統)命令進行安裝。
好處: 劃分資源比較靈活,容器較為輕量建立起來比較快捷,生產環境下會有比較好的市場。
弊端: 作業系統的選擇並沒有虛擬機器那麼豐富,目前以Linux為主。系統裡面包含的軟體非常有限,臨時需要一些軟體還需自己手動安裝。但這種修改容器的行為比較脆弱,屬於改變容器自身的狀態,一旦容器被銷燬則所有修改都會丟失。並且容器的管理需要配合docker的相關命令,管理起來相對麻煩一些。
2. 映象
裝過作業系統的人都知道,我們一般會藉助一種叫做系統映象的東西來安裝對應的作業系統。一個映象包含了所需要安裝作業系統的所有資訊,通過使用同一個映象,我們可以在不同的機器上安裝完全一樣的作業系統。
所安裝的作業系統會根據使用者的偏好設定的不同而有不同的行為,容器中的映象也與此類似。從程式設計的角度來看,映象則有點像是面嚮物件語言中的類(或者原型)。
class VirtualTech attr_accessor :name def initialize(name) self.name = name endendc = VirtualTech.new('Docker')puts c.name# =>
Dockerc = VirtualTech.new('Virtual Box')puts c.name# =>
Virtual Box複製程式碼
一個類提供了一個基礎的“樣板”,使用者可以根據自己的喜好傳入不同的引數,藉此產生各不相同的例項。在平日裡寫程式碼的時候,我們會根據業務需求的不同而抽象出不同的類。而在Docker中我們則是對基礎環境進行了抽象,進而形成了各種各樣的映象。
實踐過程中通常會使用Dockerfile
來定義基礎環境。Docker的官方倉庫裡面包含了許多常用的映象,原則上都是以一個作業系統為起點,然後再在作業系統上安裝所需要的依賴軟體。假設我只需要一個包含Vim編輯器的映象則Dockerfile
可以這樣寫
FROM ubuntuRUN apt-get update &
&
apt-get install -y vim複製程式碼
接著在Dockerfile
所在目錄下進行構建即可
docker build -t vim:hello .複製程式碼
我把映象命名為vim
並給它打上hello
標籤。整個過程感覺就像是先安裝了一個極端精簡的作業系統,然後再在上面安裝vim編輯器。
>
docker imagesREPOSITORY TAG IMAGE ID CREATED SIZEvim hello 5d2690ced0fd 6 seconds ago 170MB複製程式碼
PS: 上面的例子只是演示作用,在真實專案中幾乎不會需要這種映象,試問一個只有Vim編輯器的作業系統,到底有什麼用?用它來編輯什麼?
3. 容器
映象會包含容器中所需要的基本資訊,我們可以通過指定映象來建立出許多容器。首先通過以下命令來檢視當前服務中容器的執行情況
>
docker container lsCONTAINER ID IMAGE COMMAND CREATED STATUS複製程式碼
可見現在一個容器也沒有,接下來我們基於一個打了alpine
標籤的redis
映象來啟動一個容器,命令也很簡單
docker run redis:alpine複製程式碼
完成之後再次執行ls命令,則可以看到有一個容器正在執行,並佔用著6379這個埠
>
docker container lsCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMESf9eb72b8acd9 redis:alpine "docker-entrypoint.s…" 2 seconds ago Up 1 second 6379/tcp serene_kowalevski複製程式碼
當然並不是所有容器都會以服務的方式執行,前面的例子那個只有Vim編輯器容器建立之後就馬上停止執行了。但是停止執行並不代表容器已經銷燬了,他們只是在後臺“休息”。如同程式設計的時候我們可以通過類建立出許多例項,但是並不是所有例項你都會用得上,總會有那麼些例項一旦建立了就等待著銷燬。不過Docker似乎沒有垃圾回收機制,如果有需要我們可以通過以下命令來批量刪除一些沒有啟動的容器。
docker container prune複製程式碼
4. 容器與映象小結
映象本身是不能改變的,如果你想要對映象做出調整唯一的方式就是修改Dockerfile
然後重新構建一個新的映象。建立容器需要依賴映象,我們可以通過不同的偏好設定來定製容器的特殊行為,比如可以以特定引數來暴露容器的埠號讓容器服務可以與外界互動,使容器以daemon的形式在後臺執行,指定容器資料卷等等。
一般來說容器本身都有自己的狀態,Docker的容器提供了可寫訪問層,我們可以動態地修改容器的相關資訊(比如可以通過bash,sh登入容器內部,對容器的配置進行各種修改)。然而這種在容器本身進行的修改是很脆弱的,雖說容器的重啟並不會使相關的修改失效,但是一旦容器被銷燬則之前的修改就完全作廢了。就像是程式設計裡的例項一旦被垃圾回收,本身的資訊也就丟失了。
為了解決這種配置丟失的問題,最好的方式就是讓配置資料持久化。簡單地來說我們可以在容器之外維護一份配置檔案,每次容器啟動的時候都讀取這份檔案作為自己的預設配置。前幾日還聽運維的小夥伴說K8S叢集是用etcd來儲存這些配置以達到共享的目的,在此稍稍驚歎Go的生態。
5. 總結
這篇文章主要針對自己對容器技術的理解做個簡單總結,然而它只是容器生態的冰山一角而已。容器技術還會涉及資料卷,網路等概念。官方會建議我們對開發,生產環境進行容器化,個人也對兩方面都做了嘗試。
說實在的生成環境容器化乃是大勢所趨,個人也比較贊同,搭配上平臺之後應用的部署,擴容,CI這些事情做起來都會比較方便。然而開發環境的容器化個人覺得必要性不是太大(當然你有特殊需求就另當別論),畢竟本來簡單就能啟動的專案,加入Docker使其容器化則顯得有點雞肋了。