這篇文章我們來回顧一下 Docker 容器的發(fā)展之路,如何在巨頭中嶄露頭角,并在群雄逐鹿中脫穎而出的。在了解了這些歷史背景之后,我們就能明白 Docker 容器技術(shù)的必然性和其偉大之處。
1. 歷史環(huán)境:PaaS
我們從 2013 年開始說起,那時候云計算技術(shù)逐漸開始落地,各項主流技術(shù)也開始蓬勃發(fā)展。這里面的代表有如日中天的 AWS 和 IBM 的 OpenStack,以及后起之秀——誕生于 VMware 的 Cloud Foundry。我們下面以 Cloud Foundry 介紹一下 PaaS 的理念和核心問題。
Cloud Foundry 號稱是業(yè)界第一個開源 PaaS 云平臺,它支持多種框架、語言、運行時環(huán)境、云平臺及應(yīng)用服務(wù),使開發(fā)人員能夠在幾秒鐘內(nèi)進(jìn)行應(yīng)用程序的部署和擴(kuò)展,無需擔(dān)心任何基礎(chǔ)架構(gòu)的問題。該軟件最初由VMWare開發(fā),于2014年轉(zhuǎn)入 Pivotal 和 open source。2015年,Cloud Foundry 基金會成立。
PaaS 的核心在于為應(yīng)用提供一套打包和分發(fā)機(jī)制,Cloud Foundry 為多種主流語言都提供了一種打包格式。所謂打包就是將我們的應(yīng)用程序的可執(zhí)行文件和啟動腳本打成一個壓縮包,然后上傳到 Cloud Foundry 的存儲中心,然后有調(diào)度器選擇將壓縮包分發(fā)到特定的虛擬機(jī)上。
以 Cloud Foundary 為例,把本地的應(yīng)用部署到云端只需要一條命令:
$ cf push "應(yīng)用"
在應(yīng)用部署完之后,要在同一臺虛擬機(jī)上啟動來自不同用戶的多個應(yīng)用。Cloud Foundry 會使用 Linux 提供的 NameSpace 和 CGroup 技術(shù)對不同的應(yīng)用進(jìn)行隔離和資源限制。
稍微了解容器技術(shù)的同學(xué),應(yīng)該知道 Linux 容器也是采用了這個技術(shù)。嚴(yán)格意義上來說,Cloud Foundry 也是使用的容器技術(shù)。所以在 Docker 誕生之后,Cloud Foundry 的產(chǎn)品經(jīng)理 James Bayer 就在社區(qū)進(jìn)行了一次詳細(xì)的對比,最后結(jié)論是 Docker 使用的技術(shù)和 Cloud Foundry 沒有本質(zhì)區(qū)別,不值得關(guān)注,言下之意就是 Docker 難以撼動 Cloud Foundry 的市場地位。但是我們知道,這次Cloud Foundry 判斷錯了。
2. 嶄露頭角
Docker 確實是也是使用 NameSpace 和 CGroup 技術(shù)沒有錯,但是 Docker 相比于各種 PaaS 云平臺技術(shù)的核心優(yōu)勢其實是它的鏡像技術(shù)。正是利用鏡像技術(shù),Docker 在誕生之后的幾個月在各大 PaaS 軟件還沒有反應(yīng)過來的時候迅速占領(lǐng)市場的有力地位。
PaaS 軟件在運行用戶的應(yīng)用上和 Docker 技術(shù)沒有區(qū)別,但是在打包應(yīng)用上卻顯得極為繁瑣。以 Cloud Foundry 為例,它為不同的主流編程語言定義不同的打包方式,維護(hù)起來極其繁瑣且容易出問題,用戶往往也是因為這個環(huán)節(jié)苦不堪言。那么 Docker 鏡像是如何解決這個問題呢?
很簡單,Docker 鏡像是一套操作系統(tǒng)文件 + 應(yīng)用程序。鏡像一般都會有一個 base 鏡像,而這個 base 鏡像一般都是操作系統(tǒng)或者其 mini 版本。這樣如果我們的應(yīng)用在云端是運行在 Centos 7.4 上面,那么我們的 base 鏡像直接使用 Centos 7.4 的操作系統(tǒng)文件即可,這樣就解除了用戶要保持本地環(huán)境和云上環(huán)境的底層一致的心智負(fù)擔(dān)。簡單來說,Docker 鏡像的精髓是保證了環(huán)境的一致性。
除此之外,Docker 鏡像技術(shù)是一套統(tǒng)一的技術(shù),我們再也不需要根據(jù)我們應(yīng)用的開發(fā)語言不同而選擇不同的打包方式。在 Docker 中,我們打包鏡像使用的技術(shù)叫做 Dockerfile 技術(shù),我們只需要按照鏡像技術(shù)的規(guī)范去編寫 Dockerfile 即可,下面是一個簡單的 Dockerfile 例子。
FROM quay.io/prometheus/busybox:glibc
LABEL maintainer="The Prometheus Authors <prometheus-developers@googlegroups.com>"
COPY node_exporter /bin/node_exporter
EXPOSE 9100
USER nobody
ENTRYPOINT [ "/bin/node_exporter" ]
其中第一行 FROM 就是引用基礎(chǔ)鏡像,這里的 busybox 就是一個精簡版的操作系統(tǒng)鏡像,相當(dāng)于 Docker 鏡像中 hello world。第 4 行將我們本地的可執(zhí)行文件拷貝到鏡像中;第 6 行和第 7 行設(shè)置端口和用戶,最后一行設(shè)置應(yīng)用的啟動入口。如果沒有看懂,也沒有關(guān)系,后面我們會針對 Dockerfile 的編寫有一節(jié)單獨的內(nèi)容說明。
3. 群雄逐鹿
容器技術(shù)的繁榮促進(jìn)其生態(tài)快速發(fā)展,這其中最重要的就是容器編排技術(shù)。對于規(guī)模稍微大一點應(yīng)用,在生存環(huán)境中,需要發(fā)布的容器數(shù)量很可能極其龐大,這也就意味著如何管理容器之間的聯(lián)系和拓?fù)浣Y(jié)構(gòu)并不是一件簡單的事情。
舉個例子,對于一個成熟的 web 應(yīng)用,首先要具備高可用架構(gòu),其次其內(nèi)部可能包含數(shù)據(jù)庫、緩存等各種依賴,除此之外還有很多運維管理需求,比如監(jiān)控告警等。對于這些操作,通過人工來管理肯定是不現(xiàn)實的。這個時候我們就需要定義容器集群的編排和部署工具。
說到容器編排,不得不提三劍客:Compose、Machine 和 Swarm。
compose
為了展示容器編排的內(nèi)容,我們這里簡單展示一下如何去部署一個 Flask 應(yīng)用。Flask 應(yīng)用如下,定義一個 route:"/",當(dāng)訪問到來的時候,redis 會做一個訪問計數(shù)。
from flask import Flask
from redis import Redis
import os
app = Flask(__name__)
redis = Redis(host='redis', port=6379)
@app.route('/')
def hello():
redis.incr('hits')
return "Hello World. I am a Flask App!"
if __name__ == "__main__":
app.run(host="0.0.0.0", debug=True)
然后我們要生成一個 Docker 鏡像,打包鏡像只需要定義一個 Dockerfile,如下:
From python:2.7
ADD . /code
WORKDIR /code
RUN pip install flask
RUN pip install redis
最后我們通過 compose.yml 來定義部署拓?fù)洹?/p>
web:
build: .
command: python app.py
ports:
- "5000:5000"
volumes:
- .:/code
links:
- redis
redis:
image: redis
這里是編排部署的核心,這里定義了兩個最高級別的 key: web 和 redis,也就是說 compose 定義了由兩個 ”服務(wù)“ (web 和 redis)組成的 Docker ”集群“。這里 compose 部署的 docker 集群其實是在一臺機(jī)器上的。
通過 compose 定義完 Docker 集群之后,通過 docker-compose up 就可以將服務(wù)部署起來了。限于篇幅,我們這里不對這些 yaml 的編排規(guī)范做過多解釋。
Machine
Machine 的主要功能是幫助用戶在不同的云主機(jī)提供商上創(chuàng)建和管理虛擬機(jī),并在虛擬機(jī)中安裝 Docker。除了搭建環(huán)境之外,Machine 還可以幫助用戶配置 Docker 的連接參數(shù)等、管理 Docker 主機(jī),比如啟動、關(guān)閉、重啟、刪除等操作。
Machine 簡單來說就是一個統(tǒng)一管理工具,成功的關(guān)鍵與否還是要看各大云廠商買不買單,接不接入。目前來看,情況不太樂觀。
Swarm
Swarm 也是 Docker 集群的編排和管理工具,和 Compose 不同的是,這里的集群是真正的由多個主機(jī)組成的集群。
Docker 生態(tài)依托 Compose、Machine 和 Swarm 重新定義了一個容器生態(tài)的 PaaS。
Marathon
但是現(xiàn)實往往伴隨著攪局者,這個攪局者就是 Mesosphere,Mesosphere 公司成名作是 Mesos,屬于 Apache 社區(qū),具有和大數(shù)據(jù)調(diào)度系統(tǒng) Yarn 一樣的統(tǒng)治力。但是 Mesos 由于其初衷是針對大數(shù)據(jù)系統(tǒng)的調(diào)度,讓它來處理 Docker 典型應(yīng)用 long-running 應(yīng)用并不是具有太大的優(yōu)勢。
Mesosphere 隨之提出了針對容器系統(tǒng)編排的 Marathon,相比如 Docker 公司這個年輕后生,Mesosphere 具有足夠大規(guī)模集群資源管理的經(jīng)驗,2014 年,Mesos 就能夠管理 10000 規(guī)模的集群調(diào)度。
Marathon 誕生之后就以 Swarm 的強力競爭者而存在,但是由于 Mesosphere 公司的 Apache 屬性,生態(tài)顯得略微封閉,同時 Swarm 作為 Docker 的親兒子,與 Marathon 競爭起來并沒有顯現(xiàn)出過多的頹勢,說勢均力敵也不為過。
Kubernetes
真正的轉(zhuǎn)機(jī)出現(xiàn)在 2015 年,隨著 Docker 技術(shù)的火熱,容器領(lǐng)域的玩家開始對 Docker 公司體現(xiàn)出的強硬態(tài)度開始不滿。自然而然地,幾家大公司開始商量對 Docker 公司的話語權(quán)進(jìn)行切割,于是一個中立的基金會成立了,這個基金會叫做 CNCF。
CNCF,全名 Cloud Native Computing Foundation,由 Google、RedHat 等開源基礎(chǔ)設(shè)施領(lǐng)域玩家們共同發(fā)起。這個基金會的目的其實很容易理解:它希望以 Kubernetes 項目為基礎(chǔ),建立一個由開源基礎(chǔ)設(shè)施領(lǐng)域廠商主導(dǎo)的、按照獨立基金會方式運營的平臺級社區(qū),來對抗以 Docker 公司為核心的容器商業(yè)生態(tài)。而為了打造出這樣一個圍繞 Kubernetes 項目的“護(hù)城河”,CNCF 社區(qū)就需要至少確保兩件事情:
- Kubernetes 項目必須能夠在容器編排領(lǐng)域取得足夠大的競爭優(yōu)勢;
- CNCF 社區(qū)必須以 Kubernetes 項目為核心,覆蓋足夠多的場景。
Kubernetes 項目就是在這樣的環(huán)境下開始飛速成長。但是這并不是說 Kubernetes 是靠著 “政治” 取勝的,Kubernetes 項目具有非常優(yōu)秀的設(shè)計理念和健康的社區(qū)基礎(chǔ),其對于開發(fā)者的開放態(tài)度也迅速吸引著大量的開發(fā)者。
from flask import Flask
from redis import Redis
import os
app = Flask(__name__)
redis = Redis(host='redis', port=6379)
@app.route('/')
def hello():
redis.incr('hits')
return "hello world. I am a Flask App"
if __name__ == "__main__":
app.run(host="0.0.0.0", debug=True)
4. 鹿死誰手
鹿死誰手,這兩年市場已經(jīng)告訴了我們答案,贏家是 Kubernetes 了。Kubernetes 憑借其優(yōu)秀的設(shè)計理念和 Google 背后的技術(shù)領(lǐng)導(dǎo)力加持,迅速在市場上占據(jù)大部分和容器相關(guān)場景。早期的容器編排軟件逐漸退出了歷史舞臺,比如阿里云的容器服務(wù)就宣布自 2019 年 12 月份將不再支持 Swarm,而形成鮮明對比的是,基于 Kubernetes 的產(chǎn)品形態(tài)卻越來越多。
Kubernetes 的特點主要包括如下幾個方面:
優(yōu)秀的設(shè)計理念
Kubernetes 并不是憑空設(shè)計出來的,其前身來自 Google 內(nèi)部的 Borg 和 Omega 系統(tǒng)。我們可以看一下下圖,感受一下 Borg 和 Omega 系統(tǒng)在 Google 內(nèi)部的位置。
Borg 亦或是 Omega 在 Google 內(nèi)部是作為底層調(diào)度系統(tǒng)存在的,并且是經(jīng)過系統(tǒng)內(nèi)部多年的生產(chǎn)環(huán)境考驗的。Kubernetes 相當(dāng)于 Borg 和 Omega 針對容器的調(diào)度系統(tǒng),這里的容器可以是 Docker 也可以是其他 Linux 容器,目前默認(rèn)是 Docker。關(guān)于優(yōu)秀的設(shè)計理念,這里簡單舉一個例子:Pod。
如果我們有兩個容器 A 和 B 需要調(diào)度在一臺機(jī)器上,其中 A 和 B 各需要內(nèi)存 1 G,但是這個時候機(jī)器只有 1.5 G,如果先調(diào)度 A,A 是可以被調(diào)度到機(jī)器上的,但是后面的 B 調(diào)度卻會有問題了。要想解決這個問題,如果 A 和 B 單獨調(diào)度,往往需要考慮非常多的場景和復(fù)雜的解決算法。
Kubernetes 針對這個問題提出了 Pod 的概念,Pod 是一組容器的集合,類似進(jìn)程組,Pod 是 Kubernetes 中一個最小的調(diào)度單位,也就是說同一個 Pod 中的不同容器一定會被調(diào)度到一臺機(jī)器上,并且可以互相共享 NameSpace。Pod 的提出解決了很多調(diào)度需要考慮的復(fù)雜問題。
聲明式 API
所謂聲明式 API,是直接描述我們要什么或者要達(dá)到一種什么狀態(tài)。與之對于是命令式 API,命令式 API 對應(yīng)的則是一個具體的動作:比如 “創(chuàng)建容器”,“修改容器” 等。
聲明式 API 的一個典型例子就是 SQL,在下面的 SQL 中,我們直接說明我們需要的東西是 a, b, c 三個字段,現(xiàn)在條件是 id > 123。但是我們并不需要管這條 SQL 如果是數(shù)據(jù)庫引擎中的真正的執(zhí)行計劃是什么樣的。
select a, b, c from table where id > 123;
那么在 Kubernetes 中,我們?nèi)绾稳ヂ暶魑覀冃枰裁茨兀肯旅媸且粋€創(chuàng)建兩個 nginx 副本的 Deployment。其中的 replicas 就是我們最終需要的副本數(shù),也就是最終狀態(tài)需要有兩個副本運行,Kubernetes 如何去保證,我們不需要關(guān)心。
apiVersion: apps/v1 # for versions before 1.9.0 use apps/v1beta2
kind: Deployment
metadata:
name: nginx-deployment
spec:
selector:
matchLabels:
app: nginx
replicas: 2 # tells deployment to run 2 pods matching the template
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.7.9
ports:
- containerPort: 80
聲明式 API 的最大好處是解放開發(fā)者的心智負(fù)擔(dān),讓編程變得簡單。
開放的生態(tài)
我們看一下下面的 Kubernetes 分層架構(gòu)圖。
在上圖的最底部包含了很多開放生態(tài),包括容器運行時,網(wǎng)絡(luò)插件,存儲插件、以及云廠商 Provider。這樣各大云廠商在將 Kubernetes 運行在自己的云上環(huán)境的時候就可以很方便的將自己的存儲以及網(wǎng)絡(luò)產(chǎn)品以一種插件式的方式集成到 Kubernetes 中。
Kubernetes 正是靠著這種開放的開發(fā)者生態(tài),吸引了越來越多的開發(fā)者投入其中,并樂此不疲。反過來,生態(tài)的繁榮也促進(jìn) Kubernetes 在歷史車輪的推動下不斷向前。
5. 結(jié)語
在本篇文章中,我們回顧了 Docker 技術(shù)的誕生和發(fā)展,以及 Kubernetes 的簡介。后面專欄中,這些內(nèi)容我們將會進(jìn)行由淺入深的討論。