如何从集群外访问Kubernetes Pod
by 伊布
kubernetes集群上运行的pod,在集群内访问是很容易的,最简单的,可以通过pod的ip来访问,也可以通过对应的svc来访问。但在集群外,由于基于flannel的kubernetes集群的pod ip是内部地址,因此从集群外是访问不到的。
为了解决这个问题,kubernetes提供了如下几个方法。
hostNetwork: true
hostNetwork为true时,容器将使用宿主机node的网络,因此,只要知道容器在哪个node上运行,从集群外以 node-ip + port 的方式就可以访问容器的服务。
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
hostNetwork: true
containers:
- name: nginx
image: nginx
pod启动后,如下,可以看到pod的ip地址与node optiplex-2的地址是一致的,以node optiplex-2的ip地址来请求pod 80端口的服务,访问的是pod nginx的http服务。
$ kubectl get pods -o wide nginx
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx 1/1 Running 0 13m 192.168.0.161 optiplex-2 <none> <none>
$
$ kubectl get nodes -o wide optiplex-2
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
optiplex-2 Ready <none> 160d v1.16.4 192.168.0.161 <none> Ubuntu 18.04.3 LTS 4.15.0-63-generic docker://18.9.7
$
$ curl http://192.168.0.161
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...
hostNetwork的优点是直接使用宿主机的网络,只要宿主机能访问,Pod就可以访问;但缺点也是很明显的:
- 易用性:Pod漂移到其他node上,访问时需要更换ip地址。workaroud的做法是将Pod绑定在某几个node上,并在这几个node上运行keepalived以漂移vip,从而客户端可以使用vip+port的方式来访问。
- 易用性:Pod间可能出现端口冲突,造成Pod无法调度成功。
- 安全性:Pod可以直接观察到宿主机的网络。
hostPort
hostPort的效果与hostNetwork类似,都是可以通过Pod所在node的ip地址+ Pod Port来访问Pod的服务。
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
hostPort: 80
pod启动后,如下,可以看到,pod的ip地址是flannel的内部ip,与宿主机node的ip不同;与hostNetwork一样,也可以通过node ip + pod port访问。
$ kubectl get pods -o wide nginx
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx 1/1 Running 0 3m50s 10.244.0.156 ubuntu-1 <none> <none>
$
$ kubectl get nodes ubuntu-1 -o wide
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
ubuntu-1 Ready master 435d v1.16.4 192.168.0.154 <none> Ubuntu 18.04.2 LTS 4.15.0-49-generic docker://18.9.2
$
$ curl http://192.168.0.154
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...
hostPort与hostNetwork的原理不同,如下,hostPort实际是一系列的iptables规则所做的fullnat。
-A CNI-DN-1652b489c7cf2eeec2243 -s 10.244.0.156/32 -p tcp -m tcp --dport 80 -j CNI-HOSTPORT-SETMARK
-A CNI-DN-1652b489c7cf2eeec2243 -s 127.0.0.1/32 -p tcp -m tcp --dport 80 -j CNI-HOSTPORT-SETMARK
-A CNI-DN-1652b489c7cf2eeec2243 -p tcp -m tcp --dport 80 -j DNAT --to-destination 10.244.0.156:80
-A CNI-HOSTPORT-DNAT -p tcp -m comment --comment "dnat name: \"cbr0\" id: \"3881dd51f0971e5ccdd03f89a48e2386c5e7d8987014a12a31ad40b1df685158\"" -m multiport --dports 80 -j CNI-DN-1652b489c7cf2eeec2243
-A CNI-HOSTPORT-MASQ -m mark --mark 0x2000/0x2000 -j MASQUERADE
-A CNI-HOSTPORT-SETMARK -m comment --comment "CNI portfwd masquerade mark" -j MARK --set-xmark 0x2000/0x2000
hostPort的优缺点与hostNetwork类似,因为它们都是使用了宿主机的网络资源。hostPort相对hostNetwork的一个优点是,hostPort不需要提供宿主机的网络信息,但其性能不如hostNetwork,因为需要经过iptables的转发才能到达Pod。
NodePort
与hostPort、hostNetwork只是Pod的配置不同,NodePort是一种service,其使用的是宿主机node上的端口号,从集群外以 任意node的ip + nodePort 来访问Pod的服务。
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
name: nginx
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
---
kind: Service
apiVersion: v1
metadata:
name: nginx
spec:
type: NodePort
ports:
- name: nginx
port: 80
nodePort: 30018
selector:
name: nginx
svc配置中的nodePort,即为访问服务时,宿主机的端口号。可以在配置文件中指定(当然不能与其他nodePort类型的svc冲突),也可以不配置,由k8s来分配。
创建上述Pod和service后,如下,查看pod和svc的相关信息,我们可以通过宿主机的ip地址+noePort来访问pod的服务。
$ kubectl get pods -o wide nginx
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx 1/1 Running 0 2m35s 10.244.4.133 optiplex-2 <none> <none>
$
$ kubectl get svc -o wide nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
nginx NodePort 10.103.106.64 <none> 80:30018/TCP 190d name=nginx,run=nginx
$
$ kubectl get nodes optiplex-1 -o wide
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
optiplex-1 Ready <none> 169d v1.16.4 192.168.0.240 <none> Ubuntu 18.04.3 LTS 4.15.0-73-generic docker://18.9.7
$
$ curl http://192.168.0.240:30018
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
nodePort类型的svc是kube-proxy通过iptables来实现的,其iptables规则在所有node上均有相同的一份,相关原理请参考谈谈kubernets的service组件的Virtual IP。
LoadBalancer
LoadBalance也是一种service。LoadBalancer通常需要外部设备的支持,例如在AWS、Azure等公有云上的负载均衡设备。
在我的环境上,LoadBalancer是通过metalLB来实现的,具体请参考MetalLB:穷人的LoadBalancer。
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
name: nginx
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
---
kind: Service
apiVersion: v1
metadata:
name: nginx
spec:
type: LoadBalancer
ports:
- name: nginx
port: 80
selector:
name: nginx
与nodePort唯一不同的就是service的type。创建后,如下,查看pod及svc的状态,可以看到service nginx比nodePort类型时,多了EXTERNAL-IP的信息,其中 192.168.9.0 地址即为metalLB为LoadBalancer 类型svc所分配的外部IP地址;用户可以通过该地址从集群外访问nginx服务。
$ kubectl get pods nginx -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx 1/1 Running 0 3m18s 10.244.0.158 ubuntu-1 <none> <none>
$
$ kubectl get svc nginx -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
nginx LoadBalancer 10.102.66.83 192.168.9.0 80:31009/TCP 15s name=nginx
$
$ curl http://192.168.9.0
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
Ingress
nodePort和LoadBalancer类型的service聚焦于四层,而Ingrss则聚焦于七层。
在kubernetes的设计里,Ingress仅仅是一个概念,kubernetes并没有直接提供其实现;集群管理员需要部署ingress controller;Ingress controller可以基于nginx、treafik等等来实现,详细情况请参考kubernetes笔记: ingress。
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: nginx
spec:
rules:
- host: nginx.example.com
http:
paths:
- backend:
serviceName: nginx
servicePort: 80
如上,Ingrss nginx 绑定了service nginx,其域名为 nginx.example.com。
以基于nginx的ingress controller为例,创建上述ingress后,Ingress controller会监听service nginx,获取其endpoints,生成并更新ingress controller的配置。
当集群外请求到达ingress controller时,其会将http请求的host为nginx.example.com
的流量,转发给endpoints,从而完成七层负载均衡,集群外的客户端也就可以请求Pod的服务了。
$ curl -v http://nginx.example.com/ping
当然了,为了能够接受集群外的流量,ingress controller本身还需要使用hostPort或者hostNetwork的方式来部署。
Pod IP全局可达
当kubernetes的网络方案选择calico或者contiv时,还可以配置Pod IP全局可达,从而直接在集群外访问。
原理是宿主机与上联交换机建立BGP邻居;当Pod running时,宿主机BGP会向上联交换机发布路由,上联交换机再完成与汇聚交换机的BGP交换,从而将流量导入到Pod。
总结
以上各种方式均可以实现集群外访问Pod服务,可以根据实际需求、环境来选择。
Ref:
Subscribe via RSS