微服务中的服务网关至少有以下作用:
- 转发(服务路由与负载均衡)
- 鉴权(身份认证与权限校验)
- 限流(流量控制与熔断)
它们在SpringCloud和K8S中有着不同的实现方式:
SpringCloud | K8S | |
---|---|---|
服务网关 | SpringCloudGateway、Zuul | Ingress |
服务路由是网关的核心功能,下面将基于SpringCloudGateway与Ingress分别介绍SpinrgCloud与K8S中服务路由的使用方法。
一、SpringCloud中的服务网关
(一)启动网关服务
spring-cloud-starter-gateway
是实现SpringCloudGateway的核心依赖,同时,也可引入nacos-discovery
与loadbalancer
依赖以支持注册中心与负载均衡。如下所示:
<dependencies>
<!-- 网关依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!-- 注册中心依赖 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- 负载均衡(不加此依赖,路由地址uri使用lb协议可能会503报错) -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
</dependencies>
随后,在application.yml
文件中配置路由转发规则:
server:
port: 8888 # 网关端口
spring:
application:
name: gateway # 服务名称
cloud:
nacos:
server-addr: localhost:8848 # nacos地址
gateway:
routes: # 网关路由配置
- id: nacos-provider # 路由id,全局唯一
uri: lb://nacos-provider # 路由目的地
predicates: # 路由断言,符合断言条件才会跳转到路由目的地
- Path=/echo/** # 请求路径断言
filters: # 路由过滤器,对请求和响应做处理
- AddResponseHeader=X-Response-Special, Special # 当前路由专用过滤器
default-filters:
- AddResponseHeader=X-Response-Default, Default # 默认过滤器
这里的uri使用的是lb协议,其会默认从注册中心找到对应的服务,如果使用k8s的负载均衡或不使用负载均衡,uri可以使用http协议,如:http://localhost:8081。
这里假定本地已经启动了nacos注册中心服务和nacos-provider
服务,nacos-provider
的代码详见github。有关predicates
和filters
,详见后文。
随后,定义启动类:
@SpringBootApplication
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
}
上述代码详见github。
启动并访问:
curl "http://localhost:8888/echo?name=zhangsan"
可以看到正常打印响应结果:
echo:zhangsan
至此,该网关已经实现了对nacos-provider
服务的路由转发,用户无需知道(也不应该知道)nacos-provider
的访问地址,直接可以通关网关进行访问。
(二)路由断言与过滤
上述配置文件中,Path
请求路径断言由PathRoutePredicateFactory
处理,AddResponseHeader
由AddResponseHeaderGatewayFilterFactory
处理,断言工厂和路由过滤器工厂的详细信息可参见官网Route Predicate Factories与GatewayFilter Factories。
除了上述配置文件中定义的路由专用过滤器(routes.filters
)和默认过滤器(default-filters
)外,SpringCloudGateway还支持一种可定制性更强的全局过滤器(GlobalFilter
)。一个示例如下:
@Component
@Order(-1)
public class AddResponseHeaderGlobalFilter implements GlobalFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpResponse response = exchange.getResponse();
response.getHeaders().set("X-Response-Global", "Global");
return chain.filter(exchange);
}
}
该过滤器与上述配置文件中的AddResponseHeader
过滤器实现一样的功能:增强响应头。
再次重启应用,并发送请求:
curl -i "http://localhost:8888/echo?name=zhangsan" # curl -i参数打印响应头
响应结果如下:
HTTP/1.1 200 OK
X-Response-Default: Default
X-Response-Special: Special
X-Response-Global: Global
Content-Type: text/plain;charset=UTF-8
Content-Length: 13
Date: Sat, 10 Jun 2023 06:37:58 GMT
echo:zhangsan
可以看到,三个过滤器都生效了。上述过滤器存在以下执行顺序:
- 以Order值排序。全局过滤器的Order由用户指定(使用
@Order
注解或实现Ordered
接口)。默认过滤器和路由专用过滤器的Order则从1开始计数,按照定义的顺序累加,同时,默认过滤器与专用过滤器的Order值独立累加。 - 若Order值一致,则以默认过滤器 > 路由专用过滤器 > 全局过滤器排序。
二、K8S中的服务网关
(一)启动一个应用
- 启动一个deployment
kubectl create deployment web --image=nginx
- 暴露一个service
kubectl expose deployment web --type=NodePort --port=8080 --target-port=80
kubectl get service web
- 通过minikube调用服务
minikube service web --url # 输出类似于 http://192.168.49.2:31959
curl http://192.168.49.2:31959
192.168.49.2是minikube的集群ip,使用ifconfig命令查看网络适配器情况,可以看到一个minikube的专用IP网段192.168.49.1。
正常将会显示nginx的首页html文件内容。这是通过minikube自带的service指令实现访问minikube内部service的方法,后续将会基于Ingress提供更通用的外部访问服务的方法。
(二)启用Ingress控制器
在使用Ingress之前,需要先启用Ingress控制器。Minikube支持在本地环境快速启用Ingress控制器,下面将以Minikube为例介绍Ingress的配置过程。
生产环境Ingress控制器可以参考Ingress-Nginx Controller Installation Guide。
- 启动minikube
minikube start --image-mirror-country='cn'
- 启用Ingress控制器
minikube addons enable ingress
国内环境可能拉取不了镜像,可以参考附录minikube在国内环境启用addons方法。
- 查看Ingress控制器状态
kubectl get pods -n ingress-nginx
结果如下:
NAME READY STATUS RESTARTS AGE
ingress-nginx-admission-create-x98cn 0/1 Completed 0 90m
ingress-nginx-admission-patch-mscpc 0/1 Completed 0 90m
ingress-nginx-controller-7b7b49467c-4kt7m 1/1 Running 0 90m
(三)创建Ingress
- 定义Ingress配置文件
example-ingress.yaml
:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: example-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$1
spec:
rules:
- host: hello-world.info
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web
port:
number: 8080
该配置文件定义了hello-world.info
域名到web
这个service
的转发规则。
- 启动Ingress
kubectl create -f example-ingress.yaml
- 查看Ingress状态
kubectl get ingress example-ingress
结果如下:
NAME CLASS HOSTS ADDRESS PORTS AGE
example-ingress nginx hello-world.info 192.168.49.2 80 44s
- 通过Ingress访问服务
curl --resolve "hello-world.info:80:$( minikube ip )" -i http://hello-world.info
正常将会显示nginx的首页html文件内容。其中minikube ip
是minikube的集群IP(192.168.49.2),curl --resolve
定义了域名解析(也可在本机host中配置)。
至此,该Ingress实现了以hello-world.info
域名为入口,到pod内部的流量转发。
后续可按需基于Ingress规则配置更多的服务路由转发策略。
三、总结
SpringCloud通过SpringCloudGateway等组件实现网关。SpringCloudGateway是用Java开发,可定制性更强。其包括:服务路由、断言、过滤、断路器支持、服务发现支持、限流等功能。
K8S通过Ingress实现网关,除了前面描述的服务路由功能外,Ingress也支持其它网关功能(如:鉴权、限流等等)。Ingress服务需要依赖于特定的Ingress控制器,比如NGINX Ingress Controller。NGINX Ingress网关的功能更多地依赖于NGINX本身的特性,有关NGINX Ingress的更多特性,也可参见其官网。
与SpringCloud与K8S中的服务配置中类似:SpringCloud偏向于开发,而K8S则更偏向于运维。有关技术选型,需视情况而定。
附录
minikube在国内环境启用addons方法
- 在本地拉取docker镜像
docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/nginx-ingress-controller:v1.7.0
docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/kube-webhook-certgen:v20230312-helm-chart-4.5.2-28-g66a760794
版本号可按需修改。
- 将本地镜像加载到minikube集群中
minikube image load registry.cn-hangzhou.aliyuncs.com/google_containers/nginx-ingress-controller:v1.7.0
minikube image load registry.cn-hangzhou.aliyuncs.com/google_containers/kube-webhook-certgen:v20230312-helm-chart-4.5.2-28-g66a760794
上一步的docker pull
不是必须的。如果上一步执行了docker pull
,这里的load
命令就会从本地Docker镜像中拷贝,反之则从远程仓库拉取。
- 启用addons,并修改镜像地址
minikube addons enable ingress --images="KubeWebhookCertgenCreate=registry.cn-hangzhou.aliyuncs.com/google_containers/kube-webhook-certgen:v20230312-helm-chart-4.5.2-28-g66a760794,KubeWebhookCertgenPatch=registry.cn-hangzhou.aliyuncs.com/google_containers/kube-webhook-certgen:v20230312-helm-chart-4.5.2-28-g66a760794,IngressController=google_containers/nginx-ingress-controller:v1.7.0" --registries="IngressController=registry.cn-hangzhou.aliyuncs.com"
参考文档