首先通過一個簡單的場景來看一下為什么docker這么火?
開發(fā)人員在開發(fā)的時候是有一套開發(fā)環(huán)境,包括運行的操作系統(tǒng),依賴的服務(wù)比如weblogic,java,一些特定的配置,比如jvm大小 ,字符集,操作系統(tǒng)內(nèi)核參數(shù)等,然后就是應(yīng)用代碼了。當開發(fā)完成后,開發(fā)人員就把代碼打包發(fā)送運維人員到生產(chǎn)上部署。運維人員就需要搭建一個和開發(fā)環(huán)境一樣的生產(chǎn)環(huán)境,安裝操作系統(tǒng) ,weblogic,java,根據(jù)基線配置一些參數(shù),過程非常的繁瑣。搭建完成后還是可能因為兩個環(huán)境細微的不同都有可能導(dǎo)致應(yīng)用程序的部署失敗。做為運維人員常常聽到開發(fā)的抱怨,在我的環(huán)境里是正常的啊,怎么到你的環(huán)境就不行了呢!
在傳統(tǒng)的部署模式下,如果有非常多的服務(wù)器,運維工程師需要在每一臺服務(wù)器上進行相當復(fù)雜的操作才能夠完成部署。安裝->配置->部署。但是docker的出現(xiàn)顛覆了這種傳統(tǒng)的模式。我們看一下,docker只需要把整個開發(fā)環(huán)境做打包成一個docker image,也就是docker鏡像給運維團隊,而運維團隊直接運行就可以了,整個過程就變成打包,傳送,運行即可,非常的簡單。因為docker鏡像包含了所有的環(huán)境依賴關(guān)系,可以保證開發(fā)與生產(chǎn)環(huán)境一致,對于開發(fā)和運維工作,docker技術(shù)可以讓開發(fā)和運維豁免很多預(yù)想之外的工作和相互推脫。此外,容器可以重復(fù)運行在任何地方,簡單化了運維人員的工作 。 Docker的這種在安全、可重復(fù)的環(huán)境中可移植,跨平臺的快速部署軟件的方式也方便做持續(xù)集成,所以說docker出現(xiàn)拉開了基于云計算平臺發(fā)布產(chǎn)品方式的變革序幕,是運維人員的解放,廣受開發(fā)者和運維人員的歡迎。
Docker ,除了是云時代的應(yīng)用交付方式的變革,運維人員的解放,和微服務(wù)的結(jié)合使用還將顛覆傳統(tǒng)的軟件架構(gòu)。我們先看一下單塊架構(gòu)和微服務(wù)架構(gòu)的區(qū)別。單塊架構(gòu)就是一個實例里包含了多個業(yè)務(wù)模塊,如果說電信行業(yè)的登陸,開戶,繳費,話費查詢等功能都運行在一個實例里,這樣做有什么缺點呢?第一,隨著業(yè)務(wù)的增長,這個單塊會越來越大,變得很復(fù)雜,啟動的時間也會越來越長,如果有bug要排查起來也會非常的復(fù)雜。第二,如果其中某一個業(yè)務(wù)模塊異常將會影響所有其他的業(yè)務(wù)模塊,造成整個業(yè)務(wù)系統(tǒng)癱瘓。第三,有些功能業(yè)務(wù)壓力大,有些功能業(yè)務(wù)壓力小,因為捆綁在一起,都只能一起增加或減少,這樣就會造成資源的浪費。如果某個功能業(yè)務(wù)4個實例已經(jīng)不能支撐了,而其他業(yè)務(wù)模塊其實并沒有什么壓力,但是為了業(yè)務(wù)大的功能模塊的業(yè)務(wù)壓力,就需要增加一個實例。而微服務(wù)架構(gòu)就可以解決這三個問題,把功能按模塊運行在不同的容器里,相互不影響,各用各的資源,可以根據(jù)實現(xiàn)的業(yè)務(wù)壓力而來啟動相應(yīng)的實例數(shù)。
Docker的細粒度松耦合能夠讓我們用一個Docker容器裝載一個場景功能,讓每個Docker中運行一個微服務(wù),為微服務(wù)應(yīng)用程序創(chuàng)建出高效的分布式模型,從而順利實現(xiàn)微服務(wù)概念的現(xiàn)實轉(zhuǎn)化。
那么docker究竟是什么呢?
首先我們來看一下docker的標識,是一個大鯊魚馱著一堆集裝箱在海上航行。無邊無盡的海就是云了,大鯊魚貨輪就是云計算平臺了,docker是集裝箱。集裝箱將貨運目標標準化,Docker 將應(yīng)用程序標準化,集裝箱里面裝的以是任意類型的App,各自在自己的集裝箱里運行,相互隔離,共用大鯊魚貨輪的資源,這種封裝的集裝箱可以放到任何的平臺上去運行。非常形像的展示了docker的特性: Build, Ship and Run Any App, Anywhere!在任何平臺運行任何應(yīng)用!
Docker的英文本意是碼頭工人,也就是搬運工,這種搬運工搬運的是集裝箱。
Docker是PasS提供商DoctCloud開源的一個基于LXC的高級容器引擎。
Docker是一個由GO語言寫的程序運行的“容器” 。
Docker把App裝在Container內(nèi),通過Linux Container技術(shù)的包裝將App變成一種標準化的、可移植的、自管理的組件(集裝箱)。這種組件可以在你的電腦上開發(fā)、調(diào)試、運行,最終非常方便和一致地運行在測試環(huán)境和生產(chǎn)環(huán)境下。
Docker誕生的時間并不長,2013年3月發(fā)布0.1版本,到現(xiàn)在也才三年多,現(xiàn)在最新的版本是1.12,還在不斷的完善中。但docker并不是一種新技術(shù),而是基于Linux內(nèi)核容器技術(shù)LXC的為適應(yīng)時代需要、標準化IT結(jié)構(gòu)的新方式,一種沖擊虛擬化的新玩法。
Docker解決LXC的兩個問題集成度低,需要手工準備容器內(nèi)文件系統(tǒng)的兩個問題。
Docker的整體結(jié)構(gòu)包括兩個部分,Docker Hub 和 Docker 引擎組成。Docker Hub提供API和云服務(wù)來發(fā)布基于Docker的應(yīng)用程序。Docker Hub 是Docker 官方提供的容器鏡像倉庫,有大量的軟件公司在其中維護自己的官方軟件。目前已經(jīng)有1萬4千多個基于Docker的應(yīng)用程序package,從操作系統(tǒng)的廠商,,云計算IaaS服務(wù)商,大數(shù)據(jù),像各種各樣的編程語言等等各種各樣的軟件,包含最流行的13個應(yīng)用-CentOS, MongoDB, MySQL, Nginx, Redis, Ubuntu, and WordPress 等等,在云計算產(chǎn)業(yè)迅速發(fā)展的環(huán)境下?lián)碛性絹碓截S富的生態(tài)系統(tǒng)。后者運行在宿主機上,是一個可以移植的,輕量的應(yīng)用運行環(huán)境和打包工具,負責構(gòu)建、運行和分發(fā) Docker 容器。簡單來說,Docker Hub 是資源存放的云平臺,Docker 引擎是使用云上資源資源的終端,任何人都能到云上下載需要的資源,這就是容器云+端開放平臺的模式。
下面講一下在docker的容器云+端開放平臺結(jié)構(gòu)下,應(yīng)用程序的生命周期。先在本地基于Docker引擎構(gòu)建和打包應(yīng)用程序,然后用DockerHub云服務(wù)將程序(集裝箱)放到DockerHub,希望運行此應(yīng)用的平臺再去下載和運行。
Docker號稱Build once,run anywhere,就是鏡像一次構(gòu)建,容器到處運行。就像這個圖上所示,當使用Docker引擎構(gòu)建好應(yīng)用程序后放到DockerHub,開發(fā),測試人員都可以獲取,拿來直接運行,可以運行在物理平臺上,也可以運行的虛擬平臺上。整個過程非常的簡單方便,在這種模式下極大地提高了開發(fā)部署效率。
簡單介紹docker使用到的部分核心技術(shù),但不做深入探究,因為每一個技術(shù)都是一個獨立的項目,了解一下核心技術(shù)的作用就可以了。前面說了,docker是基于LXC的,而LXC的核心就是namespaces,容器隔離和cgroups,資源控制。每個用戶實例之間相互隔離, 互不影響。 一般的硬件虛擬化方法給出的方法是VM,而LXC給出的方法是container,更細一點講就是namespace。
PID,IPC,Network等系統(tǒng)資源不再是全局性的,而是屬于某個特定的Namespace。通過使用namespace對cpu、內(nèi)存、網(wǎng)絡(luò)和文件系統(tǒng)的隔離,在這種情況下一個容器不會影響另一個容器的資源。不同 namespace 中可以有相同pid,比如說容器A里有個進程號為123,在容器B里也可以存在一個進程號為123。每一個docker容器都有自己的Namespace,在docker用戶層只能看到屬于自己namespace下的資源。對宿主機來說,docker就是一個進程,但每個namespace看上去就像一個單獨的Linux系統(tǒng)。
cgroups控制組可以提供對宿主機上的共享資源內(nèi)存、CPU、磁盤 IO 等資源的限制和審計管理,限制相應(yīng)進程或一組進程使用的系統(tǒng)資源,實現(xiàn)了對資源的配額和度量。只有能控制分配到容器的資源,才能避免當多個容器同時運行時的對系統(tǒng)資源的競爭。
AUFS則可以讓大家像玩樂高那樣玩 Docker。我們先看一下它的結(jié)構(gòu):容器在最頂層,是writable,其下所有層都為readonly,將readonly的文件系統(tǒng)層稱作“image”,每一層鏡像的下面稱為其父鏡像,Docker鏡像位于bootfs之上,第一層鏡像為Base Image。因此想要從一個image啟動一個container,docker會先加載其父image直到base image,用戶的進程運行在writeable的layer中。得益于AUFS的特性,每一個對readonly層文件/目錄的修改都只會存在于上層的writeable層中。這樣由于不存在競爭,多個container可以共享readonly的layer。所以docker將readonly的層稱作 "image"——對于container而言整個rootfs都是read-write的,但事實上所有的修改都寫入最上層的writeable層中,image不保存用戶狀態(tài),可以用于模板、重建和復(fù)制。
1. 節(jié)省存儲空間:多個container可以共享base image存儲
2. 快速部署:如果要部署多個container,base image可以避免多次拷貝
3. 內(nèi)存更?。阂驗槎鄠€container共享base image, 以及OS的disk緩存機制,多個container中的進程命中緩存內(nèi)容的幾率大大增加
4. 升級更方便:相比于 copy-on-write 類型的FS,base-image也是可以掛載為可writeable的,可以通過更新base image而一次性更新其之上的container
5. 允許在不更改base-image的同時修改其目錄中的文件:所有寫操作都發(fā)生在最上層的writeable層中,這樣可以大大增加base image能共享的文件內(nèi)容。
在這里我們講了兩個概念,容器和鏡像,大家可能還不是很理解,下面就給講一下docker的三大核心概念。
docker三大核心概念是倉庫,鏡像,容器,從這個圖表我們可以知道,倉庫里有鏡像,容器運行在鏡像之上。
倉庫類似于代碼倉庫,是Docker集中儲存鏡像文件的場所。分為公開倉庫(Public)和私有倉庫(Private)兩種形式。最大的公開倉庫就是前面提到的dockerHub,當然,也可以在局域網(wǎng)內(nèi)構(gòu)建私有倉庫。
鏡像類似于虛擬機的鏡像,可以理解為面向Docker的只讀模板,包含了文件系統(tǒng)。鏡像是創(chuàng)建Docker的基礎(chǔ),所有的docker容器都是基于鏡像運行。
容器就是前面提到的集裝箱,是從鏡像創(chuàng)建的應(yīng)用實例,可以將其啟動、開始、停止、刪除、而這些容器都是相互隔離、互不可見的。
鏡像相應(yīng)于是java代碼寫的公用類,而容器是則是基于這個代碼運行的實例,每個人都可以調(diào)用這個公用類,并且可以根據(jù)自己的需求傳入不同的參數(shù),形成不同內(nèi)容的實例,比如我們可以通過傳參指定實例名,啟動基本weblogic鏡像容器,在同一臺機器上就可以基于同一個鏡像啟動很多個實例名不一樣的進程。但這些參數(shù)不會改變原始代碼,所有的變更都只存于容器內(nèi)。所以說鏡像是只讀的,而容器是可寫的。三個概念之間究竟是怎么相互存在,相互影響的呢?我們可以通過docker的生命周期來進一步了解。
以鏡像為基礎(chǔ),我們來看一下docker的生命周期。在我們的本地計算機上,我們可以從docker倉庫把鏡像下載下來,也可以把本地的鏡像上傳到docker倉庫。可以為鏡像的不同版本打上標簽tag用來區(qū)別。
可以基于鏡像創(chuàng)建一個容器,也可以把運行的容器commint成一個鏡像,而容器則可以作為一個docker實例在本地運行,可以啟動,停止,重啟,當然也可以刪除。我們還可以通過save把鏡像保存到一個tar包,也可以通過load加載tar包里的鏡像。我們還可以通過dockerfile來構(gòu)建一個鏡像。Docker的操作命令相對來說還是比較簡單,如果發(fā)布一個鏡像是就docker push 鏡像名,加載tar包就是docker load 包名,運行容器docker start 容器名,掌握這個圖上的命令基本就可以玩轉(zhuǎn)Docker了。而在這個里面比較復(fù)雜的就是用dockerfile構(gòu)建鏡像了,在這個圖里我們知道可以把一個運行的容器通過commit命令制作成一個新的鏡像,相對比較簡單,但是我們更建議使用dockerfile來構(gòu)建鏡像,為什么呢?下面簡單介紹一下如何用dockerfile構(gòu)建鏡像。
先我們看一下怎么用dockerfile構(gòu)建docker鏡像。只需要在當前目錄下創(chuàng)建一個Dockerfile,記住Dockerfile的首字母D是大寫,再使用命令build就可以創(chuàng)建新的鏡像了。
簡單講解一下Dockerfile的命令格式
FROM <image> 設(shè)置基本的鏡像,作為Dockerfile的第一條指令。比如右邊一個圖最底下,是基于ubuntu14.04版本創(chuàng)建的
RUN <command> 創(chuàng)建一層鏡像,RUN命令的運行過程等于把一個運行的容器commit為鏡像。比如右邊第二層和第三層,創(chuàng)建了兩層鏡像,第二層的鏡像是先基于第一層的鏡像啟動一個使用ubuntur操作系統(tǒng)的容器,然后在安裝ssh服務(wù),再用commit把第二層保存為一個鏡像。而第三是基于第二層鏡像啟動一個容器,然后安裝java,tomcat等服務(wù),再用commit保存為第三層鏡像。
ENV <key> <value> 用于設(shè)置環(huán)境變量
CMD指令指定你制作出來的鏡像在啟動成容器時運行命令的默認的參數(shù)。一個Dockerfile里只能有一個CMD,如果有多個,只有最后一個生效。docker run命令如果指定了參數(shù)會把CMD里的參數(shù)覆蓋
ENTRYPOINT cmd param1 param2 ... 也是設(shè)置在容器啟動時執(zhí)行命令
COPY復(fù)制本地主機的 <src> (為Dockerfile所在目錄的相對路徑)到容器中的 <dest> 。
EXPOSE <port> [<port>...] EXPOSE 命令可以設(shè)置一個端口在運行的鏡像中暴露在外,這樣容器外可以看到這個端口并與其通信。
前面我們說了,可以像玩樂高一樣玩docker,因為每一層的鏡像都是基本父鏡像形成的,他并不關(guān)心父鏡像具體是什么。我們可以一層一層的疊加鏡像,也可以根據(jù)需要增加刪除或修改每一層鏡像,再使用docker build生成一個最終版本的鏡像。所以強烈推薦在生產(chǎn)中使用dockerfile來構(gòu)建鏡像,一是可以通過dockerfile來“閱讀”鏡像,看一下鏡像里都包含了什么內(nèi)容,二是在升級等需要修改鏡像的場景下,直接優(yōu)化dockerfile的內(nèi)容,重構(gòu)新的鏡像就可以了。
在使用鏡像的時候為避免鏡像間依賴過深,建議三層,分別是基礎(chǔ)的操作系統(tǒng)鏡像、中間件鏡像和應(yīng)用鏡像,一個容器里只運行一個服務(wù)。環(huán)境干凈,結(jié)構(gòu)簡單,啟動快速。