【转】kubernetes之ingress和Ingress Controller

 

一、什么是Ingress?

Kubernetes 暴露服务的方式目前只有三种:LoadBlancer Service、NodePort Service、Ingress

 

1、Pod 漂移问题

在 Kubernetes 中,随着 Pod 的创建和销毁,Pod IP 肯定会动态变化;那么如何把这个动态的 Pod IP 暴露出去?可以借助于 Kubernetes 的 Service 机制,Service 可以以标签的形式选定一组带有指定标签的 Pod,并监控和自动负载他们的 Pod IP,那么我们向外只暴露 Service IP 就行了;

NodePort 模式:在每个节点上开起一个端口,然后转发到内部 Pod IP 上,访问方式:http://nodeip:nodeport,如下图所示:
 

 

 

2、端口管理问题

采用 NodePort 方式暴露服务面临的问题是,服务一旦多起来,NodePort 在每个节点上开启的端口会极其庞大,而且难以维护;

这时,我们能否使用一个Nginx直接对内进行转发呢?在集群内,Pod与Pod之间是可以互相通信的,而Pod是可以共享宿主机的网络名称空间(hostNetwork方式)的,也就是说当Pod共享宿主机的网络名称空间(hostNetwork方式)时,Pod上所监听的就是Node上的端口。那么这又该如何实现呢?简单的实现就是使用 DaemonSet 在每个 Node 上监听 80,然后写好规则,因为 Nginx 外面绑定了宿主机 80 端口(就像 NodePort),本身又在集群内,那么向后直接转发到相应 Service IP 就行了,如下图所示:

 

 

3、域名分配及动态更新问题

从上面的方法,采用 Nginx-Pod 似乎已经解决了问题,但是其实这里面有一个很大缺陷:当每次有新服务加入又该如何修改 Nginx 配置呢??我们知道使用Nginx可以通过虚拟主机域名进行区分不同的服务,而每个服务通过upstream进行定义不同的负载均衡池,再加上location进行负载均衡的反向代理,在日常使用中只需要修改 nginx.conf 即可实现,那在K8S中又该如何实现这种方式的调度呢???

假设后端的服务初始服务只有ecshop,后面增加了bbs和member服务,那么又该如何将这2个服务加入到Nginx-Pod进行调度呢?总不能每次手动改或者Rolling Update 前端 Nginx Pod 吧!!此时 Ingress 出现了。

Ingress 包含两大组件:Ingress Controller 和 Ingress。

Ingress 简单的理解就是你原来需要改 Nginx 配置,然后配置各种域名对应哪个 Service,现在把这个动作抽象出来,变成一个 Ingress 对象,你可以用 yaml 创建,每次不要去改 Nginx 了,直接改 yaml 然后创建/更新就行了;

Ingress Controoler 通过与 Kubernetes API 交互,动态的去感知集群中 Ingress 规则变化,并按照规则模板生成一段 Nginx 配置,再写到 Nginx Pod 里,最后 reload 一下,工作流程如下图:

 

 

二、Ingress的负载均衡机制

k8s提供了两种内建的云端负载均衡机制用于发布公共应用,一种是工作于传输层的service资源,它实现的是TCP负载均衡器,另一种是Ingress资源,它实现的是HTTP(S)负载均衡器。

1)TCP负载均衡器

无论是iptables还是ipvs模型的service资源都配置于Linux内核中的netfilter之上进行四层调度,是一种类型更为通用的调度器,支持调度HTTP、MYSQL等应用层服务。不过,也正是由于工作于传输层从而使得它无法做到https及ssl会话支持等,也不支持基于url的请求调度机制,而且,k8s也不支持为此类负载均衡器配置任何类型的健康检查机制。

2)HTTP(S)负载均衡器

HTTP(S)负载均衡器是应用层负载均衡机制的一种,支持根据环境做出更好的调度决策。与传输层调度器相比,它提供了诸如可自定义url映射和tls卸载等功能,并支持多种类型的后端服务器监控状态检测机制。

Ingress 和 Ingress Controller

service资源和pod资源的IP地址仅能用于集群网络内部的通信,所有的网络流量都无法穿透边界路由器以实现集群内外通信。尽管可以为service使用NodePort或LoadBalancer类型通过节点引入外部流量,但它依然是4层流量转发,可用的负载均衡器也为传输层负载均衡机制。

Ingress是k8s的标准资源类型之一,它其实就是一组基于DNS名称或URL路径把请求转发至指定的service资源的规则,用于将集群外部的请求流量转发至集群内部完成服务发布。然而,Ingress资源自身并不能进行流量穿透,它仅是规则的集合,这些规则要想真正发挥作用还需要其他功能的辅助,如监听某套接字,然后根据这些规则的匹配机制路由请求流量。这种能够为Ingress资源监听套接字并转发流量的组件称为Ingress控制器。

Ingress控制器可以由任何具有反向代理功能的服务程序实现,如Nginx、Envoy和Traefik等。Ingress控制器自身也是运行于集群中的pod资源对象,它与被代理的运行为pod资源的应用运行于同一网络中。使用Ingress资源进行流量分发时,Ingress控制器可基于某Ingress资源定义的规则将客户端的请求流量直接转发至与service对应的后端pod资源之上,这种转发机制会绕过service资源,从而省去了由kube-proxy实现的端口代理开销。Ingress规则需要由一个service资源对象辅助识别相关的所有pod对象,但Ingress-nginx控制器可经由api.ilinux.io规则的定义直接将请求流量调度至后端pod上,而无须经由service对象api的再次转发。

 

三、如何创建Ingress资源

Ingress资源时基于HTTP虚拟主机或URL的转发规则,需要强调的是,这是一条转发规则。资源清单中annotation用于识别其所属的Ingress控制器的类型,这一点在集群中部署有多个Ingress控制器时尤为重要。

Ingress 中的spec字段是Ingress资源的核心组成部分,主要包含以下3个字段:

  • rules:用于定义当前Ingress资源的转发规则列表;由rules定义规则,或没有匹配到规则时,所有的流量会转发到由backend定义的默认后端。
  • backend:默认的后端,用于服务那些没有匹配到任何规则的请求;定义Ingress资源时,必须要定义backend或rules两者之一,该字段用于让负载均衡器指定一个全局默认的后端。
  • tls:TLS配置,目前仅支持通过默认端口443提供服务,如果要配置指定的列表成员指向不同的主机,则需要通过SNI TLS扩展机制来支持该功能。

rules

rules对象由一系列的配置的Ingress资源的host规则组成,这些host规则用于将一个主机上的某个URL映射到相关后端Service对象,其定义格式如下:

1 spec:
2   rules:
3   - hosts: <string>
4     http:
5       paths:
6       - path:
7         backend:
8           serviceName: <string>
9           servicePort: <string>
View Code

 

需要注意的是,.spec.rules.host 属性值,目前暂不支持使用IP地址定义,也不支持IP:Port的格式,若该字段留空,代表着通配所有主机名。

backend

backend对象的定义由2个必要的字段组成:serviceName和servicePort,分别用于指定流量转发的后端目标Service资源名称和端口。

tls

tls对象由2个内嵌的字段组成,仅在定义TLS主机的转发规则上使用。

  • hosts: 包含 于 使用 的 TLS 证书 之内 的 主机 名称 字符串 列表, 因此, 此处 使用 的 主机 名 必须 匹配 tlsSecret 中的 名称。
  • secretName: 用于 引用 SSL 会话 的 secret 对象 名称, 在 基于 SNI 实现 多 主机 路 由 的 场景 中, 此 字段 为 可选。

 

四、Ingress资源类型

基于http暴露的每个service资源均可发布于一个独立的FQDN主机名之上,如www.ik8s.io;也可发布于某主机的URL路径之上,从而将它们整合到同一个web站点,如www.ik8s.io/frafana。至于是否需要发布为https类型的应用则取决于用户的业务需求。

Ingress的资源类型有以下4种:

  • 1、单Service资源型Ingress
  • 2、基于URL路径进行流量转发
  • 3、基于主机名称的虚拟主机
  • 4、TLS类型的Ingress资源

1、单Service资源型Ingress

暴露单个服务的方法有多种,如NodePort、LoadBanlancer等等,当然也可以使用Ingress来进行暴露单个服务,只需要为Ingress指定default backend即可,如下示例:

1 apiVersion: extensions/v1beta1
2 kind: Ingress
3 metadata:
4   name: my-ingress
5 spec:
6   backend:
7     serviceName: my-svc
8     servicePort: 80
View Code

 

Ingress控制器会为其分配一个IP地址接入请求流量,并将其转发至后端my-svc。

2、基于url路径进行流量分发

垂直拆分或微服务架构中,每个小的应用都有其专用的service资源暴露服务,但在对外开放的站点上,它们可能是财经、新闻、电商等一类的独立应用,可通过主域名的url路径分别接入,用于发布集群内名称为API和WAP的service资源。于是,可对应地创建一个如下的Ingress资源:

 1 apiVersion: extensions/v1beta1
 2 kind: Ingress
 3 metadata:
 4   name: ingress-url-demo
 5   annotations:
 6      nginx.ingress.kubernetes.io/rewrite-target: /
 7 spec:
 8   rules:
 9   - host: myapp.syztoo.com
10     http:
11       paths:
12       - path: /v1
13         backend:
14           serviceName: myappv1-svc
15           servicePort: 80
16       - path: /v2
17         backend:
18           serviceName: myappv2-svc
19           servicePort: 80
20  
21 ---
22 apiVersion: v1
23 kind: Service
24 metadata:
25   name: myappv1-svc
26   namespace: default
27 spec:
28   selector:
29     app: myappv1
30     release: canary
31   type: ClusterIP
32   ports: 
33   - port: 80
34     targetPort: 80
35  
36 ---
37 apiVersion: apps/v1
38 kind: Deployment
39 metadata:
40   name: myappv1-depoly
41   namespace: default
42 spec:
43   replicas: 2
44   selector:
45     matchLabels:
46       app: myappv1
47       release: canary
48   template:
49     metadata:
50       labels: 
51         app: myappv1
52         release: canary
53     spec:
54       containers:
55       - name: myappv1
56         image: ikubernetes/myapp:v1
57         ports:
58         - name: http
59           containerPort: 80
60  
61 ---
62 apiVersion: v1
63 kind: Service
64 metadata:
65   name: myappv2-svc
66   namespace: default
67 spec:
68   selector:
69     app: myappv2
70     release: canary
71   type: ClusterIP
72   ports: 
73   - port: 80
74     targetPort: 80
75  
76 ---
77 apiVersion: apps/v1
78 kind: Deployment
79 metadata:
80   name: myappv2-deploy
81   namespace: default
82 spec:
83   replicas: 2
84   selector:
85     matchLabels:
86       app: myappv2
87       release: canary
88   template:
89     metadata:
90       labels: 
91         app: myappv2
92         release: canary
93     spec:
94       containers:
95       - name: myappv2
96         image: ikubernetes/myapp:v2
97         ports:
98         - name: http
99           containerPort: 80
View Code

 

 

3、基于主机名称的虚拟主机

 1 apiVersion: extensions/v1beta1
 2 kind: Ingress
 3 metadata:
 4   name: test
 5 spec:
 6   rules:
 7   - host: api.ik8s.io
 8     http: 
 9       paths:
10       - backend:
11           serviceName: api
12           servicePort: 80
13   - host: wap.ik8s.io
14     http: 
15       paths:
16       - backend:
17           serviceName: wap
18           servicePort: 80
View Code

 

4、TLS类型的Ingress资源

这种类型用于以https发布service资源,基于一个含有私钥和证书的secret对象即可配置TLS协议的Ingress资源,目前来说,Ingress资源仅支持单TLS端口,并且还能卸载TLS会话,在Ingress资源中引用此secret即可让Ingress控制器加载并配置为https服务。

 1 apiVersion: extensions/v1beta1
 2 kind: Ingress
 3 metadata:
 4   name: no-rules-map
 5 spec: 
 6   tls:
 7   - secretName: ikubernetesSecret
 8   backend:
 9     serviceName: homesite
10     servicePort: 80
View Code

 

 

五、部署Ingress控制器(Nginx)

1、部署Ingress controller

下载 官网 配置清单

[root@master ~]# mkdir ingress-nginx
[root@master ~]# cd ingress-nginx/
[root@master ingress-nginx]# wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/mandatory.yaml

 部署:

[root@master ingress-nginx]# kubectl apply -f mandatory.yaml

 

ingress-controller 使用的镜像是 quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.25.1,下载的时间较长,耐心等待即可,也可提前准备好镜像。

[root@master ingress-nginx]# kubectl get pods -n ingress-nginx
NAME                                        READY   STATUS    RESTARTS   AGE
nginx-ingress-controller-79f6884cf6-8nb6f   1/1     Running   0          35m

 

2、定义 ingress-controller 的 service 资源

运行为pod资源的Ingress控制器进程接入外部的请求流量常用的有两种解决方案:

1)以deployment控制器管理Ingress控制器的pod资源,并通过NodePort或LoadBalancer类型的service对象为其接入集群外部的请求流量,这就意味着,定义一个Ingress控制器时,必须在其前端定义一个专用的service资源

2)借助于DaemonSet控制器,将Ingress控制器的pod资源各自以单一实例的方式运行于集群的所有或部分工作节点之上,并配置这类pod对象以hostPort或hostNetwork的方式在当前节点接入外部流量。

这里使用nodePort方式,下载官网nodePort文件:

[root@master ingress-nginx]# wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/provider/baremetal/service-nodeport.yaml
 1 [root@master ingress-nginx]# vim service-nodeport.yaml
 2 apiVersion: v1
 3 kind: Service
 4 metadata:
 5   name: ingress-nginx
 6   namespace: ingress-nginx
 7   labels:
 8     app.kubernetes.io/name: ingress-nginx
 9     app.kubernetes.io/part-of: ingress-nginx
10 spec:
11   type: NodePort
12   ports:
13     - name: http
14       port: 80
15       targetPort: 80
16       protocol: TCP
17       nodePort: 30080
18     - name: https
19       port: 443
20       targetPort: 443
21       protocol: TCP
22       nodePort: 30443
23   selector:
24     app.kubernetes.io/name: ingress-nginx
25     app.kubernetes.io/part-of: ingress-nginx
26  
27 ---
View Code

 

上面的文件中,使用了宿主机的固定端口(30080,30443),需保证宿主机的这两个端口未占用。

[root@master ingress-nginx]# kubectl apply -f service-nodeport.yaml 
service/ingress-nginx created
[root@master ingress-nginx]# kubectl get svc -n ingress-nginx
NAME            TYPE       CLUSTER-IP     EXTERNAL-IP   PORT(S)                      AGE
ingress-nginx   NodePort   10.106.97.57   <none>        80:30080/TCP,443:30443/TCP   13s

3、定义Ingress

[root@master resourcelist]# vim ingress-demo.yaml
 1 apiVersion: extensions/v1beta1  # api版本
 2 kind: Ingress  # 清单类型
 3 metadata:  # 元数据
 4   name: ingress-myapp  # ingress的名称
 5   namespace: default  # 所属名称空间
 6   annotations:  # 注解信息
 7     kubernetes.io/ingress.class: "nginx"
 8 spec:  # 规格
 9   rules:  # 定义后端转发的规则
10   - host: myapp.magedu.com  # 通过域名进行转发
11     http:
12       paths:
13       - path:  # 配置访问路径,如果通过url进行转发,需要修改;空默认为访问的路径为"/"
14         backend:  # 配置后端服务
15           serviceName: myapp
16           servicePort: 80
17  
18 ---
19 apiVersion: v1
20 kind: Service
21 metadata:
22   name: myapp
23   namespace: default
24 spec:
25   selector:
26     app: myapp
27     release: canary
28   type: ClusterIP
29   ports:
30   - port: 80
31     targetPort: 80
32  
33 ---
34 apiVersion: apps/v1
35 kind: Deployment
36 metadata:
37   name: myapp-deploy
38   namespace: default
39 spec:
40   replicas: 5
41   selector:
42     matchLabels:
43       app: myapp
44       release: canary
45   template:
46     metadata:
47       labels:
48         app: myapp
49         release: canary
50     spec:
51       containers:
52       - name: myapp
53         image: ikubernetes/myapp:v2
54         ports:
55         - name: http
56           containerPort: 80
View Code
[root@master resourcelist]# kubectl apply -f ingress-demo.yaml              
ingress.extensions/ingress-myapp created
service/myapp created
deployment.apps/myapp-deploy created

 

修改本地hosts文件,进行测试访问:

192.168.56.12 myapp.magedu.com
192.168.56.13 myapp.magedu.com

4、总结
从前面的部署过程中,可以再次进行总结部署的流程如下:
①到官网下载ingress-controller一键部署文件(mandatory.yaml);
②部署Ingress-controller的service,以实现接入集群外部流量;
③部署后端的服务,如myapp,并通过service进行暴露;
④定义Ingress规则,使Ingress-controller和后端服务的Pod组进行关联。 

 

 

六、构建TLS站点

(1)准备证书

[root@master ingress]# openssl genrsa -out tls.key 2048 
Generating RSA private key, 2048 bit long modulus
.......+++
.......................+++
e is 65537 (0x10001)
 
[root@master ingress]# openssl req -new -x509 -key tls.key -out tls.crt -subj /C=CN/ST=Beijing/L=Beijing/O=DevOps/CN=myapp.magedu.com

 

(2)生成secret

[root@master ingress]# kubectl create secret tls myapp-ingress-secret --cert=tls.crt --key=tls.key
secret/tomcat-ingress-secret created
[root@master ingress]# kubectl get secret
NAME                   TYPE                                  DATA   AGE
default-token-9qfpd    kubernetes.io/service-account-token   3      6d22h
myapp-ingress-secret   kubernetes.io/tls                     2      21m

 

(3)创建ingress

cat ingress-tls-demo.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: ingress-myapp-tls
  namespace: default
  annotations:
    kubernetes.io/ingress.class: "nginx"
spec:
  tls:
  - hosts:
    - myapp.magedu.com
    secretName: tomcat-ingress-secret
  rules:
  - host: myapp.magedu.com
    http:
      paths:
      - path:
        backend:
          serviceName: myapp
          servicePort: 80
 
---
apiVersion: v1
kind: Service
metadata:
  name: myapp
  namespace: default
spec:
  selector:
    app: myapp
    release: canary
  type: ClusterIP
  ports:
  - port: 80
    targetPort: 80
 
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp-deploy
  namespace: default
spec:
  replicas: 3
  selector:
    matchLabels:
      app: myapp
      release: canary
  template:
    metadata:
      labels:
        app: myapp
        release: canary
    spec:
      containers:
      - name: myapp
        image: ikubernetes/myapp:v2
        ports:
        - name: http
          containerPort: 80
[root@master resourcelist]# kubectl apply -f ingress-tls-demo.yaml
ingress.extensions/ingress-myapp-tls created
service/myapp created
deployment.apps/myapp-deploy created
[root@master resourcelist]# kubectl get ingress
NAME                HOSTS              ADDRESS   PORTS     AGE
ingress-myapp-tls   myapp.magedu.com             80, 443   10s

 

(4)访问测试:https://myapp.magedu.com:30443

 

 =========================================================================================================

k8s修改ingress默认80端口

有时候80端口被其他服务占用,我们想修改ingress的默认端口

 

kubectl edit daemonset nginx-ingress-controller -n ingress-nginx

 



containers:
- args:
- /nginx-ingress-controller
- --default-backend-service=$(POD_NAMESPACE)/default-http-backend
- --configmap=$(POD_NAMESPACE)/nginx-configuration
- --tcp-services-configmap=$(POD_NAMESPACE)/tcp-services
- --udp-services-configmap=$(POD_NAMESPACE)/udp-services
- --annotations-prefix=nginx.ingress.kubernetes.io

 




在后面加上

- --http-port=8080
- --https-port=8443 

 


删除pod,k8s将重新使用新配置,生成一个新的pod

posted @ 2021-03-23 10:39  纯捡垃圾吃的  阅读(478)  评论(0编辑  收藏  举报
返回顶部