Pod 无法访问?可能是 Ingress 配置有问题

1. 外部无法访问 Pod

最近,我们在阿里云上买了 K8S 集群,项目好不容易部署上去,Depolyment 和 Service 发布完后;接着又去买了域名,添加域名解析,又去配置了 SLB(负载均衡)。

这时候问题来了,外部无法访问我的项目,然后我该怎么办呢?

apiVersion: v1
kind: Service
metadata:
  name: $CI_PROJECT_NAME
  namespace: $NAMESPACE
spec:
  type: ClusterIP
  ports:
    - port: $PORT
      targetPort: $PORT
      name: $CI_PROJECT_NAME
  selector:
    app: $CI_PROJECT_NAME

好嘛,原来有个东西叫 Ingress,我们压根就没配置,能访问才怪呢!

当我们使用 K8S 集群部署好应用的 Service 时,默认的 Service 类型是 ClusterIP,这种类型只有 Cluster 内的节点和 Pod 可以访问

K8s 集群内访问服务

服务发现是 K8s 中的一个重要机制,其基本功能为:在集群内通过服务名对服务进行访问,即需要完成从服务名到 ClusterIP(服务的虚拟 IP) 的解析。

K8s 主要有两种服务发现机制:环境变量和 DNS。没有 DNS 服务的时候,K8s 会采用环境变量的形式,但一旦有多个服务,环境变量会变复杂,为解决该问题,我们使用 DNS 服务。

Pod 无法访问?可能是 Ingress 配置有问题
357687-20220912202953742-389968688.png

如下图,我们可以这样访问我们的服务:

  • 如果在同一个namespace内,http://hsh-product-service:80 或 http://172.21.1.41:80
  • 如果在不同的namespece外,http://hsh-product-service.pre-service:80
Pod 无法访问?可能是 Ingress 配置有问题
1686298811632.jpg

2. NodePort 和 LoadBalancer 外部访问

K8s 集群的外网访问方式有3种:

NodePort,LoadBanlancer 和 Ingress

NodePort 和 LoadBanlancer 是 k8s 中 service 的类型。上面讲到的集群内访问,ClusterIP 也是 service 的一种类型。Ingress 是 k8s 的一个抽象层,有很多的 Ingress Controller 和服务可以来实现这个 Ingress 服务,然后由这个 Ingress 服务把外网的请求转发到集群内的服务。

NodePort

NodePort 服务是让外部流量直接访问服务的最原始方式。NodePort,顾名思义,在所有的节点(虚拟机)上开放指定的端口,所有发送到这个端口的流量都会直接转发到服务。

Pod 无法访问?可能是 Ingress 配置有问题
357687-20220915202139860-456944910.png

创建 NodePort 类型的 Service 时,用户可以指定范围为 30000-32767 的端口,对该端口的访问就能通过 kube-proxy 代理到 Service 后端的 Pod 中。使用 NodeIP:PodPort 访问 Pod。

这种服务暴露方式,无法让你指定自己想要的应用常用端口,不过可以在集群上再部署一个反向代理作为流量入口。

apiVersion: v1
kind: Service
metadata:
  name: $CI_PROJECT_NAME
  namespace: $NAMESPACE
spec:
  type: NodePort
  ports:
    - port: $PORT
      targetPort: $PORT
      nodePort: 30001
      protocol: TCP
      name: $CI_PROJECT_NAME
  selector:
    app: $CI_PROJECT_NAME

从本质上来看,NodePort 服务有两个地方不同于一般的 “ClusterIP” 服务。首先,它的类型是 “NodePort”。还有一个叫做 “nodePort” 的端口,能在节点上指定开放哪个端口。如果没有指定端口,它会选择一个随机端口。大多数时候应该让 Kubernetes 选择这个端口。

NodePort 方式有一些不足

  1. 一个端口只能供一个服务使用;
  2. 只能使用30000–32767的端口;
  3. 如果节点 / 虚拟机的IP地址发生变化,需要进行处理。

因此,我不推荐在生产环境使用这种方式来直接发布服务。如果不要求运行的服务实时可用,或者在意成本,这种方式适合你。例如用于演示的应用或是临时运行就正好用这种方法。

LoadBalancer

LoadBalancer 服务是发布服务到互联网的标准方式。在GKE中,它会启动一个 Network Load Balancer,分配一个单独的IP地址,将所有流量转发到服务中。

Pod 无法访问?可能是 Ingress 配置有问题
357687-20220915210443554-424350450.png

LoadBanlancer 类型需要各个云厂商自己来实现的 CloudControllerManager,所以,采用不同的云厂商,它们的 LoadBanlancer 也就会有一些区别,它们的功能以及使用方法也就不一样了。LoadBalancer 针对于 Service,且只能在云服务平台上使用(AWS、Azure、阿里云等),使用任一节点的 IP 访问,如 LoadBalancerIP:LoadBalancerPort

如果你想直接发布服务,这是默认方式。指定端口的所有流量都会转发到服务中,没有过滤,也没有路由。这意味着你几乎可以发送任意类型的流量到服务中,比如 HTTP、TCP、UDP、Websockets、gRPC 等等。

这里我们可以购买阿里云的 SLB(负载均衡):

Pod 无法访问?可能是 Ingress 配置有问题
image.png

LoadBalancer 方式有一些不足

  1. 使用LoadBalancer发布的每个服务都会有一个自己的IP地址,你需要支付每个服务的LoadBalancer 费用,这是一笔不小的开支。

3. Ingress 外部访问(智能路由和网关

Ingress Controller 以 Docker 容器的方式部署在 K8s 的顶部,简单来说就是 K8s 所使用的负载均衡器,类型有:Nginx、Envoy、HAProxy 等。
一般使用 K8s 官方的 Ingress-Nginx Controller(注意,不是 Nginx 公司的 Nginx Ingress Controller) 作为外网访问服务的方式。

Pod 无法访问?可能是 Ingress 配置有问题
357687-20220915211126955-824587463.png

Ingress 实际上不是一种服务。相反,它在多个服务前面充当“智能路由”的角色,或者是集群的入口。使用 Ingress 可以做很多事情,不同类型的 Ingress 控制器有不同的功能。

默认的 GKE ingress 控制器会启动一个 HTTP(S) Load Balancer,可以通过基于路径或者是基于子域名的方式路由到后端服务。例如,可以通过 foo.domain.com 发送任何东西到 foo 服务,或者是发送domain.com/bar/ 路径下的任何东西到bar服务。

这里我们来举一个比较具体的例子,我们部署一个 Redis 服务:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: redis-deployment
  namespace: dev
spec:
  replicas: 3
  selector:
    matchLabels:
      app: redis-pod
  template:
    metadata:
      labels:
        app: redis-pod
    spec:
      containers:
        - name: redis
          image: redis:6.3
          ports:
            - containerPort: 6379
apiVersion: v1
kind: Service
metadata:
  name: redis-service
  namespace: dev
spec:
  selector:
    app: redis-pod
  clusterIP: None
  type: ClusterIP
  ports:
    - port: 6379
      targetPort: 6379

此时我们根据上边章节的内容,集群内部已经可以访问:redis-service.dev:6379

我们期望 外部通过 http 可以访问这个服务

http://redis.domain.com --> redis-service.dev:6379 
http://foo.domain.com/redis --> redis-service.dev:6379
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-http
  namespace: dev
  annotations:
    kubernetes.io/ingress.class: "nginx"
spec:
  rules:
    - host: redis.domain.com
      http:
        paths:
          - path: / 
            pathType: Prefix
            backend:
              serviceName: redis-service 
              servicePort: 6379
    - host: foo.domain.com
      http:
        paths:
          - path: /redis 
            pathType: Prefix
            backend:
              serviceName: redis-service 
              servicePort: 6379

这里为了方便大家理解,相当于 Nginx 原生 .conf 文件的写法:

        server {

            listen       80;
            server_name  redis.domain.com;

            location ~ / {

                proxy_pass http://redis-service:6379;
            }
        }
        
        server {

            listen       80;
            server_name  foo.domain.com;

            location ~ /redis {

                proxy_pass http://redis-service:6379;
            }
        }

这里我们升级一下,期望 外部通过 https 可以访问这个服务

    https://redis.domain.com --> redis-service.dev:6379 
    https://foo.domain.com/redis --> redis-service.dev:6379
    # 生成自签名证书(365天有效期)  
    # Key: tls.key  证书: tls.crt  
    $ openssl req -x509 -sha256 -nodes   
        -days 365 -newkey rsa:2048   
        -keyout tls.key -out tls.crt -subj "/CN=nginxsvc/O=nginxsvc"  
    Generating a 2048 bit RSA private key  
    ................+++  
    ................+++  
    writing new private key to 'tls.key'  
      
    # 在K8S中创建一个名称为tls-secret的secret格式的证书信息  
    $ kubectl create secret tls tls-secret --key tls.key --cert tls.crt  
    secret "tls-secret" created  
    Deployment/Service/Ingress
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-https
  namespace: dev
  annotations:
    kubernetes.io/ingress.class: "nginx"
spec:
  tls:
    - hosts:  
      - redis.domain.com
      - foo.domain.com
    secretName: tls-secret
  rules:
    - host: redis.domain.com
      http:
        paths:
          - path: / 
            pathType: Prefix
            backend:
              serviceName: redis-service 
              servicePort: 6379
    - host: foo.domain.com
      http:
        paths:
          - path: /redis 
            pathType: Prefix
            backend:
              serviceName: redis-service 
              servicePort: 6379

Ingress 和 网关的区别

Ingress 是 Kubernetes 中的一个抽象层,用于将多个服务公开到同一 IP 地址和端口,并根据请求路径或主机名进行路由,而网关则是提供对应用程序的访问控制、身份验证、安全性、流量管理和监视等功能的组件,它可以与 Kubernetes 集群一起使用,但也可以独立于 Kubernetes 集群之外部署。

Ingress 是 Kubernetes 中的一个抽象层,它允许您公开多个服务到同一 IP 地址和端口,并根据请求路径或主机名进行路由。Ingress 通常用于 HTTP/HTTPS 流量,并且可以支持 TLS endpoint、基于路径的路由和负载均衡等功能。可以使用 Kubernetes Ingress Controller 实现 Ingress 功能,例如 Nginx Ingress Controller、Traefik Ingress Controller 和 Istio Ingress Gateway。

网关(Gateway)通常是一个独立的组件,用于提供对应用程序的访问控制、身份验证、安全性、流量管理和监视等功能。网关通常可以支持多个协议和传输层,并且可以部署在 Kubernetes 集群之外,例如 API网关、网络应用防火墙(WAF)和服务网格(Service Mesh)等。流量可以通过不同的方式路由到网关,例如 DNS名、IP地址、负载均衡器和 Ingress 等。


原文始发于微信公众号(白菜说技术):Pod 无法访问?可能是 Ingress 配置有问题

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/172393.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!