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 服务。

357687-20220912202953742-389968688.png

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

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,顾名思义,在所有的节点(虚拟机)上开放指定的端口,所有发送到这个端口的流量都会直接转发到服务。

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地址,将所有流量转发到服务中。

357687-20220915210443554-424350450.png

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

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

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

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) 作为外网访问服务的方式。

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 等。

© 版权声明
THE END
喜欢就支持一下吧
点赞0

Warning: mysqli_query(): (HY000/3): Error writing file '/tmp/MYQRPdTz' (Errcode: 28 - No space left on device) in /www/wwwroot/583.cn/wp-includes/class-wpdb.php on line 2345
admin的头像-五八三
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

图形验证码
取消
昵称代码图片