1W字长文:K8S Ingress 原理和实操

K8S Ingress原理和实操

背景:

云原生时代如火如荼,掌握云原生的架构和开发,是Java开发高薪的必备技能。

SVC、Ingress原理和实操,是云原生的基础知识。

这里尼恩给大家 调优,做一下Ingress 的系统化、体系化的梳理。

在面试之前,也可以复习一下,使得大家可以充分展示一下大家雄厚的 “技术肌肉”,让面试官爱到 “不能自已、口水直流”

在这里也一并把这些宝贵内容作为“K8S云原生学习”重要的内容,收入尼恩的《K8S学习圣经》,供后面的小伙伴参考,提升大家的 3高 架构、设计、开发水平。

注:本文以 PDF 持续更新,最新尼恩 架构笔记、面试题 的PDF文件,请从这里获取:码云

先介绍一下svc作用与不足,再介绍Ingress原理和实操

svc的作用与不足

service的作用体现在两个方面:

  • 对集群内部,它不断跟踪pod的变化,更新endpoint中对应pod的对象,提供了ip不断变化的pod的服务发现机制;
  • 对集群外部,他类似负载均衡器,可以在集群内外部对pod进行访问。

​ 在Kubernetes中,Pod的IP地址和service的ClusterIP仅可以在集群网络内部做用,对于集群外的应用是不可见的。

为了使外部的应用能够访问集群内的服务,Kubernetes目前提供了以下几种方案:

  • NodePort:将service暴露在节点网络上,NodePort背后就是Kube-Proxy,Kube-Proxy是沟通service网络、Pod网络和节点网络的桥梁。
    测试环境使用还行,当有几十上百的服务在集群中运行时,NodePort的瑞口管理就是个灾难。因为每个端口只能是一种服务,端口范围只能是 30000-32767。
  • LoadBalancer:通过设置LoadBalancer映射到云服务商提供的LoadBalancer地址。这种用法仅用于在公有云服务提供商的云平台上设置Servic的场景。
    受限于云平台,且通常在云平台部署LoadBalancer还需要额外的费用。

Ingress 的来源

Ingress:只需一个或者少量的公网IP和LB,即可同时将多个HTTP服务暴露到外网,七层反向代理。

可以简单理解为service的service,它其实就是一组基于域名和URL路径,把用户的请求转发到一个或多个service的规则。

Ingress解决的是新的服务加入后,域名和服务的对应问题,基本上是一个ingress的对象,通过yaml进行创建和更新进行加载。工作机制大致可以用下图表示:

1W字长文:K8S Ingress 原理和实操_第1张图片

ingress相当于一个7层的负载均衡器,是k8s对反向代理的一个抽象。

大概的工作原理也确实类似于Nginx,可以理解成在 Ingress 里建立一个个映射规则 , ingress Controller 通过监听 Ingress这个api对象里的配置规则并转化成 Nginx 的配置(kubernetes声明式API和控制循环) , 然后对外部提供服务。

1W字长文:K8S Ingress 原理和实操_第2张图片

Ingress的构成

Ingress资源对象,用于将不同URL的访问请求转发到后端不同的Service,以实现HTTP层的业务路由机制。

Kubernetes使用一个Ingress策略定义和一个具体的Ingress Controller,两者结合并实现了一个完整的Ingress负载均衡器。

Ingress Controller将基于Ingress规则将客户请求直接转发到Service对应的后端Endpoint上,这样会跳过kube-proxy的转发功能,kube-proxy 不再起作用。

在定义Ingress策略之前,需要先部署Ingress Controller,以实现为所有后端Service提供一个统一的入口。

Ingress Controller需要实现基于不同HTTP URL向后转发的负载分发机制,并可以灵活设置7层的负载分发策略。如果公有云服务商提供该类型的HTTP路由LoadBalancer,则可以设置其为Ingress Controller.

在Kubernetes中,Ingress Controller将以Pod的形式运行,监控apiserver的/ingress端口后的backend services, 如果service发生变化,则Ingress Controller 应用自动更新其转发规则

什么是Ingress Controller?

在定义Ingress策略之前,需要先部署Ingress Controller,为所有后端Service都提供一个统一的入口。

Ingress Controller需要实现基于不同HTTP URL向后转发的负载分发规则,并可以灵活设置7层负载分发策略。

An API object that manages external access to the services in a cluster, typically HTTP. Ingress can provide load balancing, SSL termination and name-based virtual hosting.

引用官方关于ingress的介绍我们可以得知,ingress是一种通过http协议暴露kubernetes内部服务的api对象,即充当Edge Router边界路由器的角色对外基于七层的负载均衡调度机制,

Ingress 能够提供以下几个功能:

  • 负载均衡,将请求自动负载均衡到后端的Pod上;
  • SSL加密,客户端到Ingress Controller为https加密,到后端Pod为明文的http;
  • 基于名称的虚拟主机,提供基于域名或URI更灵活的路由方式

1W字长文:K8S Ingress 原理和实操_第3张图片

在Kubernetes中,Ingress Controller将以Pod的形式运行,监控API Server的/ingress接口后端的backend services,如果Service发生变化,则Ingress Controller应自动更新其转发规则。

Ingress包含的组件有:

1W字长文:K8S Ingress 原理和实操_第4张图片

  • Ingress 资源对象 ,kubernetes的一个资源对象,用于编写 资源 配置规则;
    Ingress配置规则 控制器通过service服务发现机制,动态实现后端Pod路由转发规则的实现;
  • Ingress Controller,Ingress控制器,
    ingress-controller,调用k8s的api动态感知集群中Pod的变化而动态更新配置文件,并重载proxy 反向代理负载均衡器 的配置
    ingress-controller监听apiserver,获取服务新增,删除等变化,并结合ingress规则动态更新到反向代理负载均衡器上,并重载配置使其生效
  • proxy 反向代理负载均衡器 ,接收并按照ingress定义的规则进行转发,通常为nginx,haproxy,traefik等,常用的是 Ingress-nginx
    反向代理负载均衡器,实现七层转发的Edge Router 边沿路由器,proxy 通过ingress-controller,proxy 需要ingress-controller监听apiserver,感知集群中Pod的变化而动态更新配置文件,
    通常以DaemonSets或Deployments的形式部署,并对外暴露80和443端口,
    对于DaemonSets来说,一般是以hostNetwork或者hostPort的形式暴露,
    对于Deployments来说则以NodePort的方式暴露,控制器的多个节点则借助外部负载均衡ExternalLB以实现统一接入;

从 开发人员的角度讲,ingress包括:ingress controller和ingress resources

ingress resources:这个就是一个类型为Ingress的k8s api对象了,这部分则是面向开发人员。Ingress解决的是新的服务加入后,域名和服务的对应问题,基本上是一个ingress的对象,通过yaml进行创建和更新进行加载。

ingress controller:包括 proxy+ Controller 两个部分,proxy比如nginx, Haproxy, trafik, Istio,proxy一般通过service 接收流量,其中service的类型可以是NodePort或者LoadBalancer。Ingress Controller是将Ingress这种变化生成一段proxy的配置,然后将这个配置通过Kubernetes API写到proxy的Pod中,然后reload.

注意:写入proxy 配置文件如 nginx.conf 的不是backend service的地址,而是backend service 的 pod 的地址,避免在 service 在增加一层负载均衡转发

1W字长文:K8S Ingress 原理和实操_第5张图片

简而言之,ingress Controller借助service的服务发现机制,实现配置的动态更新以实现Pod的负载均衡机制实现,

由于涉及到proxy 的动态更新,目前社区Ingress Controller大体包含两种类型的控制器:

  • 传统的七层负载均衡如Nginx,HAproxy,开发了适应微服务应用的插件,具有成熟,高性能等优点;
  • 新型微服务负载均衡如Traefik,Envoy,Istio,专门适用于微服务+容器化应用场景,具有动态更新特点;
类型 常见类型 优点 缺点
传统负载均衡 nginx,haproxy 成熟,稳定,高性能 动态更新需reload配置文件
微服务负载均衡 Traefik,Envoy,Istio 天生为微服务而生,动态更新 性能还有待提升

如果公有云服务商能够提供该类型的HTTP路由LoadBalancer,则也可设置其为Ingress Controller。

Ingress 资源对象

ingress是一个API对象,通过yaml文件来配置,ingress对象的作用是定义请求如何转发到 backend service的规则,可以理解为配置模板。

ingress通过http或https暴露集群backend service,给backend service提供外部URI、负载均衡、SSL/TLS能力以及基于域名的反向代理。ingress要依靠ingress-controiler来具体实现以上功能。

简单来说,ingress-controller才是负责具体转发的组件,通过各种方式将它暴露在集群入口,外部对集群的请求流量会先到ingress-controller,而ingress对象是用来告诉ingress-controller该如何转发请求,比如哪些域名哪些path要转发到哪些服务等等。

Ingress 格式示例:

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: abc-ingress
  annotations: 
    kubernetes.io/ingress.class: "nginx"
    nginx.ingress.kubernetes.io/use-regex: "true"
spec:
  tls:
  - hosts:
    - api.abc.com
    secretName: abc-tls
  rules:
  - host: api.abc.com
    http:
      paths:
      - backend:
          serviceName: apiserver
          servicePort: 80
  - host: www.abc.com
    http:
      paths:
      - path: /image/*
        backend:
          serviceName: fileserver
          servicePort: 80
  - host: www.abc.com
    http:
      paths:
      - backend:
          serviceName: feserver
          servicePort: 8080

每个 Ingress 都需要配置 rules,目前 Kubernetes 仅支持 http 规则。上面的示例表示请求/image/* 时转发到服务 fileserver的 80 端口。

与其他k8s对象一样,ingress配置也包含了apiVersion、kind、metadata、spec等关键字段。有几个关注的在spec字段中,tls用于定义https密钥、证书。rule用于指定请求路由规则。

这里值得关注的是metadata.annotations字段。在ingress配置中,annotations很重要。

前面有说ingress-controller有很多不同的实现,而不同的ingress-controller就可以根据 “kubernetes.io/ingress.class:” 来判断要使用哪些ingress配置,同时,不同的ingress-controller也有对应的annotations配置,用于自定义一些参数。

例如上面配置的’nginx.ingress.kubernetes.io/use-regex: “true”',最终是在生成nginx配置中,会采用location ~来表示正则匹配。

ingress-controller组件

ingress-controller是具体实现反向代理及负载均衡的程序,对ingress定义的规则进行解析,根据配置的规则来实现请求转发。

ingress-controller并不是k8s自带的组件,实际上ingess-controller只是一个统称,用户可以选择不同的ingress-controller实现,

目前,由k8s维护的ingress-controller只有google云的GCE与ingress-nginx两个,其他还有很多第三方维护的ingress-controller,比如Contour, Haproxy, trafik, Istio,具体可以参考官方文档。

一般来说,ingress-controller的形式都是一个pod,里面跑着daemon程序和反向代理程序。

daemon负责不断监控集群的变化,根据ingress对象生成配置并应用新配置到反向代理,比如ingress-nginx就是动态生成nginx配置,动态更新upstreanm,并在需要的时候reload程序应用新配置。

为了方便,后面的例子都以k8s官方维护的ingress-nginx为例。

Ingress-Nginx github 地址: GitHub - kubernetes/ingress-nginx: Ingress-NGINX Controller for Kubernetes

Ingress-Nginx 官方网站: Welcome - NGINX Ingress Controller

nginx-ingress-controller

nginx-ingress-controller是一个使用Nginx来实现一个Ingress Controller,需要实现的基本逻辑如下。

  1. 监听API Server,获取全部Ingress的定义
  2. 基于Ingress的定义,生成Nginx所需的配置文件/etc/nginx/nginx.conf。
  3. 执行nginx -s reload命令,重新加载nginx.conf配置文件的内容。

部署ingress-controller

Minikube安装Ingress

Ingress安装很简单,Minikube里面带了Ingress附件。

  • 安装Ingress
minikube addons enable ingress
  • 禁用Ingress
minikube addons disable ingress

1W字长文:K8S Ingress 原理和实操_第6张图片

尼恩提示:

Minikube使用addon插件的模式安装Ingress很简单,但是,插件式部署是了解不到ingress 的底层原理

要了解 ingress 的底层原理, 还是得手工部署的模式。

手工部署ingress-controller pod及相关资源

官方文档中,部署ingress-nginx 只要简单的执行一个yaml

wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/nginx-0.25.0/deploy/static/mandatory.yaml

上面的下载地址可能无法下载,可用国内的Gitee地址

cd /vagrant/chapter28/ingress

wget https://gitee.com/mirrors/ingress-nginx/raw/nginx-0.25.0/deploy/static/mandatory.yaml

wget https://gitee.com/mirrors/ingress-nginx/raw/nginx-0.30.0/deploy/static/mandatory.yaml


wget https://gitee.com/mirrors/ingress-nginx/raw/controller-v1.0.0/deploy/static/provider/cloud/deploy.yaml

下载之后,到了window本地,使用editplus就可编辑了

1W字长文:K8S Ingress 原理和实操_第7张图片

使用editplus打开mandatory.yaml,

mandatory.yaml文件中包含了很多资源的创建,包括namespace、ConfigMap、role,ServiceAccount等等所有部署ingress-controller需要的资源。

1W字长文:K8S Ingress 原理和实操_第8张图片

重点看下deployment部分:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-ingress-controller
  namespace: ingress-nginx
  labels:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      app.kubernetes.io/name: ingress-nginx
      app.kubernetes.io/part-of: ingress-nginx
  template:
    metadata:
      labels:
        app.kubernetes.io/name: ingress-nginx
        app.kubernetes.io/part-of: ingress-nginx
      annotations:
        prometheus.io/port: "10254"
        prometheus.io/scrape: "true"
    spec:
      serviceAccountName: nginx-ingress-serviceaccount
      containers:
        - name: nginx-ingress-controller
          image: quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.25.0
          args:
            - /nginx-ingress-controller
            - --configmap=$(POD_NAMESPACE)/nginx-configuration
            - --tcp-services-configmap=$(POD_NAMESPACE)/tcp-services
            - --udp-services-configmap=$(POD_NAMESPACE)/udp-services
            - --publish-service=$(POD_NAMESPACE)/ingress-nginx
            - --annotations-prefix=nginx.ingress.kubernetes.io
          securityContext:
            allowPrivilegeEscalation: true
            capabilities:
              drop:
                - ALL
              add:
                - NET_BIND_SERVICE
            # www-data -> 33
            runAsUser: 33
          env:
            - name: POD_NAME
              valueFrom:
                fieldRef:
                  fieldPath: metadata.name
            - name: POD_NAMESPACE
              valueFrom:
                fieldRef:
                  fieldPath: metadata.namespace
          ports:
            - name: http
              containerPort: 80
            - name: https
              containerPort: 443
          livenessProbe:
            failureThreshold: 3
            httpGet:
              path: /healthz
              port: 10254
              scheme: HTTP
            initialDelaySeconds: 10
            periodSeconds: 10
            successThreshold: 1
            timeoutSeconds: 10
          readinessProbe:
            failureThreshold: 3
            httpGet:
              path: /healthz
              port: 10254
              scheme: HTTP
            periodSeconds: 10
            successThreshold: 1
            timeoutSeconds: 10

可以看到主要使用了“quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.25.0”这个镜像,

并且deployment指定了一些启动参数。

同时开放了80与443两个端口,并在10254端口做了健康检查。

ingress版本的问题

网上的资料一般是基于v0.30.0来安装,但是对于[email protected]来说要安装[email protected]以上版本(目前最新版本是v1.0.4,本文采用v1.0.0),

原因是 [email protected]版本不再支持v1beta1

如果安装[email protected]版本后启动pod有如下问题

Failed to list *v1beta1.Ingress: the server could not find the requested resource

有一个版本的支持情况(https://github.com/kubernetes/ingress-nginx/)

1W字长文:K8S Ingress 原理和实操_第9张图片

尼恩也是先用的 0.25,然后换成 0.30,再换成1.0.0

躺了很多的坑

调整RBAC api-versions 版本

早期版本:

RBAC相关资源从1.17版本开始改用rbac.authorization.k8s.io/v1,rbac.authorization.k8s.io/v1beta1在1.22版本即将弃用

查看 kubectl api-versions 版本

1W字长文:K8S Ingress 原理和实操_第10张图片

1.0.0的版本,不用改

部署 ingress-nginx

修改完后执行apply,并检查服务

kubectl apply  -f  deploy.yaml

kubectl delete -A ValidatingWebhookConfiguration ingress-nginx-admission

# 检查部署情况
kubectl get daemonset -n ingress-nginx

kubectl get po -n ingress-nginx -o wide
 
 
kubectl get svc -n ingress-nginx
 
kubectl logs -f nginx-ingress-controller-hrd46 -n ingress-nginx

1W字长文:K8S Ingress 原理和实操_第11张图片

可以看到,nginx-controller的pod已经部署上了,但是没有启动。

换成最新版本,就启动啦

配置ingress资源

部署完ingress-controller,接下来就按照测试的需求来创建ingress资源。

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-test
spec:
   ingressClassName: nginx #指定Ingress 类
   defaultBackend:      #默认后端路由
    service:
      name: service1
      port:
        number: 8008
  rules:
  - host: "foo.bar.com"  #主机精确匹配
    http:
      paths:
      - pathType: Prefix
        path: "/bar"
        backend:
          service:
            name: service1
            port:
              number: 8008
  - host: "*.foo.com"   #通配符匹配,bar.foo.com匹配,test.bar.foo.com不匹配
    http:
      paths:
      - pathType: Prefix   #以 / 分隔的 URL 路径前缀匹配,匹配区分大小写(必填)
        path: "/foo"
        backend:
          service:
            name: service2
            port:
              number: 9008

部署资源

kubectl apply -f app-service.yaml

curl http://192.168.49.2:30808/demo-provider/swagger-ui.html
kubectl apply -f app-service-2.yaml

curl http://192.168.49.2:30909/demo-provider/swagger-ui.html

kubectl apply -f ingresstest.yaml
kubectl delete -f ingresstest.yaml
curl http://10.100.15.33:30909/demo-provider/swagger-ui.html

curl http://10.100.15.33:30909/demo-provider/swagger-ui.html

kubectl get svc -A

curl http://foo.bar.com:32661/bar/

curl http://a.foo.com:32661/foo/

报错 failed calling webhook

Error from server (InternalError): error when creating "ingress-http.yaml": Internal error occurred: failed calling webhook "validate.nginx.ingress.kubernetes.io": failed to call webhook: Post "https://ingress-nginx-controller-admission.ingress-nginx.svc:443/networking/v1/ingresses?timeout=10s": x509: certificate signed by unknown authority
[root@k8s-master01 ~]# kubectl get -A ValidatingWebhookConfiguration
NAME                      WEBHOOKS   AGE
ingress-nginx-admission   1          52m
[root@k8s-master01 ~]# kubectl delete -A ValidatingWebhookConfiguration ingress-nginx-admission
validatingwebhookconfiguration.admissionregistration.k8s.io "ingress-nginx-admission" deleted
[root@k8s-master01 ~]# kubectl apply -f ingresstest.yaml

再次创建就发现没有问题了

1W字长文:K8S Ingress 原理和实操_第12张图片

获取 ingress 启动的svc

kubectl get svc -A

1W字长文:K8S Ingress 原理和实操_第13张图片

kubectl get svc -A

加上本地的host

部署好以后,做一条本地host来模拟解析 foo.bar.com 到node的ip地址。

echo 192.168.49.2 foo.bar.com  >>  /etc/hosts
echo 192.168.49.2 a.foo.com  >>  /etc/hosts

curl http://foo.bar.com/demo-provider/swagger-ui.html

1W字长文:K8S Ingress 原理和实操_第14张图片

测试访问

curl http://foo.bar.com:32661/bar/

curl http://a.foo.com:32661/foo/

1W字长文:K8S Ingress 原理和实操_第15张图片

curl http://foo.bar.com:32661/bar/ ,

请求不同的path已经按照需求请求到不同服务了,这里是 请求到service1

1W字长文:K8S Ingress 原理和实操_第16张图片

curl http://a.foo.com:32661/foo/

请求不同的path已经按照需求请求到不同服务了,这里是 请求到service2

如果没有路径, 则发生了404

1W字长文:K8S Ingress 原理和实操_第17张图片

实操的善后工作

实验完成,可以清理所有服务

kubectl delete all -l app=my-app
kubectl delete all -l app=my-app-2

k8s ingress的工作原理

通过实操可以发现,k8s ingress的两大实操部分:

(1)Ingress Controller 控制器

(2)Ingress 资源对象

要理解ingress,需要区分两个概念,ingress和ingress-controller:

  • ingress对象:
    指的是k8s中的一个api对象,一般用yaml配置。作用是定义请求如何转发到service的规则,可以理解为配置模板。
  • ingress-controller:
    具体实现反向代理及负载均衡的程序,对ingress定义的规则进行解析,根据配置的规则来实现请求转发。

简单来说,ingress-controller才是负责具体转发的组件,通过各种方式将它暴露在集群入口,外部对集群的请求流量会先到ingress-controller,而ingress对象是用来告诉ingress-controller该如何转发请求,比如哪些域名哪些path要转发到哪些服务等等。

(1)Ingress Controller 控制器

Ingress Controller实质上可以理解为是个监视器,

Ingress Controller通过不断地跟Kubernetes API Server打交道,实时的感知后端Service、Pod等变化,比如新增和减少Pod,Service增加与减少等;

当得到这些变化信息后,Ingress Controller在结合下文的Ingress生成配置,然后更新反向代理负载均衡器,并刷新其配置,达到服务发现的作用。

Ingress Controller是将Ingress这种变化生成一段Nginx的配置,然后将这个配置通过Kubernetes API写到Nginx的Pod中,然后reload.(注意:写入 nginx.conf 的不是service的地址,而是service backend 的 pod 的地址,避免在 service 在增加一层负载均衡转发)。

ingress-controller并不是k8s自带的组件,实际上ingress-controller只是一个统称,用户可以选择不同的ingress-controller实现,目前,由k8s维护的ingress-controller只有google云的GCE与ingress-nginx两个,其他还有很多第三方维护的ingress-controller,具体可以参考官方文档。

但是不管哪一种ingress-controller,实现的机制都大同小异,只是在具体配置上有差异。

一般来说,ingress-controller的形式都是一个pod,里面跑着daemon程序和反向代理程序。

daemon负责不断监控集群的变化,根据ingress对象生成配置并应用新配置到反向代理,比如nginx-ingress就是动态生成nginx配置,动态更新upstream,并在需要的时候reload程序应用新配置。为了方便,后面的例子都以k8s官方维护的nginx-ingress为例。

(2) Ingress 资源对象

Ingress简单理解就是个路由规则定义

比如某个域名对应某个Serivce,即当某个域名的请求进来时转发给某个Service;

这个规则将与Ingress Controller结合,然后Ingress Controller将其动态写入到负载均衡器中,从而实现整体的服务发现和负载均衡。

Ingress解决的是新的服务加入后,域名和服务的对应问题,基本上是一个ingress的对象,通过yaml进行创建和更新进行加载。

1W字长文:K8S Ingress 原理和实操_第18张图片

从上图可以很清晰的看到,实际上请求进行被负载均衡器拦截,比如nginx,然后Ingress Controller通过交互得知某个域名对应哪个Service,再通过跟Kubernetes API交互得知Service地址等信息;

综合以后生成配置文件实时写入负载均衡器,然后负载均衡器reload该规则便可实现服务发现,即动态映射。

从上图中可以很清晰的看到,实际上请求进来还是被负载均衡器拦截,比如 nginx,然后 Ingress Controller 通过跟 Ingress 交互得知某个域名对应哪个 service,再通过跟 kubernetes API 交互得知 service 地址等信息;综合以后生成配置文件,实时写入负载均衡器,然后负载均衡器 reload 该规则便可实现服务发现,即动态映射:

  1. ingress-controller通过和 kubernetes APIServer交互,动态的去感知集群中ingress规则变化;
  2. 然后读取它,按照自定义的规则,规则就是写明了哪个域名对应哪个service,生成一段nginx配置;
  3. 再写到nginx-ingress-controller的pod里,这个ingres-controller的pod里运行着一个Nginx服务,控制器会把生成的nginx置写入/etc/nginx.conf文件中;
  4. 最后reload一下使配置生效。以此达到域名区分配置和动态更新的作用。

详解ingress资源

ingress是一个API对象,和其他对象一样,通过yaml文件来配置。

ingress通过http或https暴露集群内部service,给service提供外部URL、负载均衡、SSL/TLS能力以及基于host的方向代理。ingress要依靠ingress-controller来具体实现以上功能。前一小节的图如果用ingress来表示,大概就是如下配置:

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: abc-ingress
  annotations: 
    kubernetes.io/ingress.class: "nginx"
    nginx.ingress.kubernetes.io/use-regex: "true"
spec:
  tls:
  - hosts:
    - api.abc.com
    secretName: abc-tls
  rules:
  - host: api.abc.com
    http:
      paths:
      - backend:
          serviceName: apiserver
          servicePort: 80
  - host: www.abc.com
    http:
      paths:
      - path: /image/*
        backend:
          serviceName: fileserver
          servicePort: 80
  - host: www.abc.com
    http:
      paths:
      - backend:
          serviceName: feserver
          servicePort: 8080

与其他k8s对象一样,ingress配置也包含了apiVersion、kind、metadata、spec等关键字段。

有几个关注的在spec字段中,tls用于定义https密钥、证书rule用于指定请求路由规则

这里值得关注的是metadata.annotations字段。

在ingress配置中,annotations很重要

前面有说ingress-controller有很多不同的实现,

而不同的ingress-controller就可以根据"kubernetes.io/ingress.class:"来判断要使用哪些ingress配置,

同时,不同的ingress-controller也有对应的annotations配置,用于自定义一些参数。

列如上面配置的’nginx.ingress.kubernetes.io/use-regex: “true”’,最终是在生成nginx配置中,会采用location ~来表示正则匹配。

首先我们先看个简单的资源示例。

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-nginx
  namespace: default
  annotations:
    kubernetes.io/ingress.class: "nginx"
spec:
  rules:
  - host: foo.bar.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: service1
            port:
              number: 8008

与所有其他 Kubernetes 资源一样,Ingress 需要使用 apiVersionkindmetadata 字段。

Ingress 对象的命名必须是合法的DNS子域名名称。

ingress规约提供了配置负载均衡器或者代理服务器所需的所有信息。 最重要的是,其中包含与所有传入请求匹配的规则列表。

Ingress 资源仅支持用于转发 HTTP 流量的规则。

ingress规则

​ 每个 HTTP 规则都包含:

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

​ 通常在 Ingress 控制器中会配置 defaultBackend(默认后端),服务不符合任何规约中 path 的请求。

DefaultBackend

没有 rules 的 Ingress 将所有流量发送到同一个默认后端。

defaultBackend 通常是ingress控制器的配置选项,而非在 Ingress 资源中指定。如果 hostspaths 都没有与 Ingress 对象中的 HTTP 请求匹配,则流量将路由到默认后端

资源后端

resource 后端是一个 ObjectRef,指向同一名字空间中的另一个 Kubernetes,将其作为 Ingress 对象。ResourceService 配置是互斥的,在 二者均被设置时会无法通过合法性检查。

Resource 后端的一种常见用法是将所有入站数据导向带有静态资产的对象存储后端。

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 中的每个路径都需要有对应的路径类型(Path Type)。

未明确设置 pathType 的路径无法通过合法性检查。当前支持的三种路径类型:

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

注意:如果路径的最后一个元素是请求路径中最后一个元素的子字符串,则不会匹配 (例如:/foo/bar 匹配 /foo/bar/baz, 但不匹配 /foo/barbaz)。

路径类型示例

类型 路径 请求路径 匹配与否?
Prefix / (所有路径)
Exact /foo /foo
Exact /foo /bar
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 否,使用默认后端
混合 /foo (Prefix), /foo (Exact) /foo 是,优选 Exact 类型

路径多重匹配

在某些情况下,Ingress 中的多条路径会匹配同一个请求。

这种情况下最长的匹配路径优先。

如果仍然有两条同等的匹配路径,则精确路径类型优先于前缀路径类型。

主机名通配符

主机名可以是精确匹配(例如“foo.bar.com”)或者使用通配符来匹配 (例如“*.foo.com”)。

精确匹配要求 HTTP host 头部字段与 host 字段值完全匹配。

通配符匹配则要求 HTTP host 头部字段与通配符规则中的后缀部分相同。

主机 host 头部 匹配与否?
*.foo.com bar.foo.com 基于相同的后缀匹配
*.foo.com baz.bar.foo.com 不匹配,通配符仅覆盖了一个 DNS 标签
*.foo.com foo.com 不匹配,通配符仅覆盖了一个 DNS 标签
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-nginx-demo
  namespace: default
  annotations:
    kubernetes.io/ingress.class: "nginx"
spec:
  rules:
  - host: "foo.bar.com"  #主机精确匹配
    http:
      paths:
      - pathType: Prefix
        path: "/bar"
        backend:
          service:
            name: service1
            port:
              number: 8008
  - host: "*.foo.com"   #通配符匹配,bar.foo.com匹配,test.bar.foo.com不匹配
    http:
      paths:
      - pathType: Prefix   #以 / 分隔的 URL 路径前缀匹配,匹配区分大小写(必填)
        path: "/foo"
        backend:
          service:
            name: service2
            port:
              number: 8008

ingress类

Ingress 可以由不同的控制器实现,不同的控制器 通常使用不同的配置。

每个 Ingress 应当指定一个类,也就是一个对 IngressClass 资源的引用。

IngressClass 资源包含额外的配置,其中包括应当实现该类的控制器名称。

# Source: ingress-nginx/templates/controller-ingressclass.yaml
# We don't support namespaced ingressClass yet
# So a ClusterRole and a ClusterRoleBinding is required
apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
  labels:
    helm.sh/chart: ingress-nginx-4.0.15
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/version: 1.1.1
    app.kubernetes.io/managed-by: Helm
    app.kubernetes.io/component: controller
  name: nginx
  namespace: ingress-nginx
spec:
  controller: k8s.io/ingress-nginx

​ IngressClass 资源包含可选的 parameters 字段,可用于为该类引用额外的、 特定于具体实现的配置。

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

参数(parameters)的具体类型取决于你在 .spec.controller 字段中指定的 Ingress 控制器.

名字空间域的参数

parameters 字段中scopenamespace 字段,可用来引用特定 于名字空间的资源,对 Ingress 类进行配置。

scope 字段默认为 Cluster,表示默认是集群作用域的资源。

scope 设置为 Namespace 并设置 namespace 字段就可以引用某特定 名字空间中的参数资源。

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
    namespace: external-configuration
    scope: Namespace

默认ingress类

可以将一个特定的 IngressClass 标记为集群默认 Ingress 类。

将 IngressClass 资源的 ingressclass.kubernetes.io/is-default-class 注解设置为 true 将确保新的未指定 ingressClassName 字段的 Ingress 能够分配为这个默认的 IngressClass.

注意: 如果集群中有多个 IngressClass 被标记为默认,准入控制器将阻止创建新的未指定 ingressClassName 的 Ingress 对象。

解决这个问题只需确保集群中最多只能有一个 IngressClass 被标记为默

ingress部署的三种模式

ingress的部署,需要考虑两个方面:

  1. ingress-controller是作为pod来运行的,以什么方式部署比较好
  2. ingress解决了把如何请求路由到集群内部,那它自己怎么暴露给外部比较好

模式一:NodePort模式的Service

同样用deployment模式部署ingress-controller,并创建type为NodePort的服务。

这样,ingress就会暴露在集群节点ip的特定端口上。

由于nodeport暴露的端口是随机端口,一般会在前面,再搭建一套负载均衡器来转发请求。

该方式一般用于宿主机是相对固定的环境ip地址不变的场景。

NodePort方式暴露ingress虽然简单方便,但是NodePort多了一层NAT,在请求量级很大时可能对性能会有一定影响。

模式二:DaemonSet+nodeSelector+HostNetwork

用DaemonSet结合nodeselector来部署ingress-controller到特定的node上,

然后使用HostNetwork直接把该pod与宿主机node的网络打通,直接使用宿主机的80/433端口就能访问服务。

这时,ingress-controller的node机器就很类似传统架构的边缘节点,比如机房入口的nginx服务器。

该方式整个请求链路最简单,性能相对NodePort模式更好。

缺点是由于直接利用宿主机节点的网络和端口,一个node只能部署一个ingress-controller pod。

比较适合大并发的生产环境使用。

模式三:Deployment+LoadBalancer模式的Service

如果要把ingress部署在公有云,那用这种方式比较合适。

用Deployment部署ingress-controller,创建一个type为LoadBalancer的service关联这组pod。

大部分公有云,都会为LoadBalancer的service自动创建一个负载均衡器,通常还绑定了公网地址。

只要把域名解析指向该地址,就实现了集群服务的对外暴露。

模式一的问题:service暴露服务的问题

但是,单独用service暴露服务的方式,在实际生产环境中不太合适

ClusterIP的方式只能在集群内部访问。

NodePort方式的话,测试环境使用还行,当有几十上百的服务在集群中运行时,NodePort的端口管理是灾难。

LoadBalance方式受限于云平台,且通常在云平台部署ELB还需要额外的费用。

生产环境,推荐模式二。

云服务器环境,推荐模式三。

DaemonSet+HostNetwork+nodeselector 实操

首先来看看 nodePort的NAT性能问题

nodePort的NAT性能问题

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

这时,如果使用一个ingress直接对内进行转发,ingress的流量是很大的

NodePort方式暴露ingress虽然简单方便,但是NodePort多了一层NAT,在请求量级很大时可能对性能会有一定影响

1W字长文:K8S Ingress 原理和实操_第19张图片

众所周知的是,Pod是可以共享宿主机的网络名称空间的,也就是说当在共享网络名称空间时,Pod上所监听的就是Node上的端口。

那么这又该如何实现呢?

简单的实现就是使用 DaemonSet 在每个 Node 上监听 80端口,然后写好规则,

因为 ingress 外面绑定了宿主机 80 端口,本身又在集群内,那么向后直接转发到相应Service IP就行了,不需要进行nat 了。

如下图所示:

1W字长文:K8S Ingress 原理和实操_第20张图片

指定nginx-ingress-controller运行的node节点

这里使用daemonset+hostNetwork 模式部署,并且部署到特定node,

DaemonSet:守护进程控制器
DaemonSet 也是 Kubernetes 提供的一个 default controller,它实际是做一个守护进程的控制器,它能帮我们做到以下几件事情:

(1)首先能保证集群内的每一个节点都运行一组相同的 pod;
(2)同时还能根据节点的状态保证新加入的节点自动创建对应的 pod;
(3)在移除节点的时候,能删除对应的 pod;
(4)而且它会跟踪每个 pod 的状态,当这个 pod 出现异常、Crash 掉了,会及时地去 recovery 这个状态。

daemonset与deployment非常相似,区别是不需要设置replicas,因为daemonset是每节点启动的

首先,需要修改node的部分配置

先给要部署nginx-ingress的node打上特定标签,这里测试部署在"minikube"这个节点。

 kubectl  label   node  node-**  isIngress="true"

1W字长文:K8S Ingress 原理和实操_第21张图片

在正式环境下,也是类似的,比如部署在 node02

kubectl get node

kubectl label node node02 ingress=true

kubectl get nodes --show-labels

kubectl delete -f ingresstest.yaml
 
kubectl delete  -f  deploy.yaml

修改Deployment为Daemonset,指定节点运行,并开启 hostNetwork

vim mandatory.yaml
...
apiversion: apps/vl
kind: Daemonset   	#修改kind
...
hostNetwork: true  	#使用主机网络
nodeSelector:
  ingress: "true"   #选择节点运行
...

复制一份,改为deploy-daemonset.yaml,然后修改上面的deployment部分配置为daemonset:

1W字长文:K8S Ingress 原理和实操_第22张图片

# 修改api版本及kind
# apiVersion: apps/v1
# kind: Deployment
apiVersion: extensions/v1beta1
kind: DaemonSet
metadata:
  name: nginx-ingress-controller
  namespace: ingress-nginx
  labels:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
spec:
# 删除Replicas
# replicas: 1
  selector:
    matchLabels:
      app.kubernetes.io/name: ingress-nginx
      app.kubernetes.io/part-of: ingress-nginx
  template:
    metadata:
      labels:
        app.kubernetes.io/name: ingress-nginx
        app.kubernetes.io/part-of: ingress-nginx
      annotations:
        prometheus.io/port: "10254"
        prometheus.io/scrape: "true"
    spec:
      serviceAccountName: nginx-ingress-serviceaccount
      # 选择对应标签的node
      nodeSelector:
        isIngress: "true"
      # 使用hostNetwork暴露服务
      hostNetwork: true
      containers:
        - name: nginx-ingress-controller
          image: quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.25.0
          args:
            - /nginx-ingress-controller
            - --configmap=$(POD_NAMESPACE)/nginx-configuration
            - --tcp-services-configmap=$(POD_NAMESPACE)/tcp-services
            - --udp-services-configmap=$(POD_NAMESPACE)/udp-services
            - --publish-service=$(POD_NAMESPACE)/ingress-nginx
            - --annotations-prefix=nginx.ingress.kubernetes.io
          securityContext:
            allowPrivilegeEscalation: true
            capabilities:
              drop:
                - ALL
              add:
                - NET_BIND_SERVICE
            # www-data -> 33
            runAsUser: 33
          env:
            - name: POD_NAME
              valueFrom:
                fieldRef:
                  fieldPath: metadata.name
            - name: POD_NAMESPACE
              valueFrom:
                fieldRef:
                  fieldPath: metadata.namespace
          ports:
            - name: http
              containerPort: 80
            - name: https
              containerPort: 443
          livenessProbe:
            failureThreshold: 3
            httpGet:
              path: /healthz
              port: 10254
              scheme: HTTP
            initialDelaySeconds: 10
            periodSeconds: 10
            successThreshold: 1
            timeoutSeconds: 10
          readinessProbe:
            failureThreshold: 3
            httpGet:
              path: /healthz
              port: 10254
              scheme: HTTP
            periodSeconds: 10
            successThreshold: 1
            timeoutSeconds: 10

Deployment 部署的副本 Pod 会分布在各个 Node 上,每个 Node 都可能运行好几个副本。

DaemonSet 的不同之处在于:每个 Node 上最多只能运行一个副本。

  • kind: DaemonSet:官方原始文件使用的是deployment,replicate 为 1,这样将会在某一台节点上启动对应的nginx-ingress-controller pod。外部流量访问至该节点,由该节点负载分担至内部的service。测试环境考虑防止单点故障,改为DaemonSet然后删掉replicate ,配合亲和性部署在制定节点上启动nginx-ingress-controller pod,确保有多个节点启动nginx-ingress-controller pod,后续将这些节点加入到外部硬件负载均衡组实现高可用性。
  • hostNetwork: true:添加该字段,暴露nginx-ingress-controller pod的服务端口(如9118)
  • nodeSelector: 增加亲和性部署,有isIngress: "true"标签的节点才会部署该DaemonSet

启动nginx-ingress-controller

#主节点
kubectl delete  -f  deploy-daemonset.yaml

kubectl apply -f  deploy-daemonset.yaml
 
kubectl logs -f nginx-ingress-controller-hrd46 -n ingress-nginx

1W字长文:K8S Ingress 原理和实操_第23张图片

kubernetes “hostNetwork: true”,这是一种直接定义Pod网络的方式。

如果在POD中使用"hostNetwork: true"配置网络,pod中运行的应用程序可以直接看到宿主主机的网络接口,宿主机所在的局域网上所有网络接口都可以访问到该应用程序及端口。

hostNetwork: true
# 使用主机网络
dnsPolicy: ClusterFirstWithHostNet
# 该设置是使POD使用k8s的dns,dns配置在/etc/resolv.conf文件中
# 如果不加,pod默认使用所在宿主主机使用的DNS,这样会导致容器
# 内不能通过service name访问k8s集群中其他POD

另外: HostNetwork 模式不需要创建service

查看pod的IP和端口

kubectl describe pod  ingress-nginx-controller-nvrrq -n ingress-nginx

1W字长文:K8S Ingress 原理和实操_第24张图片

可以使用下面的命令,查看端口:
netstat -natp | grep nginx

由于配置了hostnetwork, nginx已经在 node主机本地监听团9118/9443/8443端口。

其中 9118是nginx-controller默认配置的一个defaultbackend (Ingress资源没有匹配的 rule 对象时,流量就会被导向这个default backend
这样,只要访问 node主机有公网 TP,就可以直接映射域名来对外网暴露服务了。

如果要nginx高可用的话,可以在多个node上部署,并在前面再搭建一套LVS+keepalive做负载均衡。

配置ingress资源

部署完ingress-controller,接下来就按照测试的需求来创建ingress资源。

kubectl delete -A ValidatingWebhookConfiguration ingress-nginx-admission
 
kubectl apply -f ingresstest-one.yaml
kubectl delete -f ingresstest-one.yaml
kubectl get svc -A

curl http://a.foo.com:30909/foo/

curl http://foo.bar.com:30808/demo-provider/swagger-ui.html

curl http://foo.bar.com:9118/bar/

curl http://a.foo.com:9118/foo/

查看日志
kubectl logs -f ingress-nginx-controller-nvrrq -n ingress-nginx

命令清单

#主节点
kubectl delete  -f  deploy-daemonset.yaml

kubectl apply -f  deploy-daemonset.yaml

kubectl get pod -n ingress-nginx -o wide

# 检查部署情况
kubectl get daemonset -n ingress-nginx

kubectl delete -A ValidatingWebhookConfiguration ingress-nginx-admission

kubectl get po -n ingress-nginx -o wide
 
kubectl get po -A
kubectl get svc -n ingress-nginx
 
kubectl logs -f ingress-nginx-controller-j4cq4 -n ingress-nginx

kubectl describe pod  ingress-nginx-controller-j4cq4 -n ingress-nginx

echo 192.168.49.2 foo.bar.com  >>  /etc/hosts
echo 192.168.49.2 a.foo.com  >>  /etc/hosts

查看日志
kubectl logs  -n ingress-nginx  -f 
kubectl logs -f ingress-nginx-controller-nvrrq -n ingress-nginx

生产环境 LVS+keepalive 做高可用和负载均衡

为了配置kubernetes中的ingress的高可用,对于kubernetes集群以外只暴露一个访问入口,需要使用keepalived排除单点问题。需要使用daemonset方式将ingress-controller部署在边缘节点上。

边缘节点

首先解释下什么叫边缘节点(Edge Node),所谓的边缘节点即集群内部用来向集群外暴露服务能力的节点,

集群外部的服务通过边缘节点(Edge Node)来调用集群内部的服务,

边缘节点是集群内外交流的一个Endpoint。

边缘节点要考虑两个问题

  • 边缘节点的高可用,不能有单点故障,否则整个kubernetes集群将不可用
  • 对外的一致暴露端口,即只能有一个外网访问IP和端口

生产环境可以使用 HA + LB + DaemonSet hostNetwork 架构

为了满足边缘节点的高可用需求,我们使用keepalived来实现。

在Kubernetes中添加了ingress后,在DNS中添加A记录,域名为你的ingress中host的内容,IP为你的keepalived的VIP,这样集群外部就可以通过域名来访问你的服务,也解决了单点故障。

选择Kubernetes的三个node作为边缘节点,并在前面再搭建一套LVS+keepalive做负载均衡。

在 k8s 前面部署 lvs, 并且使用 DR 模式, 解决单个 nginx-ingress 会成为性能瓶颈问题,

如图
1W字长文:K8S Ingress 原理和实操_第25张图片

四种port底层原理:nodePort、port、targetPort、containerPort 的核心

在尼恩的 读者群(50+)中,大家对K8S的四大Port 有很多的理解误区。

基于咱们的K8S学习圣经,把四种port底层原理介绍一下:nodePort、port、targetPort、containerPort

1、nodePort

nodePort提供了集群外部客户端访问service的一种方式,nodePort提供了集群外部客户端访问service的端口,即nodeIP:nodePort提供了外部流量访问k8s集群中service的入口。

比如外部用户要访问k8s集群中的一个Web应用,那么我们可以配置对应service的type=NodePort,nodePort=30082。

其他用户就可以通过浏览器http://node:30082访问到该web服务。

而这里的nodeIP,是pod启动所在的node 机器。

1W字长文:K8S Ingress 原理和实操_第26张图片

在咱们的ingress 测试中,node 机器 ip 是 192.168.49.2 ,

1W字长文:K8S Ingress 原理和实操_第27张图片

ingress 开启的node port 为 32661

1W字长文:K8S Ingress 原理和实操_第28张图片

ingress 的svc服务暴露了8008端口(参考前面的配置文件),

集群内其他容器通过8008端口访问ingress 的svc服务,但是外部流量不能通过该端口访问ingress 的svc服务,因为外部服务,访问不到这个 IP 10.102.112.207

这个ip 是 K8S管理的虚拟网络IP, 这个网络是跨 物理机器的,跨node节点, 一个统一的虚拟网络

外部的流量不能访问这个虚拟网络, 怎么办呢,需要配置NodePort访问,

NodePort 需要通过 完成NAT 转换, 转换成 10.102.112.207 虚拟IP,再通过svc的路由机制 kube-proxy +路由表 结合,路由到正确的POD。

2、port

port是暴露在cluster ip上的端口,port提供了集群内部客户端访问service的入口,即clusterIP:port。

1W字长文:K8S Ingress 原理和实操_第29张图片

对应的service.yaml如下:

1W字长文:K8S Ingress 原理和实操_第30张图片

相当于在虚拟网络上开的端口,一般和 pod的 targetPort 端口,保持一致。

3、targetPort

targetPort是pod上的端口,

从nodePort-》上来的数据,经过kube-proxy流入到后端pod的targetPort上,最后进入容器。

targetPort与制作容器时暴露的端口一致(使用DockerFile中的EXPOSE),例如官方的nginx(参考DockerFile)暴露8008端口。

我们这里设置为web服务端口8008。

1W字长文:K8S Ingress 原理和实操_第31张图片

4、containerPort

containerPort是在pod控制器中定义的、pod中的容器需要暴露的端口。如spring boot的8080,mysql的3306等。咱们实例当中的web服务端口8008。

该端口只是起到specification作用,哪怕不在yaml中定义,也是可以通过nodePort->targetPort的流向(外部)或者port->targetPort流向(内部)进行访问。

如果设置的话,也是设置为docker镜像的暴露端口。

需要注意的,nodePort的使用只是实验性质,如果在生产环境上通过通过nginx等反向代理工具去管理nodePort绝对是灾难性的。

更多是需要通过外部LoadBalancer或者ingress去做管理。

Ingress 动态域名配置底层原理

问题:当每次有新服务加入,Ingress 又该如何修改 Nginx 配置呢?

回到 Nginx的原理,Nginx可以通过虚拟主机域名进行区分不同的服务,而每个服务通过upstream进行定义不同的负载均衡池,再加上location进行负载均衡的反向代理,在日常使用中只需要修改nginx.conf即可实现,那在K8S中又该如何实现这种方式的调度呢?

关于Nginx的知识,请参见尼恩的《Java高并发核心编程 卷3 加强版》 PDF,里边同样介绍了lua 开发的知识,而 Ingress 就是在Nginx 做的 Lua 开发。

在Ingress中,假设后端的服务初始服务只有ecshop,后面增加了bbs和member服务,那么又该如何将这2个服务加入到Nginx-Pod进行调度呢?

当然,每次手动改Nginx的配置,就太low

Ingress 出现了,如果不算上面的Nginx,Ingress 包含两大组件:Ingress Controller 和 Ingress。

1W字长文:K8S Ingress 原理和实操_第32张图片

增加了新的服务,那么就是原来需要改 Nginx 配置,然后配置各种域名对应哪个 Service,

现在把这个动作抽象出来,变成一个 Ingress 对象,你可以用 yaml 创建,每次不要去改 Nginx 了,直接改 yaml 然后创建/更新就行了;

那么问题来了: Ingress 对象 如何 变成 Nginx 的配置文件呢? Ingress Controller 这东西就是承担这个 桥梁的角色。

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

1W字长文:K8S Ingress 原理和实操_第33张图片

实际上Ingress也是Kubernetes API的标准资源类型之一,它其实就是一组基于DNS名称(host)或URL路径把请求转发到指定的Service资源的规则。

用于将集群外部的请求流量转发到集群内部完成的服务发布。

Ingress 资源自身不能进行“流量穿透”,仅仅是一组规则的集合,这些集合规则还需要其他功能的辅助,比如监听某接口,然后根据这些规则的匹配进行路由转发,

这些能够为Ingress资源定义的规则,进行流量转发的组件就是Ingress Controller。里边包含了 反向代理负载均衡组件如 nginx。

Ingress 控制器不同于Deployment 控制器的是,Ingress控制器不直接运行为kube-controller-manager的一部分,它仅仅是Kubernetes集群的一个附件,需要在集群上单独部署。

ingress总结

  • ingress是k8s集群的请求入口,可以理解为对多个service的再次抽象
  • 通常说的ingress一般包括ingress资源对象及ingress-controller两部分组成
  • ingress-controller有多种实现,社区原生的是ingress-nginx,根据具体需求选择
  • ingress自身的暴露有多种方式,需要根据基础环境及业务类型选择合适的方式

实际ingress-nginx的有非常多的配置, 具体可以参考ingress-nginx的官方文档。

技术自由的实现路径:

实现你的 架构自由:

《吃透8图1模板,人人可以做架构》

《10Wqps评论中台,如何架构?B站是这么做的!!!》

《阿里二面:千万级、亿级数据,如何性能优化? 教科书级 答案来了》

《峰值21WQps、亿级DAU,小游戏《羊了个羊》是怎么架构的?》

《100亿级订单怎么调度,来一个大厂的极品方案》

《2个大厂 100亿级 超大流量 红包 架构方案》

… 更多架构文章,正在添加中

实现你的 响应式 自由:

《响应式圣经:10W字,实现Spring响应式编程自由》

这是老版本 《Flux、Mono、Reactor 实战(史上最全)》

实现你的 spring cloud 自由:

《Spring cloud Alibaba 学习圣经》 PDF

《分库分表 Sharding-JDBC 底层原理、核心实战(史上最全)》

《一文搞定:SpringBoot、SLF4j、Log4j、Logback、Netty之间混乱关系(史上最全)》

实现你的 linux 自由:

《Linux命令大全:2W多字,一次实现Linux自由》

实现你的 网络 自由:

《TCP协议详解 (史上最全)》

《网络三张表:ARP表, MAC表, 路由表,实现你的网络自由!!》

实现你的 分布式锁 自由:

《Redis分布式锁(图解 - 秒懂 - 史上最全)》

《Zookeeper 分布式锁 - 图解 - 秒懂》

实现你的 王者组件 自由:

《队列之王: Disruptor 原理、架构、源码 一文穿透》

《缓存之王:Caffeine 源码、架构、原理(史上最全,10W字 超级长文)》

《缓存之王:Caffeine 的使用(史上最全)》

《Java Agent 探针、字节码增强 ByteBuddy(史上最全)》

实现你的 面试题 自由:

4000页《尼恩Java面试宝典 》 40个专题

以上尼恩 架构笔记、面试题 的PDF文件,请到《技术自由圈》公众号领取↓↓↓

你可能感兴趣的:(面试,kubernetes,java,docker,云原生,面试)