面向开发人员的 Kubernetes: 7 内部服务和负载均衡(2)负载均衡

面向开发人员的 Kubernetes: 7 内部服务和负载均衡(2)负载均衡

精选文章moguli202025-01-07 11:50:4223A+A-

7.2 入口:HTTP(S) 负载均衡

到目前为止,在本书中,我们一直在使用类型为 LoadBalancer 的服务创建外部 IP。这为您提供了所谓的第 4 层 (L4) 负载均衡器,它在网络层平衡请求,并可以与多种协议(例如,TCP、UDP、SCTP)一起工作。您可以使用所需的协议和端口配置服务,然后获得一个将流量平衡到您的 Pods 的 IP。如果您通过负载均衡器公开 HTTP 服务,则需要实现自己的 TLS 终止处理(即,配置证书并运行 HTTPS 端点),所有到该端点的流量将根据 matchLabels 规则路由到一组 Pods。没有选项可以直接在同一个负载均衡器上公开两个或多个独立的服务(尽管可以在内部将请求代理到另一个服务)。

当您专门发布 HTTP 应用时,您可能会从所谓的第七层 (L7) 负载均衡器中获得更多的实用性,它在 HTTP 请求层进行负载均衡,并可以执行更复杂的操作,例如终止 HTTPS 连接(这意味着它将为您处理 HTTPS 细节),并执行基于路径的路由,以便您可以为单个域名主机提供多个服务。在 Kubernetes 中,HTTP 负载均衡器是通过 Ingress 对象创建的。

Ingress 允许您将多个内部服务放置在单个外部 IP 后面,并进行负载均衡。您可以根据其 URI 路径 ( /foo , /bar )、主机名 ( foo.example.com, bar.example.com ) 或两者 (图 7.4) 将 HTTP 请求定向到不同的后端服务。能够在单个 IP 上运行多个服务,并可能在单个域名下提供不同路径,这是 Ingress 的独特之处,因为如果您像前面的章节那样使用类型为 LoadBalancer 的单独服务暴露它们,这些服务将具有不同的 IP 地址, necessitating separate domains (例如, foo.example.com 用于一个, bar.example.com 用于另一个)。

图 7.4 Ingress 的规则列表或 URL 映射允许一个 HTTP 负载均衡器处理多个服务的流量

Ingress 的属性能够将多个服务放置在一个主机下,这在扩展应用程序时非常有用。当您需要将服务拆分为多个服务以提高开发效率(例如,团队希望管理自己的部署生命周期)或进行扩展(例如,能够单独扩展应用程序的某些方面)时,您可以使用 Ingress 来路由请求,而不改变任何面向公众的 URL。例如,假设您的应用程序有一个路径是特别 CPU 密集型的请求。您可能希望将其移动到自己的服务中,以便能够单独扩展。Ingress 允许您无缝地对最终用户进行这样的更改。

清单 7.6 提供了一个示例 Ingress,其中路由由不同的后端提供。在此示例中,我们将在根路径 ( / ) 上暴露一个内部 Timeserver 服务,并在 /robohash 上暴露一个内部 Robohash 服务。

清单 7.6 Chapter07/7.2_Ingress/ingress_path.yaml

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: timeserver-ingress
spec:
  rules:
  - http:
      paths:
      - path: /              ?
        pathType: Prefix
        backend:
          service:
            name: timeserver-internal
            port:
              number: 80
      - path: /robohash      ?
        pathType: Prefix
        backend:
          service:
            name: robohash-internal
            port:
              number: 80

? 第一条路径,由时间服务器内部服务处理

? 第二条路径,由 robohash 内部服务处理

清单 7.7 显示了使用不同主机的变体。每个主机也可以使用清单 7.6 中的格式具有多个路径。

清单 7.7 Chapter07/7.2_Ingress/ingress_host.yaml

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: timeserver-ingress
spec:
  rules:
  - host: timeserver.example.com     ?
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: timeserver-internal
            port:
              number: 80
  - host: robohash.example.com       ?
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: robohash-internal
            port:
              number: 80

? 第一个主机,由时间服务器内部服务处理

第二个主机,由 robohash-internal 服务处理

Ingress 引用了被指定为类型 NodePort 的内部服务,例如以下列表中的服务。

清单 7.8 Chapter07/7.2_Ingress/timeserver-service-internal.yaml

apiVersion: v1
kind: Service
metadata:
  name: timeserver-internal
spec:
  selector:
    pod: timeserver-pod
  ports:
  - port: 80
    targetPort: 80
    protocol: TCP
  type: NodePort        ?

要使用带有 Ingress 的内部服务,它需要是 NodePort 类型

Ingress 对象可以配置为执行精确匹配(即,仅将与给定路径完全匹配的请求路由到服务)或前缀匹配(即,所有与路径前缀匹配的请求将被路由)使用 pathType 属性。我在这里不打算详细说明,因为官方文档做得很好。值得重现的一个方面是关于多个匹配的规则:

在某些情况下,Ingress 中的多个路径将匹配一个请求。在这些情况下,将优先考虑最长的匹配路径。如果两个路径仍然相等,则将优先考虑精确路径类型的路径,而不是前缀路径类型。

正如您在列表 7.6 中看到的, / 有一条路径, /robohash 有第二条路径。对 /robohash 的请求将被路由到第二个服务,即使它也匹配第一条路径。如果您过去使用过其他路由机制(如 Apache URL 重写),通常优先考虑的是第一个匹配的规则——但在 Kubernetes 中,较长的匹配规则优先。我发现这种设计很方便,因为它与开发者的意图很好地匹配。

要部署此示例,如果之前的示例正在运行( kubectl delete -f Chapter07/7.1_InternalServices ),请先删除它,然后运行以下命令:

$ cd Chapter07/7.2_Ingress 
$ kubectl create -f robohash-deploy.yaml 
deployment.apps/robohash created
$ kubectl create -f robohash-service.yaml 
service/robohash-internal created
$ kubectl create -f timeserver-deploy-dns.yaml 
deployment.apps/timeserver created
$ kubectl create -f timeserver-service-internal.yaml 
service/timeserver-internal created
$ kubectl create -f ingress_path.yaml
ingress.networking.k8s.io/timeserver-ingress created
 
$ kubectl get ing -w
NAME                 CLASS    HOSTS   ADDRESS        PORTS   AGE
timeserver-ingress   <none>   *                      80      4s
timeserver-ingress   <none>   *       203.0.113.20   80      100s

一旦您的 Ingress 拥有 IP,您就可以访问它。尝试 /robohash 路径通过 Ingress 连接到 Robohash 服务。请注意,支持 Ingress 的资源可能需要额外的时间进行配置。即使您已经获得了 IP 地址并访问它,您可能会看到一段时间的 404 error 。我建议您在大约 5 分钟后再试一次,以便给云服务提供商时间更新 Ingress。

要调试 Ingress 的问题,您可以使用 kubectl describe ingress 。以下是我在描述 Ingress 时看到的情况,当时它刚分配了 IP,但还未准备好:

$ kubectl describe ingress
Name:             timeserver-ingress
Namespace:        default
Address:          203.0.113.20
Default backend:  default-http-backend:80 (10.22.0.130:8080)
Rules:
  Host        Path  Backends
  ----        ----  --------
  *           
              /           timeserver-internal:80 (10.22.0.135:80)
              /robohash   robohash-internal:80 (10.22.1.4:80)
Annotations:  ingress.kubernetes.io/backends:                      ?
                {"k8s-be-32730--a52250670846a599":"Unknown",       ?
                "k8s1-a5225067":"Unknown","k8s1-a5225067-default-timeser ...
              ingress.kubernetes.io/forwarding-rule: k8s2-fr-21mgs2fl
              ingress.kubernetes.io/target-proxy: k8s2-tp-21mgs2fl
              ingress.kubernetes.io/url-map: k8s2-um-21mgs2fl
Events:
  Type    Reason     From          Message
  ----    ------     ----          -------
  Normal  Sync       loadbalancer  UrlMap "k8s2-um-21mgs2fl" created
  Normal  Sync       loadbalancer  TargetProxy "k8s2-tp-21mgs2fl" created
  Normal  Sync       loadbalancer  ForwardingRule "k8s2-fr-21mgs2fl" created
  Normal  IPChanged  loadbalancer  IP is now 203.0.113.20
  Normal  Sync       loadbalancer  Scheduled for sync

? 后端状态未知

以下显示了再等待几分钟后的状态。请注意注释是如何从 Unknown 更改为 HEALTHY 的。之后,我能够浏览到 IP 并访问该服务:

$ kubectl describe ing
Name:             timeserver-ingress
Namespace:        default
Address:          203.0.113.20
Default backend:  default-http-backend:80 (10.22.0.130:8080)
Rules:
  Host        Path  Backends
  ----        ----  --------
  *           
              /           timeserver-internal:80 (10.22.0.135:80)
              /robohash   robohash-internal:80 (10.22.1.4:80)
Annotations:  ingress.kubernetes.io/backends:                         ?
                {"k8s-be-32730--a52250670846a599":"HEALTHY",          ?
                "k8s1-a5225067":"HEALTHY","k8s1-a5225067-default-timeser...
              ingress.kubernetes.io/forwarding-rule: k8s2-fr-21mgs2fl
              ingress.kubernetes.io/target-proxy: k8s2-tp-21mgs2fl
              ingress.kubernetes.io/url-map: k8s2-um-21mgs2fl
Events:
  Type    Reason     From          Message
  ----    ------     ----          -------
  Normal  Sync       loadbalancer  UrlMap "k8s2-um-21mgs2fl" created
  Normal  Sync       loadbalancer  TargetProxy "k8s2-tp-21mgs2fl" created
  Normal  Sync       loadbalancer  ForwardingRule "k8s2-fr-21mgs2fl" created
  Normal  IPChanged  loadbalancer  IP is now 203.0.113.20
  Normal  Sync       loadbalancer  Scheduled for sync

? 后端状态现在正常

节省成本的技巧:使用 Ingress 节省 IP 地址

Ingress 的一个好处是,通过使用基于主机的路由,您可以托管多个服务,所有服务都使用相同的外部 IP 地址。Ingress 检查 HTTP 请求中的 Host 头,并相应地路由流量。这与 LoadBalancer 类型的服务形成对比,后者每个服务都有自己分配的 IP 地址,并且不执行基于 HTTP 请求的路由。

云服务提供商通常根据负载均衡规则收费,这大致转化为分配了多少个负载均衡外部 IP 地址。通过使用 Ingress 将多个服务合并为一个,而不是每个服务都暴露其自己的 IP,您可能会节省费用。

如果您的云服务提供商将 HTTP 负载均衡器(入口)和网络负载均衡器(类型为 LoadBalancer 的服务)分开,并且有最低规则费用(就像在撰写时的 Google Cloud 一样),您可能希望在需要超过最低限度之前仅使用其中一个。

另一个技巧,但我不推荐,是运行自己的 Ingress 控制器。这种技术(本书未涵盖)意味着部署一个开源组件作为负载均衡器,以实现 Kubernetes Ingress 功能,覆盖云服务提供商的默认实现。这种方法意味着类型为 LoadBalancer 的 Ingress 对象和服务对象在计费时被视为相同的规则类型,如果你需要两者,这可以节省费用,但有一个牺牲:你现在需要自己管理这个组件。你是调试 Kubernetes Ingress 控制器的专家吗?根据我的经验,最好完全使用标准 Ingress 对象,或者如果你需要节省费用,就坚持使用纯负载均衡器。

7.2.1 使用 TLS 保护连接

Ingress 的另一个有用特性是它会为您执行 TLS 加密。现代 Web 应用程序通常作为安全的 HTTPS 应用程序托管,使用 TLS,这对安全性很重要,但会给应用服务器带来一些开销。根据您使用的服务器中间件,您可能会通过让 Ingress 负载均衡器处理 TLS 连接(充当所谓的 TLS 终端)并仅通过 HTTP 与后端通信(当然是通过您云服务提供商的安全网络)来看到性能提升(图 7.5)。如果您愿意,Ingress 可以重新加密流量并通过 HTTPS 连接到您的服务,但没有选项可以将未修改的加密流量直接从客户端传递到后端。为此,您可以使用第 3 章中提到的 LoadBalancer 类型的服务。

图 7.5 Ingress 终止 HTTPS (TLS) 流量,并可以通过普通 HTTP 或 HTTPS 连接将其转发到服务 Pod

现在,由于 Ingress 正在终止您的 TLS 连接,您需要为其设置证书。如果您像我一样,在不同的系统上做过几次这件事,您可能会对这一步感到畏惧。幸运的是,Kubernetes 让这变得轻而易举!

您只需将您的证书和密钥作为 Kubernetes Secret 导入,然后在您的 Ingress 配置中引用该 Secret。Kubernetes Secret 只是您集群中的一个数据对象,用于包含 TLS 密钥等内容。

要做到这一点,通常您需要按照证书颁发机构的指示创建一个证书,最终产品将包括我们需要的两个文件:您创建的私钥和证书颁发机构颁发的证书。

为了演示,我们可以创建自己的自签名证书来代替受信任的证书。请注意,虽然这将为连接提供相同的加密,但没有身份验证,并且您会在浏览器中看到可怕的消息。以下命令将创建这样的证书:

# create a private key
openssl genrsa -out example.key 2048
 
# create a certificate request for 'example.com'
openssl req -new -key example.key -out example.csr \
    -subj "/CN=example.com"
 
# self-issue an untrusted certificate
openssl x509 -req -days 365 -in example.csr -signkey \
    example.key -out example.crt

一旦您拥有了私钥和证书,无论是按照之前的说明创建的,还是按照您的证书颁发机构的说明创建的,您现在可以创建 Kubernetes 秘密:

kubectl create secret tls my-tls-cert --cert example.crt --key example.key

您可能会注意到这里的强制命令 kubectl create 。这是我推荐使用强制命令而不是在文件中定义配置的少数几次之一,因为这比手动创建对象并对所有数据进行 Base64 编码要简单。如果您想查看使用此命令创建的配置,可以轻松通过 kubectl get -o yaml secret my-tls-cert 查看。

最后一步是将此秘密引用到我们的 Ingress 中,如以下列表所示。

清单 7.9 Chapter07/7.2.1_TLS/ingress_tls.yaml

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: timeserver-tls
spec:
  tls:
  - secretName: my-tls-cert     ?
  rules:
  - host: example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: timeserver-internal
            port:
              number: 80
  - http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: robohash-internal
            port:
              number: 80

? 引用 TLS 密钥

参考前一部分创建的类型为 NodePort 的服务,我们可以使用 TLS 密钥创建这个新的 Ingress:

$ cd Chapter07/7.2.1_TLS/
$ kubectl create -f ingress_tls.yaml 
ingress.networking.k8s.io/timeserver-tls created
$ kubectl get ing
NAME             CLASS    HOSTS         ADDRESS          PORTS     AGE
timeserver-tls   <none>   example.com   203.0.113.15     80, 443   9m15s
$ open "https://203.0.113.15"

请记住,配置步骤可能需要一些时间,即使在 Ingress 收到 IP 的时候。如果您使用自签名证书,您将在浏览器中看到一些可怕的警告。

要测试此 Ingress 中的域名路由(示例中的 example.com ),您需要将您使用的域的 DNS 配置为 Ingress 的 IP。要进行本地测试,您还可以编辑您的 hosts 文件,添加 IP 和域名(要找到如何执行此操作的说明,可以在 Google 上搜索“在 <您的操作系统版本> 中更新 hosts 文件”)。Ingress 的 IP 可以通过 kubectl get ingress. 找到。以下是我的 Ingress 对象的样子,以及我添加到本地 hosts 文件的条目:

$ kubectl get ingress
NAME               CLASS    HOSTS         ADDRESS        PORTS     AGE
timeserver-tls     <none>   example.com   203.0.113.15   80, 443   82m
 
$ cat /etc/hosts
# ...
203.0.113.15 example.com

现在,假设您已经配置了主机,您应该能够浏览到 https://example.com。如果您生成了自签名证书,您会收到一个可怕的浏览器错误,在这种情况下,可以点击继续。要将您的服务实际发布到世界上,您需要返回并向实际的证书颁发机构请求证书,并使用该证书来创建 TLS 秘密。

再次,Kubernetes 的一个好处是所有这些配置都是以 Kubernetes 对象的形式存在,而不是主机上的随机文件,这使得在其他地方重现环境变得简单明了。

使用 GKE?尝试托管证书

之前的说明是关于将经过验证的 CA 证书添加到您的 Kubernetes Ingress 对象。如果您使用的是 Google Kubernetes Engine (GKE) 并希望采用更简单的方法,可以使用托管证书。

使用托管证书,您可以跳过 CA 签名步骤以及将私钥和证书复制到 Kubernetes 作为 Secret 的过程。相反,您需要首先向 Google(在 Google Cloud 控制台中)证明您对域的所有权,创建一个 GKE 特定的 ManagedCertificate 对象,列出您希望为其提供证书的(子)域,然后在您的 Ingress 中引用该对象。Google 将自动提供和管理证书。这一切都非常简单,因此我将让官方文档成为您的指南。


摘要

  • Kubernetes 提供了多种工具来创建、发现、连接和暴露多个服务,当您的需求超出单个容器所能承载的范围时。
  • 内部服务是一种连接各种工作负载的方式,这些工作负载可以用不同的语言编写,可能在不同的发布计划上,或者仅仅需要独立扩展。
  • 内部服务可以通过集群 IP 进行暴露,从而允许它们被集群中的其他 Pod 调用。
  • Kubernetes 提供两种服务发现形式来查找这些内部服务 IP:环境变量和 DNS。
  • Ingress 可以使用单个 IP 将多个内部服务暴露到互联网,路由通过路径或主机名进行。
  • Ingress 是一个 HTTP(S) 负载均衡器,可以配置多个 TLS 证书以执行 TLS 终止。
  • 通过在负载均衡器层执行 TLS 终止,您可以减少应用程序中的配置工作量并降低 CPU 开销。

点击这里复制本文地址 以上内容由莫古技术网整理呈现,请务必在转载分享时注明本文地址!如对内容有疑问,请联系我们,谢谢!
qrcode

莫古技术网 © All Rights Reserved.  滇ICP备2024046894号-2