SpringCloud微服务迁移至Kubernetes实践

SpringCloud微服务迁移至Kubernetes实践

前言

原SpringCloud基础上的微服务已稳定运行近1年,遗留了一些问题不太好处理。原SpringCloud的整理文章见基于SpringCloud微服务的服务平台搭建的一些总结
问题以下:java

  • 客户端侧负载均衡在服务实例故障下线时候,不能及时发现,致使请求到故障实例地址形成请求错误,若增长请求重试配置,对于非幂等接口处理困难。
  • 基于SpringCloud Config的配置中心有时候会有不及时刷新svn上的配置信息的状况(须要从新config),没找到这个问题的缘由。另外多环境(生产、测试环境独立的配置)、多级配置(本地、配置中心)的组合设置对于组内其余开发者而言,学习成本较大,常常出现用错配置的状况。
  • 若要使用微服务,则必须使用SpringCloud技术栈开发,对开发人员的技术选型是一种没必要要的约束,另外大量的SpringCloud应用对与机器内存资源的占用比较严重(一个应用动辄上百兆内存),拆分红大量微服务后对于机器内存的浪费尤其严重。
  • 虽然SpringCloud有Eureka,可是因为各个服务不是在全部物理机器上都起着副本,当服务器故障须要恢复时,仍然须要留意当时机器上运行程序的状况,经常出现机器上残留的旧版本应用被其余人启动起来的状况。没法作到不关心实例的具体部署。
  • 服务的升级较为麻烦,在缺少有效的自动化部署程序,缺少有效滚动升级方案,以达到不中断服务的同时逐个替换现有服务实例。

改造方式

部署Kubernetes集群

网上教程挺多的,这里就不详细写了,用的1.11。可是遇到过问题。咱们集群用的3.10的kernel,在1.11的kubernetes中网络默认用的ipvs,常常致使kernel panic,后来从新部署时候把ipvs去掉,换回原来的iptables就行了。nginx

SpringCloud应用改造

首先要去除对于eureka的maven依赖和config的依赖,并去除@EnableEurekaClient的注解便可。web

其次服务调用方面因为没有Eureka了,FeignClient也须要进行一些变更,可是又要尽可能简化开发和修改步骤,若是还得由开发人员记住每一个服务的IP和端口那就有点退化的厉害了。咱们作以下设置。redis

1) 应用在Kubernetes内的封装
关于端口:每个微服务在kubernetes内都有一个Deployment负责Pod的部署、端口暴露(Expose出Spring应用的server.port便可)
对服务的封装:每一个应用还须要一个Service来作服务实例的负载均衡,这样服务之间调用就只要找服务对应的Service就好。Service的ip地址由Kubernetes内部DNS负责解析。服务对外统一用80端口,这样集群内部访问时候就只要写服务名称便可。同时Service的名称和微服务的应用名称一致。spring

2)FeignClient的改造
原SpringCloud内只要指定serviceName就能经过Ribbon自行负载到对应的实例上,如今则须要经过Kubernetes的service实现调用,因为1)已经约定了Service名称与原服务名称一致,同时端口是80。所以只要加上url就行。
如:docker

// 这里的url是新增参数 @FeignClient(name="remote-service", url="remote-service") public interface RemoteServiceApi {} // 这里的url是新增参数 @FeignClient(name="remote-service", url="remote-service") public interface RemoteServiceApi {}

环境改造

1)将数据库链接改成Service和Endpoint
这样应用里的数据库链接jdbc就从:数据库

jdbc:10.2.3.2:5433/dbname

改成bootstrap

jdbc:pg-master/dbname

Service和Endpoint的配置方式:api

apiVersion: v1
kind: Endpoints
metadata:
  name: pg-endpoint-loadbalancer
subsets:
  - addresses:
    - ip: 10.2.xxx.xxx
    ports:
    - port: 4432
      protocol: TCP
---
apiVersion: v1
kind: Service
metadata:
  name: pg-endpoint-loadbalancer
spec:
  ports:
  - port: 5432
    targetPort: 4432
    protocol: TCP

由此改变的好处在于,数据库切换无需逐个更换应用配置再重启,而是仅需修改集群内Endpoint设置便可。服务器

2)部分通用配置改成ConfigMap
将通用配置,如zipkin配置、redis配置等全局范围内应用都会用到的参数提取到ConfigMap中,在经过EnvFrom的方式挂在在应用的镜像中。此时跑在容器内的镜像就能够经过环境变量获取到这些全局的设置。
好比 bootstrap.properties:

spring.rabbitmq.username=${rabbitmq_username}

环境变量挂载方式:

env:
          - name: rabbit_mq_host
            valueFrom:
              configMapKeyRef:
                name: cloud-env
                key: rabbit_mq_host
          - name: rabbit_mq_address
            valueFrom:
              configMapKeyRef:
                name: cloud-env
                key: rabbit_mq_address
          - name: redis_cluster
            valueFrom:
              configMapKeyRef:
                name: cloud-env
                key: redis_cluster

日志改造

原应用日志统一写入${HOME}/logs/${application-name.log}里边,再有logstash采集后发送到elastic search。如今采用容器方式运行后则再也不将日志文件与物理机器挂钩,而是在一个Pod内再启动一个filebeat日志采集容器,两个容器共同挂载一个/log/目录。

# 部分yaml
template:
  spec:
    volumes:
      - name: app-logs
        emptyDir: {}
	containers:
	- name: xxxx(application-image)
	# ......
	  volumeMounts:
          - name: app-logs
            mountPath: /log
     - name: filebeat-image
     # ......
	  volumeMounts:
          - name: app-logs
            mountPath: /log

结构如图所示:
pod结构

日志采集结构改变为以下图所示:
在这里插入图片描述

应用部署方式

考虑到减小其他开发人员的新增学习成本咱们将应用的打包发布流程封装成部署脚本,提供给开发人员,开发人员仅需准备jar包、lib依赖(可选)以及配置文件便可。
经过统一的模板和配置脚本将:打包Docker image、上传私服、生成yaml文件等自动完成。
为自动化部署和升级须要,yaml中默认采用Alwayl的Image拉去imagePullPolicy: Always,同时增长rollout 升级的配置。这样研发人员在部署应用时就能自动进行滚动升级,并支持回滚到旧版本。

spec:
  replicas: REPLICAS
  strategy:
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 1

发布于部署的关系以下所示:
一键发布脚本会自动完成打包docker image、推送私服、生成yaml文件三件事。研发人员仅需应用生成的yaml文件便可完成部署任务。
在这里插入图片描述

集群服务入口

  • 原服务: 互联网 -> CDN -> 防火墙 -> Nginx -> zuul网关 -> 微服务
  • 改成: 互联网 -> CDN -> 防火墙 -> Nginx -> traefik Ingress -> zuul网关 -> 微服务。

从traefik以后就已经进入微服务集群了。固然这里nginx仍然保留是由于历史元英,有些端口和应用二级目录共用问题,暂时没法彻底由traefik接管。

结束

综上完成所有改造流程,将应用迁移进Kubernetes,并适时切换nginx负载便可。