点击领取2000元阿里云代金券,付款抵现金用,云服务器69元/年限时抢>>> 点击咨询成为阿里云VIP客户享永久折扣>>> 点击领取2860元腾讯云代金券,付款抵现金用,云服务器88元/年限时抢>>> 点击成为腾讯云VIP客户享永久折扣优惠>>> 点击进入华为云最新优惠活动现场>>> 点击成为华为云VIP客户享永久折扣优惠>>> 直接访问Pod的问题Pod创建完成后,如何访问Pod呢?直接访问Pod会有如下几个问题:
举个例子,假设有这样一个应用程序,使用Deployment创建了前台和后台,前台会调用后台做一些计算处理,如图1所示。后台运行了3个Pod,这些Pod是相互独立且可被替换的,当Pod出现状况被重建时,新建的Pod的IP地址是新IP,前台的Pod无法直接感知。 使用Service解决Pod的访问问题Kubernetes中的Service对象就是用来解决上述Pod访问问题的。Service有一个固定IP地址(在创建CCE集群时有一个服务网段的设置,这个网段专门用于给Service分配IP地址),Service将访问它的流量转发给Pod,具体转发给哪些Pod通过Label来选择,而且Service可以给这些Pod做负载均衡。 那么对于上面的例子,为后台添加一个Service,通过Service来访问Pod,这样前台Pod就无需感知后台Pod的变化,如图2所示。 创建后台Pod首先创建一个3副本的Deployment,即3个Pod,且Pod上带有标签“app: nginx”,具体如下所示。 apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- image: nginx:latest
name: container-0
resources:
limits:
cpu: 100m
memory: 200Mi
requests:
cpu: 100m
memory: 200Mi
imagePullSecrets:
- name: default-secret
创建Service下面示例创建一个名为“nginx”的Service,通过selector选择到标签“app:nginx”的Pod,目标Pod的端口为80,Service对外暴露的端口为8080。 访问服务只需要通过“服务名称:对外暴露的端口”接口,对应本例即“nginx:8080”。这样,在其他Pod中,只需要通过“nginx:8080”就可以访问到“nginx”关联的Pod。 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的类型 将上面Service的定义保存到nginx-svc.yaml文件中,使用kubectl创建这个Service。 $ kubectl create -f nginx-svc.yaml
service/nginx created
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.247.0.1 <none> 443/TCP 7h19m
nginx ClusterIP 10.247.124.252 <none> 8080/TCP 5h48m
您可以看到Service有个Cluster IP,这个IP是固定不变的,除非Service被删除,所以您也可以使用ClusterIP在集群内部访问Service。 下面创建一个Pod并进入容器,使用ClusterIP访问Pod,可以看到能直接返回内容。 $ kubectl run -i --tty --image nginx:alpine test --rm /bin/sh
If you don't see a command prompt, try pressing enter.
/ # curl 10.247.124.252:8080
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...
使用ServiceName访问Service通过DNS进行域名解析后,可以使用“ServiceName:Port”访问Service,这也是Kubernetes中最常用的一种使用方式。在创建CCE集群的时候,会默认要求安装CoreDNS插件,在kube-system命名空间下可以查看到CoreDNS的Pod。 $ kubectl get po --namespace=kube-system NAME READY STATUS RESTARTS AGE coredns-7689f8bdf-295rk 1/1 Running 0 9m11s coredns-7689f8bdf-h7n68 1/1 Running 0 11m CoreDNS安装成功后会成为DNS服务器,当创建Service后,CoreDNS会将Service的名称与IP记录起来,这样Pod就可以通过向CoreDNS查询Service的名称获得Service的IP地址。 访问时通过nginx.<namespace>.svc.cluster.local访问,其中nginx为Service的名称,<namespace>为命名空间名称,svc.cluster.local为域名后缀,在实际使用中,在同一个命名空间下可以省略<namespace>.svc.cluster.local,直接使用ServiceName即可。 例如上面创建的名为nginx的Service,直接通过“nginx:8080”就可以访问到Service,进而访问后台Pod。 使用ServiceName的方式有个主要的优点就是可以在开发应用程序时可以将ServiceName写在程序中,这样无需感知具体Service的IP地址。 下面创建一个Pod并进入容器,查询nginx域名的地址,可以发现是解析出nginx这个Service的IP地址10.247.124.252;同时访问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 Server: 10.247.3.10 Address: 10.247.3.10#53 Name: nginx.default.svc.cluster.local Address: 10.247.124.252 / # curl nginx:8080 <!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> ... Service是如何做到服务发现的前面说到有了Service后,无论Pod如何变化,Service都能够发现到Pod。 如果调用kubectl describe命令查看Service的信息,您会看下如下信息。 $ kubectl describe svc nginx
Name: nginx
......
Endpoints: 172.16.2.132:80,172.16.3.6:80,172.16.3.7:80
......
可以看到一个Endpoints,Endpoints同样也是Kubernetes的一种资源对象,可以查询得到。Kubernetes正是通过Endpoints监控到Pod的IP,从而让Service能够发现Pod。 $ kubectl get endpoints
NAME ENDPOINTS AGE
kubernetes 192.168.0.127:5444 7h19m
nginx 172.16.2.132:80,172.16.3.6:80,172.16.3.7:80 5h48m
这里的172.16.2.132:80是Pod的IP:Port,通过如下命令可以查看到Pod的IP,与上面的IP一致。 $ kubectl get po -o wide NAME READY STATUS RESTARTS AGE IP NODE nginx-869759589d-dnknn 1/1 Running 0 5h40m 172.16.3.7 192.168.0.212 nginx-869759589d-fcxhh 1/1 Running 0 5h40m 172.16.3.6 192.168.0.212 nginx-869759589d-r69kh 1/1 Running 0 5h40m 172.16.2.132 192.168.0.94
如果删除一个Pod,Deployment会将Pod重建,新的Pod IP会发生变化。 $ kubectl delete po nginx-869759589d-dnknn
pod "nginx-869759589d-dnknn" deleted
$ kubectl get po -o wide
NAME READY STATUS RESTARTS AGE IP NODE
nginx-869759589d-fcxhh 1/1 Running 0 5h41m 172.16.3.6 192.168.0.212
nginx-869759589d-r69kh 1/1 Running 0 5h41m 172.16.2.132 192.168.0.94
nginx-869759589d-w98wg 1/1 Running 0 7s 172.16.3.10 192.168.0.212
再次查看Endpoints,会发现Endpoints的内容随着Pod发生了变化。 $ kubectl get endpoints
NAME ENDPOINTS AGE
kubernetes 192.168.0.127:5444 7h20m
nginx 172.16.2.132:80,172.16.3.10:80,172.16.3.6:80 5h49m
下面进一步了解这又是如何实现的。 在Kubernetes集群架构中介绍过Node节点上的kube-proxy,实际上Service相关的事情都由节点上的kube-proxy处理。在Service创建时Kubernetes会分配IP给Service,同时通过API Server通知所有kube-proxy有新Service创建了,kube-proxy收到通知后通过iptables记录Service和IP/端口对的关系,从而让Service在节点上可以被查询到。 下图是一个实际访问Service的图示,Pod X访问Service(10.247.124.252:8080),在往外发数据包时,在节点上根据iptables规则目的IP:Port被随机替换为Pod1的IP:Port,从而通过Service访问到实际的Pod。 除了记录Service和IP/端口对的关系,kube-proxy还会监控Service和Endpoint的变化,从而保证Pod重建后仍然能通过Service访问到Pod。 图3 Pod X访问Service的过程
Service的类型与使用场景Service的类型除了ClusterIP还有NodePort、LoadBalancer和None,这几种类型的Service有着不同的用途。
NodePort类型的ServiceNodePort类型的Service可以让Kubemetes集群每个节点上保留一个相同的端口, 外部访问连接首先访问节点IP:Port,然后将这些连接转发给服务对应的Pod。如下图所示。 图4 NodePort Service
下面是一个创建NodePort类型的Service。创建完成后,可以通过节点的IP:Port访问到后台Pod。
apiVersion: v1
kind: Service
metadata:
name: nodeport-service
spec:
type: NodePort
ports:
- port: 8080
targetPort: 80
nodePort: 30120
selector:
app: nginx
创建并查看,可以看到PORT这一列为8080:30120/TCP,说明Service的8080端口是映射到节点的30120端口。 $ kubectl create -f nodeport.yaml service/nodeport-service created $ kubectl get svc -o wide NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR kubernetes ClusterIP 10.247.0.1 <none> 443/TCP 107m <none> nginx ClusterIP 10.247.124.252 <none> 8080/TCP 16m app=nginx nodeport-service NodePort 10.247.210.174 <none> 8080:30120/TCP 17s app=nginx 此时,通过节点IP:端口访问Service可以访问到Pod,如下所示。 $ kubectl run -i --tty --image nginx:alpine test --rm /bin/sh
If you don't see a command prompt, try pressing enter.
/ # curl 192.168.0.212:30120
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
......
LoadBalancer类型的ServiceLoadBalancer类型的Service其实是NodePort类型Service的扩展,通过一个特定的LoadBalancer访问Service,这个LoadBalancer将请求转发到节点的NodePort。 LoadBalancer本身不是属于Kubernetes的组件,这部分通常是由具体厂商(云服务提供商)提供,不同厂商的Kubernetes集群与LoadBalancer的对接实现各不相同,例如华为云CCE对接了ELB。这就导致了创建LoadBalancer类型的Service有不同的实现。 图5 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.78.42.242 # ELB实例的IP地址
ports:
- name: service0
port: 80
protocol: TCP
targetPort: 80
nodePort: 30120
selector:
app: nginx
type: LoadBalancer # 类型为LoadBalancer
上面metadata.annotations里的参数配置是CCE的LoadBalancer类型Service需要配置的参数,表示这个Service绑定哪个ELB实例。CCE还支持创建LoadBalancer类型Service时新建ELB实例,详细的内容请参见负载均衡(LoadBalancer)。 Headless Service前面讲的Service解决了Pod的内外部访问问题,但还有下面这些问题没解决。
Headless Service正是解决这个问题的,Headless Service不会创建ClusterIP,并且查询会返回所有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 执行如下命令创建Headless Service。 # kubectl create -f headless.yaml service/nginx-headless created 创建完成后可以查询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-0.nginx Server: 10.247.3.10 Address: 10.247.3.10#53 Name: nginx-0.nginx.default.svc.cluster.local Address: 172.16.0.31 / # nslookup nginx-1.nginx Server: 10.247.3.10 Address: 10.247.3.10#53 Name: nginx-1.nginx.default.svc.cluster.local Address: 172.16.0.18 / # nslookup nginx-2.nginx Server: 10.247.3.10 Address: 10.247.3.10#53 Name: nginx-2.nginx.default.svc.cluster.local Address: 172.16.0.19 父主题: Kubernetes网络
|