你好,我是张磊。明天我和你分享的主题是:谈谈Service与Ingress。

在上一篇文章中,我为你具体解说了将Service裸露给外界的三种办法。其中有一个叫作LoadBalancer类型的Service,它会为你在Cloud Provider(比方:Google Cloud或者OpenStack)里创立一个与该Service对应的负载平衡服务。

然而,置信你也应该能感触到,因为每个 Service 都要有一个负载平衡服务,所以这个做法实际上既节约老本又高。作为用户,我其实更心愿看到Kubernetes为我内置一个全局的负载均衡器。而后,通过我拜访的URL,把申请转发给不同的后端Service。

这种全局的、为了代理不同后端Service而设置的负载平衡服务,就是Kubernetes里的Ingress服务。

所以,Ingress的性能其实很容易了解:所谓Ingress,就是Service的“Service”。

举个例子,如果我当初有这样一个站点:https://cafe.example.com。其中,https://cafe.example.com/coffee,对应的是“咖啡点餐零碎”。而,https://cafe.example.com/tea,对应的则是“茶水点餐零碎”。这两个零碎,别离由名叫coffee和tea这样两个Deployment来提供服务。

那么当初,[strong_begin]我如何能应用Kubernetes的Ingress来创立一个对立的负载均衡器,从而实现当用户拜访不同的域名时,可能拜访到不同的Deployment呢?[strong_end]

上述性能,在Kubernetes里就须要通过Ingress对象来形容,如下所示:

apiVersion: extensions/v1beta1kind: Ingressmetadata:  name: cafe-ingressspec:  tls:  - hosts:    - cafe.example.com    secretName: cafe-secret  rules:  - host: cafe.example.com    http:      paths:      - path: /tea        backend:          serviceName: tea-svc          servicePort: 80      - path: /coffee        backend:          serviceName: coffee-svc          servicePort: 80

在下面这个名叫cafe-ingress.yaml文件中,最值得咱们关注的,是rules字段。在Kubernetes里,这个字段叫作:IngressRule

IngressRule的Key,就叫做:host。它必须是一个规范的域名格局(Fully Qualified Domain Name)的字符串,而不能是IP地址。

备注:Fully Qualified Domain Name的具体格局,能够参考RFC 3986规范。

而host字段定义的值,就是这个Ingress的入口。这也就意味着,当用户拜访cafe.example.com的时候,实际上拜访到的是这个Ingress对象。这样,Kubernetes就能应用IngressRule来对你的申请进行下一步转发。

而接下来IngressRule规定的定义,则依赖于path字段。你能够简略地了解为,这里的每一个path都对应一个后端Service。所以在咱们的例子里,我定义了两个path,它们别离对应coffee和tea这两个Deployment的Service(即:coffee-svc和tea-svc)。

通过下面的解说,不难看到,所谓Ingress对象,其实就是Kubernetes我的项目对“反向代理”的一种形象。

一个Ingress对象的次要内容,实际上就是一个“反向代理”服务(比方:Nginx)的配置文件的形容。而这个代理服务对应的转发规定,就是IngressRule。

这就是为什么在每条IngressRule里,须要有一个host字段来作为这条IngressRule的入口,而后还须要有一系列path字段来申明具体的转发策略。这其实跟Nginx、HAproxy等我的项目的配置文件的写法是统一的。

而有了Ingress这样一个对立的形象,Kubernetes的用户就无需关怀Ingress的具体细节了。

在理论的应用中,你只须要从社区里抉择一个具体的Ingress Controller,把它部署在Kubernetes集群里即可。

而后,这个Ingress Controller会依据你定义的Ingress对象,提供对应的代理能力。目前,业界罕用的各种反向代理我的项目,比方Nginx、HAProxy、Envoy、Traefik等,都曾经为Kubernetes专门保护了对应的Ingress Controller。

接下来,[strong_begin]我就以最罕用的Nginx Ingress Controller为例,在咱们后面用kubeadm部署的Bare-metal环境中,和你实际一下Ingress机制的应用过程。[strong_end]

部署Nginx Ingress Controller的办法非常简单,如下所示:

$ kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/mandatory.yaml

其中,在mandatory.yaml这个文件里,正是Nginx官网为你保护的Ingress Controller的定义。咱们来看一下它的内容:

kind: ConfigMapapiVersion: v1metadata:  name: nginx-configuration  namespace: ingress-nginx  labels:    app.kubernetes.io/name: ingress-nginx    app.kubernetes.io/part-of: ingress-nginx---apiVersion: extensions/v1beta1kind: Deploymentmetadata:  name: nginx-ingress-controller  namespace: ingress-nginx  labels:    app.kubernetes.io/name: ingress-nginx    app.kubernetes.io/part-of: ingress-nginxspec:  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:        ...    spec:      serviceAccountName: nginx-ingress-serviceaccount      containers:        - name: nginx-ingress-controller          image: quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.20.0          args:            - /nginx-ingress-controller            - --configmap=$(POD_NAMESPACE)/nginx-configuration            - --publish-service=$(POD_NAMESPACE)/ingress-nginx            - --annotations-prefix=nginx.ingress.kubernetes.io          securityContext:            capabilities:              drop:                - ALL              add:                - NET_BIND_SERVICE            # www-data -> 33            runAsUser: 33          env:            - name: POD_NAME              valueFrom:                fieldRef:                  fieldPath: metadata.name            - name: POD_NAMESPACE            - name: http              valueFrom:                fieldRef:                  fieldPath: metadata.namespace          ports:            - name: http              containerPort: 80            - name: https              containerPort: 443

能够看到,在上述YAML文件中,咱们定义了一个应用nginx-ingress-controller镜像的Pod。须要留神的是,这个Pod的启动命令须要应用该Pod所在的Namespace作为参数。而这个信息,当然是通过Downward API拿到的,即:Pod的env字段里的定义(env.valueFrom.fieldRef.fieldPath)。

而这个Pod自身,就是一个监听Ingress对象以及它所代理的后端Service变动的控制器。

当一个新的Ingress对象由用户创立后,nginx-ingress-controller就会依据Ingress对象里定义的内容,生成一份对应的Nginx配置文件(/etc/nginx/nginx.conf),并应用这个配置文件启动一个 Nginx 服务。

而一旦Ingress对象被更新,nginx-ingress-controller就会更新这个配置文件。须要留神的是,如果这里只是被代理的 Service 对象被更新,nginx-ingress-controller所治理的 Nginx 服务是不须要从新加载(reload)的。这当然是因为nginx-ingress-controller通过Nginx Lua计划实现了Nginx Upstream的动静配置。

此外,nginx-ingress-controller还容许你通过Kubernetes的ConfigMap对象来对上述 Nginx 配置文件进行定制。这个ConfigMap的名字,须要以参数的形式传递给nginx-ingress-controller。而你在这个 ConfigMap 里增加的字段,将会被合并到最初生成的 Nginx 配置文件当中。

能够看到,一个Nginx Ingress Controller为你提供的服务,其实是一个能够依据Ingress对象和被代理后端 Service 的变动,来主动进行更新的Nginx负载均衡器。

当然,为了让用户可能用到这个Nginx,咱们就须要创立一个Service来把Nginx Ingress Controller治理的 Nginx 服务裸露进来,如下所示:

$ kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/provider/baremetal/service-nodeport.yaml

因为咱们应用的是Bare-metal环境,所以service-nodeport.yaml文件里的内容,就是一个NodePort类型的Service,如下所示:

apiVersion: v1kind: Servicemetadata:  name: ingress-nginx  namespace: ingress-nginx  labels:    app.kubernetes.io/name: ingress-nginx    app.kubernetes.io/part-of: ingress-nginxspec:  type: NodePort  ports:    - name: http      port: 80      targetPort: 80      protocol: TCP    - name: https      port: 443      targetPort: 443      protocol: TCP  selector:    app.kubernetes.io/name: ingress-nginx    app.kubernetes.io/part-of: ingress-nginx

能够看到,这个Service的惟一工作,就是将所有携带ingress-nginx标签的Pod的80和433端口裸露进来。

而如果你是私有云上的环境,你须要创立的就是LoadBalancer类型的Service了。

上述操作实现后,你肯定要记录下这个Service的拜访入口,即:宿主机的地址和NodePort的端口,如下所示:

$ kubectl get svc -n ingress-nginxNAME            TYPE       CLUSTER-IP     EXTERNAL-IP   PORT(S)                      AGEingress-nginx   NodePort   10.105.72.96   <none>        80:30044/TCP,443:31453/TCP   3h

为了前面方便使用,我会把上述拜访入口设置为环境变量:

$ IC_IP=10.168.0.2 # 任意一台宿主机的地址$ IC_HTTPS_PORT=31453 # NodePort端口

[strong_begin]在Ingress Controller和它所须要的Service部署实现后,咱们就能够应用它了。[strong_end]

备注:这个“咖啡厅”Ingress的所有示例文件,都在这里。

首先,咱们要在集群里部署咱们的利用Pod和它们对应的Service,如下所示:

$ kubectl create -f cafe.yaml

而后,咱们须要创立Ingress所需的SSL证书(tls.crt)和密钥(tls.key),这些信息都是通过Secret对象定义好的,如下所示:

$ kubectl create -f cafe-secret.yaml

这一步实现后,咱们就能够创立在本篇文章一开始定义的Ingress对象了,如下所示:

$ kubectl create -f cafe-ingress.yaml

这时候,咱们就能够查看一下这个Ingress对象的信息,如下所示:

$ kubectl get ingressNAME           HOSTS              ADDRESS   PORTS     AGEcafe-ingress   cafe.example.com             80, 443   2h$ kubectl describe ingress cafe-ingressName:             cafe-ingressNamespace:        defaultAddress:          Default backend:  default-http-backend:80 (<none>)TLS:  cafe-secret terminates cafe.example.comRules:  Host              Path  Backends  ----              ----  --------  cafe.example.com                      /tea      tea-svc:80 (<none>)                    /coffee   coffee-svc:80 (<none>)Annotations:Events:  Type    Reason  Age   From                      Message  ----    ------  ----  ----                      -------  Normal  CREATE  4m    nginx-ingress-controller  Ingress default/cafe-ingress

能够看到,这个Ingress对象最外围的局部,正是Rules字段。其中,咱们定义的Host是cafe.example.com,它有两条转发规定(Path),别离转发给tea-svc和coffee-svc。

当然,在Ingress的YAML文件里,你还能够定义多个Host,比方restaurant.example.commovie.example.com等等,来为更多的域名提供负载平衡服务。

接下来,咱们就能够通过拜访这个Ingress的地址和端口,拜访到咱们后面部署的利用了,比方,当咱们拜访https://cafe.example.com:443/coffee时,应该是coffee这个Deployment负责响应我的申请。咱们能够来尝试一下:

$ curl --resolve cafe.example.com:$IC_HTTPS_PORT:$IC_IP https://cafe.example.com:$IC_HTTPS_PORT/coffee --insecureServer address: 10.244.1.56:80Server name: coffee-7dbb5795f6-vglbvDate: 03/Nov/2018:03:55:32 +0000URI: /coffeeRequest ID: e487e672673195c573147134167cf898

咱们能够看到,拜访这个URL 失去的返回信息是:Server name: coffee-7dbb5795f6-vglbv。这正是 coffee 这个 Deployment 的名字。

而当我拜访https://cafe.example.com:433/tea的时候,则应该是tea这个Deployment负责响应我的申请(Server name: tea-7d57856c44-lwbnp),如下所示:

$ curl --resolve cafe.example.com:$IC_HTTPS_PORT:$IC_IP https://cafe.example.com:$IC_HTTPS_PORT/tea --insecureServer address: 10.244.1.58:80Server name: tea-7d57856c44-lwbnpDate: 03/Nov/2018:03:55:52 +0000URI: /teaRequest ID: 32191f7ea07cb6bb44a1f43b8299415c

能够看到,Nginx Ingress Controller为咱们创立的Nginx负载均衡器,曾经胜利地将申请转发给了对应的后端Service。

以上,就是Kubernetes里Ingress的设计思维和应用办法了。

不过,你可能会有一个疑难,如果我的申请没有匹配到任何一条IngressRule,那么会产生什么呢?

首先,既然Nginx Ingress Controller是用Nginx实现的,那么它当然会为你返回一个 Nginx 的404页面。

不过,Ingress Controller也容许你通过Pod启动命令里的--default-backend-service参数,设置一条默认规定,比方:--default-backend-service=nginx-default-backend。

这样,任何匹配失败的申请,就都会被转发到这个名叫nginx-default-backend的Service。所以,你就能够通过部署一个专门的Pod,来为用户返回自定义的404页面了。

总结

在这篇文章里,我为你具体解说了Ingress这个概念在Kubernetes里到底是怎么一回事儿。正如我在文章里所形容的,Ingress实际上就是Kubernetes对“反向代理”的形象。

目前,Ingress只能工作在七层,而Service只能工作在四层。所以当你想要在Kubernetes里为利用进行TLS配置等HTTP相干的操作时,都必须通过Ingress来进行。

当然,正如同很多负载平衡我的项目能够同时提供七层和四层代理一样,未来Ingress的进化中,也会退出四层代理的能力。这样,一个比较完善的“反向代理”机制就比拟成熟了。

而Kubernetes提出Ingress概念的起因其实也非常容易了解,有了Ingress这个形象,用户就能够依据本人的需要来自由选择Ingress Controller。比方,如果你的利用对代理服务的中断十分敏感,那么你就应该思考抉择相似于Traefik这样反对“热加载”的Ingress Controller实现。

更重要的是,一旦你对社区里现有的Ingress计划感到不称心,或者你曾经有了本人的负载平衡计划时,你只须要做很少的编程工作,就能够实现一个本人的Ingress Controller。

在理论的生产环境中,Ingress带来的灵便度和自由度,对于应用容器的用户来说,其实是十分有意义的。要晓得,当年在Cloud Foundry我的项目里,不晓得有多少人为了给Gorouter组件配置一个TLS而伤透了脑筋。

思考题

如果我的需要是,当拜访www.mysite.com forums.mysite.com时,别离拜访到不同的Service(比方:site-svc和forums-svc)。那么,这个Ingress该如何定义呢?请你形容出YAML文件中的rules字段。