首页 > 其他分享 >Kubernetes 网络之 Ingress 介绍

Kubernetes 网络之 Ingress 介绍

时间:2023-12-28 16:38:00浏览次数:28  
标签:Ingress abc name Kubernetes ingress 网络 nginx io

一、ingress

在Kubernetes集群中,Ingress作为集群内服务对外暴露的访问接入点,几乎承载着集群内服务访问的所有流量。Ingress是Kubernetes中的一个资源对象,用来管理集群外部访问集群内部服务的方式。可以通过Ingress资源来配置不同的转发规则,从而实现根据不同的规则设置访问集群内不同的Service所对应的后端Pod。


下面是 Ingress 的一个简单示例,可将所有流量都发送到同一 Service:

Kubernetes 网络之 Ingress 介绍_ingress-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 资源的定义,一个路径为 /testpath 的路由,所有 /testpath/** 的请求,会被 Ingress 转发至名为 test 的服务的 80 端口的 / 路径下。

此外 Ingress 经常使用注解 annotations 来配置一些选项,当然这具体取决于 Ingress 控制器的实现方式,不同的 Ingress 控制器支持不同的注解。

不同的集群版本要使用不同的 apiVersion 。这里使用的 apiVersion 是 networking.k8s.io/v1networking.k8s.io API组在Kubernetes 1.14版本中首次引入,当时使用的是extensions/v1beta1版本。在随后的版本中,Kubernetes社区决定将Ingress资源从extensions API组迁移到networking.k8s.io API组,并于Kubernetes 1.19版本中正式引入networking.k8s.io/v1版本的Ingress资源。


可以使用 kubectl explain ingress.spec 命令来了解 Ingress 资源清单的描述:

 % kubectl explain ingress.spec  
KIND:     Ingress
VERSION:  networking.k8s.io/v1

RESOURCE: spec <Object>

DESCRIPTION:
     Spec is the desired state of the Ingress. More info:
     https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status

     IngressSpec describes the Ingress the user wishes to exist.

FIELDS:
   defaultBackend       <Object>
     DefaultBackend is the backend that should handle requests that don't match
     any rule. If Rules are not specified, DefaultBackend must be specified. If
     DefaultBackend is not set, the handling of requests that do not match any
     of the rules will be up to the Ingress controller.

   ingressClassName     <string>
     IngressClassName is the name of the IngressClass cluster resource. The
     associated IngressClass defines which controller will implement the
     resource. This replaces the deprecated `kubernetes.io/ingress.class`
     annotation. For backwards compatibility, when that annotation is set, it
     must be given precedence over this field. The controller may emit a warning
     if the field and annotation have different values. Implementations of this
     API should ignore Ingresses without a class specified. An IngressClass
     resource may be marked as default, which can be used to set a default value
     for this field. For more information, refer to the IngressClass
     documentation.

   rules        <[]Object>
     A list of host rules used to configure the Ingress. If unspecified, or no
     rule matches, all traffic is sent to the default backend.

   tls  <[]Object>
     TLS configuration. Currently the Ingress only supports a single TLS port,
     443. If multiple members of this list specify different hosts, they will be
     multiplexed on the same port according to the hostname specified through
     the SNI TLS extension, if the ingress controller fulfilling the ingress
     supports SNI.

从上面描述可以看出 Ingress 资源对象中有几个重要的属性:defaultBackendingressClassNamerulestls


rules

其中核心部分是 rules 属性的配置,每个路由规则都在下面进行配置:

  • host:可选字段,上面我们没有指定 host 属性,所以该规则适用于通过指定 IP 地址的所有入站 HTTP 通信,如果提供了 host 域名,则 rules 则会匹配该域名的相关请求,此外 host 主机名可以是精确匹配(例如 foo.bar.com)或者使用通配符来匹配(例如 *.foo.com)。
  • http.paths:定义访问的路径列表,比如上面定义的 /testpath,每个路径都有一个由 backend.service.name 和 backend.service.port.number 定义关联的 Service 后端,在控制器将流量路由到引用的服务之前,host 和 path 都必须匹配传入的请求才行。
  • backend:该字段其实就是用来定义后端的 Service 服务的,与路由规则中 host 和 path 匹配的流量会将发送到对应的 backend 后端去。

此外一般情况下在 Ingress 控制器中会配置一个 defaultBackend 默认后端,当请求不匹配任何 Ingress 中的路由规则的时候会使用该后端。defaultBackend 通常是 Ingress 控制器的配置选项,而非在 Ingress 资源中指定。


resource

backend 后端除了可以引用一个 Service 服务之外,还可以通过一个 resource 资源进行关联,Resource 是当前 Ingress 对象命名空间下引用的另外一个 Kubernetes 资源对象,但是需要注意的是 ResourceService 配置是互斥的,只能配置一个,Resource 后端的一种常见用法是将所有入站数据导向带有静态资产的对象存储后端,如下所示:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-resource-backend
spec:
  rules:
    - http:
        paths:
          - path: /icons
            pathType: ImplementationSpecific
            backend:
              resource:
                apiGroup: k8s.example.com
                kind: StorageBucket #这个类似于对象存储的后端之类的。一般我们会把静态资源放到对象存储上面,然后前面加上cdn之类的。
                name: icon-assets

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


pathType

上面的示例中在定义路径规则的时候都指定了一个 pathType 的字段,事实上每个路径都需要有对应的路径类型,当前支持的路径类型有三种:

  • ImplementationSpecific:该路径类型的匹配方法取决于 IngressClass,具体实现可以将其作为单独的 pathType 处理或者与 PrefixExact 类型作相同处理。
  • Exact:精确匹配 URL 路径,且区分大小写。
  • Prefix:基于以 / 分隔的 URL 路径前缀匹配,匹配区分大小写,并且对路径中的元素逐个完成,路径元素指的是由 / 分隔符分隔的路径中的标签列表。

Exact 比较简单,就是需要精确匹配 URL 路径,对于 Prefix 前缀匹配,需要注意如果路径的最后一个元素是请求路径中最后一个元素的子字符串,则不会匹配,例如 /foo/bar 可以匹配 /foo/bar/baz, 但不匹配 /foo/barbaz,可以查看下表了解更多的匹配场景(来自官网):

Kubernetes 网络之 Ingress 介绍_ingress-nginx_02

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


ingressClass

Kubernetes 1.18 起,正式提供了一个 IngressClass 资源,作用与 kubernetes.io/ingress.class 注解类似,因为可能在集群中有多个 Ingress 控制器,可以通过该对象来定义我们的控制器,例如:

apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
  name: external-lb		# ingressClass 的名字,需要设置在ingress中
spec:
  controller: nginx-ingress-internal-controller  # ingress 控制器的名字
  parameters:
    apiGroup: k8s.example.com
    kind: IngressParameters
    name: external-lb

其中重要的属性是 metadata.namespec.controller,前者是这个 IngressClass 的名称,需要设置在 Ingress 中,后者是 Ingress 控制器的名称。

Ingress 中的 spec.ingressClassName 属性就可以用来指定对应的 IngressClass,并进而由 IngressClass 关联到对应的 Ingress 控制器,如:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: myapp
spec:
  ingressClassName: external-lb # 上面定义的 IngressClass 对象名称
  defaultBackend:
    service:
      name: myapp
      port:
        number: 80

不过需要注意的是 spec.ingressClassName 与老版本的 kubernetes.io/ingress.class 注解的作用并不完全相同,因为 ingressClassName 字段引用的是 IngressClass 资源的名称,IngressClass 资源中除了指定了 Ingress 控制器的名称之外,还可能会通过 spec.parameters 属性定义一些额外的配置。

比如 parameters 字段有一个 scope 和 namespace 字段,可用来引用特定于命名空间的资源,对 Ingress 类进行配置。 scope 字段默认为 Cluster,表示默认是集群作用域的资源。将 scope 设置为 Namespace 并设置 namespace 字段就可以引用某特定命名空间中的参数资源,比如:

apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
  name: external-lb
spec:
  controller: nginx-ingress-internal-controller
  parameters:
    apiGroup: k8s.example.com
    kind: IngressParameters
    name: external-lb
    namespace: external-configuration
    scope: Namespace

由于一个集群中可能有多个 Ingress 控制器,所以我们还可以将一个特定的 IngressClass 对象标记为集群默认是 Ingress 类。只需要将一个 IngressClass 资源的 ingressclass.kubernetes.io/is-default-class 注解设置为 true 即可,这样未指定 ingressClassName 字段的 Ingress 就会使用这个默认的 IngressClass。

如果集群中有多个 IngressClass 被标记为默认,准入控制器将阻止创建新的未指定 ingressClassName 的 Ingress 对象。最好的方式还是确保集群中最多只能有一个 IngressClass 被标记为默认。


TLS

Ingress 资源对象还可以用来配置 Https 的服务,可以通过设定包含 TLS 私钥和证书的 Secret 来保护 Ingress。 Ingress 只支持单个 TLS 端口 443,如果 Ingress 中的 TLS 配置部分指定了不同的主机,那么它们将根据通过 SNI TLS 扩展指定的主机名 (如果 Ingress 控制器支持 SNI)在同一端口上进行复用。需要注意 TLS Secret 必须包含名为 tls.crttls.key 的键名,例如:

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

在 Ingress 中引用此 Secret 将会告诉 Ingress 控制器使用 TLS 加密从客户端到负载均衡器的通道,我们需要确保创建的 TLS Secret 创建自包含 https-example.foo.com 的公用名称的证书,如下所示:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: tls-example-ingress
spec:
  tls:
    - hosts:
        - https-example.foo.com
      secretName: testsecret-tls
  rules:
    - host: https-example.foo.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: service1
                port:
                  number: 80

现在了解了如何定义 Ingress 资源对象了,但是仅创建 Ingress 资源本身没有任何效果。还需要部署 Ingress 控制器,例如 ingress-nginx,现在可以供大家使用的 Ingress 控制器有很多,比如 traefik、nginx-controller、Kubernetes Ingress Controller for Kong、HAProxy Ingress controller,当然你也可以自己实现一个 Ingress Controller,现在普遍用得较多的是 ingress-nginx、apisix 以及 traefik,traefik 的性能比 ingress-nginx 差,但是配置使用要简单许多。


实际上社区目前还在开发一组高配置能力的 API,被称为 Gateway API,新 API 会提供一种 Ingress 的替代方案,它的存在目的不是替代 Ingress,而是提供一种更具配置能力的新方案。


ingress-controller

Ingress:Ingress公开了从集群外部到集群内服务的HTTP和HTTPS路由的规则集合,而具体实现流量路由则是由Ingress Controller负责。

  • Ingress:K8s中的一个抽象资源,给管理员提供一个暴露应用的入口定义方法
  • Ingress Controller:根据Ingress生成具体的路由规则,并对Pod负载均衡器

Kubernetes 网络之 Ingress 介绍_ingress-nginx_03

Ingress Controller 可以理解为一个监听器,通过不断地监听 kube-apiserver,实时的感知后端 Service、Pod的变化,当得到这些信息变化后,Ingress Controller 再结合 Ingress 的配置,更新反向代理负载均衡器,达到服务发现的作用。其实这点和服务发现工具 consul、 consul-template 非常类似。

Ingress管理的负载均衡器,为集群提供全局的负载均衡能力。

使用流程:

  1. 部署Ingress Controller
  2. 创建Ingress规则


ingress Controller 怎么工作的?

Ingress Controller通过与 Kubernetes API 交互,动态的去感知集群中 Ingress 规则变化,然后读取它,按照自定义的规则,规则就是写明了哪个域名对应哪个service,生成一段 Nginx 配置,应用到管理的Nginx服务,然后热加载生效。以此来达到Nginx负载均衡器配置及动态更新的问题。

流程包流程:客户端 ->Ingress Controller(nginx) -> 分布在各节点Pod



二、ingress-nginx

Kubernetes Ingress 是一种 API 对象,它提供路由规则来管理外部用户对 Kubernetes 集群内的 Service 的访问。而 Ingress Controller 是 Ingress API 的真正实现。Ingress Controller 通常是一个 LoadBalancer,用于将外部流量路由到 Kubernetes 集群,负责 L4 - L7 网络服务。

Ingress 资源对象只是一个路由请求描述配置文件,要让其真正生效还需要对应的 Ingress 控制器才行,Ingress 控制器有很多,使用最多的是 ingress-nginx,它是基于 Nginx 的 Ingress 控制器。


运行原理

ingress-nginx 控制器主要是用来组装一个 nginx.conf 的配置文件,当配置文件发生任何变动的时候就需要重新加载 Nginx 来生效,但是并不会只在影响 upstream 配置的变更后就重新加载 Nginx,控制器内部会使用一个 lua-nginx-module 来实现该功能。


Kubernetes 控制器使用控制循环模式来检查控制器中所需的状态是否已更新或是否需要变更,所以 ingress-nginx 需要使用集群中的不同对象来构建模型,比如 Ingress、Service、Endpoints、Secret、ConfigMap 等可以生成反映集群状态的配置文件的对象,控制器需要一直 Watch 这些资源对象的变化,但是并没有办法知道特定的更改是否会影响到最终生成的 nginx.conf 配置文件,所以一旦 Watch 到了任何变化控制器都必须根据集群的状态重建一个新的模型,并将其与当前的模型进行比较,如果模型相同则就可以避免生成新的 Nginx 配置并触发重新加载,否则还需要检查模型的差异是否只和端点有关,如果是这样,则然后需要使用 HTTP POST 请求将新的端点列表发送到在 Nginx 内运行的 Lua 处理程序,并再次避免生成新的 Nginx 配置并触发重新加载,如果运行和新模型之间的差异不仅仅是端点,那么就会基于新模型创建一个新的 Nginx 配置了,这样构建模型最大的一个好处就是在状态没有变化时避免不必要的重新加载,可以节省大量 Nginx 重新加载。


下面简单描述了需要重新加载的一些场景:


  • 创建了新的 Ingress 资源
  • TLS 添加到现有 Ingress
  • 从 Ingress 中添加或删除 path 路径
  • Ingress、Service、Secret 被删除了
  • Ingress 的一些缺失引用对象变可用了,例如 Service 或 Secret
  • 更新了一个 Secret

对于集群规模较大的场景下频繁的对 Nginx 进行重新加载显然会造成大量的性能消耗,所以要尽可能减少出现重新加载的场景。


处理流程

下图是 Nginx Ingress Controller(简称 IC)处理新 Ingress 资源的流程:

Kubernetes 网络之 Ingress 介绍_ingress-nginx_04

  1. 用户创建新 Ingress 资源
  2. 在集群内部,IC 进程拥有资源的缓存。该缓存仅包含 IC 感兴趣的资源,比如 Ingress。缓存通过监听资源变更的方式,保持与 Kubernetes API 同步
  3. 一旦缓存有新 Ingress 资源,它通知控制循环关于被变更的资源
  4. 控制循环从缓存获取 Ingress 资源的最新版本。因为 Ingress 资源引用其它资源,比如 TLS Secret(Secret 也是 Kubernetes 的一种 API 对象),所以控制循环也获取所有被引用资源的最新版本
  5. 控制循环根据 TLS Secret 生成 TLS 证书和密钥,并且将它们写到文件系统
  6. 控制循环生成与 Ingress 资源相对应的 Nginx 配置文件,并且将它们写入文件系统
  7. 控制循环重载(reload)Nginx,等待 Nginx 重载成功。重载的部分包括:
  • Nginx 读取 TLS 证书和密钥
  • Nginx 读取配置文件
  1. 控制循环为 Ingress 资源发出事件,更新它的状态。如果重载失败,那么事件包含错误信息

更详细介绍可以参考官网:https://docs.nginx.com/nginx-ingress-controller/overview/design/


安装

安装 ingress-nginx 有多种方式,我们这里直接使用下面的命令进行一键安装:

kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.5.1/deploy/static/provider/cloud/deploy.yaml


会自动创建一个名为 ingress-nginx 的命名空间,会生成如下几个 Pod:

% kubectl get pods -n ingress-nginx
NAME                                        READY   STATUS    RESTARTS   AGE
ingress-nginx-controller-6bd9dddc78-8rl6q   1/1     Running   0          57d
ingress-nginx-controller-6bd9dddc78-ftjjs   1/1     Running   0          57d
ingress-nginx-controller-6bd9dddc78-pzj9v   1/1     Running   0          57d


还会创建两个 svc 对象

% kubectl get svc -n ingress-nginx
NAME                                 TYPE           CLUSTER-IP       EXTERNAL-IP     PORT(S)                      AGE
ingress-nginx-controller             LoadBalancer   172.17.204.150   1.2.3.4         80:35096/TCP,443:31407/TCP   57d
ingress-nginx-controller-admission   ClusterIP      172.17.13.12     <none>          443/TCP                      57d


其中 ingress-nginx-controller-admission 是为准入控制器提供服务的,我们也是强烈推荐开启该准入控制器,这样当我们创建不合要求的 Ingress 对象后就会直接被拒绝了,另外一个 ingress-nginx-controller 就是 ingress 控制器对外暴露的服务,默认是一个 LoadBalancer 类型的 Service。


到这里 ingress-nginx 就部署成功了,安装完成后还会创建一个名为 nginxIngressClass 对象:

% kubectl get ingressclass
NAME    CONTROLLER             PARAMETERS   AGE
nginx   k8s.io/ingress-nginx   <none>       57d


查看 ingressclass 的 yaml 文件

% kubectl get ingressclass nginx -o yaml
apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
  annotations:
    ingressclass.kubernetes.io/is-default-class: "true"
  labels:
    app.kubernetes.io/component: controller
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.3.1
  name: nginx
  resourceVersion: "872675654"
  uid: 1344e2a9-0533-4098-bf50-2503a0ae94bb
spec:
  controller: k8s.io/ingress-nginx

这里我们只提供了一个 controller 属性,对应的值和 ingress-nginx 的启动参数中的 controller-class 一致的。

- args:
    - /nginx-ingress-controller
    - --publish-service=$(POD_NAMESPACE)/ingress-nginx-controller
    - --election-id=ingress-nginx-leader
    - --controller-class=k8s.io/ingress-nginx
    - --ingress-class=nginx
    - --configmap=$(POD_NAMESPACE)/ingress-nginx-controller
    - --validating-webhook=:8443
    - --validating-webhook-certificate=/usr/local/certificates/cert
    - --validating-webhook-key=/usr/local/certificates/key



第一个示例

# 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
spec:
  ingressClassName: nginx 		# 使用 nginx 的 IngressClass(关联的 ingress-nginx 控制器)
  rules:
    - host: abc.abc.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 apply -f my-nginx.yaml -n wsj
deployment.apps/my-nginx created
service/my-nginx created
ingress.networking.k8s.io/my-nginx created

% kubectl get ingress -n wsj
NAME       CLASS   HOSTS           ADDRESS         PORTS   AGE
my-nginx   nginx   abc.uisee.com   1.2.3.4         80      80s


如果不配置域名解析,可以需要绑定hosts,访问

% curl abc.abc.com
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>


ingress-nginx 控制器的核心原理就是将 Ingress 这些资源对象映射翻译成 Nginx 配置文件 nginx.conf,通过查看控制器中的配置文件来验证这点:

$ kubectl exec -it ingress-nginx-controller-68b46f9864-zs8k9 -n ingress-nginx -- cat /etc/nginx/nginx.conf

......
upstream upstream_balancer {
        ### Attention!!!
        #
        # We no longer create "upstream" section for every backend.
        # Backends are handled dynamically using Lua. If you would like to debug
        # and see what backends ingress-nginx has in its memory you can
        # install our kubectl plugin https://kubernetes.github.io/ingress-nginx/kubectl-plugin.
        # Once you have the plugin you can use "kubectl ingress-nginx backends" command to
        # inspect current backends.
        #
        ###

        server 0.0.0.1; # placeholder

        balancer_by_lua_block {
                balancer.balance()
        }

        keepalive 320;
        keepalive_time 1h;
        keepalive_timeout  60s;
        keepalive_requests 10000;

}

......
## start server abc.abc.com
server {
        server_name abc.abc.com ;

        listen 80  ;
        listen [::]:80  ;
        listen 443  ssl http2 ;
        listen [::]:443  ssl http2 ;

        set $proxy_upstream_name "-";

        ssl_certificate_by_lua_block {
                certificate.call()
        }

        location / {

                set $namespace      "default";
                set $ingress_name   "my-nginx";
                set $service_name   "my-nginx";
                set $service_port   "80";
                set $location_path  "/";
                set $global_rate_limit_exceeding n;

                rewrite_by_lua_block {
                        lua_ingress.rewrite({
                                force_ssl_redirect = false,
                                ssl_redirect = true,
                                force_no_ssl_redirect = false,
                                preserve_trailing_slash = false,
                                use_port_in_redirects = false,
                                global_throttle = { namespace = "", limit = 0, window_size = 0, key = { }, ignored_cidrs = { } },
                        })
                        balancer.rewrite()
                        plugins.run()
                }

                # be careful with `access_by_lua_block` and `satisfy any` directives as satisfy any
                # will always succeed when there's `access_by_lua_block` that does not have any lua code doing `ngx.exit(ngx.DECLINED)`
                # other authentication method such as basic auth or external auth useless - all requests will be allowed.
                #access_by_lua_block {
                #}

                header_filter_by_lua_block {
                        lua_ingress.header()
                        plugins.run()
                }

                body_filter_by_lua_block {
                        plugins.run()
                }

                log_by_lua_block {
                        balancer.log()

                        monitor.call()

                        plugins.run()
                }

                port_in_redirect off;

                set $balancer_ewma_score -1;
                set $proxy_upstream_name "default-my-nginx-80";
                set $proxy_host          $proxy_upstream_name;
                set $pass_access_scheme  $scheme;

                set $pass_server_port    $server_port;

                set $best_http_host      $http_host;
                set $pass_port           $pass_server_port;

                set $proxy_alternative_upstream_name "";

                client_max_body_size                    1m;

                proxy_set_header Host                   $best_http_host;

                # Pass the extracted client certificate to the backend

                # Allow websocket connections
                proxy_set_header                        Upgrade           $http_upgrade;

                proxy_set_header                        Connection        $connection_upgrade;

                proxy_set_header X-Request-ID           $req_id;
                proxy_set_header X-Real-IP              $remote_addr;

                proxy_set_header X-Forwarded-For        $remote_addr;

                proxy_set_header X-Forwarded-Host       $best_http_host;
                proxy_set_header X-Forwarded-Port       $pass_port;
                proxy_set_header X-Forwarded-Proto      $pass_access_scheme;
                proxy_set_header X-Forwarded-Scheme     $pass_access_scheme;

                proxy_set_header X-Scheme               $pass_access_scheme;

                # Pass the original X-Forwarded-For
                proxy_set_header X-Original-Forwarded-For $http_x_forwarded_for;

                # mitigate HTTPoxy Vulnerability
                # https://www.nginx.com/blog/mitigating-the-httpoxy-vulnerability-with-nginx/
                proxy_set_header Proxy                  "";

                # Custom headers to proxied server

                proxy_connect_timeout                   5s;
                proxy_send_timeout                      60s;
                proxy_read_timeout                      60s;

                proxy_buffering                         off;
                proxy_buffer_size                       4k;
                proxy_buffers                           4 4k;

                proxy_max_temp_file_size                1024m;

                proxy_request_buffering                 on;
                proxy_http_version                      1.1;

                proxy_cookie_domain                     off;
                proxy_cookie_path                       off;

                # In case of errors try the next upstream server before returning an error
                proxy_next_upstream                     error timeout;
                proxy_next_upstream_timeout             0;
                proxy_next_upstream_tries               3;

                proxy_pass http://upstream_balancer;

                proxy_redirect                          off;

        }

}
......

nginx.conf 配置文件中看到上面新增的 Ingress 资源对象的相关配置信息,不过需要注意的是现在并不会为每个 backend 后端都创建一个 upstream 配置块,现在是使用 Lua 程序进行动态处理的,所以没有直接看到后端的 Endpoints 相关配置数据。


kubectl 安装插件 ingress-nginx

默认 kubectl 不支持控制 ingress-nginx ,想要使用 kubectl 控制 ingress-nginx 需要安装插件。

% kubectl ingress-nginx --help
error: unknown command "ingress-nginx" for "kubectl"


第一步:先安装 krew

确保已安装 git,macOS/Linux 然后运行下面的

(
  set -x; cd "$(mktemp -d)" &&
  OS="$(uname | tr '[:upper:]' '[:lower:]')" &&
  ARCH="$(uname -m | sed -e 's/x86_64/amd64/' -e 's/\(arm\)\(64\)\?.*/\1\2/' -e 's/aarch64$/arm64/')" &&
  KREW="krew-${OS}_${ARCH}" &&
  curl -fsSLO "https://github.com/kubernetes-sigs/krew/releases/latest/download/${KREW}.tar.gz" &&
  tar zxvf "${KREW}.tar.gz" &&
  ./"${KREW}" install krew
)


更新shell的profile文件

$ vim .zprofile
# 新增
export PATH="${KREW_ROOT:-$HOME/.krew}/bin:$PATH"

$ source .zprofile 


运行kubectl krew以检查安装

% kubectl krew
krew is the kubectl plugin manager.
You can invoke krew through kubectl: "kubectl krew [command]..."

Usage:
  kubectl krew [command]

Available Commands:
  help        Help about any command
  index       Manage custom plugin indexes
  info        Show information about an available plugin
  install     Install kubectl plugins
  list        List installed kubectl plugins
  search      Discover kubectl plugins
  uninstall   Uninstall plugins
  update      Update the local copy of the plugin index
  upgrade     Upgrade installed plugins to newer versions
  version     Show krew version and diagnostics

Flags:
  -h, --help      help for krew
  -v, --v Level   number for the log level verbosity

Use "kubectl krew [command] --help" for more information about a command.


第二步:安装 ingress-nginx 插件

kubectl krew install ingress-nginx

(MacOS 平台暂不支持)

% kubectl krew install ingress-nginx
Updated the local copy of plugin index.
Installing plugin: ingress-nginx
W1227 17:18:07.974339   42213 install.go:164] failed to install plugin "ingress-nginx": plugin "ingress-nginx" does not offer installation for this platform
failed to install some plugins: [ingress-nginx]: plugin "ingress-nginx" does not offer installation for this platform



nginx 配置示例

用于测试的应用都使用下面的 nginx 应用:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
spec:
  selector:
    matchLabels:
      app: nginx
  template: # pod 模板
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: app
          image: nginx # 该应用进程暴露的是80端口
          ports:
            - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: nginx
spec:
  selector:
    app: nginx
  ports:
    - name: http
      port: 80 # 这个是Service的端口


Basic Auth

在 Ingress 对象上配置一些基本的 Auth 认证,比如 Basic Auth,可以用 htpasswd 生成一个密码文件来验证身份验证。


  1. 先创建一个密码文件
% htpasswd -c auth foo
New password: 
Re-type new password: 
Adding password for user foo


  1. 利用上面生成的文件,创建一个secret对象
% kubectl create secret generic basic-auth --from-file=auth -n wsj
secret/basic-auth created

查看secret yaml

% kubectl get secret basic-auth -n wsj -o yaml
apiVersion: v1
data:
  auth: Zm9vOiRhcHIxJExoLlJWYW1sJDE1VDlxTTZGcmJFeE9ubVM5QS5Qai8K
kind: Secret
metadata:
  name: basic-auth
  namespace: wsj
type: Opaque


  1. 对上面的 my-nginx deployment 创建一个具有 Basic-auth 的 ingress 对象
% kubectl apply -f my-nginx.yaml -n wsj
deployment.apps/nginx created
service/nginx created


# ingress-basic-auth.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-with-auth
  annotations:
    nginx.ingress.kubernetes.io/auth-type: basic # 认证类型
    nginx.ingress.kubernetes.io/auth-secret: basic-auth # 包含 user/password 定义的 secret 对象名
    nginx.ingress.kubernetes.io/auth-realm: "Authentication Required - foo" # 要显示的带有适当上下文的消息,说明需要身份验证的原因
spec:
  ingressClassName: nginx # 使用 nginx 的 IngressClass(关联的 ingress-nginx 控制器)
  rules:
    - host: abc.abc.com # 将域名映射到 nginx 服务
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service: # 将所有请求发送到 nginx 服务的 80 端口
                name: nginx
                port:
                  number: 80


% kubectl get ingress -n wsj
NAME                CLASS   HOSTS         ADDRESS         PORTS   AGE
ingress-with-auth   nginx   abc.abc.com   1.2.3.4         80      4m


  1. 通过curl命令,访问服务
% curl -v abc.abc.com
*   Trying 113.31.105.66:80...
* Connected to abc.abc.com (113.31.105.66) port 80 (#0)
> GET / HTTP/1.1
> Host: abc.abc.com
> User-Agent: curl/8.1.2
> Accept: */*
> 
< HTTP/1.1 401 Unauthorized
< Date: Wed, 27 Dec 2023 10:12:51 GMT
< Content-Type: text/html
< Content-Length: 172
< Connection: keep-alive
< WWW-Authenticate: Basic realm="Authentication Required - foo"
< 
<html>
<head><title>401 Authorization Required</title></head>
<body>
<center><h1>401 Authorization Required</h1></center>
<hr><center>nginx</center>
</body>
</html>
* Connection #0 to host abc.abc.com left intact

访问出现 401 认证失败错误,然后带上配置的用户名和密码进行认证:

% curl -v abc.abc.com -u 'foo:abc123'
*   Trying 113.31.105.66:80...
* Connected to abc.abc.com (113.31.105.66) port 80 (#0)
* Server auth using Basic with user 'foo'
> GET / HTTP/1.1
> Host: abc.abc.com
> Authorization: Basic Zm9vOmFiYzEyMw==
> User-Agent: curl/8.1.2
> Accept: */*
> 
< HTTP/1.1 200 OK
< Date: Wed, 27 Dec 2023 10:16:13 GMT
< Content-Type: text/html
< Content-Length: 615
< Connection: keep-alive
< Last-Modified: Tue, 24 Oct 2023 13:46:47 GMT
< ETag: "6537cac7-267"
< Accept-Ranges: bytes
< 
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>
* Connection #0 to host abc.abc.com left intact

已经认证成功了。


  1. 配置外部 Basic Auth 认证

除了可以使用自己在本地集群创建的 Auth 信息之外,还可以使用外部的 Basic Auth 认证信息,比如使用 https://httpbin.org 的外部 Basic Auth 认证,创建如下所示的 Ingress 资源对象:

# ingress-basic-auth-external.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    # 配置外部认证服务地址
    nginx.ingress.kubernetes.io/auth-url: https://httpbin.org/basic-auth/user/passwd
  name: external-auth
  namespace: default
spec:
  ingressClassName: nginx
  rules:
    - host: abc.abc.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: nginx
                port:
                  number: 80



URL Rewrite

ingress-nginx 很多高级的用法可以通过 Ingress 对象的 annotation 进行配置,比如常用的 URL Rewrite 功能。很多时候我们会将 ingress-nginx 当成网关使用,比如对访问的服务加上 /app 这样的前缀,在 nginx 的配置里面我们知道有一个 proxy_pass 指令可以实现:

location /app/ {
  proxy_pass http://127.0.0.1/remote/;
}


proxy_pass 后面加了 /remote 这个路径,此时会将匹配到该规则路径中的 /app/remote 替换掉,相当于截掉路径中的 /app。同样的在 Kubernetes 中使用 ingress-nginx 又该如何来实现呢?可以使用 rewrite-target 的注解来实现这个需求,比如现在想要通过 abc.abc.com/gateway/ 来访问到 Nginx 服务,则需要对访问的 URL 路径做一个 Rewrite,在 PATH 中添加一个 gateway 的前缀,关于 Rewrite 的操作在 ingress-nginx 官方文档中也给出对应的说明:

Kubernetes 网络之 Ingress 介绍_ingress-nginx_05


  1. 先创建一个ingress

按照要求需要在 path 中匹配前缀 gateway,然后通过 rewrite-target 指定目标,Ingress 对象如下所示:

# ingress-rewrite.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: rewrite
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
  ingressClassName: nginx
  rules:
    - host: abc.abc.com
      http:
        paths:
          - path: /gateway(/|$)(.*)
            pathType: Prefix
            backend:
              service:
                name: nginx
                port:
                  number: 80


  1. 访问域名 /

更新后,可以预见到直接访问域名肯定是不行了,因为没有匹配 / 的 path 路径:

% curl abc.abc.com 
<html>
<head><title>404 Not Found</title></head>
<body>
<center><h1>404 Not Found</h1></center>
<hr><center>nginx</center>
</body>
</html>


  1. 带上 path 访问

带上 gateway 的前缀再去访问就正常了:

% curl abc.abc.com/gateway
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

可以看到已经可以访问到了,这是因为在 path 中通过正则表达式 /gateway(/|$)(.*) 将匹配的路径设置成了 rewrite-target 的目标路径了,所以访问 abc.abc.com/gateway 的时候实际上相当于访问的就是后端服务的 / 路径。



  1. 主域名跳转

要解决访问主域名出现 404 的问题,可以给应用设置一个 app-root 的注解,这样当访问主域名的时候会自动跳转到指定的 app-root 目录下面,如下所示:

# ingress-rewrite2.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: rewrite
  annotations:
    nginx.ingress.kubernetes.io/app-root: /gateway/
    nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
  ingressClassName: nginx
  rules:
    - host: abc.abc.com
      http:
        paths:
          - path: /gateway(/|$)(.*)
            pathType: Prefix
            backend:
              service:
                name: nginx
                port:
                  number: 80


  1. 访问主域名

这个时候我们更新应用后访问主域名 abc.abc.com 就会自动跳转到 abc.abc.com/gateway/ 路径下面去了。

% curl abc.abc.com
<html>
<head><title>302 Found</title></head>
<body>
<center><h1>302 Found</h1></center>
<hr><center>nginx</center>
</body>
</html>



  1. path后增加/

但是还有一个问题是的 path 路径其实也匹配了 /gateway 这样的路径,可能更加希望应用在最后添加一个 / 这样的 slash,同样可以通过 configuration-snippet 配置来完成,如下 Ingress 对象:

# ingress-rewrite3.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: rewrite
  annotations:
    nginx.ingress.kubernetes.io/app-root: /gateway/
    nginx.ingress.kubernetes.io/rewrite-target: /$2
    nginx.ingress.kubernetes.io/configuration-snippet: |
      rewrite ^(/gateway)$ $1/ redirect;
spec:
  ingressClassName: nginx
  rules:
    - host: abc.abc.com
      http:
        paths:
          - path: /gateway(/|$)(.*)
            pathType: Prefix
            backend:
              service:
                name: nginx
                port:
                  number: 80

更新后应用就都会以 / 这样的 slash 结尾了。



灰度发布

在日常工作中经常需要对服务进行版本更新升级,所以经常会使用到滚动升级、蓝绿发布、灰度发布等不同的发布操作。而 ingress-nginx 支持通过 Annotations 配置来实现不同场景下的灰度发布和测试,可以满足金丝雀发布、蓝绿部署与 A/B 测试等业务场景。首先需要添加 nginx.ingress.kubernetes.io/canary:true 注解来启用 canary 功能,然后可以启用以下配置金丝雀的注解:

  • nginx.ingress.kubernetes.io/canary-by-header:基于 Request Header 的流量切分,适用于灰度发布以及 A/B 测试。当 Request Header 设置为 always 时,请求将会被一直发送到 Canary 版本;当 Request Header 设置为 never 时,请求不会被发送到 Canary 入口;对于任何其他 Header 值,将忽略 Header,并通过优先级将请求与其他金丝雀规则进行优先级的比较。
  • nginx.ingress.kubernetes.io/canary-by-header-value:要匹配的 Request Header 的值,用于通知 Ingress 将请求路由到 Canary Ingress 中指定的服务。当 Request Header 设置为此值时,它将被路由到 Canary 入口。该规则允许用户自定义 Request Header 的值,必须与上一个 annotation (canary-by-header) 一起使用。
  • nginx.ingress.kubernetes.io/canary-by-header-pattern:这与 canary-by-header-value 的工作方式相同,只是它进行 PCRE 正则匹配。请注意,当设置 canary-by-header-value 时,此注解将被忽略,当给定的 Regex 在请求处理过程中导致错误时,该请求将被视为不匹配。
  • nginx.ingress.kubernetes.io/canary-weight:基于服务权重的流量切分,适用于蓝绿部署,权重范围 0 - 100 按百分比将请求路由到 Canary Ingress 中指定的服务。权重为 0 意味着该金丝雀规则不会向 Canary 入口的服务发送任何请求,权重为 100 意味着所有请求都将被发送到 Canary 入口。
  • nginx.ingress.kubernetes.io/canary-by-cookie:基于 cookie 的流量切分,适用于灰度发布与 A/B 测试。用于通知 Ingress 将请求路由到 Canary Ingress 中指定的服务的 cookie。当 cookie 值设置为 always 时,它将被路由到 Canary 入口;当 cookie 值设置为 never 时,请求不会被发送到 Canary 入口;对于任何其他值,将忽略 cookie 并将请求与其他金丝雀规则进行优先级的比较。
  • nginx.ingress.kubernetes.io/canary-weight-total:流量总权重,如果未指定,则默认为 100。


需要注意的是金丝雀规则按优先顺序进行排序:canary-by-header - > canary-by-cookie - > canary-weight


总的来说可以把以上的几个 annotation 规则划分为以下两类:

  • 基于权重的 Canary 规则

Kubernetes 网络之 Ingress 介绍_ingress-nginx_06

  • 基于用户请求的 Canary 规则

Kubernetes 网络之 Ingress 介绍_ingress-nginx_07



下面通过一个示例应用来对灰度发布功能进行说明。

第一步. 部署 Production 应用

首先创建一个 production 环境的应用资源清单:

# production.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: production
  labels:
    app: production
spec:
  selector:
    matchLabels:
      app: production
  template:
    metadata:
      labels:
        app: production
    spec:
      containers:
        - name: production
          # arm架构使用该镜像:mirrorgooglecontainers/echoserver-arm:1.8
          image: mirrorgooglecontainers/echoserver:1.10
          ports:
            - containerPort: 8080
          env:
            - name: NODE_NAME
              valueFrom:
                fieldRef:
                  fieldPath: spec.nodeName
            - name: POD_NAME
              valueFrom:
                fieldRef:
                  fieldPath: metadata.name
            - name: POD_NAMESPACE
              valueFrom:
                fieldRef:
                  fieldPath: metadata.namespace
            - name: POD_IP
              valueFrom:
                fieldRef:
                  fieldPath: status.podIP
---
apiVersion: v1
kind: Service
metadata:
  name: production
  labels:
    app: production
spec:
  ports:
    - port: 80
      targetPort: 8080
      name: http
  selector:
    app: production


创建一个用于 production 访问的 ingress 资源对象:

# production-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: production
spec:
  ingressClassName: nginx
  rules:
    - host: abc.abc.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: production
                port:
                  number: 80


部署上面两个yaml文件

% kubectl apply -f production.yaml -n wsj
% kubectl apply -f production-ingress.yaml -n wsj


访问服务

% curl abc.abc.com


Hostname: production-5d84fbdb58-69lgg

Pod Information:
        node name:      10.23.90.170
        pod name:       production-5d84fbdb58-69lgg
        pod namespace:  wsj
        pod IP: 10.23.39.50

Server values:
        server_version=nginx: 1.13.3 - lua: 10008

Request Information:
        client_address=10.23.191.97
        method=GET
        real path=/
        query=
        request_version=1.1
        request_scheme=http
        request_uri=http://abc.abc.com:8080/

Request Headers:
        accept=*/*
        host=abc.abc.com
        user-agent=curl/8.1.2
        x-forwarded-for=1.2.3.4
        x-forwarded-host=abc.abc.com
        x-forwarded-port=80
        x-forwarded-proto=http
        x-forwarded-scheme=http
        x-real-ip=1.2.3.4
        x-request-id=b855c4bexxxxxxxxf8e16724c3c9d091
        x-scheme=http

Request Body:
        -no body in request-


第二步. 创建 Canary 版本

参考将上述 Production 版本的 production.yaml 文件,再创建一个 Canary 版本的应用。

# canary.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: canary
  labels:
    app: canary
spec:
  selector:
    matchLabels:
      app: canary
  template:
    metadata:
      labels:
        app: canary
    spec:
      containers:
        - name: canary
          image: mirrorgooglecontainers/echoserver:1.10
          ports:
            - containerPort: 8080
          env:
            - name: NODE_NAME
              valueFrom:
                fieldRef:
                  fieldPath: spec.nodeName
            - name: POD_NAME
              valueFrom:
                fieldRef:
                  fieldPath: metadata.name
            - name: POD_NAMESPACE
              valueFrom:
                fieldRef:
                  fieldPath: metadata.namespace
            - name: POD_IP
              valueFrom:
                fieldRef:
                  fieldPath: status.podIP
---
apiVersion: v1
kind: Service
metadata:
  name: canary
  labels:
    app: canary
spec:
  ports:
    - port: 80
      targetPort: 8080
      name: http
  selector:
    app: canary

部署

% kubectl apply -f canary.yaml -n wsj
deployment.apps/canary created
service/canary created

接下来就可以通过配置 Annotation 规则进行流量切分了。



第三步. Annotation 规则配置

1. 基于权重:基于权重的流量切分的典型应用场景就是蓝绿部署,可通过将权重设置为 0 或 100 来实现。例如,可将 Green 版本设置为主要部分,并将 Blue 版本的入口配置为 Canary。最初,将权重设置为 0,因此不会将流量代理到 Blue 版本。一旦新版本测试和验证都成功后,即可将 Blue 版本的权重设置为 100,即所有流量从 Green 版本转向 Blue。


创建一个基于权重的 Canary 版本的应用路由 Ingress 对象。

# canary-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: canary
  annotations:
    nginx.ingress.kubernetes.io/canary: "true" 			# 要开启灰度发布机制,首先需要启用 Canary
    nginx.ingress.kubernetes.io/canary-weight: "30" # 分配30%流量到当前Canary版本
spec:
  ingressClassName: nginx
  rules:
    - host: abc.abc.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: canary
                port:
                  number: 80

部署

% kubectl apply -f canary-ingress.yaml -n wsj
ingress.networking.k8s.io/canary created

查看

% kubectl get ingress -n wsj
NAME         CLASS   HOSTS         ADDRESS         PORTS   AGE
canary       nginx   abc.abc.com   113.31.105.66   80      72s
production   nginx   abc.abc.com   113.31.105.66   80      10m


在命令行终端中来不断访问这个应用,观察 Hostname 变化:

% for i in $(seq 1 10); do curl -s abc.abc.com | grep "Hostname"; done
Hostname: production-5d84fbdb58-69lgg
Hostname: production-5d84fbdb58-69lgg
Hostname: production-5d84fbdb58-69lgg
Hostname: canary-6cc5497cfd-wfbxn
Hostname: production-5d84fbdb58-69lgg
Hostname: canary-6cc5497cfd-wfbxn
Hostname: canary-6cc5497cfd-wfbxn
Hostname: production-5d84fbdb58-69lgg
Hostname: canary-6cc5497cfd-wfbxn
Hostname: production-5d84fbdb58-69lgg

由于给 Canary 版本应用分配了 30% 左右权重的流量,所以上面访问 10 次有 4 次(不是一定)访问到了 Canary 版本的应用,符合预期。




2. 基于 Request Header: 基于 Request Header 进行流量切分的典型应用场景即灰度发布或 A/B 测试场景。

在上面的 Canary 版本的 Ingress 对象中新增一条 annotation 配置 nginx.ingress.kubernetes.io/canary-by-header: canary(这里的 value 可以是任意值),使当前的 Ingress 实现基于 Request Header 进行流量切分,由于 canary-by-header 的优先级大于 canary-weight,所以会忽略原有的 canary-weight 的规则。

annotations:
  nginx.ingress.kubernetes.io/canary: "true" # 要开启灰度发布机制,首先需要启用 Canary
  nginx.ingress.kubernetes.io/canary-by-header: canary # 基于header的流量切分
  nginx.ingress.kubernetes.io/canary-weight: "30" # 会被忽略,因为配置了 canary-by-headerCanary版本
  

更新上面的 Ingress 资源对象后,在请求中加入不同的 Header 值,再次访问应用的域名。

注意:当 Request Header 设置为 never 或 always 时,请求将不会或一直被发送到 Canary 版本,对于任何其他 Header 值,将忽略 Header,并通过优先级将请求与其他 Canary 规则进行优先级的比较。


% for i in $(seq 1 10); do curl -s -H "canary: never" abc.abc.com | grep "Hostname"; done
Hostname: production-5d84fbdb58-69lgg
Hostname: production-5d84fbdb58-69lgg
Hostname: production-5d84fbdb58-69lgg
Hostname: production-5d84fbdb58-69lgg
Hostname: production-5d84fbdb58-69lgg
Hostname: production-5d84fbdb58-69lgg
Hostname: production-5d84fbdb58-69lgg
Hostname: production-5d84fbdb58-69lgg
Hostname: production-5d84fbdb58-69lgg
Hostname: production-5d84fbdb58-69lgg

在请求的时候设置了 canary: never 这个 Header 值,所以请求没有发送到 Canary 应用中去。如果设置为其他值呢:

% for i in $(seq 1 10); do curl -s -H "canary: other-value" abc.abc.com | grep "Hostname"; done
Hostname: canary-6cc5497cfd-wfbxn
Hostname: production-5d84fbdb58-69lgg
Hostname: canary-6cc5497cfd-wfbxn
Hostname: production-5d84fbdb58-69lgg
Hostname: production-5d84fbdb58-69lgg
Hostname: canary-6cc5497cfd-wfbxn
Hostname: production-5d84fbdb58-69lgg
Hostname: production-5d84fbdb58-69lgg
Hostname: production-5d84fbdb58-69lgg
Hostname: production-5d84fbdb58-69lgg

由于请求设置的 Header 值为 canary: other-value,所以 ingress-nginx 会通过优先级将请求与其他 Canary 规则进行优先级的比较,这里也就会进入 canary-weight: "30" 这个规则去。


这个时候可以在上一个 annotation (即 canary-by-header)的基础上添加一条 nginx.ingress.kubernetes.io/canary-by-header-value: user-value 这样的规则,就可以将请求路由到 Canary Ingress 中指定的服务了。

annotations:
  nginx.ingress.kubernetes.io/canary: "true" # 要开启灰度发布机制,首先需要启用 Canary
  nginx.ingress.kubernetes.io/canary-by-header-value: user-value
  nginx.ingress.kubernetes.io/canary-by-header: canary # 基于header的流量切分
  nginx.ingress.kubernetes.io/canary-weight: "30" # 分配30%流量到当前Canary版本

同样更新 Ingress 对象后,重新访问应用,当 Request Header 满足 canary: user-value时,所有请求就会被路由到 Canary 版本:

% for i in $(seq 1 10); do curl -s -H "canary: user-value" abc.abc.com | grep "Hostname"; done
Hostname: canary-6cc5497cfd-wfbxn
Hostname: canary-6cc5497cfd-wfbxn
Hostname: canary-6cc5497cfd-wfbxn
Hostname: canary-6cc5497cfd-wfbxn
Hostname: canary-6cc5497cfd-wfbxn
Hostname: canary-6cc5497cfd-wfbxn
Hostname: canary-6cc5497cfd-wfbxn
Hostname: canary-6cc5497cfd-wfbxn
Hostname: canary-6cc5497cfd-wfbxn
Hostname: canary-6cc5497cfd-wfbxn



3. 基于 Cookie:与基于 Request Header 的 annotation 用法规则类似。例如在 A/B 测试场景下,需要让地域为北京的用户访问 Canary 版本。那么当 cookie 的 annotation 设置为 nginx.ingress.kubernetes.io/canary-by-cookie: "users_from_Beijing",此时后台可对登录的用户请求进行检查,如果该用户访问源来自北京则设置 cookie users_from_Beijing 的值为 always,这样就可以确保北京的用户仅访问 Canary 版本。


同样更新 Canary 版本的 Ingress 资源对象,采用基于 Cookie 来进行流量切分,

annotations:
  nginx.ingress.kubernetes.io/canary: "true" # 要开启灰度发布机制,首先需要启用 Canary
  nginx.ingress.kubernetes.io/canary-by-cookie: "users_from_Beijing" # 基于 cookie
  nginx.ingress.kubernetes.io/canary-weight: "30" # 会被忽略,因为配置了 canary-by-cookie
  


更新上面的 Ingress 资源对象后,在请求中设置一个 users_from_Beijing=always 的 Cookie 值,再次访问应用的域名。

% for i in $(seq 1 10); do curl -s -b "users_from_Beijing=always" abc.abc.com | grep "Hostname"; done
Hostname: canary-6cc5497cfd-wfbxn
Hostname: canary-6cc5497cfd-wfbxn
Hostname: canary-6cc5497cfd-wfbxn
Hostname: canary-6cc5497cfd-wfbxn
Hostname: canary-6cc5497cfd-wfbxn
Hostname: canary-6cc5497cfd-wfbxn
Hostname: canary-6cc5497cfd-wfbxn
Hostname: canary-6cc5497cfd-wfbxn
Hostname: canary-6cc5497cfd-wfbxn
Hostname: canary-6cc5497cfd-wfbxn

可以看到应用都被路由到了 Canary 版本的应用中去了,如果将这个 Cookie 值设置为 never,则不会路由到 Canary 应用中。

% for i in $(seq 1 10); do curl -s -b "users_from_Beijing=never" abc.abc.com | grep "Hostname"; done
Hostname: production-5d84fbdb58-69lgg
Hostname: production-5d84fbdb58-69lgg
Hostname: production-5d84fbdb58-69lgg
Hostname: production-5d84fbdb58-69lgg
Hostname: production-5d84fbdb58-69lgg
Hostname: production-5d84fbdb58-69lgg
Hostname: production-5d84fbdb58-69lgg
Hostname: production-5d84fbdb58-69lgg
Hostname: production-5d84fbdb58-69lgg
Hostname: production-5d84fbdb58-69lgg



HTTPS

如果需要用 HTTPS 来访问这个应用的话,就需要监听 443 端口了,同样用 HTTPS 访问应用必然就需要证书,这里用 openssl 来创建一个自签名的证书:

openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/CN=foo.bar.com"


然后通过 Secret 对象来引用证书文件:

# 要注意证书文件名称必须是 tls.crt 和 tls.key
$ kubectl create secret tls foo-tls --cert=tls.crt --key=tls.key
secret/who-tls created


这个时候就可以创建一个 HTTPS 访问应用的:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-with-auth
  annotations:
    # 认证类型
    nginx.ingress.kubernetes.io/auth-type: basic
    # 包含 user/password 定义的 secret 对象名
    nginx.ingress.kubernetes.io/auth-secret: basic-auth
    # 要显示的带有适当上下文的消息,说明需要身份验证的原因
    nginx.ingress.kubernetes.io/auth-realm: "Authentication Required - foo"
spec:
  ingressClassName: nginx
  tls: # 配置 tls 证书
    - hosts:
        - foo.bar.com
      secretName: foo-tls
  rules:
    - host: foo.bar.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: nginx
                port:
                  number: 80

除了自签名证书或者购买正规机构的 CA 证书之外,还可以通过一些工具来自动生成合法的证书,cert-manager 是一个云原生证书管理开源项目,可以用于在 Kubernetes 集群中提供 HTTPS 证书并自动续期,支持 Let's Encrypt/HashiCorp/Vault 这些免费证书的签发。在 Kubernetes 中,可以通过 Kubernetes Ingress 和 Let's Encrypt 实现外部服务的自动化 HTTPS。



TCP 与 UDP

由于在 Ingress 资源对象中没有直接对 TCP 或 UDP 服务的支持,要在 ingress-nginx 中提供支持,需要在控制器启动参数中添加 --tcp-services-configmap--udp-services-configmap 标志指向一个 ConfigMap,其中的 key 是要使用的外部端口,value 值是使用格式 <namespace/service name>:<service port>:[PROXY]:[PROXY] 暴露的服务,端口可以使用端口号或者端口名称,最后两个字段是可选的,用于配置 PROXY 代理。


比如现在要通过 ingress-nginx 来暴露一个 MongoDB 服务,首先创建如下的应用:

# mongo.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mongo
  labels:
    app: mongo
spec:
  selector:
    matchLabels:
      app: mongo
  template:
    metadata:
      labels:
        app: mongo
    spec:
      volumes:
        - name: data
          emptyDir: {}
      containers:
        - name: mongo
          image: mongo:4.0
          ports:
            - containerPort: 27017
          volumeMounts:
            - name: data
              mountPath: /data/db
---
apiVersion: v1
kind: Service
metadata:
  name: mongo
spec:
  selector:
    app: mongo
  ports:
    - port: 27017


部署

% kubectl apply -f mongo.yaml -n wsj
deployment.apps/mongo created
service/mongo created


查看

% kubectl get svc mongo -n wsj
NAME    TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)     AGE
mongo   ClusterIP   172.17.115.130   <none>        27017/TCP   53s


% kubectl get deploy mongo -n wsj
NAME    READY   UP-TO-DATE   AVAILABLE   AGE
mongo   1/1     1            1           90s


要通过 ingress-nginx 来暴露上面的 MongoDB 服务,需要创建一个如下所示的 ConfigMap:(因为涉及修改公共服务,仅做记录)

# tcp-ingress.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: ingress-nginx-tcp
  namespace: ingress-nginx
data:
  "27017": wsj/mongo:27017


然后在 ingress-nginx 的启动参数中添加 --tcp-services-configmap=$(POD_NAMESPACE)/ingress-nginx-tcp 这样的配置:

containers:
  - args:
      - /nginx-ingress-controller
      - --publish-service=$(POD_NAMESPACE)/ingress-nginx-controller
      - --election-id=ingress-nginx-leader
      - --controller-class=k8s.io/ingress-nginx
      - --ingress-class=nginx
      - --configmap=$(POD_NAMESPACE)/ingress-nginx-controller
      - --tcp-services-configmap=$(POD_NAMESPACE)/ingress-nginx-tcp
      - --validating-webhook=:8443
      - --validating-webhook-certificate=/usr/local/certificates/cert
      - --validating-webhook-key=/usr/local/certificates/key
    env:


重新部署即可。由于我们这里安装的 ingress-nginx 是通过 LoadBalancer 的 Service 暴露出去的,那么自然我们也需要通过 Service 去暴露我们这里的 tcp 端口,所以我们还需要更新 ingress-nginx 的 Service 对象,如下所示:

apiVersion: v1
kind: Service
metadata:
  labels:
    app.kubernetes.io/component: controller
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.5.1
  annotations:
    lb.kubesphere.io/v1alpha1: openelb
    protocol.openelb.kubesphere.io/v1alpha1: layer2
    eip.openelb.kubesphere.io/v1alpha2: eip-pool
  name: ingress-nginx-controller
  namespace: ingress-nginx
spec:
  externalTrafficPolicy: Local
  ipFamilies:
    - IPv4
  ipFamilyPolicy: SingleStack
  ports:
    - appProtocol: http
      name: http
      port: 80
      protocol: TCP
      targetPort: http
    - appProtocol: https
      name: https
      port: 443
      protocol: TCP
      targetPort: https
    - name: mongo # 暴露 27017 端口
      port: 27017
      protocol: TCP
      targetPort: 27017
  selector:
    app.kubernetes.io/component: controller
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
  type: LoadBalancer


更新该 Service 对象即可:

☸ ➜ kubectl get svc ingress-nginx-controller -n ingress-nginx
NAME                       TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)                                      AGE
ingress-nginx-controller   LoadBalancer   10.96.127.133   172.18.0.10   80:30877/TCP,443:30615/TCP,27017:30696/TCP   3d18h

现在我们就可以通过 LB 地址 172.18.0.10 加上暴露的 27017 端口去访问 Mongo 服务了,比如我们这里在节点上安装了 MongoDB 客户端 mongosh,使用命令 mongosh "mongodb://172.18.0.10:27017" 就可以访问到我们的 Mongo 服务了:

☸ ➜ mongosh "mongodb://172.18.0.10:27017"
Current Mongosh Log ID: 63f0814f292e3b15b3db5e2d
Connecting to:          mongodb://172.18.0.10:27017/?directConnection=true&appName=mongosh+1.7.1
Using MongoDB:          4.0.28
Using Mongosh:          1.7.1

For mongosh info see: https://docs.mongodb.com/mongodb-shell/

# ......

test> show dbs
admin   32.00 KiB
config  12.00 KiB
local   32.00 KiB
test>


同样的我们也可以去查看最终生成的 nginx.conf 配置文件:

☸ ➜ kubectl exec -it ingress-nginx-controller-7dfbf8d769-jfv27 -n ingress-nginx -- cat /etc/nginx/nginx.conf
......
    # TCP services
    server {
            preread_by_lua_block {
                    ngx.var.proxy_upstream_name="tcp-default-mongo-27017";
            }

            listen                  27017;

            listen                  [::]:27017;

            proxy_timeout           600s;
            proxy_next_upstream     on;
            proxy_next_upstream_timeout 600s;
            proxy_next_upstream_tries   3;

            proxy_pass              upstream_balancer;

    }

    # UDP services

    # Stream Snippets


TCP 相关的配置位于 stream 配置块下面。从 Nginx 1.9.13 版本开始提供 UDP 负载均衡,同样我们也可以在 ingress-nginx 中来代理 UDP 服务,比如我们可以去暴露 kube-dns 的服务,同样需要创建一个如下所示的 ConfigMap:

apiVersion: v1
kind: ConfigMap
metadata:
  name: ingress-nginx-udp
  namespace: ingress-nginx
data:
  53: "kube-system/kube-dns:53"

然后需要在 ingress-nginx 参数中添加一个 - --udp-services-configmap=$(POD_NAMESPACE)ingress-nginx-udp 这样的配置,Service 中也要加上暴露的 53 端口,然后重新更新即可。


全局配置

除了可以通过 annotations 对指定的 Ingress 进行定制之外,还可以配置 ingress-nginx 的全局配置,在控制器启动参数中通过标志 --configmap 指定了一个全局的 ConfigMap 对象,可以将全局的一些配置直接定义在该对象中即可:

containers:
  - args:
    - /nginx-ingress-controller
    - --configmap=$(POD_NAMESPACE)/ingress-nginx-controller
    ......


这里用于全局配置的 ConfigMap 名就为 ingress-nginx-controller

☸ ➜ kubectl get configmap ingress-nginx-controller -n ingress-nginx
NAME                       DATA   AGE
ingress-nginx-controller   1      7d


比如可以添加如下所示的一些常用配置:

➜ kubectl edit configmap ingress-nginx-controller -n ingress-nginx
apiVersion: v1
data:
  allow-snippet-annotations: "true"
  client-header-buffer-size: 32k
  client-max-body-size: 5m
  use-gzip: "true"
  gzip-level: "7"
  large-client-header-buffers: 4 32k
  proxy-connect-timeout: 11s
  proxy-read-timeout: 12s
  keep-alive: "75"   # 启用keep-alive,连接复用,提高QPS
  keep-alive-requests: "100"
  upstream-keepalive-connections: "10000"
  upstream-keepalive-requests: "100"
  upstream-keepalive-timeout: "60"
  disable-ipv6: "true"
  disable-ipv6-dns: "true"
  max-worker-connections: "65535"
  max-worker-open-files: "10240"
kind: ConfigMap
......


修改完成后 Nginx 配置会自动重载生效,可以查看 nginx.conf 配置文件进行验证:

☸ ➜ kubectl exec -it ingress-nginx-controller-gc582 -n ingress-nginx -- cat /etc/nginx/nginx.conf |grep large_client_header_buffers
        large_client_header_buffers     4 32k;


此外往往还需要对 ingress-nginx 部署的节点进行性能优化,修改一些内核参数,使得适配 Nginx 的使用场景,一般是直接去修改节点上的内核参数(可以参考官方博客 https://www.nginx.com/blog/tuning-nginx/ 进行调整),为了能够统一管理,可以使用 initContainers 来进行配置:

initContainers:
- command:
  - /bin/sh
  - -c
  - |
    mount -o remount rw /proc/sys
    sysctl -w net.core.somaxconn=655350  # 积压队列设置,具体的配置视具体情况而定
    sysctl -w net.ipv4.tcp_tw_reuse=1
    sysctl -w net.ipv4.ip_local_port_range="1024 65535"
    sysctl -w fs.file-max=1048576
    sysctl -w fs.inotify.max_user_instances=16384
    sysctl -w fs.inotify.max_user_watches=524288
    sysctl -w fs.inotify.max_queued_events=16384
image: busybox
imagePullPolicy: IfNotPresent
name: init-sysctl
securityContext:
  capabilities:
    add:
    - SYS_ADMIN
    drop:
    - ALL
......

部署完成后通过 initContainers 就可以修改节点内核参数了,生产环境建议对节点内核参数进行相应的优化。性能优化需要有丰富的经验,关于 nginx 的性能优化可以参考文章 https://cloud.tencent.com/developer/article/1026833


gRPC

ingress-nginx 控制器同样也支持 gRPC 服务的。gRPC 是 Google 开源的一个高性能 RPC 通信框架,通过 Protocol Buffers 作为其 IDL,在不同语言开发的平台上使用,同时 gRPC 基于 HTTP/2 协议实现,提供了多路复用、头部压缩、流控等特性,极大地提高了客户端与服务端的通信效率。

Kubernetes 网络之 Ingress 介绍_ingress-nginx_08


在 gRPC 服务中,客户端应用可以同本地方法一样调用到位于不同服务器上的服务端应用方法,可以很方便地创建分布式应用和服务。同其他 RPC 框架一样,gRPC 也需要定义一个服务接口,同时指定被远程调用的方法和返回类型。服务端需要实现被定义的接口,同时运行一个 gRPC 服务器来处理客户端请求。


这里使用 gRPC 示例应用 https://github.com/grpc/grpc-go/blob/master/examples/features/reflection/server/main.go 来进行说明。首先需要将该应用构建成一个容器镜像,可以用如下的 Dockerfile 进行构建:

FROM golang:buster as build

WORKDIR /go/src/greeter-server

RUN curl -o main.go https://raw.githubusercontent.com/grpc/grpc-go/master/examples/features/reflection/server/main.go && \
  go mod init greeter-server && \
  go mod tidy && \
  go build -o /greeter-server main.go

FROM gcr.io/distroless/base-debian10

COPY --from=build /greeter-server /

EXPOSE 50051

CMD ["/greeter-server"]

然后就可以使用该镜像来部署应用了,对应的资源清单文件如下所示:

# grpc-ingress-app.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: go-grpc-greeter-server
  name: go-grpc-greeter-server
spec:
  selector:
    matchLabels:
      app: go-grpc-greeter-server
  template:
    metadata:
      labels:
        app: go-grpc-greeter-server
    spec:
      containers:
        - image: xxxx/go-grpc-greeter-server:v0.1 # 换成你自己的镜像
          resources:
            limits:
              cpu: 100m
              memory: 100Mi
            requests:
              cpu: 50m
              memory: 50Mi
          name: go-grpc-greeter-server
          ports:
            - containerPort: 50051
---
apiVersion: v1
kind: Service
metadata:
  labels:
    app: go-grpc-greeter-server
  name: go-grpc-greeter-server
spec:
  ports:
    - port: 80
      protocol: TCP
      targetPort: 50051
  selector:
    app: go-grpc-greeter-server
  type: ClusterIP


直接应用上面的资源清单即可:

☸ ➜ kubectl apply -f grpc-ingress.yaml
☸ ➜ kubectl get pods -l app=go-grpc-greeter-server
NAME                                     READY   STATUS    RESTARTS   AGE
go-grpc-greeter-server-bb776ccb4-wqkx6   1/1     Running   0          98s
☸ ➜ kubectl get svc -l app=go-grpc-greeter-server
NAME                     TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)   AGE
go-grpc-greeter-server   ClusterIP   10.96.83.53   <none>        80/TCP    104s


接下来就需要创建 Ingress 对象来暴露上面的 gRPC 服务了,由于 gRPC 服务只运行在 HTTPS 端口(默认 443)上,因此需要域名和对应的 SSL 证书,这里我们使用域名 abc.abc.com 和自签的 SSL 证书。


申请 SSL 证书

使用 Ingress 转发 gRPC 服务需要对应域名拥有 SSL 证书,使用 TLS 协议进行通信,这里使用 OpenSSL 来生成自签证书。


复制以下内容并保存至 openssl.cnf 文件中。

[ req ]
#default_bits           = 2048
#default_md             = sha256
#default_keyfile        = privkey.pem
distinguished_name      = req_distinguished_name
attributes              = req_attributes
req_extensions          = v3_req

[ req_distinguished_name ]
countryName                     = Country Name (2 letter code)
countryName_min                 = 2
countryName_max                 = 2
stateOrProvinceName             = State or Province Name (full name)
localityName                    = Locality Name (eg, city)
0.organizationName              = Organization Name (eg, company)
organizationalUnitName          = Organizational Unit Name (eg, section)
commonName                      = Common Name (eg, fully qualified host name)
commonName_max                  = 64
emailAddress                    = Email Address
emailAddress_max                = 64

[ req_attributes ]
challengePassword               = A challenge password
challengePassword_min           = 4
challengePassword_max           = 20

[v3_req]
# Extensions to add to a certificate request
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
subjectAltName = @alt_names

[alt_names]
DNS.1 = abc.abc.com


然后执行以下命令签署证书请求:

$ openssl req -new -nodes -keyout grpc.key -out grpc.csr -config openssl.cnf -subj "/C=CN/ST=Beijing/L=Beijing/O=abcabc/OU=TrainService/CN=abc.abc.com"


然后执行以下命令签署证书:

$ openssl x509 -req -days 3650 -in grpc.csr -signkey grpc.key -out grpc.crt -extensions v3_req -extfile openssl.cnf


命令执行成功后,可得到证书 grpc.crt 与私钥文件 grpc.key,然后执行以下命令将名称为 grpc-secret 的 TLS Secret 添加到集群中。

$ kubectl create secret tls grpc-secret --key grpc.key --cert grpc.crt



创建 Ingress 资源

然后创建如下所示的 Ingress 对象来暴露 gRPC 服务:

# grpc-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
    # 必须要配置以指明后端服务为gRPC服务
    nginx.ingress.kubernetes.io/backend-protocol: "GRPC"
  name: grpc-ingress
  namespace: default
spec:
  ingressClassName: nginx
  rules:
    - host: abc.abc.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: go-grpc-greeter-server
                port:
                  number: 80
  tls:
    - secretName: grpc-secret
      hosts:
        - abc.abc.com


注意在该对象中添加了一个注解 nginx.ingress.kubernetes.io/backend-protocol: "GRPC",表示后端服务为 gRPC 服务,所以必须要加上,同样直接创建该对象即可。

☸ ➜ kubectl get ingress grpc-ingress
NAME           CLASS   HOSTS                     ADDRESS       PORTS     AGE
grpc-ingress   nginx   abc.abc.com               172.18.0.10   80, 443   2m13s


测试

需要安装一个 gRPCurl 工具,该工具类似于 cURL,但用于与 gRPC 服务器交互的命令行工具。


本地安装 gRPCurl 工具后,可以输入 grpcurl <域名>:443 list 命令验证请求是否成功转发到后端服务。我们这里使用域名 abc.abc.com 以及自签证书,所以执行该命令的时候需要添加一个 -insecure 参数:

☸ ➜ grpcurl -insecure abc.abc.com:443 list
grpc.examples.echo.Echo
grpc.reflection.v1alpha.ServerReflection
helloworld.Greeter
☸ ➜ grpcurl -insecure abc.abc.com:443 helloworld.Greeter/SayHello
{
  "message": "Hello "
}

正常会输出如上所示的结果,表明流量被 Ingress 成功转发到了后端 gRPC 服务。



参考

kubernetes ingress

nginx-ingress-controller

ingress 介绍

图解 ingress

一代键客 ingress 实践练习

知乎 一个人 ingress

陈桂林博客

aliyun ingress

grpc 官网

tke 上 nginx-ingress 实现 grpc 转发



标签:Ingress,abc,name,Kubernetes,ingress,网络,nginx,io
From: https://blog.51cto.com/u_11060853/9016053

相关文章

  • Kubernetes-集群卸载Rook-Ceph
    删除storageclass和pvc(卸载卷、删除卷声明等,根据实际情况修改)kubectldelete-frook/deploy/examples/csi/cephfs/pod.yamlkubectldelete-frook/deploy/examples/csi/cephfs/pvc.yamlkubectldelete-nrook-cephcephblockpoolreplicapoolkubectldelete-frook/deploy/......
  • 智安网络|实现安全与网络功能一体化:SASE的全新安全策略
    随着企业信息化和数字化程度的不断提升,网络安全面临着前所未有的挑战。传统的网络安全模式已经无法满足日益复杂的安全需求。在这一背景下,安全访问服务边缘(SASE)崭露头角,并逐渐成为新一代网络安全架构的关键概念。企业网络的规模扩大和云计算、物联网等技术的广泛应用,传统的网络安全......
  • 网络攻防技术——XSS攻击
    实验7:XSS攻击实验(Elgg)实验内容:跨站点脚本(XSS)是一种常见于web应用程序中的计算机安全漏洞。此漏洞使攻击者有可能将恶意代码(如JavaScripts)注入受害者的web浏览器。为了演示攻击者可以做什么,我们在预先构建的UbuntuVM映像中设置了一个名为Elgg的web应用程序。我们已经注释掉了El......
  • 网络拓扑图怎么画最好?
    画拓扑图的方式有很多,在线软件,Visio,PPT,都是方法。问题是你要怎么从0到1,怎么样用拓扑图完美地把你的网络逻辑结构、思路呈现出来。没经验的朋友真的不知道从哪里上手。今天就给你来一篇绘制拓扑图详解,从一页白纸开始,教你怎么从0到1亲手绘制一张拓扑图。1、什么是网络拓扑(Topology)?01......
  • 网络地址转换(NAT)之报文跟踪
    网络地址转换(NAT)之报文跟踪来源  https://fedoramagazine.org/network-address-translation-part-1-packet-tracing/参考 https://linux.cn/article-13364-1.html 这是有关 网络地址转换(networkaddresstranslation)(NAT)的系列文章中的第一篇。这一部分将展示如何使用i......
  • 网络攻防技术——DNS攻击
    实验11:TCP攻击实验实验内容:本实验的目标是让学生获得对DNS(域名系统)的各种攻击的第一手经验。DNS是互联网的电话簿;它将主机名转换为IP地址,反之亦然。这种转换是通过DNS解析实现的,这种解析发生在幕后。DNS欺骗攻击以各种方式操纵此解析过程,目的是将用户误导到其他目的地,这些目的地......
  • James F. Kurose, Keith W. Ross著,陈鸣译,《计算机网络-自顶向下方法》(第6版),机械工业出
    JamesF.Kurose,KeithW.Ross著,陈鸣译,《计算机网络-自顶向下方法》(第6版),机械工业出版社,2014 n计算机网络课程学习什么?nn计算机网络是计算机专业和信息安全专业的专业基础课程,课程主要介绍计算机网络的基本原理和技术,通过对网络协议模型多层次功能结构的展开与探讨,详细介绍......
  • Chrome 不能打开 kubernetes Dashboard 解决方法
    Chrome不能打开kubernetesDashboard解决方法步骤:mkdirkey&&cdkey#生成证书opensslgenrsa-outdashboard.key2048opensslreq-new-outdashboard.csr-keydashboard.key-subj'/CN=kubernetes-dashboard-certs'opensslx509-req-indashboard.csr-s......
  • 【网络安全入门】什么是DOS?DOS攻击类型有哪些?
    大家都知道,DOS攻击是网络安全中比较常见的攻击形式,它的类型和种类有很多,具有很大的危害,而且在网络生活中DOS攻击是不可避免的,那么到底什么是DOS?DOS攻击类型有哪些?虽然DOS攻击不可避免,但掌握攻击类型可以帮助我们有效预防DOS攻击,所以快跟着小编来了解一下吧。什么是DOS?......
  • 12.网络流量分析
    网络分析:显示网络流量:adbshelldumpsysnetstats分块展示:Activeinterfaces:活动接口ActiveUIDinterfaces:活动UID接口Devstatistics:开发统计信息Xtstatistics:Xt统计信息UIDstatistics:UID统计信息UIDtagstatistics:UID代码统计信息活动接口和活动UID接口:A......