kubernetes的Servie和Ingress架构与实践

43 阅读11分钟

一、什么是Service?

我们已经能够通过控制器来创建一组Pod来提供具有高可用性的服务。虽然每个Pod都会分配一个单独的Pod IP,然而却存在如下两问题:

Pod IP仅仅是集群内可见的虚拟IP,外部无法访问。 Pod IP会随着Pod的销毁而消失,当ReplicaSet对Pod进行动态伸缩时,Pod IP可能随时随地都会变化,这样对于我们访问这个服务带来了难度。 在这里插入图片描述

ClusterIP模式

用于为集群内Pod访问时,提供的固定访问地址,默认是自动分配地址,可使用ClusterIP关键字指定固定IP.

NodePort模式

用于为集群外部访问Service后面Pod提供访问接入端口.  这种类型的service工作流程为:       Client----->NodeIP:NodePort----->ClusterIP:ServicePort----->PodIP:ContainerPort

LoadBalancer模式

用于当K8s运行在一个云环境内时,若该云环境支持LBaaS,则此类型可自动触发创建一个软件负载均衡器用于对Service做负载均衡调度. 因为外部所有Client都访问一个NodeIP,该节点的压力将会很大, 而LoadBalancer则可解决这个问题。 而且它还直接动态监测后端Node是否被移除或新增了,然后动态更新调度的节点数。

ExternalName模式

用于将集群外部的服务引入到集群内部,在集群内部可直接访问来获取服务。 它的值必须是 FQDN, 此FQDN为集群内部的FQDN, 即: ServiceName.Namespace.Domain.LTD.

我们来看下NodePort怎么暴露服务

命令生成yaml文件

kubectl expose deploy nginx --port=80 --target-port=80  -o yaml --dry-run > service.yaml

NodePort服务的YAML文件如下:

apiVersion: v1
kind: Service
metadata:  
  name: my-nodeport-service
selector:   
  app: my-appspec #这里选择器一定要选择容器的标签,之前写name:kube-node是错的。
  type: NodePort   #这里代表是NodePort类型的  Service有四种type: ClusterIP(默认)、NodePort、LoadBalancer、ExternalName. 其中NodePort和LoadBalancer两类型的Services可以对外提供服务。
  ports:  
  - name: http
    port: 80 #这里的端口和clusterIP对应,即ip:80 ,供内部访问。
    targetPort: 80 #端口一定要和container暴露出来的端口对应
    nodePort: 30008 # 所有的节点都会开放此端口,此端口供外部调用。
    protocol: TCP

这种方式有一个“nodePort"的端口,能在节点上指定开放哪个端口,如果没有指定端口,它会选择一个随机端口,大多数时候应该让Kubernetes随机选择端口; 这种方式的不足: 1、一个端口只能供一个服务使用; 2、只能使用30000–32767之间的端口; 3、如果节点/虚拟机的IP地址发生变化,需要人工进行处理; 因此,在生产环境不推荐使用这种方式来直接发布服务,如果不要求运行的服务实时可用,或者用于演示或者临时运行一个应用可以用这种方式; 三种端口说明 nodePort 外部机器(在windows浏览器)可以访问的端口; 比如一个Web应用需要被其他用户访问,那么需要配置type=NodePort,而且配置nodePort=30001,那么其他机器就可以通过浏览器访问scheme://node:30001访问到该服务; targetPort 容器的端口,与制作容器时暴露的端口一致(Dockerfile中EXPOSE),例如docker.io官方的nginx暴露的是80端口; port Kubernetes集群中的各个服务之间访问的端口,虽然mysql容器暴露了3306端口,但外部机器不能访问到mysql服务,因为他没有配置NodePort类型,该3306端口是集群内其他容器需要通过3306端口访问该服务; 我们来看下LoadBalancer LoadBlancer可以暴露服务,这种方式需要向云平台申请负载均衡器,目前很多云平台都支持,但是这种方式深度耦合了云平台;(相当于是购买服务) 从外部的访问通过负载均衡器LoadBlancer转发到后端的Pod,具体如何实现要看云提供商;

二、什么是Ingress?

从前面的学习,我们可以了解到Kubernetes暴露服务的方式目前只有三种:LoadBlancer Service、ExternalName、NodePort Service、Ingress;而我们需要将集群内服务提供外界访问就会产生以下几个问题:

1、Pod 漂移问题

Kubernetes 具有强大的副本控制能力,能保证在任意副本(Pod)挂掉时自动从其他机器启动一个新的,还可以动态扩容等,通俗地说,这个 Pod 可能在任何时刻出现在任何节点上,也可能在任何时刻死在任何节点上;那么自然随着 Pod 的创建和销毁,Pod IP 肯定会动态变化;那么如何把这个动态的 Pod IP 暴露出去?这里借助于 Kubernetes 的 Service 机制,Service 可以以标签的形式选定一组带有指定标签的 Pod,并监控和自动负载他们的 Pod IP,那么我们向外暴露只暴露 Service IP 就行了;这就是 NodePort 模式:即在每个节点上开起一个端口,然后转发到内部

2、端口管理问题

采用 NodePort 方式暴露服务面临问题是,服务一旦多起来,NodePort 在每个节点上开启的端口会及其庞大,而且难以维护;这时,我们可以能否使用一个Nginx直接对内进行转发呢?众所周知的是,Pod与Pod之间是可以互相通信的,而Pod是可以共享宿主机的网络名称空间的,也就是说当在共享网络名称空间时,Pod上所监听的就是Node上的端口。那么这又该如何实现呢?简单的实现就是使用 DaemonSet 在每个 Node 上监听 80,然后写好规则,因为 Nginx 外面绑定了宿主机 80 端口(就像 NodePort),本身又在集群内,那么向后直接转发到相应 Service IP 就行了

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

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

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

6.我们来看下今天的主角Ingress在这里插入图片描述

虽然k8s集群内部署的pod、service都有自己的IP,但是却无法提供外网访问,以前我们可以通过监听NodePort的方式暴露服务,但是这种方式并不灵活,生产环境也不建议使用; Ingresss是k8s集群中的一个API资源对象,相当于一个集群网关,我们可以自定义路由规则来转发、管理、暴露服务(一组pod),比较灵活,生产环境建议使用这种方式; Ingress不是kubernetes内置的(安装好k8s之后,并没有安装ingress),ingress需要单独安装,而且有多种类型Google Cloud Load Balancer,Nginx,Contour,Istio等等,我们这里选择官方维护的Ingress Nginx;

部署 Ingress Nginx

文档:github.com/kubernetes/… ingress-nginx是使用NGINX作为反向代理和负载均衡器的Kubernetes的Ingress控制器;

kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v0.44.0/deploy/static/provider/baremetal/deploy.yaml

远程需要翻墙,你可以去我百度云

链接:pan.baidu.com/s/1gqyMGG0Z… 提取码:a90e 复制这段内容后打开百度网盘手机App,操作更方便哦

添加一个配置项: 在这里插入图片描述 应用:

kubectl apply -f ingress-controller.yaml

在这里插入图片描述 查看Ingress的状态

kubectl get service -n ingress-nginx
kubectl get deploy -n ingress-nginx
kubectl get pods -n ingress-nginx

在这里插入图片描述 例子 暴露Nginx应用的Ingress规则这里很多种规则具体看你需求

查看当前ingress-nginx部署在哪台服务器上

kubectl get pods -n ingress-nginx -o wide;

在这里插入图片描述 一个最小的ingress示例:

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: test-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  rules:
  - http:
      paths:
      - path: /testpath
        backend:
          serviceName: test
          servicePort: 80

ingress 规则 每一个HTTP规则包含以下信息: 一个可选的host。在本例中没有host,因此,该规则适用于通过指定的IP地址进行的所有入站HTTP通信。如果提供一个host(例如,foo.bar.com),这个规则是适用于这一个host 一个paths(例如 /testpath)的列表。每一个path都有与之关联的serviceName和servicePort,在负载均衡器将流量导向所引用的服务之前,主机和路径必须匹配传入请求的内容 后端是服务和端口名称的组合。对与规则的主机和路径匹配的入口的HTTP(和HTTPS)请求将发送到列出的后端。 默认后端通常配置在一个Ingress控制器中,该控制器将服务于任何与规范中的路径不匹配的请求。(404页面)

ingress的类型 单service的ingress 现有的Kubernetes概念允许您公开单个服务。您还可以通过指定一个没有规则的默认后端来对一个入口执行此操作。

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: test-ingress
spec:
  backend:
    serviceName: testsvc
    servicePort: 80

通过kubectl apply -f <文件名>创建后,你可以看到:

kubectl get ingress test-ingress

浏览器访问192.168.181.139 在这里插入图片描述 其中107.178.254.228是入口控制器为满足该入口而分配的IP。 在这里插入图片描述 简单的扇出 根据所请求的HTTP URI,扇出配置将流量从单个IP地址路由到多个服务。一个入口允许您将负载平衡器的数量保持到最小。例如,设置如下:、 在这里插入图片描述 定义的ingress如下:

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: simple-fanout-example
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  rules:
  - host: foo.bar.com
    http:
      paths:
      - path: /foo
        backend:
          serviceName: service1
          servicePort: 4200
      - path: /bar
        backend:
          serviceName: service2
          servicePort: 8080

通过kubectl apply -f <文件名>创建后:

kubectl describe ingress simple-fanout-example

在这里插入图片描述 基于名称的虚拟主机 基于名称的虚拟主机支持将HTTP流量路由到同一IP地址的多个主机名。 在这里插入图片描述 下面的ingress告诉后台负载均衡器根据主机头路由请求。

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: name-virtual-host-ingress
spec:
  rules:
  - host: foo.bar.com
    http:
      paths:
      - backend:
          serviceName: service1
          servicePort: 80
  - host: bar.foo.com
    http:
      paths:
      - backend:
          serviceName: service2
          servicePort: 80

如果您创建一个没有在规则中定义任何主机的Ingress资源,那么可以匹配到Ingress控制器IP地址的任何web流量,而不需要基于名称的虚拟主机。例如,下面的Ingress资源将把first.bar.com请求的流量路由到service1, second.foo.com路由到service2,将任何没有在request中定义主机名(即没有显示请求头)的流量路由到service3。

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: name-virtual-host-ingress
spec:
  rules:
  - host: first.bar.com
    http:
      paths:
      - backend:
          serviceName: service1
          servicePort: 80
  - host: second.foo.com
    http:
      paths:
      - backend:
          serviceName: service2
          servicePort: 80
  - http:
      paths:
      - backend:
          serviceName: service3
          servicePort: 80

TLS 您可以通过指定包含TLS私钥和证书的秘密来保护ingress。目前,入口只支持一个TLS端口443,并假设TLS终端。如果一个入口中的TLS配置部分指定了不同的主机,那么它们将根据通过SNI TLS扩展指定的主机名在同一个端口上进行多路复用(前提是入口控制器支持SNI)。TLS密钥必须包含名为TLS的密钥。crt和tls。包含用于TLS的证书和私钥的密钥,例如:

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

在一个Ingress中引用这个secret将告诉Ingress控制器使用TLS保护从客户机到负载均衡器的通道。您需要确保您创建的TLS secret来自一个包含sslexample.foo.com CN的证书。

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: tls-example-ingress
spec:
  tls:
  - hosts:
    - sslexample.foo.com
    secretName: testsecret-tls
  rules:
    - host: sslexample.foo.com
      http:
        paths:
        - path: /
          backend:
            serviceName: service1
            servicePort: 80

Loadbalancing

一个ingress controller 通过一些应用于所有入口的负载平衡策略设置来引导,例如负载平衡算法、后端权重方案等。更高级的负载平衡概念(例如持久会话、动态权重)还没有通过ingress公开。同样值得注意的是,尽管健康检查不是直接通过入口暴露的,但是在Kubernetes中也存在类似的概念,比如就绪探测,它允许您实现相同的最终结果。