【问题标题】:how to connect a kubernetes pod to the outside world without a forwarding rule (google container engine)如何在没有转发规则的情况下将 Kubernetes pod 连接到外部世界(谷歌容器引擎)
【发布时间】:2015-08-07 14:43:27
【问题描述】:

我正在使用 Google 的 Container Engine 服务,并且有一个运行在 3000 端口上的服务器的 pod。我将服务设置为将端口 80 连接到该 pod 的 3000 端口。我可以使用其本地和 curl 服务来自节点内部的公共 IP,但不是来自外部。我设置了防火墙规则以允许端口 80 并将其发送到节点,但我不断收到来自网络外部的“连接被拒绝”。我正在尝试在没有转发规则的情况下执行此操作,因为只有一个 pod,并且看起来转发规则需要花钱并进行负载平衡。我认为防火墙规则有效,因为当我将createExternalLoadBalancer: true 添加到服务规范时,转发规则创建的外部 IP 会按预期工作。我需要做其他事情吗?设置路线之类的?

controller.yaml

kind: ReplicationController
apiVersion: v1beta3
metadata:
    name: app-frontend
    labels:
        name: app-frontend
        app: app
        role: frontend
spec:
    replicas: 1
    selector:
        name: app-frontend
    template:
        metadata:
            labels:
                name: app-frontend
                app: app
                role: frontend
        spec:
            containers:
                - name: node-frontend
                  image: gcr.io/project_id/app-frontend
                  ports:
                    - name: app-frontend-port
                      containerPort: 3000
                      targetPort: 3000
                      protocol: TCP

service.yaml

kind: Service
apiVersion: v1beta3
metadata:
  name: app-frontend-service
  labels:
    name: app-frontend-service
    app: app
    role: frontend
spec:
  ports:
    - port: 80
      targetPort: app-frontend-port
      protocol: TCP
  publicIPs:
   - 123.45.67.89
  selector:
    name: app-frontend

编辑(附加细节): 创建此服务会添加这些附加规则,这些规则是在我运行 iptables -L -t nat 时发现的

Chain KUBE-PORTALS-CONTAINER (1 references)
target     prot opt source               destination         
REDIRECT   tcp  --  anywhere             10.247.247.206       /* default/app-frontend-service: */ tcp dpt:http redir ports 56859
REDIRECT   tcp  --  anywhere             89.67.45.123.bc.googleusercontent.com  /* default/app-frontend-service: */ tcp dpt:http redir ports 56859
Chain KUBE-PORTALS-HOST (1 references)
target     prot opt source               destination         
DNAT       tcp  --  anywhere             10.247.247.206       /* default/app-frontend-service: */ tcp dpt:http to:10.241.69.28:56859
DNAT       tcp  --  anywhere             89.67.45.123.bc.googleusercontent.com  /* default/app-frontend-service: */ tcp dpt:http to:10.241.69.28:56859

我不完全了解 iptables,所以我不确定目标端口如何匹配我的服务。我发现89.67.45.123.bc.googleusercontent.com 的 DNS 解析为 123.45.67.89

kubectl get services 显示我指定的 IP 地址和端口:

NAME                             IP(S)               PORT(S)
app-frontend-service             10.247.243.151      80/TCP
                                 123.45.67.89

/var/log/kube-proxy.log 中没有显示来自外部 IP 的最新内容

【问题讨论】:

    标签: firewall kubernetes google-kubernetes-engine


    【解决方案1】:

    如果您将节点的外部 IP 地址添加到服务的 publicIPs 字段,那么您应该能够通过节点的 IP 地址访问它。如果您的集群有多个节点,您可以在字段中输入多个 IP 地址,如果您想启用对其中任何一个上的 pod 的访问。

    在即将发布的版本中,将有一个更简单的内置选项,用于在没有负载平衡器的情况下设置外部服务。如果您对此感到好奇或将来某个时候阅读此内容,请查看 this doc 更新的“外部服务”部分,了解如何使用 NodePort 更轻松地完成相同的事情。

    【讨论】:

    • 我想我已经按照您提到的步骤进行了操作。在我的 service.yaml 中可以看到,该服务有一个publicIPs 字段,该字段设置为节点的外部 ip。
    • 端口 80 的防火墙规则并将节点的外部 IP 添加到服务的 publicIPs 字段应该是使其工作所需的全部内容。一旦您仔细检查了服务中的 IP 是节点的 IP 并且它与您正在测试的 IP 相同,接下来要查看的地方是在节点上运行 sudo iptables -L -t nat 以查看是否有iptables 规则引用 IP 地址。假设存在,请检查 /var/log/kube-proxy.log 以了解有关为什么没有路由到该服务的请求的任何详细信息。
    • 确认该节点列出的外部 IP 与服务规范中的匹配。我没有在 iptables 中看到列出的 IP,但我确实看到它作为目标域的前缀。我用一些额外的细节更新了我的问题
    • 为了测试你说的NodePort特性,我可以升级集群使用最新版本吗?还是我需要启动一个新集群?
    【解决方案2】:

    TL;DR:使用节点的内部 IP 作为服务定义中的公共 IP。


    如果您在 kube-proxy 上启用详细日志记录,您将看到它似乎正在创建适当的 IP 表规则:

    I0602 04:07:32.046823   24360 roundrobin.go:98] LoadBalancerRR service "default/app-frontend-service:" did not exist, created
    I0602 04:07:32.047153   24360 iptables.go:186] running iptables -A [KUBE-PORTALS-HOST -t nat -m comment --comment default/app-frontend-service: -p tcp -m tcp -d 10.119.244.130/32 --dport 80 -j DNAT --to-destination 10.240.121.42:36970]
    I0602 04:07:32.048446   24360 proxier.go:606] Opened iptables from-host portal for service "default/app-frontend-service:" on TCP 10.119.244.130:80
    I0602 04:07:32.049525   24360 iptables.go:186] running iptables -C [KUBE-PORTALS-CONTAINER -t nat -m comment --comment default/app-frontend-service: -p tcp -m tcp -d 23.251.156.36/32 --dport 80 -j REDIRECT --to-ports 36970]
    I0602 04:07:32.050872   24360 iptables.go:186] running iptables -A [KUBE-PORTALS-CONTAINER -t nat -m comment --comment default/app-frontend-service: -p tcp -m tcp -d 23.251.156.36/32 --dport 80 -j REDIRECT --to-ports 36970]
    I0602 04:07:32.052247   24360 proxier.go:595] Opened iptables from-containers portal for service "default/app-frontend-service:" on TCP 23.251.156.36:80
    I0602 04:07:32.053222   24360 iptables.go:186] running iptables -C [KUBE-PORTALS-HOST -t nat -m comment --comment default/app-frontend-service: -p tcp -m tcp -d 23.251.156.36/32 --dport 80 -j DNAT --to-destination 10.240.121.42:36970]
    I0602 04:07:32.054491   24360 iptables.go:186] running iptables -A [KUBE-PORTALS-HOST -t nat -m comment --comment default/app-frontend-service: -p tcp -m tcp -d 23.251.156.36/32 --dport 80 -j DNAT --to-destination 10.240.121.42:36970]
    I0602 04:07:32.055848   24360 proxier.go:606] Opened iptables from-host portal for service "default/app-frontend-service:" on TCP 23.251.156.36:80
    

    使用-L -t 列出 iptables 条目显示公共 IP 已转换为反向 DNS 名称,如您所见:

    Chain KUBE-PORTALS-CONTAINER (1 references)
    target     prot opt source               destination         
    REDIRECT   tcp  --  anywhere             10.119.240.2         /* default/kubernetes: */ tcp dpt:https redir ports 50353
    REDIRECT   tcp  --  anywhere             10.119.240.1         /* default/kubernetes-ro: */ tcp dpt:http redir ports 54605
    REDIRECT   udp  --  anywhere             10.119.240.10        /* default/kube-dns:dns */ udp dpt:domain redir ports 37723
    REDIRECT   tcp  --  anywhere             10.119.240.10        /* default/kube-dns:dns-tcp */ tcp dpt:domain redir ports 50126
    REDIRECT   tcp  --  anywhere             10.119.244.130       /* default/app-frontend-service: */ tcp dpt:http redir ports 36970
    REDIRECT   tcp  --  anywhere             36.156.251.23.bc.googleusercontent.com  /* default/app-frontend-service: */ tcp dpt:http redir ports 36970
    

    但添加 -n 选项会显示 IP 地址(默认情况下,-L 会反向查找 IP 地址,这就是您看到 DNS 名称的原因):

    Chain KUBE-PORTALS-CONTAINER (1 references)
    target     prot opt source               destination         
    REDIRECT   tcp  --  0.0.0.0/0            10.119.240.2         /* default/kubernetes: */ tcp dpt:443 redir ports 50353
    REDIRECT   tcp  --  0.0.0.0/0            10.119.240.1         /* default/kubernetes-ro: */ tcp dpt:80 redir ports 54605
    REDIRECT   udp  --  0.0.0.0/0            10.119.240.10        /* default/kube-dns:dns */ udp dpt:53 redir ports 37723
    REDIRECT   tcp  --  0.0.0.0/0            10.119.240.10        /* default/kube-dns:dns-tcp */ tcp dpt:53 redir ports 50126
    REDIRECT   tcp  --  0.0.0.0/0            10.119.244.130       /* default/app-frontend-service: */ tcp dpt:80 redir ports 36970
    REDIRECT   tcp  --  0.0.0.0/0            23.251.156.36        /* default/app-frontend-service: */ tcp dpt:80 redir ports 36970
    

    此时,您可以使用内部和外部 IP 从集群内部访问服务:

    $ curl 10.119.244.130:80
    app-frontend-5pl5s
    $ curl 23.251.156.36:80
    app-frontend-5pl5s
    

    在不添加防火墙规则的情况下,尝试远程连接到公共 ip 超时。如果您添加防火墙规则,那么您将可靠地拒绝连接:

    $ curl 23.251.156.36
    curl: (7) Failed to connect to 23.251.156.36 port 80: Connection refused
    

    如果你启用了一些 iptables 日志记录:

    sudo iptables -t nat -I KUBE-PORTALS-CONTAINER -m tcp -p tcp --dport 
    80 -j LOG --log-prefix "WTF: "
    

    然后 grep dmesg 的输出 WTF 很明显数据包到达 10. 虚拟机的 IP 地址,而不是已设置为服务公共 IP 的临时外部 IP 地址.

    事实证明,问题在于 GCE 有两种类型的外部 IP:ForwardingRules(在 DSTIP 完整的情况下转发)和 1-to-1 NAT(实际上将 DSTIP 重写为内部 IP)。 VM 的外部 IP 是后一种类型,因此当节点接收到数据包时,IP 表规则不匹配。

    修复实际上非常简单(但不直观):使用节点的内部 IP 作为服务定义中的公共 IP。更新您的 service.yaml 文件以将 publicIPs 设置为内部 IP(例如10.240.121.42)后,您将能够从 GCE 网络外部访问您的应用程序。

    【讨论】:

    • 谢谢,我得到了它在节点上使用从ifconfig 返回的 ip。我不确定如何启用详细日志记录,或者如何获取它。这适用于多节点场景吗?如果所有节点的 IP 都在列表中,它会将其发送到在该端口上侦听容器的任何一个吗?如果两个不同节点上的两个容器监听该端口会怎样?
    • 要启用详细日志记录,请编辑 /etc/default/kube-proxy 并将 --v=2 更改为 --v=4。然后运行sudo service kube-proxy restart。日志文件写入/var/log/kube-proxy.log
    • 这适用于多节点场景,Alex 在下面描述的方式——你可以向你的服务添加多个 publicIPs 条目并向集群中的所有节点发送请求(例如使用 DNS 轮罗宾)。每个节点上的 kube-proxy 拦截请求并将它们重定向到集群中运行的适当 pod。
    • 您将需要为不同的外部暴露服务使用不同的端口或不同的公共 IP(尽管 pod 可以在集群内重用相同的端口号,因为每个 pod 都有一个不同的 IP)。这是使用 GCE 外部负载均衡器的优势——因为每个服务都有不同的 IP,您可以在所有服务上使用相同的端口(例如端口 80 或 443)。
    • @RobertBailey 它似乎不再像这样工作了。如果我在服务定义中将内部 ip 设置为外部,除了集群内部之外,我无法从任何地方访问我的应用程序。运行 Kubernetes 1.1.7 btw 的全新 GCE 集群。可惜负载平衡的成本比 vm 本身还高,它搞砸了我的测试。
    【解决方案3】:

    @Robert Bailey 的回答绝对正确。 publicIPs 在 kubernetes 1.5.1 中已被弃用,您可以使用 externalIPs 代替。

    • 获取节点内部ip,kubectl describe node | grep Address
      Addresses: 10.119.244.130,101.192.150.200,gke-...
    • 或者你可以在node终端里面运行ifconfig eth0来获取内部ip

    • 在 service.yaml 中设置 ip spec: type: NodePort externalIPs: - 10.119.244.130

    • 可以卷曲并决心测试
      curl --resolve 'example.com:443:23.251.156.36' https://example.com -k

    【讨论】:

    • 这仅在您不打算扩展您的 pod 时才有效(它们可能最终在不同的 externalIp 中)。我认为它在重建的情况下也可以工作(吊舱死亡和重新创建,因为我认为它可以在其他地方创建)。
    猜你喜欢
    • 1970-01-01
    • 2010-11-27
    • 1970-01-01
    • 2018-12-26
    • 1970-01-01
    • 2018-09-29
    • 2018-07-28
    • 1970-01-01
    • 2017-05-23
    相关资源
    最近更新 更多