使用Kubernetes Ingress: Contour

2021-09-10T13:11:33+08:00 | 5分钟阅读 | 更新于 2021-09-10T13:11:33+08:00

Macro Zhao

使用Kubernetes Ingress: Contour

@TOC

推荐超级课程:

在处理Kubernetes时会遇到许多挑战,其中一个最常见的情况就是将用户流量引导至您的应用程序。而且,从 外部 Kubernetes 集群获取用户流量可能会更加困难。这时候我们就可以开始规划 Ingress。我最喜欢的之一就是 Contour 。Contour 是基于 Envoy (一7 层 代理)的高性能 Ingress 控制器。Contour 和 Envoy 都是 CNCF 项目。

在我们开始深入探讨 Contour 具体实现之前,让我们先简要讨论一下 Ingress。不过,进入 Ingress 之前,让我们往回退一步,看看没有 Ingress 会是怎样。

没有 Ingress 控制器的情况

实际上,并不一定非要有 Ingress 控制器来将外部流量引导至 Kubernetes 集群内运行的 pod。事实上,一种常见的方法是为您的应用程序 pod(s) 创建一个类型为 LoadBalancer 的服务,并由您喜欢的云提供商提供必要的基础设施来将流量引导至您的应用程序。

但是,这可能会导致一些问题:

  • 拥有的应用程序越多,需要的负载均衡器就越多。这直接转化为更多的资金投入。这种方法的成本增长非常迅速。
  • 您对云资源的管理和维护也会增加,因为您可能需要处理多个负载均衡器。
  • TLS 终止必须在您的应用程序 pod 处理。这不是一项轻松设置,更不用说维护了。
  • 这种方法只能提供简单的场景。一旦开始涉及更复杂的需求,您将不得不设计自己的解决方案。

以下是这种方法的外观:

正如您所看到的支持此配置的外部负载均衡器可能很多。这就是 Ingress 控制器可以帮助的地方。

Ingress 控制器的优势

有许多不同的 Ingress 控制器,但它们大多数实现相同的需求:简化将流量引导至您的 Kubernetes 集群并为更复杂的网络场景提供功能和好处。一旦在我们的集群中添加了 Ingress 控制器,流量的流向将改变为以下内容:

现在,您的外部流量将通过单个负载均衡器流向您的 Ingress 控制器,后者将采用 Ingress 配置确定要将流量转发到哪个服务。

部署 Contour

现在我们知道为什么可能需要一个 Ingress 控制器了,接下来一个问题是:我应该使用哪个 Ingress 控制器? 好问题。市面上有许多选择,但对于我来说 Contour 是我最喜欢的选择,原因如下:

  • 作为首选 Kubernetes Ingress 控制器开发
  • 支持动态配置(多亏了 Envoy)
  • 高性能(同样感谢 Envoy)
  • 为未来的 Gateway API 提供开发和支持工作
  • 直观而强大的 HTTPProxy API

说了这么多,现在我准备在我的 Kubernetes 集群中安装 Contour。有几种方式可以做到这一点,但最简单的方式就是应用 Contour 的清单:

$ kubectl apply -f https://projectcontour.io/quickstart/contour.yaml
namespace/projectcontour created
serviceaccount/contour created
serviceaccount/envoy created
configmap/contour created
customresourcedefinition.apiextensions.k8s.io/contourconfigurations.projectcontour.io created
customresourcedefinition.apiextensions.k8s.io/contourdeployments.projectcontour.io created
customresourcedefinition.apiextensions.k8s.io/extensionservices.projectcontour.io created
customresourcedefinition.apiextensions.k8s.io/httpproxies.projectcontour.io created
customresourcedefinition.apiextensions.k8s.io/tlscertificatedelegations.projectcontour.io created
serviceaccount/contour-certgen created
rolebinding.rbac.authorization.k8s.io/contour created
role.rbac.authorization.k8s.io/contour-certgen created
job.batch/contour-certgen-v1.20.1 created
clusterrolebinding.rbac.authorization.k8s.io/contour created
clusterrole.rbac.authorization.k8s.io/contour created
service/contour created
service/envoy created
deployment.apps/contour created
daemonset.apps/envoy created

如您所见,创建了许多 Kubernetes 对象。让我们进行一些检查以验证安装情况:

$ kubectl get po,svc,deploy,daemonset -n projectcontour
NAME                                READY   STATUS      RESTARTS   AGE
pod/contour-76cbc54fbf-jl2t2        1/1     Running     0          74s
pod/contour-76cbc54fbf-rwnsk        1/1     Running     0          74s
pod/contour-certgen-v1.20.1-27m46   0/1     Completed   0          74s
pod/envoy-7mrw7                     2/2     Running     0          73s
pod/envoy-9vwhp                     2/2     Running     0          73s
pod/envoy-zrn2w                     2/2     Running     0          73s

NAME              TYPE           CLUSTER-IP    EXTERNAL-IP      PORT(S)                      AGE
service/contour   ClusterIP      10.0.170.78   <none>           8001/TCP                     74s
service/envoy     LoadBalancer   10.0.3.217    52.224.190.106   80:31665/TCP,443:32016/TCP   74s

NAME                      READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/contour   2/2     2            2           74s

NAME                   DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR   AGE
daemonset.apps/envoy   3         3         3       3            3           <none>          74s

一切看起来都很好!这个输出突出了安装 Contour 的推荐方法,即将 Envoy 应用为 daemonset 到集群中。这意味着 Envoy 代理将在集群中的所有节点上运行。这绝对是一个很好的建议,但在某些情况下可能存在一些例外情况,除此之外您可以将 Envoy 安装为 deployment 并在那里控制副本数。

看到 Contour 在运行中

为了查看这个 Ingress 控制器起作用,让我们考虑下面的期望配置:

这里有两个不同的应用程序:App 1 和 App 2。我们希望通过负载均衡器和 Ingress 控制器导向单个端点,但我们希望 /app1 的流量通往 App 1,而 /app2 的流量通往 App 2。

但是首先,我需要创建一个快速简单的网络服务器,可以接受环境变量以自定义其输出的消息(以便我知道自己访问的是哪个应用程序):

package main

import (
    "fmt"
    "log"
    "net/http"
    "os"
)

func main() {
    fmt.Println("Starting web server...")

    message := os.Getenv("MESSAGE")
    fmt.Printf("Using message: %s
", message)

    http.HandleFunc("/", func(res http.ResponseWriter, req *http.Request) {
        fmt.Println("Request received")
        fmt.Fprintf(res, "Message: %s", message)
    })

    log.Fatal(http.ListenAndServe(":8000", nil))
}

现在我将这个打包成一个镜像:

FROM golang:1.17 AS builder
COPY . /var/app
WORKDIR /var/app
RUN ["go", "build", "-o", "app", "."]

FROM debian:bullseye
WORKDIR /var/app
COPY --from=builder /var/app/app .
CMD ["./app"]

并将其推送到我的容器注册表(在我的情况下,我使用 Azure 容器注册表):

$ docker build -t trstringeraks1.azurecr.io/basic-web-server:latest .
$ az acr login -n trstringeraks1
$ docker push trstringeraks1.azurecr.io/basic-web-server:latest

现在我将在 Kubernetes 集群中创建这些应用程序。首先是 App 1:

kind: Namespace
apiVersion: v1
metadata:
  name: app1 ---
kind: Deployment
apiVersion: apps/v1
metadata:
  name: app1
  namespace: app1
spec:
  replicas: 2
  selector:
    matchLabels:
      app: app1
  template:
    metadata:
      labels:
        app: app1
    spec:
      containers:         - name: app1
          image: trstringeraks1.azurecr.io/basic-web-server:latest
          imagePullPolicy: Always
          ports:             - containerPort: 8000
          env:             - name: MESSAGE
              value: hello from app1 ---
kind: Service
apiVersion: v1
metadata:
  name: app1
  namespace: app1
spec:
  selector:
    app: app1
  ports:     - port: 80
      targetPort: 8000

接下来让我们创建 App 2:

kind: Namespace
apiVersion: v1
metadata:
  name: app2 ---
kind: Deployment
apiVersion: apps/v1
metadata:
  name: app2
  namespace: app2
spec:
  replicas: 2
  selector:
    matchLabels:
      app: app2
  template:
    metadata:
      labels:
        app: app2
    spec:
      containers:         - name: app2
          image: trstringeraks1.azurecr.io/basic-web-server:latest
          imagePullPolicy: Always
          ports:             - containerPort: 8000
          env:             - name: MESSAGE
              value: hello from app2 ---
kind: Service
apiVersion: v1
metadata:
  name: app2
  namespace: app2
spec:
  selector:
    app: app2
  ports:     - port: 80
      targetPort: 8000

现在我们设置了应用程序,我们需要设置路由,这样我们的 Contour Ingress 控制器知道将流量发送到哪里。我们通过创建一个单一的根代理并包括另外两个代理来完成。让我们先创建我们的应用程序代理:

kind: HTTPProxy
apiVersion: projectcontour.io/v1
metadata:
  name: app1
  namespace: app1
spec:
  routes:     - services:       - name: app1
        port: 80 ---
kind: HTTPProxy
apiVersion: projectcontour.io/v1
metadata:
  name: app2
  namespace: app2
spec:
  routes:     - services:       - name: app2
        port: 80

这些内容没有太多,它们几乎只是能够被引用并将所有流量路由到其服务和端口的目标代理。真正的路由内容涉及根代理:

kind: HTTPProxy
apiVersion: projectcontour.io/v1
metadata:
  name: main
spec:
  virtualhost:
    fqdn: myapps
  includes:     - name: app1
      namespace: app1
      conditions:         - prefix: /app1     - name: app2
      namespace: app2
      conditions:         - prefix: /app2

这个根代理定义了一个虚拟主机 myapps,并包括两个应用程序的代理,条件基于前缀路由。/app1 将路由到 app1 代理,/app2 将路由到 app2 代理。创建这三个代理后,我们应该检查确保 Contour 已接受它们并且它们是有效的:

$ kubectl get proxy -A
NAMESPACE   NAME   FQDN     TLS SECRET   STATUS   STATUS DESCRIPTION
app1        app1                         valid    Valid HTTPProxy
app2        app2                         valid    Valid HTTPProxy
default     main   myapps                valid    Valid HTTPProxy

既然我们创建了所有的连接,让我们发出一个请求!首先让我们获取负载均衡器服务入口点到集群的公共 IP 地址:

$ INGRESS_IP=$(kubectl get svc -n projectcontour envoy -o jsonpath='{.status.loadBalancer.ingress[0].ip}')

最后,让我们 curl App 1:

$ curl -H "host:myapps" $INGRESS_IP/app1
Message: hello from app1

成功了!我们的请求被路由到我们的 App 1 服务和 pods。现在来验证 App 2:

$ curl -H "host:myapps" $INGRESS_IP/app2
Message: hello from app2

太棒了!一切都按预期运行。

总结

当您的 Kubernetes 集群需要接受来自外部的流量时,引入一个 Ingress 控制器来处理这个是一种典型的方法。它为您提供许多好处(成本、安全等)。Contour 是一个非常好的现代 Ingress 控制器的很好例子,性能出色且具有直观的 API。我强烈推荐使用它,希望这篇博文能帮助您了解 Contour!

© 2011 - 2025 Macro Zhao的分享站

关于我

如遇到加载502错误,请尝试刷新😄

Hi,欢迎访问 Macro Zhao 的博客。Macro Zhao(或 Macro)是我在互联网上经常使用的名字。

我是一个热衷于技术探索和分享的IT工程师,在这里我会记录分享一些关于技术、工作和生活上的事情。

我的CSDN博客:
https://macro-zhao.blog.csdn.net/

欢迎你通过评论或者邮件与我交流。
Mail Me

推荐好玩(You'll Like)
  • AI 动·画
    • 这是一款有趣·免费的能让您画的画中的角色动起来的AI工具。
    • 支持几十种动作生成。
我的项目(My Projects)
  • 爱学习网

  • 小乙日语App

    • 这是一个帮助日语学习者学习日语的App。
      (当然初衷也是为了自用😄)
    • 界面干净,简洁,漂亮!
    • 其中包含 N1 + N2 的全部单词和语法。
    • 不需注册,更不需要订阅!完全免费!
  • 小乙日文阅读器

    • 词汇不够?照样能读日语名著!
    • 越读积累越多,积跬步致千里!
    • 哪里不会点哪里!妈妈再也不担心我读不了原版读物了!
赞助我(Sponsor Me)

如果你喜欢我的作品或者发现它们对你有所帮助,可以考虑给我买一杯咖啡 ☕️。这将激励我在未来创作和分享更多的项目和技术。🦾

👉 请我喝一杯咖啡

If you like my works or find them helpful, please consider buying me a cup of coffee ☕️. It inspires me to create and share more projects in the future. 🦾

👉 Buy me a coffee