Service概述
为什么要使用Service
Kubernetes Pod是平凡的,由Deployment等控制器管理的Pod对象都是有生命周期的,它们会被创建,也会意外挂掉。虽然它们可以由控制器自动重建或者滚动更新,但是重建或更新之后的Pod对象的IP地址等都会发生新的变化。这样就会导致一个问题:
如果一组Pod(称为backend)为其它Pod(称为 frontend)提供服务,那么那些frontend该如何发现,并连接到这组Pod中的哪些backend呢? 这时候就用到了:Service
示例:
如下图所示,当Nginx Pod
作为客户端访问Tomcat Pod
中的应用时(nginx代理tomcat pod的ip地址),IP
的变动或应用规模的缩减会导致客户端访问错误。而Pod
规模的扩容又会使得客户端无法有效的使用新增的Pod
对象,从而影响达成规模扩展之目的。为此,Kubernetes
特地设计了Service
资源来解决此类问题。
下图是引入svc机制的架构:
Service实现原理
Kubernetes Service 定义了这样一种抽象:
- 将一组 Pod定义成一个逻辑分组,并通过自己的
IP
地址和端口调度代理请求至组内的Pod
对象之上; - 一种可以访问它们的策略 —— 通常称为微服务。
这一组 Pod 能够被 Service 访问到,通常是通过 Label Selector
如下图所示:它向客户端隐藏了真实的、处理用户请求的Pod
资源,使得客户端的请求看上去就像是由Service
直接处理并响应一样。
Service能够提供负载均衡的能力,但是在使用上有以下限制:
只提供 4 层负载均衡能力(ip端口代理),而没有 7 层功能(ip,端口,hostname,域名等等),但有时我们可能需要更多的匹配规则来转发请求,这点上 4 层负载均衡是不支持的
Service
资源会通过API Server
持续监视着(watch
)标签选择器匹配到的后端Pod
对象,并实时跟踪各对象的变动,例如,IP
地址变动、对象增加或减少等。Service
并不直接链接至Pod
对象,它们之间还有一个中间层——Endpoints
资源对象,它是一个由IP
地址和端口组成的列表,这些IP
地址和端口则来自由Service
的标签选择器匹配到的Pod
资源。当创建service
对象时,其关联的Endpoints
对象会自动创建。
虚拟IP和服务代理
在 Kubernetes 集群中,每个 Node 运行一个 kube-proxy 进程。 kube-proxy 负责为 Service 实现了一种VIP(虚拟 IP)的形式,而不是 ExternalName 的形式。
一个
Service
对象就是工作节点上的一些iptables
或ipvs
规则,用于将到达Service
对象IP
地址的流量调度转发至相应的Endpoints
对象指定的IP
地址和端口之上。kube-proxy
组件通过API Server
持续监控着各Service
及其关联的Pod
对象,并将其创建或变动实时反映到当前工作节点上的iptables
规则或ipvs
规则上。
在 Kubernetes v1.0 版本,代理完全在 userspace。
在Kubernetes v1.1 版本,新增了 iptables 代理,但并不是默认的运行模式。 从 Kubernetes v1.2 起,默认就是iptables 代理。
在 Kubernetes v1.8.0-beta.0 中,添加了 ipvs 代理,在 Kubernetes 1.14 版本开始默认使用 ipvs 代理。
ipvs
是借助于Netfilter
实现的网络请求报文调度框架,支持rr
、wrr
、lc
、wlc
、sh
、sed
和nq
等十余种调度算法,用户空间的命令行工具是ipvsadm
,用于管理工作与ipvs
之上的调度规则。
在 Kubernetes v1.0 版本, Service 是 “4层”(TCP/UDP over IP)概念。 在 Kubernetes v1.1 版本,新增了Ingress API(beta 版),用来表示 “7层”(HTTP)服务
Service IP
事实上是用于生成iptables
或ipvs
规则时使用的IP
地址,仅用于实现Kubernetes
集群网络的内部通信,并且能够将规则中定义的转发服务的请求作为目标地址予以相应,这也是将其称为虚拟IP
的原因之一。
!为何不使用 round-robin DNS?
解析在客户端缓存,很多客户端解析后不会及时清理,可能会造成无法及时更新。
kube-proxy
将请求代理至相应端点的方式有三种:
- userspace(用户空间);
- iptables;
- ipvs。
userspace代理模式
userspace
是Linux
操作系统的用户空间。这种模式下,kube-proxy
负责跟踪API Server
上Service
和Endpoints
对象的变动(创建或移除),并据此调整Service
资源的定义。对于每个Service
对象,它会随机打开一个本地端口(运行于用户控件的kube-proxy
进程负责监听),任何到达此端口的连接请求都将代理至当前Service
资源后端的各Pod
对象上,至于会挑选中哪个Pod
对象则取决于当前Service
资源的调度方式(通过Service
的SessionAffinity
来确定),默认的调度算法是轮循(round-robin
)。
其代理的过程是:请求到达service
后,其被转发至内核空间,经由套接字送往用户空间的kube-proxy
,而后再由它送回内核空间,并调度至后端Pod
。其传输效率太低,在1.1
版本前是默认的转发策略。
iptables代理模式
iptables
代理模式中,kube-proxy
负责跟踪API Server
上Service
和Endpoints
对象的变动(创建或移除),并据此作出Service
资源定义的变动。同时,对于每个Service
对象,它都会创建iptables
规则直接捕获到达Cluster IP
(虚拟IP)和Port
的流量,并将其重定向至当前Service
的后端。对于每个Endpoints
对象,Service
资源会为其创建iptables
规则并关联至挑选的后端Pod
资源,默认的调度算法是随机调度(random
)。实现基于客户端IP
的会话亲和性(来自同一个用户的请求始终调度到后端固定的一个Pod
),可将service.spec.sessionAffinity
的值设置为“ClientIP”
(默认值为“None”
)。
其代理过程是:请求到达service
后,其请求被相关service
上的iptables
规则进行调度和目标地址转换(DNAT
)后再转发至集群内的Pod
对象之上。
相对userspace
模式来说,iptables
模式无须将流量在用户空间和内核空间来回切换,因而更加高效和可靠。其缺点是iptables
代理模型不会在被挑中的Pod
资源无响应时自动进行重定向;而userspace
模式则可以。
ipvs代理模式
这种模式,kube-proxy 会监视 Kubernetes Service 对象和 Endpoints ,调用 netlink 接口以相应地创建ipvs 规则并定期与 Kubernetes Service 对象和 Endpoints 对象同步 ipvs 规则,以确保 ipvs 状态与期望一致。访问服务时,流量将被重定向到其中一个后端 Pod;
与 iptables 类似,ipvs 于 netfilter 的 hook 功能,但使用哈希表作为底层数据结构并在内核空间中工作。这意味着 ipvs 可以更快地重定向流量,并且在同步代理规则时具有更好的性能。此外,ipvs 为负载均衡算法提供了更多选项,例如:
- rr :轮询调度
- lc :最小连接数
- dh :目标哈希
- sh :源哈希
- sed :最短期望延迟
- nq : 不排队调度
查看自己使用的集群使用的是否是 ipvs
ipvsadm -Ln
kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubenetes ClusterIp 10.96.0.1 <none> 443/TCP 4d23h
cluster-ip的地址与ipvs的相对应
ClusterIP
clusterIP 主要在每个 node 节点使用 iptables(或ipvs),将发向 clusterIP 对应端口的数据,转发到 kube-proxy 中。然后 kube-proxy 自己内部实现有负载均衡的方法,并可以查询到这个 service 下对应 pod 的地址和端口,进而把数据转发给对应的 pod 的地址和端口
为了实现图上的功能,主要需要以下几个组件的协同工作:
- apiserver 用户通过kubectl命令向apiserver发送创建service的命令,apiserver接收到请求后将数据存储到etcd中;
- kube-proxy kubernetes的每个节点中都有一个叫做kube-porxy的进程,这个进程负责感知service,pod的变化,并将变化的信息写入本地的iptables规则中;
- iptables 使用NAT等技术将virtualIP的流量转至endpoint中
Service会话粘性
Service
资源支持Session affinity
(粘性会话或会话粘性)机制,能够将来自同一个客户端的请求始终转发至同一个后端的Pod
对象。这意味着会影响调度算法的流量分发功能,进而降低其负载均衡的效果。所以,当客户端访问pod
中的应用程序时,如果有基于客户端身份保存某些私有信息,并基于这些私有信息追踪用户的活动等一类的需求时,就可以启用session affinity
机制。
Session affinity
的效果仅在一段时间期限内生效,默认值为10800
秒,超出此时长之后,客户端的再次访问会被调度算法重新调度。Service
资源的Session affinity
机制仅能基于客户端的IP
地址识别客户端身份,把经由同一个NAT
服务器进行源地址转换的所有客户端识别为同一个客户端,便导致调度效果不佳,所以,这种方法并不常用。
Service
资源通过service.spec.sessionAffinity
和service.spec.sessionAffinityConfig
两个字段配置粘性会话。sessionAffinity
字段用于定义要使用的粘性会话的类型,仅支持使用“None”
和“ClientIp”
两种属性值。
-
None:不使用
sessionAffinity
,默认值。 -
ClientIP:基于客户端
IP
地址识别客户端身份,把来自同一个源IP
地址的请求始终调度至同一个Pod
对象。
示例
这里将上面创建的service-demo
资源对象进行修改
[root@k8s-master ~]# vim manfests/service-demo.yaml
......
spec:
selector:
app: service-deploy-demo
ports:
- port: 80
targetPort: 80
protocol: TCP
sessionAffinity: ClientIP #指定使用ClientIP
sessionAffinityConfig:
clientIP:
timeoutSeconds: 10 #配置session超时时间,这里为了看效果设置的比较短
[root@k8s-master ~]# kubectl apply -f manfests/service-demo.yaml
deployment.apps/service-deploy unchanged
#同样使用pod客户端访问测试
/ # for i in 1 2 3 4; do wget -O - -q http://10.98.31.157/hostname.html; done
service-deploy-66548cc57f-blnvg
service-deploy-66548cc57f-blnvg
service-deploy-66548cc57f-blnvg
service-deploy-66548cc57f-blnvg
#等待10秒过后再次访问
/ # for i in 1 2 3 4; do wget -O - -q http://10.98.31.157/hostname.html; done
service-deploy-66548cc57f-vcmxb
service-deploy-66548cc57f-vcmxb
service-deploy-66548cc57f-vcmxb
service-deploy-66548cc57f-vcmxb
Service的类型
Service 在 K8s 中有以下四种类型:
-
ClusterIp:默认类型,自动分配一个仅 Cluster 内部可以访问的虚拟 IP,无法被集群外部的客户端访问。
-
NodePort:(可外网访问)在 ClusterIP 基础上为 Service 在每台机器上绑定一个端口,这样就可以通过 : NodePort 来访问该服务
通过请求
<NodePort>:<NodePort> --> <ClusterIP>:<ClusterPort> --> <PodIP>:<ContainerPort>
访问到一个NodePort
服务。 -
LoadBalancer:在 NodePort 的基础上,借助 cloud provider(外部云厂商) 创建一个外部负载均衡器,并将请求转发到: NodePort
需要借助于供应商来保存注册、端口等信息,需要单独收费
-
ExternalName:把集群外部的服务引入到集群内部来,在集群内部直接使用。没有任何类型代理被创建,这只有 kubernetes1.7 或更高版本的 kube-dns 才支持
通过返回
CNAME
和它的值,可以将服务映射到externalName
字段的内容。换言之,此种类型并非定义由Kubernetes
集群提供的服务,而是把集群外部的某服务以DNS CNAME
记录的方式映射到集群内,从而让集群内的Pod
资源能够访问外部的Service
的一种实现方式。这种类型的Service
没有ClusterIP
和NodePort
,也没有标签选择器用于选择Pod
资源,因此也不会有Endpoints
存在。
ClusterIP类型的Service示例
1)编写配置清单文件(这里使用redis
作为示例);先创建一个deployment
,启动redis pod
;再使用service
绑定这个deployment
下的pod
资源。
[root@k8s-master ~]# vim manfests/redis-svc.yaml #编写yaml格式的清单文件
apiVersion: apps/v1
kind: Deployment
metadata:
name: redis-deploy
namespace: default
spec:
replicas: 2
selector:
matchLabels:
app: redis
template:
metadata:
labels:
app: redis
spec:
containers:
- name: redis-pod
image: redis
ports:
- name: redis
containerPort: 6379
---
apiVersion: v1
kind: Service
metadata:
name: redis-svc #service对象名
spec:
type: ClusterIP #这里指定使用ClusterIP,默认也是ClusterIP,这里可有可无
selector:
app: redis #匹配上面定义的pod资源
ports:
- port: 6379 #service端口
targetPort: 6379 #后端pod端口
protocol: TCP #协议
[root@k8s-master ~]# kubectl apply -f manfests/redis-svc.yaml #创建资源对象
deployment.apps/redis-deploy created
service/redis-svc created
2)查看创建的资源对象
[root@k8s-master ~]# kubectl get svc #查看service资源
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 27d
nginx ClusterIP 10.104.116.156 <none> 80/TCP 17h
redis-svc ClusterIP 10.102.44.127 <none> 6379/TCP 8s
service-demo ClusterIP 10.98.31.157 <none> 80/TCP 16h
[root@k8s-master ~]# kubectl get pods -l app=redis -o wide #查看标签app=redis的pod资源
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
redis-deploy-6559cc4c4c-5v7kx 1/1 Running 0 33s 10.244.2.65 k8s-node2 <none> <none>
redis-deploy-6559cc4c4c-npdtf 1/1 Running 0 33s 10.244.1.69 k8s-node1 <none> <none>
[root@k8s-master ~]# kubectl describe svc redis-svc #查看redis-svc资源对象详细信息
Name: redis-svc
Namespace: default
Labels: <none>
Annotations: kubectl.kubernetes.io/last-applied-configuration:
{"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"name":"redis-svc","namespace":"default"},"spec":{"ports":[{"port":6379,"...
Selector: app=redis
Type: ClusterIP
IP: 10.102.44.127
Port: <unset> 6379/TCP
TargetPort: 6379/TCP
Endpoints: 10.244.1.69:6379,10.244.2.65:6379 #可以看出这里已经和上面的pod资源绑定
Session Affinity: None
Events: <none>
3)集群内部进行测试
#(1)集群内部的节点上面测试(10.102.44.127是svc的地址)
[root@k8s-master ~]# redis-cli -h 10.102.44.127
10.102.44.127:6379> ping
PON
#(2)在后端pod上面进行测试
[root@k8s-master ~]# kubectl exec redis-deploy-6559cc4c4c-5v7kx -it -- /bin/sh
# redis-cli -h 10.102.44.127
10.102.44.127:6379> ping
PONG
# 如果我们创建一个svc,标签是redis1的话,那么我们是访问不通的,svc是通过标签来匹配pod资源的!!可以使用 `ipvsadm -Ln`来查看svc的地址下是否挂载了其他地址
NodePort类型的Service示例
NodePort
即节点Port
,通常在安装部署Kubernetes
集群系统时会预留一个端口范围用于NodePort
,默认为30000~32767
之间的端口。定义NodePort
类型的Service
资源时,需要使用.spec.type
明确指定类型为NodePort
。
其原理就是在node上开了一个端口,将向该端口的流量导入到kube-proxy,然后由kube-proxy进一步到对应的pod
1)编写配置清单文件(这里使用nginx
作为示例);先创建一个deployment
,启动nginx pod
;再使用service
绑定这个deployment
下的pod
资源。
[root@k8s-master ~]# vim manfests/nginx-svc.yaml #编写yaml格式的清单文件
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deploy
namespace: default
spec:
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx-pod
image: nginx:1.12
ports:
- name: nginx
containerPort: 6379
---
apiVersion: v1
kind: Service
metadata:
name: nginx-svc #service对象名
spec:
type: NodePort #这里指定使用ClusterIP,默认也是ClusterIP,这里可有可无
selector:
app: nginx #匹配上面定义的pod资源
ports:
- port: 80 #service端口
targetPort: 80 #后端pod端口
nodePort: 30080 #节点端口
protocol: TCP #协议
[root@k8s-master ~]# kubectl apply -f manfests/nginx-svc.yaml #创建资源对象
deployment.apps/nginx-deploy created
service/nginx-svc created
2)查看创建的资源对象
[root@k8s-master ~]# kubectl get svc #查看service资源
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 27d
nginx-svc NodePort 10.105.21.137 <none> 80:30080/TCP 4s
redis-svc ClusterIP 10.102.44.127 <none> 6379/TCP 55m
service-demo ClusterIP 10.98.31.157 <none> 80/TCP 16h
[root@k8s-master ~]# kubectl get pods -l app=nginx -o wide #查看标签app=nginx的pod资源
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-deploy-b6f876447-nlv6h 1/1 Running 0 33s 10.244.1.71 k8s-node1 <none> <none>
nginx-deploy-b6f876447-xmn2t 1/1 Running 0 33s 10.244.2.66 k8s-node2 <none> <none>
[root@k8s-master ~]# kubectl describe svc nginx-svc #查看nginx-svc资源对象详细信息
Name: nginx-svc
Namespace: default
Labels: <none>
Annotations: kubectl.kubernetes.io/last-applied-configuration:
{"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"name":"nginx-svc","namespace":"default"},"spec":{"ports":[{"nodePort":30...
Selector: app=nginx
Type: NodePort
IP: 10.105.21.137
Port: <unset> 80/TCP
TargetPort: 80/TCP
NodePort: <unset> 30080/TCP #这里可以看到多了NodePort且端口为30080
Endpoints: 10.244.1.71:80,10.244.2.66:80
Session Affinity: None
External Traffic Policy: Cluster
Events: <none>
3)集群外部进行测试
[root@courtoap ~]# curl 192.168.1.31:30080 #访问集群master节点的30080端口
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
[root@courtoap ~]# curl 192.168.1.32:30080 #访问集群node节点的30080端口
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
通过上面的测试可以看到通过NodePort
的方式实现了从集群外部端口进行访问,实践中并不建议自定义使用的节点端口,因为容易产生冲突。建议让其自动生成即可。
Headless类型的Service示例
Headless Service简称无头服务,是
StatefulSet控制器
的基础,有以下特点:有时不需要或不想要负载均衡,以及单独的Service lP。遇到这种情况,可以通过指定ClusterlP(spec.clusterlP)的值为“None”来创建Headless Service。这类Service并不会分配ClusterIP, kube-proxy不会处理它们,而且平台也不会为它们进行负载均衡和路由。
配置Service
资源配置清单时,只需要将ClusterIP
字段的值设置为“None”
即为其定义为Headless
类型。
1)编写配置清单文件(这里使用apache
作为示例);先创建一个deployment
,启动apache pod
;再使用service
绑定这个deployment
下的pod
资源。
[root@k8s-master ~]# vim manfests/httpd-svc-headless.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: httpd-deploy
namespace: default
spec:
replicas: 3
selector:
matchLabels:
app: httpd
template:
metadata:
labels:
app: httpd
spec:
containers:
- name: httpd-pod
image: httpd
ports:
- name: httpd
containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: httpd-svc #service对象名
spec:
clusterIP: None #将ClusterIP字段设置为None即表示为headless类型的service资源对象
selector:
app: httpd #匹配上面定义的pod资源
ports:
- port: 80 #service端口
targetPort: 80 #后端pod端口
protocol: TCP #协议
[root@k8s-master ~]# kubectl apply -f manfests/httpd-svc-headless.yaml
deployment.apps/httpd-deploy created
service/httpd-svc created
2)查看创建的资源对象
[root@k8s-master ~]# kubectl get svc #查看service资源
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
httpd-svc ClusterIP None <none> 80/TCP 4s
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 27d
nginx-svc NodePort 10.105.21.137 <none> 80:30080/TCP 112m
redis-svc ClusterIP 10.102.44.127 <none> 6379/TCP 168m
service-demo ClusterIP 10.98.31.157 <none> 80/TCP 18h
[root@k8s-master ~]# kubectl get pods -l app=httpd -o wide #查看标签app=httpd的pod资源
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
httpd-deploy-5494485b74-4vx64 1/1 Running 0 27s 10.244.2.72 k8s-node2 <none> <none>
httpd-deploy-5494485b74-j6hwm 1/1 Running 0 27s 10.244.2.71 k8s-node2 <none> <none>
httpd-deploy-5494485b74-jn48q 1/1 Running 0 27s 10.244.1.74 k8s-node1 <none> <none>
[root@k8s-master ~]# kubectl describe svc/httpd-svc #查看httpd-svc资源对象详细信息
Name: httpd-svc
Namespace: default
Labels: <none>
Annotations: kubectl.kubernetes.io/last-applied-configuration:
{"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"name":"httpd-svc","namespace":"default"},"spec":{"clusterIP":"None","por...
Selector: app=httpd
Type: ClusterIP
IP: None
Port: <unset> 80/TCP
TargetPort: 80/TCP
Endpoints: 10.244.1.74:80,10.244.2.71:80,10.244.2.72:80
Session Affinity: None
Events: <none>
3)测试资源发现
由Headless Service
工作特性可知,它记录于ClusterDNS
的A
记录的相关解析结果是后端Pod
资源的IP
地址。意味着客户端通过Service
资源的名称发现的是各Pod
资源。
#(1)通过创建一个专用的测试Pod资源对象,而后通过其交互式接口进行测试
[root@k8s-master ~]# kubectl run cirror-$RANDOM --rm -it --image=cirros -- /bin/sh
/ # nslookup httpd-svc
Server: 10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local
Name: httpd-svc
Address 1: 10.244.2.71 10-244-2-71.httpd-svc.default.svc.cluster.local
Address 2: 10.244.1.74 10-244-1-74.httpd-svc.default.svc.cluster.local
Address 3: 10.244.2.72 10-244-2-72.httpd-svc.default.svc.cluster.local
#(2)直接在kubernetes集群上解析
[root@k8s-master ~]# dig -t A httpd-svc.default.svc.cluster.local. @10.96.0.10
......
;; ANSWER SECTION:
httpd-svc.default.svc.cluster.local. 26 IN A 10.244.2.72
httpd-svc.default.svc.cluster.local. 26 IN A 10.244.2.71
httpd-svc.default.svc.cluster.local. 26 IN A 10.244.1.74
......
# (3) 安装dig命令
[root@k8s-master ~]# yum -y install bind-utils
LoadBalancer Service示例
loadBalancer和nodePort其实是同一种方式。区别在于loadBalancer 比 nodePort多了一步,就是可以调用cloud provider去创建LB来向节点导流
这种方式需要花钱,很贵!
ExternalName Service s示例
这种类型的Service通过返回CNAME和它的值,可以将服务映射到externalName字段的内容(例如: hub.atguigu.com )。ExternalName Service是Service的特例,它没有selector,也没有定义任何的端口和Endpoint。相反的,对于运行在集群外部的服务,它通过返回该外部服务的别名这种方式来提供服务
kind: Service
apiVersion: v1
metadata:
name: my-service-1
namespace: default
spec:
type: ExternalName
externalName: hub.atguigu.com
[root@k8s-master ~]# kubectl create -f ex.yaml
service/my-service-1 created
[root@k8s-master ~]#kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
my-service-1 ExternalName <none> hub.atguigu.com <none> 4s
[root@k8s-master ~]# dig -t A my-service-1.default.svc.cluster.local. @10.96.0.10
当查询主机 my-service1.defalut.svc.cluster.local ( SVC_NAME.NAMESPACE.svc.cluster.local )时,集群的DNS服务将返回一个值hub.atguigu.com 的CNAME记录。访问这个服务的工作方式和其他的相同,唯一不同的是重定向发生在 DNS层,而且不会进行代理或转发
Ingress概述
什么是Ingress?
通常情况下,service
和pod
仅可在集群内部网络中通过IP
地址访问。所有到达边界路由器的流量或被丢弃或被转发到其他地方。从概念上讲,可能像下面这样:
Ingress是授权入站连接到达集群服务的规则集合。
你可以给Ingress
配置提供外部可访问的URL
、负载均衡、SSL
、基于名称的虚拟主机等。用户通过POST Ingress
资源到API Server
的方式来请求Ingress
。Ingress controller
负责实现Ingress
,通常使用负载平衡器,它还可以配置边界路由和其他前端,这有助于以HA
方式处理流量。
internet
|
------------
[ Services ]
internet
|
[ Ingress ]
--|-----|--
[ Services ]
Ingress和Ingress Controller
Ingress
是Kubernetes API
的标准资源类型之一,它其实就是一组基于DNS
名称(host
)或URL
路径把请求转发至指定的Service
资源的规则,用于将集群外部的请求流量转发至集群内部完成服务发布。然而,Ingress
资源自身并不能进行“流量穿透”,它仅是一组路由规则的集合,这些规则要想真正发挥作用还需要其他功能的辅助,如监听某套接字,然后根据这些规则的匹配机制路由请求流量。这种能够为Ingress
资源监听套接字并转发流量的组件称为Ingress
控制器(Ingress Controller
)。
Ingress
控制器并不直接运行为kube-controller-manager
的一部分,它是Kubernetes
集群的一个重要组件,类似CoreDNS
,需要在集群上单独部署。
我的理解:
ingress是一组规则集合,ingress controller则是将这组规则应用并转发流量至服务的组件!
Ingress工作流程
如下图所示,
- 流量到达外部负载均衡器(externalLB)后,首先转发至Service资源Ingres-nginx上(多个ingress,为了统一固定ip,所以用service);
- 然后通过Ingress控制器基于Ingress资源定义的规则将客户端请求流量直接转发至与Service对应的后端Pod资源之上。
这种转发机制会绕过Service资源(app Service;api Service),从而省去了由kube-proxy实现的端口代理开销。Ingress规则需要由一个Service资源对象辅助识别相关的所有Pod资源。如下Ingress通过app service资源去匹配后端的pod1和pod2;这个app service只是起到一个辅助识别功能。
先决条件
在使用Ingress resource之前,必须先了解下面几件事情。Ingress是beta版本的resource,在kubernetes1.1之前还没有。你需要一个Ingress Controller来实现Ingress,单纯的创建一个Ingress没有任何意义。
GCE/GKE会在master节点上部署一个Ingress Controller。你可以在一个Pod中部署任意个自定义的Ingress Controller。你必须正确的annotate每个Ingress,比如运行多个Ingress Controller和关闭glbc。
Ingress清单文件几个字段说明
Ingress资源是基于HTTP虚拟主机或URL的转发规则,spec字段中嵌套了rules、backend、tls等字段进行定义。下面这个示例中,它包含了一个转发规则,把发往www.ilinux.io的请求代理给名为myapp-svc的Service资源。
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: ingress-demo
namespace: default
annotations:
kubernetes.io/ingress.class: "nginx"
spec:
rules:
- host: www.ilinux.io
http:
paths:
- backend:
serviceName: myapp-svc
servicePort: 80
#说明:上面资源清单文件中的annotations用于识别其所属的Ingress控制器的类别,这一点在集群上部署多个Ingress控制器时尤为重要。
Ingress Spec(# kubectl explain ingress.spec)中的字段是定义Ingress资源的核心组成部分,主要嵌套如下三个字段:
● rules <[]Object>:用于定义当前Ingress资源的转发规则列表;未由rules定义规则,或者没有匹配到任何规则时,所有流量都会转发到由backend定义的默认后端。
● backend :默认的后端用于服务那些没有匹配到任何规则的请求;定义Ingress资源时,至少应该定义backend或rules两者之一;此字段用于让负载均衡器指定一个全局默认的后端。
● tls <[]Object>:TLS配置,目前仅支持通过默认端口443提供服务;如果要配置指定的列表成员指向了不同的主机,则必须通过SNI TLS扩展机制来支持此功能。
ingress.spec.rules.http.paths.backend
对象的定义由两个必须的内嵌字段组成:serviceName和servicePort,分别用于指定流量转发的后端目标Service资源的名称和端口。
部署Ingress Controller(Nginx)
描述
Ingress 控制器自身是运行于Pod中的容器应用,一般是Nginx或Envoy一类的具有代理及负载均衡功能的守护进程,它监视着来自API Server的Ingress对象状态,并根据规则生成相应的应用程序专有格式的配置文件并通过重载或重启守护进程而使新配置生效。
Ingress控制器其实就是托管于Kubernetes系统之上的用于实现在应用层发布服务的Pod资源,跟踪Ingress资源并实时生成配置规则。
运行为Pod资源的Ingress控制器进程通过下面两种方式接入外部请求流量:
- 以Deployment控制器管理Ingress控制器的Pod资源,通过NodePort或LoadBalancer类型的Service对象为其接入集群外部的请求流量,这就意味着,定义一个Ingress控制器时,必须在其前端定义一个专用的Service资源。
- 借助于DaemonSet控制器,将Ingress控制器的Pod资源各自以单一实例的方式运行于集群的所有或部分工作节点之上,并配置这类Pod对象以HostPort(如下图中的a)或HostNetwork(如下图中的b)的方式在当前节点接入外部流量。
nginx的访问方案
原理:
部署
Ingress-nginx官网
Ingress-nginx GitHub仓库地址
Ingress安装文档
1)在github上下载配置清单yaml文件,并创建部署
[root@k8s-master ~]# mkdir ingress-nginx #这里创建一个目录专门用于ingress-nginx(可省略)
[root@k8s-master ~]# cd ingress-nginx/
[root@k8s-master ingress-nginx]# wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/mandatory.yaml #下载配置清单yaml文件
[root@k8s-master ingress-nginx]# ls #查看下载的文件
mandatory.yaml
[root@k8s-master ingress-nginx]# kubectl apply -f mandatory.yaml #创建Ingress
namespace/ingress-nginx created
configmap/nginx-configuration created
configmap/tcp-services created
configmap/udp-services created
serviceaccount/nginx-ingress-serviceaccount created
clusterrole.rbac.authorization.k8s.io/nginx-ingress-clusterrole created
role.rbac.authorization.k8s.io/nginx-ingress-role created
rolebinding.rbac.authorization.k8s.io/nginx-ingress-role-nisa-binding created
clusterrolebinding.rbac.authorization.k8s.io/nginx-ingress-clusterrole-nisa-binding created
deployment.apps/nginx-ingress-controller created
2)验证
[root@k8s-master ingress-nginx]# kubectl get pods -n ingress-nginx #查看生成的pod,注意这里在ingress-nginx名称空间
NAME READY STATUS RESTARTS AGE
nginx-ingress-controller-79f6884cf6-5fb6v 1/1 Running 0 18m
[root@k8s-master ingress-nginx]# kubectl describe pod nginx-ingress-controller-79f6884cf6-5fb6v -n ingress-nginx 查看该pod的详细信息
Name: nginx-ingress-controller-79f6884cf6-5fb6v
Namespace: ingress-nginx
Priority: 0
Node: k8s-node2/192.168.1.33
Start Time: Fri, 27 Sep 2019 17:53:07 +0800
Labels: app.kubernetes.io/name=ingress-nginx
app.kubernetes.io/part-of=ingress-nginx
pod-template-hash=79f6884cf6
Annotations: prometheus.io/port: 10254
prometheus.io/scrape: true
Status: Running
IP: 10.244.2.73
......
3)如果是裸机部署,还需要安装service。(比如VMware虚拟机、硬件服务器等)
---同样去官网下载配置清单文件,也可以自定义创建。
[root@k8s-master ingress-nginx]# wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/provider/baremetal/service-nodeport.yaml
[root@k8s-master ingress-nginx]# kubectl apply -f service-nodeport.yaml #创建service资源
service/ingress-nginx created
[root@k8s-master ingress-nginx]# kubectl get svc -n ingress-nginx #查看service资源
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ingress-nginx NodePort 10.107.40.182 <none> 80:32699/TCP,443:30842/TCP 9s
[root@k8s-master ingress-nginx]# kubectl describe svc/ingress-nginx -n ingress-nginx #查看该service的详细信息
Name: ingress-nginx
Namespace: ingress-nginx
Labels: app.kubernetes.io/name=ingress-nginx
app.kubernetes.io/part-of=ingress-nginx
Annotations: kubectl.kubernetes.io/last-applied-configuration:
{"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"labels":{"app.kubernetes.io/name":"ingress-nginx","app.kubernetes.io/par...
Selector: app.kubernetes.io/name=ingress-nginx,app.kubernetes.io/part-of=ingress-nginx
Type: NodePort
IP: 10.107.40.182
Port: http 80/TCP
TargetPort: 80/TCP
NodePort: http 32699/TCP
Endpoints: 10.244.2.73:80
Port: https 443/TCP
TargetPort: 443/TCP
NodePort: https 30842/TCP
Endpoints: 10.244.2.73:443
Session Affinity: None
External Traffic Policy: Cluster
Events: <none>
通过上面创建的service资源对象可以看出,随机分配的http的NodePort为32668,https的NodePort的为30606。该端口也可以自定义,在前面的service章节说过。单一般不建议自定义。
示例1:使用Ingress发布Nginx
该示例中创建的所有资源都位于新建的testing名称空间中。与其他的资源在逻辑上进行隔离,以方便管理。
首先创建一个单独的目录为了方便管理
[root@k8s-master ~]# mkdir ingress-nginx/ingress
[root@k8s-master ~]# cd ingress-nginx/ingress/
(1)、创建testing名称空间(也可以使用命令直接创建# kubectl create namespace my-namespace,不过这里使用资源清单格式创建)
[root@k8s-master ingress]# vim namespace-testing.yaml #编写namespace清单文件
apiVersion: v1
kind: Namespace
metadata:
name: testing
labels:
env: testing
[root@k8s-master ingress]#
[root@k8s-master ingress]# kubectl apply -f namespace-testing.yaml #创建namespace
namespace/testing created
[root@k8s-master ingress]#
[root@k8s-master ingress]# kubectl get namespace testing #验证
NAME STATUS AGE
testing Active 12s
(2)、部署nginx实例,这里使用Deployment控制器于testing中部署nginx相关的Pod对象。
[root@k8s-master ingress]# vim deployment-nginx.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy-nginx
namespace: testing
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.12
ports:
- name: http
containerPort: 80
[root@k8s-master ingress]#
[root@k8s-master ingress]# kubectl apply -f deployment-nginx.yaml
deployment.apps/deploy-nginx created
[root@k8s-master ingress]#
[root@k8s-master ingress]# kubectl get deploy -n testing
NAME READY UP-TO-DATE AVAILABLE AGE
deploy-nginx 3/3 3 3 5s
[root@k8s-master ingress]#
[root@k8s-master ingress]# kubectl get pods -n testing
NAME READY STATUS RESTARTS AGE
deploy-nginx-686bddcb56-9g7pq 1/1 Running 0 6s
deploy-nginx-686bddcb56-gqpm2 1/1 Running 0 6s
deploy-nginx-686bddcb56-vtwkq 1/1 Running 0 6s
(3)、创建Service资源,关联后端的Pod资源。这里通过service资源svc-nginx的80端口去暴露容器的80端口。
[root@k8s-master ingress]# vim service-nginx.yaml
apiVersion: v1
kind: Service
metadata:
name: svc-nginx
namespace: testing
labels:
app: svc-nginx
spec:
selector:
app: nginx
ports:
- name: http
port: 80
targetPort: 80
protocol: TCP
[root@k8s-master ingress]#
[root@k8s-master ingress]# kubectl apply -f service-nginx.yaml
service/svc-nginx created
[root@k8s-master ingress]#
[root@k8s-master ingress]# kubectl get svc -n testing
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
svc-nginx ClusterIP 10.99.233.90 <none> 80/TCP 6s
[root@k8s-master ingress]#
[root@k8s-master ingress]# kubectl describe svc/svc-nginx -n testing
Name: svc-nginx
Namespace: testing
Labels: app=svc-nginx
Annotations: kubectl.kubernetes.io/last-applied-configuration:
{"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"labels":{"app":"svc-nginx"},"name":"svc-nginx","namespace":"testing"},"s...
Selector: app=nginx
Type: ClusterIP
IP: 10.99.233.90
Port: http 80/TCP
TargetPort: 80/TCP
Endpoints: 10.244.1.76:80,10.244.1.77:80,10.244.2.74:80
Session Affinity: None
Events: <none>
(4)、创建Ingress资源,匹配Service资源svc-nginx,并将svc-nginx的80端口暴露。
[root@k8s-master ingress]# vim ingress-nginx.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: nginx
namespace: testing
annotations:
kubernetes.io/ingress.class: "nginx"
spec:
rules:
- host: nginx.ilinux.io
http:
paths:
- path:
backend:
serviceName: svc-nginx
servicePort: 80
[root@k8s-master ingress]#
[root@k8s-master ingress]# kubectl apply -f ingress-nginx.yaml
ingress.extensions/nginx created
[root@k8s-master ingress]#
[root@k8s-master ingress]# kubectl get ingress -n testing
NAME HOSTS ADDRESS PORTS AGE
nginx nginx.ilinux.io 80 16s
[root@k8s-master ingress]#
[root@k8s-master ingress]# kubectl describe ingress -n testing
Name: nginx
Namespace: testing
Address:
Default backend: default-http-backend:80 (<none>)
Rules:
Host Path Backends
---- ---- --------
tomcat.ilinux.io
svc-nginx:80 (10.244.1.76:80,10.244.1.77:80,10.244.2.74:80)
Annotations:
kubectl.kubernetes.io/last-applied-configuration: {"apiVersion":"extensions/v1beta1","kind":"Ingress","metadata":{"annotations":{"kubernetes.io/ingress.class":"nginx"},"name":"nginx","namespace":"testing"},"spec":{"rules":[{"host":"nginx.ilinux.io","http":{"paths":[{"backend":{"serviceName":"svc-nginx","servicePort":80},"path":null}]}}]}}
kubernetes.io/ingress.class: nginx
Events: <none>
(5)、测试,通过Ingress控制器的前端的Service资源的NodePort来访问此服务,
#首先查看前面部署Ingress控制器的前端的Service资源的映射端口
[root@k8s-master ingress-nginx]# kubectl get svc -n ingress-nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ingress-nginx NodePort 10.107.40.182 <none> 80:32699/TCP,443:30842/TCP 3m59s
#终端测试,添加hosts
[root@k8s-master ~]# cat /etc/hosts
192.168.1.31 k8s-master nginx.ilinux.io
192.168.1.32 k8s-node1 nginx.ilinux.io
192.168.1.33 k8s-node2 nginx.ilinux.io
#访问测试
[root@k8s-master ~]# curl nginx.ilinux.io:32699
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
......
验证是否调度到后端的Pod资源,查看日志
[root@k8s-master ~]# kubectl get pods -n testing
NAME READY STATUS RESTARTS AGE
deploy-nginx-686bddcb56-9g7pq 1/1 Running 0 56m
deploy-nginx-686bddcb56-gqpm2 1/1 Running 0 56m
deploy-nginx-686bddcb56-vtwkq 1/1 Running 0 56m
[root@k8s-master ~]# kubectl logs deploy-nginx-686bddcb56-9g7pq -n testing
10.244.2.75 - - [28/Sep/2019:02:33:45 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.29.0" "10.244.0.0"
10.244.2.75 - - [28/Sep/2019:02:44:02 +0000] "GET / HTTP/1.1" 200 612 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36" "10.244.0.0"
(6)、配置TLS Ingress资源(这里使用自签证书)
1)生成key
[root@k8s-master ingress]# openssl genrsa -out tls.key 2048
2)生成证书
[root@k8s-master ingress]# openssl req -new -x509 -key tls.key -out tls.crt -subj /C=CN/ST=ShenZhen/L=ShenZhen/O=DevOps/CN=nginx.ilinux.io -days 3650
3)创建secret资源
[root@k8s-master ingress]# kubectl create secret tls nginx-ingress-secret --cert=tls.crt --key=tls.key -n testing
secret/nginx-ingress-secret created
[root@k8s-master ingress]# kubectl get secret -n testing
NAME TYPE DATA AGE
default-token-lfzrt kubernetes.io/service-account-token 3 116m
nginx-ingress-secret kubernetes.io/tls 2 16s
4)编写Ingress资源清单文件
[root@k8s-master ingress]# vim ingress-nginx-https.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: nginx-ingress-tls
namespace: testing
annotations:
kubernetes.io/ingress.class: "nginx"
spec:
tls:
- hosts:
- nginx.ilinux.io
secretName: nginx-ingress-secret
rules:
- host: nginx.ilinux.io
http:
paths:
- path: /
backend:
serviceName: svc-nginx
servicePort: 80
5)查看Ingress资源信息
[root@k8s-master ingress]# kubectl get ingress -n testing
NAME HOSTS ADDRESS PORTS AGE
nginx nginx.ilinux.io 80 66m
nginx-ingress-tls nginx.ilinux.io 80, 443 15s
[root@k8s-master ingress]# kubectl describe ingress/nginx-ingress-tls -n testing
Name: nginx-ingress-tls
Namespace: testing
Address:
Default backend: default-http-backend:80 (<none>)
TLS:
nginx-ingress-secret terminates nginx.ilinux.io
Rules:
Host Path Backends
---- ---- --------
nginx.ilinux.io
/ svc-nginx:80 (10.244.1.76:80,10.244.1.77:80,10.244.2.74:80)
Annotations:
kubectl.kubernetes.io/last-applied-configuration: {"apiVersion":"extensions/v1beta1","kind":"Ingress","metadata":{"annotations":{"kubernetes.io/ingress.class":"nginx"},"name":"nginx-ingress-tls","namespace":"testing"},"spec":{"rules":[{"host":"nginx.ilinux.io","http":{"paths":[{"backend":{"serviceName":"svc-nginx","servicePort":80},"path":"/"}]}}],"tls":[{"hosts":["nginx.ilinux.io"],"secretName":"nginx-ingress-secret"}]}}
kubernetes.io/ingress.class: nginx
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal CREATE 64s nginx-ingress-controller Ingress testing/nginx-ingress-tls
(7)、测试https(这里由于是自签,所以上面提示不安全)
#首先查看前面部署Ingress控制器的前端的Service资源的映射端口
[root@k8s-master ingress-nginx]# kubectl get svc -n ingress-nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ingress-nginx NodePort 10.107.40.182 <none> 80:32699/TCP,443:30842/TCP 3m59s
示例2:使用Ingress发布多个服务
将不同的服务映射不同的主机上
准备工作:这里创建一个目录保存本示例的所有资源配置清单
[root@k8s-master ~]# mkdir ingress-nginx/multi_svc
[root@k8s-master ~]# cd !$
创建名称空间
创建一个名称空间保存本示例的所有对象(方便管理)
[root@k8s-master multi_svc]# vim namespace-ms.yaml #编写配置清单文件
apiVersion: v1
kind: Namespace
metadata:
name: multisvc
labels:
env: multisvc
[root@k8s-master multi_svc]# kubectl apply -f namespace-ms.yaml #创建上面定义的名称空间
namespace/multisvc created
[root@k8s-master multi_svc]# kubectl get namespace multisvc #查看名称空间
NAME STATUS AGE
multisvc Active 9s
创建后端应用和Service
这里后端应用创建为一组nginx应用和一组tomcat应用
1)编写资源清单文件,这里将service资源对象和deployment控制器写在这一个文件里
[root@k8s-master multi_svc]# vim deploy_service-ms.yaml
#tomcat应用的Deployment控制器
apiVersion: apps/v1
kind: Deployment
metadata:
name: tomcat-deploy
namespace: multisvc
spec:
replicas: 3
selector:
matchLabels:
app: tomcat
template:
metadata:
labels:
app: tomcat
spec:
containers:
- name: tomcat
image: tomcat:jdk8
imagePullPolicy: IfNotPresent
ports:
- name: httpport
containerPort: 8080
- name: ajpport
containerPort: 8009
---
#tomcat应用的Service资源
apiVersion: v1
kind: Service
metadata:
name: tomcat-svc
namespace: multisvc
labels:
app: tomcat-svc
spec:
selector:
app: tomcat
ports:
- name: httpport
port: 8080
targetPort: 8080
protocol: TCP
- name: ajpport
port: 8009
targetPort: 8009
protocol: TCP
---
#nginx应用的Deployment控制器
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deploy
namespace: multisvc
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.12
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 80
---
#nginx应用的Service资源
apiVersion: v1
kind: Service
metadata:
name: nginx-svc
namespace: multisvc
labels:
app: nginx-svc
spec:
selector:
app: nginx
ports:
- name: http
port: 80
targetPort: 80
protocol: TCP
2)创建上面定义资源对象并查看验证
[root@k8s-master multi_svc]# kubectl apply -f deploy_service-ms.yaml
deployment.apps/tomcat-deploy created
service/tomcat-svc created
deployment.apps/nginx-deploy created
service/nginx-svc created
[root@k8s-master multi_svc]# kubectl get pods -n multisvc -o wide #查看pod资源
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-deploy-86c667ff66-hl6rx 1/1 Running 0 13s 10.244.2.78 k8s-node2 <none> <none>
nginx-deploy-86c667ff66-hx4j8 1/1 Running 0 13s 10.244.2.77 k8s-node2 <none> <none>
nginx-deploy-86c667ff66-tl9mm 1/1 Running 0 13s 10.244.1.79 k8s-node1 <none> <none>
tomcat-deploy-6484688ddc-n25hn 1/1 Running 0 13s 10.244.1.78 k8s-node1 <none> <none>
tomcat-deploy-6484688ddc-s8dts 1/1 Running 0 13s 10.244.1.80 k8s-node1 <none> <none>
tomcat-deploy-6484688ddc-snszk 1/1 Running 0 13s 10.244.2.76 k8s-node2 <none> <none>
[root@k8s-master multi_svc]# kubectl get svc -n multisvc #查看service资源对象
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx-svc ClusterIP 10.104.213.237 <none> 80/TCP 26s
tomcat-svc ClusterIP 10.103.75.161 <none> 8080/TCP,8009/TCP 26s
[root@k8s-master multi_svc]# kubectl describe svc/nginx-svc -n multisvc #查看service对象nginx-svc的详细信息
Name: nginx-svc
Namespace: multisvc
Labels: app=nginx-svc
Annotations: kubectl.kubernetes.io/last-applied-configuration:
{"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"labels":{"app":"nginx-svc"},"name":"nginx-svc","namespace":"multisvc"},"...
Selector: app=nginx
Type: ClusterIP
IP: 10.104.213.237
Port: http 80/TCP
TargetPort: 80/TCP
Endpoints: 10.244.1.79:80,10.244.2.77:80,10.244.2.78:80
Session Affinity: None
Events: <none>
[root@k8s-master multi_svc]# kubectl describe svc/tomcat-svc -n multisvc #查看service对象tomcat-svc的详细信息
Name: tomcat-svc
Namespace: multisvc
Labels: app=tomcat-svc
Annotations: kubectl.kubernetes.io/last-applied-configuration:
{"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"labels":{"app":"tomcat-svc"},"name":"tomcat-svc","namespace":"multisvc"}...
Selector: app=tomcat
Type: ClusterIP
IP: 10.103.75.161
Port: httpport 8080/TCP
TargetPort: 8080/TCP
Endpoints: 10.244.1.78:8080,10.244.1.80:8080,10.244.2.76:8080
Port: ajpport 8009/TCP
TargetPort: 8009/TCP
Endpoints: 10.244.1.78:8009,10.244.1.80:8009,10.244.2.76:8009
Session Affinity: None
Events: <none>
创建Ingress资源对象
1)编写资源清单文件
[root@k8s-master multi_svc]# vim ingress_host-ms.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: multi-ingress
namespace: multisvc
spec:
rules:
- host: nginx.imyapp.com
http:
paths:
- path: /
backend:
serviceName: nginx-svc
servicePort: 80
- host: tomcat.imyapp.com
http:
paths:
- path: /
backend:
serviceName: tomcat-svc
servicePort: 8080
2)创建上面定义资源对象并查看验证
[root@k8s-master multi_svc]# kubectl apply -f ingress_host-ms.yaml
ingress.extensions/multi-ingress created
[root@k8s-master multi_svc]# kubectl get ingress -n multisvc #查看ingress资源对象
NAME HOSTS ADDRESS PORTS AGE
multi-ingress nginx.imyapp.com,tomcat.imyapp.com 80 18s
[root@k8s-master multi_svc]# kubectl describe ingress/multi-ingress -n multisvc #查看ingress资源multi-ingrsss的详细信息
Name: multi-ingress
Namespace: multisvc
Address:
Default backend: default-http-backend:80 (<none>)
Rules:
Host Path Backends
---- ---- --------
nginx.imyapp.com
/ nginx-svc:80 (10.244.1.79:80,10.244.2.77:80,10.244.2.78:80)
tomcat.imyapp.com
/ tomcat-svc:8080 (10.244.1.78:8080,10.244.1.80:8080,10.244.2.76:8080)
Annotations:
kubectl.kubernetes.io/last-applied-configuration: {"apiVersion":"extensions/v1beta1","kind":"Ingress","metadata":{"annotations":{},"name":"multi-ingress","namespace":"multisvc"},"spec":{"rules":[{"host":"nginx.imyapp.com","http":{"paths":[{"backend":{"serviceName":"nginx-svc","servicePort":80},"path":"/"}]}},{"host":"tomcat.imyapp.com","http":{"paths":[{"backend":{"serviceName":"tomcat-svc","servicePort":8080},"path":"/"}]}}]}}
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal CREATE 39s nginx-ingress-controller Ingress multisvc/multi-ingress
测试访问
这是测试自定义的域名,故需要配置host
192.168.1.31 nginx.imyapp.com tomcat.imyapp.com
192.168.1.32 nginx.imyapp.com tomcat.imyapp.com
192.168.1.33 nginx.imyapp.com tomcat.imyapp.com
查看部署的Ingress的Service对象的端口
[root@k8s-master multi_svc]# kubectl get svc -n ingress-nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ingress-nginx NodePort 10.107.40.182 <none> 80:32699/TCP,443:30842/TCP 6h39m
访问nginx.imyapp.com:32699
访问tomcat.imyapp.com:32699
配置Ingress处理TLS传输
这里使用自签证书,通过OpenSSL进行创建
1)创建证书
#创建nginx.imyapp.com域名的证书
[root@k8s-master multi_svc]# openssl genrsa -out nginx.imyapp.com.key 2048
[root@k8s-master multi_svc]# openssl req -new -x509 -key nginx.imyapp.com.key -out nginx.imyapp.com.crt -subj /C=CN/ST=ShenZhen/L=ShenZhen/O=DevOps/CN=nginx.imyapp.com -days 3650
#创建tomcat.imyapp.com域名的证书
[root@k8s-master multi_svc]# openssl genrsa -out tomcat.imyapp.com.key 2048
[root@k8s-master multi_svc]# openssl req -new -x509 -key tomcat.imyapp.com.key -out tomcat.imyapp.com.crt -subj /C=CN/ST=ShenZhen/L=ShenZhen/O=DevOps/CN=tomcat.imyapp.com -days 3650
#查看生成的证书
[root@k8s-master multi_svc]# ll *.com.*
-rw-r--r-- 1 root root 1298 9月 28 17:23 nginx.imyapp.com.crt
-rw-r--r-- 1 root root 1675 9月 28 17:22 nginx.imyapp.com.key
-rw-r--r-- 1 root root 1302 9月 28 17:24 tomcat.imyapp.com.crt
-rw-r--r-- 1 root root 1679 9月 28 17:24 tomcat.imyapp.com.key
2)创建secrte
#创建nginx域名的secret
[root@k8s-master multi_svc]# kubectl create secret tls nginx-ingress-secret --cert=nginx.imyapp.com.crt --key=nginx.imyapp.com.key -n multisvc
secret/nginx-ingress-secret created
#创建tomcat域名的secret
[root@k8s-master multi_svc]# kubectl create secret tls tomcat-ingress-secret --cert=tomcat.imyapp.com.crt --key=tomcat.imyapp.com.key -n multisvc
secret/tomcat-ingress-secret created
#查看secret
[root@k8s-master multi_svc]# kubectl get secret -n multisvc
NAME TYPE DATA AGE
default-token-mf5wd kubernetes.io/service-account-token 3 5h12m
nginx-ingress-secret kubernetes.io/tls 2 53s
tomcat-ingress-secret kubernetes.io/tls 2 27s
3)编写带TLS的Ingress资源清单(这里通过复制,没有删除上面创建的ingress)
[root@k8s-master multi_svc]# cp ingress_host-ms.yaml ingress_host_https-ms.yaml
[root@k8s-master multi_svc]# vim ingress_host_https-ms.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: multi-ingress-https
namespace: multisvc
annotations:
kubernetes.io/ingress.class: "nginx"
spec:
tls:
- hosts:
- nginx.imyapp.com
secretName: nginx-ingress-secret
- hosts:
- tomcat.imyapp.com
secretName: tomcat-ingress-secret
rules:
- host: nginx.imyapp.com
http:
paths:
- path: /
backend:
serviceName: nginx-svc
servicePort: 80
- host: tomcat.imyapp.com
http:
paths:
- path: /
backend:
serviceName: tomcat-svc
servicePort: 8080
4)创建ingress资源
[root@k8s-master multi_svc]# kubectl apply -f ingress_host_https-ms.yaml
ingress.extensions/multi-ingress-https created
[root@k8s-master multi_svc]# kubectl get ingress -n multisvc
NAME HOSTS ADDRESS PORTS AGE
multi-ingress nginx.imyapp.com,tomcat.imyapp.com 80 44m
multi-ingress-https nginx.imyapp.com,tomcat.imyapp.com 80, 443 3s
5)测试,通过Ingress控制器的前端的Service资源的NodePort来访问此服务,上面看到ingress控制器的service资源的443端口对应的节点的30842端口。
访问nginx
访问tomcat
将不同的服务映射到相同主机的不同路径
在这种情况下,根据请求的URL中的路径,请求将发送到两个不同的服务。因此,客户端可以通过一个IP地址(Ingress 控制器的IP地址)访问两种不同的服务。
注意:这里Ingress中path的定义,需要与后端真实Service提供的Path一致,否则将被转发到一个不存在的path上,引发错误。
Ingress定义示例
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: tomcat-ingress
namespace: multisvc
spec:
rules:
- host: www.imyapp.com
http:
paths:
- path: /nginx
backend:
serviceName: nginx-svc
servicePort: 80
- path: /tomcat
backend:
serviceName: tomcat-svc
servicePort: 8080
使用nginx功能
basic auth :Basic Authentication - NGINX Ingress Controller (kubernetes.github.io)
rewrite:Rewrite - NGINX Ingress Controller (kubernetes.github.io)