ingress是一种可以暴露k8s集群内部service的方式,用户编辑配置文件定义一个ingress资源即可实现外部网络访问内网service。
ingress controller是来管理所有的Ingress的对象,ingress controller内部其实是一个nginx的容器,当ingress controll 通过与 Kubernetes API 交互,感知集群中Ingress规则变化时会按照模板文件生成nginx.conf文件,然后reload该配置文件。
相对于kubernetes通过nodePort方式暴露服务来说,可以提供更加高级的特性,尤其对于HTTP服务来说,他可以实现负载均衡,SSL,虚拟主机等,并且ingress是直接将数据包转发至pod,不会在经过kube-proxy,相对来说更加高效。
用户可以参照官方文档Deployment 进行环境搭建。
首先得设置一个default-backend service。 如果未匹配到所有的Ingress对象,则会将所有的请求都转发至这个默认的ingress对象,在这里可以返回一些错误信息等;
然后是设置集群的RBAC,由于ingress controller要访问api server,所以需要给他配置资源访问权限;
安装ingress controller, 一般是以DaemonSet形式在每个node上创建一个资源,然后以hostNetwork的方式开放一个端口
最后就是创建想要暴露的服务的ingress对象
经过上面的几步就可以在集群外访问该service了, 但是访问的时候注意ingress controller内部其实是一个nginx服务器,它也是通过配置文件中的虚拟主机server_name来分辨该转发到后端哪个service上的,对于http请求必须在请求的header中加上Host字段,所以如果没有配置DNS域名解析,在浏览器里访问时可以修改/etc/hosts文件,也可以用curl -H "Host: xxxxx.com" nodeIp来实现。
对于需要自定义default-backend的同学可以根据Custom errors 自定义后端的实现,
上面讲到当我们创建ingress的时候,Ingress controller会进行relaod,这期间nginx需要替换nginx worker进程, 这将导致服务出现短暂的异常。使用Jmeter进行分布式压测一个服务中,观察到的情况是qps无影响,但是会出现0.01%的错误返回结果,在一些小并发的情况下这可以接收,但是如果是高并发的场景,并且后端服务的service ingress对象较多,此时reload的也会比较频繁,对于一些服务是无法接受的。
有一个解决方式就是在集群里里定义多个Ingress controller对象,然后根据ingress的情况分别注册到不同的ingress controller之上,这样就可以实现分组的概念,一个组里添加ingress不会导致另一个组进行reload, 使用时需要在ingress controller 的yaml定义文件中指定--ingress-class选项,然后在Ingress 的yaml 定义文件中添加 annotation : kubernetes.io/ingress.class 关联对应的controll class。
注意如果是在同一台主机之上启动多个ingress controller则需要改变默认配置的端口号,否则会发生端口冲突,需要在镜像的启动参数中添加 --http-port,--https-port, --status-port, --healthz-port这几个选项来改变端口。
nginx是个功能强大的负载均衡,反向代理服务器,如果仅仅通过默认的模板生成的nginx.conf文件可能不能满足我们的需求,并且可能存在坑,所以需要自定义配置的话可以通过三种方式实现:
通过configMap,具体参见NGINX Ingress controller configuration ConfigMap,他是全局的设置,直接配置nginx.conf的生成,影响管理的全部Ingress,但是需要指定-configmap=$(POD_NAMESPACE)/nginx-configuration选项;
通过Annotations是对单独的某个service的ingress进行配置;
高级玩家是直接自定义template来控制nginx controll 生成nginx.conf文件
nginx ingress的四层代理nginx支持四层代理,引用到ingress中也同样支持,表现形式与nodePort差不多,都需要在主机上开放一个端口,但是支持一些Nginx的功能,感觉比较鸡肋,参见Exposing TCP and UDP services, 实现方式是通过一个configMap 来指定主机端口和内网service的对应关系。