k8s☞13高级版负载均衡ingress控制器

阅读量: zyh 2020-09-22 18:30:04
Categories: > Tags:

基本

svc的NodePort解决了4层的对外服务提供。但7层的功能svc无法解决。例如https。

ingress controller 可以将多个域名转发到service,提供负载均衡/SSL管理/虚拟命名主机/path路由。

ingress controller 负责实现功能,不过路由配置规则由ingress对象实现。

ingress-controller 的实现有很多,具体可以查看页面,k8s官方维护了awsgcenginx

https://kubernetes.io/zh/docs/concepts/services-networking/ingress-controllers/

例如:

特点

流量路径

internet => Ingress => Service

image-20201023110559218

组件

之后的信息以 ingress-nginx 为基准。

Ingress controller

部署文档:

https://github.com/kubernetes/ingress-nginx/blob/master/docs/deploy/index.md

或者

https://kubernetes.github.io/ingress-nginx/deploy/

这里,通过helm来安装ingress-nginx。

当前 ingress-nginx 的仓库地址是: https://hub.helm.sh/charts/ingress-nginx/ingress-nginx

helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update

配置ingress-nginx

helm show values ingress-nginx/ingress-nginx > values.yaml

修改values.yaml,参考下列信息:

controller:
  ......
  #lifecycle:
    #preStop:
      #exec:
        #command: ["/bin/sh", "-c", "sleep 5; /usr/local/openresty/nginx/sbin/nginx -c /etc/nginx/nginx.conf -s quit; while pgrep -x nginx; do sleep 1; done"]
  ingressClass: nginx
  metrics:
    port: 10254
    # if this port is changed, change healthz-port: in extraArgs: accordingly
    enabled: true
    service:
      annotations: #{}
        prometheus.io/scrape: "true"
        prometheus.io/port: "10254"

安装ingress-nginx

kubectl create namespace ingress-nginx
## 查看最新的chat version
helm search repo ingress-nginx/ingress-nginx
version=4.0.16
helm install ingress-nginx ingress-nginx/ingress-nginx --version ${version} -n ingress-nginx -f values.yaml
kubectl get all -n ingress-nginx
===
NAME                                            READY   STATUS    RESTARTS   AGE
pod/ingress-nginx-controller-5886685d54-hf6l6   1/1     Running   0          68m

NAME                                         TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)                      AGE
service/ingress-nginx-controller             LoadBalancer   10.96.210.139   <pending>     80:32489/TCP,443:30936/TCP   68m
service/ingress-nginx-controller-admission   ClusterIP      10.96.128.101   <none>        443/TCP                      68m

NAME                                       READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/ingress-nginx-controller   1/1     1            1           68m

NAME                                                  DESIRED   CURRENT   READY   AGE
replicaset.apps/ingress-nginx-controller-5886685d54   1         1         1       68m

我们可以看到 ingress-nginx 控制器通过deployment.apps/ingress-nginx-controller 创建了一个 pod/ingress-nginx-controller-5886685d54-hf6l6。这个 pod 其实就是一个 nginx. 它通过 ingress 定义的路由规则,来动态的变更nginx配置,从而正确的将流量转发给后端应用的service对象.

kubectl describe pod/ingress-nginx exec -it pod/ingress-nginx-controller-5886685d54-hf6l6
===
    Requests:
      cpu:      100m
      memory:   90Mi

当前配置默认没有做limit限制. 所以需要关注ingress-nginx创建的pod所在物理节点的性能是否可以满足. 建议强制绑定到多个node节点(通过污点), 这批节点专门跑用来进行ingress控制器.

通过登陆pod,可以查看转化后的nginx配置.

kubectl --namespace ingress-nginx exec -it pod/ingress-nginx-controller-5886685d54-hf6l6 -- /bin/bash
==
cat /etc/nginx/nginx.conf

ingress

https://kubernetes.io/docs/concepts/services-networking/ingress/

client => http://one.foo.com/one => Ingress = > Service(one):8001 => pod

client => http://*.foo.com/other => Ingress => Service(other):8002 => pod

ingress编写路由规则,并由ingress-controller进行转化。

下面是一个示例:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: minimal-ingress
  namespace: default
#  annotations:
#    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  ingressClassName: nginx
  rules:
  - host: one.foo.com
    http:
      paths:
      - path: /one
        pathType: Prefix
        backend:
          service:
            name: one
            port:
              number: 8001
  - host: "*.foo.com"
    http:
      paths:
      - pathType: Prefix
        path: "/other"
        backend:
          service:
            name: other
            port:
              number: 8002

🌟请注意, networking.k8s.io/v1必须是v1.19+才可以使用, 如果你不是,则应该使用networking.k8s.io/v1beta1

spec.ingressClassName 用来指定 ingressClass 对象。

为了方便理解 ingress 的配置项,我参考 nginx 配置,来进行了一些横向对比:

nginx.servername: 这里是 spec.rules[].host

nginx.location: 这里是 spec.rules.http.paths[].path

nginx.upstream: 这里是 spec.rules.http.paths[].backend

关于 spec.rules[].host

这里有一些注意事项. host 支持统配匹配. 不过 *不能跨级匹配. 例如,

*.abc.com 可以匹配 one.abc.com 但是不能匹配 two.one.abc.com.

关于 spec.rules.http.paths[].path

这里有一些注意事项.如上例子所述. pathType 定义了 path 的类型. 它有三个类型:

当Exact和Prefix混用的时候, 如果一个请求同时匹配了这两个类型下的path, 那么Exact 优先级更高.

构建TLS

https://kubernetes.io/zh/docs/concepts/services-networking/ingress/#tls

默认情况下,如果为 Ingress 启用了 TLS,ingress-controller 将使用 308 永久重定向响应将 HTTP 客户端重定向到 HTTPS 端口 443。

💥ingress.spec.tls.hosts必须完全包含ingress.spec.rules.hosts

手动证书

  1. 创建 Secret 对象, 导入证书文件

    apiVersion: v1
    kind: Secret
    metadata:
      name: foo-com-tls
      namespace: default
    data:
      tls.crt: base64 encoded cert # 这里需要填写 base64 编码后的 cert 内容. tls.crt 不可更名
      tls.key: base64 encoded key  # 这里需要填写 base64 编码后的 key 内容. tls.key 不可更名
    type: kubernetes.io/tls
    

    可以通过cat 证书文件|base64获取编码后的内容

  2. 在Ingress对象中调用 Secret

    apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
      name: tls-example-ingress
    spec:
      tls:
      - hosts:
          - https-example.foo.com
        secretName: foo-com-tls
      rules:
      - host: https-example.foo.com
        http:
          paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: service1
                port:
                  number: 80
    

    …backend.service.name svc对象名

    …backend.service.port svc对象端口

不同的Ingress控制器对于TLS这块也有一定差异.具体以控制器本身说明为准.

自动证书

构建cert-manager证书管理器。具体安装参见证书管理器文档。

问题

解决svc lb类型的问题

我们可以看到通过helm默认安装的 ingress crotroller 的 Service 对象是LoadBalancer类型. 这种类型我们用于云端环境. 而我这里测试环境是裸机,因此你可以看到这里它一直无法获取到EXTERNAL-IP.

这意味着,我们无法将流量通过service/ingress-nginx-controller传递到pod/ingress-nginx-controller-5886685d54-hf6l6.🙄

如何解决这个问题.k8s官方文档这里提供了多种裸机安装下的解决方案:

https://kubernetes.github.io/ingress-nginx/deploy/baremetal/

这里通过 https://kubernetes.github.io/ingress-nginx/deploy/baremetal/#a-pure-software-solution-metallb 纯软件方案来解决 LB 类型的 EXTERNAL-IP获取问题. 其它方案并不是采用的 LB 类型, 因此这里不再叙说.

⚠️metallb 方案在当前并不是一个成熟的方案,当然它应该是可用的😄 所以用于本地测试环境是可以的. 线上环境建议直接用云服务的LB即可.

metallb

https://metallb.universe.tf/installation/#installation-with-helm

metallb 模拟了云环境中的负载均衡器功能.

🌟If you’re using kube-proxy in IPVS mode, since Kubernetes v1.14.2 you have to enable strict ARP mode.

如果你是ipvs 且 k8s 版本在1.14.2以上, 那么你需要启动严格arp模式.

kubectl edit configmap -n kube-system kube-proxy
===
ipvs:
  strictARP: true

开始安装

helm repo add metallb https://metallb.github.io/metallb
helm fetch metallb/metallb

修改 metalla/values.yaml, 添加地址池,用来给svc下发ip。请记住, 这些地址必须是没有被其它资源占用的.

configInline:
  address-pools:
   - name: default
     protocol: layer2
     addresses:
     - 10.200.16.11 - 10.200.16.19

通过 helm 安装

kubectl create ns metallb-system
helm install metallb --namespace metallb-system -f metallb/values.yaml metallb/metallb
# 下面这个命令仅在你初始安装的时候需要运行
kubectl create secret generic -n metallb-system memberlist --from-literal=secretkey="$(openssl rand -base64 128)"

下面,让我们再看看 ingress crotroller 的 svc 对象信息.

kubectl get svc -n ingress-nginx
===
NAME                                 TYPE           CLUSTER-IP      EXTERNAL-IP    PORT(S)                      AGE
ingress-nginx-controller             LoadBalancer   10.96.210.139   10.200.16.11   80:32489/TCP,443:30936/TCP   88m
ingress-nginx-controller-admission   ClusterIP      10.96.128.101   <none>         443/TCP                      88m

终于svc/ingress-nginx-controller拿到了一个EXTERNAL-IP, 现在你就可以将域名解析到EXTERNAL-IP, 并通过这个域名来访问你的应用了.(而且它必须是能通过80访问的.)😄

假设已经设置编写一个 ingress,则应用后如下所示:

kubectl get ingress -n it
===
NAME                   CLASS    HOSTS          ADDRESS        PORTS   AGE 
test-it-local-ingress   <none>   test.it.local   10.200.16.11   80      106m

可以看到 ingress 对象中也显示了 metallb 的下发的ip.

你可以在任意一个节点上通过 ipvs 规则来看到转发情况.

ipvsadm -L -n
===
TCP  10.200.16.11:80 rr
  -> 10.97.2.57:80                Masq    1      0          0
TCP  10.200.16.11:443 rr
  -> 10.97.2.57:443               Masq    1      0          0

这里 10.97.2.57 是 ingress 控制器的 pod ip.

metallb升级:https://metallb.universe.tf/installation/#upgrade

暴漏非80和非443端口

https://kubernetes.github.io/ingress-nginx/user-guide/exposing-tcp-udp-services/

默认你会发现ingress无法暴漏80和443之外的端口.这是因为默认ingress-nginx并没有开启4层转发.

开启ingress-nginx中pod的4层转发

检查 deployment: ingress-nginx 中 pod 模板是否开启了下列参数

deploymentName=`kubectl get all -n ingress-nginx | grep deployment | awk '{print $1}'`
kubectl edit ${deploymentName} -n ingress-nginx
===
- args:
    - --tcp-services-configmap=$(POD_NAMESPACE)/tcp-services
    - --udp-services-configmap=$(POD_NAMESPACE)/udp-services

创建暴漏端口的配置文件

apiVersion: v1
kind: ConfigMap
metadata:
  name: tcp-services
  namespace: ingress-nginx
data:
  8080: "it/test-it-local-svc:8080"

这里8080: "it/test-it-local-svc:8080"第一个8080指的是ingress-nginx的pod对象里需要监听的端口, it/test-it-local-svc:8080` 指的是ns:it下的svc对象test-it-local-svc的8080端口

添加暴漏端口到ingress-nginx中svc对象

kubectl get all -n ingress-nginx | grep service | awk '{print $1}' 
kubectl edit service/ingress-nginx-controller -n ingress-nginx
===
  - name: test-8080
    port: 8080
    protocol: TCP
    targetPort: 8080

三步过后, 不出意外, 你将可以通过ingress-nginx的pod对象中的nginx.conf文件看到tcp的转发配置.

        # TCP services

        server {
                preread_by_lua_block {
                        ngx.var.proxy_upstream_name="tcp-it-test-it-local-svc-8080";
                }

                listen                  8080;

                proxy_timeout           600s;
                proxy_pass              upstream_balancer;

        }

ingress无法创建,报找不到控制器的svc

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

上述问题,常见于通过helm重建ingress-nginx之后,这是因为ValidatingWebhookConfiguration陈旧配置没有被删除,以至于ingress找到了错误的配置.

➜   kubectl get ValidatingWebhookConfiguration
NAME                                      WEBHOOKS   AGE
ingress-nginx-admission                   1          46m
ingress-nginx-admission-nginx             1          29d
ingress-nginx-doc-it-local-admission      1          448d

如上所示,我这里有两个陈旧的配置ingress-nginx-admission-nginx ingress-nginx-doc-it-local-admission,删除即可.

优雅更新ingress控制器

启动临时控制器

临时控制器用于临时接收流量,创建临时控制器,并且版本和配置与原控制器一致。

kubectl create ns ingress-nginx-temp
# 原控制器版本
version=
# values.yaml 原控制器配置
helm install ingress-nginx-temp ingress-nginx/ingress-nginx --version ${version} -n ingress-nginx-temp -f values.yaml

切换DNS,指向临时控制器

通过观察原控制器日志,等待原控制器流量结束。

更新原控制器

添加要更新的内容到 values.yaml

# 更新的版本
version=
# values.yaml 更新后的配置
helm upgrade --install ingress-nginx ingress-nginx/ingress-nginx --version ${version} -n ingress-nginx -f values.yaml

查看结果

kubectl get deployment nginx-ingress-controller -n ingress-nginx -o yaml

切换DNS,指向原控制器

通过观察临时控制器日志,等待临时控制器流量结束

删除临时控制器

helm delete --purge nginx-ingress-temp --namespace ingress-nginx-temp
helm delete ns ingress-nginx-temp