【问题标题】:Kubernetes - Pass Public IP of Load Balance as Environment Variable into PodKubernetes - 将负载均衡的公共 IP 作为环境变量传递到 Pod
【发布时间】:2020-02-12 21:11:07
【问题描述】:

要点

我有一个ConfigMap,它为我的 pod 提供了必要的环境变量:

apiVersion: v1
kind: ConfigMap
metadata:
  name: global-config
data:
  NODE_ENV: prod
  LEVEL: info

  # I need to set API_URL to the public IP address of the Load Balancer
  API_URL: http://<SOME IP>:3000

  DATABASE_URL: mongodb://database:27017
  SOME_SERVICE_HOST: some-service:3000

我在 Google Cloud 上运行我的 Kubernetes 集群,因此它会自动为我的服务创建一个公共端点:

apiVersion: v1
kind: Service
metadata:
  name: gateway
spec:
  selector:
    app: gateway
  ports:
    - name: http
      port: 3000
      targetPort: 3000
      nodePort: 30000
  type: LoadBalancer

问题

我有一个 Web 应用程序需要从客户端的浏览器向gateway 服务发出 HTTP 请求。但是为了向外部服务发出请求,Web 应用需要知道它的 IP 地址。

所以我设置了 pod,它以某种方式为 Web 应用程序提供服务,它获取一个环境变量“API_URL”,结果向这个 url 发出所有 HTTP 请求。

所以我只需要一种方法将API_URL 环境变量设置为gateway 服务的公共IP 地址,以便在它启动时将其传递给一个pod。

【问题讨论】:

标签: kubernetes google-cloud-platform environment-variables google-kubernetes-engine


【解决方案1】:

我知道这不是您想要的确切方法,但我发现creating a static IP address and explicitly passing it in 往往更容易使用。

首先,创建一个静态IP地址:

gcloud compute addresses create gke-ip --region <region>

region 是您的 GKE 集群所在的 GCP 区域。

然后您可以通过以下方式获取您的新 IP 地址:

gcloud compute addresses describe gke-ip --region <region>

现在您可以通过指定显式 loadBalancerIP 将静态 IP 地址添加到服务中。1

apiVersion: v1
kind: Service
metadata:
  name: gateway
spec:
  selector:
    app: gateway
  ports:
    - name: http
      port: 3000
      targetPort: 3000
      nodePort: 30000
  type: LoadBalancer
  loadBalancerIP: "1.2.3.4"

此时,您还可以将其硬编码到您的 ConfigMap 中,而不必担心从集群本身获取值。

1如果您已经使用自动分配的 IP 地址创建了 LoadBalancer,则设置 IP 地址不会更改底层 GCP 负载平衡器的 IP。相反,您应该删除集群中的 LoadBalancer 服务,等待大约 15 分钟以清理底层 GCP 资源,然后使用显式 IP 地址重新创建 LoadBalancer

【讨论】:

    【解决方案2】:

    您正在尝试从客户端的浏览器访问网关服务。

    我想建议您另一种解决方案,该解决方案与您当前尝试实现的目标略有不同 但它可以解决你的问题。

    根据您的问题,我能够推断出您的网络应用和网关应用在同一个集群上。

    在我的解决方案中,您不需要 LoadBalancer 类型的服务,基本 Ingress 足以使其工作。

    您只需要创建一个 Service 对象(注意选项 type: LoadBalancer 现已消失)

    apiVersion: v1
    kind: Service
    metadata:
    name: gateway
    spec:
    selector:
      app: gateway
    ports:
      - name: http
        port: 3000
        targetPort: 3000
        nodePort: 30000
    

    您还需要一个入口对象(请记住,需要将 na Ingress Controller 部署到集群才能使其工作),如下所示: 有关如何部署 Nginx Ingress 控制器的更多信息,您可以找到here 如果您已经在使用一个(可能是不同的),那么您可以跳过这一步。

    apiVersion: networking.k8s.io/v1beta1
    kind: Ingress
    metadata:
    name: gateway-ingress
    annotations:
      nginx.ingress.kubernetes.io/rewrite-target: /
    spec:
    rules:
      - host: gateway.foo.bar.com
        http:
          paths:
          - path: /
              backend:
                serviceName: gateway
                servicePort: 3000
    

    注意主机字段。

    您需要为您的 Web 应用程序重复相同的操作。请记住使用适当的主机名(DNS 名称) 例如对于 Web 应用程序:foo.bar.com 和对于网关:gateway.foo.bar.com 然后只需使用gateway.foo.bar.com dns 名称从客户端 Web 浏览器连接到网关应用程序。

    您还需要创建一个 dns 条目,将 *.foo.bar.com 指向 Ingress 的公共 IP 地址 因为 Ingress 控制器将创建自己的负载均衡器。

    流量如下:

    +-------------+   +---------+   +-----------------+   +---------------------+
    | Web Browser |-->| Ingress |-->| gateway Service |-->| gateway application |
    +-------------+   +---------+   +-----------------+   +---------------------+
    

    这种方法更好,因为它不会导致客户端浏览器中的跨域资源共享 (CORS) 出现问题。

    我从官方 kubernetes 文档中获取并稍作修改的 Ingress 和 Service 清单示例。

    更多关于 Ingress 你可以找到here 和服务here

    【讨论】:

    • 这看起来很有希望...我会尝试一下,如果以后遇到任何问题,请告诉您。但现在我有一个问题:如何将我的域与 Ingress 关联?我是否只创建一个指向 Ingress 的 IP 地址的 A 记录?
    • 是的,只需创建一个指向 Ingress 的 IP 地址的 A 记录
    • 我现在已经研究了你的建议,这似乎是一个很好的解决方案。但是,我正在努力实施。您介意更详细地解释实际步骤吗? :)
    • 您可能希望像在 GKE 上一样使用 GKE Ingres 控制器,有关此主题的更多信息可以在 here 中找到,而在 k8s documentation 中可以找到有关 ingress 的一般信息。它有据可查,所以我认为你不应该有任何问题。稍后只需创建A 记录指向与分配给入口服务(kubectl get svc)的EXTERNAL-IP 相同的IP。
    • 它适用于我在本地使用microk8s,但我无法让它在 GKE 上运行。我可能需要发布另一个关于我的问题的问题
    【解决方案3】:

    以下部署每 10 秒使用kubectl 读取给定服务的外部 IP,并使用它修补给定的 configmap:

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: configmap-updater
      labels:
        app: configmap-updater
    spec:
      selector:
        matchLabels:
          app: configmap-updater
      template:
        metadata:
          labels:
            app: configmap-updater
        spec:
          containers:
          - name: configmap-updater
            image: alpine:3.10
            command: ['sh', '-c' ]
            args:
            - | #!/bin/sh
                set -x
    
                apk --update add curl
                curl -LO https://storage.googleapis.com/kubernetes-release/release/v1.16.0/bin/linux/amd64/kubectl
                chmod +x kubectl
    
                export CONFIGMAP="configmap/global-config"
                export SERVICE="service/gateway"
    
                while true
                do
                    IP=`./kubectl get services $CONFIGMAP -o go-template --template='{{ (index .status.loadBalancer.ingress 0).ip }}'`
                    PATCH=`printf '{"data":{"API_URL": "https://%s:3000"}}' $IP`
                    echo ${PATCH}
                    ./kubectl patch --type=merge -p "${PATCH}" $SERVICE
    
                    sleep 10
                done
    

    您可能在 GKE 集群中启用了 RBAC,但仍需要创建适当的 Role 和 RoleBinding 才能使其正常工作。

    您有几种可能性:

    • 如果您真的需要将其入侵到您的设置中,您可以使用类似的方法,在您的 pod 中使用 sidecar 容器或类似上面的全局服务。请记住,如果 configmap 实际发生更改以便容器的环境变量能够获取更改,则您需要重新创建 pod。

    • 直接在应用程序中观察和查询 Kubernetes-API 以获取外部 IP,无需环境变量。

    • 让您的应用程序不直接依赖外部 IP。

    【讨论】:

    • 对于这样一个简单的问题似乎需要付出很多努力。你真的会自己实施这个解决方案吗?还是您会使用上述其他可能性之一?
    • 您应该修复您的应用程序,使其无需了解负载平衡器的外部 IP。除此之外,我很确定这个解决方案对于给定的问题是最简单的,不管架构不好。
    • 修复应用程序意味着建立一个像 api.some-domain.com 这样的公共 DNS?
    • 修复意味着您的服务不需要知道从哪里调用它。
    • 服务不应该知道从哪里调用它,但我需要服务的 IP 才能从客户端应用程序向它发出请求
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-08-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-30
    相关资源
    最近更新 更多