適用於kubernetes的應用開發部署流程同時集成Istio service mesh——如何開發kubernetes native應用

本文講解了如何開發容器化應用,並使用Wercker持續集成工具構建docker鏡像上傳到docker鏡像倉庫中,然後在本地使用docker-compose測試後,再使用kompose自動生成kubernetes的yaml文件,再將注入Envoy sidecar容器,集成Istio service mesh中的詳細過程。

當我們有了一個kubernetes集群後,如何在上面開發和部署應用,應該遵循怎樣的流程?本次分享將向您展示如何使用go語言開發和部署一個kubernetes native應用,使用wercker進行持續集成與持續發布,我將以一個很簡單的前後端訪問,獲取偽造數據並展示的例子來說明。

:本文部分內容曾是我2017年9月14日在DockOne社區分享的內容,本文同時歸檔到kubernetes-handbook中。

整個過程如下圖所示。

主要內容

  • 服務API的定義
  • 使用Go語言開發kubernetes原生應用
  • 使用wercker做持續構建與發布
  • 使用traefik和VIP做邊緣節點提供外部訪問路由
  • 集成Istio Service Mesh

環境聲明

首先聲明下我們使用的集群環境:

  • Docker1.12.5
  • flannel network host-gw
  • kubernetes 1.6.0+
  • TLS enabled

詳細的部署文檔和更多資料請參考 kubernetes-handbook

應用示例

我們的這兩個示例僅僅是為了演示,開發部署一個偽造的 metric 並顯示在 web 頁面上,包括兩個service:

  • k8s-app-monitor-test:生成模擬的監控數據,發送http請求,獲取json返回值
  • K8s-app-monitor-agent:獲取監控數據並繪圖,訪問瀏覽器獲取圖表

這兩個鏡像可以直接從docker hub上下載

  • jimmysong/k8s-app-monitor-test:latest
  • jimmysong/k8s-app-monitor-agent:latest

定義API

API文檔 中的api.html文件,該文檔在API blueprint中定義,使用aglio 生成,打開後如圖所示:

關於服務發現

K8s-app-monitor-agent服務需要訪問k8s-app-monitor-test服務,這就涉及到服務發現的問題,我們在代碼中直接寫死了要訪問的服務的內網DNS地址(kubedns中的地址,即k8s-app-monitor-test.default.svc.cluster.local)。

我們知道Kubernetes在啟動Pod的時候為容器注入環境變數,這些環境變數在所有的 namespace 中共享(環境變數是不斷追加的,新啟動的Pod中將擁有老的Pod中所有的環境變數,而老的Pod中的環境變數不變)。但是既然使用這些環境變數就已經可以訪問到對應的service,那麼獲取應用的地址信息,究竟是使用變數呢?還是直接使用DNS解析來發現?

答案是使用DNS,詳細說明見Kubernetes中的服務發現與Docker容器間的環境變數傳遞源碼探究

持續集成

開源項目的構建離不開CI工具,你可能經常會在很多GitHub的開源項目首頁上看到這樣的東西:

這些圖標都是CI工具提供的,可以直觀的看到當前的構建狀態,例如wercker中可以在Application-magpie-options中看到:

將文本框中的代碼複製到你的項目的README文件中,就可以在項目主頁上看到這樣的標誌了。

現在市面上有很多流行的CI/CD工具和DevOps工具有很多,這些工具提高了軟體開發的效率,增加了開發人員的幸福感。這些工具有:

適用於GitHub上的開源項目,可以直接使用GitHub賬戶登陸,對於公開項目可以直接使用:Travis-ci、CircleCI、Wercker。從目前GitHub上開源項目的使用情況來看,Travis-ci的使用率更高一些。

適用於企業級的:Jenkins

不僅包括CI/CD功能的DevOps平台:JFrog、Spinnaker、Fabric8

Wercker簡介

Wercker是一家為現代雲服務提供容器化應用及微服務的快速開發、部署工具的初創企業,成立於2012年,總部位於荷蘭阿姆斯特丹。其以容器為中心的平台可以對微服務和應用的開發進行自動化。開發者通過利用其命令行工具能夠生成容器到桌面,然後自動生成應用並部署到各種雲平台上面。其支持的平台包括Heroku、AWS以及Rackspace等。

Wercker於2016年獲得450萬美元A輪融資,此輪融資由Inkef Capital領投,Notion Capital跟投,融資所得將用於商業版產品的開發。此輪融資過後其總融資額為750萬美元。

Wercker於2017年4月被Oracle甲骨文於收購。

如何使用

通過Wercker搭建CI環境只需經過三個基本步驟。

1.在Wercker網站中創建一個應用程序。

2.將wercker.yml添加到應用程序的代碼庫中。

3.選擇打包和部署構建的位置。

可以使用GitHub帳號直接登錄Wercker,整個創建應用CI的流程一共3步。

一旦擁有了賬戶,那麼只需簡單地點擊位於頂部的應用程序菜單,然後選擇創建選項即可。如果系統提示是否要創建組織或應用程序,請選擇應用程序。Wercker組織允許多個Wercker用戶之間進行協作,而無須提供信用卡。下圖為設置新應用程序的嚮導頁面。

選擇了GitHub中的repo之後,第二步配置訪問許可權,最後一步Wercker會嘗試生成一個wercker.yml文件(後面會討論)。不過至少對於Go應用程序來說,這個配置很少會滿足要求,所以我們總是需要創建自己的Wercker配置文件。

創建Wercker配置文件Wercker.yaml

Wercker配置文件是一個YAML文件,該文件必須在GitHub repo的最頂層目錄,該文件主要包含三個部分,對應可用的三個主要管道。

Dev:定義了開發管道的步驟列表。與所有管道一樣,可以選定一個box用於構建,也可以全局指定一個box應用於所有管道。box可以是Wercker內置的預製Docker鏡像之一,也可以是Docker Hub託管的任何Docker鏡像。

Build:定義了在Wercker構建期間要執行的步驟和腳本的列表。與許多其他服務(如Jenkins和TeamCity)不同,構建步驟位於代碼庫的配置文件中,而不是隱藏在服務配置里。

Deploy:在這裡可以定義構建的部署方式和位置。

Wercker中還有工作流的概念,通過使用分支、條件構建、多個部署目標和其他高級功能擴展了管道的功能,這些高級功能讀著可以自己在wercker的網站中探索。

因為我使用wercker自動構建,構建完成後自動打包成docker鏡像並上傳到docker hub中(需要先在docker hub中創建repo),如何使用 wercker 做持續構建與發布,並集成docker hub插件請參考:wercker構建

K8s-app-monitor-agent的wercker配置文件如下:

box: golangbuild: steps: - setup-go-workspace - script: name: go get code: | cd $WERCKER_SOURCE_DIR go version go get -u github.com/Masterminds/glide export PATH=$WERCKER_SOURCE_DIR/bin:$PATH glide install # Build the project - script: name: go build code: | go build - script: name: copy files to wercker output code: | cp -R ./ ${WERCKER_OUTPUT_DIR}deploy: steps: - internal/docker-push: username: $USERNAME password: $PASSWORD cmd: /pipeline/source/k8s-app-monitor-agent port: "3000" tag: latest repository: jimmysong/k8s-app-monitor-agent

其中的$USERNAME$PASSWORD是docker hub的用戶名和密碼,這些是作為wercker構建時候的環境變數,在wercker的web端進行配置的。

此文件包含兩個管道:build和deploy。在開發流程中,我們使用Wercker和Docker創建一個乾淨的Docker鏡像,然後將它push到Docker Hub中。Wercker包含一個叫做Internal/docker-push的deploy plugin,可以將構建好的docker鏡像push到鏡像倉庫中,默認是Docker Hub,也可以配置成私有鏡像倉庫。

box鍵的值是golang。這意味著我們使用的是一個基礎的Docker鏡像,它已經安裝了Go環境。這一點至關重要,因為執行Wercker構建的基準Docker鏡像需要包含應用程序所需的構建工具。

查看詳細構建流程

當然你還可以使用其他的CI工具,因為wercker的插件比較方便,可以直接構建成docker鏡像上傳到docker hub中,比較方便,所以我選擇了wercker,作為個人項目和開源項目的話可以選擇它,企業內部建議選擇Jenkins。

生成了如下兩個docker鏡像:

  • jimmysong/k8s-app-monitor-test:latest
  • jimmysong/k8s-app-monitor-agent:latest

本地測試

在將服務發布到線上之前,我們可以先使用docker-compose在本地測試一下,這兩個應用的docker-compose.yaml文件如下:

version: 2services: k8s-app-monitor-agent: image: jimmysong/k8s-app-monitor-agent:234d51c container_name: monitor-agent depends_on: - k8s-app-monitor-test ports: - 8888:8888 environment: - SERVICE_NAME=k8s-app-monitor-test k8s-app-monitor-test: image: jimmysong/k8s-app-monitor-test:9c935dd container_name: monitor-test ports: - 3000:3000

執行下面的命令運行測試。

docker-compose up

在瀏覽器中訪問localhost:8888/k8s-app-就可以看到監控頁面。

啟動服務

所有的kubernetes應用啟動所用的yaml配置文件都保存在那兩個GitHub倉庫的manifest.yaml文件中。

比如k8s-app-monitor-agentmanifest.yaml文件如下:

apiVersion: extensions/v1beta1kind: Deploymentmetadata: name: k8s-app-monitor-agent namespace: defaultspec: replicas: 1 template: metadata: labels: k8s-app: k8s-app-monitor-agent spec: containers: - image: jimmysong/k8s-app-monitor-agent:latest imagePullPolicy: Always name: app ports: - containerPort: 8080 env: - name: APP_PORT value: "3000" - name: SERVICE_NAME value: "k8s-app-monitor-test"---apiVersion: v1kind: Servicemetadata: name: k8s-app-monitor-agent labels: k8s-svc: k8s-app-monitor-agentspec: ports: - port: 8080 protocol: TCP name: http selector: k8s-app: k8s-app-monitor-agent

注意其中的env,包括了兩個環境變數(注意:環境變數名稱必須為大寫字母):APP_PORTSERVICE_NAME,這兩個環境變數,在 main.go的代碼中我們可以看到:

func drawChart(res http.ResponseWriter, req *http.Request) { port := os.Getenv("APP_PORT") service := os.Getenv("SERVICE_NAME") if len(port) == 0 { port = "3000" } if len(service) == 0 { service = "localhost" }

如果程序啟動的時候找不到該環境變數,將會使用程序內置的默認值,當然我們不該將服務地址寫死在程序內部,而應該是可配置的,在kubernetes中最佳配置方式是環境變數或者ConfigMap。

分別在兩個GitHub目錄下執行kubectl create -f manifest.yaml即可啟動服務。

邊緣節點配置

邊緣節點架構圖

選擇Kubernetes的三個node作為邊緣節點,並安裝keepalived,上圖展示了邊緣節點的配置,同時展示了向Kubernetes中添加服務的過程。

邊緣節點定義

首先解釋下什麼叫邊緣節點(Edge Node),所謂的邊緣節點即集群內部用來向集群外暴露服務能力的節點,集群外部的服務通過該節點來調用集群內部的服務,邊緣節點是集群內外交流的一個Endpoint。

邊緣節點要考慮兩個問題

  • 邊緣節點的高可用,不能有單點故障,否則整個kubernetes集群的外部訪問將不可用
  • 對外的一致暴露埠,即只能有一個外網訪問IP和埠

為了滿足邊緣節點的以上需求,我們使用keepalived來實現。

在Kubernetes中添加了service的同時,在DNS中增加一個記錄,這條記錄需要跟ingress中的host欄位相同,IP地址即VIP的地址,本示例中是172.20.0.119,這樣集群外部就可以通過service的DNS名稱來訪問服務了。

參考詳細操作步驟和配置

發布

所有的kubernetes應用啟動所用的yaml配置文件都保存在那兩個GitHub倉庫的manifest.yaml文件中。也可以使用kompose這個工具,可以將*docker-compose*的YAML文件轉換成kubernetes規格的YAML文件。

分別在兩個GitHub目錄下執行kubectl create -f manifest.yaml即可啟動服務。也可以直接在*k8s-app-monitor-agent*代碼庫的k8s目錄下執行kubectl apply -f kompose

在以上YAML文件中有包含了Ingress配置,是為了將*k8s-app-monitor-agent*服務暴露給集群外部訪問。

方式一

服務啟動後需要更新ingress配置,在ingress.yaml文件中增加以下幾行:

- host: k8s-app-monitor-agent.jimmysong.io http: paths: - path: /k8s-app-monitor-agent backend: serviceName: k8s-app-monitor-agent servicePort: 8888

保存後,然後執行kubectl replace -f ingress.yaml即可刷新ingress。

修改本機的/etc/hosts文件,在其中加入以下一行:

172.20.0.119 k8s-app-monitor-agent.jimmysong.io

當然你也可以將該域名加入到內網的DNS中,為了簡單起見我使用hosts。

方式二

或者不修改已有的Ingress,而是為該隊外暴露的服務單獨創建一個Ingress,如下:

apiVersion: extensions/v1beta1kind: Ingressmetadata: name: k8s-app-monitor-agent-ingress annotations: kubernetes.io/ingress.class: "treafik"spec: rules: - host: k8s-app-monitor-agent.jimmysong.io http: paths: - path: / backend: serviceName: k8s-app-monitor-agent servicePort: 8888

詳見邊緣節點配置。

集成Istio service mesh

上一步中我們生成了kubernetes可讀取的應用的YAML配置文件,我們可以將所有的YAML配置和併到同一個YAML文件中假如文件名為k8s-app-monitor-istio-all-in-one.yaml,如果要將其集成到Istio service mesh,只需要執行下面的命令。

kubectl apply -n default -f <(istioctl kube-inject -f k8s-app-monitor-istio-all-in-one.yaml)

這樣就會在每個Pod中注入一個sidecar容器。

驗證

如果您使用的是Traefik ingress來暴露的服務,那麼在瀏覽器中訪問k8s-app-monitor-agent.jimmysong.io,可以看到如下的畫面,每次刷新頁面將看到新的柱狀圖。

使用kubernetes-vagrant-centos-cluster來部署的kubernetes集群,該應用集成了Istio service mesh後可以通過172.17.8.101:32000/k8s-來訪問。

在對k8s-app-monitor-agent服務進行了N此訪問之後,再訪問http://grafana.istio.jimmysong.io可以看到Service Mesh的監控信息。

訪問servicegraph.istio.jimmysong.io可以看到服務的依賴和QPS信息。

訪問http://zipkin.istio.jimmysong.io可以選擇查看k8s-app-monitor-agent應用的追蹤信息。

至此從代碼提交到上線到Kubernetes集群上並集成Istio service mesh的過程就全部完成了。


推薦閱讀:

TAG:cloudnative | Kubernetes | DevOps |