Go微服務 - 第六部分 - 健全狀態檢查

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

第六部分: Go微服務 - 健全狀態檢查

隨著我們的微服務越來越多,越來越複雜, 需要一種可以讓Docker Swarm知道服務是否健康的機制就變得十分重要了。因此,本文重點看看如何為微服務加入健全狀態檢查。

假如accountservice微服務不具備下面的能力,就毫無用處了:

  • 提供HTTP服務。
  • 串連到它自己的資料庫。

健康終端監測模式

在應用程式中實現功能檢查,外部工具可以通過暴露的端點進行定期訪問來實現。這有助於驗證應用程式和服務是否正確執行。

在微服務中處理這些情況的慣用方式是提供健全狀態檢查路由(Azure Docs的好文章), 在我們這個情況中,既然我們是基於HTTP的,應該映射一個/health,並響應200,如果將康的話, 可能需要帶一些機器可能性的訊息來解釋什麼是OK的。 如果有問題,返回非200響應, 以及可能的不健康的原因。注意有些人對檢查失敗應該返回200以及在響應負載中附加錯誤的資訊。 我也同意那樣做,但是為了簡化,本文中我們直接使用非200來檢查。因此我們需要在accountservice中添加一個/health路由。

原始碼

嚮往常一樣,我們一樣可以從git中籤出對應的分支,以擷取部分更改的代碼。

https://github.com/callistaen...

給boltdb添加檢查

我們的服務如果不能正常訪問底層資料庫的話,就沒有什麼用。因此,我們需要給IBoltClient添加一個介面Check().

type IBoltClient interface {    OpenBoltDb()    QueryAccount(accountId string) (model.Account, error)    Seed()    Check() bool              // NEW!} 

Check方法看起來可能比較單純,但是它在本文中還是很起作用的。它根據BoltDB是否可用而相應的返回true或false。

我們在boltclient.go檔案中Check的實現不很現實,但是它已經足夠解釋概念了。

// Naive healthcheck, just makes sure the DB connection has been initialized.func (bc *BoltClient) Check() bool {    return bc.boltDB != nil}

mockclient.go裡邊的類比實現也遵循我們的延伸/測試(stretchr/testify)標準模式:

func (m *MockBoltClient) Check() bool {    args := m.Mock.Called()    return args.Get(0).(bool)}

添加/health路由

這裡非常直接。 我們直接在service/routes.go裡邊添加下面的路由:

var routes = Routes{    Route{        "GetAccount",             // Name        "GET",                    // HTTP method        "/accounts/{accountId}",  // Route pattern        GetAccount,    },    Route{        "HealthCheck",        "GET",        "/health",        HealthCheck,    },}

/health請求讓HealthCheck來處理。下面是HealthCheck的內容:

func HealthCheck(w http.ResponseWriter, r *http.Request) {    // Since we're here, we already know that HTTP service is up. Let's just check the state of the boltdb connection    dbUp := DBClient.Check()    if dbUp {        data, _ := json.Marshal(healthCheckResponse{Status: "UP"})        writeJsonResponse(w, http.StatusOK, data)    } else {        data, _ := json.Marshal(healthCheckResponse{Status: "Database unaccessible"})        writeJsonResponse(w, http.StatusServiceUnavailable, data)    }}func writeJsonResponse(w http.ResponseWriter, status int, data []byte) {    w.Header().Set("Content-Type", "application/json")    w.Header().Set("Content-Length", strconv.Itoa(len(data)))    w.WriteHeader(status)    w.Write(data)}type healthCheckResponse struct {    Status string `json:"status"`}

HealthCheck函數代理檢查DB狀態, 即DBClient中添加的Check()方法。如果OK, 我們建立一個healthCheckResponse結構體。 注意首字母小寫,只在模組內可見的範圍。我們同時實現了一個寫HTTP響應的方法,這樣代碼看起來簡潔一些。

運行

運行修改後的代碼:

> go run *.goStarting accountserviceSeeded 100 fake accounts...2017/03/03 21:00:31 Starting HTTP service at 6767

然後開啟新的視窗,使用curl訪問/health介面:

> curl localhost:6767/health{"status":"UP"}

It works!

Docker的健全狀態檢查

接下來,我們將使用Docker的HEALTHCHECK機制讓Docker Swarm檢查我們的服務活躍性。 這是通過在Dockerfile檔案中添加一行來實現的:

FROM iron/baseEXPOSE 6767ADD accountservice-linux-amd64 /ADD healthchecker-linux-amd64 /HEALTHCHECK --interval=1s --timeout=3s CMD ["./healthchecker-linux-amd64", "-port=6767"] || exit 1ENTRYPOINT ["./accountservice-linux-amd64"]

healthchecker-linux-amd64是什麼東西?我們需要稍微協助一下Docker, 因為Docker自己沒有為我們提供HTTP用戶端或類似的東西來執行健全狀態檢查。 而是,Dockerfile中的HEALTHCHECK指令指定了一種命令(CMD), 它應該執行調用/health路由。 依賴運行程式的退出碼,Docker會確定服務是否健康。 如果太多後續健全狀態檢查失敗,Docker Swarm將殺死這個容器,並啟動一個新的容器。

最常見的實現真實健全狀態檢查的方式看起來類似curl。 然而,這需要我們的基礎docker映像來實際安裝有curl(或者任何底層依賴), 並且在這個時候我們不會真正希望處理它。取而代之的是讓Go語言來釀造我們自己的健全狀態檢查小程式。

建立健全狀態檢查程式

是時候在src/github.com/callistaenterprise/goblog下面建立一個新的子項目了。

mkdir healthchecker

然後在這個目錄下面建立一個main.go檔案, 其內容如下:

package mainimport (    "flag"    "net/http"    "os")func main() {    port := flag.String("port", "80", "port on localhost to check")    flag.Parse()    resp, err := http.Get("http://127.0.0.1:" + *port + "/health") // 注意使用 * 間接引用    // If there is an error or non-200 status, exit with 1 signaling unsuccessful check.    if err != nil || resp.StatusCode != 200 {        os.Exit(1)    }    os.Exit(0)}

代碼量不是很大,它做了些什嗎?

  • 使用flag包讀取-port命令列參數。如果沒有指定,回退使用預設值80。
  • 執行HTTP GET請求http://127.0.0.1:[port]/health。
  • 如果HTTP請求發生錯誤,狀態代碼為非200,以推出碼1退出。 否則以退出碼0退出。0 == success, 1 == fail.

讓我們試試看,如果我們已經把accountservice停掉了,那麼重新運行它,然後運行healthchecker。

go build./accountservice

然後運行這個程式:

> cd $GOPATH/src/github.com/callistaenterprise/goblog/healtchecker> go run *.goexit status 1

上面我們忘記指定連接埠號碼了,因此它使用的是預設80連接埠。讓我們再來一次:

> go run *.go -port=6767>

這裡沒有輸出,表示我們請求是成功的。 很好,那麼我們構建一個linux/amd64的二進位,然後將它添加到accountservice中,通過添加healthchecker二進位到Dockerfile檔案中。 我們繼續使用copyall.sh指令碼來自動完成重新構建和部署。

#!/bin/bashexport GOOS=linuxexport CGO_ENABLED=0cd accountservice;go get;go build -o accountservice-linux-amd64;echo built `pwd`;cd ..// NEW, builds the healthchecker binarycd healthchecker;go get;go build -o healthchecker-linux-amd64;echo built `pwd`;cd ..export GOOS=darwin   // NEW, copies the healthchecker binary into the accountservice/ foldercp healthchecker/healthchecker-linux-amd64 accountservice/docker build -t someprefix/accountservice accountservice/

最後我們還需要做一件事,就是更新accountservice的Dockerfile。它完整內容如下:

FROM iron/baseEXPOSE 6767ADD accountservice-linux-amd64 /# NEW!! ADD healthchecker-linux-amd64 /HEALTHCHECK --interval=3s --timeout=3s CMD ["./healthchecker-linux-amd64", "-port=6767"] || exit 1ENTRYPOINT ["./accountservice-linux-amd64"]

我們附加了如下內容:

  • 添加ADD指令,確保healthchecker二進位包含到鏡像中。
  • HEALTHCHECK語句指定我們的二進位檔案以及參數,告訴Docker每隔3秒去執行一次健全狀態檢查, 並接受3秒的逾時。

部署健全狀態檢查服務

現在我們準備部署我們更新後的帶healthchecker的accountservice服務了。如果要更加自動,將這兩行添加到copyall.sh檔案中,每次啟動並執行時候,它會從Docker Swarm中自動刪除accountservice並且重新建立它。

docker service rm accountservicedocker service create --name=accountservice --replicas=1 --network=my_network -p=6767:6767 someprefix/accountservice

那麼現在運行./copyall.sh, 等幾秒鐘,所有構建更新好。然後我們再使用docker ps檢查容器狀態, 就可以列舉出所有啟動並執行容器。

> docker psCONTAINER ID        IMAGE                             COMMAND                 CREATED        STATUS1d9ec8122961        someprefix/accountservice:latest  "./accountservice-lin"  8 seconds ago  Up 6 seconds (healthy)107dc2f5e3fc        manomarks/visualizer              "npm start"             7 days ago     Up 7 days

我們尋找STATUS頭下面的"(healthy)"文本。服務沒有配置healthcheck的完全沒有health指示。

故意造成失敗

要讓事情稍微更加有意思, 我們添加一個可測試API, 可以允許我們讓端點扮演不健康的目的。在routes.go檔案中,聲明另外一個路由。

var routes = Routes{    Route{        "GetAccount", // Name        "GET",        // HTTP method        "/accounts/{accountId}", // Route pattern        /*func(w http.ResponseWriter, r *http.Request) {            w.Header().Set("Content-Type", "application/json; charset=UTF-8")            w.Write([]byte("{\"result\":\"OK\"}"))        },*/        GetAccount,    },    Route{        "HealthCheck",        "GET",        "/health",        HealthCheck,    },    Route{        "Testability",        "GET",        "/testability/healthy/{state}",        SetHealthyState,    },}

這個路由(在生產服務中不要有這樣的路由!)提供了一種REST-ish路由的故障健全狀態檢查目的。SetHealthyState函數在goblog/accountservice/handlers.go檔案中,代碼如下:

var isHealthy = true // NEWfunc SetHealthyState(w http.ResponseWriter, r *http.Request) {    // Read the 'state' path parameter from the mux map and convert to a bool    var state, err = strconv.ParseBool(mux.Vars(r)["state"])        // If we couldn't parse the state param, return a HTTP 400    if err != nil {        fmt.Println("Invalid request to SetHealthyState, allowed values are true or false")        w.WriteHeader(http.StatusBadRequest)        return    }        // Otherwise, mutate the package scoped "isHealthy" variable.    isHealthy = state    w.WriteHeader(http.StatusOK)}

最後,將isHealthy布爾作為HealthCheck函數的檢查條件:

func HealthCheck(w http.ResponseWriter, r *http.Request) {        // Since we're here, we already know that HTTP service is up. Let's just check the state of the boltdb connection        dbUp := DBClient.Check()                if dbUp && isHealthy {              // NEW condition here!                data, _ := json.Marshal(                ...        ...        }

重啟accountservice.

> cd $GOPATH/src/github.com/callistaenterprise/goblog/accountservice> go run *.goStarting accountserviceSeeded 100 fake accounts...2017/03/03 21:19:24 Starting HTTP service at 6767

然後在新視窗產生一個新的healthcheck調用。

> cd $GOPATH/src/github.com/callistaenterprise/goblog/healthchecker> go run *.go -port=6767

第一次嘗試成功,然後我們通過使用下面的curl請求testability來改變accountservice的狀態。

> curl localhost:6767/testability/healthy/false> go run *.go -port=6767exit status 1

起作用了!然後我們在Docker Swarm中運行它。使用copyall.sh重建並重新部署accountservice。

> cd $GOPATH/src/github.com/callistaenterprise/goblog> ./copyall.sh

嚮往常一樣,等待Docker Swarm重新部署"accountservice", 使用最新構建的"accountservice"容器映像。然後,運行docker ps來看是否啟動並運行了帶有健康的服務。

> docker psCONTAINER ID    IMAGE                            COMMAND                CREATED         STATUS 8640f41f9939    someprefix/accountservice:latest "./accountservice-lin" 19 seconds ago  Up 18 seconds (healthy)

注意CONTAINER ID和CREATED欄位。可以在你的Docker Swarm上調用testability API。(我的IP是: 192.168.99.100)。

> curl $ManagerIP:6767/testability/healthy/false>

然後,我們在幾秒時間內再次運行docker ps命令.

> docker psCONTAINER ID        IMAGE                            COMMAND                CREATED         STATUS                                                             NAMES0a6dc695fc2d        someprefix/accountservice:latest "./accountservice-lin" 3 seconds ago  Up 2 seconds (healthy)

你可以看到,這裡有了全新的CONTAINER ID和CREATED和STATUS. 真正發生的是Docker Swarm監測到三個(重試的預設值)連續失敗的健全狀態檢查, 並立即確定服務變得不健康, 需要用新的執行個體來代替, 這完全是在沒有任何管理員幹預的情況下發生的。

總結

在這一部分中,我們使用一個簡單的/health路由和healthchecker程式結合Docker的HEALTHCHECK機制,展示了這個機制如何讓Docker Swarm自動為我們處理不健康的服務。

下一章,我們深入到Docker Swarm的機制, 我們聚焦兩個關鍵領域 - 服務發現和負載平衡。

參考連結

  • 英文第6部分
  • 健康端點檢查
  • 專題首頁
  • 下一節
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.