使用 Traefik 指引 Kubernetes 流量

关于如何将流量引入 Kubernetes-Raspberry Pi 集群的分步演练。
160 位读者喜欢这篇文章。
Digital creative of a browser on the internet

在本文中,我们将部署几个简单的网站,并学习如何使用 Traefik 将来自外部世界的流量引入我们的集群。之后,我们还将学习如何删除 Kubernetes 资源。让我们开始吧!

所需材料

要跟随本文进行操作,您只需要我们在之前文章中构建的 k3s Raspberry Pi 集群。由于您的集群将从 Web 上拉取镜像,因此集群需要能够访问互联网。

本文将展示一些配置文件和示例 HTML 文件以进行解释。所有示例文件都可以在此处下载。

部署一个简单的网站

之前,我们使用 kubectl 进行了直接部署。然而,这不是部署事物的典型方式。通常,使用 YAML 配置文件,这正是我们将在本文中使用的。我们将从顶层开始,并采用自顶向下的方法创建配置文件。

Deployment 配置

首先是 Deployment 配置。配置如下所示,随后是解释。我通常使用 Kubernetes 文档 中的示例作为起点,然后修改它们以满足我的需求。例如,以下配置是在从 deployment 文档 中复制示例后修改的。

创建一个文件,mysite.yaml,内容如下

apiVersion: apps/v1
kind: Deployment
metadata:
  name: mysite-nginx
  labels:
    app: mysite-nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      app: mysite-nginx
  template:
    metadata:
      labels:
        app: mysite-nginx
    spec:
      containers:
      - name: nginx
        image: nginx
        ports:
        - containerPort: 80

大部分是样板代码。重要的部分是,我们将 Deployment 命名为 mysite-nginx,并带有 app 标签 mysite-nginx。我们指定想要一个 replica,这意味着只会创建一个 Pod。我们还指定了 一个容器,我们将其命名为 nginx。我们指定的 imagenginx。这意味着,在部署时,k3s 将从 DockerHub 下载 nginx 镜像并从中创建一个 Pod。最后,我们指定了 containerPort80,这仅仅意味着在容器内部,Pod 将监听端口 80

我上面强调了“在容器内部”,因为这是一个重要的区别。正如我们配置的容器一样,它只能在容器内部访问,并且进一步限制在内部网络中。这对于允许多个容器监听相同的容器端口是必要的。换句话说,使用此配置,一些其他 Pod 也可以监听其容器端口 80,而不会与此 Pod 冲突。为了提供对该 Pod 的正式访问,我们需要一个 Service 配置。

Service 配置

在 Kubernetes 中,Service 是一种抽象。它提供了一种访问 Pod 或一组 Pod 的方法。连接到 Service,Service 会路由到单个 Pod 或负载均衡到多个 Pod(如果定义了多个 Pod 副本)。

Service 可以在同一个配置文件中指定,这正是我们在此处要做的。使用 --- 分隔配置区域。将以下内容添加到 mysite.yaml

---
apiVersion: v1
kind: Service
metadata:
  name: mysite-nginx-service
spec:
  selector:
    app: mysite-nginx
  ports:
    - protocol: TCP
      port: 80

在此配置中,我们将 Service 命名为 mysite-nginx-service。我们提供了 selectorapp: mysite-nginx。这就是 Service 如何选择它路由到的应用程序容器。请记住,我们为容器提供了 app 标签,即 mysite-nginx。这就是 Service 将用于查找我们的容器的内容。最后,我们指定 Service 协议为 TCP,Service 监听端口 80

Ingress 配置

Ingress 配置指定如何将流量从集群外部引入到集群内部的 Service。请记住,k3s 预配置了 Traefik 作为 Ingress 控制器。因此,我们将编写特定于 Traefik 的 Ingress 配置。将以下内容添加到 mysite.yaml(并且不要忘记使用 --- 分隔)

---
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: mysite-nginx-ingress
  annotations:
    kubernetes.io/ingress.class: "traefik"
spec:
  rules:
  - http:
      paths:
      - path: /
        backend:
          serviceName: mysite-nginx-service
          servicePort: 80

在此配置中,我们将 Ingress 记录命名为 mysite-nginx-ingress。我们通过 kubernetes.io/ingress.class 注解告诉 Kubernetes 我们期望 traefik 成为我们的 Ingress 控制器。

rules 部分,我们基本上是在说,当 http 流量进入时,并且 path/(或其下的任何内容)匹配时,将其路由到由 serviceName mysite-nginx-service 指定的 backend Service,并将其路由到 servicePort 80。这会将传入的 HTTP 流量连接到我们之前定义的 Service。

要部署的内容

这就是配置方面的全部内容。如果我们现在部署,我们将获得默认的 nginx 页面,但这并不是我们想要的。让我们创建一些简单但自定义的内容来部署。创建文件 index.html,内容如下

<html>
<head><title>K3S!</title>
  <style>
    html {
      font-size: 62.5%;
    }
    body {
      font-family: sans-serif;
      background-color: midnightblue;
      color: white;
      display: flex;
      flex-direction: column;
      justify-content: center;
      height: 100vh;
    }
    div {
      text-align: center;
      font-size: 8rem;
      text-shadow: 3px 3px 4px dimgrey;
    }
  </style>
</head>
<body>
  <div>Hello from K3S!</div>
</body>
</html>

我们尚未介绍 Kubernetes 中的存储机制,因此我们将稍微作弊一下,只是将此文件存储在 Kubernetes ConfigMap 中。这不是部署网站的推荐方法,但它适用于我们的目的。运行以下命令

kubectl create configmap mysite-html --from-file index.html

此命令从本地文件 index.html 创建一个名为 mysite-htmlconfigmap 资源。这本质上是将文件(或一组文件)存储在 Kubernetes 资源中,我们可以在配置中调用它。它通常用于存储配置文件(因此得名),因此我们在这里稍微滥用它。在以后的文章中,我们将讨论 Kubernetes 中合适的存储解决方案。

创建 ConfigMap 后,让我们将其挂载到我们的 nginx 容器内部。我们分两步完成此操作。首先,我们需要指定一个 volume,调用 ConfigMap。然后,我们需要将卷挂载到 nginx 容器中。通过在 spec 标签下(紧随 containers 之后)在 mysite.yaml 中添加以下内容来完成第一步

      volumes:
      - name: html-volume
        configMap:
          name: mysite-html

这告诉 Kubernetes 我们想要定义一个 volume,名称为 html-volume,并且该卷应包含名为 html-volumeconfigMap 的内容(我们在上一步中创建了它)。

接下来,在 nginx 容器规范中,紧随 ports 之后,添加以下内容

        volumeMounts:
        - name: html-volume
          mountPath: /usr/share/nginx/html

这告诉 Kubernetes,对于 nginx 容器,我们想要将名为 html-volumevolume 挂载到路径(在容器中) /usr/share/nginx/html。为什么是 /usr/share/nginx/html?这是 nginx 镜像提供 HTML 服务的位置。通过在该路径上挂载我们的卷,我们用我们的卷内容替换了默认内容。

作为参考,配置文件的 deployment 部分现在应如下所示

apiVersion: apps/v1
kind: Deployment
metadata:
  name: mysite-nginx
  labels:
    app: mysite-nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      app: mysite-nginx
  template:
    metadata:
      labels:
        app: mysite-nginx
    spec:
      containers:
      - name: nginx
        image: nginx
        ports:
        - containerPort: 80
        volumeMounts:
        - name: html-volume
          mountPath: /usr/share/nginx/html
      volumes:
      - name: html-volume
        configMap:
          name: mysite-html

部署它!

现在我们准备好部署了!我们可以使用以下命令进行部署

kubectl apply -f mysite.yaml

您应该看到类似于以下内容

deployment.apps/mysite-nginx created
service/mysite-nginx-service created
ingress.networking.k8s.io/mysite-nginx-ingress created

这意味着 Kubernetes 为我们指定的三个配置中的每一个都创建了资源。使用以下命令检查 Pod 的状态

kubectl get pods

如果您看到 ContainerCreating 状态,请稍等片刻,然后再次运行 kubectl get pods。通常,第一次需要一段时间,因为 k3s 必须下载 nginx 镜像才能创建 Pod。稍等片刻后,您应该获得 Running 状态。

试试看!

一旦 Pod 正在运行,就可以尝试了。打开浏览器并在地址栏中键入 kmaster

恭喜!您已在 k3s 集群上部署了一个网站!

再来一个

现在我们有一个完整的 k3s 集群运行一个网站。但是我们可以做更多的事情!如果我们还有另一个想要在同一集群上提供的网站怎么办?让我们看看如何做到这一点。

同样,我们需要部署一些东西。碰巧我的狗有一些信息想让全世界知道已经有一段时间了。因此,我专门为她制作了一些 HTML(可从示例 zip 文件中获得)。同样,我们将使用 ConfigMap 技巧来托管我们的 HTML。这次我们将把整个目录(html 目录)放入 ConfigMap 中,但调用方式是相同的。

kubectl create configmap mydog-html --from-file html

现在我们需要为此站点创建一个配置文件。它几乎与 mysite.yaml 的配置文件完全相同,因此首先将 mysite.yaml 复制到 mydog.yaml。现在编辑 mydog.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: mydog-nginx
  labels:
    app: mydog-nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      app: mydog-nginx
  template:
    metadata:
      labels:
        app: mydog-nginx
    spec:
      containers:
      - name: nginx
        image: nginx
        ports:
        - containerPort: 80
        volumeMounts:
        - name: html-volume
          mountPath: /usr/share/nginx/html
      volumes:
      - name: html-volume
        configMap:
          name: mydog-html
---
apiVersion: v1
kind: Service
metadata:
  name: mydog-nginx-service
spec:
  selector:
    app: mydog-nginx
  ports:
    - protocol: TCP
      port: 80
---
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: mydog-nginx-ingress
  annotations:
    kubernetes.io/ingress.class: "traefik"
    traefik.frontend.rule.type: PathPrefixStrip
spec:
  rules:
  - http:
      paths:
      - path: /mydog
        backend:
          serviceName: mydog-nginx-service
          servicePort: 80

我们可以通过简单地搜索和替换 mysitemydog 来完成大部分编辑。另外两个编辑在 Ingress 部分。我们将 path 更改为 /mydog,并添加了一个注解 traefik.frontend.rule.type: PathPrefixStrip

路径 /mydog 的规范指示 Traefik 将任何请求路径以 /mydog 开头的传入请求路由到 mydog-nginx-service。任何其他路径将继续路由到 mysite-nginx-service

新的注解 PathPrefixStrip 告诉 Traefik 在将请求发送到 mydog-nginx-service 之前剥离前缀 /mydog。我们这样做是因为 mydog-nginx 应用程序不期望前缀。这意味着我们可以通过简单地更改 Ingress 记录中的前缀来更改 Service 的挂载位置。

现在我们可以像以前一样部署

kubectl apply -f mydog.yaml

现在,我狗的消息应该可以在 http://kmaster/mydog/ 上访问。

呼!消息发出去了!也许我们今晚都可以睡个好觉了。

现在,我们有一个 k3s 集群托管两个网站,Traefik 根据路径名决定将请求传递给哪个 Service!但是,我们不仅限于基于路径的路由。我们也可以使用基于主机名的路由,我们将在以后的文章中探讨这一点。

此外,我们刚刚托管的网站是标准的未加密 HTML 网站。现在的一切都使用 SSL/TLS 加密。在我们的下一篇文章中,我们还将为我们的 k3s 集群添加支持,以托管 SSL/TLS HTTPS 网站!

清理

在您离开之前,由于本文主要处理示例站点,我想向您展示如何删除不需要保留在集群上的示例。

对于大多数配置,您可以通过使用您部署时使用的相同配置文件运行 delete 命令来撤消配置。因此,让我们清理 mysitemydog

kubectl delete -f mysite.yaml
kubectl delete -f mydog.yaml

由于我们是手动创建的 ConfigMap,因此我们还需要手动删除它们。

kubectl delete configmap mysite-html
kubectl delete configmap mydog-html

现在,如果我们执行 kubectl get pods,我们应该看到我们的 nginx Pod 不再存在。

$ kubectl get pods
No resources found in default namespace.

一切都已清理干净。

请在下面的评论中告诉我您对该项目的想法。

接下来阅读什么
User profile image.
Lee 首先是一位基督徒、丈夫和父亲,其次是一位软件工程师,内心深处是一位修补匠/创客。

3 条评论

你好 Lee!

非常感谢您提供的精彩教程,当您是 Kubernetes 新手时,它很容易理解,并且是少数几个开箱即用的教程之一。

请多多提供此类教程!

来自德国的问候,

Fabian

Lee,感谢您撰写了一篇易于理解的优秀文章。

如果我遗漏了什么,请原谅,但我想知道 Traefik 控制器本身在哪里?我看到您已经创建了 Ingress 资源。

非常好的示例,但无法让这个 Traefik 示例在 kind(https://kind.kubernetes.ac.cn/) 中工作。external-ip 列始终处于 "" 状态。如有任何提示,将不胜感激。

Creative Commons License本作品根据 Creative Commons Attribution-Share Alike 4.0 International License 获得许可。
© . All rights reserved.