【问题标题】:Exposing multiple TCP/UDP services using a single LoadBalancer on K8s在 K8s 上使用单个 LoadBalancer 公开多个 TCP/UDP 服务
【发布时间】:2020-08-09 07:43:14
【问题描述】:

试图弄清楚如何在 Kubernetes 上使用单个 LoadBalancer 公开多个 TCP/UDP 服务。假设服务是 ftpsrv1.com 和 ftpsrv2.com,每个服务都在端口 21 上提供服务。

以下是我能想到的选项及其局限性:

  • 每个 svc 一个 LB:太贵了。
  • Nodeport : 想要使用 30000-32767 范围之外的端口。
  • K8s Ingress:目前不支持 TCP 或 UDP 服务。
  • 使用 Nginx Ingress 控制器:再次will be one on one mapping
  • 找到this custom implementation:但是好像没更新,上次更新差不多一年前了。

任何意见将不胜感激。

【问题讨论】:

    标签: kubernetes kubernetes-ingress


    【解决方案1】:

    关于“Nodeport:想要使用 30000-32767 范围之外的端口。”

    您可以通过服务的 yaml 文件中的“nodePort”设置为每个服务实现手动选择服务的端口,或者设置下面指示的标志,以便为所有服务实现自动分配您的自定义端口范围。

    来自文档: “如果将 type 字段设置为 NodePort,Kubernetes 控制平面会从 --service-node-port-range 标志指定的范围内分配一个端口(默认值:30000-32767)。” services

    【讨论】:

      【解决方案2】:

      实际上是possible 使用 NGINX Ingress 来实现。

      Ingress 不支持 TCP 或 UDP 服务。出于这个原因,这个 Ingress 控制器使用标志 --tcp-services-configmap--udp-services-configmap 来指向一个现有的配置映射,其中键是要使用的外部端口,值指示使用以下格式公开的服务:<namespace/service name>:<service port>:[PROXY]:[PROXY]

      This guide 描述了如何使用 minikube 实现它,但在本地 kubernetes 上执行此操作是不同的,需要更多步骤。

      缺乏描述如何在非 minikube 系统上完成的文档,这就是我决定在这里完成所有步骤的原因。本指南假设您有一个未安装 NGINX Ingress 的新集群。

      我正在使用 GKE 集群,所有命令都在我的 Linux 工作站上运行。它也可以在裸机 K8S 集群上完成。

      创建示例应用程序和服务

      在这里,我们将创建一个应用程序,它是稍后使用我们的入口公开它的服务。

      apiVersion: apps/v1
      kind: Deployment
      metadata:
        name: redis-deployment
        namespace: default
        labels:
          app: redis
      spec:
        replicas: 1
        selector:
          matchLabels:
            app: redis
        template:
          metadata:
            labels:
              app: redis
          spec:
            containers:
            - image: redis
              imagePullPolicy: Always
              name: redis
              ports:
              - containerPort: 6379
                protocol: TCP
      ---
      apiVersion: v1
      kind: Service
      metadata:
        name: redis-service
        namespace: default
      spec:
        selector:
          app: redis
        type: ClusterIP
        ports:
          - name: tcp-port
            port: 6379
            targetPort: 6379
            protocol: TCP
      ---      
      apiVersion: v1
      kind: Service
      metadata:
        name: redis-service2
        namespace: default
      spec:
        selector:
          app: redis
        type: ClusterIP
        ports:
          - name: tcp-port
            port: 6380
            targetPort: 6379
            protocol: TCP      
      

      请注意,我们正在为同一个应用程序创建 2 个不同的服务。这只是作为概念证明。我不想在后面展示许多端口可以仅使用一个 Ingress 进行映射。

      使用 Helm 安装 NGINX Ingress:

      安装 helm 3:

      $ curl https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 | bash
      

      添加 NGINX Ingress 仓库:

      $ helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
      

      在 kube-system 命名空间上安装 NGINX Ingress:

      $ helm install -n kube-system ingress-nginx ingress-nginx/ingress-nginx
      

      准备我们的新 NGINX 入口控制器部署

      我们必须在 spec.template.spec.containers.args 下添加以下几行:

              - --tcp-services-configmap=$(POD_NAMESPACE)/tcp-services
              - --udp-services-configmap=$(POD_NAMESPACE)/udp-services
      

      所以我们必须使用以下命令进行编辑:

      $ kubectl edit deployments -n kube-system ingress-nginx-controller
      

      让它看起来像这样:

      ...
          spec:
            containers:
            - args:
              - /nginx-ingress-controller
              - --publish-service=kube-system/ingress-nginx-controller
              - --election-id=ingress-controller-leader
              - --ingress-class=nginx
              - --configmap=kube-system/ingress-nginx-controller
              - --tcp-services-configmap=$(POD_NAMESPACE)/tcp-services
              - --udp-services-configmap=$(POD_NAMESPACE)/udp-services
              - --validating-webhook=:8443
              - --validating-webhook-certificate=/usr/local/certificates/cert
              - --validating-webhook-key=/usr/local/certificates/key
      ...
      

      创建 tcp/udp 服务配置映射

      apiVersion: v1
      kind: ConfigMap
      metadata:
        name: tcp-services
        namespace: kube-system
      
      apiVersion: v1
      kind: ConfigMap
      metadata:
        name: udp-services
        namespace: kube-system
      

      由于这些 configmap 是集中式的,并且可能包含配置,因此最好只修补它们,而不是每次添加服务时完全覆盖它们:

      $ kubectl patch configmap tcp-services -n kube-system --patch '{"data":{"6379":"default/redis-service:6379"}}'
      
      $ kubectl patch configmap tcp-services -n kube-system --patch '{"data":{"6380":"default/redis-service2:6380"}}'
      

      地点:

      • 6379 : 你的服务应该从 minikube 虚拟机外部监听的端口
      • default : 安装服务的命名空间
      • redis-service : 服务名称

      我们可以使用以下命令验证我们的资源是否已修补:

      $ kubectl get configmap tcp-services -n kube-system -o yaml
      
      apiVersion: v1
      data:
        "6379": default/redis-service:6379
        "6380": default/redis-service2:6380
      kind: ConfigMap
      metadata:
        annotations:
          kubectl.kubernetes.io/last-applied-configuration: |
            {"apiVersion":"v1","kind":"ConfigMap","metadata":{"annotations":{},"name":"tcp-services","namespace":"kube-system"}}
        creationTimestamp: "2020-04-27T14:40:41Z"
        name: tcp-services
        namespace: kube-system
        resourceVersion: "7437"
        selfLink: /api/v1/namespaces/kube-system/configmaps/tcp-services
        uid: 11b01605-8895-11ea-b40b-42010a9a0050
      

      您需要验证的唯一值是data 属性下的值如下所示:

        "6379": default/redis-service:6379
        "6380": default/redis-service2:6380
      

      向 NGINX 入口控制器部署添加端口

      我们需要修补我们的 nginx 入口控制器,以便它侦听端口 6379/6380 并将流量路由到您的服务。

      spec:
        template:
          spec:
            containers:
            - name: controller
              ports:
               - containerPort: 6379
                 hostPort: 6379
               - containerPort: 6380
                 hostPort: 6380 
      

      创建一个名为nginx-ingress-controller-patch.yaml 的文件并粘贴上面的内容。

      接下来使用以下命令应用更改:

      $ kubectl patch deployment ingress-nginx-controller -n kube-system --patch "$(cat nginx-ingress-controller-patch.yaml)"
      

      向 NGINX 入口控制器服务添加端口

      与为 minikube 提供的解决方案不同,我们必须修补 NGINX 入口控制器服务,因为它负责暴露这些端口。

      spec:
        ports:
        - nodePort: 31100
          port: 6379
          name: redis
        - nodePort: 31101
          port: 6380
          name: redis2
      

      创建一个名为nginx-ingress-svc-controller-patch.yaml 的文件并粘贴上面的内容。

      接下来使用以下命令应用更改:

      $ kubectl patch service ingress-nginx-controller -n kube-system --patch "$(cat nginx-ingress-svc-controller-patch.yaml)"
      

      查看我们的服务

      $ kubectl get service -n kube-system ingress-nginx-controller
      NAME                       TYPE           CLUSTER-IP      EXTERNAL-IP    PORT(S)                                                    AGE
      ingress-nginx-controller   LoadBalancer   10.15.251.203   34.89.108.48   6379:31100/TCP,6380:31101/TCP,80:30752/TCP,443:30268/TCP   38m
      

      请注意,我们的ingress-nginx-controller 正在侦听端口 6379/6380。

      测试您是否可以通过以下命令使用 telnet 访问您的服务:

      $ telnet 34.89.108.48 6379
      

      您应该会看到以下输出:

      Trying 34.89.108.48...
      Connected to 34.89.108.48.
      Escape character is '^]'.
      

      要退出 telnet,请同时输入 Ctrl]。然后输入quit 并回车。

      我们也可以测试6380端口:

      $ telnet 34.89.108.48 6380
      Trying 34.89.108.48...
      Connected to 34.89.108.48.
      Escape character is '^]'.
      

      如果您无法连接,请查看上述步骤。

      相关文章

      【讨论】:

      • 感谢您的出色回答。这是迄今为止我能找到的最详细的一个。还有一点我不明白。当您说“由于这些配置映射是集中的并且可能包含配置”时,您的确切意思是什么?当我不得不猜测时,我会说这是对更高级场景(在生产环境中)的提示,但在您的示例中没有任何作用,对吗?我想排除任何导致我的设置失败的失败源。
      • 感谢您的反馈。此评论强调,修补 configmap 以代替编辑或应用已编辑的 yaml 更为实用。
      • 好的,我想我明白了,谢谢。与此同时,我能够让我的 TCP 入口控制器补丁工作,但 UDP 还不能工作。当我想允许 UDP 流量时,如何更改 ingress-nginx-controller 服务补丁?我只是尝试应用以下补丁:spec: ports: - nodePort: 31101 port: 3478 name: turn-server protocol: UDP 但最终出现以下错误消息:“[...]无法使用混合协议创建外部负载均衡器”
      • 第二个补丁出现复制粘贴错误,文件名应该是nginx-ingress-svc-controller-patch.yaml
      • 非常感谢!!我不能给你足够的分数。
      【解决方案3】:

      @mWatney 的回答很棒。但是,它不适用于 UDP,因为您无法使用带有混合协议和 ingress-nginx 的负载均衡器。

      要解决这个问题,您实际上需要添加一个专门用于 UDP 服务的新负载平衡器,以及另一个入口控制器部署。

      在遵循所有@mWatney 的步骤后,这对我有用(虽然我没有使用 kube-system 命名空间,只是坚持使用 ingress-nginx):

      1. 应用此部署
      
          apiVersion: apps/v1
          kind: Deployment
          metadata:
            labels:
              helm.sh/chart: ingress-nginx-3.10.1
              app.kubernetes.io/name: ingress-nginx
              app.kubernetes.io/instance: ingress-nginx
              app.kubernetes.io/version: 0.41.2
              app.kubernetes.io/managed-by: Helm
              app.kubernetes.io/component: controller
            name: ingress-nginx-udp-controller
            namespace: ingress-nginx
          spec:
            selector:
              matchLabels:
                app.kubernetes.io/name: ingress-nginx
                app.kubernetes.io/instance: ingress-nginx
                app.kubernetes.io/component: udp-controller
            revisionHistoryLimit: 10
            minReadySeconds: 0
            template:
              metadata:
                labels:
                  app.kubernetes.io/name: ingress-nginx
                  app.kubernetes.io/instance: ingress-nginx
                  app.kubernetes.io/component: udp-controller
              spec:
                dnsPolicy: ClusterFirst
                containers:
                  - name: udp-controller
                    image: k8s.gcr.io/ingress-nginx/controller:v0.41.2@sha256:1f4f402b9c14f3ae92b11ada1dfe9893a88f0faeb0b2f4b903e2c67a0c3bf0de
                    imagePullPolicy: IfNotPresent
                    lifecycle:
                      preStop:
                        exec:
                          command:
                            - /wait-shutdown
                    args:
                      - /nginx-ingress-controller
                      - --publish-service=$(POD_NAMESPACE)/ingress-nginx-udp-controller
                      - --election-id=ingress-controller-leader
                      - --ingress-class=nginx
                      - --configmap=$(POD_NAMESPACE)/ingress-nginx-controller
                      - --udp-services-configmap=$(POD_NAMESPACE)/udp-services
                      - --validating-webhook=:8443
                      - --validating-webhook-certificate=/usr/local/certificates/cert
                      - --validating-webhook-key=/usr/local/certificates/key
                    securityContext:
                      capabilities:
                        drop:
                          - ALL
                        add:
                          - NET_BIND_SERVICE
                      runAsUser: 101
                      allowPrivilegeEscalation: true
                    env:
                      - name: POD_NAME
                        valueFrom:
                          fieldRef:
                            fieldPath: metadata.name
                      - name: POD_NAMESPACE
                        valueFrom:
                          fieldRef:
                            fieldPath: metadata.namespace
                      - name: LD_PRELOAD
                        value: /usr/local/lib/libmimalloc.so
                    livenessProbe:
                      httpGet:
                        path: /healthz
                        port: 10254
                        scheme: HTTP
                      initialDelaySeconds: 10
                      periodSeconds: 10
                      timeoutSeconds: 1
                      successThreshold: 1
                      failureThreshold: 5
                    readinessProbe:
                      httpGet:
                        path: /healthz
                        port: 10254
                        scheme: HTTP
                      initialDelaySeconds: 10
                      periodSeconds: 10
                      timeoutSeconds: 1
                      successThreshold: 1
                      failureThreshold: 3
                    volumeMounts:
                      - name: webhook-cert
                        mountPath: /usr/local/certificates/
                        readOnly: true
                    resources:
                      requests:
                        cpu: 100m
                        memory: 90Mi
                nodeSelector:
                  kubernetes.io/os: linux
                serviceAccountName: ingress-nginx
                terminationGracePeriodSeconds: 300
                volumes:
                  - name: webhook-cert
                    secret:
                      secretName: ingress-nginx-admission
       
      1. 应用此服务
      
      
          apiVersion: v1
          kind: Service
          metadata:
            labels:
              helm.sh/chart: ingress-nginx-3.10.1
              app.kubernetes.io/name: ingress-nginx
              app.kubernetes.io/instance: ingress-nginx
              app.kubernetes.io/version: 0.41.2
              app.kubernetes.io/managed-by: Helm
              app.kubernetes.io/component: udp-controller
            name: ingress-nginx-udp-controller
            namespace: ingress-nginx
          spec:
            type: LoadBalancer
            externalTrafficPolicy: Local
            ports:
              - name: udp
                port: 5004
                protocol: UDP
                targetPort: 5004
            selector:
              app.kubernetes.io/name: ingress-nginx
              app.kubernetes.io/instance: ingress-nginx
              app.kubernetes.io/component: udp-controller
      
      

      跑步应该会给你类似的东西 kubectl get services -n ingress-nginx

      
      NAME                                 TYPE           CLUSTER-IP       EXTERNAL-IP   PORT(S)                      AGE
      ingress-nginx-controller             LoadBalancer   10.103.60.70     localhost     80:30885/TCP,443:30370/TCP   13m
      ingress-nginx-controller-admission   ClusterIP      10.111.245.103           443/TCP                      14d
      ingress-nginx-udp-controller         LoadBalancer   10.111.249.180   localhost     5004:30565/UDP               9m48s
      

      要测试它是否正常工作,您可以使用 netcat 访问您的 udp 服务器,例如 nc -u -v localhost 5004

      【讨论】:

        【解决方案4】:

        Mark Watney 接受的答案效果很好。但是无需手动编辑和修补配置,Helm 可以为您完成。

        为 ingress-nginx 下载默认的values.yaml 文件。
        改变

        tcp: {}
        #  8080: "default/example-tcp-svc:9000"
        

        tcp:
          6379: default/redis-service:6379
          6380: default/redis-service:6380
        

        以下命令将安装或更新(如果已安装)您的 nginx 控制器,创建所需的配置映射并更新配置字段:

        helm upgrade --install -n kube-system ingress-nginx ingress-nginx/ingress-nginx --values values.yaml --wait
        

        【讨论】:

          猜你喜欢
          • 2021-05-22
          • 2020-01-03
          • 1970-01-01
          • 2022-08-04
          • 1970-01-01
          • 2019-11-26
          • 2021-03-13
          • 1970-01-01
          • 2021-07-21
          相关资源
          最近更新 更多