kubernetes-handbook/practice/service-discovery-and-loadb...

140 lines
5.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 服务发现与负载均衡
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在每台机器上绑定一个端口这样就可以通过`<NodeIP>:NodePort`来访问改服务
- LoadBalancer在NodePort的基础上借助cloud provider创建一个外部的负载均衡器并将请求转发到`<NodeIP>:NodePort`
另外也可以将已有的服务以Service的形式加入到Kubernetes集群中来只需要在创建Service的时候不指定Label selector而是在Service创建好后手动为其添加endpoint。
## Ingress Controller
Service虽然解决了服务发现和负载均衡的问题但它在使用上还是有一些限制比如
- 对外访问的时候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使用方法见<https://docs.traefik.io/user-guide/kubernetes/>
## 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