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