當今,Kubernetes 已經成為容器編排領域的領導者。但是在 Coinbase 公司,卻沒有使用 Kubernetes。這是為什么?運行 Kubernetes 會產生哪些問題?
本文要點:容器編排平臺是一項復雜而令人驚嘆的技術,它可以幫助一些企業和團隊解決一系列的問題。然而,我們經常忽略的是,容器技術還帶來了一系列的挑戰,企業只有克服這些挑戰才能避免失敗。
https://github.com/hjacobs/kubernetes-failure-stories
1. 歷史
在討論現狀之前,讓我們先了解下時至今日這項技術的發展歷程。
1980 年代:chroot
1990 年代:jail
2000 年代(早期):jail > FreeBSD
2000 年代(中期):cgroups
2000 年代(后期):LXC(Linux 容器)
2010 年代(早期):Docker
2010 年代(后期):Kubernetes
如果想進一步了解其歷史,請查閱 Enterprise Docker 第七章。
https://www.oreilly.com/library/view/enterprise-docker/9781491994986/
讓我們從 10 年前說起,那時還沒有現如今大家都知道的容器。那個時候,我們沒有 / 不使用 docker、rkt 或任何其他主流的容器封裝器 / 服務。為了將應用程序從源代碼打包為生產部署,大多數大型公司都在構建內部系統。工程師在他們機器上運行的東西通常不是在生產環境中運行的東西,或者即使是在生產環境中運行的東西,也通常是以一種深度定制化而且非常復雜的方式一次性構建 / 打包的。
使用內部系統打包和部署應用程序需要一個大型運營團隊。通常,該團隊隸屬于負責管理打包 / 構建流程、部署和部署后工作的平臺或基礎設施組織。這些角色的職責通常以操作型工作為主,包括主機故障排除、診斷 OS 補丁 / 升級中的特定依賴問題等。部署后的工作包括容量規劃、訂購更多服務器、上架 / 安裝以及升級上面的軟件,幾乎不涉及自動編排。
幸運的話,有一些常規過程可以用來構建一個“黃金鏡像”(想想 Hashicorp 的 Packer。這個鏡像有詳細的文檔,甚至可能被編碼,并由 Hudson(在 Jenkins 之前)這樣的持續集成系統運行。這些鏡像可以手動或借助某些配置管理工具自動分發到你的系統中,然后以某種順序啟動(比如用 Parallel SSH 或類似的方式)。
https://www.packer.io/
https://en.wikipedia.org/wiki/Hudson_(software)
在過去的十年里,一切都變了。我們不再使用龐大的單體應用程序,而是將服務分解為許多離散的、低耦合的部件。我們從必須構建 / 擁有自己的計算,轉為擁有托管服務或公有云服務,而這個過程只需點擊幾下鼠標和一張信用卡。我們從垂直擴展應用程序,轉為重構它們實現水平擴展。所有這一切都是同時發生的,社會也發生了變化:每個人的口袋里都有手機,網絡速度在提高,全球范圍內的網絡延時在下降,從預約遛狗者到商品化的視頻會議,一切都在網上進行。
2009 年,AWS 提供的服務還相當有限。AWS 的 EC2 服務 2008 年才完成 beta 測試并開始提供 SLA。相比之下,GCP 直到 2013 年才正式推出計算服務。
2. 為什么企業會選擇容器化他們的應用程序?
企業選擇容器化他們的應用程序,是為了以快速、安全、可靠的方式提高工程輸出 / 開發人員的生產力。容器化是不同于構建鏡像的另一個選項,盡管有時可以將容器構建到鏡像中,但這超出了本文的范圍參見這里。
https://thenewstack.io/bakery-foundation-container-images-microservices/
容器使工程師在本地開發、測試和運行應用程序的方式與在其他環境(過渡環境和生產環境)中運行的方式相同或類似。容器允許描述依賴綁定關系,可以是顯式的,也可以是隱式的(操作系統總是包含服務所依賴的包 $foo)。容器允許更小的服務封裝和資源定義(使用 X CPU 和 Y GB 內存)。容器讓你可以考慮水平伸縮應用程序,而不是垂直伸縮應用程序,從而實現更健壯的架構。
其中一些觀點可能還需要進一步地討論。為了推動對話,這些觀點都有點過于大膽和發散,因為這并不是在討論容器化或服務化(service-ification)的利弊(例如,將單個應用程序拆分為許多更小的獨立運行的服務)。
3. 虛擬化呢?
虛擬化的概念是指能夠在一個 OS 虛擬化系統] 上運行多個容器。容器只能看到授權給它的設備 / 資源。在 AWS 這樣的托管計算平臺上,你實際上是運行在一個管理程序之下,它管理運行你的操作系統和容器的 VM。
簡圖
虛擬化使當今的容器世界成為可能。如果沒有虛擬化能力,那么現在是不可能使硬件資源在容器中運行多個應用程序的。
4. 容器編排平臺(Mesos、Kubernetes、Docker Swarm)有什么問題需要解決?
容器編排平臺解決以下幾類問題:
托管 / 標準化部署工具(部署);
根據一些定義好的啟發式規則擴展應用程序(橫向擴展);
當出現故障時重新調度 / 移動容器(自愈)。
有些平臺可能聲稱他們有其他特性,如存儲編排、秘密 / 配置管理和自動裝箱。但實際上,如果要把它們應用于大規模安裝,就需要大量的投資,要么是在分支 / 定制方面,要么是在集成與分離方面。
例如,大多數運行大型容器編排平臺的人都無法使用其內置的秘密或配置管理。這些原語通常不是為幾十個團隊中的幾百名工程師設計或構建的,而且通常不包括能夠讓他們穩健地管理、擁有和操作應用程序所需的控件。對于提供更強保證和控制(更不用說擴展)的系統,人們通常會把秘密管理和配置管理分開。
類似地,對于服務發現和負載均衡,將其分離出來并運行 Overlay 或抽象控制平面是很常見的。人們經常會部署 Istio 為 Kubernetes 處理這個問題。管理和運行 Istio 并不是一項簡單的任務,許多現代化集群宕機都是由對這個控制平面 / 服務網格的錯誤配置以及對其細節缺乏理解造成的。
5. 我們用什么作為容器編排平臺?
我們的容器編排平臺是 Odin + AWS ASG(自動伸縮組)。當你在 Codeflow(我們用于部署的內部 UI)上點擊 Deploy 時,Odin 將通過 Codeflow 的 API 調用被激活。Odin 啟動一個 Step Function 并開始部署應用程序。AWS 上會新啟動一個 VM 并將其加載到新的 ASG 中,軟件都是從各種內部位置獲取的,負載均衡器開始對這些新實例進行健康檢查,最終,流量以藍 / 綠方式切換到負載均衡器后面新 ASG 中的新主機上。
https://github.com/coinbase/odin
https://blog.coinbase.com/scaling-developer-productivity-d23ce491f869
我們的容器編排平臺非常簡單。我們啟用了與 Kubernetes 相同的關鍵特性:Codeflow 中有一個 Deploy + Rollback 按鈕,基于一些定義好的啟發式規則(我們支持自定義 AWS 指標或標準 CPU 指標)進行伸縮,并能在 ASG 中的 VM 宕掉或變得不健康時重新調度 / 移動容器。
為了處理秘密和配置管理,我們構建了一個動態配置服務,它為所有內部客戶提供庫,第 95 百分位延遲為 6ms。它后臺基于 DynamoDB,每分鐘可以為成百上千個同步和異步方法請求提供服務。
為了處理服務發現和負載平衡,我們使用了 Route53(DNS)、ALB(應用程序負載均衡器)和 gRPC 客戶端負載均衡(可以是原生的,也可以通過 Envoy)。我們預計今年晚些時候還會開展進一步的工作。
6. 我們為什么不使用 Kubernetes?
運行 Kubernetes 不能解決任何客戶(工程)問題。相反,運行 Kubernetes 實際上會產生一系列新的問題。
我們就需要組建一支全職的計算團隊。盡管隨著發展,我們可能會這樣做,但運行 Kubernetes 的話,我們立即就需要這樣做,這樣才能集中精力構建數十個集群(可能每個團隊 / 組織都是分開的),開始研究 / 構建封裝 / 膠水工具,開始構建抽象控制平面 / 服務網格,等等。
保護 Kubernetes 安全不是一項輕松簡單或易于理解的操作。為了使我們能夠擁有 / 運營 Kubernetes,我們整個平臺中使用的工具和控件(Odin、ASG、Step Deployer——以及它們依賴的東西)都要有。構建相同的原語,提供與目前相同的安全級別,對于(未來的)計算團隊和我們的安全團隊來說都是一項非常大的投資。
托管的 Kubernetes(AWS 的 EKS、谷歌的 GKE)還處于起步階段,擁有 / 運營 Kubernetes 的大多數挑戰都尚未解決(如果有什么問題的話,就更困難了)。在 AWS,為了運行 EKS,他們正在擴充支持 / 運營團隊,而在谷歌,GKE 中斷數小時的情況并不少見(參見這里。你只是把一些運營問題和挑戰轉移給了另一個運營團隊(而可見性大幅降低了)。
集群升級和管理要比我們現在所做的操作多很多。合理運行 Kubernetes 的唯一方法是讓團隊 / 組織擁有自己的集群(類似于讓他們有自己的 AWS 帳戶或 GCP 項目)。即使有了 Istio 和相關工具,升級集群和修補漏洞也不是一件容易的事。通常,你必須構建 / 運行一個輔助集群,將所有的應用程序故障轉移,然后在升級后進行故障恢復。目前,這個原語還沒有構建到任何抽象中。雖然托管集群(GKE)中可能提供了,但它并不總是像你預期的那樣工作,并且啟動后回滾通常也沒有得到很好的處理。
如今,我們沒有這個負擔。我們運行在一個堅固的操作系統上,幾乎沒什么依賴。我們的 AMI 上線是從開發開始,然后經過數周的測試再繼續。如果需要回滾,只需要簡單地更改一行代碼就可以實現。平均而言,我們每個月花在與這個領域密切相關的任何事情上的時間都不到 5 小時。
7. Kubernetes 安全保護
讓我們看一下,在一個保存了超過 80 億美元加密資產的企業中,運行 Kubernetes 并保證其安全的復雜性。
https://blog.coinbase.com/our-focus-on-the-institutional-space-5c8e87332268
組件
保證 Kubernetes 集群安全的基礎知識眾所周知,但是如果需要對其中的每一項都做深入的研究,復雜性就來了。保護所有系統組件(etcd、kubelet)、API 服務器和任何抽象 /Overlay(Istio)的安全,就有許多東西需要理解、測試和保護。考慮到攻擊面增加,必須要深入研究命名空間、seccomp、SELinux、cgroups 等。Kubernetes 非常大,它有自己的 CIS 基準測試和 InSpec 套件(謝天謝地)。
https://www.cisecurity.org/benchmark/kubernetes/
https://github.com/dev-sec/cis-kubernetes-benchmark
漏洞
下面是一個簡短的列表,可以作為漏洞研究的起點:
CVE-2019–5736(8.6 高):使攻擊者可以改寫主機的二進制文件 runc(從而獲得主機 root 訪問權限)。
https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-5736
CVE-2019–11246(6.5 中):如果容器中的二進制文件 tar 存在惡意代碼,它就可以運行任何代碼并輸出意料之外的不良后果。
https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-11246
CVE-2019–11253(7.5 高) :使授權用戶可以發送惡意 YAML 或 JSON 載荷,導致 API 服務器占用過多的 CPU 或內存,可能會導致崩潰或服務不可用。
https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-11253
概述
Kubernetes 是一個功能強大的 PaaS 工具包,具有許多安全相關的選項,可以支持各種部署場景。當它成為大家普遍認可的 PaaS 選項時,從安全的角度來看,這是非常有價值的,因為這些安全選項中的大多數都可以抽象出來,并且必須配備輔助系統以支持其使用。
從根本上說,Kubernetes 是為工作負載編排而設計的——信任并不是 Kubernetes 中封裝或部件產生的原因;多租戶的目的是為了打包,而不是為了支持拓展權限邊界。它提供了幾個層,你可以選擇在其中為不同的可執行性設置適當的邊界。其中一些邊界是內置的,而其他邊界只是用于集成其他工具來輔助管理的集成點。下面是用于隔離工作負載的一些原語,有的 Kube 提供了,有的沒有提供。
控制平面(AWS 賬號 / GCP 項目)
Kubernetes 集群的操作是在提供給它們的服務和網絡中進行的,自然地,就會與 AWS/GCP 控制平面進行一些交互,比如配置 Ingress 負載均衡器、訪問存儲在 KMS 中的秘密等。隨著時間的推移,團隊會發展壯大,擁有獨立的帳戶、項目并進一步的隔離。一個單獨的 AWS 帳戶或 GCP 項目是實現完全 IAM 分割(segmentation)的主要原語。
另一方面,Kubernetes 集群需要在一個 AWS 帳戶內操作(即使與其他地方的集群聯合)。這限制了分割選項和靈活性。我們可以為每個團隊或服務提供一個集群,但我們就無法利用 Kubernetes 的許多好處了,并且會帶來新的管理問題,比如對所有這些集群進行元編排。
集群 & 節點 &Pod& 容器
集群
集群主(API)服務器是一個次控制平面(AWS 控制平面之外),我們也需要做好安全防護。服務帳戶和訪問范圍(容器可以假定要訪問集群內外的資源)與 AWS 的 IAM 一樣復雜,并且需要嚴格地相互映射,以便中時斷不會影響 AWS 控制平面。
節點
底層節點的操作系統必須像我們現在所做的那樣進行維護。事實上,我們的操作系統與谷歌用于 GKE 的基本操作系統非常相似。雖然將我們的操作系統轉到 Kubernetes 不必做任何修改,但我們也不會得到任何東西。
Pod
在集群中創建 Pod,以及定義它們創建時必須滿足哪些標準的規則,都是通過 PodSecurityPolicy 完成的,它的運作方式類似于 Salus 和我們現在使用的一致性管理工具。要實現干凈利落的集成,我們將需要做大量的集成工作,并投資于附加的開源依賴項。
https://github.com/coinbase/salus
Pod 通過網絡策略相互隔離,就像我們現在使用安全組和 / 或內部服務框架所做的那樣。但在 Kubernetes 領域,Pod 相互通信所需的標識、身份驗證和授權涉及大量的支持技術,如用于節點級以下標識格式和認證的 SPIFFE&SPIRE,用于授權控制的 Envoy,Istio authN 和 Z 編排,OPA 授權策略。對于其中的每一項技術,要將其標準化并應用到生產中都需要付出很大的努力。
容器
容器不是安全邊界,而是資源邊界。為了定義容器的安全邊界,需要深入研究自定義內核命名空間、系統調用過濾、強制訪問控制框架和 / 或為容器設計的基于 VM 的隔離技術,如 gVisor。
https://github.com/google/gvisor
目前,我們在這個領域的投入還不太多,因為我們還沒有采用多租戶的方式。如果我們轉向多租戶模型,我們將不得不立即進行大量的投資,通過主機 /VM 隔離技術保證類似分類的 Pod/ 容器在相同的節點上運行,不會相互干擾。
8. 我們何時會運行 Kubernetes?它在我們未來計劃中嗎?
當更高級的容器編配平臺有重要的用例時,我們可能會首先查看問題聲明。如果該平臺很容易添加到我們現有的平臺上:我們可能會首先訪問它,然后從那里開始探索 / 了解。如果我們認為擴展 / 添加到我們的平臺上不合理,那么我們將訪問所有可能的選項——而不僅僅是 Kubernetes。更可能的情況是,我們會先了解 AWS 的托管服務,如 Fargate 和 ECS,然后再了解 Kubernetes。
如果提供 Kubernetes(或任何其他容器編配平臺)我們的工程師可以從中獲得顯著的收益時,我們才會研究提供它們。現在,提供 Kubernetes 并沒有什么明顯的好處。如果 Kubernetes 提供了許多我們現在沒有的新特性,償清了技術債務,或者我們的客戶需要它們可以提供的新功能,而我們在可預見的將來都無法提供,這種情況就可能會改變。如果妨礙其進入我們當前平臺的因素發生了顯著的變化,并且它有了明顯的獨特之處,那么我們也會研究提供一個不同的平臺。
如果 / 當我們現有的平臺達到了極限,由于缺少客戶需要的特性而負擔太重或者可以預見將會負擔太重,而擴展我們平臺的工作又過于繁重,或者中斷太多違反我們的 SLA,那么我們可能會重新審視不同的容器編排平臺。
如果 / 當我們失去了主要上游依賴方(如 AWS 或 ASG)的支持,我們也會考慮其他選項。
這是我們可能會選擇研究另一個容器編排平臺的幾個理由。目前,我們還沒有構建、擁有、運營 Kubernetes 的計劃。
Kubernetes 不是解決了像再平衡 / 自愈、自動擴展和服務發現等多方面的問題嗎?我們現在是如何解決這些問題的?
在規模較小時,Kubernetes 解決了這些問題中的大部分,而且也不是很麻煩。在規模較大時,就需要更多的思考和膠水代碼,并在幾乎所有的東西上增加封裝器 / 安全保護,以使其能夠安全可靠地工作。通常,如前所述,人們傾向于添加 Istio 這樣的服務網格來支持更高級的特性 / 需求。
目前,我們是這樣解決的:
借助 Odin 和 ASG 實現再平衡 / 自愈;
借助 DNS 和 Envoy 實現服務發現。
Kubernetes 有存儲編排,我們目前還沒有,我們應該有嗎?
現在,Coinbase 有兩個主要的有狀態應用程序——區塊鏈節點和交易引擎,它們可能是存儲編排等特性的潛在用例。對于前者(區塊鏈節點),存儲的使用定制化程度很高,我們構建了一個自定義的部署器,為它們提供所需的特性。對于后者(交易引擎),我們依賴可靠性(SRE)團隊為他們的一些特定挑戰提供支持。
雖然 Kubernetes 內置的存儲編排對于區塊鏈節點和交易引擎來說都是一個很好的起點,但是我們在底層技術中遇到的很多問題仍然存在。
9. 如果 Kubernetes 不是容器編排平臺的未來,那么什么才是?
對于部分應用程序,我們將探索并遷移到更高級的抽象服務。我們將探討把 Fargate 和 ECS 作為這方面的候選者。目前的首要原因是利用率和成本的增加——這兩者都不是很以客戶為中心。我們可以選擇再等等,到我們有更多以客戶為中心的理由時才實施。
以客戶為中心的潛在問題可能是部署時間、部署模式(除了金絲雀之外)、比目前更復雜的服務網格需求,或者在現有的工具中構建不可能 / 不合理但可以添加到 Fargate 或 ECS 的特定改進 / 特性。這些是一些潛在的以客戶為中心的問題,這些問題可能會有,但目前還不知道或沒有發現。
在理想情況下,向另一種底層容器技術的轉移是不可見的,因為與它們交互的工具不會從根本上改變。遷移到不同的平臺可能會揭示出關于現有系統隱藏的或未知的期望。如何在過渡環境和生產環境部署和調試服務仍然是抽象的,但是可能提供了一些現在沒有的特性。
10. 我 / 我們討厭 Kubernetes 嗎?它是一個失敗的容器平臺嗎?
不。盡管存在挑戰,但它是一個了不起的工具。Kubernetes 已經將我們的行業推向了一個越來越積極的方向。隨著 Kubernetes 進入 v1 版本,Knative、Fargate 和 Cloud Run 的開發正在不斷提高抽象級別,并解決管理 Kubernetes 的潛在挑戰。未來是光明的。隨著這些潛在的挑戰得到解決,許多現存的問題未來可能會得到緩解。