Pod 被创建之后,该如何访问呢?直接访问 Pod 会有以下几个问题:
为了以一种固定的方式访问 Pod,Kubernetes 提供了一种负载均衡和服务发现的机制: Service。Service 创建后会提供一个固定的虚拟 IP(以 ClusterIP 类型的 Service 为例), 这样,用户无需关心 Pod 在哪个节点,通过固定 Service IP 即可实现对 Pod 地访问,并且 Service 可以对访问进行负载均衡。
示例:创建一个 Service
apiVersion: v1 kind: Service metadata: name: nginx # Service的名称 spec: selector: # Label Selector,选择包含app=nginx标签的Pod app: nginx ports: - name: service0 targetPort: 80 # Pod的端口 port: 8080 # Service对外暴露的端口 protocol: TCP # 转发协议类型,支持TCP和UDP type: ClusterIP # Service的类型 |
使用 IP 直连有一个问题,如果 Service 被删除,相应的 Cluster IP 也会被回收。域名发现
为此 Kubernetes 结合 DNS 的。CoreDNS 安装成功后会成为 DNS 服务器,当创建 Service 后,CoreDNS 会将 Service 的名称与 IP 记录起来,这样 Pod 就可以通过向 CoreDNS 查询 Service 的名称获得 Service 的 IP 地址。
coreDNS 提供格式如 <service-name>.<namespace-name>.svs.cluster.local 的服务,访问该地址,集群内的域名解析解析服务器会返回该服务所对应的 A 记录。实际使用中,同一个命名空间下可以省略<namespace>.svc.cluster.local,直接使用 ServiceName 即可。
例如上面创建的名为 nginx 的 Service,直接通过 nginx:8080 就可以访问到 Service,进而访问 Pod, 这样无需感知具体 Service 的 IP 地址。
Service 的类型除了 ClusterIP 还有 NodePort、LoadBalancer 和 Headless Service,这几种类型的 Service 有着不同的用途。
NodePort 类型的 Service 可以让 Kubernetes 集群每个节点上保留一个相同的端口, 外部访问连接首先访问节点 IP:Port,然后将这些连接转发给服务对应的 Pod。如下图所示。
下面是一个创建 NodePort 类型的 Service。创建完成后,可以通过节点的 IP:Port访问到后台 Pod
apiVersion: v1 kind: Service metadata: name: nodeport-service spec: type: NodePort ports: - port: 8080 targetPort: 80 nodePort: 3030 selector: app: nginx |
LoadBalancer 类型的 Service 其实是 NodePort 类型 Service 的扩展,通过一个特定的 LoadBalancer 访问 Service,这个 LoadBalancer 将请求转发到节点的 NodePort。
LoadBalancer 本身不是属于 Kubernetes 的组件,这部分通常是由具体厂商(云服务提供商)提供,不同厂商的 Kubernetes 集群与 LoadBalancer 的 对接实现各不相同,例如 CCE 对接了 ELB。这就导致了创建 LoadBalancer 类型的 Service 有不同的实现。
下面是一个创建 LoadBalancer 类型的 Service。创建完成后,可以通过 ELB 的 IP:Port访问到后台 Pod
apiVersion: v1 kind: Service metadata: annotations: kubernetes.io/elb.id: 3c7caa5a-a641-4bff-801a-feace27424b6 labels: app: nginx name: nginx spec: loadBalancerIP: 10.28.12.100 # ELB实例的IP地址 ports: - name: service0 port: 80 protocol: TCP targetPort: 80 nodePort: 3030 selector: app: nginx type: LoadBalancer # 类型为LoadBalancer |
前面讲的 Service 解决了 Pod 的内外部访问问题,允许客户端连接到 Service 关联的某个 Pod。但还有下面这些问题没解决。
为了解决以上问题,Kubernetes 提供了另一种较为特殊的 Service 类型,称为 Headless Service。
对于其他 Service 来说,客户端在访问服务时,DNS 查询时只会返回 Service 的 ClusterIP 地址,具体访问到哪个 Pod 是由集群转发规则(IPVS 或 iptables)决定的。而 Headless Service 并不会分配单独的 ClusterIP,在进行 DNS 查询时会返回所有 Pod 的 DNS 记录,这样就可查询到每个 Pod 的 IP 地址。StatefulSet 中 StatefulSet 正是使用 Headless Service 解决 Pod 间互相访问的问题
apiVersion: v1 kind: Service # 对象类型为Service metadata: name: nginx-headless labels: app: nginx spec: ports: - name: nginx # Pod间通信的端口名称 port: 80 # Pod间通信的端口号 selector: app: nginx # 选择标签为app:nginx的Pod clusterIP: None # 必须设置为None,表示Headless Service |
创建完成后可以查询 Service。
# kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE nginx-headless ClusterIP None <none> 80/TCP 5s |
创建一个 Pod 来查询 DNS,可以看到能返回所有 Pod 的记录,这就解决了访问所有 Pod 的问题了。
$ kubectl run -i --tty --image tutum/dnsutils dnsutils --restart=Never --rm /bin/sh If you don't see a command prompt, try pressing enter. / # nslookup nginx-headless Server: 10.247.3.10 Address: 10.247.3.10#53 Name: nginx-headless.default.svc.cluster.local Address: 172.16.0.31 Name: nginx-headless.default.svc.cluster.local Address: 172.16.0.18 Name: nginx-headless.default.svc.cluster.local Address: 172.16.0.19 |