Kubernetes 的服务发现与负载均衡

Service 的作用

  在 如何在 Kubernetes 部署应用 中,我们讲到了如何部署应用程序,但还有几个问题没有解决:

  • 服务发现:由于 Kubernetes 的调度机制,在 Kubernetes 中,Pod 的 IP 不是固定的。如果其它程序需要访问这个 Pod,要怎么知道这个 Pod 的 IP 呢?
  • 负载均衡:由于 Deployment 管理着多个 Pod 的副本,如果其它程序需要访问这些 Pod,显然需要一个 proxy 为这些 Pod 做负载均衡。
  • 外部路由:如果应用程序运行在 Kubernetes 外部,如何访问 Kubernetes 内部的 Pod 呢?

  Kubernetes 提供了 Service 功能,用来解决这些问题。

服务发现与负载均衡

  Service 通常会和 Deployment 结合在一起使用,首先通过 Deployment 部署应用程序,然后再使用 Service 为应用程序提供服务发现、负载均衡和外部路由的功能。
  首先,让我们创建一个 Deployment:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$ cat nginx-deployment.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 2
selector:
matchLabels:
app: nginx-server # 这个 Deployment 管理着那些拥有这个标签的 Pod
template:
metadata:
labels:
app: nginx-server # 为所有 Pod 都打上这个标签
spec:
containers:
- name: nginx
image: nginx:1.7.9
ports:
- containerPort: 80
$ kubectl apply -f nginx-deployment.yaml

  接着,为这个 Deployment 创建一个 Service:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ cat nginx-service.yaml
apiVersion: v1
kind: Service
metadata:
name: nginx-service
labels:
name: nginx-service
spec:
ports:
- port: 5000
targetPort: 80
selector:
app: nginx-server
$ kubectl apply -f nginx-service.yaml

  这里我们创建了一个名为nginx-service的 Service,它监听的端口是 5000,同时它会把全部的流量都转发给它代理的所有 Pod(这些 Pod 都必须拥有app: nginx-server这个标签)。
  Service 提供了两种服务发现的方式,第一种是环境变量,第二种是 DNS。先说第一种,上面我们创建了nginx-service这个 Service,接着如果我们再创建另外一个 Pod,那么在这个 Pod 中,可以通过环境变量知道nginx-service的地址。
  首先,创建一个新的 Pod(这个 Pod 已经安装好了curl工具):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ cat curl-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: curl
spec:
containers:
- name: curl
image: tutum/curl
command:
- sleep
- "3600"
ports:
- containerPort: 80
$ kubectl apply -f curl-pod.yaml

  接着,进入这个 Pod,可以查看它的环境变量。可以看到,当 Kubernetes 创建这个 Pod 时,会自动注入这些环境变量:

1
2
3
4
5
6
7
8
9
$ kubectl exec -it curl bash
root@curl:/# env | grep NGINX
NGINX_SERVICE_PORT_5000_TCP=tcp://10.43.169.158:5000
NGINX_SERVICE_PORT_5000_TCP_PORT=5000
NGINX_SERVICE_PORT_5000_TCP_PROTO=tcp
NGINX_SERVICE_SERVICE_HOST=10.43.169.158
NGINX_SERVICE_PORT_5000_TCP_ADDR=10.43.169.158
NGINX_SERVICE_PORT=tcp://10.43.169.158:5000
NGINX_SERVICE_SERVICE_PORT=5000

  因此,在curl这个 Pod 中,可以通过访问这些环境变量,从而访问nginx-service


  另一种服务发现的方式是 DNS 解析。例如,我们进入 curl Pod 里面,可以通过 DNS 解析,访问nginx-service

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
$ kubectl exec -it curl bash
root@curl:/# curl http://nginx-service.default:5000
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>

  由于nginx-service的 namespace 是default,因此它的 DNS 域名是nginx-service.default


  再说说负载均衡,我们上面创建了nginx-service,这个 Service 会自动将接收到的流量转发给它代理的两个 Nginx Pod。

外部路由

  默认情况下,Service 的类型是 ClusterIP,因此当我们创建了一个 Service 之后,这个 Service 不能被外部访问。

1
2
3
$ kubectl get services nginx-service
NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx-service 10.43.169.158 <none> 5000/TCP 33m

  可以看到,nginx-service的 IP 是10.43.169.158,这个 IP 只能从 Kubernetes 内部才能访问。当然,除了 ClusterIP 之外,Service 还可以是其它类型:NodePort、LoadBalancer 和 ExternalName。
  这里说下 NodePort 类型的 Service。Kubernetes 会为 Service 分配一个端口,并且在所以节点都打开这个端口,因此我们可以通过任意一个节点,访问到这个 Service。
  例如,这里我们修改nginx-service的类型为 NodePort:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ cat nginx-service.yaml
apiVersion: v1
kind: Service
metadata:
name: nginx-service
labels:
name: nginx-service
spec:
type: NodePort
ports:
- port: 5000
targetPort: 80
selector:
app: nginx-server
$ kubectl apply -f nginx-service.yaml
service "nginx-service" configured

  可以看到分配的端口号为 31623:

1
2
3
$ kubectl get services nginx-service
NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx-service 10.43.169.158 <nodes> 5000:31623/TCP 45m

  这时候,通过集群的任何一个节点的 31623 端口,都可以访问到nginx-service。例如,假设集群有一个节点的 IP 是172.16.1.116,那么通过它的 31623 端口访问nginx-service

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
$ curl 172.16.1.116:31623
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>

参考资料