> 对于Kubernetes的Service,无论是Cluster-Ip和NodePort均是四层的负载,集群内的服务如何实现七层的负载均衡,这就需要借助于Ingress,Ingress控制器的实现方式有很多,比如nginx, Contour, Haproxy, trafik, Istio。

几种常用的ingress功能对比和选型:https://www.kubernetes.org.cn/5948.html

实现逻辑

Ingress-nginx是7层的负载均衡器 ,负责统一管理外部对k8s cluster中Service的请求。主要包含:

  • ingress-nginx-controller:根据用户编写的ingress规则(创建的ingress的yaml文件),动态的去更改nginx服务的配置文件,并且reload重载使其生效(是自动化的,通过lua脚本来实现);
  • Ingress资源对象:将Nginx的配置抽象成一个Ingress对象
  1. ingress controller通过和kubernetes api交互,动态的去感知集群中ingress规则变化
  2. 然后读取ingress规则(规则就是写明了哪个域名对应哪个service),按照自定义的规则,生成一段nginx配置
  3. 再写到nginx-ingress-controller的pod里,这个Ingress controller的pod里运行着一个Nginx服务,控制器把生成的nginx配置写入/etc/nginx/nginx.conf文件中
  4. 然后reload一下使配置生效。以此达到域名分别配置和动态更新的问题。

安装

  • 官方文档:https://kubernetes.github.io/ingress-nginx/deploy/
  • 部署文档:https://github.com/kubernetes/ingress-nginx/blob/nginx-0.30.0/docs/deploy/index.md
  • K8S Annotations文档: https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/annotations/#enable-cors

Support Versions

官网地址:https://github.com/kubernetes/ingress-nginx Ingress-NGINX version k8s supported version Alpine Version Nginx Version
v1.2.1 1.23, 1.22, 1.21, 1.20, 1.19 3.14.6 1.19.10†
v1.2.0 1.23, 1.22, 1.21, 1.20, 1.19 3.14.6 1.19.10†
v1.1.3 1.23, 1.22, 1.21, 1.20, 1.19 3.14.4 1.19.10†
v1.1.2 1.23, 1.22, 1.21, 1.20, 1.19 3.14.2 1.19.9†
v1.1.1 1.23, 1.22, 1.21, 1.20, 1.19 3.14.2 1.19.9†
v1.1.0 1.22, 1.21, 1.20, 1.19 3.14.2 1.19.9†
v1.0.5 1.22, 1.21, 1.20, 1.19 3.14.2 1.19.9†
v1.0.4 1.22, 1.21, 1.20, 1.19 3.14.2 1.19.9†
v1.0.3 1.22, 1.21, 1.20, 1.19 3.14.2 1.19.9†
v1.0.2 1.22, 1.21, 1.20, 1.19 3.14.2 1.19.9†
v1.0.1 1.22, 1.21, 1.20, 1.19 3.14.2 1.19.9†
v1.0.0 1.22, 1.21, 1.20, 1.19 3.13.5 1.20.1
v0.51.0 1.21, 1.20, 1.19 3.14.4 1.19.10†
v0.49.3 1.21, 1.20, 1.19 3.14.2 1.19.9†
v0.49.2 1.21, 1.20, 1.19 3.14.2 1.19.9†
v0.49.1 1.21, 1.20, 1.19 3.14.2 1.19.9†
v0.49.0 1.21, 1.20, 1.19 3.13.5 1.20.1
v0.48.1 1.21, 1.20, 1.19 3.13.5 1.20.1

Helm 部署(推荐)

部署文档:https://github.com/bitnami/charts/tree/master/bitnami/nginx-ingress-controller/#installing-the-chart

1. 安装helm

#Linux 直接运行curl 命令安装
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash

2. 下载Chart

#下载Chart
git clone https://github.com/bitnami/charts.git
cd charts/bitnami/nginx-ingress-controller/

3. 配置Chart

#为安装ingress的node节点添加label
#高可用模式可以在多个node上打标签,host模式不会调度在同一个机器
kubectl label node k8s-master ingress=true
kubectl label node k8s-node01 ingress=true

#查询包含指定label(ingress=true)的node节点
kubectl get nodes --show-labels -l "ingress=true"
##配置Chart
$ vim values.yaml

81 ##NGINX 的自定义configmap配置选项
82 ##官方文档: https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/configmap/
83 ##添加下面三行,获取客户端真实IP
84 #config: {}
85 config:
86   compute-full-forwarded-for: "true"
87   forwarded-for-header: "X-Forwarded-For"
88   use-forwarded-headers: "true"

##如果使用hostNetwork=true,设置reportNodeInternalIp=true,会将标志“report-node-internal-ip-address”传递给Nginx 入口控制器
104 reportNodeInternalIp: true

##配置控制器所需的 pod 数量
222 replicaCount: 2

##添加为host模式,启用主机网络,通过宿主机ip+port访问。
358 hostNetwork: true

##选择器,决定将ingress部署在哪些机器
402 #nodeSelector: {}
403 nodeSelector:
404   ingress: "true"

##禁用基于 NGINX 的默认后端
464 defaultBackend:
465   ## @param defaultBackend.enabled Enable a default backend based on NGINX
466   ##
467   enabled: false      ##设置为false

757 service:
758   ## @param service.type Kubernetes Service type for Controller
759   ##
760   #type: LoadBalancer
761   type: NodePort   ## 修改服务类型为 NodePort

4. 安装Chart

#添加chart仓库
helm repo add mynginx https://charts.bitnami.com/bitnami
helm repo update
helm search repo mynginx

#安装Chart
helm upgrade --install -f values.yaml --timeout 20m --create-namespace --namespace nginx-ingress-controller k8s-nginx mynginx/nginx-ingress-controller --wait --wait-for-jobs --debug

查看发布版本列表

helm list -n nginx-ingress-controller

升级Chart

##因服务类型为 NodePort,需要--set replicaCount=1 将pod数量改为1,否则端口被占用
helm upgrade -f values.yaml --timeout 30m --namespace nginx-ingress-controller k8s-nginx mynginx/nginx-ingress-controller --set replicaCount=1 --atomic --wait --wait-for-jobs --debug

##查看deployment信息
[ec2-user@eks ~]$ kubectl get deploy -n nginx-ingress-controller
NAME                                 READY   UP-TO-DATE   AVAILABLE   AGE
k8s-nginx-nginx-ingress-controller   1/1     1            1           39h

##手动对pod进行扩容
kubectl scale --replicas 2 deployment k8s-nginx-nginx-ingress-controller -n nginx-ingress-controller

卸载Chart

helm delete k8s-nginx -n nginx-ingress-controller

自定义yaml文件部署

安装ingress-controller

hostNetwork: true 修改为host模式,使用node节点ip访问

1.下载yaml文件

wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/nginx-0.30.0/deploy/static/mandatory.yaml -O ./ingress-nginx.yaml

#wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.2.1/deploy/static/provider/cloud/deploy.yaml -O ./ingress-nginx.yaml
  • 或者使用myblog/deployment/ingress/mandatory.yaml

2.修改yaml配置文件

$ grep -n5 nodeSelector ingress-nginx.yaml
199-    replicas: 2   #设置副本数,host模式不会被调度到同一个node
212-    spec:
213-      hostNetwork: true #添加为host模式
214-      # wait up to five minutes for the drain of connections
215-      terminationGracePeriodSeconds: 300
216-      serviceAccountName: nginx-ingress-serviceaccount
217:      nodeSelector:
218-        ingress: "true"        #替换此处,来决定将ingress部署在哪些机器
219-      containers:
220-        - name: nginx-ingress-controller
221-          image: quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.30.0
222-          args:

4.为安装ingress的node节点添加label

#为node节点添加label
#高可用模式可以在多个node上打标签,host模式不会调度在同一个机器
kubectl label node k8s-master ingress=true
kubectl label node k8s-node01 ingress=true

#查询包含指定label(ingress=true)的node节点
kubectl get nodes --show-labels -l "ingress=true"

5.创建ingress-controller

kubectl apply -f ingress-nginx.yaml

6.检查运行状态

$ kubectl get pods -n ingress-nginx
NAME                                        READY   STATUS    RESTARTS   AGE
nginx-ingress-controller-55dd6f8d7b-tkx77   1/1     Running   0          83d
nginx-ingress-controller-55dd6f8d7b-vkvqp   1/1     Running   0          83d

AWS 中部署

官方文档:https://docs.amazonaws.cn/eks/latest/userguide/alb-ingress.html
在 AWS 中,我们使用网络负载均衡器 (NLB) 在 服务类型 为LoadBalancer 之后暴露 NGINX 入口控制器。

  • AWS 负载均衡器 (NLB) 中的 TLS 终止

    默认情况下,TLS 在入口控制器中终止。但也可以在负载均衡器中终止 TLS。本节介绍如何使用 NLB 在 AWS 上执行此操作。

1.下载 deploy.yaml模板

wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.2.1/deploy/static/provider/aws/nlb-with-tls-termination/deploy.yaml

2.编辑文件并更改 Kubernetes 集群使用的 VPC CIDR

proxy-real-ip-cidr: XXX.XXX.XXX/XX

3.同时更改 AWS Certificate Manager (ACM) ID

service.beta.kubernetes.io/aws-load-balancer-ssl-cert: arn:aws:acm:us-west-2:XXXXXXXX:certificate/XXXXXX-XXXXXXX-XXXXXXX-XXXXXXXX

4.部署清单

kubectl apply -f deploy.yaml

Ingress controller配置

透传client源IP

配置X-Forwarded-For 获取客户端真实IP,实现IP白名单和透传client源IP的功能

##查看configmap 信息
[ec2-user@eks ~]$ kubectl get configmap -n nginx-ingress-controller
NAME                                 DATA   AGE
k8s-nginx-nginx-ingress-controller   3      19m

##编辑configmap 
[ec2-user@eks ~]$ kubectl edit cm/k8s-nginx-nginx-ingress-controller -n nginx-ingress-controller

修改内容如下:

apiVersion: v1
data:
  #data里,加入以下配置
  #将远程地址附加到 X-Forwarded-For 标头而不是替换它。启用此选项后,上游应用程序负责根据自己的受信任代理列表提取客户端 IP。
  compute-full-forwarded-for: "true"
  #设置用于标识客户端的原始 IP 地址的标头字段。默认值:X-Forwarded-For
  forwarded-for-header: X-Forwarded-For
  #如果为真,NGINX 会将传入的 X-Forwarded-* 标头传递给上游。当 NGINX 在设置这些标头的另一个代理/负载均衡器之后使用此选项;如果为 false,NGINX 会忽略传入的 X-Forwarded-* 标头,并用它看到的请求信息填充它们。
  use-forwarded-headers: "true"
kind: ConfigMap

验证是否生效:

[ec2-user@eks ~]$ kubectl exec pods/k8s-nginx-nginx-ingress-controller-69c99c6c7-hcbd2 -n nginx-ingress-controller /bin/cat  /etc/nginx/nginx.conf |grep -b5 'real_ip_header'
... ... ...
##生效后,在 nginx-ingress-controller 中 nginx.conf 增加了以下配置:
2424:   real_ip_header      X-Forwarded-For;
2462-
2464-   real_ip_recursive   on;
2489-
2491-   set_real_ip_from    0.0.0.0/0;
2523-

Ingress配置

Ingress yml配置

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-wildcard-host
  namespace: ops
  annotations:
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/ssl-redirect: 'false'
spec:
  rules:
  - host: "foo.bar.com"
    http:
      paths:
      - pathType: Prefix
        path: "/bar"
        backend:
          service:
            name: service1
            port:
              number: 80
  - host: "bar.foo.com"
    http:
      paths:
      - pathType: Prefix
        path: "/foo"
        backend:
          service:
            name: service2
            port:
              number: 80

检查ingress动态生成的nginx配置

$ kubectl -n ingress-nginx exec -ti nginx-ingress-xxxxxxx bash
# ps aux
# cat /etc/nginx/nginx.conf|grep myblog -A10 -B1

跨域配置

  • 跨域配置1:(直接在ingress 的annation里面加)

    annotations:
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/Access-Control-Allow-Origin: '*'
    nginx.ingress.kubernetes.io/cors-allow-credentials: 'true'
    nginx.ingress.kubernetes.io/cors-allow-headers: >-
      DNT,web-token,app-token,Authorization,Accept,Origin,Keep-Alive,User-Agent,X-Mx-ReqToken,X-Data-Type,X-Auth-Token,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,token,Cookie
    nginx.ingress.kubernetes.io/cors-allow-methods: 'PUT, GET, POST, OPTIONS'
    nginx.ingress.kubernetes.io/cors-allow-origin: 'https://*.qqq.cn, http://*.qqq.cn, http://localhost:8081'
    nginx.ingress.kubernetes.io/enable-cors: 'true'
    nginx.ingress.kubernetes.io/cors-max-age: '600'
  • 跨域配置2:(使用configuration-snippet配置)
    nginx.ingress.kubernetes.io/configuration-snippet: |
      if ($request_method = 'OPTIONS') { 
        more_set_headers "Access-Control-Allow-Origin: $http_origin"; 
        more_set_headers 'Access-Control-Allow-Credentials: true'; 
        more_set_headers 'Access-Control-Allow-Methods: GET, POST, OPTIONS';
        more_set_headers 'Access-Control-Allow-Headers: DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
        more_set_headers 'Access-Control-Max-Age: 1200';
        more_set_headers 'Content-Type: text/plain charset=UTF-8';
        more_set_headers 'Content-Length: 0'; 
        return 204; 
      }
      more_set_headers "Access-Control-Allow-Origin: $http_origin"; 
      more_set_headers 'Access-Control-Allow-Credentials' 'true';
      more_set_headers 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
      more_set_headers 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';

白名单限制

nginx.ingress.kubernetes.io/whitelist-source-range您可以通过注解指定允许的客户端 IP 源范围。该值是一个逗号分隔的CIDR列表,例如10.0.0.0/24,172.10.0.1

前端没有负载均衡器的情况下

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    nginx.ingress.kubernetes.io/whitelist-source-range: '172.21.0.0/16,10.0.0.0/16'

前端有负载均衡器的情况下(阿里云 SLB,AWS ALB)

Ingress controller配置透传client源IP(需要额外增加下面配置,在data 下添加 use-forwarded-headers)

kind: ConfigMap
apiVersion: v1
metadata:
  name: nginx-configuration
  namespace: ingress-nginx
  labels:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
data:
  use-forwarded-headers: "true"   # 添加

重定向配置

官方地址:https://kubernetes.github.io/ingress-nginx/examples/rewrite/Kubernetes 安装配置ingress controller_第1张图片

域名跳转

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/rewrite-target: 'https://ops-bbb.adsdesk.cn/$1'
  generation: 2
  name: ops-web
  namespace: ops
spec:
  rules:
    - host: ops-aaa.adsdesk.cn
      http:
        paths:
          - backend:
              service:
                name: ops-web
                port:
                  number: 8000
            path: /(.*)
            pathType: Prefix
status:
  loadBalancer: {}

路径跳转

$ echo '
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /$2
  name: rewrite
  namespace: default
spec:
  ingressClassName: nginx
  rules:
  - host: rewrite.bar.com
    http:
      paths:
      - path: /something(/|$)(.*)
        pathType: Prefix
        backend:
          service:
            name: http-svc
            port: 
              number: 80
' | kubectl create -f -

报错小结

posthog-ingress-nginx-controller-admission not found

Error from server (InternalError): error when creating "ingress-nginx-posthog.yaml": Internal error occurred: failed calling webhook "validate.nginx.ingress.kubernetes.io": Post "https://posthog-ingress-nginx-controller-admission.posthog.svc:443/networking/v1/ingresses?timeout=10s": service "posthog-ingress-nginx-controller-admission" not found

原因分析:
类似问题:https://stackoverflow.com/questions/61616203/nginx-ingress-controller-failed-calling-webhook/61681896#61681896

我删除了它创建的命名空间和clusterrole and clusterrolebinding as noted in the documentation,但这并没有删除安装在清单中的ValidatingWebhookConfiguration,但在默认情况下使用helm 时不会删除。

解决方法1:

kubectl delete -A ValidatingWebhookConfiguration ingress-nginx-admission

解决方法2:
1、检查是否有validation webhook 和服务。

[root@ops ~]# kubectl get -A ValidatingWebhookConfiguration
NAME                              WEBHOOKS   AGE
aws-load-balancer-webhook         2          90d
cert-manager-webhook              1          91d
posthog-ingress-nginx-admission   1          5d1h
vpc-resource-validating-webhook   2          107d

[root@ops ~]# kubectl get svc -n ingress-nginx
No resources found in ingress-nginx namespace.

2、手动删除posthog-ingress-nginx-admission后恢复正常

[root@ops ~]# kubectl get ValidatingWebhookConfiguration/posthog-ingress-nginx-admission -o yaml >posthog-ingress-nginx-admission.yaml

[root@ops ~]# kubectl delete -f posthog-ingress-nginx-admission.yaml

no matches for kind "Role" in version "rbac.authorization.k8s.io/v1beta1"

执行异常信息:
unable to recognize "ingress-nginx.yaml": no matches for kind "ClusterRole" in version "rbac.authorization.k8s.io/v1beta1"
unable to recognize "ingress-nginx.yaml": no matches for kind "Role" in version "rbac.authorization.k8s.io/v1beta1"
unable to recognize "ingress-nginx.yaml": no matches for kind "RoleBinding" in version "rbac.authorization.k8s.io/v1beta1"
unable to recognize "ingress-nginx.yaml": no matches for kind "ClusterRoleBinding" in version "rbac.authorization.k8s.io/v1beta1"

原因分析:
自 v1.22 起,Ingress 的 extensions/v1beta1 和networking.k8s.io/v1beta1 API 版本不再提供。
官方文档:https://kubernetes.io/docs/reference/using-api/deprecation-guide/#ingress-v122

解决方法:

sed -i 's#rbac.authorization.k8s.io/v1beta1#rbac.authorization.k8s.io/v1#' ingress-nginx.yaml

Unable to continue with install: IngressClass "nginx" in namespace "" exists and cannot be imported into the current release

Error: INSTALLATION FAILED: rendered manifests contain a resource that already exists. Unable to continue with install: IngressClass "nginx" in namespace "" exists and cannot be imported into the current release: invalid ownership metadata; annotation validation error: missing key "meta.helm.sh/release-name": must be set to "k8s-nginx"; annotation validation error: missing key "meta.helm.sh/release-namespace": must be set to "nginx-ingress-controller"
helm.go:84: [debug] IngressClass "nginx" in namespace "" exists and cannot be imported into the current release: invalid ownership metadata; annotation validation error: missing key "meta.helm.sh/release-name": must be set to "k8s-nginx"; annotation validation error: missing key "meta.helm.sh/release-namespace": must be set to "nginx-ingress-controller"
rendered manifests contain a resource that already exists. Unable to continue with install

原因分析:
使用 helm 创建nginx-ingress-controller时出错

查看helm chart仓库values.yaml文件

#... ... ...
##查看以下字段 
ingressClassResource:
  name: nginx  ##修改资源名
  enabled: true
  default: false
  controllerClass: "k8s.io/ingress-nginx"
  parameters: {}
#... ... ...

解决方法:

helm install k8s-nginx mynginx/nginx-ingress-controller -n nginx-ingress-controller --create-namespace --set ingressClassResource.name="nginx-new"

如果没生效,使用以下命令:

helm install k8s-nginx mynginx/nginx-ingress-controller -n nginx-ingress-controller --create-namespace --set controller.ingressClassResource.name="nginx-new"