Service 存在的意义
在进行业务升级的时候pod中容器的ip会发生变化,Service引入主要是解决Pod的动态变化,提供统一访问入口,下面是service的两个主要功能:
- 防止Pod失联,准备找到提供同一个服务的Pod(服务发现)
- 定义一组Pod的访问策略(负载均衡)
我们不应该期望 Kubernetes Pod 是健壮的,而是要假设 Pod 中的容器很可能因为各种原因发生故障而死掉。Deployment 等 controller 会通过动态创建和销毁 Pod 来保证应用整体的健壮性。换句话说,Pod 是脆弱的,但应用是健壮的。
每个 Pod 都有自己的 IP 地址。当 controller 用新 Pod 替代发生故障的 Pod 时,新 Pod 会分配到新的 IP 地址。这样就产生了一个问题:如果一组 Pod 对外提供服务(比如 HTTP),它们的 IP 很有可能发生变化,那么客户端如何找到并访问这个服务呢?
Kubernetes 给出的解决方案是 Service。(Service为了给客户端提供固定的访问端点,因此在客户端和服务端提供了中间层叫Service,Service名称解析是强依赖于Dns解析组件的,部署完k8s还需要部署CoreDns)
Service三种常用类型
NodePort:在每个节点上启用一个端口来暴露服务,可以在集群外部访问。也会分配一个稳定内部集群IP地址。
访问地址:<任意NodeIP>:<NodePort>
端口范围:30000-32767
除了 Cluster 内部可以访问 Service,很多情况我们也希望应用的 Service 能够暴露给 Cluster 外部。Kubernetes 提供了多种类型的 Service,默认是 ClusterIP。
ClusterIP
Service 通过 Cluster 内部的 IP 对外提供服务,只有 Cluster 内的节点和 Pod 可访问,这是默认的 Service 类型。
LoadBalancer:与NodePort类似,在每个节点上启用一个端口来暴露服务。除此之外,Kubernetes会请求底层云平台(例如阿里云、腾讯云、AWS等)上的负载均衡器,将每个Node ([NodeIP]:[NodePort])作为后端添加进去
Pod与Service的关系
• Service通过标签关联一组Pod
• Service使用iptables或者ipvs为一组Pod提供负载均衡能力
创建测试Pod
Kubernetes Service 从逻辑上代表了一组 Pod,具体是哪些 Pod 则是由 label 来挑选。Service 有自己 IP,而且这个 IP 是不变的。客户端只需要访问 Service 的 IP,Kubernetes 则负责建立和维护 Service 与 Pod 的映射关系。无论后端 Pod 如何变化,对客户端不会有任何影响,因为 Service 没有变。
来看个例子,创建下面的这个 Deployment:
[root@k8s-master ~]# cat service-clusterip.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
namespace: default
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels: #这个标签很重要,service通过该标签来关联pod
app: nginx
spec:
containers:
- name: web
image: nginx:1.19
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: web
namespace: default
spec:
ports:
- port: 80 #service端口,相对于负载均衡器的端口,为前端提供的端口
protocol: TCP #协议
targetPort: 80 #容器端口
selector: # 指定关联Pod的标签
app: nginx
type: ClusterIP # 服务类型,默认为clusterip
#当使用clusterip的时候只有两个端口有用,servicer地址端口和容器端口
ports:
- port: 88 service地址上端口
targetPort: 80 容器端口
#selector 指明挑选那些 label 为 run: httpd 的 Pod 作为 Service 的后端
#将 Service 的 80 端口映射到 Pod 的 80 端口,使用 TCP 协议
我们启动了三个 Pod,label 是app:nginx
,Service 将会用这个 label 来挑选 Pod。( Pod 分配了各自的 IP,这些 IP 只能被 Kubernetes Cluster 中的容器和节点访问。 )
[root@k8s-master ~]# kubectl get pod -l app=nginx -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
web-559cd747d5-58v72 1/1 Running 0 3m29s 10.244.169.141 k8s-node2 <none> <none>
web-559cd747d5-cwmjt 1/1 Running 0 3m29s 10.244.36.77 k8s-node1 <none> <none>
web-559cd747d5-fz8c7 1/1 Running 0 3m29s 10.244.169.142 k8s-node2 <none> <none>
[root@k8s-master ~]# curl 10.244.169.141
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
看看service关联哪些pod
[root@k8s-master ~]# kubectl get svc,ep
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 39d
service/web ClusterIP 10.111.237.182 <none> 80/TCP 10m
NAME ENDPOINTS AGE
endpoints/kubernetes 192.168.179.102:6443 39d
endpoints/web 10.244.169.141:80,10.244.169.142:80,10.244.36.77:80 10m
访问这个Ip就会转发到后端的pod,这里相对于lb负载均衡。前端要访问也是访问该service的clusterIP加上端口
[root@k8s-master ~]# curl 10.111.237.182:80
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
根据前面的端口映射,这里要使用 8080 端口。另外,除了我们创建的 httpd-svc
,还有一个 Service kubernetes
,Cluster 内部通过这个 Service 访问 kubernetes API Server。
通过 kubectl describe
可以查看 web 与 Pod 的对应关系。
[root@k8s-master ~]# kubectl describe svc web
Name: web
Namespace: default
Labels: <none>
Annotations: <none>
Selector: app=nginx
Type: ClusterIP
IP: 10.111.237.182
Port: <unset> 80/TCP
TargetPort: 80/TCP
Endpoints: 10.244.169.141:80,10.244.169.142:80,10.244.36.77:80
Session Affinity: None
Events: <none>
Coredns解析
还可以通过名称去访问,注意这里的busybox版本要是这个1.28.4版本,最新版本会出现dns解析有问题,coredns只能解析出IP具体访问需要IP+端口
[root@k8s-master ~]# kubectl run -it dns-test --rm --image=busybox:1.28.4 -- sh
If you don't see a command prompt, try pressing enter.
/ # nslookup web
Server: 10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local
Name: web
Address 1: 10.111.237.182 web.default.svc.cluster.local
/ # wget nginx-service:8888
Connecting to nginx-service:8888 (10.111.237.182:80)
index.html 100% |******************************************************************| 612 0:00:00 ETA
如果集群内部访问直接写 web.default这个名称就行或者clusterip也可以,写名称好一些,即使service重新创建了,名称也不会发生变化。如果service误删除了,重新创建那么还得去程序修改Ip
上面的Endpoints
罗列了3个 Pod 的 IP 和端口。我们知道 Pod 的 IP 是在容器中配置的,那么 Service 的 Cluster IP 又是配置在哪里的呢?CLUSTER-IP 又是如何映射到 Pod IP 的呢?
答案是 iptables,下篇博客介绍。