在 Kubernetes 中使用 SOFABoot 的 Readiness Check 能力

SOFABoot 是蚂蚁金服中间件团队开源的基于 Spring Boot 的一个开发框架,其在 Spring Boot 的健康检查的基础上,加上了 Readiness Check 的能力,以更好地适应大规模金融级的服务化场景,防止在应用启动有问题的情况下让外部流量进入应用。在本文中,我们将通过 Kubernetes 来演示 SOFABoot 的 Readiness Check 的能力,主要涉及到两个部分的能力的演示:

  1. SOFABoot 的 Readiness Check 失败之后,SOFABoot 不会将发布的 RPC 服务的地址注册到 ZooKeeper 上面,防止 RPC 的流量进入。
  2. Kubernetes 通过 http://localhost:8080/health/readiness 访问到 SOFABoot 的 Readiness 检查的结果之后,不会将 Pod 挂到对应的 Service 之上,防止 Kubernetes 上的流量进入。

准备一个 Kubernetes 的环境

为了演示在 Kubernetes 中使用 SOFABoot 的 Readiness Check 的能力,首先需要准备好一个 Kubernetes 的环境,在这个例子中,我们直接选择在本机安装一个 minikube,minikube 是 Kubernetes 为了方便研发人员在自己的研发机器上尝试 Kubernetes 而准备的一个工具,对于学习 Kubernetes 的使用非常方便。关于如何在本机安装 minikube,大家参考这个官方的安装教程即可。

安装完成以后,大家可以直接终端中使用 minikube start来启动 minikube。

<em>需要​注意的是,由于国内网络环境的问题,直接用 </em><code><em>minikube start</em></code><em> 可能会无法启动 minikube,如果遇到无法启动 minikube 的问题,可以尝试加上代理的设置,大家可以参考以下的命令来设置代理服务器的地址:</em>

minikube start --docker-env HTTP_PROXY=http://xxx.xxx.xxx.xxx:6152 --docker-env HTTPS_PROXY=http://xxx.xxx.xxx.xxx:6152

在 Kubernetes 上安装一个 ZooKeeper

在准备好了 Kubernetes 的环境之后,我们接下来需要在 Kubernetes 上安装一个 ZooKeeper 作为 SOFARPC 的服务自动发现的组件。首先我们需要有一个 ZooKeeper 的 Deployment:

apiVersion: apps/v1beta1
kind: Deployment
metadata:
  name: zookeeper-deployment
  labels:
    app: zookeeper
spec:
  replicas: 1
  selector:
    matchLabels:
      app: zookeeper
  template:
    metadata:
      labels:
        app: zookeeper
    spec:  
      containers:
        - name: zookeeper
          image: zookeeper
          imagePullPolicy: IfNotPresent
          ports:
            - containerPort: 2181

这个 Deployment 会部署一个 ZooKeeper 的实例,并且将 2181 端口暴露出来。

有了这个 YAML 文件之后,我们再部署一个 Service 来作为 ZooKeeper 的负载均衡,这样我们在应用中就可以直接通过域名来访问,而不用 IP 来访问 ZooKeeper 了。这个 Service 的 Yaml 文件如下:

apiVersion: v1
kind: Service
metadata:
  name: zookeeper-service
spec:
  selector:
    app: zookeeper
  ports:
  - protocol: TCP
    port: 2181
    targetPort: 2181

这个 Service 直接将 2181 端口映射到 ZooKeeper 的 2181 端口上,这样,我们就可以在应用中直接通过 zookeeper-service:2181 来访问了。

准备一个 SOFABoot 的应用

在前面的两步都 OK 之后,我们需要准备好一个 SOFABoot 的应用,并且在这个应用中发布一个 SOFARPC 的服务。首先,我们需要从 start.spring.io 上生成一个工程,例如 GroupId 设置为 <span data-type="color" style="color: rgb(36, 41, 46);">com.alipay.sofa,ArtifactId 设置为 rpcserver。</span>

<span data-type="color" style="color: rgb(36, 41, 46);">生成好了之后,接下来,我们需要把 SOFABoot 的依赖加上,将 pom.xml 中的 parent 修改成:</span>

<parent>
    <groupId>com.alipay.sofa</groupId>
    <artifactId>sofaboot-dependencies</artifactId>
    <version>2.3.1</version>
</parent>

然后,增加一个 SOFARPC 的 Starter 的依赖:

<dependency>
    <groupId>com.alipay.sofa</groupId>
    <artifactId>rpc-sofa-boot-starter</artifactId>
</dependency>

接着,在 application.properties 里面加上我们的配置,包括应用名和 ZooKeeper 的地址:

# Application Name
spring.application.name=SOFABoot Demo
# ZooKeeper 的地址
com.alipay.sofa.rpc.registry.address=zookeeper://127.0.0.1:2181

上面的事情准备好之后,我们可以在应用中发布一个服务,首先,我们需要分别声明好一个接口和一个实现:

package com.alipay.sofa.rpcserver;

public interface SampleService {
    String hello();
}
package com.alipay.sofa.rpcserver;

public class SampleServiceImpl implements SampleService {
    @Override
    public String hello() {
        return "Hello";
    }
}

接下来,将这个接口和实现发布成一个 SOFARPC 的服务,我们可以新建一个 src/main/resources/spring/rpc-server.xml 的文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:sofa="http://sofastack.io/schema/sofaboot"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
            http://sofastack.io/schema/sofaboot   http://sofastack.io/schema/sofaboot.xsd">
    <bean class="com.alipay.sofa.rpcserver.SampleServiceImpl" id="sampleService"/>
    <sofa:service ref="sampleService" interface="com.alipay.sofa.rpcserver.SampleService">
        <sofa:binding.bolt/>
    </sofa:service>
</beans>

需要注意的是,通过 XML 定义好上面的服务之后,我们还需要在 Main 函数所在的类里面增加一个 @Import,将 XML Import 进去:

package com.alipay.sofa.rpcserver;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ImportResource;

@SpringBootApplication
@ImportResource("classpath*:spring/*.xml")
public class RpcServerApplication {

    public static void main(String[] args) {
        SpringApplication.run(RpcServerApplication.class, args);
    }
}

然后​,为了演示 Readiness Check 的能力,我们还需要增加一个 HealthIndicator 来控制 Readiness Check 的结果:

package com.alipay.sofa.rpcserver;

import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.stereotype.Component;

@Component
public class SampleHealthIndicator implements HealthIndicator {
    @Override
    public Health health() {
        return Health.up().build();
    }
}

这里​,我们首先直接返回成功,先演示 Readiness Check 成功的场景。

将应用部署到 Kubernetes 里面

在前面的步骤完成之后,应用的代码都已经准备好了,现在可以准备将应用部署到 Kubernetes 里面。首先,需要将应用打包成一个 Docker 镜像,需要注意的是,为了让 Kubernetes 能够找到这个 Docker 镜像,在打包镜像之前,要先将 Docker 环境切成 Minikube 的环境,运行以下的命令即可:

eval $(minikube docker-env)

然后准备一个 Dockerfile:

FROM openjdk:8-jdk-alpine
ARG JAR_FILE
ADD ${JAR_FILE} app.jar
ENTRYPOINT [ "java", "-jar", "/app.jar"]

最后,运行如下的命令来进行打包:

docker build --build-arg JAR_FILE=./target/rpcserver-0.0.1-SNAPSHOT.jar . -t rpc-server-up

其中 JAR_FILE 参数 SOFABoot 应用程序的 JAR 包路径。镜像打包出来后,我们就可以准备一个 YAML 来部署应用了:

apiVersion: apps/v1beta1
kind: Deployment
metadata:
  name: rpc-server-deployment
  labels:
    app: rpc-server
spec:
  replicas: 1
  selector:
    matchLabels:
      app: rpc-server
  template:
    metadata:
      labels:
        app: rpc-server
    spec:  
      containers:
        - name: rpc-server
          image: rpc-server-up
          imagePullPolicy: IfNotPresent
          ports:
            - containerPort: 8080
          readinessProbe:
            httpGet:
              path: /health/readiness
              port: 8080

注意在上个面的 YAML 中,我们定义了一个 Kubernetes 的 Readiness Probe,访问 localhost:8080/health/readiness 来获取 SOFABoot Readiness Check 的结果。

打包完成之后,可以运行如下的命令来将应用部署到 Kubernetes 中:

kubectl apply -f rpcserver.xml

部署完成后,我们再通过一个 Service,将应用的实例挂到一个 Service 下面去,这样就可以通过查看 Service 下的 EndPoint 节点的数量来看 Readiness Check 是否起作用了:

apiVersion: v1
kind: Service
metadata:
  name: rpc-server-service
spec:
  selector:
    app: rpc-server
  ports:
  - protocol: TCP
    port: 8080
    targetPort: 8080

运行如下命令将 Service 部署到 Kubernetes 里面去:

kubectl apply -f rpc-server-service.yml

Readiness Check 成功的节点挂载情况

由于上面我们写的 HealthIndicator 直接返回了一个 Up,所以 Readiness Check 应该成功,我们可以分别从 ZooKeeper 和 Service 里面查看节点的情况。

首先看下 ZooKeeper 里面,为了查看 ZooKeeper 里面的节点的情况,需要在本地有一个 ZooKeeper 的程序在,这个可以直接从 ZooKeeper 的官网上下载。

然后,我们需要拿到在 Kubernetes 里面部署的 ZooKeeper 的对外暴露的地址,通过如下命令拿到地址:

kubectl expose deployment zookeeper-deployment --type=NodePort && minikube service zookeeper-deployment --url

在我本机拿到的地址是 192.168.99.100:30180

然后,就可以通过本地的 ZooKeeper 程序里面的 zkCli.sh 来查看 ZooKeeper 里面的节点了,运行如下的命令:

./zkCli.sh -server 192.168.99.100:30180
......
[zk: 192.168.99.100:30180(CONNECTED) 5]ls /sofa-rpc/com.alipay.sofa.rpcserver.SampleService/providers

就可以看到里面有一个节点的信息,就是我们的 rpcserver 部署在 Kubernetes 里面的节点。

也可以去看下 rpcserver 的 Service 里面的节点的信息,运行如下的命令:

kubectl describe service rpc-server-service

也可以看到红框中有一个节点的信息。

Readiness Check 失败的节点挂载情况

在上面,我们已经看到了 Readiness Check 成功之后,可以在 ZooKeeper 里面和 Service 的 EndPoints 里面都可以看到节点的信息,现在来看下 Readiness Check 失败后的情况。

为了让 Readiness Check 失败,要将之前写的 SampleHealthIndicator 改成 Down,代码如下:

package com.alipay.sofa.rpcserver;

import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.stereotype.Component;

@Component
public class SampleHealthIndicator implements HealthIndicator {
    @Override
    public Health health() {
        return Health.down().build();
    }
}

然后使用 mvn clean install 重新打包程序,打包之后,我们需要重新构建镜像,为了跟前面的 Readiness Check 成功的镜像以示区分,我们将镜像的名称换成 rpc-server-down

docker build --build-arg JAR_FILE=./target/rpcserver-0.0.1-SNAPSHOT.jar . -t rpc-server-down

然后我们再将之前的应用的 Deployment 的 YAML 文件中的镜像名称换成新的镜像名称,其他保持不变:

apiVersion: apps/v1beta1
kind: Deployment
metadata:
  name: rpc-server-deployment
  labels:
    app: rpc-server
spec:
  replicas: 1
  selector:
    matchLabels:
      app: rpc-server
  template:
    metadata:
      labels:
        app: rpc-server
    spec:  
      containers:
        - name: rpc-server
          image: rpc-server-down
          imagePullPolicy: IfNotPresent
          ports:
            - containerPort: 8080
          readinessProbe:
            httpGet:
              path: /health/readiness
              port: 8080

最后,通过 kubectl apply -f rpcserver.yml 来更新 Kubernetes 里面的 RPCServer 这个应用。

更新之后,我们再去 ZooKeeper 里面看下服务发布的情况,就只能看到一个空的列表了:

通过 kubectl describe 查看新的 Pod 的情况,也可以看到 Readiness Check 失败:

通过 kubectl describe service rpc-server-service 也可以看到 Service 下面的 EndPoint 还是之前的那个,新的并没有挂载上去。

总结

本文中,我们演示了如何通过 Readiness Check 来控制应用的流量,在 Readiness Check 失败的情况下,让流量不进入应用,防止业务受损。在上面的例子中,我们通过 Readiness Check 完成了两个部分的流量的控制,一个是 Readiness Check 失败之后,SOFARPC 不会将服务的地址上报到对应的服务注册中心上,控制通过自动服务发现进入的流量,另一个方面,Kubernetes 也不会将 Pod 挂到对应额 Service 之上,防止负载均衡器进入的流量。

虽然 SOFABoot 提供了 Readiness Check 的能力,并且对应的中间件也已经实现了根据 SOFABoot 的 Readiness Check 的结果来控制流量,但是完整的流量控制,还需要外围的平台进行配合,比如负载均衡的流量就需要 Kubernetes 的 Readiness Check 的能力来一起配合才可以完成控制。

本文中所有涉及到的代码以及 Kuberentes 的 YAML 配置文件都已经放到了 Github 上,欢迎大家参考下载:https://github.com/khotyn/sofa-boot-readiness-check-demo

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,699评论 6 513
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,124评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 167,127评论 0 358
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,342评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,356评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,057评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,654评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,572评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,095评论 1 318
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,205评论 3 339
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,343评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,015评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,704评论 3 332
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,196评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,320评论 1 271
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,690评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,348评论 2 358

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,681评论 18 139
  • 内容已经移动到这里[https://blog.csdn.net/WeiPeng2K/article/details...
    weipeng2k阅读 2,575评论 1 22
  • 一、 K8s 是什么? Kubernetes(k8s)是自动化容器操作的开源平台,这些操作包括部署,调度和节点集群...
    loveroot阅读 6,647评论 1 21
  • 感赏孩子今天没有出现昨天那么大的情绪波动,情绪比昨天稳定。 感赏孩子今天给我弹了一个小时的钢琴。 感赏自己能接纳他...
    欣然怡然阅读 128评论 2 3
  • 如果一直友好的朋友,跟你渐行渐远,在某个梦里,你会与其相遇,梦里你们会交流的很好。说明你是恋旧之人。 每个人来此一...
    真浈阅读 501评论 0 2