Ingress
Ingress
内部服务如何暴露给集群外部访问
使用NodePort类型的service
将k8s集群中的服务暴露给集群外部访问,最简单的方式就是使用NodePort,类似在docker环境下为容器的服务端口绑定宿主机的端口。
定义NodePort类型的service后,即可通过集群中任意节点的IP加nodePort指定的端口访问到k8s集群中的服务。
使用LoadBalancer类型的service
LoadBalancer类型的service
创建LB类型的service时,k8s会调用云厂商的api自动创建并配置外部负载均衡器。这个外部负载均衡器负责将外部流量引导到集群中的相应服务。
问题
使用NodePort类型的service
随着服务的增多,使用NodePort访问的问题也会逐渐的显现出来:
1.可用作NodePort的端口是一个有限的范围 ,并且不容易记忆和管理
2.定义NodePort的service之后,无论是基于iptables还是ipvs,它都是4层调度,而4层调度是无法实现卸载https会话
使用LoadBalancer类型的service
LoadBalancer方式的缺点是每个service需要一个LB,浪费、麻烦,并且创建一个 LoadBalancer 的 Service,集群也会自动创建一个 NodePort 和 ClusterIp 类型的 Service,用于接收从 ULB 接入的流量
需要k8s之外的设备的支持,k8s需要运行在支持LoadBalancer的基础设施上
是否有更优雅的方式访问集群内部的服务呢?
解决方案
1.可以在集群中部署一个Nginx服务
2.使用NodePort类型的service暴露Nginx的端口
3.由Nginx代理访问集群内的服务,这样即可卸载https会话
这样的话,这个Nginx就是一个7层的负载均衡器,外部流量可以统一通过NodePort或者LoadBalancer类型的service引入集群内的nginx中,然后再由nginx做7层的代理,这样就可以在nginx中进行https会话的卸载
引入ingress
在 k8s 中也有这样的一个资源Ingress,它与对应的controller配合就能够起到与这个 Nginx 类似的作用。
基于7层调度,可以将外部流量引入到内部
Ingress只需要一个NodePort或者一个LB就可以满足暴露多个Service的需求
Ingress&Ingress Controller
Ingress和Ingress Controller的工作流程
Ingress在k8s集群中的作用,是定义外部对集群内服务的访问路由,可以将Ingress狭义的理解为nginx中的配置文件nginx.conf
1.外部流量被外部负载均衡器externalLB将请求代理到NodePort类型的service(ingress-nginx)
2.ingress-nginx这个service再将请求调度到对应的内部nginx的pod中(Ingress Controller)
3.根据Ingress定义的规则(虚拟主机还是url路径映射),调度到对应的一组pod上
4.Ingress通过一个service来动态的感知pod的动态变化,然后反应到nginx(Ingress Controller)的配置文件中
Ingress的版本
k8s 1.19开始,Ingress资源的api版本从extensions/v1beta1向networking.k8s.io/v1过渡
k8s 1.22版本,正式移除extensions/v1beta1和networking.k8s.io/v1beta1
k8s 1.22以及之后的版本,ingress的api版本为networking.k8s.io/v1
Ingress的调度方式
ingress支持的调度方式
1.url路径映射调度:location /aaa location /bbb。
2.基于虚拟主机的调度: server aaa server bbb
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app-test
namespace: test
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$2 # 开启路径rewrite功能,并指定重写的对象
# kubernetes.io/ingress.class: nginx-internal
spec:
ingressClassName: nginx
defaultBackend:
service:
name: mydeploy-test-svc
port:
number: 80
rules:
- host: www.mytest.com # 基于host
http:
paths:
- path: /first # 基于路径
pathType: Prefix
backend:
service:
name: mydeploy-test-svc
port:
number: 80
- path: /second
pathType: Prefix
backend:
service:
name: mydeploy-test2-svc
port:
number: 80
- host: www.mytest2.com
http:
paths:
- path: /third(/|$)(.*) # 路径rewrite
pathType: ImplementationSpecific
backend:
service:
name: mydeploy-test3-svc
port:
number: 80
pathType
Exact (ExactPath
):
表示精确匹配,请求的路径必须与 Ingress 中定义的路径完全相同
path: /exact-path
pathType: Exact
这样的规则将匹配请求路径为 /exact-path
,而不会匹配 /exact-path/
或者 /exact-path/some-subpath
Prefix (PrefixPath
)
path: /prefix-path
pathType: Prefix
这样的规则将匹配请求路径为 /prefix-path
、/prefix-path/subpath
、/prefix-path/some/subpath
等。
并且会将整个匹配的所有内容传递给后端
ImplementationSpecific (ImplementationSpecificPath
)
表示使用 Ingress Controller 的特定实现来处理路径。这允许 Ingress Controller 使用自定义的路径匹配
Directory (DirectoryPath
)
表示将请求路径视为文件系统路径中的目录。这通常用于处理静态文件服务器。
path: /static
pathType: Directory
这样的规则将匹配请求路径为 /static
,并将其视为文件系统中的目录。
内外网ULB
在暴露Ingress Controller的LoadBalancer的svc中添加annotation
# 将Ingress Controller向外暴露的svc
apiVersion: v1
kind: Service
metadata:
annotations:
#"service.beta.kubernetes.io/ucloud-load-balancer-type": "inner" # 内网ULB
"service.beta.kubernetes.io/ucloud-load-balancer-type": "outer" # 外网ULB
"service.beta.kubernetes.io/ucloud-load-balancer-vserver-protocol": "tcp"
"service.beta.kubernetes.io/ucloud-load-balancer-eip-chargetype": "dynamic"
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/version: "1.2.1"
app.kubernetes.io/part-of: ingress-nginx
app.kubernetes.io/component: controller
name: ingress-nginx-controller
namespace: ingress-nginx
spec:
type: LoadBalancer
externalTrafficPolicy: Local
ports:
- name: http
port: 80
protocol: TCP
targetPort: http
appProtocol: http
- name: https
port: 443
protocol: TCP
targetPort: https
appProtocol: https
selector:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/component: controller
URL的rewrite功能
在ingress中配置
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app-test
namespace: test
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$2 # 开启路径重写,并重写指定的对象
# kubernetes.io/ingress.class: nginx
spec:
ingressClassName: nginx
defaultBackend:
service:
name: mydeploy-test-svc
port:
number: 80
rules:
- host: www.mytest2.com
http:
paths:
- path: /third(/|$)(.*) # 路径匹配
pathType: ImplementationSpecific
backend:
service:
name: mydeploy-test3-svc
port:
number: 80
这样只会将匹配到的内容传递给后端,不会传递test1、test2
TLS支持
创建证书secret
# 创建一个自签名证书和私钥
HOST=www.mytest.com
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/CN=${HOST}/O=${HOST}"
# 根据证书创建secret
kubectl create secret tls mytest-tls --key tls.key --cert tls.crt -n test
ingress支持tls
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app-test
namespace: test
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$2
nginx.ingress.kubernetes.io/whitelist-source-range: 172.16.0.0/16,172.18.0.0/16 # 访问白名单
spec:
ingressClassName: nginx
defaultBackend:
service:
name: mydeploy-test-svc
port:
number: 80
tls: # 配置证书,可以添加多个
- hosts:
- www.mytest.com
secretName: mytest-tls
# - hosts:
# - www.mytest2.com
# secretName: mytest2-tls
rules:
- host: www.mytest.com
http:
paths:
- path: /first
pathType: Prefix
backend:
service:
name: mydeploy-test-svc
port:
number: 80
- path: /second
pathType: Prefix
backend:
service:
name: mydeploy-test2-svc
port:
number: 80
验证:
# 使用https访问
curl --insecure https://www.mytest.com/first
# 查看证书信息
curl -kv https://www.mytest.com/first
设置访问白名单
如果要设置只允许指定的 IP 地址访问,这可以通过添加 annotations 来实现,多个ip用逗号隔开
nginx.ingress.kubernetes.io/whitelist-source-range: 172.16.0.0/16,172.18.0.0/16
跨namespace访问服务
例如ns ns-a下想使用ns ns-b下的 hello 服务,那么可以在 ns ns-a 下创建一个 externalName 类型的服务来引入ns-b
apiVersion: v1
kind: Service
metadata:
name: hello-ns-b
namespace: ns-a
spec:
type: ExternalName
externalName: hello.ns-b.svc.cluster.local
Ingress Class
单个Ingress Controller的问题
一个集群里有一个 Ingress Controller
,再给它配上不同的 Ingress
规则,是否可以所有解决请求的路由和分发问题?
引起问题:
1.如果因为某些原因,需要引入不同的 Ingress Controller,则无法实现
2.Ingress 规则太多,都交给一个 Ingress Controller 处理会让它不堪重负
3.多个 Ingress 对象没有很好的逻辑分组方式,管理和维护成本很高
4.集群里有不同的租户,他们对 Ingress 的需求差异很大甚至有冲突,无法部署在同一个 Ingress Controller 上
IngressClass解决方案
所以,Kubernetes
就又提出了一个 Ingress Class
的概念,让它插在 Ingress
和 Ingress Controller
中间,作为流量规则和控制器的协调人,解除了 Ingress
和 Ingress Controller
的强绑定关系
现在,用户可以转向管理 Ingress Class
,用它来定义不同的业务逻辑分组,简化 Ingress
规则的复杂度。比如说,我们可以用 Class A
处理博客流量、Class B
处理短视频流量、Class C
处理购物流量
这些 Ingress
和 Ingress Controller
彼此独立,不会发生冲突,所以上面的那些问题也就随着 Ingress Class
的引入迎刃而解了
创建多个Ingress Controller
# 创建新的namespace
# 确保每个ingress controller的--controller-class= 和 --ingress-class 是不同的
apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
labels:
app.kubernetes.io/component: controller
app.kubernetes.io/instance: ingress-nginx-new
app.kubernetes.io/name: ingress-nginx-new
app.kubernetes.io/part-of: ingress-nginx-new
app.kubernetes.io/version: 1.8.2
name: nginx-new
spec:
controller: k8s.io/ingress-nginx-new
apigateway
概述
apigateway相对ingress的优势
Kubernetes Gateway API 是 Kubernetes 1.18 版本引入的一种新的 API 规范,Gateway API 会成为 Ingress 的下一代替代方案
-
Ingress 主要面向 HTTP 流量,Gateway API 提供更丰富的功能,支持 TCP、UDP、TLS 等,不仅仅是 HTTP
-
Ingress 的最小路由单元是路径,Gateway API 支持更细粒度的流量路由规则,可以精确到服务级别
-
具有更好的扩展性,通过 CRD 可以轻松地支持各种 Gateway 的自定义类型,更灵活
版本
v1beta1: 当前的主要迭代版本,可以在生产中使用 Gateway API
目前 beta 版本仅支持 HTTP 协议,其他的TCP 协议、UDP 协议、gRPC 协议、TLS 协议均为 alpha 版本
基本概念
GatewayClass:GatewayClass是一组共享通用配置和行为的 Gateway 集合,与 IngressClass、StorageClass 类似
Gateway:可以说成 GatewayClass 的具体实现,Gateway 资源是一个中间层,需要定义所要监听的端口、协议、TLS 配置等信息,可以将网络流量的管理和控制集中到一个中心化的位置,提高集群的可用性和安全性
Route:真实的路由,定义了特定协议的规则,用于将请求从 Gateway 映射到 Kubernetes 服务。目前只有 HTTPRoute 进入了 v1beta 版本。
GatewayClass
GatewayClass是一组共享通用配置和行为的 Gateway 集合,与 IngressClass、StorageClass 类似
通过部署 GatewayClass 绑定下游实现提供的 Controller,为集群提供一种网关能力
apiVersion: gateway.networking.k8s.io/v1beta1
kind: GatewayClass
metadata:
name: eg
spec:
controllerName: gateway.envoyproxy.io/gatewayclass-controller # 绑定的 Controller 名称
Gateway
Gateway可以说成是 GatewayClass 的具体实现,声明后由 GatewayClass 的基础设备提供商提供一个具体存在的 Pod,充当了进入 Kubernetes 集群的流量的入口,负责流量接入以及往后转发,同时还可以起到一个初步过滤的效果。
Gateway 资源是一个中间层,需要定义所要监听的端口、协议、TLS 配置等信息,可以将网络流量的管理和控制集中到一个中心化的位置,提高集群的可用性和安全性
配置完成后,由 GatewayClass 绑定的 Controller 为我们提供一个具体存在 Pod 作为流量入口
spec:
gatewayClassName: eg # 绑定的 GatewayClass 名称。
listeners: # 定义了一些监听项,供 Route 进行绑定
- allowedRoutes: # 定义流量转发范围
namespaces:
from: All # 允许 Gateway 往所有的 Namespace 的 Pod 转发流量
name: http # 监听项名称
port: 80 # 监听项所占用的端口
hostname: www.gateway.*.com # 定义一个域名,一般为泛域名、匹配来自该域名的流量
protocol: HTTP # 定义协议,HTTP或者HTTPS
- allowedRoutes:
namespaces:
from: All
name: https
port: 8443
protocol: HTTPS
tls: # 为 HTTPS 配置加密协议
mode: Terminate # 加密协议类型,Terminate 或 Passthrough
certificateRefs:
- kind: Secret
name: cafe-secret
namespace: default
mode类型:
-
Terminate :将加密的流量解密并将明文流量转发到后端服务。这种模式需要在网关处配置证书和密钥,以便对客户端和服务器之间的流量进行加密和解密,确保数据安全性。
-
Passthrough: 将加密的流量原样转发到后端服务。这种模式不需要在网关处配置证书和密钥,因为 TLS 连接只在后端服务处终止。这种模式适用于需要将 TLS 流量直接传递到后端服务的场景,如需要对后端服务进行更细粒度的访问控制或流量监控的情况。
HTTPRoute
真实的路由,定义了特定协议的规则,用于将请求从 Gateway 映射到 Kubernetes 服务。目前只有 HTTPRoute 进入了 v1beta 版本,是比较稳定的版本,后续 TCPRoute、UDPRoute、GRPCRoute、TLSRoute 等也会陆续进入 beta 版本达到生产可用
HTTPRoute定义详细的规则,将流量代理到对应的业务服务上
# HTTPRoute A
spec:
parentRefs: # 绑定 Gateway 监听项
- name: gateway # Gateway 资源名称
namespace: envoy # Gateway所在命名空间
sectionName: http # 监听项名称
hostnames: # 为路由配置域名
- "www.gateway.example.com" # 可配置泛域名,可配置多个
rules: # 配置详细的路由规则,可配置多个,下面有对各种规则类型的详细解析
- matches: # 匹配条件
- path: # 路径匹配
type: PathPrefix # 路径类型:Exact 完全匹配/PathPrefix 前缀匹配/RegularExpression 正则匹配
value: /gateway
filters: # 高级设置
- type: requestHeaderModifier # 加工请求头
requestHeaderModifier: # 支持 set 覆盖/add 添加/remove 删除
set:
- name: service
value: goodrain
- type: RequestRedirect # 请求重定向
requestRedirect:
scheme: https # 重定向所使用的协议,http/https
hostname: www.baidu.com # 重定向的域名
port: 8443 # 重定向所使用的端口
statusCode: 301 # 重定向状态码:301 永久的重定向/302 临时重定向
-----------------
# HTTPRoute B
spec:
parentRefs: # 绑定 Gateway 监听项
- name: gateway # Gateway 资源名称
namespace: envoy # Gateway所在命名空间
sectionName: https # 监听项名称
hostnames: # 为路由配置域名
- "www.gateway.example.com" # 可配置泛域名,可配置多个
rules: # 配置详细的路由规则,可配置多个,下面有对各种规则类型的详细解析
- matches: # 匹配条件
- headers: # 请求头匹配
- name: service
value: goodrain
backendRefs: # 后端路由
- name: goodrain-v1 # service 名称
port: 80 # service 端口
weight: 80 # 权重
- name: goodrain-v2
port: 80
weight: 20
规则类型:
-
matches:
- 由一个或多个匹配条件组成,这些匹配条件可以基于 HTTP 请求的各种属性(如请求方法、路径、头部、查询参数等)进行匹配,从而确定哪些请求应该被路由到该规则对应的后端服务。
-
filters:
-
对传入请求进行更细粒度的控制,例如修改请求的头部、转发请求到其他服务、将请求重定向到不同的 URL 等。
-
它们由一组规则组成,每个规则都包含一个或多个过滤器。这些过滤器可以在请求被路由到后端服务之前或之后进行处理,以实现各种不同的功能。
-
-
backendRefs:
-
用来指定后端服务的引用,它包含一个后端服务的列表,每个服务由名称和端口号组成
-
可以使用不同的负载均衡算法,将请求路由到后端服务的其中一个实例中,实现负载均衡。
-
HTTPRoute 的用法非常的灵活,可以通过将不同的规则组合搭配,来创建一条适合业务的路由
以上面的 yaml 为例,整体流量走向如下图所示,当 http 协议的请求流量进入后,按照规则匹配,流量会向下转发到 HTTPRoute A 的路由上,HTTPRoute A 按照规则顺序,先对请求进行加工处理添加请求头,之后将请求重定向到 HTTPRoute B 上,再由 HTTPRoute 将流量按照权重比例路由到对应的后端服务。
需要注意的是,规则集有优先级,当同时存在多个规则(rule)的时候,流量会从上往下进行匹配,只要有匹配上流量会直接代理到其对应的后端或重定向到对应的路由。
安装Envoy的apigateway
Gateway API CRD 和 Envoy Controller
安装 Gateway、HTTPRoute 及示例应用
kubectl apply -f https://github.com/envoyproxy/gateway/releases/download/v0.3.0/install.yaml
查看安装效果
# 查看安装的 CRD 资源
kubectl get crd |grep networking.k8s.io
gatewayclasses.gateway.networking.k8s.io 2023-11-29T06:07:25Z
gateways.gateway.networking.k8s.io 2023-11-29T06:07:26Z
grpcroutes.gateway.networking.k8s.io 2023-11-29T06:07:27Z
httproutes.gateway.networking.k8s.io 2023-11-29T06:07:30Z
referencegrants.gateway.networking.k8s.io 2023-11-29T06:07:31Z
tcproutes.gateway.networking.k8s.io 2023-11-29T06:07:32Z
tlsroutes.gateway.networking.k8s.io 2023-11-29T06:07:32Z
udproutes.gateway.networking.k8s.io 2023-11-29T06:07:34Z
# 查看安装的 envoy controller
kubectl get pod -n envoy-gateway-system
NAME READY STATUS RESTARTS AGE
envoy-gateway-68b6bbbf6b-jslxd 2/2 Running 0 7m55s
Gateway、HTTPRoute
安装 Gateway、HTTPRoute 及示例应用
kubectl apply -f https://github.com/envoyproxy/gateway/releases/download/v0.3.0/quickstart.yaml
内部 GatewayClass 资源
GatewayClass资源的 controllerName 属性字段配置绑定了 envoy 的 controller
apiVersion: gateway.networking.k8s.io/v1beta1
kind: GatewayClass
metadata:
name: eg
spec:
controllerName: gateway.envoyproxy.io/gatewayclass-controller
内部 Gateway 资源
Gateway资源的 gatewayClassName 属性字段配置绑定了 gatewayclass 资源名称 eg,同时提供了一个 对内监听端口为 80,协议类型为 http 的监听项。
apiVersion: gateway.networking.k8s.io/v1beta1
kind: Gateway
metadata:
name: eg
spec:
gatewayClassName: eg
listeners:
- name: http
protocol: HTTP
port: 80
内部的 HTTPRoute 资源
HTTPRoute资源的 parentRefs 属性字段配置绑定了 gateway 资源名称 eg。域名为 www.example.com,代理的后端服务类型选择了 service,名称为 backend,服务端口为 3000。
apiVersion: gateway.networking.k8s.io/v1beta1
kind: HTTPRoute
metadata:
name: backend
spec:
parentRefs:
- name: eg
hostnames:
- "www.example.com"
rules:
- backendRefs:
- group: ""
kind: Service
name: backend
port: 3000
weight: 1
matches:
- path:
type: PathPrefix
value: /
验证和查看效果
# 查看安装的 gatewayclass 资源
kubectl get gatewayclass
NAME CONTROLLER ACCEPTED AGE
eg gateway.envoyproxy.io/gatewayclass-controller True 27s
# 查看安装的 gateway 资源
kubectl get gateway
NAME CLASS ADDRESS PROGRAMMED AGE
eg eg 123.58.210.63 True 36s
# 查看安装的 httproute 资源
kubectl get httproute
NAME HOSTNAMES AGE
backend ["www.example.com"] 46s
#查看由 Controller 提供的流量入口 Pod
kubectl get pod -n envoy-gateway-system
NAME READY STATUS RESTARTS AGE
envoy-default-eg-64656661-7869c6db96-qddt4 1/1 Running 0 105s
envoy-gateway-68b6bbbf6b-jslxd 2/2 Running 2 (96m ago) 130m
# 查看路由解析地址,其中 LoadaBalancer 类型的 svc 便是解析地址
kubectl get svc -n envoy-gateway-system|grep LoadBalancer
envoy-default-eg-64656661 LoadBalancer 192.168.36.226 123.58.210.63 80:31235/TCP 2m10s
# 访问
# 先修改/etc/hosts,添加 123.58.210.63 www.example.com
curl www.example.com/test
{
"path": "/test",
"host": "www.example.com",
"method": "GET",
"proto": "HTTP/1.1",
"headers": {
"Accept": [
"*/*"
],
"User-Agent": [
"curl/8.2.1"
],
"X-Envoy-Expected-Rq-Timeout-Ms": [
"15000"
],
"X-Forwarded-Proto": [
"http"
],
"X-Request-Id": [
"5acefafc-473c-4108-8318-dc62260bf28e"
]
},
"namespace": "default",
"ingress": "",
"service": "",
"pod": "backend-86bc6f5c86-96nq9"
}
Case
内外网ULB的问题
在暴露Ingress Controller的LoadBalancer的svc中配置注解
apiVersion: v1
kind: Service
metadata:
annotations:
#"service.beta.kubernetes.io/ucloud-load-balancer-type": "inner" # 内网ULB
"service.beta.kubernetes.io/ucloud-load-balancer-type": "outer" # 外网ULB
向外暴露的svc如何限制IP访问
使用Ingress白名单
nginx.ingress.kubernetes.io/whitelist-source-range: 172.16.0.0/16,172.18.0.0/16
externalTrafficPolicy
客户咨询的问题
1.客户咨询Ingress Controller前面的LoadBalancer类型的svc对应的ULB,是否需要把rserver配置到vserver的列表内
答:
-
会自动同步,vserver不需要手动配置
-
externalTrafficPolicy: Cluster的配置下所有节点都会加进去
-
externalTrafficPolicy: Local的配置下只有nginx-ingress-controller-xxx所在的节点会加进去
2.客户反馈在k8s master1节点上通过LoadBalancer的外网ip无法访问ingress服务,只有一个节点可以访问
答:
-
LB的svc的externalTrafficPolicy设置成了Load,这样只有controller所在的节点被加入后端,从而只有nginx-ingress-controller-xxx所在的节点能访问服务
-
将其改成Cluster即可,这样会将所有节点加入后端
Local和Cluster的区别
Cluster模式:默认模式,Kube-proxy不管容器实例在哪,公平转发;Kube-proxy转发时会替换掉报文的源IP,因为它会做一次SNAT
Local模式:只转发给本机的容器,绝不跨节点转发;但是实际上各个节点上的端口都是开着的,只是不接受本节点以外的流量请求这个端口;
配置证书
LoadBalancer的ULB上通过注解 service.beta.kubernetes.io/ucloud-load-balancer-vserver-ssl-cert
配置了证书