<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Apache Kafka的4個混沌工程實驗 | IDCF

          共 8074字,需瀏覽 17分鐘

           ·

          2021-07-14 20:17

          來源:混沌工程實踐   作者:李大山
          原文作者:Andree Newman 原文來源:Gremlin.com
          原文標(biāo)題:The first 4 chaos experiments to run on Apache Kafka

          Apache Kafka是一個開放源代碼的分布式消息傳遞平臺,高吞吐量和低延遲交付數(shù)據(jù)。在規(guī)模上,它每天可以處理數(shù)萬億條記錄,同時還提供容錯、復(fù)制和自動災(zāi)難恢復(fù)功能。 

          盡管Kafka有許多不同的使用場景,但最常見的是作為應(yīng)用程序之間的消息broker。Kafka可以從多個上游源頭接收、處理和重新分配消息到多個下游使用者,而無需重新配置應(yīng)用程序。這就可以流式傳輸大量數(shù)據(jù),同時保持應(yīng)用程序的松散耦合,支持諸如分布式計算、日志記錄和監(jiān)控、網(wǎng)站活動跟蹤以及物聯(lián)網(wǎng)(IoT)設(shè)備通信之類的場景。

          由于Kafka提供了應(yīng)用程序之間的關(guān)鍵管道,因此可靠性至關(guān)重要。我們需要計劃來減輕幾種可能的故障模式,包括:

          • 消息broker中斷和其他不正常的群集狀況; 
          • Apache ZooKeeper失效,這是Kafka的關(guān)鍵依賴項;
          • 上游和下游應(yīng)用程序中的故障。
          不必等待這些故障在預(yù)生產(chǎn)或生產(chǎn)中發(fā)生,我們就能通過混沌工程主動對其進行測試,以便制定適當(dāng)?shù)牟呗詼p輕影響。
          本文,我們將演示混沌工程如何幫助提高Kafka部署的可靠性。為此,我們將使用企業(yè)SaaS混沌工程平臺Gremlin創(chuàng)建并運行四個混沌實驗。通過閱讀本文,了解Kafka群集可能發(fā)生故障的不同方式,如何設(shè)計混沌實驗來測試這些故障模式,以及如何使用觀測結(jié)果提高其可靠性。
          本文中,我們將在Confluent平臺上演示混亂的實驗,該平臺是Kafka最初的創(chuàng)建者提供的企業(yè)事件流平臺。Confluent平臺基于Kafka構(gòu)建并添加企業(yè)功能(例如基于Web的GUI,全面的安全控制以及輕松部署多區(qū)域集群的功能)。但是,本文中的實驗將適用于任何Kafka集群。

          一、Apache Kafka架構(gòu)概述



          為了了解Kafka如何從混沌工程中受益,我們應(yīng)該首先研究一下Kafka的體系架構(gòu)設(shè)計。
          Kafka使用發(fā)布者/訂閱者(或pub/sub)消息傳遞模型傳輸數(shù)據(jù)。上游應(yīng)用程序(在Kafka中稱為發(fā)布者或生產(chǎn)者)會生成發(fā)送到Kafka服務(wù)器(稱為broker)的消息。然后,下游應(yīng)用程序(在Kafka中稱為訂閱者或使用者)可以從broker獲取這些消息。消息被組織在主題的類別中,消費者可以訂閱一個或多個主題以使用其消息。通過充當(dāng)生產(chǎn)者和消費者之間的中間人,Kafka使我們能夠彼此獨立地管理上游和下游應(yīng)用程序。
          Kafka將每個主題細分為多個分區(qū)。分區(qū)可以跨多個broker鏡像以提供復(fù)制。還允許多個使用者(更具體地說,是使用者組)同時處理一個主題。為了防止多個生產(chǎn)者寫入單個分區(qū),每個分區(qū)都有一個充當(dāng)領(lǐng)導(dǎo)者的broker,以及沒有或多個充當(dāng)追隨者的broker。新消息將被寫入領(lǐng)導(dǎo)者,而追隨者則將其復(fù)制。追隨者被完全復(fù)制后,稱為同步副本(ISR)。
          該過程由Apache ZooKeeper協(xié)調(diào),ZooKeeper管理有關(guān)Kafka集群的元數(shù)據(jù),例如哪些分區(qū)分配給了哪些broker。ZooKeeper是Kafka的必要依賴項(編者注:2.8版本就可以不需要ZooKeeper了),但在其自己的集群上作為完全獨立的服務(wù)運行。改善Kafka集群的可靠性必然涉及提高其關(guān)聯(lián)的ZooKeeper集群的可靠性。
          Kafka和Confluent平臺還有其他組件,但是在提高可靠性時,這些是最重要的考慮因素。當(dāng)本文介紹其他組件時,我們會對其進行更詳細的說明。

          二、為何要對Kafka進行混沌工程?



          混沌工程是一種主動測試系統(tǒng)故障的方法,目的是使它們更具韌性。我們通過向系統(tǒng)中注入少量受控故障,觀察影響并解決觀察到的問題。這使我們能夠在系統(tǒng)出現(xiàn)問題之前,為用戶找到解決問題的方法,同時還教會了我們更多關(guān)于系統(tǒng)在各種條件下的行為信息。
          由于有數(shù)不清的配置選項,靈活的生產(chǎn)者和消費者的部署方式以及許多其他因素,使得像Kafka這樣的分布式系統(tǒng)難以高效管理和運維。僅僅使我們的broker和ZooKeeper節(jié)點不失效是不夠的,我們需要考慮在應(yīng)用程序、副本和其他基礎(chǔ)架構(gòu)組件中可能發(fā)生的更加細微和難以預(yù)測的問題。這些可能會以意想不到的方式影響整個部署,如果在生產(chǎn)中發(fā)生,則可能需要進行大量的排障開銷。
          使用混沌工程,我們可以主動測試這些類型的故障,并在部署到生產(chǎn)之前解決這些故障,從而降低停機和緊急事件的風(fēng)險。

          三、在Kafka上運行混沌實驗



          在本節(jié)中,我們將逐步介紹在Confluent平臺上部署執(zhí)行四個不同的混沌實驗。混沌實驗是一個有計劃的過程,將故障注入系統(tǒng)以了解其響應(yīng)方式。在系統(tǒng)上運行任何實驗之前,應(yīng)充分考慮并開發(fā)要運行的實驗。
          創(chuàng)建實驗時。
          • 第一步:設(shè)定假說要回答的問題以及預(yù)期結(jié)果是什么。例如,如果實驗是測試承受broker中斷的能力,則假說可能會指出:“如果broker節(jié)點發(fā)生故障,則消息會自動路由到其他broker,而不會丟失數(shù)據(jù)?!?/span>
          • 第二步:定義爆炸半徑,受實驗影響的基礎(chǔ)架構(gòu)組件。減小爆炸半徑限制了實驗可能對基礎(chǔ)架構(gòu)造成的潛在危害,同時還可以將精力集中在特定系統(tǒng)上。我們強烈建議從盡可能小的爆炸半徑開始,然后隨著對進行混沌實驗的適應(yīng)性提高,將其增大。另外,還應(yīng)該定義幅度,即注入攻擊的規(guī)?;蛴绊懥?。例如,低幅度的實驗可能是在生產(chǎn)者和broker之間的網(wǎng)絡(luò)流量中增加20毫秒的延遲。大幅度的實驗可能是增加500毫秒的延遲,因為這將對性能和吞吐量產(chǎn)生顯著的影響。與爆炸半徑一樣,從低幅度開始,然后逐漸增大。
          • 第三步:監(jiān)控基礎(chǔ)架構(gòu)。確定哪些指標(biāo)將助力得出有關(guān)假說的結(jié)論,在測試之前進行觀測以建立基線,并在整個測試過程中記錄這些指標(biāo),以便可以監(jiān)測預(yù)期和意外的變化。借助Confluent平臺,我們可以使用Control Center從Web瀏覽器實時直觀地觀察群集的性能。
          • 第四步:運行實驗。Gremlin允許以簡單、安全和可靠的方式在應(yīng)用程序和基礎(chǔ)架構(gòu)上運行實驗。我們通過運行注入攻擊來做到這一點,它提供了各種向系統(tǒng)中注入故障的方法。我們還要定義中止條件,這是我們應(yīng)該停止測試以避免意外損壞的條件。使用Gremlin,我們可以將狀態(tài)檢查定義為場景的一部分。通過狀態(tài)檢查,我們可以在進行注入攻擊時驗證服務(wù)狀態(tài)。如果基礎(chǔ)架構(gòu)運行不正常,并且狀態(tài)檢查失敗,將自動停止實驗。另外,我們可以使用內(nèi)置的暫停按鈕立即停止實驗。
          • 第五步:從觀察結(jié)果得出結(jié)論。它是否證實或反駁了原先的假設(shè)?使用收集的結(jié)果來修改基礎(chǔ)架構(gòu),然后圍繞這些改進設(shè)計新的實驗。隨著時間的推移,重復(fù)此過程將有助于使Kafka部署更具韌性。本文中介紹的實驗絕不是詳盡無遺的,而應(yīng)將其作為在系統(tǒng)上進行實驗的起點。請記住,盡管我們正在Confluent平臺上運行這些實驗,但它們可以在任何Kafka集群上執(zhí)行。
          請注意,我們使用的是基于Kafka 2.5.0構(gòu)建的Confluent 平臺 5.5.0。屏幕截圖和配置詳細信息可能會因版本而異。
          實驗1: Broker負載對處理延遲的影響
          資源利用率可能會對消息吞吐量產(chǎn)生顯著影響。如果broker正在經(jīng)歷較高的CPU、內(nèi)存或磁盤I/O利用率,則處理消息的能力將受到限制。由于Kafka的效率依賴于最慢的組件,因此延遲可能會在整個管道中產(chǎn)生級聯(lián)效應(yīng),并導(dǎo)致故障條件,例如生產(chǎn)者備份和復(fù)制延遲。高負載還會影響集群操作,例如broker運行狀況檢查,分區(qū)重新分配和領(lǐng)導(dǎo)者選舉,從而使整個集群處于不正常狀態(tài)。
          優(yōu)化Kafka時要考慮的兩個最重要的指標(biāo)是網(wǎng)絡(luò)延遲和磁盤I/O。Broker不斷在本地存儲中讀寫數(shù)據(jù),隨著消息速率和群集大小的增加,帶寬使用可能成為限制因素。在確定集群大小時,我們應(yīng)該確定資源利用率在哪一點上會對性能和穩(wěn)定性產(chǎn)生不利影響。
          為了確定這一點,我們將進行一個混沌實驗,逐步提高broker之間的磁盤I/O利用率,并觀察其對吞吐量的影響。在運行此實驗時,我們將使用Kafka Music演示應(yīng)用程序發(fā)送連續(xù)的數(shù)據(jù)流。該應(yīng)用程序?qū)⑾l(fā)送到分布在所有三個broker中的多個主題,并使用Kafka Streams聚合和處理消息。
          使用IO Gremlin生成Broker負載
          在本實驗中,我們將使用IO Gremlin在broker節(jié)點上生成大量磁盤I/O請求。我們將創(chuàng)建一個方案,并在四個階段中逐步增加注入攻擊的強度。每次注入攻擊將運行三分鐘,中間會間隔一分鐘,因此我們可以輕松地將I/O利用率的變化與吞吐量的變化聯(lián)系起來。
          此外,我們將創(chuàng)建一個狀態(tài)檢查,該狀態(tài)檢查使用Kafka Monitoring API在每個階段之間檢查broker的運行狀況。狀態(tài)檢查是通過Gremlin發(fā)送自動HTTP請求到我們選定的端點,在這種情況下,該端點是我們集群的REST API服務(wù)器。我們將使用主題的端點來檢索broker的狀態(tài),并解析JSON響應(yīng)以確定它們當(dāng)前是否處于同步狀態(tài)。如果任何broker不同步,我們將立即停止實驗并將其標(biāo)記為失敗。在場景運行期間,我們還將使用Confluent Control Center監(jiān)視吞吐量和延遲。

          • 假設(shè):磁盤I/O的增加將導(dǎo)致吞吐量的相應(yīng)下降。
          • 結(jié)論:即使將磁盤I/O增加到150 MB/s以上,技術(shù)攻擊也不會對吞吐量或延遲產(chǎn)生明顯影響。兩項指標(biāo)均保持穩(wěn)定,我們的broker均未失去同步或復(fù)制不足,也沒有消息丟失或損壞。

          目前,這給我們留下了很多開銷,但是隨著應(yīng)用范圍的擴大,對吞吐量的要求可能會增加。我們應(yīng)該密切關(guān)注磁盤I/O利用率,以確保有足夠的擴展空間。如果開始注意到磁盤I/O增加和吞吐量減少,則應(yīng)考慮:
          • 使用更快的存儲設(shè)備,例如更高的RPM磁盤或固態(tài)存儲;
          • 使用更有效的壓縮算法,例如Snappy或LZ4。
          實驗2: 消息丟失造成數(shù)據(jù)丟失的風(fēng)險 
          為了確保成功傳遞消息,生產(chǎn)者和broker使用確認機制。當(dāng)broker將消息提交到其本地日志時,它會與響應(yīng)生產(chǎn)者進行確認,表明已成功接收到該消息,并且生產(chǎn)者可以發(fā)送下一條消息。這樣可以減少生產(chǎn)者和broker之間丟失消息的風(fēng)險,但是不能防止broker之間丟失消息。
          例如,假設(shè)我們有一個broker領(lǐng)導(dǎo),剛從生產(chǎn)者那里收到消息并發(fā)送了確認。Broker的每個訂閱者現(xiàn)在都應(yīng)該獲取消息并將其提交到他們自己的本地日志中。但是,broker在其任何訂閱者獲取最新消息之前意外失敗。跟隨者沒有一個知道生產(chǎn)者發(fā)送了一條消息,但是生產(chǎn)者已經(jīng)收到了確認,因此它已移至下一條消息。除非我們可以恢復(fù)發(fā)生故障的broker,或者找到其他重新發(fā)送消息的方法,否則消息實際上已經(jīng)丟失了。
          我們?nèi)绾未_定在集群上發(fā)生這種情況的風(fēng)險?借助混沌工程,我們可以模擬broker領(lǐng)導(dǎo)的故障并監(jiān)控消息流,以確定潛在的數(shù)據(jù)丟失。
          使用黑洞Gremlin模擬Broker領(lǐng)導(dǎo)中斷
          在此實驗中,我們將使用黑洞Gremlin刪除往返于broker領(lǐng)導(dǎo)的所有網(wǎng)絡(luò)流量。此實驗很大程度上取決于時間安排,因為我們希望在broker收到消息之后,但在其訂閱者可以復(fù)制消息之前,引起broker的失敗。這可通過兩種方式執(zhí)行此操作:
          • 比追隨者低的時間間隔,發(fā)送連續(xù)的消息流,啟動實驗,并尋找使用者輸出中的空白(replica.fetch.wait.max.ms)。 
          • 發(fā)送消息后,立即使用Gremlin API從生產(chǎn)者應(yīng)用程序觸發(fā)混沌實驗。 
          在本實驗中,我們將使用第一種方式。應(yīng)用程序每100毫秒產(chǎn)生一條新消息。消息流的輸出記錄為JSON列表,并對其分析以查找任何差距或時序上的不一致。我們將對其注入攻擊30秒,這將生成300條消息(每100毫秒一條消息)。
          • 假設(shè):由于領(lǐng)導(dǎo)者失敗,我們將丟失一些消息,但是Kafka將迅速選出新的領(lǐng)導(dǎo)者,并再次成功復(fù)制消息。
          • 結(jié)果:盡管領(lǐng)導(dǎo)者突然失敗,消息列表仍顯示所有成功通過的消息。由于實驗前后記錄了額外的消息,因此我們的管道總共產(chǎn)生了336個事件,每個消息在上一個事件之后大約有100毫秒的時間戳。消息沒有按時間順序顯示,但這很好,因為Kafka不保證分區(qū)之間消息的順序。這是輸出示例:

          如果想保證所有消息都已保存,則可以在生產(chǎn)者配置中設(shè)置acks = all。這告訴生產(chǎn)者在將消息復(fù)制到broker領(lǐng)導(dǎo)及其所有訂閱者之前,不要發(fā)送新消息。這是最安全的選擇,但是它將吞吐量限制為最慢broker的速度,因此可能會對性能和延遲產(chǎn)生重大影響。
          實驗3: 避免腦裂
          Kafka、ZooKeeper和類似的分布式系統(tǒng)容易受到稱為“腦裂”問題的影響。在腦裂中,同一群集內(nèi)的兩個節(jié)點失去同步并分區(qū),從而產(chǎn)生群集中兩個單獨且可能不兼容的視圖。這會導(dǎo)致數(shù)據(jù)不一致,數(shù)據(jù)損壞,甚至形成第二個群集。
          這是怎么發(fā)生的?在Kafka中,為單個broker節(jié)點分配了控制器角色??刂破髫撠?zé)檢測群集狀態(tài)的更改,例如失敗的broker、領(lǐng)導(dǎo)者選舉和分區(qū)分配。每個集群只有一個且只有一個控制器,以維護集群的單個一致性視圖。盡管這使控制器成為單點故障,但Kafka擁有處理此類故障的過程。
          所有broker都會定期向ZooKeeper登記,以證明自己的健康。如果broker的響應(yīng)時間超過zookeeper.session.timeout.ms設(shè)置(默認為18,000毫秒),則ZooKeeper會將broker標(biāo)記為不正常。如果該broker是控制者,則觸發(fā)控制者選舉,副本ISR成為新的控制者。
          這個新的控制器被分配了一個稱為控制器紀(jì)元的編號,該編號跟蹤最新的控制器選舉。如果發(fā)生故障的控制器重新聯(lián)機,它將比較自己的控制器紀(jì)元與ZooKeeper中存儲的紀(jì)元,識別出新選的控制器,然后退回為普通broker。
          這個過程可以防止少數(shù)broker失敗,但如果大部分broker都發(fā)生了重大故障,那該怎么辦呢?我們可以在不產(chǎn)生腦裂的情況下重新啟動它們嗎?我們可以使用混沌工程對此進行驗證。

          使用關(guān)機Gremlin重啟大多數(shù)broker節(jié)點 
          此實驗中,我們將使用關(guān)機Gremlin重新啟動集群中的三個broker節(jié)點中的兩個。由于此實驗可能會對集群穩(wěn)定性造成潛在風(fēng)險(例如,我們不想意外關(guān)閉所有ZooKeeper節(jié)點),因此想確保在運行該broker之前,所有三個broker都應(yīng)處于健康狀態(tài)。我們將創(chuàng)建一個狀態(tài)檢查,從Kafka Monitoring API中獲取健康broker的列表,以驗證所有三個broker均已啟動并正在運行。
          這是我們完全配置的場景,顯示狀態(tài)檢查和關(guān)機Gremlin:

          • 假設(shè):Kafka的吞吐量將暫時停止,但是兩個broker節(jié)點都將重新加入群集,而不會出現(xiàn)問題。
          • 結(jié)果:控制中心仍列出了三個broker,但顯示其中兩個不同步且分區(qū)復(fù)制不足。這是預(yù)料之中的,因為節(jié)點已經(jīng)失去了與其余broker和ZooKeeper的聯(lián)系。

          當(dāng)先前的控制器(broker1)脫機時,ZooKeeper立即選舉剩余的broker(broker3)為新的控制器。由于這兩個broker在不超過ZooKeeper會話超時的情況下重新啟動,因此,從broker正常運行時間的圖表,可以看出它們一直處于聯(lián)機狀態(tài)。但是,當(dāng)我們的消息管道轉(zhuǎn)移到broker3時,查看吞吐量和副本的圖表,就會發(fā)現(xiàn)這明顯地影響了吞吐量和分區(qū)運行狀況。

          盡管如此,broker毫無意外地重新加入了集群。由此可以得出結(jié)論,我們的集群可以承受暫時的多數(shù)失敗。性能將顯著下降,集群將需要選舉新的領(lǐng)導(dǎo)者,重新分配分區(qū),并在其余broker之間復(fù)制數(shù)據(jù),但是不會陷入腦裂的局面。如果花更長的時間來恢復(fù)broker,則此結(jié)果可能會有所不同,因此,我們希望確定在重大生產(chǎn)中斷的情況下,已制定了事件響應(yīng)計劃。
          實驗4: ZooKeeper中斷
          ZooKeeper是Kafka的基本依賴項。它負責(zé)諸如識別broker、選舉領(lǐng)導(dǎo)者以及跟蹤broker之間的分區(qū)分布等活動。ZooKeeper中斷不一定會導(dǎo)致Kafka發(fā)生故障,但是如果解決時間越長,它可能會導(dǎo)致意外問題。
          在一個示例中,HubSpot遇到了ZooKeeper故障,原因是短時產(chǎn)生大量備份的請求。ZooKeeper在幾分鐘內(nèi)無法恢復(fù),這又導(dǎo)致Kafka節(jié)點開始崩潰。最終導(dǎo)致數(shù)據(jù)損壞,團隊不得不手動將備份數(shù)據(jù)還原到服務(wù)器。雖然這是HubSpot解決的一個不尋常的情況,但它強調(diào)了將ZooKeeper和Kafka作為單獨服務(wù)和整體服務(wù)進行測試的重要性。
          https://blog.hubspot.com/customers/last-weeks-product-outage-what-happened-how-were-changing-as-a-result
          使用黑洞Gremlin模擬ZooKeeper中斷
          此實驗中,我們要驗證Kafka集群可以在ZooKeeper意外中斷時能幸免于難。我們將使用黑洞Gremlin丟棄往返ZooKeeper節(jié)點的所有流量。我們將在控制中心中監(jiān)視群集狀態(tài)的同時運行注入攻擊五分鐘。

          • 假設(shè):Kafka可以忍受短期的ZooKeeper中斷,而不會崩潰,丟失數(shù)據(jù)或損壞數(shù)據(jù)。但是,直到ZooKeeper重新聯(lián)機之前,對群集狀態(tài)的任何更改都將無法解決。
          • 結(jié)果:運行實驗對消息吞吐量或broker可用性沒有任何結(jié)果。正如我們所假設(shè)的,可以繼續(xù)產(chǎn)生和使用消息而不會出現(xiàn)意外問題。
          如果其中一個broker失敗,則在ZooKeeper重新聯(lián)機之前,broker無法重新加入群集。僅此一項并不太可能導(dǎo)致失敗,但是可能導(dǎo)致另一個問題:級聯(lián)失敗。例如,broker失敗將導(dǎo)致生產(chǎn)者將負擔(dān)轉(zhuǎn)移到其余broker上。如果這些broker接近生產(chǎn)極限,就會依次崩潰。即使我們使失敗的broker重新聯(lián)機,也將無法加入集群,直到ZooKeeper再次可用。
          該實驗表明,我們可以容忍ZooKeeper暫時失敗,但是仍然應(yīng)該迅速工作以使其恢復(fù)在線狀態(tài)。還應(yīng)該尋找減輕完全中斷風(fēng)險的方法,例如在多個區(qū)域之間分布ZooKeeper以實現(xiàn)冗余。盡管這將增加運營成本并增加延遲,但與使整個集群出現(xiàn)生產(chǎn)故障相比,這是一個很小的成本。

          四、進一步提高Kafka的可靠性



          Kafka是一個復(fù)雜的平臺,具有大量相互依賴的組件和流程。要使Kafka可靠地運行,就需要進行計劃、持續(xù)的監(jiān)控和主動的故障測試。這不僅適用于我們的Kafka和ZooKeeper集群,還適用于使用Kafka的應(yīng)用程序。混沌工程使我們能夠安全有效地發(fā)現(xiàn)Kafka部署中的可靠性問題。為今天的故障做準(zhǔn)備可以防止或減少將來發(fā)生故障的風(fēng)險和影響,從而節(jié)省組織的時間、工作量以及挽救客戶的信任。
          現(xiàn)在,我們已經(jīng)展示了Kafka的四個不同的混沌實驗,請嘗試注冊Gremlin Free帳戶來運行這些實驗。創(chuàng)建實驗時,請考慮Kafka部署中的潛在故障點(例如,broker與使用者之間的連接),并觀察它們?nèi)绾螒?yīng)對不同的注入攻擊。如果發(fā)現(xiàn)問題,請實施修復(fù)程序并重復(fù)實驗以驗證它可以解決問題。隨著系統(tǒng)變得更加可靠,逐漸增加幅度(實驗的強度)和爆炸半徑(受實驗影響的系統(tǒng)數(shù)量),以便全面地測試整個部署。
          IDCF DevOps黑客馬拉松,獨創(chuàng)端到端DevOps體驗,精益創(chuàng)業(yè)+敏捷開發(fā)+DevOps流水線的完美結(jié)合,2021年僅有的3場公開課,數(shù)千人參與并一致五星推薦的金牌訓(xùn)練營,追求卓越的你一定不能錯過!
          瀏覽 99
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  在线看的黄片网站 | 国产二级片 | 久久99精品久久久久久国产越南 | 精品少妇久久 | 国产精品探花在线观看 |