是否可以在 kubernetes 上为 nginx 入口控制器配置两个外部 IP 地址

是否可以在 kubernetes 上为 nginx 入口控制器配置两个外部 IP 地址

我想根据命名空间(dev 或 prod)的环境提供两个外部 IP。我的想法是在 dev 和 prod 命名空间上托管相同的 fqdns。例如,如果客户端解析www.mywebsite.lan在 192.168.171.1 上,那么他正在开发环境中工作,如果他在 192.168.171.10 上解析,那么他的目标是生产。

请注意,我仍然是 k8s 的初学者,并且我认为我犯了一些错误或误解。

我已经建立了两个 metallb IP 池,每个池都有单独的 IP 地址:

NAME              AUTO ASSIGN   AVOID BUGGY IPS   ADDRESSES
ac3-dev-ippool    true          false             ["192.168.171.1/32"]
ac3-prod-ippool   true          false             ["192.168.171.10/32"]

我有一个 nginx 入口控制器(默认配置,除处于集群模式的 externalTrafficPolicy 和启用了 appProtocol https 外):

NAME                                        READY   STATUS    RESTARTS      AGE     IP           NODE       NOMINATED NODE   READINESS GATES
ingress-nginx-controller-68fb8cf9cc-vkbfz   1/1     Running   2 (34h ago)   5d23h   10.244.3.2   ktux-130   <none>           <none>

以及 prod 和 dev 命名空间上的两个入口:

$ k get ingress -n ac3
NAME      CLASS   HOSTS               ADDRESS         PORTS     AGE
ingress   nginx   www.mywebsite-prod.lan   192.168.171.1   80, 443   19h
$ k get ingress -n ac3-dev
NAME      CLASS   HOSTS               ADDRESS         PORTS     AGE
ingress   nginx   www.mywebsite-dev.lan   192.168.171.1   80, 443   13d

但是,正如您所注意到的,两个入口都具有相同的 IP 地址 (192.168.171.1),我不明白为什么。请注意,这样可以正常工作,但我希望能够重命名虚拟主机以同时为两者提供服务www.mywebsite.lan

以下是入口配置:

(...)
metadata:
  name: ingress
  namespace: ac3  
  annotations:
    metallb.universe.tf/address-pool: ac3-prod-ippool
    kubernetes.io/ingress.class: nginx
spec:
  ingressClassName: nginx
  tls:
    - secretName: tls-certs-mywebsite-prod
      hosts:
        - "www.mywebsite-prod.lan"
  rules:
    - host: "www.mywebsite-prod.lan"
      http:
(...)

---
(...)
metadata:
  name: ingress
  namespace: ac3-dev
  annotations:
    metallb.universe.tf/address-pool: ac3-dev-ippool
    kubernetes.io/ingress.class: nginx
spec:
  ingressClassName: nginx
  tls:
    - secretName: tls-certs-mywebsite-dev
      hosts:
        - "www.mywebsite-dev.lan"
  rules:
    - host: "www.mywebsite-dev.lan"
      http:
(...)

令我不安的是,入口控制器仅从 metallb 获取一个 IP 地址:

$ k get svc -n ingress-nginx ingress-nginx-controller -oyaml | tail -5
  type: LoadBalancer
status:
  loadBalancer:
    ingress:
    - ip: 192.168.171.1

我认为它会获得两个 IP 地址,每个范围一个?

nginx ingress controller 服务是默认服务:

apiVersion: v1
kind: Service
metadata:
  labels:
    app.kubernetes.io/component: controller
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.8.1
  name: ingress-nginx-controller-admission
  namespace: ingress-nginx
spec:
  ports:
  - appProtocol: https
    name: https-webhook
    port: 443
    targetPort: webhook
  selector:
    app.kubernetes.io/component: controller
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
  type: ClusterIP

所以现在我的想法是创建两个入口控制器,一个在命名空间 ac3 上,另一个在 ac3-dev 上?但我想知道是否还有其他方法,更简单、更“符合 k8s 标准”来实现我的目标。

所以,k8s 专家们,您能给我建议或者告诉我我的错误在哪里吗?

谢谢

答案1

与您的 Ingress 资源关联的 IP 地址是来自入口控制器;它并不是 Ingress 本身所特有的东西。

您正在运行 nginx 入口控制器,因此在某处您有一个与ingress-nginx-controller-68fb8cf9cc-vkbfzpod 关联的 LoadBalancer 类型的服务。分配给该服务的地址就是您将看到的由该入口控制器提供服务的所有入口资源的地址。

metallb.universe.tf/address-pool在 Ingress 上设置注释没有任何意义;这仅对服务资源有意义。

如果您想要在多个地址上公开 Ingress,则需要通过创建多个服务资源(每个地址一个)为您的入口控制器分配多个地址。

一个例子

我正在使用 Traefik 入口控制器运行 k3s。在这个例子中,我有一个部署:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: example
spec:
  template:
    spec:
      containers:
        - name: whoami
          image: docker.io/traefik/whoami:latest
          ports:
            - name: http
              containerPort: 80

服务:

apiVersion: v1
kind: Service
metadata:
  name: example
spec:
  ports:
  - name: http
    port: 80
    targetPort: http

最后是 Ingress:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: example
spec:
  rules:
  - host: example.k3s.virt
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: example
            port:
              name: http

开箱即用,在kube-system命名空间中,我有:

$ kubectl -n kube-system get service traefik
NAME             TYPE           CLUSTER-IP      EXTERNAL-IP       PORT(S)                      AGE
traefik          LoadBalancer   10.43.215.182   192.168.124.201   80:32271/TCP,443:32683/TCP   15d

您可以看到该服务已分配地址192.168.124.201;这就是我们所说的“内部”地址池。查看我的示例应用程序,我们可以在 Ingress 资源中看到来自上述服务的 IP 地址:

$ kubectl get ingress
NAME      CLASS     HOSTS              ADDRESS           PORTS   AGE
example   traefik   example.k3s.virt   192.168.124.201   80      22m

我可以通过给定的地址访问该应用程序:

$ curl -s http://192.168.124.201 -H host:example.k3s.virt
Hostname: example-758b5cd967-5gk7h
...

我有一个使用范围的“公共”池192.168.126.0/24;为了在该网络上公开入口,我将在与入口控制器相同的命名空间中创建一个新服务:

apiVersion: v1
kind: Service
metadata:
  annotations:
    metallb.universe.tf/address-pool: public
  name: traefik-public
spec:
  allocateLoadBalancerNodePorts: true
  internalTrafficPolicy: Cluster
  ports:
  - name: web
    port: 80
    targetPort: web
  - name: websecure
    port: 443
    targetPort: websecure
  selector:
    app.kubernetes.io/instance: traefik-kube-system
    app.kubernetes.io/name: traefik
  type: LoadBalancer

我们可以看到它从“公共”池中分配了一个地址:

$ kubectl -n kube-system get service traefik traefik-public
NAME             TYPE           CLUSTER-IP      EXTERNAL-IP       PORT(S)                      AGE
traefik          LoadBalancer   10.43.215.182   192.168.124.201   80:32271/TCP,443:32683/TCP   15d
traefik-public   LoadBalancer   10.43.30.212    192.168.126.11    80:32452/TCP,443:32082/TCP   13m

现在我的应用程序可以访问两个都地址:

$ curl -s http://192.168.124.201 -H host:example.k3s.virt
Hostname: example-758b5cd967-5gk7h
...
$ curl -s http://192.168.126.11 -H host:example.k3s.virt
Hostname: example-758b5cd967-5gk7h
...

答案2

您遵循的过程是正确的,但需要进行一些修改。Metallb IP 池因环境prod而异dev。Nginx Ingress Controller 仅从 metal lb 获取一个 IP 地址,但您希望根据环境为同一主机名设置不同的 IP 地址。

由于 Nginx 控制器部署在单个命名空间中,metal lb 仅向控制器分配一个 IP 地址,这是预期行为。此外,入口资源具有不同的主机名(“www.mywebsite-prod.lan”和“www.mywebsite-dev.lan”),但它们指向相同的 IP 地址,这就是您没有获得预期行为的原因。

您可以尝试按照以下步骤解决该问题:

  1. 尝试动态主机解析您不需要为两个环境指定不同的主机名,而可以使用相同的主机名(“www.mywebsite.lan”)。

  2. 使用 Kubernetes 功能配置图或者环境变量确定应用程序内的环境。然后,您可以根据环境解析服务的 IP 地址。

  3. 在 Nginx Config 中,根据主机名动态地将流量路由到与不同 IP 地址关联的不同后端服务。

  4. 对于每个环境,创建指向不同后端服务的单独 kubernetes 服务。确保每个服务都使用来自 metallb 的适当 IP 池。

例如,‘ingress-nginx’命名空间中有一个 Nginx Ingress Controller,其中包含两个 kubernetes 服务,每个服务都有从 Metallb 池分配的自己的 IP 地址。在 Nginx 配置中,根据主机名将流量路由到适当的服务。

相关内容