Service—服务暴露

如果你使用 Deployment 来运行你的应用程序,则它可以动态创建和销毁 Pod。每个 Pod 都有自己的 IP 地址,但是在 Deployment 中,在同一时刻运行的 Pod 集合可能与稍后运行该应用程序的 Pod 集合不同,也就是说Pod是非永久性的资源。这导致了一个问题: 如果一组 Pod(称为“后端”)为集群内的其他 Pod(称为“前端”)提供功能, 那么前端如何找出并跟踪要连接的 IP 地址,以便前端可以使用提供工作负载的后端部分?

Service 资源允许 Kubernetes 对外暴露 Pods 中运行的应用程序,以支持他们被访问。使用 Kubernetes,你无需修改应用程序即可使用不熟悉的服务发现机制。 Kubernetes 为 Pods 提供自己的 IP 地址,并为一组 Pod 提供相同的 DNS 名, 并且可以在它们之间进行负载均衡。

对一些应用的某些部分(如前端),可能希望将其暴露给 Kubernetes 集群外部 的 IP 地址。
Service 允许指定你所需要的 Service 类型,默认是 ClusterIP。类型Type的取值以及行为如下:
(1)ClusterIP:通过集群的内部 IP 暴露服务,选择该值时服务只能够在集群内部访问。 这也是默认的 ServiceType。
(2)NodePort:通过每个节点上的 IP 和静态端口(NodePort)暴露服务。 通过请求节点 IP上的节点端口,可以从集群的外部访问一个 NodePort 服务。
(3)LoadBalancer:使用云提供商的负载均衡器向外部暴露服务。 外部负载均衡器可以将流量路由到自动创建的 NodePort 服务和 ClusterIP 服务上。
(4)ExternalName:通过返回 CNAME 和对应值,可以将服务映射到 externalName 字段的内容(例如,foo.bar.example.com)。 无需创建任何类型代理。

一、Service ClusterIP

Kubernetes Service 定义了这样一种抽象:逻辑上的一组 Pod,一种可以访问它们的策略 —— 通常称为微服务。 Service 所针对的 Pods 集合通常是通过选择算符来确定的。Service来负责维护这一组标签选择符选择的Pod。

当Service的类型为ClusterIP时,Kubernetes集群会为Service为分配对应的集群IP,并同时产生一个域名。集群内部可以通过这集群IP或者域名都可以访问整个Service对应的Pod。

举个例子,我们创建1个yaml文件,内容如下,其中的—代表文件的分隔符,可以类比Java编码中的不同的类的源码文件。

apiVersion: v1
kind: Service
metadata:
  name: nginx-service
spec:
  ports:
    - port: 80
      protocol: TCP
      targetPort: 80
  selector:
    app: nginx # 定义此Service关联的Pod对应的标签
  type: ClusterIP # 此Service类型为ClusterIP

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels: # 定义Deployment关联的Pod的标签
      app: nginx
  replicas: 3 # 告诉Deployment运行的Pod实例数目
  template: # Pod的模板
    metadata:
      labels:
        app: nginx # Pod的标签
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80

第一步,根据这个yaml文件,创建Service,此Service的type为ClusterIP,管理的Pod是标签为app: nginx的一组Pod。再次强调,Deployment负责维护Pod的数目与Pod的生命周期的管理,Service负责这组Pod的被访问机制。

kubectl apply -f 1-service.yaml

第二步,验证生成的资源情况,如图所示,Deployment、Pod、ReplicaSet均正常创建,Service也创建成功,对应的ClusterIP为10.109.194.117。

[root@kubernetes-master01 service]# kubectl get all
NAME                                    READY   STATUS    RESTARTS   AGE
pod/nginx-deployment-6b474476c4-c7fbb   1/1     Running   0          69s
pod/nginx-deployment-6b474476c4-c94mh   1/1     Running   0          69s
pod/nginx-deployment-6b474476c4-sm5f2   1/1     Running   0          69s

NAME                    TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE
service/nginx-service   ClusterIP   10.109.194.117   <none>        80/TCP    69s

NAME                               READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/nginx-deployment   3/3     3            3           69s

NAME                                          DESIRED   CURRENT   READY   AGE
replicaset.apps/nginx-deployment-6b474476c4   3         3         3       69s

第三步,接下来详细的看下Service的情况,如图所示,nginx-service下面关联的Endpoints,为10.244.1.49:80,10.244.1.50:80,10.244.2.19:80。

[root@kubernetes-master01 service]# kubectl describe service nginx-service
Name:              nginx-service
Namespace:         default
Labels:            <none>
Annotations:       Selector:  app=nginx
Type:              ClusterIP
IP:                10.109.194.117
Port:              <unset>  80/TCP
TargetPort:        80/TCP
Endpoints:         10.244.1.49:80,10.244.1.50:80,10.244.2.19:80
Session Affinity:  None
Events:            <none>

第四步,查看集群创建的标签为app: nginxPod的IP地址如下图,与Service所关联的IP地址是相同的。

[root@kubernetes-master01 service]# kubectl get pod -l app=nginx -o wide
NAME                                READY   STATUS    RESTARTS   AGE     IP
nginx-deployment-6b474476c4-c7fbb   1/1     Running   0          7m41s   10.244.1.50   
nginx-deployment-6b474476c4-c94mh   1/1     Running   0          7m41s   10.244.2.19   
nginx-deployment-6b474476c4-sm5f2   1/1     Running   0          7m41s   10.244.1.49   

第五步,通过Service的CLUSTER-IP来访问这三个Pod组成的服务,注意此CLUSTER-IP只能在Kubernetes集群所安装的机器上访问,外部是无法访问的。

[root@kubernetes-master01 ~]# curl 10.109.194.117
<!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>

第六步,执行如下命令,进入集群中随意的一个Pod中,查看service的DNS记录情况,如图所示,此Service对应的IP正是此Service对应的ClusterIP 10.109.194.117。如图所示,我们在Pod内部直接以域名的方式访问service,

kubectl exec -it nginx-deployment-6b474476c4-c7fbb – /bin/sh

# nslookup nginx-service
Server:		10.96.0.10
Address:	10.96.0.10#53

Name:	nginx-service.default.svc.cluster.local
Address: 10.109.194.117
# curl nginx-service
<!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>

在 Kubernetes 中,支持两种基本的服务发现模式, 环境变量和 DNS。

(1)环境变量
当 Pod 运行在 Node 上,kubelet 会为每个活跃的 Service 添加一组环境变量。例如简单的 {SVCNAME}_SERVICE_HOST 和 {SVCNAME}_SERVICE_PORT 变量。 这里 Service 的名称需大写,横线被转换成下划线。
举个例子,一个名称为 redis-master 的 Service 暴露了 TCP 端口 6379, 同时给它分配了 Cluster IP 地址 10.0.0.11,这个 Service 生成了如下环境变量:
1)REDIS_MASTER_SERVICE_HOST=10.0.0.11
2)REDIS_MASTER_SERVICE_PORT=6379
3)REDIS_MASTER_PORT=tcp://10.0.0.11:6379
4)REDIS_MASTER_PORT_6379_TCP=tcp://10.0.0.11:6379
5)REDIS_MASTER_PORT_6379_TCP_PROTO=tcp
6)REDIS_MASTER_PORT_6379_TCP_PORT=6379
7)REDIS_MASTER_PORT_6379_TCP_ADDR=10.0.0.11
说明:
当你具有需要访问服务的 Pod 时,并且你正在使用环境变量方法将端口和集群 IP 发布到客户端 Pod 时,必须在客户端 Pod 出现 之前 创建服务。 否则,这些客户端 Pod 将不会设定其环境变量。如果仅使用 DNS 查找服务的集群 IP,则无需担心此设定问题。

(2)DNS
可以使用附加组件(例如 CoreDNS) 为 Kubernetes 集群设置 DNS 服务。支持集群的 DNS 服务器监视 Kubernetes API 中的新服务,并为每个服务创建一组 DNS 记录。 如果在整个集群中都启用了 DNS,则所有 Pod 都应该能够通过其 DNS 名称自动解析服务。
例如,如果在 Kubernetes 命名空间 my-ns 中有一个名为 my-service 的服务, 则控制平面和 DNS 服务共同为 my-service.my-ns 创建 DNS 记录。 my-ns 命名空间中的 Pod 应该能够通过简单地按名检索 my-service 来找到它 (my-service.my-ns 也可以工作)。其他命名空间中的 Pod 必须将名称限定为 my-service.my-ns。 这些名称将解析为为服务分配的集群 IP。

如上所介绍,在进群内部,我们可以通过ClusterIP、DNS、以及环境变量的方式访问提供服务的一组Pod。如图所示,在Pod内部查看环境变量,是nginx-service对应的CLUSTER-IP地址与端口,验证了环境变量的理论。

# echo ${NGINX_SERVICE_PORT}
tcp://10.109.194.117:80

可以在Pod内部以Service的域名的方式,访问集群内部的无状态服务,或者以变量的方式来访问,一般不会以ClusterIP的方式来访问,因为需要Service创建后,Kubernetes才会给Service分配ClusterIP,不方便集成,同时不同的环境中同一Service对应的ClusterIP也不会相同,如测试环境与生产环境。

二、猜你喜欢

如果你对容器化技术的相关知识感兴趣,可以阅读:秀丽的容器化技术Kubernetes专栏

本专栏特点:

  • 理论结合实战
  • 讲解深入浅出
Logo

开源、云原生的融合云平台

更多推荐