應用開發的先鋒:容器和Kubernetes的故事
本文介紹了容器和kubernetes的底層概念, 以及它們如何給應用開發提供了新的模式.
容器就是新的進程
讓我們從計算機開聊. 當計算機啟動時, 它會運行一個叫init的程序. 然后init會啟動其他所需的程序: 服務器, 終端, 窗口管理器等. Init能做幾件有趣的事情, 例如讓一個程序開機啟動, 隔一段時間運行一個程序, 還有確保一個程序沒有失敗或者crash, 如果有就重啟它. 正在運行的程序可以看到這臺機器上的所有東西: 其它在運行的程序, 所有的文件, 以及網絡.

多個進程同時跑在一臺計算機上. 所有的進程可以自由的互相之間交互, 或者與常規的資源交互.
通過將進程進行劃分,程序員可以有一個更加簡單的模型來方便理解, 所以創建命名空間(namespace)的工具也被開發出來了. 程序或者進程只能看到運行在同一個命名空間下的其他進程. 如果它們尋找文件, 那么只能看見硬盤上分配到這個命名空間的那一部分. 從安全的角度而言, 一個命名空間里面的某個進程被黑掉了影響的僅僅也只是這個命名空間而已.
類似于Docker和Rkt這樣的工具被開發出來以后使得我們能系統化地使用這些特性. 這些工具提供了打包的功能, 將一個命名空間打包成一個容器, 使得我們可以很方便的將它搬到另一臺機器上運行, 不出意外的它會跟之前完全一致的方式繼續運行, 因為它本身的隔離特性. 事實上, 通常可以很容易的將容器想象為可以完全獨立的運行的小計算機. 因為這些新的工具非常易用, 它們就成為了一種非常流行的構建軟件的方式.
容器就是新的進程.

容器中的進程.在這里, 一個進程僅僅能夠與所在同一個容器里面的其他進程和資源交互
擴展: 一個好”難題”
一臺計算機的資源是有限的, 而且同時僅能處理有限的數據和運行有限的進程. 當面臨增長的負載時(比如更多用戶, 更大的數據集)一個簡單的應對方式是垂直擴展, 也即是增加更多的處理能力和內存給到這臺計算機, 但是很快這個代價就會非常昂貴,而且本身擴展的空間也相當有限. 另一種方式就是通過增加更多的計算機來水平擴展. 這些計算機一起就組成了集群.
為了能跑在集群上,應用也需要以不同的方式架構. 例如, 如果我們確認同一個程序的兩份拷貝可以需要訪問對方的數據就能運行, 那么我們就能放心的將它的多份拷貝放到不同的計算機上運行.

水平擴展:在這里集群里, 三臺計算機每臺運行兩個容器. 一共有兩個app server的實例來處理大的負載.
雖然容器本身并沒有給我們任何其他的工具來構建分布式應用, 但是考慮一下這個級別上的抽象能讓構建集群的應用方便一些. 容器模型所鼓勵的模型是:
- 可以有多份拷貝同時運行(架構要考慮并發性)
- 容器可以在集群中的任意一臺機器上動態啟動和停止(最好是無狀態或者臨時的), 而且
- 計算機或者進程可能會在任意的時間點失敗或者不可用但是整個系統仍然保持工作(架構要考慮失敗和恢復)
由于在集群里面有這么多的計算機要管理, 我們面臨一些額外挑戰:
- 首先, 我們需要管理計算機上的資源, 比如處理能力和存儲. 這意味著我們不得不有效地分發和調度進程到不同的計算機上去執行.
-
我們也需要”親和性”和方法將相關的進程放在一起跑,以便高效利用共享存儲; 而同時”反親和性”的要求又需要保證對同一個資源有競爭性的進程不能運行在同一臺機器上.
例如, 如果我們想要將應用服務器的進程跑兩份來服務兩倍的請求, 我們可能希望他們跑在集群里兩臺不同的服務器上. - 當許多的進程跑在不同的地方時, 我們需要一種方式讓他們互相發現和溝通. 我們只需要某個進程運行所在的機器ip就可以與這個進程通信.
在只有一臺計算機的時候, 只有一個ip地址就可以了. 在有多個計算機之后,我們需要維護一個進程到ip的映射, 例如像etcd這樣的分布式數據庫. 當一個進程在一臺機器上啟動時, 這個信息就被加入到數據庫中. 如果進程掛掉或者機器宕機, 也需要將這個條目從數據庫中刪除.
程序員對于開發跑在一臺計算機上的應用很得心應手了. 理想狀態下,我們想要的是有一個工具能將集群里面所有的計算機管理起來,而展現給程序員的就像一臺”巨型”的計算機.
這個方向上的一個進展是CoreOS的Fleet項目, 它的基本思想就是像一臺計算機上的init進程那樣延伸做整個集群的init.
Google 貢獻的Kubernetes項目則讓我們更加接近我們想要一臺”巨型”計算機的模型.
Kubernetes: pod就是新的計算機
Kubernetes做的第一件事情就是拿走你的所有計算機, 然后還回給你一個”巨型”計算機--一個kubernetes的集群.
一個kubernetes的pod指定一組需要運行docker或者rkt容器.
之前我們描述的是一個集群里面不同計算機上跑著不同進程, 現在我們看到的是kubernetes集群里面的不同pod里跑著不同進程.

一個kubernetes集群圍繞著pod也就是容器組構建了一個模型. 這些pod基于資源和”親和度”的約束被動態分配到底層節點上.
之前,我們考慮的是什么進程需要在一臺機器上一起運行. 現在, 我們考慮將哪些進程組構造成什么pod; pod已經成為一種優美的方式來對一個應用的一個功能單元構造模型.我們甚至可以直接使用社區構造的pod,直接將他們跑起來, 例如日志和監控.
一個pod里面的所有進程跑在同一臺機器上, 這樣解決了類似掛載磁盤這樣的資源共享的問題. 背后是kubernetes將pod分配到不同的計算節點也就是kubernetes node上.我們可以給pod或者node設置發生的條件例如資源約束, 親和性等.
計算機就是資源的集合: 計算能力, 內存, 磁盤和網絡接口. 與之類似, 一個pod可以從底層的資源池中分配一定量的資源. 它也會有自己的網卡和pod所在的虛擬網絡的ip.
所以, pod就是新的計算機.
如果我們需要某個特定功能進行擴展, 我們只需要在集群中多跑幾個這個pod的拷貝. 當硬件不足, 我們就往集群里面增加更多的計算和存儲. 通過將資源與它所承載的功能解耦, 調度器可以保證所有的可用資源會被盡可能高效利用.
Kubernetes復制控制器用來保證任意時間某個pod的一定數量的拷貝在運行. 就像一個分布式的init, 如果一個pod掛了: 起因可能是里面的一個進程失敗了, 或者pod 的依賴掛了, 或者它所在的節點down了; kubernetes會探測到并在另一個可用的節點上啟動一個新的拷貝.
一個kubernetes的service會跟蹤集群里某種特定type的pod的所有實例. 例如, 我們有一個ap server service, 它會跟蹤cluster里面所有的app server的pod. service是一個非常簡便的抽象; 我們的應用可以非常快的找到某種類型服務的所有功能單元然后將工作分發給他們.

一個完整的Kubernetes集群圖
Pod被動態分配到節點上. 每一種pod對應的服務都有服務發現和負載均衡. 同時也描繪了pod和服務的虛擬網絡.
Kubernetes既是一個在集群里面管理和調度進程的框架, 也是一種構建應用的新的思維模型, 基于的是pod里面的進程分組和service所提供的服務發現.
整個生態以及未來發展
管理一臺計算機已經是一個難題了. 管理一大群互相通訊的機器更是復雜得多. 感謝發明了像docker, kubernetes這樣非凡工具的好心人, 我們現在有了容器這樣的簡單模型, 也有工具將集群管理起來就像一臺計算機. 構建可擴展的應用也從沒像現在這樣如此簡單.
容器和集群管理軟件業也影響了人們構建應用的方式. 他們創造了新的模式和抽象, 很多的可能性仍在探索中. 例如, 使用容器來構建可重用的應用組件或者庫可能也會很有意思. 在Hasura, 我們正給數據庫, 搜索, 用戶管理, 文件管理等等創建組件, 構建應用就只需將它們快速組裝起來.
總的來說, 在追求創造更簡模型的道路上我們已經前進了一大步. 當今的所有軟件本質就是運行代碼,執行功能. 從這個角度, 我們做的所有的事情僅僅是管理這些功能: 將它們分組, 運行它們的多份拷貝, 找到并與它們交互, 然后處理失敗的情況. 由此推出一個邏輯的結論, 或許某一天我們會有這樣一個系統, 我們只需要描述我們需要的功能, 余下的交給系統按照它完成即可. 那確實是求之不得啊!
Akshaya Acharya
Akshaya領導著Hasuar的平臺工程團隊. 他曾經在Intellectual Ventures的一個咨詢團隊與敏捷開發團隊一起工作過, 也曾經作為Tech mentor在MEST, Ghana工作過.
來自: http://dockone.io/article/953