添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

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

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

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

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

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

  • nginx ingress 基于 nginx 的 ingress 控制器
  • istio ingress 基于 istio 的 ingress 控制器
  • traefik ingress 基于 traefik 的 ingress 控制器
  • ingress 提供路由规则给 ingress controller。ingress 和后端业务位于同一个 namespace 中。另外,configMap 也可以给 ingress controller 提供配置。

    ingress controller 可以单独位于一个ns,它会自动去解析 Ingress 对象。

  • 需要 ingress 对象通过 ingressClass对象关联 ingress controller。
  • ingress 针对的主要是 http 和 https. 这些之外的端口暴漏,如果仅仅是4层,则通过 Service.Type=NodePort 或者 Service.Type=LoadBalancer 即可。

    internet => Ingress => Service

    之后的信息以 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

    ## 查看 repo:ingress-nginx 下 chart:ingress-nginx 的所有版本
    ➜   helm search repo ingress-nginx/ingress-nginx  -l
    NAME                            CHART VERSION   APP VERSION     DESCRIPTION
    ingress-nginx/ingress-nginx     4.0.19          1.1.3           Ingress controller for Kubernetes using NGINX a...
    ingress-nginx/ingress-nginx     4.0.18          1.1.2           Ingress controller for Kubernetes using NGINX a...
    ingress-nginx/ingress-nginx     4.0.17          1.1.1           Ingress controller for Kubernetes using NGINX a...
    ingress-nginx/ingress-nginx     4.0.16          1.1.1           Ingress controller for Kubernetes using NGINX a...
    ingress-nginx/ingress-nginx     4.0.15          1.1.1           Ingress controller for Kubernetes using NGINX a...
    ingress-nginx/ingress-nginx     4.0.13          1.1.0           Ingress controller for Kubernetes using NGINX a...
    ingress-nginx/ingress-nginx     4.0.12          1.1.0           Ingress controller for Kubernetes using NGINX a...
    ingress-nginx/ingress-nginx     4.0.11          1.1.0           Ingress controller for Kubernetes using NGINX a...
    ingress-nginx/ingress-nginx     4.0.10          1.1.0           Ingress controller for Kubernetes using NGINX a...
    

    ✨需要注意的是,CHART VERSION 和 APP VERSION 都不是 nginx 版本。

    CHART VERSION 是这个图表的版本,APP VERSION 是 ingress-nginx 的版本。

    对应关系看:

    https://github.com/kubernetes/ingress-nginx#support-versions-table

    ✨建议安装倒数第二个chart版本。

    ➜   version=4.0.18
    ➜   # helm fetch ingress-nginx/ingress-nginx 
    ➜   helm show values ingress-nginx/ingress-nginx --version=${version} > values.yaml
    

    helm 通过 values.yaml 去修改 template。

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

    command 的示例配置是之前的一种控制器关闭时防止丢失链接的方法。非必须添加。其中sleep 5是必须的,但原因不明。没有它,控制器关闭的时候会丢失连接。

  • 不过现在官方已经加入了 /wait-shutdown 命令来解决这个问题。
  • ingressClass 单 ingress 可以不用指定。如果创建多个 ingress-nginx,则在一个集群中名称不可以相同。

  • ingress 对象通过这个名称来将自身规则写入到对应的 ingress-controller。
  • metrics 开启 prometheus 指标。示例配置里,监控指标服务类型是 clusterip,如果你需要集群外访问,则需要设置为nodeport

    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"
    

    ✨应该保存好 values.yaml,便于之后优雅的0停机升级。

    安装ingress-nginx

    ➜   helm install ingress-nginx-001 ingress-nginx/ingress-nginx --version ${version} -n ingress-nginx --create-namespace -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 的类型. 它有三个类型:

    ImplementationSpecific: 根据选用的 ingress class 来定性.这是默认类型.

    Exact: 严格匹配. 不能有一点不同.

    Prefix: 基于"/"分段进行前缀匹配. 例如,

  • /aaa/bbb/ 可以匹配任何 /aaa/bbb/ 开头的, 或者 /aaa/bbb. 因此 /aaa/bbbb 是不能匹配的.
  • /aaa/ 可以匹配任何 /aaa/ 开头的, 或者 /aaa. 因此, /aaaa 是不能匹配的.
  • / 可以匹配任何 / 开头的. 因此, 匹配所有.
  • 💥请注意, Prefix 类型下.你写的匹配字符串首尾都需要加/, 如果你尾部没有加/, 那么 ingress 在匹配的时候, 也会帮你加上/.

    当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

    创建 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获取编码后的内容

    在Ingress对象中调用 Secret

    apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
      name: tls-example-ingress
    spec:
      - 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模拟LB

    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
    # 原控制器版本
    oldVersion=
    # 查看原控制器的 values
    helm get values ingress-nginx
    # 通过原控制器的 values.yaml 创建原控制器版本的临时版本
    helm install ingress-nginx-temp ingress-nginx/ingress-nginx --version ${oldVersion} -n ingress-nginx-temp -f values-${oldVersion}.yaml
    

    切换DNS,指向临时控制器

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

    导出新控制器配置

    # 新版本
    newVersion=
    # 导出新版本的 ingress-nginx 的 values.yaml 
    helm show values ingress-nginx/ingress-nginx --version=${newVersion} > values-${newVersion}.yaml 
    

    比对新旧配置

    将旧的配置转移到新配置中,并确认新配置中有没有额外的参数

    更新原控制器

    # values.yaml 更新后的配置
    helm upgrade --install ingress-nginx ingress-nginx/ingress-nginx --version ${newVersion} -n ingress-nginx -f values-${newVersion}.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
    

    release 卸载

    helm uninstall ingress-nginx
    

    release 版本

    helm history ingress-nginx
    

    release 回滚

    helm rollback ingress-nginx <history_num>