0%

【Kubernetes解读】Ingress

Ingress 是 Kubernetes 的一种 API 对象,将集群内部的 Service 通过 HTTP/HTTPS 方式暴露到集群外部,并通过规则定义 HTTP/HTTPS 的路由。Ingress 具备如下特性:集群外部可访问的 URL、负载均衡、SSL Termination、按域名路由。

Background

Kubernetes 暴露服务的有三种方式:分别为 LoadBlancer Service、NodePort Service、Ingress。

LoadBlancer Service 是 Kubernetes 结合云平台的组件,如国外 GCE、AWS、国内阿里云等等,使用它向使用的底层云平台申请创建负载均衡器来实现,有局限性,对于使用云平台的集群比较方便,但是和云平台强绑定。

NodePort Service 是通过在节点上暴露端口,然后通过将端口映射到具体某个服务上来实现服务暴露,比较直观方便,但是对于集群来说,随着 Service 的不断增加,需要的端口越来越多,很容易出现端口冲突,而且不容易管理。此外,打开节点的端口,也会面临安全上的风险,生产环境并不推荐使用。

Ingress 作为 Kubernetes 基本API对象,是外部请求访问集群的入口,负责为进入集群的请求提供路由规则集合和转发,将外部的请求转发到集群内部Service上。相对于上述两种暴露方式,有以下特点:

  • 动态配置服务:增加新的服务时,不再需要在流量入口新增反向代理指向新的服务,只需配置好Ingress即可
  • 减少不必要端口暴露:只需要将Ingress服务映射出去,即可代理所有后端服务,更加安全,便于管理端口

Ingress

组成模块

Ingress 通过反向代理负载均衡服务器实现对外暴露服务目的,一般有三个组件组成:

  • 反向代理负载均衡服务器:拦截外部请求,通常以 Deployment 或者 DaemonSet 的方式部署到集群中,常见的有 NginxApacheTraefik等。
  • Ingress:定义路由规则,将路由配置抽象成一个 Ingress 对象。
  • Ingress Controller:实时监控集群,获取 ServicePodIngress等的变化,将 Ingress 的规则动态更新到反向代理负载均衡服务器上,刷新其路由配置信息,实现 服务发现

Kubernetes Ingress

语法详解

下面是一个Ingress资源示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-demo
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- http:
paths:
- path: /testpath
pathType: Prefix
backend:
service:
name: test
port:
number: 80
- host: "foo.bar.com"
http:
paths:
- pathType: Prefix
path: "/bar"
backend:
service:
name: service1
port:
number: 80
- host: "*.foo.com"
http:
paths:
- pathType: Prefix
path: "/foo"
backend:
service:
name: service2
port:
number: 80

与所有其他 Kubernetes 资源一样,Ingress 需要使用 apiVersionkindmetadata 字段。 Ingress 对象的命名必须是合法的 DNS 子域名名称。 Ingress 经常使用注解(annotations)来配置一些选项,具体取决于 Ingress 控制器, 不同的 Ingress 控制器 支持不同的注解。

Ingress 规约 提供了配置负载均衡器或者代理服务器所需的所有信息。 最重要的是,其中包含与所有传入请求匹配的规则列表。 Ingress 资源仅支持用于转发 HTTP 流量的规则。

Ingress Rules

每个 HTTP 规则都包含以下信息:

  • 可选主机。在此示例第一个规则中,未指定主机,因此该规则适用于通过指定 IP 地址的所有入站 HTTP 通信。 如果提供了主机(例如 foo.bar.com),则规则适用于该主机。
  • 路径列表(例如,/testpath),每个路径都有一个由 serviceNameservicePort 定义的关联后端。 在负载均衡器将流量定向到引用的服务之前,主机和路径都必须匹配传入请求的内容。
  • 后端是 Service 文档中所述的服务和端口名称的组合。 与规则的主机和路径匹配的对 Ingress 的 HTTP(和 HTTPS )请求将发送到列出的后端。

通常在 Ingress 控制器中会配置默认后端,以服务任何不符合规范中路径的请求。

DefaultBackend

没有规则的 Ingress 将所有流量发送到同一个默认后端。 默认后端通常是 Ingress 控制器 的配置选项,并且未在 Ingress 资源中指定。

如果主机或路径都没有与 Ingress 对象中的 HTTP 请求匹配,则流量将路由到默认后端。

Path Types

Ingress 中的每个路径都有对应的路径类型。当前支持的路径类型有三种:

  • ImplementationSpecific (默认):对于这种类型,匹配取决于 IngressClass。 具体实现可以将其作为单独的 pathType 处理或者与 PrefixExact 类型作相同处理。
  • Exact:精确匹配 URL 路径,且对大小写敏感。
  • Prefix:基于以 / 分隔的 URL 路径前缀匹配。匹配对大小写敏感,并且对路径中的元素逐个完成。 路径元素指的是由 / 分隔符分隔的路径中的标签列表。 如果每个 p 都是请求路径 p 的元素前缀,则请求与路径 p 匹配。

Traefik实战

Traefik是一个用Go语言开发的轻量级的Http反向代理和负载均衡器,能够监听后端的变化并自动更新服务配置。它的特点如下:

  • 天然拥抱kubernetes,直接与集群k8s的Api Server通信,反应非常迅速,实时感知集群中Ingress定义的路由规则集合和后端ServicePod的变化,自动热更新Traefik后端配置,根本不用创建Ingress controller对象
  • 提供了友好的控制面板和监控界面,不仅可以方便地查看Traefik根据Ingress生成的路由配置信息,还可以查看统计的一些性能指标数据
  • 支持丰富的annotations配置,可配置众多出色的特性,例如:自动熔断负载均衡策略黑名单白名单
  • 支持许多后端存储,如:zookeeper、eureka、consul、rancher、docker等,它会自动感知这些统一配置中心的变化,热更新自己的路由配置

如何在 K8S 集群中部署 Traefik Ingress Controller 详细介绍了如何在 k8s 集群部署 Traefik Ingress Controller 的详细过程,本文在此复现其操作。

前期准备

在本文的后半部分,我将演示如何在一个 Kubernetes 集群部署 Traefik 作为 Ingress Controller,同时使用 Ingress 实现按域名路由和流量分配。在此之前,你需要一个已经部署成功的 Kubernetes 集群和一个能够与集群通信的kubectl 工具。

软文时间:你可以在MiniKube创建运行 Kubernetes 集群,也欢迎使用腾讯云的TKE容器服务,快速实现1分钟内自动创建好 Kubernetes 集群。

启用RBAC

为了能够使 Traefik 能够访问集群中运行的 Pod、Endpoint、Ingress和 Service等资源,需要向 Traefik 授予一些权限。这里我们创建了一个具有一组权限的ClusterRole,授予其管理和监视集群中所有命名空间的资源。同时,我们创建一个新的ServiceAccount,为Traefik 提供集群中的身份。最后,通过 ClusterRoleBinding 将二者绑定在一起。

rbac.yam
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
apiVersion: v1
kind: ServiceAccount
metadata:
name: traefik-ingress
namespace: kube-system
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: traefik-ingress
rules:
- apiGroups:
- ""
resources:
- services
- endpoints
- secrets
verbs:
- get
- list
- watch
- apiGroups:
- extensions
resources:
- ingresses
verbs:
- get
- list
- watch
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: traefik-ingress
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: traefik-ingress
subjects:
- kind: ServiceAccount
name: traefik-ingress
namespace: kube-system

在命令行执行以下命令:

1
2
3
4
[root@VM-1-28-centos traefik]# kubectl apply -f rbac.yaml
serviceaccount/traefik-ingress created
clusterrole.rbac.authorization.k8s.io/traefik-ingress created
clusterrolebinding.rbac.authorization.k8s.io/traefik-ingress created

部署Traefik

官方 Traefik 文档支持三种类型的部署:使用 Deployment 对象、使用 DaemonSet 对象或使用 Helm Chart。这里我们使用 Deployment manifest。

deployment.yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
apiVersion: extensions/v1beta1
metadata:
name: traefik-ingress
namespace: kube-system
labels:
k8s-app: traefik-ingress-lb
spec:
replicas: 1
selector:
matchLabels:
k8s-app: traefik-ingress-lb
template:
metadata:
labels:
k8s-app: traefik-ingress-lb
name: traefik-ingress-lb
spec:
serviceAccountName: traefik-ingress
terminationGracePeriodSeconds: 60
containers:
- image: traefik:v1.7.16
name: traefik-ingress-lb
ports:
- name: http
containerPort: 80
- name: admin
containerPort: 8080
args:
- --api
- --kubernetes
- --logLevel=INFO

在命令行执行以下命令:

1
2
3
4
5
[root@VM-1-28-centos traefik]# kubectl apply -f deployment.yaml
deployment.extensions/traefik-ingress created
[root@VM-1-28-centos traefik]# kubectl get pods -n kube-system
NAME READY STATUS RESTARTS AGE
traefik-ingress-56f459f5cb-psrhr 1/1 Running 0 16s

为外部访问创建NodePorts

接下来创建一个服务来从集群外部访问 Traefik,这里我们只暴露Traefik给外部,其他的内部服务都可以通过定义Ingress规则来通过 Traefik 暴露出来,极大避免了 NodePort的冲突。在实际生产环境中,这里的 NodePort 可以替换成云服务商提供的LB。

service.yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
kind: Service
apiVersion: v1
metadata:
name: traefik-ingress-service
namespace: kube-system
spec:
selector:
k8s-app: traefik-ingress-lb
ports:
- protocol: TCP
port: 80
name: web
- protocol: TCP
port: 8080
name: admin
type: NodePort

在命令行执行以下命令:

1
2
3
4
5
6
7
[root@VM-1-28-centos traefik]# kubectl create -f service.yaml
service/traefik-ingress-service created
[root@VM-1-28-centos traefik]# kubectl get svc -n kube-system
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
hpa-metrics-service ClusterIP 172.18.254.168 <none> 443/TCP 51d
kube-dns ClusterIP 172.18.253.26 <none> 53/TCP,53/UDP 51d
traefik-ingress-service NodePort 172.18.253.6 <none> 80:30993/TCP,8080:31846/TCP 2s

这个时候,已经可以在浏览器访问 Traefik 服务了,在浏览器输入 http://<NodeIP>:<AdminNodePort> 就可以看到 Traefik 的 WebUI了。

这个时候WebUI还没有管理任何Ingress,接下来我们创建Ingress。

实现按域名的路由

animals-ingress.yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: animals
annotations:
kubernetes.io/ingress.class: traefik
spec:
rules:
- host: hare.houmin
http:
paths:
- path: /
backend:
serviceName: hare
servicePort: http
- host: bear.houmin
http:
paths:
- path: /
backend:
serviceName: bear
servicePort: http
- host: moose.houmin
http:
paths:
- path: /
backend:
serviceName: moose
servicePort: http

在命令行执行命令:

1
2
[root@VM-1-28-centos traefik]# kubectl create -f animals-ingress.yaml
ingress.extensions/animals created

这个时候只创建了 Ingress,还没有创建 Frontend 的 Service 和后端服务的 EndPoint,查看Traefik看到

创建对应的 Service:

animals-service.yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
---
apiVersion: v1
kind: Service
metadata:
name: bear
spec:
ports:
- name: http
targetPort: 80
port: 80
selector:
app: animals
task: bear
---
apiVersion: v1
kind: Service
metadata:
name: moose
spec:
ports:
- name: http
targetPort: 80
port: 80
selector:
app: animals
task: moose
---
apiVersion: v1
kind: Service
metadata:
name: hare
annotations:
traefik.backend.circuitbreaker: "NetworkErrorRatio() > 0.5"
spec:
ports:
- name: http
targetPort: 80
port: 80
selector:
app: animals
task: hare

在命令行执行命令:

1
2
3
4
[root@VM-1-28-centos traefik]# kubectl create -f animals-service.yaml
service/bear created
service/moose created
service/hare created

这个时候 Ingress 中 Frontend 对应的 Service已经正常工作,但是 Backend 的EndPoint还没有就绪:

修改本机Host,添加记录如下:

1
<NodeIP> hare.houmin moose.houmin bear.houmin

这个时候在浏览器访问 http://hare.houmin:<WebNodePort>,在这里 WebNodePort 也就对应于 30993,显示 Service Unavailable。符合预期,因为Backend的Endpoint还没有起来。

创建对应的 Deployment:

animals-deployment.yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
---
kind: Deployment
apiVersion: extensions/v1beta1
metadata:
name: bear
labels:
app: animals
animal: bear
spec:
replicas: 2
selector:
matchLabels:
app: animals
task: bear
template:
metadata:
labels:
app: animals
task: bear
version: v0.0.1
spec:
containers:
- name: bear
image: supergiantkir/animals:bear
ports:
- containerPort: 80
---
kind: Deployment
apiVersion: extensions/v1beta1
metadata:
name: moose
labels:
app: animals
animal: moose
spec:
replicas: 2
selector:
matchLabels:
app: animals
task: moose
template:
metadata:
labels:
app: animals
task: moose
version: v0.0.1
spec:
containers:
- name: moose
image: supergiantkir/animals:moose
ports:
- containerPort: 80
---
kind: Deployment
apiVersion: extensions/v1beta1
metadata:
name: hare
labels:
app: animals
animal: hare
spec:
replicas: 2
selector:
matchLabels:
app: animals
task: hare
template:
metadata:
labels:
app: animals
task: hare
version: v0.0.1
spec:
containers:
- name: hare
image: supergiantkir/animals:hare
ports:
- containerPort: 80

在命令行中执行命令:

1
2
3
4
[root@VM-1-28-centos traefik]# kubectl apply -f animals-deployment.yaml
deployment.extensions/bear created
deployment.extensions/moose created
deployment.extensions/hare created

查看WebUI,显示后端EndPoint服务已就绪

这个是否分别访问Bear、Hare和Moose服务: