kubernetes | Ingress

2022年3月17日 · 3909 字 · 8 分钟 · Kubernetes

一、引言

在 Kubernetes 中,Ingress 是一种 API 对象,用于管理外部访问集群服务的方式,通常是 HTTP 和 HTTPS 流量。可以将 Ingress 理解为一个智能的路由器,它根据请求的 URL 路径或主机名将流量路由到不同的服务。 主要功能:

  • 路由:Ingress 可以根据请求的 URL 路径或主机名将流量路由到不同的服务。例如,你可以将 example.com/api 的请求路由到一个服务,而将 example.com/app 的请求路由到另一个服务。
  • 负载均衡:Ingress 可以在多个后端服务之间分配流量,从而实现负载均衡。
  • SSL/TLS 终止:Ingress 可以处理 SSL/TLS 加密,允许你在 Ingress 控制器中配置证书,而不是在每个服务中配置。
  • 基于主机和路径的路由:Ingress 支持基于主机名和路径的路由规则,使得多个服务可以通过同一个 IP 地址和端口进行访问。

NodePort

1、把端口号对外暴露,通过 IP + 端口号 进行访问。
1)使用 Service 里面的 NodePort 实现。

2、NodePort 缺陷
1)在每个节点上都会启动端口,在访问时候通过任何节点,通过节点 IP + 暴露端口号 实现访问。
2)意味着每个端口只能使用一次,一个端口对应一个应用。
3)实际访问中都是用域名,根据不同域名跳转到不同端口服务中。

二、Ingress 概述

通常情况下,service 和 pod 的 IP 仅可在集群内部访问。集群外部的请求需要通过负载均衡转发到 service 在 Node 上暴露的 NodePort 上,然后再由 kube-proxy 将其转发给相关的 Pod。

Ingress 为 Kubernetes 集群中的服务提供了入口,可以提供负载均衡、SSL 终止和基于名称的虚拟主机,在生产环境中常用的 Ingress 有 Treafik、Nginx、HAProxy、Istio 等。在 Kubernetes v1.1 版中添加的 Ingress 用于从集群外部到集群内部 Service 的 HTTP 和 HTTPS 路由,流量从 Internet 到 Ingress 再到 Services 最后到 Pod 上,通常情况下,Ingress 部署在所有的 Node 节点上。Ingress 可以配置提供服务外部访问的 URL、负载均衡、终止 SSL,并提供基于域名的虚拟主机。但 Ingress 不会暴露任意端口或协议。

三、Ingress 工作流程

Ingress 公开了从集群外部到集群内服务的 HTTP 和 HTTPS 路由。 流量路由由 Ingress 资源上定义的规则控制。


可以将 Ingress 配置为服务提供外部可访问的 URL、负载均衡流量、终止 SSL/TLS,以及提供基于名称的虚拟主机等能力。 Ingress 控制器 通常负责通过负载均衡器来实现 Ingress,尽管它也可以配置边缘路由器或其他前端来帮助处理流量。

Ingress 不会公开任意端口或协议。 将 HTTP 和 HTTPS 以外的服务公开到 Internet 时,通常使用 Service.Type=NodePort 或 Service.Type=LoadBalancer 类型的服务。

四、Ingress 控制器 (官方维护)

4.1、官方维护 Ingress 控制器

为了让 Ingress 资源工作,集群必须有一个正在运行的 Ingress 控制器。

与作为 kube-controller-manager 可执行文件的一部分运行的其他类型的控制器不同, Ingress 控制器不是随集群自动启动的。 基于此页面,你可选择最适合你的集群的 ingress 控制器实现。 Kubernetes 作为一个项目,目前支持和维护 AWS, GCE 和 nginx Ingress 控制器。

4.2、其他 Ingress 控制器

  • AKS 应用程序网关 Ingress 控制器 是一个配置 Azure 应用程序网关 的 Ingress 控制器。
  • Ambassador API 网关是一个基于 Envoy 的 Ingress 控制器。
  • Apache APISIX Ingress 控制器 是一个基于 Apache APISIX 网关 的 Ingress 控制器。
  • Avi Kubernetes Operator 使用 VMware NSX Advanced Load Balancer 提供第 4 到第 7 层的负载均衡。
  • BFE Ingress 控制器 是一个基于 BFE 的 Ingress 控制器。
  • Citrix Ingress 控制器 可以用来与 Citrix Application Delivery Controller 一起使用。
  • Contour 是一个基于 Envoy 的 Ingress 控制器。
  • EnRoute 是一个基于 Envoy API 网关, 可以作为 Ingress 控制器来执行。
  • Easegress IngressController 是一个基于 Easegress API 网关,可以作为 Ingress 控制器来执行。
  • F5 BIG-IP 的用于 Kubernetes 的容器 Ingress 服务 让你能够使用 Ingress 来配置 F5 BIG-IP 虚拟服务器。
  • Gloo 是一个开源的、基于 Envoy 的 Ingress 控制器,能够提供 API 网关功能,
  • HAProxy Ingress 针对 HAProxy 的 Ingress 控制器。
    用于 Kubernetes 的 HAProxy Ingress 控制器 也是一个针对 HAProxy 的 Ingress 控制器。
  • Istio Ingress 是一个基于 Istio 的 Ingress 控制器。
    用于 Kubernetes 的 Kong Ingress 控制器 是一个用来驱动 Kong Gateway 的 Ingress 控制器。
    用于 Kubernetes 的 NGINX Ingress 控制器 能够与 NGINX Web 服务器(作为代理) 一起使用。
  • Skipper HTTP 路由器和反向代理可用于服务组装,支持包括 Kubernetes Ingress 这类使用场景, 设计用来作为构造你自己的定制代理的库。
  • Traefik Kubernetes Ingress 提供程序 是一个用于 Traefik 代理的 Ingress 控制器。
  • Tyk Operator 使用自定义资源扩展 Ingress,为之带来 API 管理能力。Tyk Operator 使用开源的 Tyk Gateway & Tyk Cloud 控制面。
  • Voyager 是一个针对 HAProxy 的 Ingress 控制器。

五、安装 Nginx-Ingress

访问架构图:

5.1、helm 安装 Ingress

# 1. 新建 ingress 存放目录
[root@kubernetes-master-001 ~]# mkdir ingress && cd ingress

# 2. 添加官方 ingress-nginx 的 helm 仓库
[root@kubernetes-master-001 ~/ingress]# helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
"ingress-nginx" has been added to your repositories

# 3. 搜索仓库中已有的 ingress-nginx 版本信息 (建议安装 0.40.2 以上的版本)
[root@kubernetes-master-001 ~/ingress]# helm search repo ingress-nginx
NAME                        CHART VERSION   APP VERSION DESCRIPTION                                       
ingress-nginx/ingress-nginx 4.0.9           1.0.5       Ingress controller for Kubernetes using NGINX a...

# 4. 拉取 ingress-nginx 的包
helm pull [repo 名称 /chart 名称]
[root@kubernetes-master-001 ~/ingress]# helm pull ingress-nginx/ingress-nginx

# 5. 解压包,修改 values.yaml 配置文件
[root@kubernetes-master-001 ~/ingress]# tar -xf ingress-nginx-4.0.9.tgz && cd ingress-nginx

# 6. 修改 values.yaml
# 修改镜像
# 注释掉 registry,image,digest,并打开 repository
repository: willdockerhub/ingress-nginx-controller
tag: "v1.0.5"

repository: jettech/kube-webhook-certgen
tag: v1.5.2

repository: mirrorgooglecontainers/defaultbackend-amd64
tag: "1.5"
# 修改 dns 策略
dnsPolicy: ClusterFirstWithHostNet

# 使用 hostNetwork, 即使用宿主机上的端口 80 443
hostNetwork: false

# 使用 DaemonSet, 将 ingress 部署在指定节点上
kind: DaemonSet

# 节点选择,将需要部署的节点打上 ingress=true 的 label
nodeSelector:
  kubernetes.io/os: linux
  ingress: "true"

# 选择节点打 label
kubectl label node node01 ingress=true

# 创建一个 ingress 的 namespace
kubectl create ns ingress-nginx

# 创建 ingress
# 如果提示 cannot re-use a name that is still in use
# 则先 helm uninstall ingress-nginx --namespace ingress-nginx
# 如果安装完成后无法访问其他节点日志,则需要配置 hosts,将所有节点配置到 hosts 中即可
helm install ingress-nginx ingress-nginx -n ingress-nginx

5.2、直接部署安装

#1. 部署 ingress-nginx 比较简单,直接下载 github 上的 deploy.yaml 部署即可,由于网络问题镜像如果拉取失败,我们可以直接去 GitHub 复制这个 yaml 文件
[root@kubernetes-master-001 ~]# wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.0.0/deploy/static/provider/baremetal/deploy.yaml

#2. 修改镜像地址
[root@kubernetes-master-001 ~]# sed -i's@k8s.gcr.io/ingress-nginx/controller:v1.0.0\(.*\)@willdockerhub/ingress-nginx-controller:v1.0.0@' deploy.yaml

[root@kubernetes-master-001 ~]# sed -i's@k8s.gcr.io/ingress-nginx/kube-webhook-certgen:v1.0\(.*\)$@hzde0128/kube-webhook-certgen:v1.0@' deploy.yaml

#3. 安装 ingress-nginx
[root@kubernetes-master-001 ~]# kubectl apply  -f ingress-1.0.0.yaml
namespace/ingress-nginx created
serviceaccount/ingress-nginx created
configmap/ingress-nginx-controller created
clusterrole.rbac.authorization.k8s.io/ingress-nginx configured
clusterrolebinding.rbac.authorization.k8s.io/ingress-nginx configured
role.rbac.authorization.k8s.io/ingress-nginx created
rolebinding.rbac.authorization.k8s.io/ingress-nginx created
service/ingress-nginx-controller-admission created
service/ingress-nginx-controller created
deployment.apps/ingress-nginx-controller created
ingressclass.networking.k8s.io/nginx configured
validatingwebhookconfiguration.admissionregistration.k8s.io/ingress-nginx-admission configured
serviceaccount/ingress-nginx-admission created
clusterrole.rbac.authorization.k8s.io/ingress-nginx-admission configured
clusterrolebinding.rbac.authorization.k8s.io/ingress-nginx-admission configured
role.rbac.authorization.k8s.io/ingress-nginx-admission created
rolebinding.rbac.authorization.k8s.io/ingress-nginx-admission created
job.batch/ingress-nginx-admission-create created
job.batch/ingress-nginx-admission-patch created

#4. 查看 ingress-nginx 状态
[root@kubernetes-master-001 ~]# kubectl  get pod  -n ingress-nginx
NAME                                        READY   STATUS      RESTARTS   AGE
ingress-nginx-admission-create-fgcrk        0/1     Completed   0          20m
ingress-nginx-admission-patch-b6d96         0/1     Completed   0          20m
ingress-nginx-controller-6b64bc6f47-4sd7g   1/1     Running     0          20m

[root@kubernetes-master-001 ~]# kubectl  get svc  -n ingress-nginx
NAME                                 TYPE        CLUSTER-IP       EXTERNAL-IP   PORT (S)                      AGE
ingress-nginx-controller             NodePort    10.107.105.141   <none>        80:31386/TCP,443:31083/TCP   20m
ingress-nginx-controller-admission   ClusterIP   10.103.7.53      <none>        443/TCP                      20m

六、部署示例验证

6.1、创建 Pod

[root@kubernetes-master-001 ~]# kubectl  create deployment web --image=nginx
deployment.apps/web created

[root@kubernetes-master-001 ~]# kubectl get pods
NAME                  READY   STATUS    RESTARTS   AGE
web-96d5df5c8-prps4   1/1     Running   0          44s

6.2、对外暴露应用

[root@kubernetes-master-001 ~]# kubectl  expose  deployment  web  --port=80 --target-port=80 --type=NodePort
service/web exposed

[root@kubernetes-master-001 ~]# kubectl  get svc
NAME             TYPE        CLUSTER-IP     EXTERNAL-IP   PORT (S)        AGE
kubernetes       ClusterIP   10.96.0.1      <none>        443/TCP        4d23h
web              NodePort    10.106.214.3   <none>        80:30645/TCP   4d23h

6.3、创建 Ingress 规则

ingress-http.yaml

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: example-ingress
  namespace: default
  annotations:
    kubernetes.io/ingress.class: "nginx"    # 最关键的标记。K8s 本身不自带 Ingress 的“处理能力”,你必须安装一个 Ingress Controller(如 Nginx Ingress)。这个注解告诉集群:“这条路由规则由 Nginx 控制器来接管”。
spec:
  rules:
  - host: example.ingredemo.com             # 只有 HTTP 请求头中的 Host 字段匹配 example.ingredemo.com 时,规则才会生效。
    http:
      paths:
      - path: "/"
        pathType: Prefix                # Prefix:前缀匹配。例如 / 会匹配所有以 / 开头的请求(即所有请求)。
        backend:
          service: 
            name: web
            port: 
              number: 80

操作

[root@kubernetes-master-001 ~]# kubectl  apply  -f  ingress-http.yaml
ingress.networking.k8s.io/example-ingress created

[root@kubernetes-master-001 ~]# kubectl get  svc -n ingress-nginx
NAME                                 TYPE        CLUSTER-IP       EXTERNAL-IP   PORT (S)                      AGE
ingress-nginx-controller             NodePort    10.107.105.141   <none>        80:31386/TCP,443:31083/TCP   3h55m
ingress-nginx-controller-admission   ClusterIP   10.103.7.53      <none>        443/TCP                      3h55m

[root@kubernetes-master-001 ~]# kubectl  get ingress
NAME              CLASS    HOSTS                   ADDRESS          PORTS   AGE
example-ingress   <none>   example.ingredemo.com                    80      5s

七、Ingress-Nginx 优化

上面每次部署 ingres-nginx 都随机一个 nodePort ,而使用 ingres-nginx 访问的时候也要以 域名:端口 的形式去访问如何直接使用域名去访问呢?下面介绍 hostNetwork 的方式部署 Ingress-Nginx。

7.1、下载 Ingress-Nginx

[root@kubernetes-master-001 ~]#  wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.0.0/deploy/static/provider/baremetal/deploy.yaml

[root@kubernetes-master-001 ~]# sed -i's@k8s.gcr.io/ingress-nginx/controller:v1.0.0\(.*\)@willdockerhub/ingress-nginx-controller:v1.0.0@' deploy.yaml
[root@kubernetes-master-001 ~]# sed -i's@k8s.gcr.io/ingress-nginx/kube-webhook-certgen:v1.0\(.*\)$@hzde0128/kube-webhook-certgen:v1.0@' deploy.yaml

7.2、使用 hostNetwork

默认 ingress-nginx 随机提供 nodeport 端口,开启 hostNetwork 启用 80、443 端口。 修改 Deployment 下面的 spec

参数如下:

spec:
  hostNetwork: true # 新增
  dnsPolicy: ClusterFirst
  containers:
    - name: controller
      image: willdockerhub/ingress-nginx-controller:v1.0.0  # 更换镜像地址
      imagePullPolicy: IfNotPresent
      lifecycle:

7.3、修改负载均衡问题

把 kind: Deployment 改为 kind: DaemonSet 模式,这样每台 node 上都有 ingress-nginx-controller pod 副本。 参数如下:

# Source: ingress-nginx/templates/controller-deployment.yaml
apiVersion: apps/v1
#kind: Deployment   # 注释
kind: DaemonSet     # 新增
metadata:
  labels:
    helm.sh/chart: ingress-nginx-4.0.1

7.4、修改 ingressClass 问题

如果不关心 ingressClass 或者很多没有 ingressClass 配置的 ingress 对象, 添加参数 ingress-controller --watch-ingress-without-class=true 。

args:
  - /nginx-ingress-controller
  - --publish-service=$(POD_NAMESPACE)/ingress-nginx-dev-v1-test-controller
  - --election-id=ingress-controller-leader
  - --controller-class=k8s.io/ingress-nginx
  - --configmap=$(POD_NAMESPACE)/ingress-nginx-dev-v1-test-controller
  - --validating-webhook=:8443
  - --validating-webhook-certificate=/usr/local/certificates/cert
  - --validating-webhook-key=/usr/local/certificates/key
  - --watch-ingress-without-class=true  # 新增

7.5. 部署检查 ingress

#1. 部署 ingress
[root@kubernetes-master-001 ~]# kubectl  apply  -f deploy.yaml 
namespace/ingress-nginx created
serviceaccount/ingress-nginx created
configmap/ingress-nginx-controller created
clusterrole.rbac.authorization.k8s.io/ingress-nginx unchanged
clusterrolebinding.rbac.authorization.k8s.io/ingress-nginx unchanged
role.rbac.authorization.k8s.io/ingress-nginx created
rolebinding.rbac.authorization.k8s.io/ingress-nginx created
service/ingress-nginx-controller-admission created
service/ingress-nginx-controller created
daemonset.apps/ingress-nginx-controller created
ingressclass.networking.k8s.io/nginx unchanged
validatingwebhookconfiguration.admissionregistration.k8s.io/ingress-nginx-admission configured
serviceaccount/ingress-nginx-admission created
clusterrole.rbac.authorization.k8s.io/ingress-nginx-admission unchanged
clusterrolebinding.rbac.authorization.k8s.io/ingress-nginx-admission unchanged
role.rbac.authorization.k8s.io/ingress-nginx-admission created
rolebinding.rbac.authorization.k8s.io/ingress-nginx-admission created
job.batch/ingress-nginx-admission-create created
job.batch/ingress-nginx-admission-patch created

#2. 部署 pod
[root@kubernetes-master-001 ~]# kubectl  get  pod -n ingress-nginx -o wide
NAME                                   READY   STATUS      RESTARTS   AGE    IP               NODE                  NOMINATED NODE   READINESS GATES
ingress-nginx-admission-create-cm5h4   0/1     Completed   0          4m4s   10.244.1.97      kubernetes-node-002   <none>           <none>
ingress-nginx-admission-patch-49wc7    0/1     Completed   1          4m4s   10.244.1.98      kubernetes-node-002   <none>           <none>
ingress-nginx-controller-cks5x         1/1     Running     0          4m5s   192.168.13.102   kubernetes-node-002   <none>           <none>
ingress-nginx-controller-cqgfb         1/1     Running     0          4m4s   192.168.13.101   kubernetes-node-001   <none>           <none>

#3.node 节点检查端口
[root@kubernetes-node-002 ~]# netstat  -lntp |grep 443
tcp        0      0 0.0.0.0:443             0.0.0.0:*               LISTEN      70037/nginx: master 
tcp6       0      0 :::8443                 :::*                    LISTEN      70016/nginx-ingress 
tcp6       0      0 :::443                  :::*                    LISTEN      70037/nginx: master 
[root@kubernetes-node-002 ~]# netstat  -lntp |grep 80
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      70037/nginx: master 
tcp6       0      0 :::80                   :::*                    LISTEN      70037/nginx: master

[root@kubernetes-node-001 ~]# netstat  -lntp |grep 443
tcp        0      0 0.0.0.0:443             0.0.0.0:*               LISTEN      93825/nginx: master 
tcp6       0      0 :::8443                 :::*                    LISTEN      93765/nginx-ingress 
tcp6       0      0 :::443                  :::*                    LISTEN      93825/nginx: master 
[root@kubernetes-node-001 ~]# netstat  -lntp |grep 80
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      93825/nginx: master 
tcp6       0      0 :::80                   :::*                    LISTEN      93825/nginx: master

八、Ingress 与 Ingress-Controller

8.1、Ingress 与 Ingress-Controller 的关系

在 Kubernetes 中,它们是“配置”与“程序”的关系:

  • Ingress (资源对象):就像是一份路由说明书。它只是一条记录,告诉集群:example.com 应该去访问 web 服务。它存放在 etcd 数据库里,自己不会干活。
  • Nginx-Ingress-Controller (执行程序):这是真正的反向代理服务器。它是一个一直运行的 Pod,任务是:
    • 时刻盯着 API Server:“有没有新的 Ingress 说明书(YAML)发布?”
    • 一旦发现新规则,它就自动修改内部的 nginx.conf。
    • 执行 nginx -s reload,让配置生效,开始引导流量。

8.2、为什么要以 Pod 形式运行

将 Nginx 运行在 Pod 里有几个核心优势:

  • 动态扩容:如果你的网站流量暴增,可以直接增加 Ingress Pod 的副本数。
  • 高可用:如果承载 Nginx 的某个节点挂了,K8s 会自动在其他节点重启这个 Pod,保证入口不挂。
  • 自愈性:Nginx 配置写错导致进程崩溃?K8s 会帮你自动拉起。

8.3、安装方式

Kubernetes 官方代码库里并不包含 Nginx 的二进制文件,它只定义了 Ingress 的标准。

  • 如果你用的是自建集群(二进制或 Kubeadm):你必须手动部署,通常是执行官方提供的 deploy.yaml 或者使用 Helm。
  • 如果你用的是云平台(如阿里云 ACK):云厂商通常会在创建集群时询问你是否勾选“安装 Ingress 组件”,它们会帮你预装好。

8.4、外部流量是怎么进到这个 Pod

既然 Nginx 运行在 Pod 里,它也有一个 Pod IP。那外面的用户怎么访问这个 Pod 呢?通常有三种方案:

  • NodePort:给 Nginx Pod 再套一个 Service,暴露一个 30000+ 的端口。
  • LoadBalancer:配合云厂商的负载均衡器(SLB),直接给 Nginx Pod 映射一个公网 IP。
  • HostNetwork:让 Nginx Pod 直接占用宿主机的 80/443 端口。这对性能最好,但一个节点只能跑一个副本。

8.5、核心工作流

  • 编写 YAML:定义域名和路径。
  • Controller 监测:运行在 Pod 里的 Nginx-Ingress-Controller 发现了你的 YAML。
  • 配置转化:它把 YAML 转化成 nginx.conf 里的 upstream 和 server 块。
  • 流量分发:外部请求打到 Nginx Pod -> Nginx 根据配置 -> 直接转发给后端的业务 Pod IP。

8.6、Ingress 规则

查看集群中定义了哪些 IngressClass

kubectl get ingressclass

输出示例:

NAMECONTROLLER
nginx-publick8s.io/ingress-nginx-public
nginx-internalk8s.io/ingress-nginx-internal