多个 docker 应用程序 运行 nginx 在多个不同的子路径

Multiple docker apps running nginx at multiple different subpaths

我正尝试在 GKE 实例中 运行 几个 Docker 应用程序,负载均衡器设置公开它们。每个应用程序都包含一个简单的 node.js 应用程序和 nginx 来为网站提供服务;一个简单的 nginx 配置通过响应 / 的位置块公开应用程序。这在本地开发时效果很好,因为我可以 运行 每个 pod 在一个单独的端口上,并只需在 127.0.0.1:8080 或类似的地址访问它们。

我遇到的问题是,在使用 GCP 负载平衡器时,虽然我可以轻松地将流量路由到 Kubernetes 服务,这样 https://example.com/ maps to my foo service/pod and https://example.com/bar 就可以转到我的 bar 服务,bar pod 响应 404,因为路径 /bar 与位置块中指定的路径不匹配。

这些 pods 的数量会增加很多,所以我不想提前手动知道每个 pod 的路径,我也不希望在我的 git 回购。

有没有一种方法可以动态定义位置块匹配的路径,例如通过环境变量,这样我就可以将其定义为我用来部署这些服务的 Helm 图表的一部分?或者是否可以匹配所有路径?这是一个可行的解决方案,还是只是提出问题?

感谢您的帮助。

只需使用 ingress。它将允许您将不同的路径映射到不同的后端 Services。在 GCP docs as well as in the official kubernetes documentation.

中都有很好的解释

典型的入口对象定义可能如下所示:

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: my-ingress
spec:
  backend:
    serviceName: my-products
    servicePort: 60001
  rules:
  - http:
      paths:
      - path: /
        backend:
          serviceName: my-products
          servicePort: 60000
      - path: /discounted
        backend:
          serviceName: my-discounted-products
          servicePort: 80
      - path: /special
        backend:
          serviceName: special-offers
          servicePort: 80
      - path: /news
        backend:
          serviceName: news
          servicePort: 80
           

当您在 GKE 上应用入口定义时,会自动创建 负载均衡器。请注意,所有 Services 都可以使用相同的标准 http 端口,您不必使用任何自定义端口。

您可能想要指定 a default backend,出现在上面的示例中(backend 部分就在 spec 下方),但它是可选的。它将确保:

Any requests that don't match the paths in the rules field are sent to the Service and port specified in the backend field. For example, in the following Ingress, any requests that don't match / or /discounted are sent to a Service named my-products on port 60001.

GKE 上使用默认 ingress controller 时可能遇到的唯一问题是暂时 它不支持重写

如果您的 nginx pods 仅在 "/" 路径上公开应用程序内容,则不支持重写根本不应该成为限制,据我所知,这适用于您的情况:

Each app comprises a simple node.js app with nginx to serve the site; a simple nginx config exposes the apps with a location block responding to /

但是,如果您决定在某个时候需要提及重写,因为例如您的一个应用程序未在 / 下公开,而是在 Pod 内公开 /bar 您可以决定部署 nginx ingress controller 这也可以在 [=110= 上很容易地完成]GKE.

所以你只会在以下场景需要它:用户访问入口IP后/foo -> 请求不仅被重定向到暴露你的 nginx Pod 的特定后端 Service,而且原始路径 (/foo) 需要重写为新路径 ( /bar) 应用程序在 Pod

中公开

更新:

Thank you for your reply. The above ingress configuration is very similar to what I've already configured forwarding /foo and /bar to different pods. The issue is that the path gets forwarded, and (after doing some more research on the issue) I believe I need to rewrite the URL that's sent to the pod, since the location / { ... } block in my nginx config won't match against the received path of /foo or /bar. – aodj Aug 14 at 9:17

嗯,你是对的。原始访问路径,例如/foo 确实被转发到目标 Pod。因此,除了将您引导至 ingress 资源 中定义的相应 backend 之外,选择 /foo 路径意味着目标 nginx 服务器 运行 在 Pod 中也必须在 /foo 路径下提供其内容。

我用 GKE ingress 验证了它,并且可以通过检查 Pod 日志来确认发送到 nginx 的 http 请求Pod 通过 /foo 路径,确实作为 /usr/share/nginx/html/foo 的请求到达 Pod,同时它在 / 下提供其内容,而不是来自 [=42] 的 /foo =].因此,请求目标服务器上不存在的内容不可避免地会导致 404 Error.

正如我之前提到的,GKE 上可用的默认入口控制器不支持重写,因此如果您出于某种原因想要使用它,请重新配置您的目标 nginx 服务器 似乎是让它工作的唯一解决方案。

幸运的是,我们还有另一个选择,即 nginx ingress controller。它支持重写,因此可以轻松解决我们的问题。我们可以通过 运行 以下两个命令将其部署在我们的 GKE 集群 上:

kubectl create clusterrolebinding cluster-admin-binding \
  --clusterrole cluster-admin \
  --user $(gcloud config get-value account)

kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v0.34.1/deploy/static/provider/cloud/deploy.yaml

是的,就这么简单!可以在official docs.

中仔细查看安装过程

然后我们可以应用下面的ingress资源定义:

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  annotations:
    kubernetes.io/ingress.class: "nginx"
    nginx.ingress.kubernetes.io/rewrite-target: /
  name: rewrite
  namespace: default
spec:
  rules:
  - http:
      paths:
      - backend:
          serviceName: nginx-deployment-1
          servicePort: 80
        path: /foo(/|$)(.*)
      - backend:
          serviceName: nginx-deployment-2
          servicePort: 80
        path: /bar(/|$)(.*)

请注意,我们使用 kubernetes.io/ingress.class: "nginx" 注释 select 我们新部署的 nginx-ingress 控制器 来处理这个 入口资源 而不是默认的 GKE-ingress 控制器.

使用的重写将确保在到达目标 nginx Pod 之前重写原始访问路径。因此,由 nginx-deployment-1nginx-deployment-2 Services 公开的两组 Pods"/".

下提供它们的内容是完全没问题的

如果您想自己快速检查它是如何工作的,可以使用以下 Deployments:

nginx-deployment-1.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment-1
  labels:
    app: nginx-1
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx-1
  template:
    metadata:
      labels:
        app: nginx-1
    spec:
      initContainers:
      - name: init-myservice
        image: nginx:1.14.2
        command: ['sh', '-c', "echo DEPLOYMENT-1 > /usr/share/nginx/html/index.html"]
        volumeMounts:
        - mountPath: /usr/share/nginx/html
          name: cache-volume
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80
        volumeMounts:
        - mountPath: /usr/share/nginx/html
          name: cache-volume
      volumes:
      - name: cache-volume
        emptyDir: {}

nginx-deployment-2.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment-2
  labels:
    app: nginx-2
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx-2
  template:
    metadata:
      labels:
        app: nginx-2
    spec:
      initContainers:
      - name: init-myservice
        image: nginx:1.14.2
        command: ['sh', '-c', "echo DEPLOYMENT-2 > /usr/share/nginx/html/index.html"]
        volumeMounts:
        - mountPath: /usr/share/nginx/html
          name: cache-volume
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80
        volumeMounts:
        - mountPath: /usr/share/nginx/html
          name: cache-volume
      volumes:
      - name: cache-volume
        emptyDir: {}

并通过 Services 公开它们 运行:

kubectl expose deployment nginx-deployment-1 --type NodePort --target-port 80 --port 80
kubectl expose deployment nginx-deployment-2 --type NodePort --target-port 80 --port 80

您甚至可以省略 --type NodePort,因为 nginx-ingress 控制器也接受 ClusterIP Services.