36

如何从集群外访问Kubernetes Pod

 4 years ago
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服务,可以根据实际需求、环境来选择。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK