从0到1聊聊kubernetes service 到 pod的网络链路定位
导语
最近一个环境出现pod尚在not ready状态时就开始有业务流量进入;定位此问题也让我进一步加深对kubernetes集群内(service 到 pod)的网络链路的了解。另外当前kubernetes越来越普及,遇到一些关于kubernetes网络问题也会越频繁。本文将从认识kuberneres service 到kubernetes service 使用场景再到kubernetes service底层实现原理,最后通过示例方式展示如何从service到pod网络链路一步步定位分析。
认识service
Service 是 kuberneres将一组Pod暴露给外界访问的一种机制。为什么引入service?直接pod进行交互不行吗?引入service主要原因有二:
- pod 生命周期是短暂的,会随着发布新版本而被销毁,pod ip不固定;
- 一组pod负载均衡需求
而service恰好能通过标签选择器或直接与endpoint关联将一组pod进行绑定做为一组pod的统一入口和提供负载均衡能力。Service 有两种类型:service和Headless Service, Service 通常应用在无状态服务上;Headless Service 则应用在有状态服务。Service和Headless Service有什么区别呢?Service 和 Headless Service主要有如下表象区别:
- service 有一个cluster ip(虚拟ip)作为一组pod的统一入口,而headless service 则没有这个cluster ip;
- 通过service 名访问时,coredns或者kubedns会解析到service的vip地址,而headless service 则通常需要headless service 名前加上pod_name,有状态服务pod_name 都会带编号,这样coredns或者kubedns就可以直接解析到具体的pod实例。
为什么有了service还需要引入headless service呢?引入headless service为了解决有状态服务既要解决pod ip不固定并且还需要固定访问具体的pod(mysql 的主或者备)的问题。service 会采用负载均衡的方式引流到后端,无法固定;而headless service 通过结合coredns/kubedns解析后直接对接具体的pod。上面聊了这么多,接下来看看Service面纱背后面目:
apiVersion: v1
kind: Service # 定义当前kuernetes资源类型为Service
metadata:
name: my-service # service 名称
spec:
selector: # 标签选择器与一组pod进行绑定
app: MyApp # Pod上定义的一组标签
ports:
- protocol: TCP # 对外暴露的协议
port: 80 # service 的端口
targetPort: 9376 # pod监听端口
上面是通常使用的Service定义,那headless service又如何定义?其实只需要spec.clusterIP值设置为"None" 即可。
service使用场景
对Service有了一定认识,接下来聊聊Service的使用场景。
1. ClusterTpye
clusterType 场景很普遍,通常集群内通信几乎均采用clusterType类型场景;clusterType类型会创建一个虚拟ip地址作为一组pod的统一入口以及负载均衡。例子:
apiVersion: v1
kind: Service
metadata:
labels:
app: kibana
heritage: Tiller
release: kibana
name: kibana-kibana
namespace: default
spec:
ports:
- name: http # 当一个service需要对外暴露多个端口时,需要加名称加以区分
port: 5601
protocol: TCP
targetPort: 5601
selector:
app: kibana
release: kibana
sessionAffinity: None # 默认时rr(round-robin轮询),可以设置为: ClientIP,基于客户端ip的会话保持类似ha的source ip,nginx的ip_hash
type: ClusterIP # 定义为cluster Type类型的service,ClusterIP也是默认的service类型
通过使用
kubectl get svc $SERVICE_NAME -n $NAME_SPACES
[root@VM_70_26_centos ~]# kubectl get svc -n default
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
elasticsearch-master ClusterIP 172.x.y.127 <none> 9200/TCP,9300/TCP 6d6h
elasticsearch-master-headless ClusterIP None <none> 9200/TCP,9300/TCP 6d6h
kafka ClusterIP 172.x.y.142 <none> 9092/TCP 6d6h
kafka-headless ClusterIP None <none> 9092/TCP 6d6h
kafka-manager ClusterIP 172.x.y.198 <none> 9000/TCP 6d6h
kafka-zookeeper ClusterIP 172.x.y.86 <none> 2181/TCP 6d6h
kafka-zookeeper-headless ClusterIP None <none> 2181/TCP,3888/TCP,2888/TCP 6d6h
kibana-kibana ClusterIP 172.x.y.143 <none> 5601/TCP 6d6h
获取当前namespace下的service 列表. 使用
kubectl describe svc $SERVICE_NAME -n $NAME_SPACES
[root@VM_70_26_centos ~]# kubectl describe svc kibana-kibana -n pot
Name: kibana-kibana
Namespace: pot
Labels: app=kibana
heritage=Tiller
release=kibana
Annotations: <none>
Selector: app=kibana,release=kibana
Type: ClusterIP
IP: 172.x.y.143
Port: http 5601/TCP
TargetPort: 5601/TCP
Endpoints: 172.x.z.8:5601 # pod ip+port
Session Affinity: None
Events: <none>
查看service事件信息。使用
kubectl get svc $SERVICE_NAME -n $NAME_SPACES -o yaml
查看当前service配置内容。
2. NodePort
当一组pod需要跨集群或需要提供集群外访问能力时可以采用NodePort模式,使用NodePort方式会在集群内所有节点占用一个宿主机端口(默认值:30000-32767),可以通过–service-node-port-range设置端口范围段,为此使用此场景需要注意端口规划;访问时直接访问kubernetes集群中任意节点IP+映射的宿主机端口即可。
apiVersion: v1
kind: Service
labels:
app: kibana
heritage: Tiller
release: kibana
name: kibana-kibana
namespace: default
spec:
- name: http
port: 5601
protocol: TCP
targetPort: 5601
selector:
app: kibana
release: kibana
sessionAffinity: None
type: NodePort # 只需要将type 类型改为NodePort即可。
3. Headless
Headless service 主要应用在statefulset(有状态服务),对于有状态服务有的需要固定访问,比如mysql主从模式时需要写的时候通过service 访问时候不能轮训模式,因为只有主节点可以写,这时候就需要headless service,通过访问$pod_name.$headless_service_name.$namespace.svc.cluster.local
coredns/kubedns就会将此域名解析到具体的pod ip实现固定访问。
apiVersion: v1
kind: Service
metadata:
labels:
app: kibana
heritage: Tiller
release: kibana
name: kibana-kibana
namespace: default
spec:
clusterIP: None # 只需要将clusterIP设置为None即可
ports:
- name: http
port: 5601
protocol: TCP
targetPort: 5601
selector:
app: kibana
release: kibana
sessionAffinity: None
type: ClusterIP
4. LoadBalancer
当一组pod需要跨集群或需要提供集群外访问除了上述提到的NodePort方式,还可以对接IaaS层的负载均衡器,当然这个是需要编写对应的driver实现注册到IaaS的LoadBalancer,这种场景在云上(腾讯云,阿里云,华为云,ucloud等)已经做好了集成;不同厂商需要添加的annotations不一样,annotations届时看所应用的厂商配置说明即可,但根还是一样的。这种方式就解决了NodePort端口规划以及当某个节点异常了也会导致统一入口的高可用问题。以下annotations 以腾讯云tke为例:
apiVersion: v1
kind: Service
metadata:
annotations: # annotations 部分需要根据不同厂商而定
service.kubernetes.io/loadbalance-id: lb-xxx
service.kubernetes.io/qcloud-loadbalancer-clusterid: cls