使用 Docker 構(gòu)建你自己的 PaaS 平臺
首先,我要澄清一點,Docker 自身并不是“平臺即服務(wù)”(PaaS),而是其中重要的組件,使得部署 PaaS 更為簡單。
其次,從我個人觀點出發(fā),我在這里談到的所謂構(gòu)建“屬于自己的 PaaS ”是指像我這樣運行一些博客或是 Wordpress 站點。這些站點并非為了商業(yè)或盈利目的,單純用于朋友間交流,并且我有一臺聯(lián)網(wǎng)的服務(wù)器硬件設(shè)備來運行這些網(wǎng)站。我覺得這臺服務(wù)器如果提供一個 Wordpress 的 PaaS 服務(wù),能夠在需要的時候自動進行橫向擴展那該是很有意思的實情。
從某種程度上來說,我所做的不過是為證明 wordpress 可以作為服務(wù)來提供,對于單個主機而言可以支持幾乎所有的應(yīng)用,并且有可以自動擴展到多主機的能力。
為什么不直接用 Dokku
Dokku 是一個由 Jeff Lindsay 寫的在單主機環(huán)境下構(gòu)建 PaaS 的軟件。
注: Dokku 是由 Docker 支持的迷你版 Heroku ,最小的 PaaS 實現(xiàn)工具。
雖說 Dokku 可能是最好的實現(xiàn)工具,但我不會用它,而是自己設(shè)計最小的系統(tǒng)以及創(chuàng)建 low-fi PaaS 所需的組建。而諸如 Deis 、Flynn 和 其它 的容器管理系統(tǒng)也都各具特色。
組件
以下是一些你在構(gòu)建自己的 PaaS 時可能會用到的組件。
- 泛域名解析( Wildcard DNS entry )
- Web 路由器(例如 Hipache )
- Docker - 應(yīng)用服務(wù)器/容器,創(chuàng)建鏡像
- 應(yīng)用源碼
- 環(huán)境變量
- 數(shù)據(jù)存儲,例如 MySQL 、NoSQL 、對象存儲等
- 把上邊的組件組合在一起的東西
泛域名解析
首先,你需要的是對一個域名的泛域名解析。 這篇文檔 介紹了如何使用 Namecheap 相關(guān)配置。 Namecheap 是我的注冊服務(wù)商(其他一些注冊管理服務(wù)商也有類似服務(wù),支持雙因子認證),我購買了一個類似于“ somedomainapp.com ”這么一個域名來運行我的應(yīng)用,并且使用一個 Namecheap 提供的泛域名解析服務(wù)。
很顯然,在一個大型的生產(chǎn)環(huán)境中,你必須管理自己的域名服務(wù)或是使用諸如 Google的DNS服務(wù)(我喜歡),或是其他一些諸如負載均衡的設(shè)備。
至此,你有了一個泛域名解析到你指定的服務(wù)IP地址,例如 *.yourdomainapp.com
Web路由
(上圖為 hipache 的 non-existent domain 頁面)
我不知道改如何稱呼這一層, Heroku 稱它為 HTTP 路由 ,我想這個名字合適。
本質(zhì)上講,它的工作就是將輸入請求路由到正確的 web 服務(wù)器,在我們的例子中也就是 docker 容器。一個請求了 someapp.somedomainapp.com 的請求可能被送到 127.0.0.1:49899 或是 172.17.0.3:80 或其它,這背后都是docker 容器。
在我們的案例中,我使用 hipache ,它后臺使用 redis 。這也就是說你在 hipache 中添加路由也就是把這些規(guī)則添加到 redis 里邊,并且 hipache 不需要重啟,因為它可以查詢 redis 以獲取域配置。默認情況下 hipache 允許使用通配符域名,所以它可以路由任何請求并且如果目標(biāo)不存在則發(fā)送到默認頁面。
我的 PoC Python 腳本被稱為“ wpd ”,它能夠輸出在 redis 中存儲的 hipache 鍵配置。以下的輸出意味著 hipache 隨機將對 someapp.yourdomainapp.com 的請求平均分布到兩個容器之中,如下:
- $ wpd listkeys
- someapp.yourdomainapp.com
- ===> http://127.0.0.1:49156
- ===> http://127.0.0.1:49157
- $ redis-cli lrange someapp.yourdomainapp.com 0 -1
- 1) "someapp.yourdomainapp.com"
- 2) "http://127.0.0.1:49156"
- 3) "http://127.0.0.1:49157"
有其他很多可以做 web 路由的方法。 Dokku 使用 nginx ,還有使用 etcd 的 vulcand ,這個新東西著實讓人興奮。 Hipache 支持 SSL ,不過幾周前 Vulcand 還不支持,但我想這肯定是在計劃內(nèi)的,因為我是 golang 的粉絲,所以相對有些偏心 ;) 。
Docker!
再來比較一下 Heroku 和我們正在做的實情,我認為 Docker 能夠扮演 buildpack 和 dyno 的角色,雖然也許嚴(yán)格說來 buildpack 不包含應(yīng)用代碼,或者更確切的說只有應(yīng)用運行所需的環(huán)境。把 Dockerfile 看作是一種 buildpack 也許更容易理解。
以我的 wordpress 為例,Dockerfile 文件可以創(chuàng)建 docker 鏡像以用來生成一個運行 wordpress 應(yīng)用的容器,比如使用 apache2 + php。
Docker 管理容器,并且提供網(wǎng)絡(luò)以及網(wǎng)絡(luò)地址轉(zhuǎn)換以將 apache2 的端口暴露給 web 路由。
所以 docker 為我們做了很多事情。沒有 docker 的話,我們可能需要寫代碼以實現(xiàn)一個創(chuàng)建虛擬機鏡像的方法,并且還得管理啟動、網(wǎng)絡(luò)、實例化等諸多的實情,這就跟寫一個簡單的 packer 或 libvirtd ( kvm 或 lxc ) 一樣了,就像 openstack 做的那樣。毫無疑問那將耗費更多的資源。(有意思的是 packer 也能夠創(chuàng)建 docker 鏡像)
Application source code
應(yīng)用源代碼
在 Dokku 中,代碼是被 push 到一個 git 容器中,并啟動其他的進程,這也是 Heroku 的做法。這些進程將代碼放置到應(yīng)用容器之中。
然而,在我的 wordpress 案例中, wordpress 的代碼是可通過起始腳本下載的。一旦容器從 wordpress 鏡像啟動,起始腳本就開始運行:
- if [ ! -e /app/wp-settings.php ]; then
- cd /app
- curl -O http://wordpress.org/latest.tar.gz
- tar zxvf latest.tar.gz
- mv wordpress/* /app
- rm -f /app/wordpress
- chown -R www-data:www-data /app
- rm -f latest.tar.gz
- fi
看看代碼吐吐槽,用來下載的 url 應(yīng)該從環(huán)境變量中獲取而不是如上例直接寫在代碼里邊。
git 的 push/receive 風(fēng)格可能在 PaaS 中更有效,但是我還沒有深入去研究那是怎么做到的。 Jeff Lindsay 有一個工具 gitreceive ,并且Flynn ( Jeff 的另一個項目)有 gitreceived 。他還有 execd 和其他項目,真是大忙人!
顯然,有很多方法可以講代碼放到容器中去執(zhí)行。如果要說 PaaS 有什么重要的事情,那么就是運行代碼了。
#p#
環(huán)境變量
我認為 docker 鏡像應(yīng)該會變得相當(dāng)普及。同時你不想將敏感的配置信息諸如密碼等放到鏡像之中,所以它們應(yīng)該從環(huán)境變量中獲取,并且這些變量需要通過某種方式注入容器環(huán)境中。
在我的 wordpress 例子中,我設(shè)定了 docker 的環(huán)境變量。 Docker 可以用“ -e ”參數(shù)運行命令,以使得設(shè)定環(huán)境變量的方式暴露出來,來看下面的例子:
- $ docker run -e FOO=bar -e BAR=foo busybox env
- HOME=/
- PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
- HOSTNAME=6cf2d6e8acb3
- FOO=bar
- BAR=foo
我的 wordpress 啟動腳本檢查以下幾項環(huán)境變量:
- DB_NAME=${DB_NAME:-"wordpress"}
- DB_USER=${DB_USER:-"wordpress"}
- DB_PASSWORD=${DB_PASSWORD:-"wordpress"}
- DB_HOST=${DB_HOST:-$1}
并且使用它們和正確的數(shù)據(jù)庫設(shè)置創(chuàng)建 wordpress 的配置文件。
稍后我會談及“如何將所有部分整合”。我通過一個 Python 腳本完成,在這個腳本中,我設(shè)置了容器的環(huán)境變量。
下面的是 python 代碼的一些片段,用環(huán)境變量初始化容器:
- env = {
- 'DB_HOST': MYSQL_HOST,
- 'DB_NAME': dbname,
- 'DB_USER': dbname,
- 'DB_PASSWORD': dbpass,
- }
- container = dockerConn.create_container(image, detach=True, environment=env)
可以使用 docker-py 來聯(lián)合使用 docker 和 python。
另一種方法是是使用共享配置系統(tǒng),諸如我之前提到的 etcd 。
注: etcd 是一個高可用的鍵值存儲系統(tǒng),用于共享配置和服務(wù)發(fā)現(xiàn)。
etcd 能夠存儲配置信息; confd 則是一個配置管理代理軟件,能夠查詢 etcd 以生成針對應(yīng)用的 配置文件,并且能夠使用這些配置文件重啟服務(wù)。
說了這么多,我認為 環(huán)境/配置 變量是 PaaS 的核心部分,諸如 etcd 、confd 和 consul 都將是重要的項目組件。但是,對于本文所說的 wordpress 例子而言,我們只是做一個簡單的驗證系統(tǒng),環(huán)境變量從容器運行時中獲取。然而,我非常建議大型的 PaaS 或是其他類 PaaS 系統(tǒng)能夠使用 consul 或 etcd 這樣的組件。
數(shù)據(jù)存儲
如果你的應(yīng)用需要存留數(shù)據(jù),那么將數(shù)據(jù)放在某個地方就是必然的了,但是使用應(yīng)用容器來存儲顯然不是一個好選擇。通常來說,我認為有兩種解決方案。
- 另一個容器
- 一個單獨的服務(wù)
對于“一個單獨的服務(wù)”而言,我指的是諸如亞馬遜 RDS 或 OpenStack Trove (二者都是數(shù)據(jù)庫即服務(wù))或諸如 OpenStack Swift 那樣的對象存儲系統(tǒng)。簡而言之就是有第三方管理的服務(wù),或者 Docker 也運行其上的服務(wù)器。
另一個選擇是使用“另一個 docker 容器”。再拿 wordpress 來做例子,我可能不只是啟動一個應(yīng)用容器,而是啟動第二個包含 MySQL 服務(wù)器的容器(或者兩個服務(wù)運行在一個容器中)。也許 MySQL 的服務(wù)是一個容器,也許那是一個通過 Ansible 配置的硬件服務(wù)器,誰知道呢。 docker 同樣也推薦使用卷( volume ),尤其是當(dāng)數(shù)據(jù)不會分布到多個容器中的時候;如果數(shù)據(jù)分散,那么就是用 MySQL 或是 openstack swift 吧。
我認為這幾種方式都 OK ,但是我更傾向于使用一個單獨的服務(wù)。正因如此,在我的例子中,存在一個單獨的 MySQL 服務(wù)器,所有的 wordpress 應(yīng)用都會連接它,每個應(yīng)用都有其自己的數(shù)據(jù)庫。或許這個單獨的服務(wù)也是用 docker 來完成的。
把上面講的內(nèi)容都串起來
我用一個名為 wpd 的腳本來串起所有環(huán)節(jié):
- 在 wpd 數(shù)據(jù)庫中傳經(jīng)一個站點記錄
- 為這個 wordpress 站點創(chuàng)建一個數(shù)據(jù)庫存儲
- 創(chuàng)建多個 wordpress 容器
- 將環(huán)境變量傳送給這些容器,讓它們知道該如何連接數(shù)據(jù)庫
- 將站點添加到 redis 以便 hipache 能夠做路由/負載均衡
- $ wpd -h
- usage: wpd [-h] {listkeys,addsite,listsites,addimage,deploysite,dumpsite} ...
- positional arguments:
- {listkeys,addsite,listsites,addimage,deploysite,dumpsite}
- listkeys list all the keys in redis
- addsite add a site to the database
- listsites list all the sites in the database
- addimage add a docker image to the database
- deploysite startup a sites containers
- dumpsite show all information about a site
- optional arguments:
- -h, --help show this help message and exit
正如你所見,有一些選項,諸如“ addsite ”和“ deploysite ”還沒有完全弄完。添加站點僅僅是將其放到 wpd 數(shù)據(jù)庫中;部署站點意味著啟動容器,并且向 redis 添加了信息以便 hipache 能夠?qū)?http 請求路由給它們。
這看起來想是一個大型系統(tǒng)…… 我不太確定哈??雌饋砀袷且粋€用戶管理系統(tǒng),因為用戶能夠擁有站點,站點能夠有名字、容器、鏡像和數(shù)據(jù)存儲等。
問題
這里有幾個問題我必須提一提(也可能我沒有全概括到)。
- 日志
從 docker 之外獲取日志仍舊無解。所以在這里你可能需要在容器中配置 syslog 將日志記錄到一個中心系統(tǒng)中。我期望 docker 這邊能夠想辦法解決日志的問題。
- 文件系統(tǒng)
Wordpress 是一個很好的 Web 應(yīng)用示例,它很難擴展,因為它依賴文件系統(tǒng)做數(shù)據(jù)存儲,例如用戶上傳的多媒體文件。為了跨多個 docker 主機擴展文件系統(tǒng)你需要一個分布式的文件系統(tǒng),這個文件系統(tǒng)必須能夠支持動態(tài)的擴展,這很令人頭疼。所以我建議不用文件系統(tǒng)而是使用對象存儲,例如 OpenStack Swift ,其實它并不是這么難搭。但是 Swift 并不能同時保證一致性和可用性。
- 數(shù)據(jù)安全
我不確定什么是最好的保證數(shù)據(jù)安全的做法。一些密碼或其他重要的配置信息都需要注入容器中,并且需要存儲在諸如 etcd 的系統(tǒng)中,可能會被看到。
小結(jié)
在最后,我認為 docker 用來做 PaaS 組件是非常棒的,對我來說,它簡化了將自己的小型平臺改造成服務(wù)提供者的過程。你所需要做的就是 web 路由、 dockerfile 、 docker 主機、將應(yīng)用放到容器內(nèi)的方法,準(zhǔn)備好之后你就能夠做自己的 PaaS 了。請記住將你的容器做的盡量通用,多用環(huán)境變量和配置變量(或是從其他什么地方獲取到相關(guān)信息),并且盡可能避免使用文件系統(tǒng)。
未來展望!
很顯然我拉下了一些東西。搭建一個驗證系統(tǒng)和實際弄一個生產(chǎn)用的 PaaS 還是有非常大的區(qū)別的。我還沒有提到諸如“服務(wù)發(fā)現(xiàn)”和“容器調(diào)度”,這些偏理論的東西可以用 etcd 或是 libswarm 來幫助你解決,雖然我不清楚 libswarm 是否能做容器調(diào)度器。最近 Google 開源了 Kubernetes ——一個 docker 集群的管理工具,但是它現(xiàn)在仍然只能運行在 GCE 上。 Apache 的 Mesos 同樣也只能運行在它的 Deimos 項目上。長遠來看,CoreOS 有 fleet 。同樣我也沒有談到諸如資源限制問題,如容器所用內(nèi)存、CPU 等其他的細節(jié),不過我計劃深入研究。
這篇文章由 serverascode 撰寫, 邵靖 翻譯。點擊 這里 可閱讀原文。
文章出自:https://www.dockboard.org/build-your-own-platform-as-a-service-with-docker/