service
每个 Pod 都有自己的 IP 地址。当 controller 用新 Pod 替代发生故障的 Pod 时,新 Pod 会分配到新的 IP 地址。这样就产生了一个问题:文章源自靠谱运维-https://www.ixdba.net/archives/1381
如果一组 Pod 对外提供服务(比如 HTTP),它们的 IP 很有可能发生变化,那么客户端如何找到并访问这个服务呢?文章源自靠谱运维-https://www.ixdba.net/archives/1381
Kubernetes 给出的解决方案是 Service。文章源自靠谱运维-https://www.ixdba.net/archives/1381
创建 Service
Kubernetes Service 从逻辑上代表了一组 Pod,具体是哪些 Pod 则是由 label 来挑选。Service 有自己 IP,而且这个 IP 是不变的。客户端只需要访问 Service 的 IP,Kubernetes 则负责建立和维护 Service 与 Pod 的映射关系。无论后端 Pod 如何变化,对客户端不会有任何影响,因为 Service 没有变。文章源自靠谱运维-https://www.ixdba.net/archives/1381
第一步:创建下面的这个 Deployment:文章源自靠谱运维-https://www.ixdba.net/archives/1381
查看支持的apiversion使用命令kubectl api-versions文章源自靠谱运维-https://www.ixdba.net/archives/1381
文章源自靠谱运维-https://www.ixdba.net/archives/1381
第二步:部署并查看pod文章源自靠谱运维-https://www.ixdba.net/archives/1381
我们启动了三个 Pod,运行 httpd 镜像,label 是 run: httpd,Service 将会用这个 label 来挑选 Pod。文章源自靠谱运维-https://www.ixdba.net/archives/1381
[root@ken ~]# kubectl apply -f httpd.yml deployment.apps/httpd created [root@ken ~]# kubectl get pod -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES httpd-8c6c4bd9b-ljvlb 1/1 Running 0 41s 10.244.1.27 host1 <none> <none> httpd-8c6c4bd9b-ngxqv 1/1 Running 0 41s 10.244.1.28 host1 <none> <none> httpd-8c6c4bd9b-wxblj 1/1 Running 0 41s 10.244.2.18 host2 <none> <none>
第三步:集群内部测试连通性文章源自靠谱运维-https://www.ixdba.net/archives/1381
Pod 分配了各自的 IP,这些 IP 只能被 Kubernetes Cluster 中的容器和节点访问。文章源自靠谱运维-https://www.ixdba.net/archives/1381
[root@ken ~]# curl 10.244.1.28 <html><body><h1>It works!</h1></body></html> [root@ken ~]# curl 10.244.1.27 <html><body><h1>It works!</h1></body></html> [root@ken ~]# curl 10.244.2.18 <html><body><h1>It works!</h1></body></html>
第四步:接下来创建 Service,其配置文件如下:文章源自靠谱运维-https://www.ixdba.net/archives/1381
文章源自靠谱运维-https://www.ixdba.net/archives/1381
① v1 是 Service 的 apiVersion。文章源自靠谱运维-https://www.ixdba.net/archives/1381
② 指明当前资源的类型为 Service。文章源自靠谱运维-https://www.ixdba.net/archives/1381
③ Service 的名字为 httpd-svc。文章源自靠谱运维-https://www.ixdba.net/archives/1381
④ selector 指明挑选那些 label 为 run: httpd 的 Pod 作为 Service 的后端。文章源自靠谱运维-https://www.ixdba.net/archives/1381
⑤ 将 Service 的 8080 端口映射到 Pod 的 80 端口,使用 TCP 协议。文章源自靠谱运维-https://www.ixdba.net/archives/1381
第五步: 执行 kubectl apply 创建 Service httpd-svc。文章源自靠谱运维-https://www.ixdba.net/archives/1381
[root@ken ~]# kubectl apply -f service.yml service/httpd-svc created [root@ken ~]# kubectl get service NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE httpd-svc ClusterIP 10.106.64.97 <none> 8080/TCP 17s kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 11h
httpd-svc 分配到一个 CLUSTER-IP 10.106.64.97。可以通过该 IP 访问后端的 httpd Pod。文章源自靠谱运维-https://www.ixdba.net/archives/1381
[root@ken ~]# curl 10.106.64.97:8080 <html><body><h1>It works!</h1></body></html>
根据前面的端口映射,这里要使用 8080 端口。另外,除了我们创建的 httpd-svc,还有一个 Service kubernetes,Cluster 内部通过这个 Service 访问 kubernetes API Server。文章源自靠谱运维-https://www.ixdba.net/archives/1381
通过 kubectl describe 可以查看 httpd-svc 与 Pod 的对应关系。文章源自靠谱运维-https://www.ixdba.net/archives/1381
[root@ken ~]# kubectl describe service httpd-svc Name: httpd-svc Namespace: default Labels: <none> Annotations: kubectl.kubernetes.io/last-applied-configuration: {"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"name":"httpd-svc","namespace":"default"},"spec":{"ports":[{"port":8080,"... Selector: run=httpd Type: ClusterIP IP: 10.106.64.97 Port: <unset> 8080/TCP TargetPort: 80/TCP Endpoints: 10.244.1.27:80,10.244.1.28:80,10.244.2.18:80 Session Affinity: None Events: <none>
Endpoints 罗列了三个 Pod 的 IP 和端口。文章源自靠谱运维-https://www.ixdba.net/archives/1381
service ip底层原理分析
我们知道 Pod 的 IP 是在容器中配置的,那么 Service 的 Cluster IP 又是配置在哪里的呢?CLUSTER-IP 又是如何映射到 Pod IP 的呢?文章源自靠谱运维-https://www.ixdba.net/archives/1381
答案是 iptables文章源自靠谱运维-https://www.ixdba.net/archives/1381
Service Cluster IP 是一个虚拟 IP,是由 Kubernetes 节点上的 iptables 规则管理的。文章源自靠谱运维-https://www.ixdba.net/archives/1381
查看svc文章源自靠谱运维-https://www.ixdba.net/archives/1381
[root@ken1 ~]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 15d
my-svc ClusterIP 10.107.111.108 <none> 80/TCP 16s
可以通过 iptables-save 命令打印出当前节点的 iptables 规则,因为输出较多,这里只截取与my-svc Cluster IP 10.99.229.179 相关的信息:文章源自靠谱运维-https://www.ixdba.net/archives/1381
[root@ken1 ~]# iptables-save | grep 10.107.111.108
-A KUBE-SERVICES ! -s 10.244.0.0/16 -d 10.107.111.108/32 -p tcp -m comment --comment "default/my-svc: cluster IP" -m tcp --dport 80 -j KUBE-MARK-MASQ
-A KUBE-SERVICES -d 10.107.111.108/32 -p tcp -m comment --comment "default/my-svc: cluster IP" -m tcp --dport 80 -j KUBE-SVC-NSPZQM4WNKQARS3D
这两条规则的含义是:文章源自靠谱运维-https://www.ixdba.net/archives/1381
- 如果 Cluster 内的 Pod(源地址来自 10.244.0.0/16)要访问 httpd-svc,则允许。
- 其他源地址访问 my-svc,跳转到规则 KUBE-SVC-NSPZQM4WNKQARS3D。
KUBE-SVC-NSPZQM4WNKQARS3D规则如下:文章源自靠谱运维-https://www.ixdba.net/archives/1381
[root@ken1 ~]# iptables-save | grep KUBE-SVC-NSPZQM4WNKQARS3D
:KUBE-SVC-NSPZQM4WNKQARS3D - [0:0]
-A KUBE-SERVICES -d 10.107.111.108/32 -p tcp -m comment --comment "default/my-svc: cluster IP" -m tcp --dport 80 -j KUBE-SVC-NSPZQM4WNKQARS3D
-A KUBE-SVC-NSPZQM4WNKQARS3D -m statistic --mode random --probability 0.33332999982 -j KUBE-SEP-PHYMVJLGA3SBF53W
-A KUBE-SVC-NSPZQM4WNKQARS3D -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-6WM3EVRMZRPSARU6
-A KUBE-SVC-NSPZQM4WNKQARS3D -j KUBE-SEP-MF65K4BIWBNT73JP
- 1/3 的概率跳转到规则 KUBE-SEP-PHYMVJLGA3SBF53W
- 1/3 的概率(剩下 2/3 的一半)跳转到规则 KUBE-SEP-6WM3EVRMZRPSARU6
- 1/3 的概率跳转到规则 KUBE-SEP-MF65K4BIWBNT73JP
上面三个跳转的规则如下:文章源自靠谱运维-https://www.ixdba.net/archives/1381
[root@ken1 ~]# iptables-save | grep KUBE-SEP-PHYMVJLGA3SBF53W
-A KUBE-SEP-PHYMVJLGA3SBF53W -s 10.244.1.50/32 -j KUBE-MARK-MASQ
-A KUBE-SEP-PHYMVJLGA3SBF53W -p tcp -m tcp -j DNAT --to-destination 10.244.1.50:80
[root@ken1 ~]# iptables-save | grep KUBE-SEP-6WM3EVRMZRPSARU6
-A KUBE-SEP-6WM3EVRMZRPSARU6 -s 10.244.2.129/32 -j KUBE-MARK-MASQ
-A KUBE-SEP-6WM3EVRMZRPSARU6 -p tcp -m tcp -j DNAT --to-destination 10.244.2.129:80
[root@ken1 ~]# iptables-save | grep KUBE-SEP-MF65K4BIWBNT73JP
-A KUBE-SEP-MF65K4BIWBNT73JP -s 10.244.2.130/32 -j KUBE-MARK-MASQ
-A KUBE-SEP-MF65K4BIWBNT73JP -p tcp -m tcp -j DNAT --to-destination 10.244.2.130:80
即将请求分别转发到后端的三个 Pod。通过上面的分析,我们得到如下结论:文章源自靠谱运维-https://www.ixdba.net/archives/1381
iptables 将访问 Service 的流量转发到后端 Pod,而且使用类似轮询的负载均衡策略。文章源自靠谱运维-https://www.ixdba.net/archives/1381
另外需要补充一点:Cluster 的每一个节点都配置了相同的 iptables 规则,这样就确保了整个 Cluster 都能够通过 Service 的 Cluster IP 访问 Service。文章源自靠谱运维-https://www.ixdba.net/archives/1381
DNS访问service
在 Cluster 中,除了可以通过 Cluster IP 访问 Service,Kubernetes 还提供了更为方便的 DNS 访问。文章源自靠谱运维-https://www.ixdba.net/archives/1381
第一步:查看coredns文章源自靠谱运维-https://www.ixdba.net/archives/1381
kubeadm 部署时会默认安装 coredns 组件。文章源自靠谱运维-https://www.ixdba.net/archives/1381
[root@ken ~]# kubectl get deployment --namespace=kube-system NAME READY UP-TO-DATE AVAILABLE AGE coredns 2/2 2 2 11h
coredns 是一个 DNS 服务器。每当有新的 Service 被创建,coredns 会添加该 Service 的 DNS 记录。Cluster 中的 Pod 可以通过 <SERVICE_NAME>.<NAMESPACE_NAME> 访问 Service。文章源自靠谱运维-https://www.ixdba.net/archives/1381
第二步:dns访问文章源自靠谱运维-https://www.ixdba.net/archives/1381
比如可以用 httpd-svc.default 访问 Service httpd-svc。文章源自靠谱运维-https://www.ixdba.net/archives/1381
[root@ken ~]# kubectl run busybox --rm -it --image=busybox /bin/sh / # wget httpd-svc:8080 Connecting to httpd-svc:8080 (10.106.64.97:8080) index.html 100% |*************| 45 0:00:00 ETA / # ls bin home root usr dev index.html sys var etc proc tmp / # cat index.html <html><body><h1>It works!</h1></body></html>
由于这个 Pod 与 httpd-svc 同属于 default namespace,可以省略 default 直接用 httpd-svc 访问 Service。文章源自靠谱运维-https://www.ixdba.net/archives/1381
第三步:查看namespace文章源自靠谱运维-https://www.ixdba.net/archives/1381
如果要访问其他 namespace 中的 Service,就必须带上 namesapce 了。kubectl get namespace 查看已有的 namespace。文章源自靠谱运维-https://www.ixdba.net/archives/1381
[root@ken ~]# kubectl get namespace NAME STATUS AGE default Active 11h kube-public Active 11h kube-system Active 11h
第四步:在 kube-public 中部署 Service httpd2-svc文章源自靠谱运维-https://www.ixdba.net/archives/1381
配置如下:文章源自靠谱运维-https://www.ixdba.net/archives/1381
文章源自靠谱运维-https://www.ixdba.net/archives/1381
第五步:创建资源文章源自靠谱运维-https://www.ixdba.net/archives/1381
通过 namespace: kube-public 指定资源所属的 namespace。多个资源可以在一个 YAML 文件中定义,用 — 分割。执行 kubectl apply 创建资源:文章源自靠谱运维-https://www.ixdba.net/archives/1381
[root@ken ~]# kubectl apply -f service.yml deployment.apps/httpd2 created service/httpd2-svc created
第六步:查看 kube-public 的 Service:文章源自靠谱运维-https://www.ixdba.net/archives/1381
[root@ken ~]# kubectl get service --namespace=kube-public NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE httpd2-svc ClusterIP 10.111.175.138 <none> 8080/TCP 63s
第七步:在 busybox Pod 中访问 httpd2-svc:文章源自靠谱运维-https://www.ixdba.net/archives/1381
[root@ken ~]# kubectl run busybox --rm -it --image=busybox /bin/sh / # wget httpd2-svc:8080 wget: bad address 'httpd2-svc:8080' / # wget httpd2-svc.kube-public:8080 Connecting to httpd2-svc.kube-public:8080 (10.111.175.138:8080) index.html 100% |*************| 45 0:00:00 ETA
因为属于不同的 namespace,必须使用 httpd2-svc.kube-public 才能访问到。文章源自靠谱运维-https://www.ixdba.net/archives/1381
外网访问service
除了 Cluster 内部可以访问 Service,很多情况我们也希望应用的 Service 能够暴露给 Cluster 外部。Kubernetes 提供了多种类型的 Service,默认是 ClusterIP。文章源自靠谱运维-https://www.ixdba.net/archives/1381
ClusterIP
Service 通过 Cluster 内部的 IP 对外提供服务,只有 Cluster 内的节点和 Pod 可访问,这是默认的 Service 类型,前面实验中的 Service 都是 ClusterIP。文章源自靠谱运维-https://www.ixdba.net/archives/1381
NodePort
Service 通过 Cluster 节点的静态端口对外提供服务。Cluster 外部可以通过 <NodeIP>:<NodePort> 访问 Service。文章源自靠谱运维-https://www.ixdba.net/archives/1381
LoadBalancer
Service 利用 cloud provider 特有的 load balancer 对外提供服务,cloud provider 负责将 load balancer 的流量导向 Service。目前支持的 cloud provider 有 GCP、AWS、Azur 等。文章源自靠谱运维-https://www.ixdba.net/archives/1381
第一步:实践 NodePort,Service httpd-svc 的配置文件修改如下:文章源自靠谱运维-https://www.ixdba.net/archives/1381
文章源自靠谱运维-https://www.ixdba.net/archives/1381
添加 type: NodePort,重新创建 httpd-svc。文章源自靠谱运维-https://www.ixdba.net/archives/1381
第二步:创建service文章源自靠谱运维-https://www.ixdba.net/archives/1381
[root@ken ~]# kubectl apply -f service.yml service/httpd-svc created [root@ken ~]# kubectl get service httpd-svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE httpd-svc NodePort 10.108.118.202 <none> 8080:31785/TCP 12s
Kubernetes 依然会为 httpd-svc 分配一个 ClusterIP,不同的是:文章源自靠谱运维-https://www.ixdba.net/archives/1381
EXTERNAL-IP 为 nodes,表示可通过 Cluster 每个节点自身的 IP 访问 Service。文章源自靠谱运维-https://www.ixdba.net/archives/1381
PORT(S) 为 8080:31785。8080 是 ClusterIP 监听的端口(每个节点都有该端口),31785 则是节点上监听的端口。Kubernetes 会从 30000-32767 中分配一个可用的端口,每个节点都会监听此端口并将请求转发给 Service。文章源自靠谱运维-https://www.ixdba.net/archives/1381
[root@ken ~]# ss -tnl | grep 31785 LISTEN 0 128 :::31785 :::*
第三步:测试nodeport是否正常工作文章源自靠谱运维-https://www.ixdba.net/archives/1381
[root@ken ~]# curl 172.20.10.2:31785 <html><body><h1>It works!</h1></body></html> [root@ken ~]# curl 172.20.10.7:31785 <html><body><h1>It works!</h1></body></html> [root@ken ~]# curl 172.20.10.9:31785 <html><body><h1>It works!</h1></body></html>
通过三个节点 IP + 32312 端口都能够访问 httpd-svc。文章源自靠谱运维-https://www.ixdba.net/archives/1381
第四步:指定特定端口文章源自靠谱运维-https://www.ixdba.net/archives/1381
NodePort 默认是的随机选择,不过我们可以用 nodePort
指定某个特定端口。文章源自靠谱运维-https://www.ixdba.net/archives/1381
文章源自靠谱运维-https://www.ixdba.net/archives/1381
现在配置文件中就有三个 Port 了:文章源自靠谱运维-https://www.ixdba.net/archives/1381
nodePort 是节点上监听的端口。文章源自靠谱运维-https://www.ixdba.net/archives/1381
port 是 ClusterIP 上监听的端口。文章源自靠谱运维-https://www.ixdba.net/archives/1381
targetPort 是 Pod 监听的端口。文章源自靠谱运维-https://www.ixdba.net/archives/1381
最终,Node 和 ClusterIP 在各自端口上接收到的请求都会通过 iptables 转发到 Pod 的 targetPort。文章源自靠谱运维-https://www.ixdba.net/archives/1381
第四步:应用新的 nodePort 并验证:文章源自靠谱运维-https://www.ixdba.net/archives/1381
[root@ken ~]# kubectl apply -f service.yml service/httpd-svc configured [root@ken ~]# kubectl get service httpd-svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE httpd-svc NodePort 10.108.118.202 <none> 8080:30000/TCP 6m8s [root@ken ~]# curl 172.20.10.2:30000 <html><body><h1>It works!</h1></body></html> [root@ken ~]# curl 172.20.10.7:30000 <html><body><h1>It works!</h1></body></html> [root@ken ~]# curl 172.20.10.9:30000 <html><body><h1>It works!</h1></body></html>
nodePort: 30000 已经生效了。文章源自靠谱运维-https://www.ixdba.net/archives/1381
接下来我们深入探讨一个问题:Kubernetes 是如何将 <NodeIP>:<NodePort> 映射到 Pod 的呢?文章源自靠谱运维-https://www.ixdba.net/archives/1381
与 ClusterIP 一样,也是借助了 iptables。与 ClusterIP 相比,每个节点的 iptables 中都增加了下面两条规则:文章源自靠谱运维-https://www.ixdba.net/archives/1381
第一步:查看svc端口文章源自靠谱运维-https://www.ixdba.net/archives/1381
[root@ken1 ~]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 15d
my-svc NodePort 10.107.111.108 <none> 80:32287/TCP 14m
第二步:查看iptables规则文章源自靠谱运维-https://www.ixdba.net/archives/1381
[root@ken1 ~]# iptables-save | grep 32287
-A KUBE-NODEPORTS -p tcp -m comment --comment "default/my-svc:" -m tcp --dport 32287 -j KUBE-MARK-MASQ
-A KUBE-NODEPORTS -p tcp -m comment --comment "default/my-svc:" -m tcp --dport 32287 -j KUBE-SVC-NSPZQM4WNKQARS3D
规则的含义是:访问当前节点 32312
端口的请求会应用规则 KUBE-SVC-NSPZQM4WNKQARS3D文章源自靠谱运维-https://www.ixdba.net/archives/1381
第三步:相应规则KUBE-SVC-NSPZQM4WNKQARS3D文章源自靠谱运维-https://www.ixdba.net/archives/1381
[root@ken1 ~]# iptables-save | grep KUBE-SVC-NSPZQM4WNKQARS3D
-A KUBE-SERVICES -d 10.107.111.108/32 -p tcp -m comment --comment "default/my-svc: cluster IP" -m tcp --dport 80 -j KUBE-SVC-NSPZQM4WNKQARS3D
-A KUBE-SVC-NSPZQM4WNKQARS3D -m statistic --mode random --probability 0.33332999982 -j KUBE-SEP-PHYMVJLGA3SBF53W
-A KUBE-SVC-NSPZQM4WNKQARS3D -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-6WM3EVRMZRPSARU6
-A KUBE-SVC-NSPZQM4WNKQARS3D -j KUBE-SEP-MF65K4BIWBNT73JP
其作用就是负载均衡到每一个 Pod。文章源自靠谱运维-https://www.ixdba.net/archives/1381
评论