kubernetes之ingress和Ingress Controller

原文博客:

https://www.cnblogs.com/linuxk/p/9706720.html

https://www.cnblogs.com/caibao666/p/11235500.html

 

一、什么是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,如下图所示:
 

 

kubernetes之ingress和Ingress Controller_第1张图片

 

2、端口管理问题

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

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

 

kubernetes之ingress和Ingress Controller_第2张图片

 

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 一下,工作流程如下图:

 

kubernetes之ingress和Ingress Controller_第3张图片

 

二、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对象,其定义格式如下:

spec:
  rules:
  - hosts: 
    http:
      paths:
      - path:
        backend:
          serviceName: 
          servicePort: 

需要注意的是,.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即可,如下示例:

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: my-ingress
spec:
  backend:
    serviceName: my-svc
    servicePort: 80

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

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

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

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: ingress-url-demo
  annotations:
     nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  rules:
  - host: myapp.syztoo.com
    http:
      paths:
      - path: /v1
        backend:
          serviceName: myappv1-svc
          servicePort: 80
      - path: /v2
        backend:
          serviceName: myappv2-svc
          servicePort: 80

---
apiVersion: v1
kind: Service
metadata:
  name: myappv1-svc
  namespace: default
spec:
  selector:
    app: myappv1
    release: canary
  type: ClusterIP
  ports: 
  - port: 80
    targetPort: 80

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myappv1-depoly
  namespace: default
spec:
  replicas: 2
  selector:
    matchLabels:
      app: myappv1
      release: canary
  template:
    metadata:
      labels: 
        app: myappv1
        release: canary
    spec:
      containers:
      - name: myappv1
        image: ikubernetes/myapp:v1
        ports:
        - name: http
          containerPort: 80

---
apiVersion: v1
kind: Service
metadata:
  name: myappv2-svc
  namespace: default
spec:
  selector:
    app: myappv2
    release: canary
  type: ClusterIP
  ports: 
  - port: 80
    targetPort: 80

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myappv2-deploy
  namespace: default
spec:
  replicas: 2
  selector:
    matchLabels:
      app: myappv2
      release: canary
  template:
    metadata:
      labels: 
        app: myappv2
        release: canary
    spec:
      containers:
      - name: myappv2
        image: ikubernetes/myapp:v2
        ports:
        - name: http
          containerPort: 80

 

kubernetes之ingress和Ingress Controller_第4张图片

kubernetes之ingress和Ingress Controller_第5张图片

 

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

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: test
spec:
  rules:
  - host: api.ik8s.io
    http: 
      paths:
      - backend:
          serviceName: api
          servicePort: 80
  - host: wap.ik8s.io
    http: 
      paths:
      - backend:
          serviceName: wap
          servicePort: 80

4、TLS类型的Ingress资源

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

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: no-rules-map
spec: 
  tls:
  - secretName: ikubernetesSecret
  backend:
    serviceName: homesite
    servicePort: 80

 

五、部署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
[root@master ingress-nginx]# vim service-nodeport.yaml
apiVersion: v1
kind: Service
metadata:
  name: ingress-nginx
  namespace: ingress-nginx
  labels:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
spec:
  type: NodePort
  ports:
    - name: http
      port: 80
      targetPort: 80
      protocol: TCP
      nodePort: 30080
    - name: https
      port: 443
      targetPort: 443
      protocol: TCP
      nodePort: 30443
  selector:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx

---

上面的文件中,使用了宿主机的固定端口(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           80:30080/TCP,443:30443/TCP   13s

3、定义Ingress

[root@master resourcelist]# vim ingress-demo.yaml
apiVersion: extensions/v1beta1  # api版本
kind: Ingress  # 清单类型
metadata:  # 元数据
  name: ingress-myapp  # ingress的名称
  namespace: default  # 所属名称空间
  annotations:  # 注解信息
    kubernetes.io/ingress.class: "nginx"
spec:  # 规格
  rules:  # 定义后端转发的规则
  - host: myapp.magedu.com  # 通过域名进行转发
    http:
      paths:
      - path:  # 配置访问路径,如果通过url进行转发,需要修改;空默认为访问的路径为"/"
        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: 5
  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-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

kubernetes之ingress和Ingress Controller_第6张图片

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

kubernetes之ingress和Ingress Controller_第7张图片

 

 

六、构建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

 

kubernetes之ingress和Ingress Controller_第8张图片

 

 

 

你可能感兴趣的:(Kubernetes)