Kubernetes Service ClusterIP
Kubernetes的service有三种类型:ClusterIP,NodePort,LoadBalancer,
今天我们来看看ClusterIP。
创建Deployment
首先我们先创建一个Deployment,这个Deployment是一个Python实现的HTTP服务,请求这个Web Server的时候,会发回给我们这个server的hostname(如果是container,那就是container的hostname)。
这个Deployment有四个Replica。
$ more deployment_python_http.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: service-test
spec:
replicas: 4
selector:
matchLabels:
app: service_test_pod
template:
metadata:
labels:
app: service_test_pod
spec:
containers:
- name: simple-http
image: python:2.7
imagePullPolicy: IfNotPresent
command: ["/bin/bash"]
args: ["-c", "echo \"<p>Hello from $(hostname)</p>\" > index.html; python -m SimpleHTTPServer 8080"]
ports:
- name: http
containerPort: 8080
$ kubectl create -f deployment_python_http.yml
deployment.apps "service-test" created
创建完我们看到pod是这样的;
$ kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE
service-test-54b5b4b547-8l9s4 1/1 Running 0 1m 10.36.0.0 ks8-node2
service-test-54b5b4b547-c2t85 1/1 Running 0 1m 10.36.0.1 ks8-node2
service-test-54b5b4b547-nxn9z 1/1 Running 0 1m 10.40.0.1 k8s-node1
service-test-54b5b4b547-vlpff 1/1 Running 0 1m 10.40.0.0 k8s-node1
这四个pod IP我们都可以在k8s cluster任意一个节点上访问,每一个都会返回自己的container name。
$ curl 10.36.0.0:8080
<p>Hello from service-test-54b5b4b547-8l9s4</p>
创建Service
通过kubectl expose给刚才这个deployment创建一个service,端口绑定为8088.
kubectl expose deployment service-test --port 8088 --target-port=8080
service "service-test" exposed
这样,就给我们生成了一个类型为ClusterIP的service,这个service有一个Cluster IP,其实就一个VIP。
kubectl get service -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 1d <none>
service-test ClusterIP 10.101.90.210 <none> 8088/TCP 11s app=service_test_pod
首先我们可以通过这个Cluster IP加端口8088访问我们的deployment。
$ for i in `seq 4`; do curl 10.101.90.210:8088; done
<p>Hello from service-test-54b5b4b547-nxn9z</p>
<p>Hello from service-test-54b5b4b547-vlpff</p>
<p>Hello from service-test-54b5b4b547-vlpff</p>
<p>Hello from service-test-54b5b4b547-nxn9z</p>
并且我们发现,这个VIP实现了负载均衡
,每次返回的hostname不同。
VIP和负载均衡的实现
为什么我们访问VIP就能访问我们的四个pod,并且还做了负载均衡呢?下面我们就看一下,
其实呢,我们刚才创建这个deployment,service的时候,k8s集群下面的几个部件参与了相关的工作。
apiserver kubectl命令向apiserver发送创建service的命令,apiserver接收到请求以后将数据存储到etcd中。
kube-proxy kubernetes的每个节点中都有一个叫做kube-proxy的进程,这个进程负责感知service,pod的变化,并将变化的信息写入本地的iptables中。
iptables 使用NAT等技术将virtualIP的流量转至endpoint中。
那么IPtable到底是如何转发我们的流量的呢,我们到任意一台k8s节点运行 sudo iptables -L -v -n -t nat
首先找到我们的ClusterIP 10.101.90.210,发现他在一个iptables chain中
Chain KUBE-SERVICES (2 references)
pkts bytes target prot opt in out source destination
0 0 KUBE-MARK-MASQ tcp -- * * !172.100.0.0/16 10.101.90.210 /* default/service-test: cluster IP */ tcp dpt:8088
0 0 KUBE-SVC-LY73ZDGF4KGO4YFJ tcp -- * * 0.0.0.0/0 10.101.90.210 /* default/service-test: cluster IP */ tcp dpt:8088
这个chain类似一个链条,那么访问10.101.90.210:8088的流量到底怎么被转发了呢,我们需要看一下 KUBE-SVC-LY73ZDGF4KGO4YFJ 这个Chain, 找到这个chain
Chain KUBE-SVC-LY73ZDGF4KGO4YFJ (1 references)
pkts bytes target prot opt in out source destination
0 0 KUBE-SEP-YOQWVZZ4NQDNEBVN all -- * * 0.0.0.0/0 0.0.0.0/0 /* default/service-test: */ statistic mode random probability 0.25000000000
0 0 KUBE-SEP-WHOFXZ2VQXEUUKVO all -- * * 0.0.0.0/0 0.0.0.0/0 /* default/service-test: */ statistic mode random probability 0.33332999982
0 0 KUBE-SEP-3TBKTCTGJZ27RFOH all -- * * 0.0.0.0/0 0.0.0.0/0 /* default/service-test: */ statistic mode random probability 0.50000000000
0 0 KUBE-SEP-6LDVTIHDBDOU3D3G all -- * * 0.0.0.0/0 0.0.0.0/0 /* default/service-test: */
这个Chain比较有意思,过来的流量它按照随机的概率,分别以0.25000000000, 0.33332999982,0.50000000000的概率,转发到这三个Chain,这三个Chain其实就是我们的pod,那为啥有一个pod不会被转发呢,这个应该是负载均衡的配置,最大几个负载均衡的问题。
我们随便拿出一个
Chain KUBE-SEP-WHOFXZ2VQXEUUKVO (1 references)
pkts bytes target prot opt in out source destination
0 0 KUBE-MARK-MASQ all -- * * 10.36.0.1 0.0.0.0/0 /* default/service-test: */
0 0 DNAT tcp -- * * 0.0.0.0/0 0.0.0.0/0 /* default/service-test: */ tcp to:10.36.0.1:8080
好的,那经过这么一转发,我们的流量就可以转发到正确的pod上了,不知道大家明白没有。