Ingress的出现
Ingress是一种Kubernetes资源,用于将外部流量路由到Kubernetes集群内的服务。与NodePort相比,它提供了更高级别的路由功能和负载平衡,可以根据HTTP请求的路径、主机名、HTTP方法等来路由流量。
因此,可以说Ingress是为了弥补NodePort在流量路由方面的不足而生的。使用NodePort,只能将流量路由到一个具体的Service,并且必须使用Service的端口号来访问该服务。但是,使用Ingress,就可以使用自定义域名、路径和其他HTTP头来定义路由规则,以便将流量路由到不同的Service。
tantianran@test-b-k8s-master:~$ kubectl api-resources | grep ingress ingressclasses networking.k8s.io/v1 false IngressClass ingresses ing networking.k8s.io/v1 true Ingress
此外,Ingress还可以与负载均衡器配合使用,以提供高可用性和水平扩展。这些功能使得Ingress比NodePort更适合在生产环境中使用。
Ingress和Ingress Controller
- 「Ingress」Ingress 是 Kubernetes 中的一个抽象资源,它提供了一种定义应用暴露入口的方法,可以帮助管理员在 Kubernetes 集群中管理多个服务的访问入口,方便用户访问。Ingress资源对象只是一个规范化的API对象,用于定义流量路由规则和 TLS 设置等信息。它本身不会直接处理或转发流量,而是需要配合一个 Ingress 控制器来实现。
刚才提到,Ingress 需要配合 Ingress Controller 使用,需要注意了,不同的 Ingress Controller 可能支持的功能和配置方式不同,需要根据实际情况进行选择和配置。
- 「Ingress Controller」Ingress 控制器是一个独立的组件,它会监听 Kubernetes API 中的 Ingress 资源变化,并根据定义的路由规则配置负载均衡器、反向代理或其他网络代理,从而实现外部流量的转发。因此,可以将 Ingress 控制器视为 Ingress 资源的实际执行者。
总之,Kubernetes 的 Ingress 资源对象需要配合 Ingress 控制器才能实现外部流量的转发和路由。Ingress 控制器是 Ingress 资源的实际执行者,负责根据定义的路由规则配置网络代理。
主流的Ingress Controller
在 Kubernetes 中,有很多不同的 Ingress 控制器可以选择,例如 Nginx、Traefik、HAProxy、Envoy 等等。不同的控制器可能会提供不同的功能、性能和可靠性,可以根据实际需求来选择合适的控制器。Kubernetes生态系统中有许多不同的Ingress控制器可供选择,其中比较主流的有:
- Nginx Ingress Controller:基于Nginx的Ingress控制器,提供了广泛的功能和配置选项。
- Traefik Ingress Controller:Traefik是一个流行的反向代理和负载均衡器,Traefik Ingress Controller提供了灵活的配置选项和自动发现服务的功能。
- Istio Ingress Gateway:Istio是一种服务网格,它提供了基于Envoy代理的Ingress Gateway来管理入站和出站流量。
- Contour Ingress Controller:基于Envoy代理的Ingress控制器,具有高度可扩展性和灵活的路由规则。
- Kong Ingress Controller:Kong是一个API网关,提供了可扩展的路由和服务管理功能。
- Ambassador API Gateway:Ambassador是一个Kubernetes-native API Gateway,提供了自动化的服务发现和路由管理功能。
可以根据实际需要选择适当的Ingress控制器,并进行相应的配置和部署。
控制器的部署方案
Ingress控制器通常建议部署在 Kubernetes 集群内部。这样可以确保 Ingress 控制器与 Kubernetes API Server 之间的网络延迟较低,并且可以通过 Kubernetes Service 来管理 Ingress 控制器的负载均衡和高可用性。在 Kubernetes 集群内部部署 Ingress 控制器通常有两种方式:
- 部署一个独立的 Ingress 控制器 Pod:可以通过将 Ingress 控制器部署为一个独立的 Pod,使用 Kubernetes Service 对其进行负载均衡和暴露服务。
- 部署一个 DaemonSet 类型的 Ingress 控制器:可以通过部署一个 DaemonSet 类型的 Ingress 控制器,使每个节点上都运行一个 Ingress 控制器 Pod,并通过 Kubernetes Service 对其进行负载均衡和暴露服务。
无论是哪种方式,Ingress 控制器都应该被部署在 Kubernetes 集群内部,以便与 Kubernetes API Server 进行通信,并与 Kubernetes 资源(如 Service、Pod、Endpoints 等)进行交互。
安装Nginx Ingress
Nginx 是一个高性能的 Web 服务器和反向代理服务器,可以提供静态内容的快速响应,同时也可以通过反向代理将请求转发到后端应用程序。
Nginx Ingress 是基于 Nginx 的 Kubernetes Ingress 控制器,它可以在 Kubernetes 集群中提供负载均衡、路由和 TLS 终止等功能。它将 Kubernetes Ingress API 对象转换为 Nginx 配置,并将其应用于 Nginx 服务器。通过 Nginx Ingress,可以轻松地将 HTTP(S) 流量路由到 Kubernetes 中的不同服务。
https://kubernetes.github.io/ingress-nginx/https://github.com/kubernetes/ingress-nginx/
安装 Nginx Ingress Controller 的方式有多种,以下是其中的一些:
- Helm 安装:使用 Helm 工具,可以在 Kubernetes 集群上轻松安装和升级 Nginx Ingress Controller。
- Kubernetes YAML 安装:使用 Kubernetes YAML 配置文件,可以在 Kubernetes 集群上安装 Nginx Ingress Controller。
- Docker 安装:可以通过 Docker 容器运行 Nginx Ingress Controller。
- Bare-metal 安装:可以在没有 Kubernetes 集群的裸机上安装 Nginx Ingress Controller。
- Cloud 安装:在某些云平台上,可以使用托管服务的形式安装 Nginx Ingress Controller,例如 Google Cloud Platform 上的 GKE。
无论选择哪种方式,都需要根据实际情况进行调整和配置,以确保 Nginx Ingress Controller 能够正常工作。
- 安装Nginx Ingress Controller
使用Helm安装:
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx helm repo update kubectl create namespace ingress-nginx helm install ingress-nginx ingress-nginx/ingress-nginx \ --namespace ingress-nginx \ --set controller.publishService.enabled=true kubectl get pods -n ingress-nginx kubectl get svc -n ingress-nginx
上面的命令将在命名空间中安装控制器,如果该命名空间尚不存在,则创建该命名空间ingress-nginx。 此命令是幂等的:
- 如果未安装入口控制器,它将安装它,
- 如果已安装入口控制器,它将对其进行升级。
使用YAML安装:
wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.6.4/deploy/static/provider/cloud/deploy.yaml mv deploy.yaml ingress-nginx-controller.yaml kubectl apply -f ingress-nginx-controller.yaml # 或者直接 kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.6.4/deploy/static/provider/cloud/deploy.yaml
注意,可能会应网络问题无法拉取相关镜像,可以修改ingress-nginx-controller.yaml文件里image字段的仓库地址,我已经把镜像传到我的Docker Hub仓库中:
- tantianran/ingress-controller:v1.6.4
- tantianran/kube-webhook-certgen:v20220916-gd32f8c343
例如,修改后:
tantianran@test-b-k8s-master:~$ egrep "image" ingress-nginx-controller.yaml | grep -v imagePullPolicy image: tantianran/ingress-controller:v1.6.4 image: tantianran/kube-webhook-certgen:v20220916-gd32f8c343 image: tantianran/kube-webhook-certgen:v20220916-gd32f8c343
- 安装后查看pod
tantianran@test-b-k8s-master:~$ kubectl get pod -n ingress-nginx NAME READY STATUS RESTARTS AGE ingress-nginx-admission-create-z4hlb 0/1 Completed 0 47h ingress-nginx-admission-patch-ffbwz 0/1 Completed 0 47h ingress-nginx-controller-5f4c9fdd9b-l55ch 1/1 Running 1 (10m ago) 47h
- ingress-nginx-controller是Ingress-nginx的控制器组件,它负责监视Kubernetes API server上的Ingress对象,并根据配置动态地更新Nginx配置文件,实现HTTP(S)的负载均衡和路由。
- ingress-nginx-admission-create和ingress-nginx-admission-patch都是Kubernetes Admission Controller,它们不是一直处于运行状态的容器,而是根据需要动态地生成和销毁。这些Admission Controller在Kubernetes中以Deployment的方式进行部署,因此,它们的Pod将根据副本数创建多个副本,并根据负载和需要动态地生成和销毁。
- ingress-nginx-admission-create是一个Kubernetes Admission Controller,它可以拦截Kubernetes集群内的Ingress对象的创建操作,并在Ingress对象创建之前,对其进行一些额外的验证和处理。
- ingress-nginx-admission-patch也是一个Kubernetes Admission Controller,它可以在Ingress对象更新之前,对其进行额外的验证和处理,类似于ingress-nginx-admission-create。
「有没有注意到nginx-admission-create和ingress-nginx-admission-patch这两个pod的状态是Completed?」 当这两个Pod被创建时,它将开始运行容器,执行必要的初始化和验证操作,然后尝试处理Kubernetes API server发送的请求。如果请求已经被处理完毕,容器将正常终止,并将Pod的状态设置为Completed。因此,Pod处于Completed状态并不表示有任何问题或错误,而是表示容器已经完成了它需要完成的任务并终止了运行。
需要注意的是,如果在Pod终止之前出现错误或异常,Pod的状态将会被设置为Failed,这可能需要进行进一步的故障排除和修复。
- 安装后查看Service
tantianran@test-b-k8s-master:~$ kubectl get svc -n ingress-nginx NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE ingress-nginx-controller LoadBalancer 10.106.241.48 <pending> 80:30806/TCP,443:30178/TCP 2d ingress-nginx-controller-admission ClusterIP 10.109.39.65 <none> 443/TCP 2d tantianran@test-b-k8s-master:~$
ingress-nginx-controller Service:这个Service负责将请求转发到ingress-nginx-controller Pods。它通常会将流量分发到ingress-nginx-controller的多个副本中,并确保副本集的负载平衡。这个Service可以被配置为使用NodePort、LoadBalancer或ClusterIP类型,根据需要进行暴露。
ingress-nginx-controller-admission Service:这个Service是用于 Kubernetes Admission Webhooks 的,允许在创建、更新或删除资源时,对其进行校验或修改。它提供了一个API Endpoint,用于与 Kubernetes API Server 进行通信,以便进行这些校验或修改。该Service也可以被配置为使用NodePort、LoadBalancer或ClusterIP类型,根据需要进行暴露。
通常情况下,ingress-nginx-controller和ingress-nginx-controller-admission都是在同一个Deployment中运行的,以确保它们始终具有相同的标签。这些标签允许其他Kubernetes对象(例如Ingress)可以识别哪些Pods是由ingress-nginx-controller和ingress-nginx-controller-admission负责的,并将请求路由到正确的Pods中。
创建测试应用和ingress资源对象
- 创建测试应用的Deployment和Services,编辑test-goweb.yaml:
apiVersion: apps/v1 kind: Deployment metadata: labels: app: test-goweb name: test-goweb spec: replicas: 6 selector: matchLabels: app: test-goweb template: metadata: labels: app: test-goweb spec: containers: - image: 192.168.11.247/web-demo/goweb-demo:20221229v3 name: goweb-demo ports: - containerPort: 8090 protocol: TCP --- apiVersion: v1 kind: Service metadata: labels: app: test-goweb name: test-goweb spec: ports: - name: 80-8090 port: 80 protocol: TCP targetPort: 8090 selector: app: test-goweb type: ClusterIP
在上面的Yaml配置文件中,采用的Service类型是ClusterIP类型,ClusterIP类型的Service只能从K8S集群内部访问,因此需要将其与Ingress Controller结合使用,以便外部客户端可以访问集群中的应用程序。当客户端请求到达Ingress时,Ingress Controller会将请求路由到相应的Service,然后Service再将请求路由到Pod中运行的容器。
kubectl apply -f test-goweb.yaml
- 查看Service资源
tantianran@test-b-k8s-master:~$ kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 120d test-goweb ClusterIP 10.105.248.32 <none> 80/TCP 81m # 这个等会是要用到的,Service名称为test-goweb,端口为80 tantianran@test-b-k8s-master:~$
- 查看当前集群中定义的所有IngressClass对象
tantianran@test-b-k8s-master:~$ kubectl get ingressclass NAME CONTROLLER PARAMETERS AGE nginx k8s.io/ingress-nginx <none> 2d2h # 这个nginx控制器就是我们等会要用的 tantianran@test-b-k8s-master:~$
在k8s中,Ingress 资源对象可以用来暴露服务,将外部流量路由到内部集群服务。但是,在一个集群中,可能需要使用不同的 Ingress 控制器来满足不同的需求,而每个控制器都需要使用不同的配置和规则。这就是 IngressClass 的作用。通过定义不同的 IngressClass,可以为不同的 Ingress 控制器指定不同的配置和规则,从而更好地管理 Ingress 资源对象。
- 定义Ingress资源的YAML文件,用于创建Ingress资源对象,编辑test-goweb-ingress.yaml:
apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: test-goweb spec: ingressClassName: nginx rules: - host: "test.noblameops.local" http: paths: - path: /login pathType: Prefix backend: service: name: test-goweb port: number: 80 - path: /home pathType: Prefix backend: service: name: test-goweb port: number: 80 - path: / pathType: Prefix backend: service: name: test-goweb port: number: 80
这个 Ingress 配置了3个路径,后端为同一个Services,下面仅拿路径"/"做讲解:
- 在 metadata 字段中,name 字段指定了创建的 Ingress 资源的名称。
- 在 spec 字段中,ingressClassName 字段指定了要使用的 Ingress 控制器。在这里使用了名为 nginx 的 Ingress 类别。
- rules 字段指定了 Ingress 的规则列表。在这里只有一个规则,即当外部流量通过 HTTP 访问 Ingress 时,应该使用下面的配置:
- path 字段指定了应该匹配的 URL 路径。在这里,所有路径都匹配 /。
- pathType 字段指定了路径匹配方式,这里使用了 Prefix 类型,表示只要路径以 / 开头,就匹配成功。
- backend 字段指定了要将匹配的流量转发到哪个后端(Service)。在这里选择了名为 test-goweb 的 Service(ClusterIP类型),该 Service 中的 80 端口将接收流量。
开始创建
kubectl apply -f test-goweb-ingress.yaml
- 创建ingress资源对象后查看
tantianran@test-b-k8s-master:~$ kubectl get ingress NAME CLASS HOSTS ADDRESS PORTS AGE test-goweb nginx test.noblameops.local 10.105.254.90 80 9m32s tantianran@test-b-k8s-master:~$
这条命令输出了一个名为 test-goweb 的 Ingress 资源,它将路由到 test.noblameops.local 这个主机名,并在端口 80 上监听请求。我们来看看输出字段的意义:
- NAME: Ingress 资源的名称是 test-goweb。
- CLASS: Ingress 资源使用的 Ingress 类别为 nginx。
- HOSTS: Ingress 资源配置的主机名为 test.noblameops.local。这表明所有请求都将被路由到这个主机名。
- ADDRESS: 没有指定 IP 地址,这意味着 Ingress 控制器将为 Ingress 资源分配一个 IP 地址。
- PORTS: Ingress 资源将监听端口 80 上的请求。
- AGE: Ingress 资源的创建时间是 9 秒前。
Ingress Controller的暴露方式
当使用K8S中的Ingress资源对象来暴露应用时,用户访问应用的入口是 Ingress Controller 的地址。Ingress Controller 会根据 Ingress 规则将请求路由到相应的服务,并将服务的响应返回给客户端。这时候就要把Ingress Controller暴露出去,暴露方式有以下几种:
- NodePort:使用 NodePort 服务类型来暴露 Ingress Controller,这种方式可以将 Ingress Controller 暴露到 Kubernetes 集群的所有节点上,通过节点的 IP 地址和 NodePort 可以访问到 Ingress Controller。NodePort方式将Ingress Controller的端口映射到节点的一个随机端口上,这种方式的优点是比较简单,易于配置和管理。但是,它的缺点是需要暴露每个节点的端口,这可能会导致端口号不够用,或者在安全方面存在一些隐患。
- HostNetwork(共享宿主机网络):使用 HostNetwork 字段来暴露 Ingress Controller,这种方式可以将 Ingress Controller 直接暴露到节点的网络命名空间中,从而可以直接使用节点的 IP 地址来访问 Ingress Controller。HostNetwork方式将Ingress Controller的容器直接绑定到主机的网络上,这种方式的优点是可以直接使用主机的网络资源,性能较好,并且可以避免NodePort方式中的安全隐患。缺点是如果Ingress Controller容器崩溃,它会影响主机上的其他容器。
- LoadBalancer:使用 LoadBalancer 服务类型来暴露 Ingress Controller,这种方式可以将 Ingress Controller 暴露到云服务提供商的负载均衡器上,从而可以通过负载均衡器的 IP 地址来访问 Ingress Controller。可以实现更好的负载均衡和高可用性。这种方式的优点是可以自动创建负载均衡器,可以动态地分配IP地址,易于管理和扩展。但是,它的缺点是需要依赖云厂商提供的负载均衡器服务,对于一些不支持负载均衡器服务的云平台或者本地环境不太适用。
- ExternalIPs:使用 ExternalIPs 字段来指定外部 IP 地址来暴露 Ingress Controller,这种方式需要手动配置外部 IP 地址,并且只适用于具有静态 IP 地址的环境。
接下来的场景,我们采用NodePort方式来暴露Ingress Controller。
- 查看ingress controller的Service
从下面的查询结果可以看到,ingress-nginx-controller的Service类型默认是LoadBalancer,该类型可以通过云提供商的负载均衡器(如 AWS ELB、Google Cloud Load Balancer 等)来公开服务。
tantianran@test-b-k8s-master:~$ kubectl get svc -n ingress-nginx NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE ingress-nginx-controller LoadBalancer 10.106.241.48 <pending> 80:30806/TCP,443:30178/TCP 2d6h ingress-nginx-controller-admission ClusterIP 10.109.39.65 <none> 443/TCP 2d6h tantianran@test-b-k8s-master:~$
我的环境是本地的K8S环境,我只是需要在本地 K8S 环境中进行测试和开发而已,所以可以将其修改为 NodePort 类型,以便将服务公开到集群内的所有节点。NodePort 类型的 Service 可以将服务公开到 K8S 集群中的所有节点的固定端口,这种方式就是适用于本地测试和开发环境。
- 修改成NodePort的方式: 在下载回来的ingress-nginx-controller.yaml配置文件里找到ingress-nginx-controller的Service
apiVersion: v1 kind: Service metadata: labels: app.kubernetes.io/component: controller app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx app.kubernetes.io/version: 1.6.4 name: ingress-nginx-controller namespace: ingress-nginx spec: externalTrafficPolicy: Local ipFamilies: - IPv4 ipFamilyPolicy: SingleStack ports: - appProtocol: http name: http port: 80 protocol: TCP targetPort: http nodePort: 30080 # 增加此处 - appProtocol: https name: https port: 443 protocol: TCP targetPort: https nodePort: 30443 # 增加此处 selector: app.kubernetes.io/component: controller app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx type: NodePort # 修改此处
修改完成后干掉原来的再重新创建:
kubectl delete -f ingress-nginx-controller.yaml kubectl create -f ingress-nginx-controller.yaml tantianran@test-b-k8s-master:~$ kubectl get svc -n ingress-nginx NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE ingress-nginx-controller NodePort 10.100.117.82 <none> 80:30080/TCP,443:30443/TCP 9s # 成功修改为NodePort类型 ingress-nginx-controller-admission ClusterIP 10.101.46.195 <none> 443/TCP 9s tantianran@test-b-k8s-master:~$ kubectl get pod -n ingress-nginx -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES ingress-nginx-admission-create-ftntz 0/1 Completed 0 10m 10.244.240.43 test-b-k8s-node01 <none> <none> ingress-nginx-admission-patch-9xvhv 0/1 Completed 1 10m 10.244.240.49 test-b-k8s-node01 <none> <none> ingress-nginx-controller-55cbbdbf87-dq558 1/1 Running 0 10m 10.244.240.23 test-b-k8s-node01 <none> <none> tantianran@test-b-k8s-master:~$
- 访问
ingress-nginx-controller的POD只设置了1个,且当前是运行在 test-b-k8s-node01 节点上,该节点的宿主机IP就可以作为入口了,然后在系统hosts添加域名映射:
192.168.11.14 test.noblameops.local
Https配置
下面使用cfssl工具给域名test.noblameops.local生成自签名证书,并在k8s中使用nginx ingress以https的方式暴露。
- 安装cfssl相关工具
curl -L https://github.com/cloudflare/cfssl/releases/download/v1.5.0/cfssl_1.5.0_linux_amd64 -o cfssl curl -L https://github.com/cloudflare/cfssl/releases/download/v1.5.0/cfssljson_1.5.0_linux_amd64 -o cfssljson curl -L https://github.com/cloudflare/cfssl/releases/download/v1.5.0/cfssl-certinfo_1.5.0_linux_amd64 -o cfssl-certinfo chmod +x cfssl cfssljson cfssl-certinfo mv cfssl cfssljson cfssl-certinfo /usr/local/bin/
- 创建证书配置文件 创建一个证书配置文件,例如cert.json,包含以下内容:
cat > cert.json <<EOF { "hosts": [ "test.noblameops.local" ], "key": { "algo": "rsa", "size": 2048 }, "names": [ { "C": "CN", "ST": "GuangDong", "L": "GuangZhou", "O": "Noblameops", "OU": "Noblameops", "CN": "test.noblameops.local" } ] } EOF
其中,"hosts"字段指定要包含在证书中的主机名或IP地址,这里只包含了"test.noblameops.local";"key"字段指定使用RSA算法和2048位密钥生成证书;"names"字段指定证书中的各种名称和标识,包括组织、部门和通用名称(CN)。
- 生成证书和密钥
cfssl gencert -initca cert.json | cfssljson -bare test.noblameops.local
这将生成三个文件:test.noblameops.local.csr、test.noblameops.local-key.pem和test.noblameops.local.pem。其中,test.noblameops.local.pem是证书文件,test.noblameops.local-key.pem是证书私钥文件。
- 创建k8s secret对象 将生成的证书和密钥文件打包成一个k8s secret对象。可以使用以下命令创建该对象
kubectl create secret tls test-noblameops-local-tls --key test.noblameops.local-key.pem --cert test.noblameops.local.pem kubectl get secret
其中,test-noblameops-local-tls是secret对象的名称,--key参数指定私钥文件的路径,--cert参数指定证书文件的路径。
- 创建nginx ingress资源 创建一个nginx ingress资源,将证书配置和TLS配置添加到该资源中
apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: test-goweb spec: ingressClassName: nginx tls: - hosts: - test.noblameops.local secretName: test-noblameops-local-tls rules: - host: "test.noblameops.local" http: paths: - path: /login pathType: Prefix backend: service: name: test-goweb port: number: 80 - path: /home pathType: Prefix backend: service: name: test-goweb port: number: 80 - path: / pathType: Prefix backend: service: name: test-goweb port: number: 80
- https访问