如何从集群外访问Kubernetes Pod
source link: https://ieevee.com/tech/2020/01/06/access-pods.html?
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
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服务,可以根据实际需求、环境来选择。
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK