注 4 http://martinfowler.com/bliki/BlueGreenDeployment.html
Blue-green deployment方法其實(shí)很簡(jiǎn)單,就是保持兩套一樣的生產(chǎn)環(huán)境,而實(shí)際上只有一套環(huán)境真正的對(duì)外提供服務(wù)(圖中綠色環(huán)境),而另一套環(huán)境則處于待機(jī)狀態(tài)(圖中藍(lán)色)。部署的時(shí)候,我們會(huì)先上線到藍(lán)色環(huán)境中,如果測(cè)試沒有問題了,再將路由切換到新的服務(wù)上。
Blue-green部署能帶來如下好處。
- 最小化停機(jī)時(shí)間
- 快速回滾
- hot standby
而未來的開發(fā)和部署和可能就會(huì)像下面這樣進(jìn)行了。
- ① 開發(fā)人員將代碼push到Git倉庫
- ② CI工具通過webhook得到最新代碼,構(gòu)建Docker鏡像并啟動(dòng)容器進(jìn)行測(cè)試。
- ③ 測(cè)試通過后將鏡像打標(biāo)簽后push到私有鏡像Registry
- ④ CI工具通知CD工具
- ⑤ CD工具通過Mesos/Marathon等進(jìn)行基于容器的部署
- ⑥ 測(cè)試沒有問題后進(jìn)行容器的切換(即Blue-green切換)
2. Docker架構(gòu)解析
2.1. Docker整體結(jié)構(gòu)
Docker是一個(gè)構(gòu)建、發(fā)布、運(yùn)行分布式應(yīng)用的平臺(tái)(見下圖),Docker平臺(tái)由Docker Engine(運(yùn)行環(huán)境 + 打包工具)、Docker Hub(API + 生態(tài)系統(tǒng))兩部分組成。
圖 Docker平臺(tái)
從圖中我們可以看到,Docker的底層是各種Linux OS以及云計(jì)算基礎(chǔ)設(shè)施,而上層則是各種應(yīng)用程序和管理工具,每層之間都是通過API來通信的。
Docker引擎
Docker引擎是一組開源軟件,位于Docker平臺(tái)的核心位置。它提供了容器運(yùn)行時(shí)以及打包、管理等工具。
Docker引擎可以直觀理解為就是在某一臺(tái)機(jī)器上運(yùn)行的Docker程序,實(shí)際上它是一個(gè)C/S結(jié)構(gòu)的軟件,有一個(gè)后臺(tái)守護(hù)進(jìn)程在運(yùn)行,每次我們運(yùn)行docker命令的時(shí)候?qū)嶋H上都是通過RESTful Remote API來和守護(hù)進(jìn)程進(jìn)行交互的,即使是在同一臺(tái)機(jī)器上也是如此。
Docker Hub
Docker Hub是一個(gè)云端的分布式應(yīng)用服務(wù),它專注于內(nèi)容、協(xié)作和工作流。Docker Hub除了可以托管、下載、查找Docker鏡像之外,還提供了包括更管理、團(tuán)隊(duì)協(xié)作、生命周期流程自動(dòng)化等功能,以及對(duì)第三方工具和服務(wù)的集成。
2.2. Docker鏡像(image)
2.2.1. Docker鏡像
Docker鏡像是Docker系統(tǒng)中的構(gòu)建模塊(Build Component),是啟動(dòng)一個(gè)Docker容器的基礎(chǔ)。
我們可以通過一個(gè)官方提供的示意圖來幫助我們來理解一下鏡像的概念。
Docker鏡像位于bootfs之上,實(shí)際上bootfs在系統(tǒng)啟動(dòng)后會(huì)被卸載的。Docker鏡像(Images)是分層的,這得益于其采用的聯(lián)合文件系統(tǒng),前面我們已經(jīng)介紹過了。鏡像是有繼承(父子)關(guān)系的,每一層鏡像的下面一層稱為父鏡像,沒有父鏡像的稱為基礎(chǔ)鏡像(Base Iamge,其實(shí)叫做Root Image可能更確切,不過這可能容易和rootfs混淆)。
2.2.2. 鏡像倉庫
我們可以將Docker鏡像倉庫理解為Git倉庫。Dcoker鏡像倉庫分為遠(yuǎn)程和本地,本地的概念好理解,而一般來說遠(yuǎn)程倉庫就是Registry,包括官方的或者自建的私有Registry;我們通過docker pull和docker push命令在本地和遠(yuǎn)程之間進(jìn)行鏡像傳輸。
Docker鏡像的命名規(guī)則和GitHub也很像。比如我們自己創(chuàng)建的倉庫名稱都是類似liubin/redis這樣格式的,前面的liubin是用戶名或namespace,后面是倉庫名。
不過我們前面已經(jīng)看到運(yùn)行的ubuntu鏡像的時(shí)候是倉庫名就是ubuntu,而不帶用戶名前綴,這是表明它是由官方制作的,或者由官方認(rèn)可的第三方制作的鏡像。我們可以認(rèn)為官方倉庫提供的鏡像都是安全的、最新的,所以也可以放心使用。
2.3. Docker容器(Container)
容器是一個(gè)基于Docker鏡像創(chuàng)建、包含為了運(yùn)行某一特定程序的所有需要的OS、軟件、配置文件和數(shù)據(jù),是一個(gè)可移植的運(yùn)行單元。在宿主機(jī)來看,它只不過是一個(gè)簡(jiǎn)單的用戶進(jìn)程而已。
容器啟動(dòng)的時(shí)候,Docker會(huì)在鏡像最上層掛載一個(gè)read-write的文件系統(tǒng),即上圖中標(biāo)記為writable的Container層,容器將跑在這個(gè)文件系統(tǒng)上。這層可寫的文件系統(tǒng)是容器中才有的概念,如果我們對(duì)此容器進(jìn)行commit操作,那么該層文件系統(tǒng)則會(huì)被提交為一個(gè)新的只讀的鏡像層,并位于鏡像層的最上面的。
我們可以認(rèn)為Docker鏡像是“靜”的".exe"文件,只在“硬盤”上;而容器是“動(dòng)”的,是在“內(nèi)存中”的,要想啟動(dòng)一個(gè)容器,需要先把".exe"裝載到內(nèi)存。
鏡像和容器具有如下的轉(zhuǎn)換關(guān)系:
- 鏡像 -> docker run -> 容器
- 容器 -> docker commit -> 鏡像
有時(shí)候我們經(jīng)常會(huì)將兩個(gè)名稱混用,不過這并不會(huì)影響我們的理解。
2.4. Docker Registry
Docker Registry是Docker架構(gòu)中的分發(fā)模塊,它用來存儲(chǔ)Docker鏡像,我們可以將它理解為GitHub。
Docker Hub是一個(gè)官方的Docker Registry,也是Docker鏡像的默認(rèn)存儲(chǔ)位置。
當(dāng)然從安全管理的角度上來說,我們可能更愿意在自己公司內(nèi)部托管一個(gè)私有的Docker Registry,這可以通過使用Docker官方提供的Registry注 5軟件實(shí)現(xiàn)。
注 5 Docker Registry https://github.com/dotcloud/docker-registry
運(yùn)行私有Registry非常簡(jiǎn)單,這也是一個(gè)典型的Docker風(fēng)格的應(yīng)用發(fā)布例子。
docker run –p 5000:5000 registry
3. 使用Docker
3.1. 初識(shí)容器
3.1.1. 創(chuàng)建并啟動(dòng)容器
這里我們假定各位讀者已經(jīng)在自己的機(jī)器上安裝好了Docker。Docker主要的命令就是docker了,它的參數(shù)很多,關(guān)于它的具體使用方法,可以參考官方的文檔注 6,這里我們只簡(jiǎn)單的介紹其中一些常用的用法。
注 6 https://docs.docker.com/reference/commandline/cli/ 和 https://docs.docker.com/reference/run/
啟動(dòng)一個(gè)容器很簡(jiǎn)單,我們只需要運(yùn)行docker run命令就可以了注 6。
注 6 為了方便區(qū)分,本文中運(yùn)行命令的時(shí)候如果提示符為$,表示實(shí)在宿主機(jī)(Ubuntu)中,如果是#,則表示是在Docker容器中
$ sudo docker run -i -t ubuntu /bin/bash Unable to find image 'ubuntu' locally Pulling repository ubuntu e54ca5efa2e9: Pulling dependent layers ... 省略 ... 6c37f792ddac: Download complete ... 省略 ... root@81874a4a6d2e:/#
docker run命令會(huì)啟動(dòng)一個(gè)容器。參數(shù)ubuntu指定了我們需要運(yùn)行的鏡像名稱,后面的bash則指定了要運(yùn)行的命令,注意這個(gè)命令是容器中的命令,而不是宿主機(jī)中的命令。參數(shù)-i用來為容器打開標(biāo)準(zhǔn)輸入以和宿主機(jī)進(jìn)行交互,-t則會(huì)為容器分配一個(gè)終端。
在第一次啟動(dòng)某鏡像的時(shí)候,如果我們本地還沒有這個(gè)鏡像,則Docker會(huì)先從遠(yuǎn)程倉庫(Docker Hub)將容器的鏡像下載下來,下載完成之后才會(huì)啟動(dòng)容器。
注意Docker里有一個(gè)很重要的概念就是容器ID或者鏡像ID,比如這個(gè)例子里的e54ca5efa2e9。這個(gè)ID是一個(gè)容器或者鏡像的唯一標(biāo)識(shí),它的長(zhǎng)度為64位,不過很多時(shí)候都可以簡(jiǎn)寫為12位,這也和Git很像。
3.1.2. 讓Docker容器在后臺(tái)運(yùn)行
這時(shí)候我們可以使用-d參數(shù)來通過守護(hù)模式啟動(dòng)一個(gè)容器,這樣容器將會(huì)在后臺(tái)一直運(yùn)行下去。這非常適合運(yùn)行服務(wù)類程序。如果需要,我們可以再通過docker attach命令連接到運(yùn)行中的容器。
3.1.3. 常用命令
docker ps
docker ps用來查看正在運(yùn)行中的容器。
從下面的輸出結(jié)果我們可以看出該容器狀態(tài)(STATUS列)為已經(jīng)停止執(zhí)行,且沒有錯(cuò)誤(Exited后面的狀態(tài)碼)。
$ sudo docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 60bab6f881e5 ubuntu:latest /bin/bash 14 minutes ago Exited (0) 5 seconds ago agitated_hopper
docker ps命令的常用參數(shù)(及組合)如下。
- -a: 查看所有容器,包括已經(jīng)停止運(yùn)行的。
- -l: 查看剛剛啟動(dòng)的容器。
- -q: 只顯示容器ID
- -l -q: 則可以返回剛啟動(dòng)的容器ID。
docker stop/start/restart
docker stop用來停止運(yùn)行中的容器,同時(shí)你還可以用docker start來重新啟動(dòng)一個(gè)已經(jīng)停止的容器。
docker restart可以重啟一個(gè)運(yùn)行中的容器。這就相當(dāng)于對(duì)一個(gè)容器先進(jìn)行stop再start。
3.2. 深入了解Docker鏡像
在對(duì)Docker容器有一個(gè)簡(jiǎn)單的感性認(rèn)識(shí)之后,我們?cè)賮砩钊肓私庖幌翫ocker鏡像的概念。
Docker鏡像實(shí)際上就是一個(gè)tarball,它是一個(gè)能完整運(yùn)行的OS系統(tǒng),這非常像OS或VM鏡像。它里面有基礎(chǔ)OS、各種軟件包及類庫等。我們啟動(dòng)一個(gè)容器,相當(dāng)于是啟動(dòng)了一個(gè)“基礎(chǔ)OS”。
3.2.1. 標(biāo)簽(Tag)
我們還可以為鏡像打標(biāo)簽,這也和Git非常相似。其實(shí)你也可能在前面留意到了,docker images的輸出中有一列就是TAG的。我們?cè)趫?zhí)行docker build或者docker commit的時(shí)候都可以同時(shí)為倉庫名稱指定一個(gè)TAG,格式為user_name/repo_name:tag,如果沒有指定這個(gè)TAG,則默認(rèn)為latest。
3.2.2. 常見鏡像操作
這里我們?cè)俳榻B一下對(duì)鏡像常見的一些操作。
查看本地鏡像列表
docker images命令用來列出當(dāng)前系統(tǒng)中的所有本地鏡像,即我們已經(jīng)通過docker run或者docker pull下載下來的鏡像,鏡像文件保存在本地的/var/lib/docker文件夾下。
下載鏡像到本地
只需要運(yùn)行docker pull命令即可,命令非常簡(jiǎn)單,問題在于你的網(wǎng)路速度和連通性。
docker rmi用來從本地倉庫中刪除一個(gè)不再需要的鏡像,即"rm image"的縮寫。
3.3. 構(gòu)建鏡像
我們可以創(chuàng)建自己的Docker鏡像,在我們