Kubernetes Service通过虚拟IP地址或者节点端口为用户应用提供访问入口,然而这些IP地址和端口是动态分配的,如果用户重建一个服务,其分配的clusterIP和nodePort或者LoadBalancerIP都是会变化的,实际项目中无法把一个可变的入口发布出去供用户访问。为了解决这个问题,Kubernetes提供了内置的域名服务,用户定义的服务会自动获取域名,通过域名解析,可以对外向用户提供一个固定的服务访问地址。CoreDNS包含一个内存态DNS,以及其他controller类似的控制器。CoreDNS的实现原理是控制器监听Service和Endpoint的变化并配置DNS,客户端Pod在进行域名解析时,从CoreDNS中查询服务对应的地址记录。对于不同类型的Service,DNS规则是什么呢?DNS创建规则如下所示:
接下来通过实际例子理解CoreDNS是如何工作的。首先部署2个nginx的pod作为后端服务,另外再部署一个service。pod和service的yaml文件如下所示
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
---
apiVersion: v1
kind: Service
metadata:
name: nginx-basic
spec:
type: ClusterIP
ports:
- port: 80
protocol: TCP
name: http
selector:
app: nginx
~
pod的service部署成功后,通过kubectl exec -it podName bash命令进入到pod中,查看pod的etc目录下的resolv.conf文件。
nameserver : 表示解析域名时使用该地址指定的主机为域名服务器。其中域名服务器是按照文件中出现的顺序来查询的,且只有当第一个nameserver没有反应时才查询下面的nameserver。
search:它的多个参数指明域名查询顺序。当要查询没有域名的主机,主机将在由search声明的域中分别查找。
domain:声明主机的域名。很多程序用到它,如邮件系统;当为没有域名的主机进行DNS查询时,也要用到。如果没有域名,主机名将被使用,删除所有在第一个点( .)前面的内容。
需要注意domain和search不能共存,在上述的pod里面的resolv.conf文件就只有search的定义,没有domain的定义。
当前service的ClusterIP是:10.99.151.150,在pod里面通过域名访问部署的nginx的服务,即部署的nginx pod. curl nginx-basic.default.svc.cluster.local.可以看到域名的格式是$serviceName.$namespaceName.svc.$cluserDomain。clusterDomain如果不修改,默认是cluster.local.当通过域名访问时,请求转发到的service的ClusterIP地址上,故最终访问到了后端的服务。
以上就是CoreDNS工作原理,接下来看看Ingress是如何工作的,以下是对service和ingress的一个简单对比结果。
Ingress包含两部分处理逻辑,一部分是ingress是一层代理,负责根据hostname和path将流量转发到不同的服务上,使得一个负责均衡器用于多个后台服务。Kubernetes Ingress Spec是转发规则的集合。另外一部分是Ingress Controller,controller负责DNS配置,负载均衡配置等。下面是一个定义ingress的yaml文件例子。
接下来通过实际例子看看ingress是如何工作,在理解ingress工作过程中,还需要用到上面部署的nginx的service和nginx的pod。
首先是通过helm安装ingress,具体安装命令如下所示
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm install ingress-nginx ingress-nginx/ingress-nginx --create-namespace --namespace ingress
安装完成后,查看ingress namespace,可以看到启动了ingress-controller的pod,另外还创建了一个service,默认情况下service是LoadBanlancer类型,为了后面演示在外网访问的过程,这里把service类型修改成NodePort类型。
接着是编写创建Ingress的yaml文件,下面是ingress的yaml文件内容,里面配置了tls的secret,因为这个例子后续是通过https访问nginx服务,故需要配置证书,即生成key和crt,存放到secret对象中,rules中指定了转发规则。
这里是通过x509对"ingress.demo.com"域名签发证书,执行命令后会生成key和crt。
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/CN=ingress.demo.com/O=ingress" -addext "subjectAltName = DNS:ingress.demo.com"
创建secret对象存放生成的key和crt。
kubectl create secret tls ingress-tls --cert=./tls.crt --key=./tls.key
查看创建好的sercret对象,可以看到里面将key和crt存放了,这样ingress就可以通过secret获取到key和crt信息,完成https的访问。
接着在node节点上通过curl命令尝试访问部署的nginx服务,因为签发证书的域名设置的是ingress.demo.com,故这里在header中设置Host是ingress.demo.com. 10.111.26.96是ingress的IP地址。
curl -H "Host: ingress.demo.com" https://10.111.26.96 -v -k
http的curl的结果如下所示:可以看到我们访问的入口是ingress,ingress在通过niginx的service,然后就访问到了部署的nginx pod。
https的curl结果如下所示:因为是https访问,所以有TLS握手的过程,但是证书验证的过程失败了,导致访问nginx的返回了503.在证书配置过程中可能还有些问题,需要单独查找原因。
在安装ingress的时候还创建了NodePort类型的ingress service,所以还可以在外网访问nginx服务,ip地址是node的公网地址,端口32058是NodePort类型的ingress service的对外的端口。外网访问的情况下,请求入口是ingress的service接收请求,然后转发到ingress对象上,下面日志可以看到请求的Location是https://ingress.demo.com,ingress再访问nginx的service,service再访问nginx的pod,最终完成整个访问。
以上就是ingress工作过程,可以看到通过ingress可以实现https的访问,增加了安全性。但是在实际项目中用ingress做负载均衡也存在问题,例如ingress只能基于URL进行负载均衡,如果想实现更多规则的负载均衡就不行,例如基于header等。故在实际项目中还需要其他手段才能完成负载均衡。