k8s指南-Ingress

目录:
(1)k8s指南-概述
(2)k8s指南-架构
(3)k8s指南-工作负载(1)
(4)k8s指南-工作负载(2)
(5)k8s指南-工作负载(3)
(6)k8s指南-工作负载(4)
(7)k8s指南-Service
(8)k8s指南-Ingress
(9)k8s指南-DNS与服务发现
(10)K8S指南-平滑升级与自动扩缩容

Ingress是k8s集群中负责管理服务外部访问的API对象。


概述

什么是Ingress

通常情况下,service和pod仅可在集群内部网络中通过ip地址访问。如果想从集群外部访问集群内部,则必须要有Ingress。

Ingress则相当于一个服务网关,控制着从集群外部到集群内部的HTTP/HTTPS的访问路由。下面是一个简单的ingress对流量的路由示意图:

k8s指南-Ingress_第1张图片
可以给Ingress配置提供外部可访问的URL、负载均衡、SSL和基于名称的虚拟主机等。

为什么需要Ingress

在Kubernetes中,为了使外部的应用能够访问集群内的service,最为常用的是使用NodePortLoadBalancer两种类型的service。但这两种类型的服务各有缺点:NodePort方式会占用很多集群机器的端口;而LoadBanlancer类型要求k8s必须跑在支持的云上。当同时存在多个LoadBanlancer类型的Service时,就会占用大量公网ip地址,而Ingress正式为了解决以上两种问题而生的。

Ingress资源

Ingress必须要有Ingress控制器,单独的创建一个Ingress是没有任何效果的。通常我们说Ingress,实际上指的是Ingress配置和Ingress控制器。

Ingress控制器类似于nginx,Ingress类似于nginx的配置文件。如果只安装nginx,而没有nginx的配置文件,则此nginx将毫无意义;反过来,若只有nginx的配置文件,但没安装nginx,则配置文件无运行载体

下面是一个流量路由示意图:

k8s指南-Ingress_第2张图片

从中可以看出,客户端首先对 ngdemo.qikqiak.com 执行 DNS 解析,得到 Ingress 控制器所在节点的 IP,然后客户端向 Ingress 控制器发送 HTTP 请求,然后根据 Ingress 对象里面的描述匹配域名,找到对应的 Service 对象,并获取关联的 Endpoints 列表,将客户端的请求转发给其中一个 Pod。

理想情况下,所有的Ingress控制器都应该符合参考规范,但实际上不同的Ingress控制器操作略有不同。

Ingress 控制器

Ingress控制器有很多种,有官方维护的如ingress-nginx,AWS和GCE,也有第三方的。

下面安装Ingress-nginx控制器。

按照官方文档官方yaml文,直接运行yaml文件的话国内会由于访问不了google仓库而报错。网上有人建了一个中转站,将google镜像转成docker镜像,然后通过脚本拉下来之后重新打原来的标签。因此只需要执行脚本就能将镜像拉到本地了(传送门)。

下载官方yaml文件,将其中的image中的数字摘要去掉(结果如:image: registry.k8s.io/ingress-nginx/controller:v1.6.4),这样就能启动Ingress控制器了。执行kubectl get all -n ingress-nginx,结果如下所示:

NAME                                           READY   STATUS      RESTARTS   AGE
pod/ingress-nginx-admission-create-9jpfg       0/1     Completed   0          5h54m
pod/ingress-nginx-admission-patch-5c6qd        0/1     Completed   1          5h54m
pod/ingress-nginx-controller-5b648bf48-wrsnd   1/1     Running     0          5h54m

NAME                                         TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)                      AGE
service/ingress-nginx-controller             LoadBalancer   10.98.207.53    localhost     80:31871/TCP,443:31482/TCP   5h54m
service/ingress-nginx-controller-admission   ClusterIP      10.104.227.61           443/TCP                      5h54m

NAME                                       READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/ingress-nginx-controller   1/1     1            1           5h54m

NAME                                                 DESIRED   CURRENT   READY   AGE
replicaset.apps/ingress-nginx-controller-5b648bf48   1         1         1       5h54m

NAME                                       COMPLETIONS   DURATION   AGE
job.batch/ingress-nginx-admission-create   1/1           7s         5h54m
job.batch/ingress-nginx-admission-patch    1/1           8s         5h54m

这个Ingress控制器创建之后会带一个名为nginxIngressClass,接下来我们创建Ingress,指定ingressClassName: nginx 即可使用ingress-nginx控制器。当然,也要创建Ingress路由的service和pod:

# my-nginx.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
spec:
  selector:
    matchLabels:
      app: my-nginx
  template:
    metadata:
      labels:
        app: my-nginx
    spec:
      containers:
      - name: my-nginx
        image: nginx
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: my-nginx
  labels:
    app: my-nginx
spec:
  ports:
  - port: 80
    protocol: TCP
    name: http
  selector:
    app: my-nginx
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: my-nginx
  namespace: default
spec:
  ingressClassName: nginx  # 使用 nginx 的 IngressClass(关联的 ingress-nginx 控制器)
  rules:
  - host: ngdemo.qikqiak.com  # 将域名映射到 my-nginx 服务
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:  # 将所有请求发送到 my-nginx 服务的 80 端口
            name: my-nginx
            port:
              number: 80
# 不过需要注意大部分Ingress控制器都不是直接转发到Service
# 而是只是通过Service来获取后端的Endpoints列表,直接转发到Pod,这样可以减少网络跳转,提高性能

执行kubectl get ingress -n ingress-nginx查看Ingress:

NAME                    CLASS   HOSTS                ADDRESS     PORTS   AGE
my-nginx                nginx   ngdemo.qikqiak.com   localhost   80      94m

可以看到Ingress控制器地址为localhost,修改/etc/hosts,将域名ngdemo.qikqiak.com指向本地。然后在浏览器访问该域名,结果如下:
k8s指南-Ingress_第3张图片

访问该域名,实际上就是访问Ingress控制器,Ingress控制器根据Ingress配置的规则,将请求转发给my-nginx服务,从而返回该页面。

Ingress

下面是一个最简单的Ingress定义:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: minimal-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  ingressClassName: nginx-example
  rules:
  - http:
      paths:
      - path: /testpath
        pathType: Prefix
        backend:
          service:
            name: test
            port:
              number: 80

如上所示,Ingress spec中包含配置loadbalancerproxy server的所有信息。最重要的是,它包含了一个匹配所有入站请求的规则列表。目前,Ingress只支持HTTP(S)请求。

如果省略ingressClassName,那么你应该定义一个默认的Ingress class
当然也有一些Ingress控制器不需要默认的IngressClass。比如Ingress-NGINX,它可以通过参数--watch-ingress-without-class来配置,不过仍然推荐指定一个默认IngressClass

Ingress规则

每个Http规则包含以下内容:

  • 一个可选的主机。在示例中没有指定主机,所以该规则适配所有通过指定ip地址进来的http请求。如果指定了host(比如foo.bar.com),则入站请求还需要匹配主机。也就是说请求中的host与规则中的host相同才允许进入。
  • 路径列表(例如/testpath)。每个路径都有一个与之关联的,由service.nameservice.port.nameservice.port.number定义的后端服务(如test:80)。只有当host和path都匹配了,才能被负载均衡器转发给对应的服务。
  • 后端是Service文档中描述的服务和端口的组合。匹配了入站规则的请求将被发送给后端。

通常会在Ingress 控制器中配置一个默认的后端,以便没有匹配入站规则的请求被转发给这个默认后端。

默认后端

如果没有设置Ingress规则,则所有的流量将被发送到默认后端 defaultBackend,也就是说,如果没有设置rules,则defaultBackend是必须有的。注意,默认后端通常是Ingress控制器的常规配置项,而不是在Ingress中指定

默认后端用于处理未匹配任何规则的请求,如果没有指定默认后端,则这些请求将被转发到Ingress控制器。

资源后端

事实上后端除了引用一个Service之外,还可以通过一个resource资源进行关联,这也是所谓的资源后端。资源后端是一个对象引用,指向同一个命名空间下的另一个k8s资源。资源后端和服务后端是互斥的,在二者均被设置时会无法通过合法性检查。

资源后端的一种常见用法是将所有入站数据转发到对象存储后端进行存储。

下面是一个资源Ingress:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-resource-backend
spec:
  defaultBackend:
    resource:
      apiGroup: k8s.example.com
      kind: StorageBucket
      name: static-assets
  rules:
    - http:
        paths:
          - path: /icons
            pathType: ImplementationSpecific
            backend:
              resource:
                apiGroup: k8s.example.com
                kind: StorageBucket
                name: icon-assets

创建了如上的Ingress之后,你可以通过describe命令查看:

kubectl describe ingress ingress-resource-backend
Name:             ingress-resource-backend
Labels:           
Namespace:        default
Address:          
Ingress Class:    
Default backend:  APIGroup: k8s.example.com, Kind: StorageBucket, Name: static-assets
Rules:
  Host        Path  Backends
  ----        ----  --------
  *           
              /icons   APIGroup: k8s.example.com, Kind: StorageBucket, Name: icon-assets
Annotations:  
Events:       

该Ingress对象表明所有/icons请求会被路由到相同命名空间下名为icon-assetsStorageBucket资源中去进行处理。

路径类型

Ingress中的每个路径都需要有对应的路径类型,没有路径类型是不合法的。当前支持的路径类型有三种:

  • ImplementationSpecific: 该类型的路径匹配取决于IngressClass的具体实现。可以将其作为单独的pathType,也可以将其与Prefix或Exact类型作相同处理。
  • Exact:严格匹配,大小写敏感
  • Prefix:基于以/分隔的URL路径前缀匹配,大小写敏感

下面是一个匹配示例

路径类型 匹配路径 请求路径 是否匹配
Prefix / (all paths)
Prefix /foo /foo
Exact /foo /foo/
Exact /foo/ /foo
Prefix /foo /foo, /foo/
Prefix /foo/ /foo, /foo/
Prefix /aaa/bb /aaa/bbb
Prefix /aaa/bbb /aaa/bbb
Prefix /aaa/bbb/ /aaa/bbb 是,忽略斜杠
Prefix /aaa/bbb /aaa/bbb/ 是,忽略斜杠
Prefix /aaa/bbb /aaa/bbb/ccc 是,子路径匹配
Prefix /aaa/bbb /aaa/bbbxyz 否,前缀不匹配
Prefix /, /aaa /aaa/ccc 是,前缀匹配/aaa
Prefix /, /aaa, /aaa/bbb /aaa/bbb 是,前缀匹配/aaa/bbb
Prefix /, /aaa, /aaa/bbb /ccc 是,前缀匹配/
Prefix /aaa /ccc
Mixed /foo (Prefix), /foo (Exact) /foo 是,严格匹配优先

多重匹配
在某些情况下,Ingress中的多条路径会匹配同一请求,这种情况下最长的匹配路径优先。如果有两条相同的匹配路径,则精确路径优先于前缀路径。

主机名通配符

主机名可以精确匹配,也可以通过通配符来匹配。
下面是一个通配符匹配示例:

host host header match?
*.foo.com bar.foo.com 基于相同的后缀匹配
*.foo.com baz.bar.foo.com 不匹配
*.foo.com foo.com 不匹配

Ingress Class

在上文中说过,如果在Ingress定义时省略了ingressClassName,那么需要定义一个默认的Ingress class。在Ingress class中,定义了实现Ingress的控制器。下面是一个示例:

apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
  name: external-lb
spec:
  controller: example.com/ingress-controller
  parameters:
    apiGroup: k8s.example.com
    kind: IngressParameters
    name: external-lb

其中,比较重要的属性是metadata.namespec.controller,前者是这个Ingress Class的名称,也就是Ingress中的spec.ingressClassName,后者是Ingress Controller的名称。另外,parameters字段可用于引用其他资源以提供额外的配置,其类型由controller决定。

默认的Ingress Class用于处理所有没有指定Ingress Class的Ingress资源,只需要将ingressclass.kubernetes.io/is-default-class设置为true即可。

注意,当存在多个默认Ingress Class时,新的Ingress如果没有指定ingressClassName则不会被允许创建。解决这个问题只需要确保集群中最多只能有一个默认的IngressClass。

多个Ingress Controller

除了可能会有多个不同类型的Ingress Controller外,还可能存在多个相同的Ingress Controller,比如部署了两个NGINX Ingress Controller,一个负载处理外网访问,一个负责处理内网访问。

此时也可以通过上面的方式,为每个Controller指定唯一的一个class。

Ingrss Class作用域

集群作用域和命名空间作用域

如果设置了spec.parameters字段且未设置spec.parameters.scope字段,或者将spec.parameters.scope字段设置为Cluster时,则该IngressClass所引用的是一个集群作用域的资源。

除了集群作用域之外,Ingrss Class还可以设置Namespace作用域。

Ingress类型

单服务Ingress
暴露单个服务的Ingress,如下所示:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: test-ingress
spec:
  defaultBackend:
    service:
      name: test
      port:
        number: 80

Simple fanout

简单扇出,也可以说是多服务Ingress。它可以将来自同一IP地址的流量路由到多个Service。如下图所示:

k8s指南-Ingress_第4张图片

对应的Ingress如下:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: simple-fanout-example
spec:
  rules:
  - host: foo.bar.com
    http:
      paths:
      - path: /foo
        pathType: Prefix
        backend:
          service:
            name: service1
            port:
              number: 4200
      - path: /bar
        pathType: Prefix
        backend:
          service:
            name: service2
            port:
              number: 8080

基于名称的虚拟托管

基于名称的虚拟主机支持将针对多个主机名的HTTP流量路由到同一IP地址上。如下图所示:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: name-virtual-host-ingress
spec:
  rules:
  - host: foo.bar.com
    http:
      paths:
      - pathType: Prefix
        path: "/"
        backend:
          service:
            name: service1
            port:
              number: 80
  - host: bar.foo.com
    http:
      paths:
      - pathType: Prefix
        path: "/"
        backend:
          service:
            name: service2
            port:
              number: 80

如果创建的Ingress资源没有在rules中定义任何的hosts,则可以匹配指向Ingress控制器ip地址的任何网络流量,而无需基于名称的虚拟主机。

TLS

你可以通过设定包含TLS私钥和证书的Secret来保护Ingress。

你可以通过以下几种方式获得证书:

  • 自签名证书:用自己的CA(证书颁发机构)来创建和签名TLS证书。
  • 购买一个TLS证书:从浏览器和操作系统信任的知名CA购买TLS证书。
  • 使用LetSencrpt证书:LetSencrpt是一家非盈利的可信证书颁发机构,提供免费的TLS证书。

当你把TLS证书作为Kubernetes Secret添加到Ingress里去时,Ingress 控制器就能获取到证书,并使其变成自己的配置信息。
在Ingress-Nginx控制器中,TLS证书就是由nginx.conf动态地处理:

ssl_certificate_by_lua_block { 
           certificate.call()     
}

下面是Ingress TLS的工作流程:
k8s指南-Ingress_第5张图片

TLS Secret的数据中必须包含以tls.crt保存的证书和以tls.key保存的私钥。例如:

apiVersion: v1
kind: Secret
metadata:
  name: testsecret-tls
  namespace: default
data:
  tls.crt: base64 编码的证书
  tls.key: base64 编码的私钥
type: kubernetes.io/tls

把TLS证书添加到Ingress中:

spec:
  ingressClassName: nginx
  tls:
  - hosts:
    - foo.bar.com
    secretName: testsecret-tls
  rules:
  - hosts: "foo.bar.com"
     http:
        paths:
            ...

注意,默认规则上无法使用 TLS,因为需要为所有可能的子域名发放证书。 因此,tls 字段中的 hosts 的取值需要与 rules 字段中的 host 完全匹配。

默认情况下,TLS连接终止与Ingress节点,也就是说,Ingress控制器与Service及其Pod之间的流量都以明文传输。

如果想要全程加密,可以通过在ingress controller中添加支持的annotation。例如如果使用的是Ingress-Nginx控制器,可以在Ingress中添加注解:nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"

各种Ingress控制器所支持的TLS功能之间存在差异。

负载均衡

Ingress控制器启动时会使用一些适用于所有Ingress的负载均衡策略设置,例如负载均衡算法等。更高级的负载均衡概念(例如持久会话、动态权重)尚未通过 Ingress 公开。

值得注意的是,健康检查不是通过Ingress直接暴露的。在Kubernetes中存在并行的概念,比如就绪检查,允许你实现相同的目的。

Ingress是7层的负载均衡,负载将HTTP/HTTPS请求路由到Service。接下来再由service通过iptables来将请求转发到具体的pod上[3]

参考资料

[1]. https://kubernetes.io/docs/concepts/services-networking/ingress/
[2]. https://cloud.tencent.com/developer/article/1925519
[3]. https://tech.citahub.com/k8s-service-iptables/

你可能感兴趣的:(分布式,云原生,kubernetes,云原生)