2.12 Ingress

2.12 Ingress #

Service 的负载均衡功能有限,只能够依据 IP 地址和端口号做一些简单的判断和组合,而更多的高级路由条件,比如主机名、URI、请求头、证书等 Service 无法实现。Service 还有一个缺点,它比较适合代理集群内部的服务。如果想要把服务暴露到集群外部,就只能使用 NodePort 或者 LoadBalancer 这两种方式,而它们缺乏足够的灵活性,难以管控。

Ingress 对象可以作为流量的总入口,统管集群的进出口数据,“扇入” “扇出” 流量(也就是常说的 “南北向”),让外部用户能够安全、顺畅、便捷地访问内部服务。

2.12.1 Ingress Controller & Class #

Service 本身是没有服务能力的,它只是一些 iptables 规则,真正配置、应用这些规则的实际上是节点里的 kube-proxy 组件。如果没有 kube-proxy,Service 定义得再完善也没有用。

Ingress 只是一些 HTTP 路由规则的集合,相当于一份静态的描述文件,真正要把这些规则在集群里实施运行,需要的是 Ingress Controller,它的作用就相当于 Service 的 kube-proxy,能够读取、应用 Ingress 规则,处理、调度流量。

由于 Ingress Controller 与上层业务联系密切,所以 Kubernetes 把 Ingress Controller 的实现交给了社区,只要遵守 Ingress 规则,任何人都可以开发 Ingress Controller。在众多 Ingress Controller 中,Nginx 公司开发实现 Ingress Controller 是最多使用的。

最初 Kubernetes 的构想是,一个集群里有一个 Ingress Controller,再给它配上许多不同的 Ingress 规则,应该就可以解决请求的路由和分发问题了。但随着 Ingress 在实践中的大量应用,有很多问题逐渐显现出来,比如:

  • 由于某些原因,项目组需要引入不同的 Ingress Controller,但 Kubernetes 不允许这样做;

  • Ingress 规则太多,都交给一个 Ingress Controller 处理会让它不堪重负;

  • 多个 Ingress 对象没有很好的逻辑分组方式,管理和维护成本很高;

  • 集群里有不同的租户,他们对 Ingress 的需求差异很大甚至有冲突,无法部署在同一个 Ingress Controller 上。

基于以上的这些问题,又有了 Ingress Class 的概念,让它插在 Ingress 和 Ingress Controller 中间,作为流量规则和控制器的协调人,解除了 Ingress 和 Ingress Controller 的强绑定关系。

Kubernetes 用户可以转向管理 Ingress Class,用它来定义不同的业务逻辑分组,简化 Ingress 规则的复杂度。比如,可以用 Class A 处理博客流量、Class B 处理短视频流量、Class C 处理购物流量。

2.12.2 描述 Ingress #

Ingress 和 Ingress Class 的 apiVersion 都是 networking.k8s.io/v1,而且 Ingress 有一个简写 ing。Ingress Controller 和 Ingress,Ingress Class 两个对象不太一样,它不只是描述文件,是一个要实际干活、处理流量的应用程序,而应用程序在 Kubernetes 里早就有对象来管理了,那就是 Deployment 和 DaemonSet。

Ingress 也是可以使用 kubectl create 来创建,它需要用两个附加参数:

  • --class,指定 Ingress 从属的 Ingress Class 对象。

  • --rule,指定路由规则,基本形式是URI=Service。也就是说是访问 HTTP 路径就转发到对应的 Service 对象,再由 Service 对象转发给后端的 Pod。

export out="--dry-run=client -o yaml"
kubectl create ing ngx-ing --rule="ngx.test/=ngx-svc:80" --class=ngx-ink $out
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ngx-ing

spec:
  ingressClassName: ngx-ink
  rules:
    - host: ngx.test
      http:
        paths:
          - path: /
            pathType: Exact
            backend:
              service:
                name: ngx-svc
                port:
                  number: 80

以上的 YAML 描述文件,ingressClassName 和 rules,分别对应了命令行参数。rules 字段嵌套层次比较深,其实只是把路由规则拆散了,有 host 和 http path,在 path 里又指定了路径的匹配方式,可以是精确匹配(Exact)或者是前缀匹配(Prefix),再用 backend 来指定转发的目标 Service 对象。

其实 Ingress Class 本身并没有什么实际的功能,只是起到联系 Ingress 和 Ingress Controller 的作用,在 spec 里只有一个必需的字段 controller,表示要使用哪个 Ingress Controller,具体的名字就要看实现文档了。比如,要用 Nginx 开发的 Ingress Controller,那么就要用名字 nginx.org/ingress-controller:

apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
  name: ngx-ink

spec:
  controller: nginx.org/ingress-controller

2.12.3 使用 Ingress #

因为 Ingress Class 的 YAML 描述很小,可以把 Ingress Class 与 Ingress 合成了一个 YAML 文件,也就是下面的这个 ingress.yaml:

apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
  name: ngx-ink

spec:
  controller: nginx.org/ingress-controller

---

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ngx-ing

spec:
  ingressClassName: ngx-ink
  rules:
    - host: ngx.test
      http:
        paths:
          - path: /
            pathType: Exact
            backend:
              service:
                name: ngx-svc
                port:
                  number: 80

在使用 kubectl create 创建 Ingress 的 YAML 描述时,通过--rule指定了路由形式,Server 的 name 是 ngx-svc,所以创建 Service 的 YAML 描述可以是 ngx-svc.yml:

apiVersion: v1
kind: Service
metadata:
  name: ngx-svc

spec:
  type: NodePort
  selector:
    app: ngx-dep

  ports:
    - port: 80
      targetPort: 80
      protocol: TCP

对应创建 Deployment 的描述是 nginx.yml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: ngx-dep

spec:
  replicas: 2
  selector:
    matchLabels:
      app: ngx-dep

  template:
    metadata:
      labels:
        app: ngx-dep
    spec:
      volumes:
        - name: ngx-conf-vol
          configMap:
            name: ngx-conf

      containers:
        - image: nginx:alpine
          name: nginx
          ports:
            - containerPort: 80

          volumeMounts:
            - mountPath: /etc/nginx/conf.d
              name: ngx-conf-vol

nginx.yml 需要的 ConfigMap 的描述 ngx-conf.yaml 为:

apiVersion: v1
kind: ConfigMap
metadata:
  name: ngx-conf

data:
  default.conf: |
    server {
      listen 80;
      location / {
        default_type text/plain;
        return 200
          'srv : $server_addr:$server_port\nhost: $hostname\nuri : $request_method $host $request_uri\ndate: $time_iso8601\n';
      }
    }    

具体命令如下:

命令 kubectl describe 可以看到更详细的 Ingress 信息:

kubectl describe ing ngx-ing

Ingress 对象的路由规则 Host/Path 就是在 YAML 里设置的域名ngx.test/,而且已经关联了我们创建的 Service 对象,还有 Service 中的两个 Pod。

Ingress 里 Default backend 的错误,在找不到路由的时候,它被设计用来提供一个默认的后端服务,不设置也不会有什么问题,在大多数时候可以忽略这个错误。

2.12.4 使用 Ingress Controller #

有了 Ingress 和 Ingress Class 对象,就可以部署真正处理路由规则的 Ingress Controller 了。这里使用 Nginx Ingress Controller 项目 https://github.com/nginxinc/kubernetes-ingress,它以 Pod 的形式运行在 Kubernetes 里,所以同时支持 Deployment 和 DaemonSet 两种部署方式,这里选择 Deployment 的部署方式 https://github.com/nginxinc/kubernetes-ingress/blob/main/deployments/deployment/nginx-ingress.yaml

Nginx Ingress Controller 的安装有点麻烦,有很多个 YAML 需要执行,但如果只是做简单的试验,只需要用到以下的 4 个 YAML:

kubectl apply -f ns-and-sa.yaml
kubectl apply -f rbac.yaml
kubectl apply -f nginx-config.yaml
kubectl apply -f default-server-secret.yaml

前两条命令为 Ingress Controller 创建了一个独立的名字空间 nginx-ingress,还有相应的账号和权限,这是为了访问 apiserver 获取 Service、Endpoint 信息用的;后两条则是创建了一个 ConfigMap 和 Secret,用来配置 HTTP/HTTPS 服务。

ns-and-sa.yaml 如下:

apiVersion: v1
kind: Namespace
metadata:
  name: nginx-ingress
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: nginx-ingress
  namespace: nginx-ingress

rbac.yaml 如下:

kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: nginx-ingress
rules:
  - apiGroups:
      - ""
    resources:
      - services
      - endpoints
    verbs:
      - get
      - list
      - watch
  - apiGroups:
      - ""
    resources:
      - secrets
    verbs:
      - get
      - list
      - watch
  - apiGroups:
      - ""
    resources:
      - configmaps
    verbs:
      - get
      - list
      - watch
      - update
      - create
  - apiGroups:
      - ""
    resources:
      - pods
    verbs:
      - list
      - watch
  - apiGroups:
      - ""
    resources:
      - events
    verbs:
      - create
      - patch
      - list
  - apiGroups:
      - networking.k8s.io
    resources:
      - ingresses
    verbs:
      - list
      - watch
      - get
  - apiGroups:
      - networking.k8s.io
    resources:
      - ingresses/status
    verbs:
      - update
  - apiGroups:
      - k8s.nginx.org
    resources:
      - virtualservers
      - virtualserverroutes
      - globalconfigurations
      - transportservers
      - policies
    verbs:
      - list
      - watch
      - get
  - apiGroups:
      - k8s.nginx.org
    resources:
      - virtualservers/status
      - virtualserverroutes/status
      - policies/status
      - transportservers/status
    verbs:
      - update
  - apiGroups:
      - networking.k8s.io
    resources:
      - ingressclasses
    verbs:
      - get
  - apiGroups:
      - cis.f5.com
    resources:
      - ingresslinks
    verbs:
      - list
      - watch
      - get
  - apiGroups:
      - cert-manager.io
    resources:
      - certificates
    verbs:
      - list
      - watch
      - get
      - update
      - create
      - delete
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: nginx-ingress
subjects:
  - kind: ServiceAccount
    name: nginx-ingress
    namespace: nginx-ingress
roleRef:
  kind: ClusterRole
  name: nginx-ingress
  apiGroup: rbac.authorization.k8s.io

nginx-config.yaml 如下:

kind: ConfigMap
apiVersion: v1
metadata:
  name: nginx-config
  namespace: nginx-ingress
data:

default-server-secret.yaml 如下:

apiVersion: v1
kind: Secret
metadata:
  name: default-server-secret
  namespace: nginx-ingress
type: kubernetes.io/tls
data:
  tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUN2akNDQWFZQ0NRREFPRjl0THNhWFhEQU5CZ2txaGtpRzl3MEJBUXNGQURBaE1SOHdIUVlEVlFRRERCWk8KUjBsT1dFbHVaM0psYzNORGIyNTBjbTlzYkdWeU1CNFhEVEU0TURreE1qRTRNRE16TlZvWERUSXpNRGt4TVRFNApNRE16TlZvd0lURWZNQjBHQTFVRUF3d1dUa2RKVGxoSmJtZHlaWE56UTI5dWRISnZiR3hsY2pDQ0FTSXdEUVlKCktvWklodmNOQVFFQkJRQURnZ0VQQURDQ0FRb0NnZ0VCQUwvN2hIUEtFWGRMdjNyaUM3QlBrMTNpWkt5eTlyQ08KR2xZUXYyK2EzUDF0azIrS3YwVGF5aGRCbDRrcnNUcTZzZm8vWUk1Y2Vhbkw4WGM3U1pyQkVRYm9EN2REbWs1Qgo4eDZLS2xHWU5IWlg0Rm5UZ0VPaStlM2ptTFFxRlBSY1kzVnNPazFFeUZBL0JnWlJVbkNHZUtGeERSN0tQdGhyCmtqSXVuektURXUyaDU4Tlp0S21ScUJHdDEwcTNRYzhZT3ExM2FnbmovUWRjc0ZYYTJnMjB1K1lYZDdoZ3krZksKWk4vVUkxQUQ0YzZyM1lma1ZWUmVHd1lxQVp1WXN2V0RKbW1GNWRwdEMzN011cDBPRUxVTExSakZJOTZXNXIwSAo1TmdPc25NWFJNV1hYVlpiNWRxT3R0SmRtS3FhZ25TZ1JQQVpQN2MwQjFQU2FqYzZjNGZRVXpNQ0F3RUFBVEFOCkJna3Foa2lHOXcwQkFRc0ZBQU9DQVFFQWpLb2tRdGRPcEsrTzhibWVPc3lySmdJSXJycVFVY2ZOUitjb0hZVUoKdGhrYnhITFMzR3VBTWI5dm15VExPY2xxeC9aYzJPblEwMEJCLzlTb0swcitFZ1U2UlVrRWtWcitTTFA3NTdUWgozZWI4dmdPdEduMS9ienM3bzNBaS9kclkrcUI5Q2k1S3lPc3FHTG1US2xFaUtOYkcyR1ZyTWxjS0ZYQU80YTY3Cklnc1hzYktNbTQwV1U3cG9mcGltU1ZmaXFSdkV5YmN3N0NYODF6cFErUyt1eHRYK2VBZ3V0NHh3VlI5d2IyVXYKelhuZk9HbWhWNThDd1dIQnNKa0kxNXhaa2VUWXdSN0diaEFMSkZUUkk3dkhvQXprTWIzbjAxQjQyWjNrN3RXNQpJUDFmTlpIOFUvOWxiUHNoT21FRFZkdjF5ZytVRVJxbStGSis2R0oxeFJGcGZnPT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
  tls.key: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcEFJQkFBS0NBUUVBdi91RWM4b1JkMHUvZXVJTHNFK1RYZUprckxMMnNJNGFWaEMvYjVyYy9XMlRiNHEvClJOcktGMEdYaVN1eE9ycXgrajlnamx4NXFjdnhkenRKbXNFUkJ1Z1B0ME9hVGtIekhvb3FVWmcwZGxmZ1dkT0EKUTZMNTdlT1l0Q29VOUZ4amRXdzZUVVRJVUQ4R0JsRlNjSVo0b1hFTkhzbysyR3VTTWk2Zk1wTVM3YUhudzFtMApxWkdvRWEzWFNyZEJ6eGc2clhkcUNlUDlCMXl3VmRyYURiUzc1aGQzdUdETDU4cGszOVFqVUFQaHpxdmRoK1JWClZGNGJCaW9CbTVpeTlZTW1hWVhsMm0wTGZzeTZuUTRRdFFzdEdNVWozcGJtdlFmazJBNnljeGRFeFpkZFZsdmwKMm82MjBsMllxcHFDZEtCRThCay90elFIVTlKcU56cHpoOUJUTXdJREFRQUJBb0lCQVFDZklHbXowOHhRVmorNwpLZnZJUXQwQ0YzR2MxNld6eDhVNml4MHg4Mm15d1kxUUNlL3BzWE9LZlRxT1h1SENyUlp5TnUvZ2IvUUQ4bUFOCmxOMjRZTWl0TWRJODg5TEZoTkp3QU5OODJDeTczckM5bzVvUDlkazAvYzRIbjAzSkVYNzZ5QjgzQm9rR1FvYksKMjhMNk0rdHUzUmFqNjd6Vmc2d2szaEhrU0pXSzBwV1YrSjdrUkRWYmhDYUZhNk5nMUZNRWxhTlozVDhhUUtyQgpDUDNDeEFTdjYxWTk5TEI4KzNXWVFIK3NYaTVGM01pYVNBZ1BkQUk3WEh1dXFET1lvMU5PL0JoSGt1aVg2QnRtCnorNTZud2pZMy8yUytSRmNBc3JMTnIwMDJZZi9oY0IraVlDNzVWYmcydVd6WTY3TWdOTGQ5VW9RU3BDRkYrVm4KM0cyUnhybnhBb0dCQU40U3M0ZVlPU2huMVpQQjdhTUZsY0k2RHR2S2ErTGZTTXFyY2pOZjJlSEpZNnhubmxKdgpGenpGL2RiVWVTbWxSekR0WkdlcXZXaHFISy9iTjIyeWJhOU1WMDlRQ0JFTk5jNmtWajJTVHpUWkJVbEx4QzYrCk93Z0wyZHhKendWelU0VC84ajdHalRUN05BZVpFS2FvRHFyRG5BYWkyaW5oZU1JVWZHRXFGKzJyQW9HQkFOMVAKK0tZL0lsS3RWRzRKSklQNzBjUis3RmpyeXJpY05iWCtQVzUvOXFHaWxnY2grZ3l4b25BWlBpd2NpeDN3QVpGdwpaZC96ZFB2aTBkWEppc1BSZjRMazg5b2pCUmpiRmRmc2l5UmJYbyt3TFU4NUhRU2NGMnN5aUFPaTVBRHdVU0FkCm45YWFweUNweEFkREtERHdObit3ZFhtaTZ0OHRpSFRkK3RoVDhkaVpBb0dCQUt6Wis1bG9OOTBtYlF4VVh5YUwKMjFSUm9tMGJjcndsTmVCaWNFSmlzaEhYa2xpSVVxZ3hSZklNM2hhUVRUcklKZENFaHFsV01aV0xPb2I2NTNyZgo3aFlMSXM1ZUtka3o0aFRVdnpldm9TMHVXcm9CV2xOVHlGanIrSWhKZnZUc0hpOGdsU3FkbXgySkJhZUFVWUNXCndNdlQ4NmNLclNyNkQrZG8wS05FZzFsL0FvR0FlMkFVdHVFbFNqLzBmRzgrV3hHc1RFV1JqclRNUzRSUjhRWXQKeXdjdFA4aDZxTGxKUTRCWGxQU05rMXZLTmtOUkxIb2pZT2pCQTViYjhibXNVU1BlV09NNENoaFJ4QnlHbmR2eAphYkJDRkFwY0IvbEg4d1R0alVZYlN5T294ZGt5OEp0ek90ajJhS0FiZHd6NlArWDZDODhjZmxYVFo5MWpYL3RMCjF3TmRKS2tDZ1lCbyt0UzB5TzJ2SWFmK2UwSkN5TGhzVDQ5cTN3Zis2QWVqWGx2WDJ1VnRYejN5QTZnbXo5aCsKcDNlK2JMRUxwb3B0WFhNdUFRR0xhUkcrYlNNcjR5dERYbE5ZSndUeThXczNKY3dlSTdqZVp2b0ZpbmNvVlVIMwphdmxoTUVCRGYxSjltSDB5cDBwWUNaS2ROdHNvZEZtQktzVEtQMjJhTmtsVVhCS3gyZzR6cFE9PQotLS0tLUVORCBSU0EgUFJJVkFURSBLRVktLS0tLQo=

部署 Ingress Controller 不需要从头编写 Deployment,Nginx 已经为我们提供了 示例 YAML,创建之前为了适配我们上面自己的应用还必须要做几处小改动:

  • metadata 里的 name 要改成自己的名字,比如 ngx-kic-dep。

  • spec.selector 和 template.metadata.labels 也要修改成自己的名字,比如还是用 ngx-kic-dep。

  • containers.image 可以改用 alpine 版本,加快下载速度,比如 nginx/nginx-ingress:2.2-alpine。

  • 最下面的 args 要加上 -ingress-class=ngx-ink,也就是前面创建的 Ingress Class 的名字,这是让 Ingress Controller 管理 Ingress 的关键。

修改完之后,Ingress Controller 的 YAML 如下:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: ngx-kic-dep
  namespace: nginx-ingress
spec:
  replicas: 1
  selector:
    matchLabels:
      app: ngx-kic-dep
  template:
    metadata:
      labels:
        app: ngx-kic-dep
        app.kubernetes.io/name: nginx-ingress
        #annotations:
        #prometheus.io/scrape: "true"
        #prometheus.io/port: "9113"
        #prometheus.io/scheme: http
    spec:
      serviceAccountName: nginx-ingress
      automountServiceAccountToken: true
      securityContext:
        seccompProfile:
          type: RuntimeDefault
      #      volumes:
      #      - name: nginx-etc
      #        emptyDir: {}
      #      - name: nginx-cache
      #        emptyDir: {}
      #      - name: nginx-lib
      #        emptyDir: {}
      #      - name: nginx-log
      #        emptyDir: {}
      containers:
        - image: nginx/nginx-ingress:2.2-alpine
          imagePullPolicy: IfNotPresent
          name: nginx-ingress
          ports:
            - name: http
              containerPort: 80
            - name: https
              containerPort: 443
            - name: readiness-port
              containerPort: 8081
            - name: prometheus
              containerPort: 9113
          readinessProbe:
            httpGet:
              path: /nginx-ready
              port: readiness-port
            periodSeconds: 1
          resources:
            requests:
              cpu: "100m"
              memory: "128Mi"
            #limits:
            #  cpu: "1"
            #  memory: "1Gi"
          securityContext:
            allowPrivilegeEscalation: false
            #          readOnlyRootFilesystem: true
            runAsUser: 101 #nginx
            runAsNonRoot: true
            capabilities:
              drop:
                - ALL
              add:
                - NET_BIND_SERVICE
          #        volumeMounts:
          #        - mountPath: /etc/nginx
          #          name: nginx-etc
          #        - mountPath: /var/cache/nginx
          #          name: nginx-cache
          #        - mountPath: /var/lib/nginx
          #          name: nginx-lib
          #        - mountPath: /var/log/nginx
          #          name: nginx-log
          env:
            - name: POD_NAMESPACE
              valueFrom:
                fieldRef:
                  fieldPath: metadata.namespace
            - name: POD_NAME
              valueFrom:
                fieldRef:
                  fieldPath: metadata.name
          args:
            - -nginx-configmaps=$(POD_NAMESPACE)/nginx-config
            - -ingress-class=ngx-ink
            - -enable-custom-resources=false
            #- -default-server-tls-secret=$(POD_NAMESPACE)/default-server-secret
            #- -include-year
            #- -enable-cert-manager
            #- -enable-external-dns
            #- -v=3 # Enables extensive logging. Useful for troubleshooting.
            #- -report-ingress-status
            #- -external-service=nginx-ingress
            #- -enable-prometheus-metrics
            #- -global-configuration=$(POD_NAMESPACE)/nginx-configuration
#      initContainers:
#      - image: nginx/nginx-ingress:3.1.1
#        imagePullPolicy: IfNotPresent
#        name: init-nginx-ingress
#        command: ['cp', '-vdR', '/etc/nginx/.', '/mnt/etc']
#        securityContext:
#          allowPrivilegeEscalation: false
#          readOnlyRootFilesystem: true
#          runAsUser: 101 #nginx
#          runAsNonRoot: true
#          capabilities:
#            drop:
#            - ALL
#        volumeMounts:
#        - mountPath: /mnt/etc
#          name: nginx-etc

有了 Ingress Controller,这些 API 对象的关联就更复杂,下面这张图示可以看出它们是如何使用对象名字联系起来的:

执行命令如下:

Ingress Controller 位于名字空间 nginx-ingress,查看状态需要用 -n 参数显式指定,否则我们只能看到 default 名字空间里的 Pod:

kubectl get deploy -n nginx-ingress
kubectl get pod -n nginx-ingress

如截图所示,Ingress Controller 已经运行起来了。因为 Ingress Controller 本身也是一个 Pod,想要向外提供服务还是要依赖于 Service 对象。所以至少还要再为它定义一个 Service,使用 NodePort 或者 LoadBalancer 暴露端口,才能真正把集群的内外流量打通。

可以简单使用命令 kubectl port-forward,它可以直接把本地的端口映射到 Kubernetes 集群的某个 Pod 里,在测试验证的时候非常方便。

下面这条命令就把本地的 8080 端口映射到了 Ingress Controller Pod 的 80 端口:

kubectl port-forward -n nginx-ingress ngx-kic-dep-59bdb44896-2bgvm 8080:80 &

因为 Ingress 的路由规则是 HTTP 协议,所以就不能用 IP 地址的方式访问,必须要用域名、URI。可以修改 /etc/hosts 来手工添加域名解析,也可以在 curl 时使用 –resolve 参数,指定域名的解析规则,比如在这里把 ngx.test 强制解析到 127.0.0.1,也就是被 kubectl port-forward 转发的本地地址:

curl --resolve ngx.test:8080:127.0.0.1 http://ngx.test:8080

可以看到已经成功的把请求转发到了集群内部的 Pod。

参考 #