1. 程式人生 > 其它 >K8S+Docker容器化雲高可用叢集實戰1.1-雲端計算平臺解讀去

K8S+Docker容器化雲高可用叢集實戰1.1-雲端計算平臺解讀去

K8S+Docker容器化雲高可用叢集實戰1.2-雲原生解讀

1. 雲原生的生態系統

  • 微服務
  • 容器化
  • 持續部署
  • 自動化DEVOPS

2. 雲原生常見技術---基礎研究量

  • Docker、Docker Compose:容器化技術
  • Kubernetes:大規模容器編排
  • Helm:雲原生應用商店
  • Rancher:一用的容器管理平臺
  • KubeSphere:一站式容器雲平臺
  • OpenTracing:雲原生鏈路追蹤標準
  • Jaeger:雲原生鏈路追蹤實現產品
  • Istio:ServiceMesh下的服務流量治理
  • Jenkins、JenkinsX、Jenkins-BlueOcean:老牌的CI/CD平臺
  • Gitlab/hub-CICD:Gitlab/hub自帶的CICD
  • Argo:kubernetes宣告式持續整合
  • Nexus:Maven私庫
  • Habor:Docker私庫
  • Prometheus+Gragana:監控與視覺化方案
  • ElasicSearch+Fluentd+Kibanna:日誌與視覺化方案
  • Serverless:無伺服器上雲方案
  • SpringCloud Kubernetes:微服務上雲方案

3. 雲原生的術語

3.1 應用12要素  12-Factor 

3.1.1  Codebase 基準程式碼    一份基準程式碼(Codebase),多份部署(deploy)

12-Factor應用(譯者注:應該是說一個使用本文概念來設計的應用,下同)通常會使用版本控制系統加以管理,如Git, Mercurial, Subversion。一份用來跟蹤程式碼所有修訂版本的資料庫被稱作 程式碼庫(code repository, code repo, repo)。

基準程式碼和應用之間總是保持一一對應的關係:

  • 一旦有多個基準程式碼,就不能稱為一個應用,而是一個分散式系統。分散式系統中的每一個元件都是一個應用,每一個應用可以分別使用 12-Factor 進行開發。
  • 多個應用共享一份基準程式碼是有悖於 12-Factor 原則的。解決方案是將共享的程式碼拆分為獨立的類庫,然後使用 依賴管理 策略去載入它們。

儘管每個應用只對應一份基準程式碼,但可以同時存在多份部署。每份 部署 相當於運行了一個應用的例項。通常會有一個生產環境,一個或多個預釋出環境。此外,每個開發人員都會在自己本地環境執行一個應用例項,這些都相當於一份部署。

所有部署的基準程式碼相同,但每份部署可以使用其不同的版本。比如,開發人員可能有一些提交還沒有同步至預釋出環境;預釋出環境也有一些提交沒有同步至生產環境。但它們都共享一份基準程式碼,我們就認為它們只是相同應用的不同部署而已。

3.1.2 Dependency 依賴    顯示宣告依賴關係

大多數程式語言都會提供一個打包系統,用來為各個類庫提供打包服務,就像 Perl 的 CPAN 或是 Ruby 的 Rubygems 。通過打包系統安裝的類庫可以是系統級的(稱之為 “site packages”),或僅供某個應用程式使用,部署在相應的目錄中(稱之為 “vendoring” 或 “bunding”)。

12-Factor規則下的應用程式不會隱式依賴系統級的類庫。 它一定通過 依賴清單 ,確切地宣告所有依賴項。此外,在執行過程中通過 依賴隔離 工具來確保程式不會呼叫系統中存在但清單中未宣告的依賴項。這一做法會統一應用到生產和開發環境。

例如, Ruby 的 Bundler 使用 Gemfile 作為依賴項宣告清單,使用 bundle exec 來進行依賴隔離。Python 中則可分別使用兩種工具 – Pip 用作依賴宣告, Virtualenv 用作依賴隔離。甚至 C 語言也有類似工具, Autoconf 用作依賴宣告,靜態連結庫用作依賴隔離。無論用什麼工具,依賴宣告和依賴隔離必須一起使用,否則無法滿足 12-Factor 規範。

顯式宣告依賴的優點之一是為新進開發者簡化了環境配置流程。新進開發者可以檢出應用程式的基準程式碼,安裝程式語言環境和它對應的依賴管理工具,只需通過一個 構建命令 來安裝所有的依賴項,即可開始工作。例如,Ruby/Bundler 下使用 bundle install,而 Clojure/Leiningen 則是 lein deps。

12-Factor 應用同樣不會隱式依賴某些系統工具,如 ImageMagick 或是curl。即使這些工具存在於幾乎所有系統,但終究無法保證所有未來的系統都能支援應用順利執行,或是能夠和應用相容。如果應用必須使用到某些系統工具,那麼這些工具應該被包含在應用之中。

3.1.3 Config 配置   在環境中儲存配置

通常,應用的 配置 在不同 部署 (預釋出、生產環境、開發環境等等)間會有很大差異。這其中包括:

  • 資料庫,Memcached,以及其他 後端服務 的配置
  • 第三方服務的證書,如 Amazon S3、Twitter 等
  • 每份部署特有的配置,如域名等

有些應用在程式碼中使用常量儲存配置,這與 12-Factor 所要求的程式碼和配置嚴格分離顯然大相徑庭。配置檔案在各部署間存在大幅差異,程式碼卻完全一致。

判斷一個應用是否正確地將配置排除在程式碼之外,一個簡單的方法是看該應用的基準程式碼是否可以立刻開源,而不用擔心會暴露任何敏感的資訊。

需要指出的是,這裡定義的“配置”並不包括應用的內部配置,比如 Rails 的 config/routes.rb,或是使用 Spring 時 程式碼模組間的依賴注入關係 。這類配置在不同部署間不存在差異,所以應該寫入程式碼。

另外一個解決方法是使用配置檔案,但不把它們納入版本控制系統,就像 Rails 的 config/database.yml 。這相對於在程式碼中使用常量已經是長足進步,但仍然有缺點:總是會不小心將配置檔案簽入了程式碼庫;配置檔案的可能會分散在不同的目錄,並有著不同的格式,這讓找出一個地方來統一管理所有配置變的不太現實。更糟的是,這些格式通常是語言或框架特定的。

12-Factor推薦將應用的配置儲存於 環境變數 中( env vars, env )。環境變數可以非常方便地在不同的部署間做修改,卻不動一行程式碼;與配置檔案不同,不小心把它們簽入程式碼庫的概率微乎其微;與一些傳統的解決配置問題的機制(比如 Java 的屬性配置檔案)相比,環境變數與語言和系統無關。

配置管理的另一個方面是分組。有時應用會將配置按照特定部署進行分組(或叫做“環境”),例如Rails中的 development,test, 和 production 環境。這種方法無法輕易擴充套件:更多部署意味著更多新的環境,例如 staging 或 qa。 隨著專案的不斷深入,開發人員可能還會新增他們自己的環境,比如 joes-staging ,這將導致各種配置組合的激增,從而給管理部署增加了很多不確定因素。

12-Factor 應用中,環境變數的粒度要足夠小,且相對獨立。它們永遠也不會組合成一個所謂的“環境”,而是獨立存在於每個部署之中。當應用程式不斷擴充套件,需要更多種類的部署時,這種配置管理方式能夠做到平滑過渡。

3.1.4 Backing services)後端服務    把後端服務當做附件資源

後端服務是指程式執行所需要的通過網路呼叫的各種服務,如資料庫(MySQL,CouchDB),訊息/佇列系統(RabbitMQ,Beanstalkd),SMTP 郵件傳送服務(Postfix),以及快取系統(Memcached)。

類似資料庫的後端服務,通常由部署應用程式的系統管理員一起管理。除了本地服務之外,應用程式有可能使用了第三方釋出和管理的服務。示例包括 SMTP(例如 Postmark),資料收集服務(例如 New Relic 或 Loggly),資料儲存服務(如 Amazon S3),以及使用 API 訪問的服務(例如 Twitter, Google Maps, Last.fm)。

12-Factor 應用不會區別對待本地或第三方服務。 對應用程式而言,兩種都是附加資源,通過一個 url 或是其他儲存在 配置中的服務定位/服務證書來獲取資料。12-Factor 應用的任意 部署 ,都應該可以在不進行任何程式碼改動的情況下,將本地 MySQL 資料庫換成第三方服務(例如 Amazon RDS)。類似的,本地 SMTP 服務應該也可以和第三方 SMTP 服務(例如 Postmark )互換。上述 2 個例子中,僅需修改配置中的資源地址。

每個不同的後端服務是一份 資源 。例如,一個 MySQL 資料庫是一個資源,兩個 MySQL 資料庫(用來資料分割槽)就被當作是 2 個不同的資源。12-Factor 應用將這些資料庫都視作 附加資源 ,這些資源和它們附屬的部署保持鬆耦合。

3.1.5 構建,釋出,執行     嚴格分離構建和執行

基準程式碼 轉化為一份部署(非開發環境)需要以下三個階段:

  • 構建階段 是指將程式碼倉庫轉化為可執行包的過程。構建時會使用指定版本的程式碼,獲取和打包 依賴項,編譯成二進位制檔案和資原始檔。
  • 釋出階段 會將構建的結果和當前部署所需 配置 相結合,並能夠立刻在執行環境中投入使用。
  • 執行階段 (或者說“執行時”)是指標對選定的釋出版本,在執行環境中啟動一系列應用程式 程序。

12-facfor 應用嚴格區分構建,釋出,執行這三個步驟。 舉例來說,直接修改處於執行狀態的程式碼是非常不可取的做法,因為這些修改很難再同步回構建步驟。

部署工具通常都提供了釋出管理工具,最引人注目的功能是退回至較舊的釋出版本。比如, Capistrano 將所有釋出版本都儲存在一個叫 releases 的子目錄中,當前的線上版本只需對映至對應的目錄即可。該工具的 rollback 命令可以很容易地實現回退版本的功能。

每一個釋出版本必須對應一個唯一的釋出 ID,例如可以使用釋出時的時間戳(2011-04-06-20:32:17),亦或是一個增長的數字(v100)。釋出的版本就像一本只能追加的賬本,一旦釋出就不可修改,任何的變動都應該產生一個新的釋出版本。

新的程式碼在部署之前,需要開發人員觸發構建操作。但是,執行階段不一定需要人為觸發,而是可以自動進行。如伺服器重啟,或是程序管理器重啟了一個崩潰的程序。因此,執行階段應該保持儘可能少的模組,這樣假設半夜發生系統故障而開發人員又捉襟見肘也不會引起太大問題。構建階段是可以相對複雜一些的,因為錯誤資訊能夠立刻展示在開發人員面前,從而得到妥善處理。

3.1.6 程序    以一個或多個無狀態程序執行應用

執行環境中,應用程式通常是以一個和多個 程序 執行的。

最簡單的場景中,程式碼是一個獨立的指令碼,執行環境是開發人員自己的膝上型電腦,程序由一條命令列(例如python my_script.py)。另外一個極端情況是,複雜的應用可能會使用很多 程序型別 ,也就是零個或多個程序例項。

12-Factor 應用的程序必須無狀態且 無共享 。 任何需要持久化的資料都要儲存在 後端服務 內,比如資料庫。

記憶體區域或磁碟空間可以作為程序在做某種事務型操作時的快取,例如下載一個很大的檔案,對其操作並將結果寫入資料庫的過程。12-Factor應用根本不用考慮這些快取的內容是不是可以保留給之後的請求來使用,這是因為應用啟動了多種型別的程序,將來的請求多半會由其他程序來服務。即使在只有一個程序的情形下,先前儲存的資料(記憶體或檔案系統中)也會因為重啟(如程式碼部署、配置更改、或執行環境將程序排程至另一個物理區域執行)而丟失。

原始檔打包工具(Jammit, django-compressor) 使用檔案系統來快取編譯過的原始檔。12-Factor 應用更傾向於在 構建步驟做此動作——正如 Rails資源管道 ,而不是在執行階段。

一些網際網路系統依賴於 “粘性 session”, 這是指將使用者 session 中的資料快取至某程序的記憶體中,並將同一使用者的後續請求路由到同一個程序。粘性 session 是 12-Factor 極力反對的。Session 中的資料應該儲存在諸如 Memcached 或 Redis 這樣的帶有過期時間的快取中。

3.1.7 埠繫結   通過埠繫結(Port binding)來提供服務

網際網路應用有時會運行於伺服器的容器之中。例如 PHP 經常作為 Apache HTTPD 的一個模組來執行,正如 Java 運行於 Tomcat 。

12-Factor 應用完全自我載入 而不依賴於任何網路伺服器就可以建立一個面向網路的服務。網際網路應用 通過埠繫結來提供服務 ,並監聽傳送至該埠的請求。

本地環境中,開發人員通過類似http://localhost:5000/的地址來訪問服務。在線上環境中,請求統一發送至公共域名而後路由至綁定了埠的網路程序。

通常的實現思路是,將網路伺服器類庫通過 依賴宣告 載入應用。例如,Python 的 Tornado, Ruby 的Thin , Java 以及其他基於 JVM 語言的 Jetty。完全由 使用者端 ,確切的說應該是應用的程式碼,發起請求。和執行環境約定好繫結的埠即可處理這些請求。

HTTP 並不是唯一一個可以由埠繫結提供的服務。其實幾乎所有伺服器軟體都可以通過程序繫結埠來等待請求。例如,使用 XMPP 的 ejabberd , 以及使用 Redis 協議 的 Redis 。

還要指出的是,埠繫結這種方式也意味著一個應用可以成為另外一個應用的 後端服務 ,呼叫方將服務方提供的相應 URL 當作資源存入 配置 以備將來呼叫。

3.1.8 併發  通過程序模型進行擴充套件

任何計算機程式,一旦啟動,就會生成一個或多個程序。網際網路應用採用多種程序執行方式。例如,PHP 程序作為 Apache 的子程序存在,隨請求按需啟動。Java 程序則採取了相反的方式,在程式啟動之初 JVM 就提供了一個超級程序儲備了大量的系統資源(CPU 和記憶體),並通過多執行緒實現內部的併發管理。上述 2 個例子中,程序是開發人員可以操作的最小單位。

擴充套件表現為執行中的程序,工作多樣性表現為程序型別。

在 12-factor 應用中,程序是一等公民。12-Factor 應用的程序主要借鑑於 unix 守護程序模型 。開發人員可以運用這個模型去設計應用架構,將不同的工作分配給不同的 程序型別 。例如,HTTP 請求可以交給 web 程序來處理,而常駐的後臺工作則交由 worker 程序負責。

這並不包括個別較為特殊的程序,例如通過虛擬機器的執行緒處理併發的內部運算,或是使用諸如 EventMachine, Twisted, Node.js 的非同步/事件觸發模型。但一臺獨立的虛擬機器的擴充套件有瓶頸(垂直擴充套件),所以應用程式必須可以在多臺物理機器間跨程序工作。

上述程序模型會在系統急需擴充套件時大放異彩。 12-Factor 應用的程序所具備的無共享,水平分割槽的特性 意味著新增併發會變得簡單而穩妥。這些程序的型別以及每個型別中程序的數量就被稱作 程序構成 。

12-Factor 應用的程序 不需要守護程序 或是寫入 PID 檔案。相反的,應該藉助作業系統的程序管理器(例如 Upstart ,分散式的程序管理雲平臺,或是類似 Foreman 的工具),來管理 輸出流 ,響應崩潰的程序,以及處理使用者觸發的重啟和關閉超級程序的請求。

3.1.9 Disposable易處理    快速啟動和優雅終止可最大化健壯性

12-Factor 應用的 程序 是 易處理(disposable)的,意思是說它們可以瞬間開啟或停止。 這有利於快速、彈性的伸縮應用,迅速部署變化的 程式碼 或 配置 ,穩健的部署應用。

程序應當追求 最小啟動時間 。 理想狀態下,程序從敲下命令到真正啟動並等待請求的時間應該只需很短的時間。更少的啟動時間提供了更敏捷的 釋出 以及擴充套件過程,此外還增加了健壯性,因為程序管理器可以在授權情形下容易的將程序搬到新的物理機器上。

程序 一旦接收 終止訊號(SIGTERM) 就會優雅的終止 。就網路程序而言,優雅終止是指停止監聽服務的埠,即拒絕所有新的請求,並繼續執行當前已接收的請求,然後退出。此型別的程序所隱含的要求是HTTP請求大多都很短(不會超過幾秒鐘),而在長時間輪詢中,客戶端在丟失連線後應該馬上嘗試重連。

對於 worker 程序來說,優雅終止是指將當前任務退回佇列。例如,RabbitMQ 中,worker 可以傳送一個NACK訊號。 Beanstalkd 中,任務終止並退回佇列會在worker斷開時自動觸發。有鎖機制的系統諸如 Delayed Job 則需要確定釋放了系統資源。此型別的程序所隱含的要求是,任務都應該 可重複執行 , 這主要由將結果包裝進事務或是使重複操作 冪等 來實現。

程序還應當在面對突然死亡時保持健壯,例如底層硬體故障。雖然這種情況比起優雅終止來說少之又少,但終究有可能發生。一種推薦的方式是使用一個健壯的後端佇列,例如 Beanstalkd ,它可以在客戶端斷開或超時後自動退回任務。無論如何,12-Factor 應用都應該可以設計能夠應對意外的、不優雅的終結。Crash-only design 將這種概念轉化為 合乎邏輯的理論。

3.1.10 開發環境與線上環境等價    儘可能的保持開發,預釋出,線上環境相同

從以往經驗來看,開發環境(即開發人員的本地 部署)和線上環境(外部使用者訪問的真實部署)之間存在著很多差異。這些差異表現在以下三個方面:

  • 時間差異: 開發人員正在編寫的程式碼可能需要幾天,幾周,甚至幾個月才會上線。
  • 人員差異: 開發人員編寫程式碼,運維人員部署程式碼。
  • 工具差異: 開發人員或許使用 Nginx,SQLite,OS X,而線上環境使用 Apache,MySQL 以及 Linux。

12-Factor 應用想要做到 持續部署 就必須縮小本地與線上差異。 再回頭看上面所描述的三個差異:

  • 縮小時間差異:開發人員可以幾小時,甚至幾分鐘就部署程式碼。
  • 縮小人員差異:開發人員不只要編寫程式碼,更應該密切參與部署過程以及程式碼在線上的表現。
  • 縮小工具差異:儘量保證開發環境以及線上環境的一致性。

後端服務 是保持開發與線上等價的重要部分,例如資料庫,佇列系統,以及快取。許多語言都提供了簡化獲取後端服務的類庫,例如不同型別服務的 介面卡 。下列表格提供了一些例子。

型別 語言 類庫 介面卡
資料庫 Ruby/Rails ActiveRecord MySQL, PostgreSQL, SQLite
佇列 Python/Django Celery RabbitMQ, Beanstalkd, Redis
快取 Ruby/Rails ActiveSupport::Cache Memory, filesystem, Memcached

開發人員有時會覺得在本地環境中使用輕量的後端服務具有很強的吸引力,而那些更重量級的健壯的後端服務應該使用在生產環境。例如,本地使用 SQLite 線上使用 PostgreSQL;又如本地快取在程序記憶體中而線上存入 Memcached。

12-Factor 應用的開發人員應該反對在不同環境間使用不同的後端服務 ,即使介面卡已經可以幾乎消除使用上的差異。這是因為,不同的後端服務意味著會突然出現的不相容,從而導致測試、預釋出都正常的程式碼在線上出現問題。這些錯誤會給持續部署帶來阻力。從應用程式的生命週期來看,消除這種阻力需要花費很大的代價。

與此同時,輕量的本地服務也不像以前那樣引人注目。藉助於Homebrew,apt-get等現代的打包系統,諸如Memcached、PostgreSQL、RabbitMQ 等後端服務的安裝與執行也並不複雜。此外,使用類似 Chef 和 Puppet 的宣告式配置工具,結合像 Vagrant 這樣輕量的虛擬環境就可以使得開發人員的本地環境與線上環境無限接近。與同步環境和持續部署所帶來的益處相比,安裝這些系統顯然是值得的。

不同後端服務的介面卡仍然是有用的,因為它們可以使移植後端服務變得簡單。但應用的所有部署,這其中包括開發、預釋出以及線上環境,都應該使用同一個後端服務的相同版本。

3.1.11 日誌  把日誌當作事件流

日誌 使得應用程式執行的動作變得透明。在基於伺服器的環境中,日誌通常被寫在硬碟的一個檔案裡,但這只是一種輸出格式。

日誌應該是 事件流 的彙總,將所有執行中程序和後端服務的輸出流按照時間順序收集起來。儘管在回溯問題時可能需要看很多行,日誌最原始的格式確實是一個事件一行。日誌沒有確定開始和結束,但隨著應用在執行會持續的增加。

12-factor應用本身從不考慮儲存自己的輸出流。 不應該試圖去寫或者管理日誌檔案。相反,每一個執行的程序都會直接的標準輸出(stdout)事件流。開發環境中,開發人員可以通過這些資料流,實時在終端看到應用的活動。

在預釋出或線上部署中,每個程序的輸出流由執行環境截獲,並將其他輸出流整理在一起,然後一併傳送給一個或多個最終的處理程式,用於檢視或是長期存檔。這些存檔路徑對於應用來說不可見也不可配置,而是完全交給程式的執行環境管理。類似 Logplex 和 Fluent 的開源工具可以達到這個目的。

這些事件流可以輸出至檔案,或者在終端實時觀察。最重要的,輸出流可以傳送到 Splunk 這樣的日誌索引及分析系統,或 Hadoop/Hive 這樣的通用資料儲存系統。這些系統為檢視應用的歷史活動提供了強大而靈活的功能,包括:

  • 找出過去一段時間特殊的事件。
  • 圖形化一個大規模的趨勢,比如每分鐘的請求量。
  • 根據使用者定義的條件實時觸發警報,比如每分鐘的報錯超過某個警戒線。

3.1.12 管理程序   後臺管理任務當作一次性程序執行

程序構成(process formation)是指用來處理應用的常規業務(比如處理 web 請求)的一組程序。與此不同,開發人員經常希望執行一些管理或維護應用的一次性任務,例如:

  • 執行資料移植(Django 中的 manage.py migrate, Rails 中的 rake db:migrate)。
  • 執行一個控制檯(也被稱為 REPL shell),來執行一些程式碼或是針對線上資料庫做一些檢查。大多數語言都通過直譯器提供了一個 REPL 工具(python 或 perl) ,或是其他命令(Ruby 使用 irb, Rails 使用 rails console)。
  • 執行一些提交到程式碼倉庫的一次性指令碼。

一次性管理程序應該和正常的 常駐程序 使用同樣的環境。這些管理程序和任何其他的程序一樣使用相同的 程式碼 和 配置 ,基於某個 釋出版本 執行。後臺管理程式碼應該隨其他應用程式程式碼一起釋出,從而避免同步問題。

所有程序型別應該使用同樣的 依賴隔離 技術。例如,如果Ruby的web程序使用了命令 bundle exec thin start ,那麼資料庫移植應使用 bundle exec rake db:migrate 。同樣的,如果一個 Python 程式使用了 Virtualenv,則需要在執行 Tornado Web 伺服器和任何 manage.py 管理程序時引入 bin/python 。

12-factor 尤其青睞那些提供了 REPL shell 的語言,因為那會讓執行一次性指令碼變得簡單。在本地部署中,開發人員直接在命令列使用 shell 命令呼叫一次性管理程序。在線上部署中,開發人員依舊可以使用ssh或是執行環境提供的其他機制來執行這樣的程序。

...............