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等知识的时间较短,如果有理解错误的地方,欢迎批评指正,可以加我微信一起探讨学习
有疑问加站长微信联系(非本文作者)