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