從代碼的視角深入淺出理解DevOps

来源:https://www.cnblogs.com/code-craftsman/archive/2019/11/14/11858091.html
-Advertisement-
Play Games

對於DevOps的理解大家眾說紛紜,就連維基百科(Wikipedia)都沒有給出一個統一的定義。一般的解釋都是從字面上來理解,就是把開發(Development)和運維(Operations)整合到一起,來加速產品從啟動到上線的過程,並使之自動化。這個是對DevOps的廣義解釋,而且大多數人都是認可 ...


對於DevOps的理解大家眾說紛紜,就連維基百科(Wikipedia)都沒有給出一個統一的定義。一般的解釋都是從字面上來理解,就是把開發(Development)和運維(Operations)整合到一起,來加速產品從啟動到上線的過程,並使之自動化。這個是對DevOps的廣義解釋,而且大多數人都是認可的。但這個解釋太寬泛了,幾乎包括了IT的所有內容,使之沒有太大意義。 而DevOps是近幾年才興起的(2014年才開始流行),它是對某種項目模式的描述,是有著其特定內涵的。任何項目都可以分成開發和運維兩個部分,而開發的一整套流程和工具在DevOps之前早就有了,並沒有改變。

DevOps真正改變的是運維。因此從運維的角度去理解DevOps,才能抓住它的本質。你可以把它理解為用開發的方式做運維(Operation as Development),這就是對它的狹義的理解。 開發的方式就是寫代碼,換句話說DevOps就是通過寫代碼來做運維。運維里一個非常流行的概念叫“Iac(InfrastructureAsCode)” 基礎設施即代碼,也就是把運行環境的創建用代碼的形式來描述出來,通過運行代碼來創建環境。它是運維領域的一場革命,開創了現代運維技術,它也是DevOps的基石。但基礎設施創建只是運維的一部分,如果我們把這場革命繼續延伸到運維的各個領域,讓代碼覆蓋整個運維,那時就是代碼即運維(Operation as Code),這才是DevOps的精髓。

那麼從一個應用程式項目的角度看,什麼是DevOps呢?它就是把應用程式的代碼和運維的代碼都放到一個源程式庫中,並對它進行版本管理,這樣你就擁有了關於這個項目的所有信息,隨時可以部署這個程式(包括程式本身和它的運行環境),而且可以保證每次創建出來的程式的運行結果是一樣的(因為它的運行環境也是一樣的)。

運維即代碼(Operation as Code):

下麵我們就把運維所做的事情一件一件拆分開,看看他們是怎麼用代碼來實現的。

運維的工作通常包括下麵幾個方面:

  • 基礎設施:即程式運行環境的創建和維護。
  • 持續部署:部署應用程式,並使整個過程自動化。
  • 服務的健壯性:是指當服務的的運行環境出現了問題,例如網路故障或服務過載或某些微服務宕機的情況下,程式仍能夠提供部分或大部分服務。
  • 運行監測:它既包含對程式的監測也包含對運行環境的監測。

基礎設施即代碼 (Infrastructure as code)

我們通過一個Go(別的語言也大同小異)微服務程式做例子來展示如何用代碼來創建基礎設施。程式本身的功能非常簡單,只是用SQL語句訪問資料庫中的數據,並寫入日誌。你可以簡單地把它分成兩層,後端數據訪問層和資料庫層。程式的部署環境是基於k8s的容器環境。在k8s中它被分成兩個服務。一個是後端程式服務,另一個是資料庫(用MySQL)服務。後端程式要調用資料庫服務,然後會把一些數據寫入日誌。

在這種新的模式下,運行環境的代碼和應用程式的代碼是存在一個源碼庫中的,這樣當你下載了源碼庫之後,你不但擁有了程式的所有源碼,而且也擁有了運行環境的源碼。這樣當要創建新的運行環境時,只要運行一遍代碼就能創建出整套的運行環境,而且每次創建出來的環境都是一致的。

file

上面就是這個Go程式的目錄結構,它裡面有一個目錄“script”是專門存放與運行環境相關的文件的,裡面的“kuburnetes”子目錄就是整個運行環境的代碼。除了“script”之外的其它目錄存有應用程式的代碼。這樣,與這個應用程式有關的信息都以代碼的方式保存在了一個源代碼庫。有了它之後,你可以隨時部署出一個相同的程式的運行環境,而且保證是一模一樣的。

“kubernetes”目錄下有兩個子目錄“backend”和“database”分別存放後端程式和資料庫的配置文件。它們內部的結構是類似的,都有三個“yaml”文件:

  • backend-deployment.yaml:部署配置文件,
  • backend-service.yaml:服務配置文件
  • backend-volume.yaml:持久捲配置文件.

另外還有一個“.sh”文件是它的運行命令,當你運行這個shell文件時,它就調用上面三個k8s配置文件來創建運行環境。

kubernetes目錄的最外層有兩個“yaml”文件“k8sdemo-config.yaml”和"k8sdemo-secret.yaml",它們是用來創建k8s運行環境參數的,因為它們是被不同服務共用的,因此放在最外層。另外還有一個"k8sdemo.sh"文件是k8s命令文件,用來創建k8s對象。

這種源程式結構的一個好處就是使應用程式和它的運行環境能夠更好地集成。舉個例子,當你要修改服務的埠時,以前,你需要在運行環境和源碼里分別修改,但它是分別由開發和運維完成的,這很容易造成修改的不同步。當你把它們放在同一個源碼庫中,只需要修改一個地方,這樣就保證了應用程式和運行環境的一致性。

下麵就是後端服務的k8s配置代碼:

apiVersion: v1
kind: Service
metadata:
  name: k8sdemo-backend-service
  labels:
    app: k8sdemo-backend
spec:
  type: NodePort
  selector:
    app: k8sdemo-backend
  ports:
    - protocol : TCP
      nodePort: 32080
      port: 80
      targetPort: 8080

由於篇幅的關係,這裡就不詳細解釋程式了,有興趣的請參見如何把應用程式遷移到k8s.

基礎設施可以分成兩個層面,一個就是上面講到的k8s層面,也就是容器層面,這個是跟應用程式緊密相關的。還有一個層面就是容器下麵的支持層,也就是虛機層面,當然還包括網路,負載均衡等設備或軟體。當你在阿裡雲或華為雲上創建k8s之前,先要把這些構建好才行。它的部署也可以用代碼來完成。Terraform就是一款非常流行的用來完成創建的工具,它是被ThoughtWorks推薦的(詳見ThoughtWorks TECHNOLOGY RADAR VOL.20 ),它支持用代碼來創建虛機。

代碼如下:

resource "aws_instance" "example" {
  count         = 10
  ami           = "ami-v1"
  instance_type = "t2.micro"
}

但在這一層面,基礎設施的工作與應用程式的關聯並沒有那麼緊密,因此這部分的代碼沒有放在應用程式里,你可以單獨創建一個基礎設施的源碼項目,用來存放這部分代碼。

持續部署(Continuous Deployment)

部署應用程式是運維的一項重要工作。隨著商業競爭的加劇,要求更快的程式更新,從原來的的幾周部署一次,到後來的一天部署十幾次甚至幾十次。這樣手工部署就完全不能滿足需要,於是就要把整個流程自動化,這就是持續部署。

管線(pipeline)是一個很重要的概念,它用來描述持續部署的整個步驟和流程。Jenkin是一款非常流行的持續集成和部署工具,它提出了“管線即代碼”(“Pipeline as code”,詳見Pipeline)。就是把持續部署的管線也作為程式源碼的一部分,和應用程式一起管理起來,讓它有著和應用程式一樣的版本和覆審流程。

下麵我們就通過一個具體例子來說明他是怎樣實現的。這個例子用的是和上面一樣的程式。先來看一下程式的目錄結構。

file

與持續部署相關的文件都在“script”目錄下,他被分成兩部分,一個是“cd”子目錄,它存有Jenkins的管線,另一個是“Kubernetes”下的“jenkins”子目錄,它存有Jenkinsde的k8s配置文件。你如果仔細看一下的話會發現它裡面的文件和前面講到的後端程式和資料庫的k8s配置文件很相似,有了它,你就可以在k8s里創建出Jenkins的運行環境。

這裡我把Jenkins的k8s配置文件也放在應用程式里了,但實際上它是應該放在前面提到的基礎設施項目源碼里。因為Jenkins是被應用程式共用的,而不是屬於單獨的一個應用。這裡為了說明方便放在一起了,真正用的時候要把它抽取出來。

下麵就是管線的代碼:

def POD_LABEL = "k8sdemopod-${UUID.randomUUID().toString()}"
podTemplate(label: POD_LABEL, cloud: 'kubernetes', containers: [
    containerTemplate(name: 'modified-jenkins', image: 'jfeng45/modified-jenkins:1.0', ttyEnabled: true, command: 'cat')
  ],
  volumes: [
     hostPathVolume(mountPath: '/var/run/docker.sock', hostPath: '/var/run/docker.sock')
  ]) {

    node(POD_LABEL) {
       def kubBackendDirectory = "/script/kubernetes/backend"
       stage('Checkout') {
            container('modified-jenkins') {
                sh 'echo get source from github'
                git 'https://github.com/jfeng45/k8sdemo'
            }
          }
       stage('Build image') {
            def imageName = "jfeng45/jenkins-k8sdemo:${env.BUILD_NUMBER}"
            def dockerDirectory = "${kubBackendDirectory}/docker/Dockerfile-k8sdemo-backend"
             container('modified-jenkins') {
               withCredentials([[$class: 'UsernamePasswordMultiBinding',
                 credentialsId: 'dockerhub',
                 usernameVariable: 'DOCKER_HUB_USER',
                 passwordVariable: 'DOCKER_HUB_PASSWORD']]) {
                 sh """
                   docker login -u ${DOCKER_HUB_USER} -p ${DOCKER_HUB_PASSWORD}
                   docker build -f ${WORKSPACE}${dockerDirectory} -t ${imageName} .
                   docker push ${imageName}
                   """
               }
             }
           }
       stage('Deploy') {
           container('modified-jenkins') {
               sh "kubectl apply -f ${WORKSPACE}${kubBackendDirectory}/backend-deployment.yaml"
               sh "kubectl apply -f ${WORKSPACE}${kubBackendDirectory}/backend-service.yaml"
             }
       }
    }
}

由於篇幅的關係,這裡不詳細解釋。如果有興趣並想瞭解如何運行Jenkins來完成持續部署,請參閱 在容器上構建持續部署及最佳實踐初探.

這裡我用的是Jenkins軟體,它另外還有一個子項目叫Jenkins-x,是專門為k8s環境量身打造的,它的主要功能是能夠幫助你自動生成管線代碼(你需要回答他的一些問題)。如果你不想自己寫代碼,那麼你可以試一下它,詳情請見Jenkins-X

服務的韌性(Service Resilience as Code)

又叫服務的健壯性,這部分不像前面兩個部分有著公認的名字,英文叫“Service Resilience”,翻譯成中文就五花八門了,我覺得叫服務的韌性比較合適。

“Service Resilience”是指當服務的的運行環境出現了問題,例如網路故障或服務過載或某些微服務宕機的情況下,程式仍能夠提供部分或大部分服務,這時我們就說服務的韌性很強。它是衡量服務質量的一個重要指標。

這部分的功能包括下麵幾個部分:

  • 服務超時 (Timeout)
  • 服務重試 (Retry)
  • 服務限流(Rate Limiting)
  • 熔斷器 (Circuit Breaker)
  • 故障註入(Fault Injection)
  • 艙壁隔離技術(Bulkhead)

這部分與前面兩個部分略有不同,前面兩個部分都是典型的運維任務,而這部分以前是應用程式的一部分,只是這些年才慢慢開始轉移到運維的。

最開始的時候,這些功能都是和程式的業務邏輯混在一起,對業務邏輯的侵入很大,後來,大家開始把這部分邏輯抽取出來,劃分成單獨的一部分。下麵通過一個具體的例子(Go微服務程式)來講解:

file

上圖是程式的目錄結構,它分為客戶端(client)和服務端(server),它們的內部結構是類似的。“middleware”包是實現服務韌性功能的包。 “service”包是業務邏輯包,在服務端就是微服務的實現函數,在客戶端就是調用服務端的函數。在客戶端(client)下的“middleware”包中包含四個文件並實現了三個功能:服務超時,服務重試和熔斷器。“clientMiddleware.go"是總入口。在服務端(server)下的“middleware”包中包含了兩個文件並實現了一個功能,服務限流。“serverMiddleware.go"是總入口。

註意,這裡的服務韌性的功能是完全從業務邏輯中抽出來了,對業務邏輯沒有任何侵入,它是在一個單獨的包(middleware)里實現的。這裡用的是修飾模式。有關程式實現的詳情,請參閱Go微服務容錯與韌性(Service Resilience)

上面講的是用程式來實現這些功能,但從本質上來講這些功能不應該屬於應用程式,而是應該由基礎設施來完成。現在公認的看法是,服務網格(Service Mesh)是完成這些功能的最佳方案。使用服務網格的方式和k8s類似,也是創建配置文件,然後通過運行配置文件來建立服務網格的運行環境。我們這裡用Istio來舉例說明,Istio是一款非常流行的服務網格軟體。

file

上圖就是下載的Istio軟體的目錄,“bookinfo”是它的一個示常式序,在這個例子里,它展示了多種使用Istio的方式,其中就有如何實現服務韌性的方法。詳情請參見istio.

運行監測 (Monitoring or Observability)

運行監測是運維的一項重要內容,它通常包含如下幾個方面的內容:

  • 日誌(logging): 記錄的是程式運行過程中的信息
  • 跟蹤(tracing): 記錄的是與一條請求相關的信息,特別是請求的與時間有關的信息。
  • 指標(metrics): 與上面的離散的信息不同,這裡記錄的是可累加的信息,一般是按照時間軸進行累加。

我們經常稱之為觀測的三個支柱(Three Pillars of Observabilty),有一篇很不錯的講解它們之間關係的文章,詳情請見"Metrics, tracing 和 logging 的關係"。

這部分的內容可能會有些爭議。因為前幾個部分都是清清楚楚的運維工作,即使服務韌性, 雖然以前是開發的工作,但現在也已經一直公認是運維的事,而且它們的代碼都能很乾凈的摘出來。但運行監測不一樣,雖然主要工作還是由運維來完成,但它的代碼與業務邏輯代碼混在一起,很難摘得清楚。

日誌:

這部分的代碼都是在應用程式里,但日誌的採集,彙總,分析和展示都是由運維來完成。它的代表就是著名的ELK系列。採用DevOps之後,這裡面的變化不大,頂多就是採集代理(Agent)更好地和服務網格或k8s進行集成,使之變得更為容易。

分散式跟蹤:

這部分有點像服務韌性,開始的時候是由程式完成,慢慢地把它變成單獨的部分與應用程式隔離開,最終大部分的工作交由服務網格來完成。但它又與服務韌性不太相同。服務韌性可以和應用程式做一個非常乾凈的切割,而分散式跟蹤取決於跟蹤的顆粒度。如果僅是服務之間的跟蹤,就一點問題都沒有,完全可以由服務網格來完成。但如果是服務內部的跟蹤,服務網格就無能為力了,還是要由程式代碼來完成,就像日誌一樣。但我覺得服務之間的跟蹤是投入產出比最高的,大多數情況下有它就足夠了,不必需要服務內部的跟蹤。

詳細情況請參見Go微服務全鏈路跟蹤詳解

Metrics:

這部分觀測的是累加信息。大多數情況下,只要安裝好工具,就能採集數據進行分析。最流行的工具是Prometheus. 你不需要寫代碼來獲取數據,不過你如果想要快速地找到需要的信息,k8s的配置還是要和Prometheus的設置相匹配,因此你需要做一些詳細的設計。詳細情況請參見Prometheus and Kubernetes: Monitoring Your Applications

當然你如果有一些更細緻的監測需求,Prometheus不能直接滿足。這時需要在應用程式里插入一些Prometheus的監測代碼來滿足你的需要。

其他工作

是不是還有其他運維工作被漏掉了?

持續集成(Continious Integration)

很多人都把持續集成(CI)算作DevOps的重要組成部分,那是因為他用的是廣義的定義。按照狹義的理解,DevOps只包括運維的內容。持續集成(CI)與持續部署(CD)有著明顯的不同,持續集成是開發的工作,而持續部署是運維的工作,下圖展示了它們的差異。

file

圖片來源

如圖所示,整個流程是這樣的:
程式員從源碼庫(Source Control)中下載源代碼,編寫程式,完成後提交代碼到源碼庫,持續集成(Continuous Integration)工具從源碼庫中下載源代碼,編譯源代碼,然後提交到運行庫(Repository),然後持續交付(Continuous Delivery)工具從運行庫(Repository)中下載代碼,生成發佈版本,併發布到不同的運行環境(例如DEV,QA,UAT, PROD)。

圖中,左邊的部分是持續集成,它主要跟開發和程式員有關;右邊的部分是持續部署,它主要跟測試和運維有關。持續交付(Continuous Delivery)又叫持續部署(Continuous Deployment),它們如果細分的話還是有一點區別的,但我們這裡不分得那麼細,統稱為持續部署

我並沒有把持續集成放到DevOps裡面,因為本文用的是狹義的解釋,也就是只包含運維的部分。

結論

本文從代碼的視角詮釋了對DevOps的理解,DevOps的精髓就是用寫代碼的方式來做運維,並對運維的各個部分給出了具體的實例,希望能對想採用DevOps的朋友有所幫助。DevOps對開發和運維的改變都是巨大的,尤其是對運維。在不久的將來,就沒有開發和運維之分了,只有一個工作,就是寫代碼,當然也許會細分成開發碼農和運維碼農。運維的工作都是通過寫代碼來完成。應用程式里不但包括業務邏輯的代碼,也包括運維的代碼,它們會被同時存儲在一個源碼庫中。

源碼庫

完整源碼的github鏈接:

索引:

  1. InfrastructureAsCode
  2. 如何把應用程式遷移到k8s
  3. ThoughtWorks TECHNOLOGY RADAR VOL.20
  4. Pipeline
  5. 在容器上構建持續部署及最佳實踐初探
  6. Go微服務容錯與韌性(Service Resilience)
  7. istio
  8. Metrics, tracing 和 logging 的關係
  9. Go微服務全鏈路跟蹤詳解
  10. Prometheus and Kubernetes: Monitoring Your Applications
  11. Nexus Platform Overview

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1 /** 2 * @param: url 附件地址 3 * @param: filename 下載後的文件名 4 */ 5 function download(url, filename) { 6 getBlob(url, function (blob) { 7 saveAs(blob, file ...
  • 頂級對象:window onload = function(){} getElementById('nav').offsetHieght //獲取div=nav元素,距離html頂部元素的距離,同類型的還有offsetLeft,offsetRight.offsetBottom 頁面滾動事件: <bo ...
  • vue中添加滾動載入更多,因為是單頁面所以需要在跳出頁面時候銷毀滾動,要不會出現錯亂。我們在mounted建立滾動,destroyed銷毀滾動。 mounted () { window.addEventListener('scroll', this.handleScroll) }, destroye ...
  • 2d x y 3d x y z 左手坐標系 伸出左手,讓拇指和食指成“L”形,大拇指向右,食指向上,中指指向前方。這樣我們就建立了一個左手坐標系,拇指、食指和中指分別代表X、Y、Z軸的正方向。如下圖 CSS3中的3D坐標系與上述的3D坐標系是有一定區別的,相當於其繞著X軸旋轉了180度,如下圖 簡單 ...
  • 現在很多網站會用到進入網站特效,到網頁沒有載入完成的時候,會有一個loding特效,載入完了之後才能看到頁面,今天就帶著做一個js進度條效果,今天要做的效果是純js進度條載入,沒有用到框架,方便大家進行深入理解: 首先我們要進行js進度條的佈局 js進度條佈局如下: 1234567891011121 ...
  • 網路請求編碼表示會從你是否關註Request head的哪些內容入手,一般關註點放在statusCode和method上就夠用,重點200,304。同時掌握了基礎後希望註意的點為文件上傳的內容(百度問過斷點續傳的實現) 200: ‘伺服器成功返回請求的數據。’,201: ‘新建或修改數據成功。’,2 ...
  • 一.起因 我在做爬蟲的時候發現很多網站上都在url上加一個 一開始我以為是啥加密後面發現其實他在後臺解析的時候也不需要 ,那他加個隨機數是做啥子 二.查看文獻得到總結 常用修改url方式 ...
  • transform是CSS3中具有顛覆性的特征之一,可以實現元素的位移、旋轉、傾斜、縮放,甚至支持矩陣方式,配合過渡和即將學習的動畫知識,可以取代大量之前只能靠Flash才可以實現的效果。 變形轉換 transform transform 變換 變形的意思 《 transformers 變形金剛》 ...
一周排行
    -Advertisement-
    Play Games
  • 概述:在C#中,++i和i++都是自增運算符,其中++i先增加值再返回,而i++先返回值再增加。應用場景根據需求選擇,首碼適合先增後用,尾碼適合先用後增。詳細示例提供清晰的代碼演示這兩者的操作時機和實際應用。 在C#中,++i 和 i++ 都是自增運算符,但它們在操作上有細微的差異,主要體現在操作的 ...
  • 上次發佈了:Taurus.MVC 性能壓力測試(ap 壓測 和 linux 下wrk 壓測):.NET Core 版本,今天計劃準備壓測一下 .NET 版本,來測試並記錄一下 Taurus.MVC 框架在 .NET 版本的性能,以便後續持續優化改進。 為了方便對比,本文章的電腦環境和測試思路,儘量和... ...
  • .NET WebAPI作為一種構建RESTful服務的強大工具,為開發者提供了便捷的方式來定義、處理HTTP請求並返迴響應。在設計API介面時,正確地接收和解析客戶端發送的數據至關重要。.NET WebAPI提供了一系列特性,如[FromRoute]、[FromQuery]和[FromBody],用 ...
  • 原因:我之所以想做這個項目,是因為在之前查找關於C#/WPF相關資料時,我發現講解圖像濾鏡的資源非常稀缺。此外,我註意到許多現有的開源庫主要基於CPU進行圖像渲染。這種方式在處理大量圖像時,會導致CPU的渲染負擔過重。因此,我將在下文中介紹如何通過GPU渲染來有效實現圖像的各種濾鏡效果。 生成的效果 ...
  • 引言 上一章我們介紹了在xUnit單元測試中用xUnit.DependencyInject來使用依賴註入,上一章我們的Sample.Repository倉儲層有一個批量註入的介面沒有做單元測試,今天用這個示例來演示一下如何用Bogus創建模擬數據 ,和 EFCore 的種子數據生成 Bogus 的優 ...
  • 一、前言 在自己的項目中,涉及到實時心率曲線的繪製,項目上的曲線繪製,一般很難找到能直接用的第三方庫,而且有些還是定製化的功能,所以還是自己繪製比較方便。很多人一聽到自己畫就害怕,感覺很難,今天就分享一個完整的實時心率數據繪製心率曲線圖的例子;之前的博客也分享給DrawingVisual繪製曲線的方 ...
  • 如果你在自定義的 Main 方法中直接使用 App 類並啟動應用程式,但發現 App.xaml 中定義的資源沒有被正確載入,那麼問題可能在於如何正確配置 App.xaml 與你的 App 類的交互。 確保 App.xaml 文件中的 x:Class 屬性正確指向你的 App 類。這樣,當你創建 Ap ...
  • 一:背景 1. 講故事 上個月有個朋友在微信上找到我,說他們的軟體在客戶那邊隔幾天就要崩潰一次,一直都沒有找到原因,讓我幫忙看下怎麼回事,確實工控類的軟體環境複雜難搞,朋友手上有一個崩潰的dump,剛好丟給我來分析一下。 二:WinDbg分析 1. 程式為什麼會崩潰 windbg 有一個厲害之處在於 ...
  • 前言 .NET生態中有許多依賴註入容器。在大多數情況下,微軟提供的內置容器在易用性和性能方面都非常優秀。外加ASP.NET Core預設使用內置容器,使用很方便。 但是筆者在使用中一直有一個頭疼的問題:服務工廠無法提供請求的服務類型相關的信息。這在一般情況下並沒有影響,但是內置容器支持註冊開放泛型服 ...
  • 一、前言 在項目開發過程中,DataGrid是經常使用到的一個數據展示控制項,而通常表格的最後一列是作為操作列存在,比如會有編輯、刪除等功能按鈕。但WPF的原始DataGrid中,預設只支持固定左側列,這跟大家習慣性操作列放最後不符,今天就來介紹一種簡單的方式實現固定右側列。(這裡的實現方式參考的大佬 ...