如何将多个springboot应用部署在k8s并实现互相通信
近来因工作需要,springboot / spring cloud 构建的产品需要提供嵌入k8s的能力,所以手动部署了一下。
目标:部署服务A与B,请求到达服务A,服务A内部请求服务B,然后返回结果。
一、构建springboot应用
要实现在k8s上面通信,笔者使用了 spring-cloud-kubernetes 这个依赖包来完成,且使用的 maven 来构建 project,读者可以自行更换 gradle 等以及对应版本,个人只贴出pom依赖,且version并未贴出,请自行寻找与自己使用的springboot版本对应的veision:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-kubernetes</artifactId>
</dependency>
1.编写两个springboot应用,分别名字为 demo-request 以及 demo-data,实现以上功能,具体如何构建不再赘述,
demo-data服务部分核心服务代码如下(忽略了部分重要的导包):
1.1 定义一个controller,返回了当前请求的 ip 与 hostname
@RestController
public class DataController {
@GetMapping("/data")
public ResponseVO<HashMap> data(){
HashMap<String, String> map = new HashMap<>();
try {
InetAddress address = InetAddress.getLocalHost();
map.put("name",address.getHostName());
map.put("address",address.getHostAddress());
System.out.println("Host Name: " + address.getHostName());
System.out.println("Host Address: " + address.getHostAddress());
} catch (UnknownHostException e) {
e.printStackTrace();
}
return ResponseVOUtil.success(map);
}
}
1.2 main class标记为一个web应用启动
这里有一个比较重要的点,是通过 @EnableDiscoveryClient 开启服务发现
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@EnableDiscoveryClient
@SpringBootApplication
public class DataApp {
public static void main(String[] args) {
SpringApplication.run(DataApp.class, args);
}
}
1.3 appliaction.yml配置
其中定义了服务的端口、名称以及指定 namespace。
(笔者懒,直接使用之前测试es用的namespace,读者可以提前构建,命令如下:kubectl create ns ${your define namespace})
server:
port: 12001
spring:
application:
name: demo-data
cloud:
kubernetes:
discovery:
namespaces:
- myes
demo-request 服务部分核心服务代码如下(忽略了部分重要的导包),大体与 demo-data 类似:
1.3 定义一个请求其他服务的请求
笔者这里使用openFeign组件调用 demo-data 服务中的 /data 请求拿到结果,读者可以自行使用其他组件比如 RestTemplate、okhttp等等
@RestController
public class RequestController {
@Resource
private DataServer dataServer;
@GetMapping("request")
public ResponseVO data(){
return dataServer.data();
}
}
1.4 feign接口配置
这里的url很重要,因为未引入其他组件,此处的 url 采用了 k8s的 dns 寻址,拼接规则采用的 {service-name}.{namespace},读者也可使用{namespace}.{service-name}等等,还可以使用环境变量,可以自行查看官方文档描述。
但是在 spring-cloud-kubernetes 的官网中有这个描述:使用 spring-cloud-starter-kubernetes-discoveryclient 这个依赖包的全的命名规则为:{service-name}.{namespace}.svc.{cluster}.local:{service-port}。这里是官网描述,但是本人尝试过没有连通,如果有大佬能告知感激不尽!
@FeignClient(name = "demo-data", url = "http://demo-data.myes:12001")
public interface DataServer {
@GetMapping("data")
public ResponseVO<HashMap> data();
}
1.5 application.yml配置
server:
port: 12002
spring:
application:
name: demo-request
cloud:
kubernetes:
discovery:
namespaces:
- myes
1.6 构建镜像
自行测试以上服务能够正常请求即可,打包以及构建过程省略,请自行构建镜像
贴一个简单的构建demo-request服务的Dockerfile,笔者此处只写了最小功能,如有需要可以自行修改
FROM java:8
MAINTAINER jyu
ADD ./demo-request-1.0-SNAPSHOT.jar /app.jar
EXPOSE 12002
ENTRYPOINT ["java","-jar","-Xms512m","-Xmx512m","-Dfile.encoding=utf-8","/app.jar"]
二、编写k8s的yaml文件
笔者自行创建 namespace 以及修改对应的配置,其实这块没什么好写的,因为是为了实现本文的目标写最小资源清单,笔者可以自行根据需要新增配置。
2.1 配置demo-data的yaml文件
apiVersion: apps/v1
kind: Deployment
metadata:
name: demo-data
namespace: myes
spec:
# 副本数增加为了测试service的负载均衡
replicas: 2
selector:
matchLabels:
run: demo-data
template:
metadata:
labels:
run: demo-data
spec:
# 此处笔者懒,指定了机器加载镜像,但是在实际开发过程中,pod的构建应该交给k8s本身,读者可以去掉
nodeName: node4
containers:
# 此处自行传到自己的镜像仓库或者使用本地镜像
- image: demo-data:1.0
name: demo-data
ports:
- containerPort: 12001
---
kind: Service
apiVersion: v1
metadata:
name: demo-data
namespace: myes
labels:
run: demo-data
spec:
type: NodePort
selector:
run: demo-data
ports:
- port: 12001
protocol: TCP
nodePort: 31991
2.2 配置demo-request的yaml文件
总体与demo-data类似
apiVersion: apps/v1
kind: Deployment
metadata:
name: demo-request
namespace: myes
spec:
selector:
matchLabels:
run: demo-request
template:
metadata:
labels:
run: demo-request
spec:
nodeName: node4
containers:
- image: demo-request:1.0
name: demo-request
ports:
- containerPort: 12002
---
kind: Service
apiVersion: v1
metadata:
name: demo-request
namespace: myes
labels:
run: demo-request
spec:
type: NodePort
selector:
run: demo-request
ports:
- port: 12002
protocol: TCP
nodePort: 31992
2.3 结果测试
根据2.1以及2.2的资源清单创建以后,会有如下资源出现:
其中 pod/curl-test 为笔者创建的一个用于 curl 测试的临时镜像,命令如下,自行修改 namespace:
kubectl run -i --tty --image radial/busyboxplus:curl curl-test --restart=Never --rm -n myes
此时我们在 curl-test 这个 pod 中 ping 一下 service/demo-request 的预定义名称,看看 ip 是否对应的上:
ping demo-request.myes
结果如下,可以很清楚的看得到 demo-request.myes 这个域名能够对应 service/demo-request 的 cluster-ip:
那么下一步请求一下 1.3 中预定义的请求:
curl demo-request.myes:12002/request
结果如下:
这里能够正常返回,注意此处的结果,实际上是 demo-data 服务返回的结果,注意看返回的 name 与 address 字段。
那么整个请求单向链路图为:
那么多来几次就能看到负载能力,可以很清楚的看到 ip 是变了的:
总结:本文属于入门级教程,只告诉了读者 springboot application 在 k8s 内部如何内部不通过其他组件实现通信,有一些更深入的玩法请自行了解官网。
比如 service 的负载均衡策略,配合 ingress 使用等等。