文章目录
- 一、服务发现机制
- 1.1 环境变量注入
- 1.2 DNS解析
- 二、Ingress
- 4.1 部署Ingress controller
- 4.2 暴露Ingress Controller
- 4.2.1 SVC NodePort方式
- 4.2.2 共享宿主机网络方式
- 4.3 默认后端
- 4.4 同域名不同URL转不同服务
- 4.5 不同域名转不同服务
- 4.6 使用https
- 4.6.1 安装cfssl
- 4.6.2 自签根证书
- 4.6.3 证书放入secret
- 4.6.4 ingress规则指定secret
- 4.7 TLS+deamonset
- 4.8 Headless Services
一、服务发现机制
什么是服务发现机制?
- 指客户端应用在一个K8s集群中如何获知后端服务的访问地址。
- K8s提供了两种机制供客户端应用以固定的方式获取后端服务的访问地址:环境变量方式和DNS方式。
1.1 环境变量注入
- 在一个Pod运行起来的时候,系统会自动为其容器运行环境注入所有集群中有效Service的信息。
- Service相关信息包括服务IP、服务端口号、各端口号相关的协议等。
1.查看当前svc,并创建一个deploy,没有指定标签与任何svc关联。
[root@k8s-master ~]# kubectl create deploy web2 --image=nginx
2.进入pod容器查看注入的环境变量。
1.2 DNS解析
为什么会有service DNS解析 ?
- 在前面我们都是用cluster ip来访问各节点容器,一般情况下是很稳定的。但是当换其他集群部署时,这个cluster ip就会改变,那代码里就需要更改,比较麻烦,所以可以通过service名称DNS解析方式就比较方便了。
DNS解析是怎么实现的?
- 是通过CoreDNS来实现的。它是一个DNS服务器,为K8s默认采用,以Pod方式部署在集群中,CoreDNS服务监视Kubernetes API,为每一个Service创建DNS记录用于域名解析。
svc的dns域名格式:
- Cluster IP记录格式:< service-name >.< namespace >.svc.cluster.local
- 示例:qingjun1.default.svc.cluster.local
注意事项:
- 访问其他命名空间的svc时,需要跟上对应的命名空间。
- 代码里写全名可以提高解析效率。
dns服务发展史:
- 最开始K8s 的DNS服务并非由Coredns来实现,有个发展历程,可以了解下。
- 1.2版本时,DNS服务是由SkyDNS提供的,由4个容器组成:kube2sky、skydns、etcd和healthz。
- kube2sky容器监控svc资源的变化,根据svc的名称和IP地址信息生成DNS记录,并将其保存到etcd中。
- skydns容器从etcd中读取DNS记录,并为客户端容器应用提供DNS查询服务。
- healthz容器提供对skydns服务的健康检查功能。
- 1.4版本时,因整体性能低就被替换成由KubeDNS实现,它由3个容器组成:kubedns、dnsmasq和sidecar。
- kubedns容器监控K8s中svc资源的变化,根据svc的名称和IP地址生成DNS记录,并将DNS记录保存在内存中。
- dnsmasq容器从kubedns中获取DNS记录,提供DNS缓存,为客户端容器应用提供DNS查询服务。
- sidecar提供对kubedns和dnsmasq服务的健康检查功能。
- 1.11版本才开始换成CoreDNS来实现。因是Go语言实现的高性能、插件式、易扩展的DNS服务端,并且解决了KubeDNS的一些问题,还支持自定义DNS记录及配置upstream DNS Server,可以统一管理Kubernetes基于服务的内部DNS和数据中心的物理DNS,一个容器便实现了KubeDNS内3个容器的全部功能。
Corndns工作流程:
- 每创建一个svc,Coredns就会生成一条记录。
- 当pod访问svc名称时,会先发送给Coredns去解析,Coredns通过kube-apiserver获取svc创建的dns记录。
- Coredns根据dns记录返回给pod对应的cluster ip。
Node本地DNS缓存:
- 大规模集群使用dns解析存在很多问题,比如集群DNS服务压力会剧增、域名解析性能很差,此时可以部署Node本地DNS缓存工具,从而提高整个集群的DNS域名解析的性能。
- 工作流程:客户端Pod首先会通过本地DNS缓存进行域名解析,当缓存中不存在域名时,会将请求转发到集群DNS服务进行解析。
1.运行进入一个pod,查看baimu的svc dns域名。最后也返回了baimu的cluster ip。
[root@k8s-master ~]# kubectl run bs --image=busybox:1.28.4 -- sleep 24h
2.容器dns配置文件有corndns的clusterip和域名后缀,在/var/lib/kubelet/config.yaml文件里配置。
2.解析其他命名空间的svc时,需要跟上命名空间。
二、Ingress
Ingress的存在的意义?
- Ingress为弥补NodePort不足而生,那后者有哪些不足呢?
- 一个端口只能一个服务使用,端口需提前规划,增加工作量。
- 只支持4层负载均衡,支持的iptables就只满足4层(基于IP、端口转发),7层(基于应用层http转发)功能满足不了,功能具备局限性。
Ingress是什么?
- 是K8s中的一个抽象资源,给管理员提供一个暴露应用的入口定义方法。类似于service作用,编写yaml文件,定义把哪个pod应用暴露出来。
- Ingress只能以HTTP和HTTPS提供服务,对于使用其他网络协议的服务,可以通过设置Service的类型(type)为NodePort或LoadBalancer对集群外部的客户端提供服务。
Ingress Controller是什么?
- 是负责流量路由,根据Ingress生成具体的路由规则,可以给集群提供全局的负载均衡能力。
- 相当于service nodeport 是基于iptables/ipvs方式实现的,而Ingress是基于Ingress controller来实现的。
Ingress Controller怎么使用?
- 部署Ingress Controller。
- 创建Ingress规则。
Ingress controller工作流程图:
- 管理员定义ingress转发规则,ingress controller根据Ingress定义的规则生成具体的转发规则。
- 用户访问时,ingress controller根据规则转发到具体的应用上。
Ingress Controller工作流程:
- Ingress Controller通过与 Kubernetes API 交互,动态感知集群中 Ingress 规则变化,然后读取它,按照自定义的规则生成一段 Nginx 配置,应用到管理的Nginx服务,然后热加载生效。以此来达到Nginx负载均衡器配置及动态更新的问题。
- nginx-ingress-controller 调用k8s api获取ingress,自动在nginx.conf生成server块转发配置。nginx 七层的负载均衡upstream,转发pod。
- 部署ingress controller pod后,进入此容器会看到nginx配置,同时也能看到这2个进程。
- 流程包流程:客户端 ->Ingress Controller(nginx) -> 分布在各节点Pod
4.1 部署Ingress controller
- 官方维护github项目地址
- Ingress controller官方地址
注意事项:
- 安装流程是先要下载下图中这个yaml文件的,然后再apply -f 拉取镜像,需要注意的是拉去的两个镜像是国外网站的,在国内是拉取不到的,所以需要先把拉取镜像的问题解决掉。
- 部署ingress controller插件的方式有很多,这里用nginx方式,可以将镜像地址改成lizhenliang/ingress-nginx-controller:v1.1.0,lizhenliang/kube-webhook-certgen:v1.1.1拉取
- 其他Ingress controller控制器
1.准备好yaml文件,确定能下载到镜像,导入yaml。
2.查看pod。
[root@k8s-master bck]# kubectl get pods -n ingress-nginx
4.2 暴露Ingress Controller
- 如下图:客户端网页访问域名时,是通过ingress controller插件来完成转发的,而ingress controller是以pod方式部署,需要将它对应绑定的svc端口暴露出来才能访问。
- 在安装时就已经部署了ingress controller svc ,到时只需要查看以下暴露端口即可。
公网负载均衡器用途:
- 把svc和ingress controller暴露出去,数据交互。
- 提供统一访问端口80。使用server NodePort方式暴露ingress controller时暴露出来的端口不同意,每次访问需要查询svc的暴露端口,而负载均衡器可以直接把它代理统一访问入口。
- 加强内网k8s集群的安全性。
4.2.1 SVC NodePort方式
1.创建两个应用Pod。
[root@k8s-master bck]# kubectl create deployment nginx --image=nginx
[root@k8s-master bck]# kubectl create deployment httpd --image=httpd
2.创建对应的service。
[root@k8s-master bck]# kubectl expose deployment nginx --port=88 --target-port=80
[root@k8s-master bck]# kubectl expose deployment httpd --port=99 --target-port=80
3.检查,此时httpd和nginx在K8s内部是可以访问的。
4.编辑一个ingress yaml文件,定义转发规则。
5.再在yaml文件里添加一个字段,指定控制器标识,最后导入yaml。
[root@k8s-master ~]# cat ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: qingjun
spec:
ingressClassName: nginx ##添加控制器标识,通过kubectl get ingressclass查看,不同控制器标识不一样。
rules:
- host: qingjun.mq.com ##访问域名,注意测试的两个域名的子域名要不同。
http:
paths:
- backend:
service: ##访问域名转发到哪个svc的哪个端口上。
name: nginx
port:
number: 88
path: / ##转发地址,默认/就是转发到首页
pathType: Exact ##转发类型。
[root@k8s-master bck]# kubectl apply -f ingress.yaml
6.查看ingress。
7.IP域名绑定。我们要访问域名是需要绑定IP的,项目中购买的公网域名是需要厂家给你一个解析的公网IP的,我们这里内网部署就在本地的hosts文件里绑定,绑定ip可以是K8s集群中的任意一个节点ip。
8.本地能访问域名,但是浏览器不能访问。是因为以上操作我们只是在定义Ingress规则,客户在浏览器上访问应用是通过ingress controller来进行访问,而ingress controller是通过pod方式部署的,并采用NodePort方式对外暴露。
9.获取ingress controller的svc暴露端口。ingress controller pod与svc进行绑定,通过NodePort方式对外暴露端口。
[root@k8s-master bck]# kubectl get pods,svc,ep -n ingress-nginx
10.域名+ingress controller svc端口访问。
11.第一个应用测试成功,再把第二个应用暴露出来。编辑yaml文件,创建对应ingress规则。
[root@k8s-master ~]# cat ingress2.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: httpd
spec:
ingressClassName: nginx
rules:
- host: baimu.mq.com
http:
paths:
- backend:
service:
name: httpd
port:
number: 99
path: /
pathType: Exact
[root@k8s-master ~]# kubectl apply -f ingress2.yaml
2.域名IP绑定解析,,访问网页。
4.2.2 共享宿主机网络方式
- 如下图,采用共享网络方式时,需要确认ingress controller pod被分配在哪台机器上,做IP域名解析时填写的IP必须是这台机器的IP,此举才能达到宿主机网络共享效果。
1.修改ingress yaml文件,添加参数hostNetwork: True,切换成网络共享模式。
2.查看新pod容器,此时ingress分配在node1节点。
3.查看node1、node2节点监听端口变化。node1(192.168.130.146)上有监听80端口,node2(192.168.130.147)节点上没有。
4.修改本机解析文件,测试得出结论:只有将IP修改成监听端口的服务器IP,才能正常访问;否则访问不了,这就是共享了宿主机网络。
4.3 默认后端
1.当下有两个svc,我只想把所有的请求转发到其中一个的httpd中,最后只返回httpd应用的数据。
2.定义ingress策略。
[root@k8s-master ~]# cat ingress3.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: minimal-ingress
spec:
ingressClassName: nginx
defaultBackend: ##添加此参数,代表将处理请求转发给默认后端。
service:
name: httpd
port:
number: 99
[root@k8s-master ~]# kubectl apply -f ingress3.yaml
3.访问网页。
4.4 同域名不同URL转不同服务
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: simple-fanout-example
spec:
rules:
- host: foo.bar.com
http:
paths:
- path: /foo
pathType: Prefix
backend:
service:
name: service1
port:
number: 4200
- path: /bar
pathType: Prefix
backend:
service:
name: service2
port:
number: 8080
4.5 不同域名转不同服务
1.根据请求头转发不同的svc上。
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: name-virtual-host-ingress
spec:
rules:
- host: foo.bar.com
http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
name: service1
port:
number: 80
- host: bar.foo.com
http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
name: service2
port:
number: 80
2.若创建的 Ingress 资源没有在 rules 中定义的任何 hosts,则可以匹配指向 Ingress 控制器 IP 地址的任何网络流量,而无需基于名称的虚拟主机。比如下面这个 Ingress 会将请求 first.bar.com 的流量路由到 service1,将请求 second.bar.com 的流量路由到 service2,而所有其他流量都会被路由到 service3。
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: name-virtual-host-ingress-no-third-host
spec:
rules:
- host: first.bar.com
http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
name: service1
port:
number: 80
- host: second.bar.com
http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
name: service2
port:
number: 80
- http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
name: service3
port:
number: 80
4.6 使用https
配置HTTPS步骤:
- 准备域名证书文件(来自:openssl/cfssl工具自签或者权威机构颁发)
- 将证书文件保存到Secret
- Ingress规则配置tls
生成证书步骤:
- 客户端证书都是基于K8s根证书来生成的,根证书在搭建K8s集群时候就被创建了,且创建时生成的ca-config配置文件是被删除了,根证书位置在/etc/kubernetes/pki目录下,一个是在K8s层面上的根证书,一个是etcd是用的。
- 生成ca-config配置文件,里面记录的根证书的有效期等信息。因为最后生成客户端证书时需要指定该配置文件。
- 生成客户端证书配置文件,里面声明一个CN字段(标识),可信任IP,生成的算法,证书里的设计属性。
- 使用cfssl工具指定根证书、根证书的配置文件和指定该文件中使用哪块配置、客户端证书的请求文件来生成。
注意事项:
- Ingress 只支持单个 TLS 端口 443,需要使用网络共享方式实现。
- tls字段中的 hosts 的取值需要与 rules 字段中的 host 完全匹配,前者是https,后者是http。
4.6.1 安装cfssl
1.安装cfssl工具,cfssl官网。
curl -s -L -o cfssljson https://github.com/cloudflare/cfssl/releases/download/v1.6.4/cfssljson_1.6.4_linux_amd64
curl -s -L -o cfssl https://github.com/cloudflare/cfssl/releases/download/v1.6.4/cfssl_1.6.4_linux_amd64
curl -s -L -o cfssl-certinfo https://github.com/cloudflare/cfssl/releases/download/v1.6.4/cfssl-certinfo_1.6.4_linux_amd64
[root@k8s-master bck]# chmod +x cfssl
[root@k8s-master bck]# chmod +x cfssl-certinfo
[root@k8s-master bck]# chmod +x cfssljson
[root@k8s-master bck]# mv cfssl /usr/bin/
[root@k8s-master bck]# mv cfssl-certinfo /usr/bin/
[root@k8s-master bck]# mv cfssljson /usr/bin/
4.6.2 自签根证书
1.自定义根证书配置文件 ca-config.json。
- profiles:指定不同角色的配置信息。
- 服务端使用server auth(TLS Web Server Authentication X509 V3 extension)。
- 客户端使用client auth(TLS Web Server Authentication X509 V3 extension)
- expiry:证书过期时间。
cat > ca-config.json <<EOF
{
"signing": {
"default": {
"expiry": "87600h"
},
"profiles": {
"kubernetes": {
"expiry": "87600h",
"usages": [
"signing",
"key encipherment",
"server auth",
"client auth"
]
}
}
}
}
EOF
2.自定义根证书请求文件ca-csr.json。
- CN,即common name
- key:指定加密算法,一般使用rsa(size:2048)
cat > ca-csr.json <<EOF
{
"CN": "kubernetes",
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "CN",
"L": "Beijing",
"ST": "Beijing"
}
]
}
EOF
3.根据ca-csr.json自签CA。通过gencert -initca来生成私钥和公钥,使用cfssljson命令将它们分别保存。
[root@k8s-master ssl]# cfssl gencert -initca ca-csr.json | cfssljson -bare ca -
- 私钥ca-key.pem、证书请求ca.csr、公钥ca.pem
4.生成客户端证书。
cat > qingjun-csr.json <<EOF
{
"CN": "web.qingjun.cn", ##自定义访问域名
"hosts": [],
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "CN",
"L": "BeiJing",
"ST": "BeiJing"
}
]
}
EOF
##生成客户端证书,再传入到cfssljson中,生成以qingjun.baimu.cn为前缀的证书。
## -profile参数代表使用请求文件中的哪块配置。
[root@k8s-master ssl]# cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes qingjun-csr.json | cfssljson -bare web.qingjun.cn
4.6.3 证书放入secret
1.将证书放入K8s里。
[root@k8s-master ssl]# kubectl create secret tls baimu --cert=web.qingjun.cn.pem --key=web.qingjun.cn-key.pem
2.查看结果。
4.6.4 ingress规则指定secret
1.编辑ingress规则文件,添加几行,导入yaml。
[root@k8s-master ~]# cat ingress2.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: httpd
spec:
ingressClassName: nginx
tls: ##定义TLS。
- hosts:
- web.qingjun.cn ##指定https域名。
secretName: baimu ##secret名称
rules:
- host: web.qingjun.cn ##http域名,需要与https域名完全一致。
http:
paths:
- backend:
service:
name: httpd
port:
number: 99
path: /
pathType: Exact
6.IP域名绑定解析,需要确定ingress controller pod所在宿主机IP。
7.访问网页。
4.7 TLS+deamonset
- 上面测试的所有效果都有一个弊端,就职只能配置ingress controller pod所在宿主机ip才能实现效果,这时可以配合daemonset给每个Node部署一个pod,就可以解决这个问题。
1.修改ingress yaml文件,将Deployment模式改成DaemonSet模式,再apply -f创建新的。
2.查看,此时2个工作节点都有ingress pod。
4.此时使用192.168.130.147、192.168.130.146就都可以访问网页了。
4.8 Headless Services
1.创建headless services
[root@k8s-master ~]# cat service.yaml
apiVersion: v1
kind: Service
metadata:
name: nginx
labels:
app: nginx
spec:
type: ClusterIP
clusterIP: None ##设置为None。
selector:
app: nginx
ports:
- port: 188
[root@k8s-master ~]# kubectl apply -f service.yaml
2.查看这个svc关联的pod,DNS返回的Endpoint IP地址不在是一个,而是与svc的selector labels相匹配的所有pod。当客户端访问到这个svc时会返回所有ep列表,然后由客户端程序自行决定如何返回具体pod程序操作。
3.若Headless Service没有设置Label Selector,就不会自动创建ep列表。
- DNS系统会根据下列条件尝试对该服务名设置DNS记录:
- 如果Service的类型为ExternalName,则对服务名的访问将直接被DNS系统转换为Service设置的外部名称(externalName)。
- 系统中存在与Service同名的Endpoint定义,则服务名将被解析为Endpoint定义中的列表,适用于非ExternalName类型的Service。