1. 程式人生 > 程式設計 >牌類遊戲使用微服務重構筆記(十二): 在k8s中部署

牌類遊戲使用微服務重構筆記(十二): 在k8s中部署

micro在k8s中部署是相當容易的,本文以專案一、專案二、公共專案為例詳解部署方式

helm

helm是k8s的軟體包管理工具,簡單閱讀幾分鐘便可上手使用,具體參見檔案連結

配置

鍵入micro --help你會發現有許多的配置項,這些配置項在開發階段和上線環境中可能會有不同,例如開發階段可能沒有指定--registry, 那麼micro預設使用了mdns進行服務發現,但是線上環境中一般不太會使用這個

好在micro在設計之初就考慮到了這些需求,所以這些配置項都是以外掛方式提供的,例如etcd、etcdv3、kubernetes、nats、zookeeper、consul這些都可以輕易的接入到micro,並且使用一行程式碼進行切換 --registry=consul

micro中的broker(pub/sub)、transport拓展方式與registry類似,再次不做贅述,根據自己的需求進行選擇即可

  • 服務發現

既然是微服務框架,那麼服務發現肯定是比較重要的環節

  1. 使用k8s自帶的服務發現

之前的部落格中有對k8s service做過簡單介紹 kubernetes學習筆記(二):k8s初體驗,也是首推的服務發現方式。

服務的訪問方式為k8s service => micro service(k8s pod),這樣還可以利用到k8s service自帶的健康檢查。

不過這種部署方式在筆者當時有名稱空間問題還未解決,再加上部署較為複雜,因此我還沒有在生產環境中使用,待專案後續更改為k8s service後再來完善這裡

  1. 使用consul

我的專案中使用的是consul服務發現,比較簡單。使用consul-helm軟體包,即可在k8s中建立consul叢集。一個名稱空間建立一個consul叢集即可,多個專案可以共用。

使用consul服務發現,訪問方式為 api閘道器(k8s pod) => api(k8s pod) => srv(k8s pod),相當於pod之間的訪問,沒有經過k8s service 自然也無法使用健康檢查

helm install --name=ack-consul-prod --namespace=yourNamespace ./

  • 建立helm軟體包

helm create yourChartName

建立一個空的軟體包,包含以下檔案列表

foo/
	  |
	  |- .helmignore   # 類似於.gitignore排除一些檔案使用
	  |
	  |- Chart.yaml    # 軟體包配置資訊
	  |
	  |- values.yaml   # 軟體包的各種數值
	  |
	  |- charts/       # 依賴
	  |
	  |- templates/    # 軟體包使用的模板檔案,+ values.yaml裡的數值,組成k8s的yaml檔案提供給 kubectl

複製程式碼

我們主要需要編輯的就是 templates和values.yaml,可以先把templates資料夾清空,values.yaml數值都刪掉。

  • 部署api閘道器

如果你的專案有對外提供的介面,那麼就需要一個api 閘道器。在templates資料夾中新增api.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: micro-api
spec:
  replicas: {{ .Values.apiReplicaCount }}
  selector:
    matchLabels:
      app: micro-api-deployment
      env: {{ .Values.env }}
  template:
    metadata:
      labels:
        app: micro-api-deployment
        env: {{ .Values.env }}
    spec:
      imagePullSecrets:
        - name: {{ $.Values.imagePullSecretsName }}
      containers:
        - name: api
          args:
            - bin/micro
            - --registry={{ .Values.registry }}
            - --registry_address={{ .Values.registryAddress }}
            - --selector=cache
            - api
            - --namespace={{ .Values.microNameSpace }}.api
            - --handler={{ .Values.microHandler }}
            - --cors-allowed-headers={{ .Values.corsAllowedHeaders }}
            - --cors-allowed-origins={{ .Values.corsAllowedOrigins }}
            - --cors-allowed-methods={{ .Values.corsAllowedMethods }}
          image: {{ .Values.apiImage }}:{{ .Values.apiImagesVersion }}
          imagePullPolicy: Always
          ports:
            - containerPort: 8080
              name: api-port
---
apiVersion: v1
kind: Service
metadata:
  name: micro-api
  labels:
    name: micro-api-svc
spec:
  type: ClusterIP
  ports:
    - name: http
      port: 80
      targetPort: 8080
  selector:
    app: micro-api-deployment
    env: {{ .Values.env }}
複製程式碼
  1. 不難看出,其實就是釋出了一組pod來啟動 micro api,即api閘道器
  2. 出現了許多的 {{}},這是helm的插值語法,裡面的數值都取自於 values.yaml,因此要把template 儘可能的做到配置化
  3. 還出現了一個service,這是因為叢集外部訪問叢集,需要使用ingress路由,路由又訪問服務,服務轉發到pod(http 請求 => k8s ingress => k8s service => k8s pods),這裡的pod 就是我們部署的micro api,接下來才是micro內部的處理

此時已經可以釋出一下試試看了,與釋出consul叢集類似

執行helm install --name=yourProjectName --namespace=yourNamespace ./ 觀察下k8s叢集的變化情況,新增k8s ingress,訪問一下試試看

  • 部署api和srv

正如前文所說,如果不使用k8s service作為服務發現,那麼micro內部的東西對於k8s來說 就是一堆pod而已。所以無論是api層還是srv層,部署只需要pod即可

這裡以一個賬戶中心的api和srv舉例

{{- range .Values.versions }}
apiVersion: apps/v1
kind: Deployment
metadata:
  name: account-api-{{ .version | replace "." "" }}
  labels:
    name: {{ .name }}
    version: {{ .version }}
spec:
  replicas: {{ $.Values.accountApiReplicaCount }}
  selector:
    matchLabels:
      app: account-api-deployment
      env: {{ $.Values.env }}
      version: {{ .version }}
  template:
    metadata:
      labels:
        app: account-api-deployment
        env: {{ $.Values.env }}
        version: {{ .version }}
    spec:
      volumes:
        - name: host-time
          hostPath:
            path: /etc/localtime
      imagePullSecrets:
        - name: {{ $.Values.imagePullSecretsName }}
      containers:
        - name: account
          args:
            - bin/ccgame
            - --registry={{ $.Values.registry }}
            - --registry_address={{ $.Values.registryAddress }}
            - --selector=cache
            - --server_address=0.0.0.0:8080
            - start
            - --name=account
            - --resource=api
          image: {{ .image }}:{{ .version }}
          volumeMounts:
              - name: host-time
                mountPath: /etc/localtime
          imagePullPolicy: Always
          env:
            - name: ENV
              valueFrom:
                fieldRef:
                  fieldPath: metadata.labels['env']
            - name: VERSION
              valueFrom:
                fieldRef:
                  fieldPath: metadata.labels['version']
          ports:
            - containerPort: 8080
              name: account-port
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: account-srv-{{ .version | replace "." "" }}
  labels:
    name: {{ .name }}
    version: {{ .version }}
spec:
  replicas: {{ $.Values.accountSrvReplicaCount }}
  selector:
    matchLabels:
      app: account-srv-deployment
      env: {{ $.Values.env }}
      version: {{ .version }}
  template:
    metadata:
      labels:
        app: account-srv-deployment
        env: {{ $.Values.env }}
        version: {{ .version }}
    spec:
      volumes:
        - name: host-time
          hostPath:
            path: /etc/localtime
      imagePullSecrets:
        - name: {{ $.Values.imagePullSecretsName }}
      containers:
        - name: account
          args:
            - bin/ccgame
            - --registry={{ $.Values.registry }}
            - --registry_address={{ $.Values.registryAddress }}
            - --selector=cache
            - --server_address=0.0.0.0:8080
            - start
            - --name=account
            - --resource=srv
          image: {{ .image }}:{{ .version }}
          volumeMounts:
              - name: host-time
                mountPath: /etc/localtime
          imagePullPolicy: Always
          env:
            - name: ENV
              valueFrom:
                fieldRef:
                  fieldPath: metadata.labels['env']
            - name: VERSION
              valueFrom:
                fieldRef:
                  fieldPath: metadata.labels['version']
          ports:
            - containerPort: 8080
              name: account-port
---
{{ end }}
複製程式碼

添加了新的模板,再次釋出時就屬於更新了,執行helm upgrade yourProjectName ./更新這個軟體包,再次觀察叢集的變化情況。

會發現,之前的api.yaml建立的api部署沒有發生變化,叢集新建立了 account-api、account-srv兩個部署,以及一些pod。

helm的更新其實就是提交給kubectl去執行yaml檔案,因此每次更新時叢集將會發生什麼變化,去思考kubectl執行這些檔案會發生什麼變化即可,這點很重要,每次釋出前思考一下,避免出現問題

每次helm更新都會形成一個版本,執行helm history yourProjectName可檢視。萬一更新出錯了,別慌。執行helm rollback yourProjectName 1,便可回滾上一個第1次釋出的狀態

values.yaml描述的是helm釋出後的最終狀態,例如如我想同時保留兩套api和srv

# 多版本配置
versions:
  - name: stable # 目前正在使用的版本
    image: yourDockerImageAddress # 映象
    version: 1.0.0
  - name: next # 目前正在使用的版本
    image: yourDockerImageAddress # 映象
    version: 1.0.1
複製程式碼

更新時,如果有這樣的helm語句 {{- range .Values.versions }},就相當於遍歷了一下,形成了兩個yaml提交給kubectl,自然會形成兩套api和srv。具體可參照helm檔案。

同理,如果我想下掉某一個版本,只需更改

# 多版本配置
versions:
  - name: stable # 目前正在使用的版本
    image: yourDockerImageAddress # 映象
    version: 1.0.1
複製程式碼
  • 多專案部署

有時候,免不了會出現一些公用的服務在不同的專案中。對於我這種有強迫症的選手來說,再發布一遍肯定接受不了。那麼就可以把公眾的服務,抽離出來,作為單獨的軟體包來發布。

例如,專案1(game1)和專案2(game2)都有賬戶相關的服務,那麼我就可以把賬戶中心抽出來當做一個單獨的專案(account)。

按照以上步驟,做成3個helm軟體包,game1、game2、account,使用同一個服務發現叢集。釋出好時候,觀察micro web面板,你會發現3個專案的一堆服務都在裡面。更新其中一個專案不會影響到另外的,達到了我們的目標。

最後就是game1、game2、account,3個專案之間如何互相訪問的問題。其實這根本不是一個問題,牌類遊戲使用微服務重構筆記(四): micro框架使用經驗中說過,micro是按照[名稱空間].[資源型別].[服務名]定義服務的,那麼提供完整的服務名字,就可建立這個服務的客戶端。

例如:

game1專案使用game1作為micro名稱空間,有game1.api.user、game1.srv.user

game2專案使用game2作為micro名稱空間,有game1.api.user、game1.srv.user

account專案使用account作為micro名稱空間,有account.srv.account

game1需要訪問account專案,只需使用account.srv.account和對應的proto建立客戶端;需要訪問game2專案,只需使用game2.srv.user和對應的proto建立客戶端。即使他們之間不在同一個服務發現裡,也沒有關係,建立客戶端時增加服務發現的選項即可。

這樣的部署方式在多個專案有關聯時,非常的方便,補一下結構圖

本人學習golang、micro、k8s、grpc、protobuf等知識的時間較短,如果有理解錯誤的地方,歡迎批評指正,可以加我微信一起探討學習