对于许多企业来说,将生产环境转移到Kubernetes集群上,会让应用程序的流量管理变得复杂且具有挑战性。
而Ingress Controller允许通过Yaml编排脚本提供高可用的七层负载均衡、Waf防火墙或者API Gateway,它是Kubernetes集群对外服务的核心组件。
Ingress-nginx是Kubernetes Ingress Controller开源版本中的一种,它使用了NGINX作为反向代理和负载均衡器,生态完善、功能丰富,性能与稳定性也是极优秀的。
最近五年随着devops的落地,很多运维岗位都消失了,可能只需要在抽象的web界面上配置一下,或者k8s里面yaml去管理。
现在很多的开源产品就是拿openresty或者nginx做了一些插件的开发,通过WAF或者service mesh去包装给到用户。
Ingress是七层负载均衡,nginx最擅长的,这里主要讲nginx的两种实现。第一种是k8s官方的,第二种是nginx官方的。注意这两个ingress control是基于go实现的。
工作场景:Ingress control就是在处理南北向的流量,尤其是整个集群出入口的流量
早期时候是基于物理机,之后是基于虚拟机,比如在公有云上面买了ecs,后面有了docker,K8s相对于docker来说最强大之处是在编排上面,托管上面。
外部的traffic流量下来以后,来到ingress这里可以理解为nginx,nginx是基于虚拟主机域名,这个东西就是server_name,根据server_name做第一级的匹配,匹配到之后再匹配url,也就是location。Nginx里面最核心的两级匹配一个是server一个是location。
匹配完以后就丢给了service
负载均衡服务怎么实现的呢?其实挺简单的,当有一个VIP的时候其实是通过iptables完成的,Linux上面有一个配置net.ipv4.ip_forward当其值为1的时候就具备了转发报文的能力,转发报文的时候有一个iptables的规则,这个规则就可以做了。
如果你去访问虚拟IP,50%的流量给到pod1,50%的流量给到pod2,这个就是通过iptables来完成的。
一个service可以绑定多个endpoint。
Ingress将nginx的很多东西给抽象出来了,server、location、upstream。
Ingress抽象出来了域名,host的值就和server_name的值一样,path就和我们的location一样。我们知道nginx location进行匹配的时候可以基于正则匹配,忽略大小写匹配。但是k8s抽象出来的是非常有限的,因为k8s没有认为其是其唯一的选择,因为还可以放lvs和其他的负载均衡,所以只将最小能力抽象出来了。
在nginx里面会有upstream(server pod1 ip、server pod2 ip)其实,一个service就对应了一个upstream
nginx要实现高可用,要很多维度保障,随着机器宕机,nginx的配置也会随之丢掉。
还有nginx这台机器宕机了,那么nginx是否也能够被拉起,能不能启动新的机器。
nginx.conf和共享内存、数据要放在哪里呢?这个要放在数据库当中才不会丢失。
其实k8s当中所有的数据都是放在etcd当中,这是key value数据库。apiserver会通过etcd本身的grpc server写入数据库当中。
paxos两段式提交,就是分布式数据库可以主备那么就有单点,要搞一个没有主备的概念,或者主节点挂掉之后无所谓,小于2/n节点即可,可以提供读写服务。
ingress就是之前的yaml文件,然后还有endpoint和service,一个是service的yaml,一个是定义的pod。这些pod是通过选择器进行关联的。
一个HTTP请求进来,先通过ingress的host来做域名判断,再通过location即url做二次匹配。然后就进入了upstream,也及时proxy pass,然后它开始选择upstream了,upstream里面根据service endpoint唯一确定了给哪一批server。
secret在配置域名的时候,那么外部流量是走tls的,实现http->https来保证其安全。
因为公钥私钥是通过secret资源,加密之后存放到etcd里面。即使你拿走了etcd数据库,那么你还是解析不了。
另外一部分高可用体现在k8s集群管理上面。
在nginx上面本身做插件和apiserver去做通信,这个是不行的,开发效率太低了。
不管你是使用c语言与apiserver去做通信,复杂度都是非常恐怖的一件事情,因为需要支持grpc和tls。基于c和lua写都不行。
不管是k8s官方还是nginx官方还是kong都是这么干的,搞出一个新的进程,这个进程是上面图片当中的m,由这个进程和nginx在同一个pod里面,也就是一个pod里面是多进程的。
这个外部的进程m通过apiserver去管理nginx。
怎么管理呢?
这个进程的实现都是k8s和nginx官方都是使用了go语言。
bash-5.0$ ps -ef
PID USER TIME COMMAND
1 www-data 0:00 /usr/bin/dumb-init -- /nginx-ingress-controller --configmap=ingress-nginx/nginx-configuration --tcp-services-configmap=ingress-nginx/tcp-services --udp-servi
7 www-data 47:55 /nginx-ingress-controller --configmap=ingress-nginx/nginx-configuration --tcp-services-configmap=ingress-nginx/tcp-services --udp-services-configmap=ingress-
26 www-data 0:00 nginx: master process /usr/local/nginx/sbin/nginx -c /etc/nginx/nginx.conf
etcd当中存储了ingress,service,endpoint,secret,configmap。这五类数据库一旦发生了任何的变化,那么就需要推送給store协程,机器挂掉了endpoint就变化了,等等。
更改了密钥,也是一样。
发生变化了那么store协程就知道了,store将apiserver传递给他的五类数据的变更写入到channel当中,nginxcontroller会从这个协程当中取出数据来,取出来放到同步队列当中,还有一个协程会将其中的数据读取出来处理。