From ed9f64d53d2ad62d1cce70fc17707d98289ebc14 Mon Sep 17 00:00:00 2001 From: Jimmy Song Date: Mon, 31 Jul 2017 11:31:13 +0800 Subject: [PATCH] =?UTF-8?q?=E7=A7=BB=E5=8A=A8=E6=96=87=E7=AB=A0=E4=BD=8D?= =?UTF-8?q?=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- concepts/service.md | 144 ------------------ images/service.jpg | Bin 42978 -> 0 bytes .../service-discovery-and-loadbalancing.md | 141 ++++++++++++++++- 3 files changed, 140 insertions(+), 145 deletions(-) delete mode 100644 images/service.jpg diff --git a/concepts/service.md b/concepts/service.md index 7fd9efccc..e69de29bb 100644 --- a/concepts/service.md +++ b/concepts/service.md @@ -1,144 +0,0 @@ -# 服务发现与负载均衡 - -Kubernetes在设计之初就充分考虑了针对容器的服务发现与负载均衡机制,提供了Service资源,并通过kube-proxy配合cloud provider来适应不同的应用场景。随着kubernetes用户的激增,用户场景的不断丰富,又产生了一些新的负载均衡机制。目前,kubernetes中的负载均衡大致可以分为以下几种机制,每种机制都有其特定的应用场景: - -- Service:直接用Service提供cluster内部的负载均衡,并借助cloud provider提供的LB提供外部访问 -- Ingress Controller:还是用Service提供cluster内部的负载均衡,但是通过自定义LB提供外部访问 -- Service Load Balancer:把load balancer直接跑在容器中,实现Bare Metal的Service Load Balancer -- Custom Load Balancer:自定义负载均衡,并替代kube-proxy,一般在物理部署Kubernetes时使用,方便接入公司已有的外部服务 - -## Service - -![](../images/service.jpg) - -Service是对一组提供相同功能的Pods的抽象,并为它们提供一个统一的入口。借助Service,应用可以方便的实现服务发现与负载均衡,并实现应用的零宕机升级。Service通过标签来选取服务后端,一般配合Replication Controller或者Deployment来保证后端容器的正常运行。 - -Service有三种类型: - -- ClusterIP:默认类型,自动分配一个仅cluster内部可以访问的虚拟IP -- NodePort:在ClusterIP基础上为Service在每台机器上绑定一个端口,这样就可以通过`:NodePort`来访问改服务 -- LoadBalancer:在NodePort的基础上,借助cloud provider创建一个外部的负载均衡器,并将请求转发到`:NodePort` - -另外,也可以将已有的服务以Service的形式加入到Kubernetes集群中来,只需要在创建Service的时候不指定Label selector,而是在Service创建好后手动为其添加endpoint。 - -## Ingress Controller - -Service虽然解决了服务发现和负载均衡的问题,但它在使用上还是有一些限制,比如 - -- 只支持4层负载均衡,没有7层功能 -- 对外访问的时候,NodePort类型需要在外部搭建额外的负载均衡,而LoadBalancer要求kubernetes必须跑在支持的cloud provider上面 - -Ingress就是为了解决这些限制而引入的新资源,主要用来将服务暴露到cluster外面,并且可以自定义服务的访问策略。比如想要通过负载均衡器实现不同子域名到不同服务的访问: - -``` -foo.bar.com --| |-> foo.bar.com s1:80 - | 178.91.123.132 | -bar.foo.com --| |-> bar.foo.com s2:80 -``` - -可以这样来定义Ingress: - -```yaml -apiVersion: extensions/v1beta1 -kind: Ingress -metadata: - name: test -spec: - rules: - - host: foo.bar.com - http: - paths: - - backend: - serviceName: s1 - servicePort: 80 - - host: bar.foo.com - http: - paths: - - backend: - serviceName: s2 - servicePort: 80 -``` - -**注意:** Ingress本身并不会自动创建负载均衡器,cluster中需要运行一个ingress controller来根据Ingress的定义来管理负载均衡器。目前社区提供了nginx和gce的参考实现。 - -[Traefik](https://traefik.io)提供了易用的Ingress Controller,使用方法见。 - -## Service Load Balancer - -在Ingress出现以前,Service Load Balancer是推荐的解决Service局限性的方式。Service Load Balancer将haproxy跑在容器中,并监控service和endpoint的变化,通过容器IP对外提供4层和7层负载均衡服务。 - -社区提供的Service Load Balancer支持四种负载均衡协议:TCP、HTTP、HTTPS和SSL TERMINATION,并支持ACL访问控制。 - -## Custom Load Balancer - -虽然Kubernetes提供了丰富的负载均衡机制,但在实际使用的时候,还是会碰到一些复杂的场景是它不能支持的,比如: - -- 接入已有的负载均衡设备 -- 多租户网络情况下,容器网络和主机网络是隔离的,这样`kube-proxy`就不能正常工作 - -这个时候就可以自定义组件,并代替kube-proxy来做负载均衡。基本的思路是监控kubernetes中service和endpoints的变化,并根据这些变化来配置负载均衡器。比如weave flux、nginx plus、kube2haproxy等。 - -## Endpoints - -有几种情况下需要用到没有selector的service。 - -- 使用kubernetes集群外部的数据库时 -- service中用到了其他namespace或kubernetes集群中的service -- 在kubernetes的工作负载与集群外的后端之间互相迁移 - -可以这样定义一个没有selector的service。 - -```yaml -kind: Service -apiVersion: v1 -metadata: - name: my-service -spec: - ports: - - protocol: TCP - port: 80 - targetPort: 9376 -``` - -定义一个Endpoints来对应该service。 - -```yaml -kind: Endpoints -apiVersion: v1 -metadata: - name: my-service -subsets: - - addresses: - - ip: 1.2.3.4 - ports: - - port: 9376 -``` - -访问没有selector的service跟访问有selector的service时没有任何区别。 - -使用kubernetes时有一个很常见的需求,就是当数据库部署在kubernetes集群之外的时候,集群内的service如何访问数据库呢?当然你可以直接使用数据库的IP地址和端口号来直接访问,有没有什么优雅的方式呢?你需要用到`ExternalName Service`。 - -```yaml -kind: Service -apiVersion: v1 -metadata: - name: my-service - namespace: prod -spec: - type: ExternalName - externalName: my.database.example.com - ports: - - port: 12345 -``` - -这个例子中,在kubernetes集群内访问`my-service`实际上会重定向到`my.database.example.com:12345`这个地址。 - -## 参考资料 - -- https://kubernetes.io/docs/concepts/services-networking/service/ -- http://kubernetes.io/docs/user-guide/ingress/ -- https://github.com/kubernetes/contrib/tree/master/service-loadbalancer -- https://www.nginx.com/blog/load-balancing-kubernetes-services-nginx-plus/ -- https://github.com/weaveworks/flux -- https://github.com/AdoHe/kube2haproxy - diff --git a/images/service.jpg b/images/service.jpg deleted file mode 100644 index 233aa89bb69636ffb5dcf0a6f7ed3c80766d0da9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 42978 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 SX1=6LtRF!^$a<3heEt`3lHd{m diff --git a/practice/service-discovery-and-loadbalancing.md b/practice/service-discovery-and-loadbalancing.md index 8fe919a72..8348d675f 100644 --- a/practice/service-discovery-and-loadbalancing.md +++ b/practice/service-discovery-and-loadbalancing.md @@ -1,2 +1,141 @@ -# 服务发现和负载均衡 +# 服务发现与负载均衡 +Kubernetes在设计之初就充分考虑了针对容器的服务发现与负载均衡机制,提供了Service资源,并通过kube-proxy配合cloud provider来适应不同的应用场景。随着kubernetes用户的激增,用户场景的不断丰富,又产生了一些新的负载均衡机制。目前,kubernetes中的负载均衡大致可以分为以下几种机制,每种机制都有其特定的应用场景: + +- Service:直接用Service提供cluster内部的负载均衡,并借助cloud provider提供的LB提供外部访问 +- Ingress Controller:还是用Service提供cluster内部的负载均衡,但是通过自定义LB提供外部访问 +- Service Load Balancer:把load balancer直接跑在容器中,实现Bare Metal的Service Load Balancer +- Custom Load Balancer:自定义负载均衡,并替代kube-proxy,一般在物理部署Kubernetes时使用,方便接入公司已有的外部服务 + +## Service + +Service是对一组提供相同功能的Pods的抽象,并为它们提供一个统一的入口。借助Service,应用可以方便的实现服务发现与负载均衡,并实现应用的零宕机升级。Service通过标签来选取服务后端,一般配合Replication Controller或者Deployment来保证后端容器的正常运行。 + +Service有三种类型: + +- ClusterIP:默认类型,自动分配一个仅cluster内部可以访问的虚拟IP +- NodePort:在ClusterIP基础上为Service在每台机器上绑定一个端口,这样就可以通过`:NodePort`来访问改服务 +- LoadBalancer:在NodePort的基础上,借助cloud provider创建一个外部的负载均衡器,并将请求转发到`:NodePort` + +另外,也可以将已有的服务以Service的形式加入到Kubernetes集群中来,只需要在创建Service的时候不指定Label selector,而是在Service创建好后手动为其添加endpoint。 + +## Ingress Controller + +Service虽然解决了服务发现和负载均衡的问题,但它在使用上还是有一些限制,比如 + +- 只支持4层负载均衡,没有7层功能 +- 对外访问的时候,NodePort类型需要在外部搭建额外的负载均衡,而LoadBalancer要求kubernetes必须跑在支持的cloud provider上面 + +Ingress就是为了解决这些限制而引入的新资源,主要用来将服务暴露到cluster外面,并且可以自定义服务的访问策略。比如想要通过负载均衡器实现不同子域名到不同服务的访问: + +``` +foo.bar.com --| |-> foo.bar.com s1:80 + | 178.91.123.132 | +bar.foo.com --| |-> bar.foo.com s2:80 +``` + +可以这样来定义Ingress: + +```yaml +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + name: test +spec: + rules: + - host: foo.bar.com + http: + paths: + - backend: + serviceName: s1 + servicePort: 80 + - host: bar.foo.com + http: + paths: + - backend: + serviceName: s2 + servicePort: 80 +``` + +**注意:** Ingress本身并不会自动创建负载均衡器,cluster中需要运行一个ingress controller来根据Ingress的定义来管理负载均衡器。目前社区提供了nginx和gce的参考实现。 + +[Traefik](https://traefik.io)提供了易用的Ingress Controller,使用方法见。 + +## Service Load Balancer + +在Ingress出现以前,Service Load Balancer是推荐的解决Service局限性的方式。Service Load Balancer将haproxy跑在容器中,并监控service和endpoint的变化,通过容器IP对外提供4层和7层负载均衡服务。 + +社区提供的Service Load Balancer支持四种负载均衡协议:TCP、HTTP、HTTPS和SSL TERMINATION,并支持ACL访问控制。 + +## Custom Load Balancer + +虽然Kubernetes提供了丰富的负载均衡机制,但在实际使用的时候,还是会碰到一些复杂的场景是它不能支持的,比如: + +- 接入已有的负载均衡设备 +- 多租户网络情况下,容器网络和主机网络是隔离的,这样`kube-proxy`就不能正常工作 + +这个时候就可以自定义组件,并代替kube-proxy来做负载均衡。基本的思路是监控kubernetes中service和endpoints的变化,并根据这些变化来配置负载均衡器。比如weave flux、nginx plus、kube2haproxy等。 + +## Endpoints + +有几种情况下需要用到没有selector的service。 + +- 使用kubernetes集群外部的数据库时 +- service中用到了其他namespace或kubernetes集群中的service +- 在kubernetes的工作负载与集群外的后端之间互相迁移 + +可以这样定义一个没有selector的service。 + +```yaml +kind: Service +apiVersion: v1 +metadata: + name: my-service +spec: + ports: + - protocol: TCP + port: 80 + targetPort: 9376 +``` + +定义一个Endpoints来对应该service。 + +```yaml +kind: Endpoints +apiVersion: v1 +metadata: + name: my-service +subsets: + - addresses: + - ip: 1.2.3.4 + ports: + - port: 9376 +``` + +访问没有selector的service跟访问有selector的service时没有任何区别。 + +使用kubernetes时有一个很常见的需求,就是当数据库部署在kubernetes集群之外的时候,集群内的service如何访问数据库呢?当然你可以直接使用数据库的IP地址和端口号来直接访问,有没有什么优雅的方式呢?你需要用到`ExternalName Service`。 + +```yaml +kind: Service +apiVersion: v1 +metadata: + name: my-service + namespace: prod +spec: + type: ExternalName + externalName: my.database.example.com + ports: + - port: 12345 +``` + +这个例子中,在kubernetes集群内访问`my-service`实际上会重定向到`my.database.example.com:12345`这个地址。 + +## 参考资料 + +- https://kubernetes.io/docs/concepts/services-networking/service/ +- http://kubernetes.io/docs/user-guide/ingress/ +- https://github.com/kubernetes/contrib/tree/master/service-loadbalancer +- https://www.nginx.com/blog/load-balancing-kubernetes-services-nginx-plus/ +- https://github.com/weaveworks/flux +- https://github.com/AdoHe/kube2haproxy \ No newline at end of file