【问题标题】:Kubenetes: Is it possible to hit multiple pods with a single request in Kubernetes clusterKubernetes:是否可以在 Kubernetes 集群中使用单个请求来命中多个 pod
【发布时间】:2018-09-11 17:41:49
【问题描述】:

我想清除 Kubernetes 命名空间中所有 pod 中的缓存。我想向端点发送一个请求,然后它将向命名空间中的所有 pod 发送 HTTP 调用以清除缓存。目前,我使用 Kubernetes 只能命中一个 pod,而且我无法控制哪个 pod 会被命中。

即使负载均衡器设置为 RR,连续访问 pod(n 次,其中 n 是 pod 的总数)也无济于事,因为其他一些请求可能会悄悄进入。

这里讨论了同样的问题,但我找不到实现的解决方案: https://github.com/kubernetes/kubernetes/issues/18755

我正在尝试使用 Hazelcast 实现清除缓存部分,其中我将存储所有缓存,而 Hazelcast 会自动处理缓存更新。

如果有解决此问题的替代方法,或者配置 kubernetes 以针对某些特定请求达到所有端点的方法,那么在此处分享将有很大帮助。

【问题讨论】:

  • 这是您已经想到的扩展。当 pod 收到 http 调用时,它将广播到一个主题(在 rabbitmq 或类似的)。所有 pod 监听主题并接收事件并响应事件执行任务
  • 感谢@MohitMutha,但我们正在通过使用外部主题/队列服务器添加另一个外部依赖项。

标签: docker kubernetes kubernetes-ingress


【解决方案1】:

如果你的 pod 中有 kubectl 并且可以访问 api-server,你就可以获取所有端点地址并将它们传递给 curl:

kubectl get endpoints <servicename> \
        -o jsonpath="{.subsets[*].addresses[*].ip}" | xargs curl

pod 中没有 kubectl 的替代方案:

从 pod 访问 api 服务器的推荐方法是使用 kubectl 代理:https://kubernetes.io/docs/tasks/access-application-cluster/access-cluster/#accessing-the-api-from-a-pod 这当然会增加至少相同的开销。或者您可以直接调用 REST api,您必须手动提供令牌。

APISERVER=$(kubectl config view --minify | grep server | cut -f 2- -d ":" | tr -d " ")
TOKEN=$(kubectl describe secret $(kubectl get secrets \
     | grep ^default | cut -f1 -d ' ') | grep -E '^token' | cut -f2 -d':' | tr -d " ")

如果你提供 APISERVER 和 TOKEN 变量,你的 pod 中不需要 kubectl,这样你只需要 curl 访问 api 服务器和“jq”来解析 json 输出:

curl $APISERVER/api/v1/namespaces/default/endpoints --silent \
     --header "Authorization: Bearer $TOKEN" --insecure \
     | jq -rM ".items[].subsets[].addresses[].ip" | xargs curl

更新(最终版本)

APISERVER 通常可以设置为 kubernetes.default.svc 并且令牌应该在 pod 中的 /var/run/secrets/kubernetes.io/serviceaccount/token 可用,因此无需手动提供任何东西:

TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token); \
curl https://kubernetes.default.svc/api/v1/namespaces/default/endpoints --silent \
     --header "Authorization: Bearer $TOKEN" --insecure \
     | jq -rM ".items[].subsets[].addresses[].ip" | xargs curl

jq 在此处可用:https://stedolan.github.io/jq/download/

【讨论】:

  • 不会将 kubectl 添加到我的 pod 会增加额外的开销吗?
  • 它将为 kube 配置添加一个 ~50 MiB 二进制文件和几个 KiB。
  • 我在 pod 中添加了一个不需要 kubectl 的替代方案
【解决方案2】:

对于那些试图寻找替代方案的人,我使用 hazelcast 作为分布式事件侦听器。在github上添加了一个类似的POC:https://github.com/vinrar/HazelcastAsEventListener

【讨论】:

    【解决方案3】:

    我使用this script 解决了这个问题。您只需编写等效命令即可进行 API 调用。我使用curl 来做到这一点。

    以下是脚本的用法:

    function usage {
        echo "usage: $PROGNAME [-n NAMESPACE] [-m MAX-PODS] -s SERVICE -- COMMAND"
        echo "  -s SERVICE   K8s service, i.e. a pod selector (required)"
        echo "     COMMAND   Command to execute on the pods"
        echo "  -n NAMESPACE K8s namespace (optional)"
        echo "  -m MAX-PODS  Max number of pods to run on (optional; default=all)"
        echo "  -q           Quiet mode"
        echo "  -d           Dry run (don't actually exec)"
    }
    

    例如要在名称为s1 和命名空间n1 的服务的所有pod 上运行命令curl http://google.com,您需要执行./kcdo -s s1 -n n1 -- curl http://google.com

    【讨论】:

    • 脚本的一些解释,以及代码 sn-ps 是可取的。外部链接消失了,在没有任何解释的情况下将脚本转储给人们是没有帮助的。
    • 我赶时间,所以没有提供太多细节。无论如何,我已经更新了答案。让我知道它是否可以。
    • 不错的脚本,谢谢!如何使用此脚本对所有 pod 的 IP 进行 curl?例如我想使用这个脚本在所有 pod 上执行 /status/live 而不是 curl google.com
    • 得到了解决方案:./kcdo-s s1 -n n1 -- curl localhost:8080/status/live。因此,可以通过脚本使用 localhost 并且它可以工作。
    【解决方案4】:

    我也遇到过类似的情况。这是我解决它的方法(我使用的是“默认”以外的命名空间)。

    设置访问集群Using RBAC Authorization

    通过创建一个ServiceAccount,将其分配给Pod并将一个Role绑定到它来完成对API的访问。

    1.创建一个ServiceAccount

    apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: my-serviceaccount
      namespace: my-namespace
    

    2.创建角色:在此部分中,您需要提供资源列表和您希望有权访问的操作列表。这是您想要列出端点并获取特定端点详细信息的示例。

    apiVersion: rbac.authorization.k8s.io/v1
    kind: Role
    metadata:
      name: my-role
      namespace: my-namespace
    rules:
    - apiGroups: [""]
      resources: ["endpoints"]
      verbs: ["get", "list"]
    

    3.将角色绑定到服务帐号

    apiVersion: rbac.authorization.k8s.io/v1
    kind: RoleBinding
    metadata:
      name: my-role-binding
      namespace: my-namespace
    subjects:
    - kind: ServiceAccount
      name: my-serviceaccount
    roleRef:
      kind: Role
      name: my-role
      apiGroup: rbac.authorization.k8s.io
    

    4.将服务帐户分配给部署中的 pod(它应该在 template.spec 下)

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: my-deployment
      namespace: my-namespace
    spec:
      replicas: 1
      selector:
        matchLabels:
              app: my-pod
      template:
        metadata:
          labels:
            app: my-pod
        spec:
          serviceAccountName: my-serviceaccount
          containers:
          - name: my-pod
            ...
    

    Access Clusters Using the Kubernetes API

    设置好所有安全方面后,您将有足够的权限访问 Pod 中的 API。与 API Server 通信所需的所有信息都安装在 Pod 中的 /var/run/secrets/kubernetes.io/serviceaccount 下。 您可以使用以下 shell 脚本(可能将其添加到 Docker 映像的 COMMAND 或 ENTRYPOINT 中)。

    #!/bin/bash
    # Point to the internal API server hostname
    API_SERVER=https://kubernetes.default.svc
    
    # Path to ServiceAccount token
    SERVICE_ACCOUNT=/var/run/secrets/kubernetes.io/serviceaccount
    
    # Read this Pod's namespace
    NAMESPACE=$(cat ${SERVICE_ACCOUNT}/namespace)
    
    # Read the ServiceAccount bearer token
    TOKEN=$(cat ${SERVICE_ACCOUNT}/token)
    
    # Reference the internal certificate authority (CA)
    CA_CERT=${SERVICE_ACCOUNT}/ca.crt
    

    从现在开始,它只是简单的 REST API 调用。您可以使用您选择的任何语言读取这些环境变量并访问 API。

    这里是列出您的用例的端点的示例

    # List all the endpoints in the namespace that Pod is running
    curl --cacert ${CA_CERT} --header "Authorization: Bearer ${TOKEN}" -X GET \
      "${API_SERVER}/api/v1/namespaces/${NAMESPACE}/endpoints"
    
    # List all the endpoints in the namespace that Pod is running for a deployment
    curl --cacert ${CA_CERT} --header "Authorization: Bearer ${TOKEN}" -X GET \
      "${API_SERVER}/api/v1/namespaces/${NAMESPACE}/endpoints/my-deployment"
    

    有关可用 API 端点以及如何调用它们的更多信息,请参阅API Reference

    【讨论】:

      【解决方案5】:

      我需要访问所有 pod,以便我可以更改类的日志级别,所以我从其中一个 pod 内部进行:

      // Change level to DEBUG
      host <service-name>| awk '{print $4}' | while read line; do
      curl --location --request POST "http://$line:9111/actuator/loggers/com.foo.MyClassName" \
      --header 'Content-Type: application/json' \
      --data-raw '{"configuredLevel": "DEBUG"}' 
      done
      // Query level on all pods
      host <service-name>| awk '{print $4}' | while read line; do    
      curl --location --request GET "http://$line:9111/actuator/loggers/com.foo.MyClassName"
      echo
      done
      

      您需要hostcurl 来执行它。

      不确定这是否是好的做法。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-02-23
        • 2019-05-13
        • 1970-01-01
        • 1970-01-01
        • 2023-03-13
        • 2020-09-19
        • 1970-01-01
        相关资源
        最近更新 更多