micro在k8s中部署是相当容易的,本文以项目一、项目二、公共项目为例详解部署方式
helm是k8s的软件包管理工具,简单阅读几分钟便可上手使用,具体参见 文档链接
键入 micro --help
你会发现有许多的配置项,这些配置项在开发阶段和上线环境中可能会有不同,例如开发阶段可能没有指定 --registry
, 那么micro默认使用了mdns进行服务发现,但是线上环境中一般不太会使用这个
好在micro在设计之初就考虑到了这些需求,所以这些配置项都是以插件方式提供的,例如etcd、etcdv3、kubernetes、nats、zookeeper、consul这些都可以轻易的接入到micro,并且使用一行代码进行切换 --registry=consul
micro中的broker(pub/sub)、transport拓展方式与registry类似,再次不做赘述,根据自己的需求进行选择即可
既然是微服务框架,那么服务发现肯定是比较重要的环节
之前的博客中有对k8s service做过简单介绍 kubernetes学习笔记(二):k8s初体验 ,也是首推的服务发现方式。
不过这种部署方式在笔者当时有命名空间问题还未解决,再加上部署较为复杂,因此我还没有在生产环境中使用,待项目后续更改为k8s service后再来完善这里
我的项目中使用的是consul服务发现,比较简单。使用 consul-helm软件包 ,即可在k8s中创建consul集群。一个命名空间创建一个consul集群即可,多个项目可以共用。
helm install --name=ack-consul-prod --namespace=yourNamespace ./
helm create yourChartName
创建一个空的软件包,包含以下文件列表
foo/ | |- .helmignore # 类似于.gitignore排除一些文件使用 | |- Chart.yaml # 软件包配置信息 | |- values.yaml # 软件包的各种数值 | |- charts/ # 依赖 | |- templates/ # 软件包使用的模板文件,+ values.yaml里的数值,组成k8s的yaml文件提供给 kubectl 复制代码
我们主要需要编辑的就是 templates和values.yaml,可以先把templates文件夹清空,values.yaml数值都删掉。
如果你的项目有对外提供的接口,那么就需要一个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 }} 复制代码
此时已经可以发布一下试试看了,与发布consul集群类似
执行 helm install --name=yourProjectName --namespace=yourNamespace ./
观察下k8s集群的变化情况,添加k8s ingress,访问一下试试看
正如前文所说,如果不使用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 history yourProjectName
可查看。万一更新出错了,别慌。执行 helm rollback yourProjectName 1
,便可回滚上一个第1次发布的状态 # 多版本配置 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等知识的时间较短,如果有理解错误的地方,欢迎批评指正,可以加我微信一起探讨学习