Kubernetes中每个Pod都会被分配一个单独的IP地址,而且每个Pod都提供了一个独立的Endpoint(Pod IP+ContainerPort)以被客户端访问,现在多个Pod副本组成了一个集群来提供服务,那么客户端如何来访问它们呢?
运行在每个Node上的kube-proxy进程就是软件负载均衡器,负责把对Service的请求转发到后端的某个Pod实例上,并在内部实现服务的负载均衡与会话保持机制。
每个Service都被分配了一个全局唯一的虚拟IP地址(Cluster IP)。这样每个服务就变成了具备唯一IP地址的通信节点,服务调用就变成了最基础的TCP网络通信问题。
Pod 的 Endpoint 地址会随着 Pod 的销毁和重新创建而发生改变,因为新 Pod 的IP地址与之前旧 Pod 的不同。而Service一旦被创建,Kubernetes就会自动为它分配一个可用的Cluster IP,而且在Service的整个生命周期内其Cluster IP不会发生改变。只要用 Service 的 Name 与 Service 的 Cluster IP 地址做一个DNS域名映射即可解决集群内部的服务发现需求。
对于 Kubernetes 的 Service,Cluster-Ip 和 NodePort 均是四层的负载。
K8S中IP概念
Node IP
Kubernetes 集群中每个节点的物理网卡的IP地址,是一个真实存在的物理网络。
Pod IP
Pod的IP地址,通常是一个虚拟的二层网络。位于不同Node上的不同Pod的通信是通过Pod IP所在的虚拟二层网络进行通信的,而真实的TCP/IP流量走的是Node IP所在的物理网卡。
Cluster IP
Cluster IP也是一种虚拟的IP,但更像“伪造”的IP网络,原因如下:
- Cluster IP仅仅作用于Service对象,由Kubernetes管理和分配IP地址
- Cluster IP无法被Ping,因为没有一个“实体网络对象”来响应。
- Cluster IP只能结合Service Port组成一个具体的通信端口,单独的Cluster IP不具备TCP/IP通信能力
容器端口映射
要求:将容器应用端口号映射到物理机
方式一:设置Container级别的hostPort,将容器应用的端口号映射到物理机上
方式二:设置Pod级别的hostNetwork,该Pod中所有容器的端口号都将被直接映射到物理机上
说明:
设置hostNetwork=true时,在容器的ports定义部分若不指定hostPort,则默认hostPort等于containerPort。若指定hostPort,其值必须等于containerPort。
SVC的使用
有组 Pod 服务,对外暴露 8080 端口,有 app=myapp 标签,对应的 svc 对象如下:
apiVersion: v1kind: Servicemetadata:name: myservicespec:selector:app: myappports:- protocol: TCPport: 80targetPort: 8080name: myapp-http
Service 能够支持 TCP 和 UDP 协议,默认是 TCP 协议。
SVC支持的服务类型:
- ClusterIP:通过集群的内部 IP 暴露服务,是默认的服务类型。
- NodePort:通过每个 Node 节点上的 IP 和静态端口(NodePort)暴露服务。
- LoadBalancer:使用云提供商的负载局衡器,可以向外部暴露服务。
- ExternalName:通过返回 CNAME 和它的值,可以将服务映射到 externalName 字段的内容(例如, foo.bar.example.com)。
SVC端口映射
映射随机端口
当 svc 中 type 值为 NodePort,Kubernetes 将从配置范围内(默认:30000-32767)分配端口(不指定端口将随机分配),每个 node 将流量从此端口代理到 svc。
示例 service-demo.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deploy
namespace: default
spec:
replicas: 3 # 期望的 Pod 副本数量,默认值为1
selector: # Label Selector,必须匹配 Pod 模板中的标签
matchLabels:
app: myapp
template: # Pod 模板
metadata:
labels:
app: myapp
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: myservice
spec:
selector:
app: myapp
type: NodePort
ports:
- protocol: TCP
port: 80
targetPort: 80
name: myapp-http
# 应用资源
$ kubectl apply -f service-demo.yaml
# 查看
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 87d
myservice NodePort 10.110.89.149 <none> 80:30710/TCP 3m58s
# 访问
$ curl -I 10.110.89.149
HTTP/1.1 200 OK
...
Accept-Ranges: bytes
$ curl -I localhost:30710 # localhost 可以是集群中任意node节点的ip
HTTP/1.1 200 OK
...
Accept-Ranges: bytes
映射指定端口
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deploy
namespace: default
spec:
replicas: 3
selector:
matchLabels:
app: myapp
template: # Pod 模板
metadata:
labels:
app: myapp
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: myservice
spec:
selector:
app: myapp
type: NodePort
ports:
- protocol: TCP
port: 80
targetPort: 80
nodePort: 30001 # 指定映射到node端口
name: myapp-http
映射多端口
有些服务存在多端口需求,通常一个端口提供业务服务,另外一个端口提供管理服务。
apiVersion: v1
kind: Service
metadata:
name: xxx
namespace: open
spec:
type: NodePort
ports:
- name: tcp
port: 8888
targetPort: 8888
nodePort: 8888
protocol: TCP
- name: http
port: 8080
targetPort: 8080
nodePort: 8080
protocol: TCP
selector:
name: xxx
