在不同命名空间中为k8s配置Ingress

101
我需要在Azure k8s上配置Ingress Nginx,我的问题是是否可能将Ingress配置在一个命名空间(例如ingress-nginx),而将一些服务放置在其他命名空间(例如资源)?
我的文件看起来像这样:
# ingress-nginx.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: nginx-ingress-controller
  namespace: ingress-nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: ingress-nginx
  template:
    metadata:
      labels:
        app: ingress-nginx
      annotations:
        prometheus.io/port: '10254'
        prometheus.io/scrape: 'true' 
    spec:
      containers:
        - name: nginx-ingress-controller
          image: quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.12.0
          args:
            - /nginx-ingress-controller
            - --default-backend-service=$(POD_NAMESPACE)/default-http-backend
            - --configmap=$(POD_NAMESPACE)/nginx-configuration
            - --tcp-services-configmap=$(POD_NAMESPACE)/tcp-services
            - --udp-services-configmap=$(POD_NAMESPACE)/udp-services
            - --annotations-prefix=nginx.ingress.kubernetes.io
            - --publish-service=$(POD_NAMESPACE)/ingress-nginx
          env:
            - name: POD_NAME
              valueFrom:
                fieldRef:
                  fieldPath: metadata.name
            - name: POD_NAMESPACE
              valueFrom:
                fieldRef:
                  fieldPath: metadata.namespace
          ports:
          - name: http
            containerPort: 80
          - name: https
            containerPort: 443
          livenessProbe:
            failureThreshold: 3
            httpGet:
              path: /healthz
              port: 10254
              scheme: HTTP
            initialDelaySeconds: 10
            periodSeconds: 10
            successThreshold: 1
            timeoutSeconds: 1
          readinessProbe:
            failureThreshold: 3
            httpGet:
              path: /healthz
              port: 10254
              scheme: HTTP
            periodSeconds: 10
            successThreshold: 1
            timeoutSeconds: 1
# configmap.yaml
kind: ConfigMap
apiVersion: v1
metadata:
  name: nginx-configuration
  namespace: ingress-nginx
  labels:
    app: ingress-nginx
---
kind: ConfigMap
apiVersion: v1
metadata:
  name: tcp-services
  namespace: ingress-nginx
---
kind: ConfigMap
apiVersion: v1
metadata:
  name: udp-services
  namespace: ingress-nginx
---
# default-backend.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: default-http-backend
  labels:
    app: default-http-backend
  namespace: ingress-nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      app: default-http-backend
  template:
    metadata:
      labels:
        app: default-http-backend
    spec:
      terminationGracePeriodSeconds: 60
      containers:
      - name: default-http-backend
        # Any image is permissible as long as:
        # 1. It serves a 404 page at /
        # 2. It serves 200 on a /healthz endpoint
        image: gcr.io/google_containers/defaultbackend:1.4
        livenessProbe:
          httpGet:
            path: /healthz
            port: 8080
            scheme: HTTP
          initialDelaySeconds: 30
          timeoutSeconds: 5
        ports:
        - containerPort: 8080
        resources:
          limits:
            cpu: 10m
            memory: 20Mi
          requests:
            cpu: 10m
            memory: 20Mi
---
apiVersion: v1
kind: Service
metadata:
  name: default-http-backend
  namespace: ingress-nginx
  labels:
    app: default-http-backend
spec:
  ports:
  - port: 80
    targetPort: 8080
  selector:
    app: default-http-backend

kind: Service
apiVersion: v1
metadata:
  name: ingress-nginx
  namespace: ingress-nginx
  labels:
    app: ingress-nginx
spec:
  externalTrafficPolicy: Local
  type: LoadBalancer
  selector:
    app: ingress-nginx
  ports:
  - name: http
    port: 80
    targetPort: http
  - name: https
    port: 443
    targetPort: https
        # app-ingress.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: app-ingress
  namespace: ingress-nginx
  annotations:
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  tls:
    - hosts:
      - api-sand.fake.com
  rules:
  - host: api-sand.fake.com
    http:
      paths:
      - backend:
          serviceName: api-sand
          servicePort: 80
        path: /

然后我有一些运行在资源命名空间中的应用程序,问题在于我遇到了以下错误:

error obtaining service endpoints: error getting service resources/api-sand from the cache: service resources/api-sand was not found

如果我将 api-sand 部署在与 Ingress 相同的命名空间中,那么该服务就能正常工作。


它被称为“扇出”入口(例如,请参见此GCP示例)。 - mirekphd
6个回答

181

我想为那些相对新于Kubernetes及其入口选项的人简化答案。有两个独立的东西需要存在才能使入口(es)工作:

  1. 入口控制器:一个单独的 DaemonSet (在所有节点上运行的控制器,包括任何未来的节点),以及可以用于利用路由和代理的 Service 。 它基于例如NGINX,它作为老派反向代理接收传入流量,并将其转发到下面第2点中定义的 Ingress 资源中定义的HTTP(S)路由(通过其不同的路由/ URL进行区分);
  2. 入口规则:具有 kind:Ingress 的单独Kubernetes资源。仅在已经 deployed该节点上的入口控制器时才会生效。

尽管Ingress控制器可以部署在任何命名空间中,但通常会在与您的应用服务分开的命名空间中部署它(例如ingresskube-system)。它可以查看所有其他命名空间中的Ingress规则并拾取它们。但是,每个Ingress规则必须驻留在配置它们的应用所在的命名空间中。

虽然有一些解决方法,但这是最常见的方法。


2
好的回答:我可以问一下,在创建Ingress规则时,它们是否都需要在单个Ingress规则中定义,还是可以在每个命名空间中有多个Ingress规则,每个规则映射到该命名空间中的一个服务?谢谢。 - Choco
1
嗯,这并不像网络策略那么简单。并不是说网络策略很简单,但有一种相对简单的方法可以合并它们。对于Ingress来说,情况就没有那么简单了,因为很难标准化底层的Ingress Controller实现,特别是因为它们是建立在完全独立的组件之上的,这些组件早在Kubernetes出现之前就已经存在了,比如Nginx。话虽如此,你不是第一个考虑这个想法的人,@Choco,他也有类似的想法:https://github.com/kubernetes/ingress-nginx/issues/1539 - yuranos
5
如果我有100个微服务,它们应该通过1个主机名(和不同的路径)可访问,那么我必须将所有这些服务放在同一个命名空间中,以便它们可以使用相同的Ingress资源吗? - Marc
1
在这种情况下,您需要设计更先进的网络架构。也许它将包括多个级别的入口/代理/网关,或者使用ExternalName服务。这是一个真实的例子还是一个假设的例子? - yuranos
1
嘿@yuranos,你的回答看起来很棒,我很感激你如何针对“相对新手使用Kubernetes”的人进行了解释。但是当你说“应用程序必须驻留在配置它们的应用程序所在的命名空间中”时,是否可以扩展一下你所说的“应用程序”是什么意思?因为没有叫做“应用程序”的东西,这是否意味着“服务”,“部署”或所有这些内容?谢谢(来自一个相对新手!) - Andy Lorenz
显示剩余4条评论

76

不要在ingress-nginx命名空间中创建app-ingress,而应该在您拥有api-sand服务和Pod的命名空间中创建它。

或者,您可以通过externalName在一个命名空间中实现Ingress,在另一个命名空间中实现服务。请查看Kubernetes跨命名空间Ingress网络

这里是一个示例,参考自这里

kind: Service
apiVersion: v1
metadata:
  name: my-service
spec:
  type: ExternalName
  externalName: test-service.namespacename.svc.cluster.local

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: example-ingress
  annotations:
    kubernetes.io/ingress.class: "nginx"
spec:
  rules:
  - host: example.com
    http:
      paths:
      - path: /
        backend:
          serviceName: my-service
          servicePort: 80

你是怎么让它工作的?我一直收到 service "default/my-service" is type "ExternalName", expected "NodePort" or "LoadBalancer"; 的错误信息。 - Lukas
1
我也无法让它工作。我在想这是不是提供商和/或版本问题?我使用的是EKS 1.14... - metasim
1
在描述入口时出现(<error: endpoints "scc-worker-service" not found>)错误。有什么线索可以解决吗? - Damith Udayanga
如果您阅读示例链接,他们只讨论Ngnix Plus。 - YoShade
1
这个特定于任何入口控制器吗?似乎这种方法对AWS ALB控制器不起作用。 - raghu_manne
关于“在不同命名空间中为k8s配置Ingress”怎么样?(我想这是标题) - undefined

12

实际上是可以做到的,您可以在A命名空间中定义入口和“ExternalName”类型的服务,而“ExternalName”指向B命名空间中服务的DNS。有关更多详细信息,请参阅此答案:https://dev59.com/s1QK5IYBdhLWcg3wbfSW#51899301


6

外部流量经由入口控制器服务进入,该服务负责根据定义的路由规则或称为k8s世界中的ingress规则进行流量路由。

换句话说,ingress资源只是路由规则(可以类比于DNS记录),因此当您定义一个ingress资源时,您只是为ingress控制器定义了一个规则,以便基于这些定义的规则进行流量路由。

解决方案:

  1. 由于Ingress只是路由规则,因此您可以在集群中的任何位置(任何命名空间)定义此类规则,控制器应该会捕获到它们,并根据其监视的资源创建情况做出反应。

    以下是使用kubectl轻松创建ingress的方法:

    kubectl create ingress <name> -n 命名空间名称 --rule="host/prefix=服务名称:端口号"

    注意:将--dry-run=client -oyaml添加到生成yaml清单文件中

  2. 或者,您可以在定义ingress的同一命名空间中创建一个ExternalName类型的服务。这样的外部服务可以指向任何URL(位于命名空间外或甚至k8s群集外部的服务)。

    以下是使用kubectl创建ExternalName服务的示例:

    kubectl create service externalname ingress-ns -n 命名空间名称 --external-name=服务名称.命名空间.svc.cluster.local --tcp=80:80 --dry-run=client -oyaml

这将生成类似于以下内容的东西:

kind: Service
apiVersion: v1
metadata:
  name: nginx
  namespace: ingress-ns
spec:
  type: ExternalName
  externalName: serviceName.namespace.svc.cluster.local #or any external svc
  ports:
  - port: 80 #specify the port of service you want to expose 
    targetPort: 80 #port of external service 

如上所述,创建如下的入口点: kubectl create ingress <name> -n namespaceName --rule="host/prefix=serviceName:portNumber" 注意: 添加 --dry-run=client -oyaml 以生成 yaml 的清单文件。

1
我用的方法是为每个命名空间创建Ingress。

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-service
  namespace: production
  annotations:
    kubernetes.io/ingress.class: nginx
spec:
  rules:
    - host: api.youtube.com
      http:
        paths:
          - pathType: Prefix
            path: "/api/users"
            backend:
              service:
                name: youtube-srv
                port:
                  number: 3000


apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-service
  namespace: development
  annotations:
    kubernetes.io/ingress.class: nginx
spec:
  rules:
    - host: dev.youtube.com
      http:
        paths:
          - pathType: Prefix
            path: "/api/users"
            backend:
              service:
                name: youtube-srv
                port:
                  number: 3000

1
如果入口是云中的负载均衡器,则为每个命名空间拥有独立的负载均衡器会花费更多的费用。 - tom10271

-2

虽然文档说通常在Ingress控制器级别配置默认后端,但是有一种配置方式可以针对每个Ingress资源进行配置。

例如:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: myingress
  namespace: myns
spec: 
  defaultBackend:
    service:
      name: default-http-backend
      port: 
        number: 80
...

在此处,default-http-backend必须与ingress资源在同一命名空间中定义。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接