每创建一个pod,kubernetes都会为pod创建一个虚拟的ip地址,基于这样一个虚拟的IP地址,kebernetes下的每一个pod就可以相互访问了,而不用管该pod是否在同一个node上。如下图所示,node-worker1下的pod-1可以访问node-worker2下的pod-web提供的nginx服务。
# 一个master节点和一个work节点
apiVersion: v1
kind: Pod
metadata:
name: svc-pod1
spec:
containers:
- name: nginx
image: nginx
# 由于只有一个work节点,所以该pod将会被调度到work节点上
# 此时可以在master节点查看该pod的ip地址,在master节点上使用curl命令查看是否能否访问
# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP
svc-pod1 1/1 Running 0 32m 10.44.0.30
# curl http://10.44.0.30
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...
# 说明从master节点上可以访问来自于work节点上的pod所提供的服务
由kubernetes为pod创建的虚拟IP只能够在kubernetes集群范围内有效,集群外的客户端无法通过pod的ip地址加容器端口访问到其所提供的服务,因此kubernetes为Pod提供了一个端口映射的字段-hostPort。其作用是能够通过主机ip加端口的方式访问pod中容器提供的服务。基本原理是将hostport端口映射到pod中某一容器的端口上,从而实现访问。客户根据node的ip + hostport端口请求服务,node接到请求后,根据iptables,会将请求转发到pod_web上的nginx端口上去从而实现访问
apiVersion: v1
kind: Pod
metadata:
name: svc-pod1
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
hostPort: 10000
# 在有多个节点的情况下,首先需要查询该pod被调度在哪个node上,然后基于该node的ip地址加端口进行访问
# kubectl describe pod svc-pod1 # 查询node的ip地址
# curl http://node_ip:10000
# 需要注意的是,hostPort端口在node节点上必须是唯一的。若出现相同的hostPort则会应该port冲突导致pod创建失败。比如,在上一个pod的基础上,在同一个节点添加这样一个pod,则会创建失败
apiVersion: v1
kind: Pod
metadata:
name: svc-pod2
spec:
containers:
- name: tomcat
image: tomcat
ports:
- containerPort: 8080
hostPort: 10000
# 报错如下
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning FailedScheduling 35s default-scheduler 0/2 nodes are available: 1 node(s) didn't have free ports for the requested pod ports, 1 node(s) had taints that the pod didn't tolerate.
由于kubernete所提供强大的编排能力,根据上图所示,如果pod_web从worker2调度到worker1上,此时访问pod_web所提供nginx服务,就需要重新查询woker1的ip地址。也就是说通过hostport暴露pod服务是和node的ip地址所绑定的。
那么,有没有一种方式,不管pod被调度在哪里,也就是上图中的pod_web,我不管你在node_worker1上还是在node_worker2上,我都有一个固定的访问地址,能够访问pod所提供的服务呢?
service对象就是专门来做这样事情的,service是为多个功能相同的pod提供一个固定的访问入口,不管背后的pod是如何变化,是增加一个还是减少一个,还是从一个node调度到另外一个node。使用这个固定的访问入口就可以访问到背后的pod。。如下图所示,首先service对象(web-service)通过选择器选择相应的pod(web1和web2),然后通过配置映射相应的端口,最后kubernetes会为service生成一个cluster ip地址。就可以通过cluster ip加端口访问其选择的pod了。
# 分别在两个节点上创建一个pod
# cat pod1.yaml
apiVersion: v1
kind: Pod
metadata:
name: svc-pod1
labels:
app: web
spec:
nodeName: compute1
containers:
- name: nginx
image: nginx
- name: tomcat
image: tomcat
# cat pod2.yaml
apiVersion: v1
kind: Pod
metadata:
name: svc-pod2
labels:
app: web
spec:
nodeName: worker1
containers:
- name: nginx
image: nginx
- name: tomcat
image: tomcat
# 创建一个service对象
# cat svc1.yaml
apiVersion: v1
kind: Service
metadata:
name: svc-web1
spec:
selector:
app: web
ports:
- port: 80
targetPort: 80
name: nginx
- port: 8080
targetPort: 8080
name: tomcat
# 查看service的cluster ip
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S)
svc-web1 ClusterIP 10.102.136.177 <none> 80/TCP,8080/TCP
# 通过同一个cluster ip,即可以访问worker1上的pod,也可以访问worker2上的p,访问规则可以根据cpu等资源使用来确定,默认是轮询
# curl http://10.102.136.177:8080 # 在任何一个节点(master或worker)上都能够得到正常的响应
# 设置type为NodePort使得外部可以进行访问
# cat svc-web2
apiVersion: v1
kind: Service
metadata:
name: svc-web2
spec:
type: NodePort
selector:
app: web
ports:
- port: 80
targetPort: 80
name: nginx
# nodePort: 30000 nodeport不手动指定,kubernetes会自动指定
- port: 8080
targetPort: 8080
name: tomcat
# kubectl get service -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S)
svc-web2 NodePort 10.100.104.20 <none> 80:31245/TCP,8080:31723/TCP
# curl http://node_ip:31245 # 集群中的任何一个节点的ip地址都可以
外网访问流程(以上为例子)
- 创建一个service对象后,每个node上的kube-proxy会对iptables进行编写转发规则
- 发起一个请求 curl http://node_ip:31723
- iptables中的规则会将该流转发对某一个worker_ip
- iptables中的规则会将2中的流转发到servie_ip:
- iptables中的规则会将3中的流转发到pod_ip上去
- 最后请求到达了pod中的容器中去了
小结: 不管iptables怎么进行转换,最终的目的是将请求发送到某个pod里的某容器中去