牌类游戏使用微服务重构笔记(十二): 在k8s中部署

段鹏举 · · 1079 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

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等知识的时间较短,如果有理解错误的地方,欢迎批评指正,可以加我微信一起探讨学习


有疑问加站长微信联系(非本文作者)

本文来自:掘金

感谢作者:段鹏举

查看原文:牌类游戏使用微服务重构笔记(十二): 在k8s中部署

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

1079 次点击  
加入收藏 微博
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传