牌類遊戲使用微服務重構筆記(十二): 在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類似,再次不做贅述,根據自己的需求進行選擇即可
-
服務發現
既然是微服務框架,那麼服務發現肯定是比較重要的環節
- 使用k8s自帶的服務發現
之前的部落格中有對k8s service做過簡單介紹 kubernetes學習筆記(二):k8s初體驗,也是首推的服務發現方式。
服務的訪問方式為k8s service => micro service(k8s pod),這樣還可以利用到k8s service自帶的健康檢查。
不過這種部署方式在筆者當時有名稱空間問題還未解決,再加上部署較為複雜,因此我還沒有在生產環境中使用,待專案後續更改為k8s service後再來完善這裡
- 使用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 }}
複製程式碼
- 不難看出,其實就是釋出了一組pod來啟動 micro api,即api閘道器
- 出現了許多的 {{}},這是helm的插值語法,裡面的數值都取自於 values.yaml,因此要把template 儘可能的做到配置化
- 還出現了一個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等知識的時間較短,如果有理解錯誤的地方,歡迎批評指正,可以加我微信一起探討學習