部署高可用Kubernetes集群

写在前面的话

整个安装过程中尽量不要出现写死的IP的情况出现,尽量全部使用域名代替IP。

网上大量的人使用KeepAlive+VIP的形式完成高可用,这个方式有两个不好的地方:

其一,受限于使用者的网络,无法适用于SDN网络,比如Aliyun的VPC。

其二,虽然是高可用的,但是流量还是单点的,所有node的的网络I/O都会高度集中于一台机器上(VIP),一旦集群节点增多,pod增多,单机的网络I/O迟早是网络隐患。

本文的高可用可通用于任何云上的SDN环境和自建机房环境,列如阿里云的VPC环境中。

整体架构图

  • 网上流传最多的基于VIP的高可用

VIP

  • 本文使用的基于NginxProxy的高可用

NginxProxy

节点清单

ip hostname OS kernel version
127.0.0.1 server.k8s.local Ubuntu 16.04 4.19.11-041911-generic
192.168.0.100 server1.k8s.local Ubuntu 16.04 4.19.11-041911-generic
192.168.0.101 server2.k8s.local Ubuntu 16.04 4.19.11-041911-generic
192.168.0.102 server3.k8s.local Ubuntu 16.04 4.19.11-041911-generic

阅读全文 »

容器中的JVM资源该如何被安全的限制?

前言

Java与Docker的结合,虽然更好的解决了application的封装问题。但也存在着不兼容,比如Java并不能自动的发现Docker设置的内存限制,CPU限制。

这将导致JVM不能稳定服务业务!容器会杀死你JVM进程,而健康检查又将拉起你的JVM进程,进而导致你监控你的pod一天重启次数甚至能达到几百次。

我们希望当Java进程运行在容器中时,java能够自动识别到容器限制,获取到正确的内存和CPU信息,而不用每次都需要在kubernetes的yaml描述文件中显示的配置完容器,还需要配置JVM参数。

使用JVM MaxRAM参数或者解锁实验特性的JVM参数,升级JDK到10+,我们可以解决这个问题(也许吧~.~)。

首先Docker容器本质是是宿主机上的一个进程,它与宿主机共享一个/proc目录,也就是说我们在容器内看到的/proc/meminfo/proc/cpuinfo
与直接在宿主机上看到的一致,如下。

  • Host

    1
    2
    3
    4
    cat /proc/meminfo 
    MemTotal: 197869260 kB
    MemFree: 3698100 kB
    MemAvailable: 62230260 kB
  • 容器

    1
    2
    3
    4
    docker run -it --rm alpine cat /proc/meminfo
    MemTotal: 197869260 kB
    MemFree: 3677800 kB
    MemAvailable: 62210088 kB

阅读全文 »

Zuul源码分析(1)生命周期

前言

  • Zuul在Spring Cloud Netfilx 体系中扮演着接入者网关的角色。
  • 本质上来说Zuul本身就是一系列的filters, 可以类比Servlet框架的Filter。按照生命周期我们可以分为四种类型(pre,route,post,err)分别对应请求过程。你可以从com.netflix.zuul.FilterProcessor类里面找到所有的生命周期处理。
  • 为什么我们要去了解它?比如我们想在网关统一对用户进行鉴权,进行JWT的解析和参数转换,比如我们想实现自己的httpClient,再比如我们想在后端业务微服务返回的结果内进行一些特别的处理,比如脱敏啊,比如去掉一些字段啊。

Zuul每个周期的流转过程

  • 请求流程

Sample

阅读全文 »

记SpringCloud 1.X 升级到2.x

前言

  • 前后花了两周多个时间完成了 Spring Boot 1.5.6.RELEASE & Spring Cloud Dalston.SR4 升级到 Spring Boot 2.0.6.RELEASE & Spring Cloud Finchley.SR2 & spring-cloud-netflix 2.0.2.RELEASE 的工作。
  • 总结一下遇到的一些问题

一些问题的总结

  • 首先 1.x和2.x的所有的服务注册,服务发现,灰度调用,服务调用,zuul网关等等组件核心都是兼容的。so大胆的升级吧。
  • 其次maven pom变化较大,主要是netifilx的artifactId变化比较多,其余的变化都不是太大,这都可以通过spring-cloud-netflix-dependenciespom中找到。
  • 然后是feign的变化比较大,整个包名发生了变化。
  • NotBlank,NotEmpty 现在已经纳入了JSR303了,不需要在使用hibernate提供的注解了。

一些建议

  • 建议统一抽象出一个业务服务使用pom依赖项目,并打包发布维护起来,比如我们这里就叫my-server-dependencies的这么一个pom项目。这样做有几个好处:
  • 第一个是通过将版本统一管理起来了,方便对所有的基础服务jar包进行升级,而且能够完成版本的基础依赖管理。
  • 第二个是能够避免掉由于未付项目过多之后导致的依赖版本混乱。
  • 第三个是能够将maven插件统一的配置,比如说docker打包插件,fatjar插件,compiler插件 ,能统一的对他们进行控制和配置,并通过properties暴露出集体的调优指标。

阅读全文 »

记Kafka的一个BUG,导致整个集群不能工作

现象

  • Kafka producer 无法正确的发出任何消息一直抛出Timeout(由于我们业务设计上就把业务时间和mq完全分离了这里,不会导致业务不能正常进行,只会产生业务暂时的不一致性)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19

    java.util.concurrent.TimeoutException: Timeout after waiting for 5000 ms.
    at org.apache.kafka.clients.producer.internals.FutureRecordMetadata.get(FutureRecordMetadata.java:76)
    at org.apache.kafka.clients.producer.internals.FutureRecordMetadata.get(FutureRecordMetadata.java:29)
    at com.my.fnc.mq.FncRunner.apply(FncRunner.java:42)
    at io.goudai.starter.runner.zookeeper.AbstractMultipartRunner$1.doRun(AbstractMultipartRunner.java:76)
    at io.goudai.starter.runner.zookeeper.AbstractRunner.takeLeadership(AbstractRunner.java:94)
    at org.apache.curator.framework.recipes.leader.LeaderSelector$WrappedListener.takeLeadership(LeaderSelector.java:537)
    at org.apache.curator.framework.recipes.leader.LeaderSelector.doWork(LeaderSelector.java:399)
    at org.apache.curator.framework.recipes.leader.LeaderSelector.doWorkLoop(LeaderSelector.java:444)
    at org.apache.curator.framework.recipes.leader.LeaderSelector.access$100(LeaderSelector.java:64)
    at org.apache.curator.framework.recipes.leader.LeaderSelector$2.call(LeaderSelector.java:245)
    at org.apache.curator.framework.recipes.leader.LeaderSelector$2.call(LeaderSelector.java:239)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:748)

阅读全文 »

如何构建SpringBoot的Docker镜像

目标

  • 自定义Dockerfile构建一个生产可用的jre base image
  • 配置maven-docker-plugin插件完成从源码的 打包fatjar -> build docker image with fatjar -> push image
  • 支持docker对JVM相关参数的配置。比如Xmx,Xms,以及完全自定义的java启动参数。

rootfs

  • 说打包之前我们先了解linux内核与发行版操作系统(如centos,ubuntu,debian)之间的关系。
  • 由于linux内核与具体的操作系统是解耦的。即互相不干涉的,docker利用了这个特性,将操作系统的文件打包成一个压缩文件。
  • 在运行时,解压这个压缩包,并通过chroot进行挂载,就完成了容器的内部我们看到的操作系统了。即我们的rootfs。
  • 那么这个和我们docker打包java应用的关系在哪里呢?
  • 总所周知的是java是又提供打包解决方案的,打包成jar,但是此方案的问题在于我并不能在任何一个环境里面运行直接运行(因为依赖JRE),而每一个操作系统又不一样,这就导致许多环境带来的时间浪费。
  • 结合前面提到了docker打包是把操作系统的文件打包的,所以我们能不能把操作系统+jre+application.jar这三老铁一锅端,全给他打包起来不就解决了吗?没错,这就是我们要构建镜像。
  • 没错我们想到了一个好的办法来解决打包的问题。那我们在来看看这个东西是不是还有啥问题?你看啊,我们最初了发布一个fatjar也就60M,要结合上OS jre岂不是每次都大很多,浪费很多的磁盘,网络传送开销也加大的蛮多。
  • 这个问题的docker中利用了分层文件系统来解决这个问题,即我们的OS JRE 这些不变的东西只会在第一次使用时下载一次或者上传一次,其他时候我们只有变化的application.jar层需要进行上传和下载

最终形态

  • 我们像搭积木一样一层一层的把我们所需要的文件系统叠加起来,就完成了我们想要的Image。
    Sample

    阅读全文 »

扩展spring-cloud-ribbon支持灰度

目标

  • 扩展ribbon完成灰度调用
  • 完成对zuul的支持
  • 完成服务间调用的支持
  • 实战,解决在开发环境,进行开发中的测试,DEBUG.在微服务的模式下,需要在开发者的机器启动大量服务,
    启动大量的服务需要大量的内存和大量的时间,在我们时间的项目开发中,在16G的机器上甚至无法进行调
    和测试相关工作。

思路

阅读全文 »

kubernetes全自动弹性伸缩组件

目标

  • 使用容器完成 metrics-server 部署
  • 演示 Horizontal Pod Autoscaling (pod 自动缩放)

部署 metrics-server 服务

  • Horizontal Pod Autoscaler(HPA)控制器用于实现基于CPU使用率进行自动Pod伸缩的功能。
  • HPA控制器基于Master的kube-controller-manager服务启动参数–horizontal-pod-autoscaler-sync-period定义是时长(默认30秒),周期性监控目标Pod的CPU使用率,并在满足条件时对ReplicationController或Deployment中的Pod副本数进行调整,以符合用户定义的平均Pod CPU使用率。
  • 在新版本的kubernetes中 Pod CPU使用率不在来源于heapster,而是来自于metrics-server
  • 官网原话是 The --horizontal-pod-autoscaler-use-rest-clients is true or unset. Setting this to false switches to Heapster-based autoscaling, which is deprecated.

    阅读全文 »