Service的地址仅在集群内可以通信,但是我们总需要把我们自己的服务暴露到集群外以供客户端访问,这个时候就需要在集群的边缘添加一层转发机制,来实现集群外部请求流量接入集群的Service资源上。
Kubernetes中的Service共有四种类型:ClusterIP、NodePort、LoadBanlacer和ExternalName。
下面就着重讲解这四种类型的Service。
ClusterIP
通过集群内部IP地址暴露服务,改地址只能够在集群内部访问,无法被集群外部客户端所访问,ClusterIP类型为默认的服务类型。
NodePort
这种类型建立在ClusterIP类型之上,在每个Node节点上的IP和和静态端口暴露服务,因此,它依然会为Service分配集群IP地址,并将地址作为NodePort的路由目标(外部客户端首先会访问Node上的静态端口,然后Node上的静态端口将请求转发至Service的ClusterIP和Port上)。因此,这种类型的Service即可如ClusterIP一样受到集群内部的客户端Pod访问,也可以被集群外部客户端通过 NodeIP:NodePort 访问。

LoadBalancer
这种类型构建在NodePort类型之上,通过云厂商提供的负载均衡器将服务暴露到集群外部,因此LoadBalancer一样具有NodePort和ClusterIP。一个LoadBalancer类型的Service会指向关联至Kubernetes集群外部某个负载均衡设备,该设备通过工作节点之上的NodePort向集群内部发送流量。这种类型的优势在于,能把来自集群外部客户端的请求调度到Node的所有节点或者指定节点的NodePort,而不是依赖于客户端自行决定连接到哪个Node上,从而避免了因客户端指定的节点故障而导致的服务不可用。

ExternalName
这种类型是将Service映射至由ExternalName字段内容指定的主机名来暴露服务,此主机名需要被DNS服务解析至CNAME
类型的记录,换言之,此种类型并非定义由Kubernetes集群提供的服务,而是把集群外部的某服务以DNS CNAME记录的方式映射到集群内部,从而让集群内的Pod资源能够访问外部Service的一种实现方式,因此这种类型没有ClusterIP和NodePort,也没有标签选择器用于选择Pod资源,也不会又Endpoints的概念存在。

NodePort类型使用
如果设置type为“NodePort”,在安装部署Kubernetes集群系统时会预留一个端口范围用于NodePort,默认为30000~32767之间的端口。NodePort类型在使用的时候需要使用spec.type
属性来指定该类型,Cluster类型由于是默认类型,所以不需要指定。
1.创建NodePort类型资源配置清单
cat NodePort.yaml
apiVersion: v1
kind: Service
metadata:
name: service-nodeport
spec:
type: NodePort
selector:
type: nodeport
ports:
- protocol: TCP
port: 80
targetPort: 80
#nodePort: 32345 #这里可以手动指定在Node节点上所监听的端口,但是如果不是为了测试用途,不建议这样做
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deploy
spec:
replicas: 3
selector:
matchLabels:
type: nodeport
template:
metadata:
labels:
type: nodeport
spec:
containers:
- name: nginx-proxy
image: nginx:latest
ports:
- containerPort: 80
name: http
2.创建NodePort类型的Service
kubectl apply -f NodePort.yaml
3.查看资源对象的状态
可以看到如下Service的TYPE已经变为了NodePort类型
kubectl get svc,deploy,pods -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 33d <none>
service/service-nodeport NodePort 10.96.239.254 <none> 80:31417/TCP 9m1s type=nodeport
NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR
deployment.apps/nginx-deploy 3/3 3 3 30s nginx-proxy nginx:latest type=nodeport
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod/nginx-deploy-84bff99d54-27nkv 1/1 Running 0 30s 10.244.5.140 k8s-node03 <none> <none>
pod/nginx-deploy-84bff99d54-2kzvg 1/1 Running 0 30s 10.244.2.98 k8s-node02 <none> <none>
pod/nginx-deploy-84bff99d54-zdf8h 1/1 Running 0 30s 10.244.3.19 k8s-node01 <none> <none>
4.查看Service的详细信息
如下,NodePort监听TCP的31417端口之上,后端Pod为Endpoints字段
kubectl describe svc service-nodeport
Name: service-nodeport
Namespace: default
Labels: <none>
Annotations: Selector: type=nodeport
Type: NodePort
IP: 10.96.239.254
Port: <unset> 80/TCP
TargetPort: 80/TCP
NodePort: <unset> 31417/TCP
Endpoints: 10.244.2.98:80,10.244.3.19:80,10.244.5.140:80
Session Affinity: None
External Traffic Policy: Cluster
Events: <none>
5.测试访问NodePort类型的Service
从集群外部访问任何Node节点的31417端口

从集群内部访问Service的ClusterIP加Port

6.验证NodePort类型从Node节点接收到请求后将请求调度到Service的ClusterIP:Port

动态查看service的日志,如下
kubectl logs -f svc/service-nodeport
然后打开网页去去访问集群Node的31417端口

kubectl get svc/service-nodeport -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
service-nodeport NodePort 10.96.239.254 <none> 80:31417/TCP 21m type=nodeport
LoadBalancer类型使用
NodePort类型的Service虽然能够让集群外部访问到,但外部客户端必须要知道NodePort和集群中至少一个节点的IP及端口地址,且选定节点发生故障时,客户端还得自行选择请求访问其它节点,另外集群节点如果是在线下机房使用私有IP的地址VMware等,那么外部的客户端怎么可能访问的到这类私有地址呢?
因此一般还应该在集群之外创建一个具有公网地址的负载均衡器,由它接入外部客户端的请求并调度至集群节点相应的NodePort上。
云计算环境通常提供了一个LBaaS服务,它允许租户动态在自己网络中创建一个负载均衡器设备。那些部署于此环境之上的Kubernetes集群在创建Service资源时可以直接调用此接口按照需要创建出一个软负载均衡器,而且有这种功能的Service资源即为LoadBalancer类型,不过如果Kubernetes部署在逻金属之上,我们也可以手动部署一个负载均衡器(最好有冗余功能),并配置将其请求流量调度至各节点的NodePort之上。
1.LoadBalancer类型的Service配置清单
cat LoadBalancer.yaml
apiVersion: v1
kind: Service
metadata:
name: loadbalancer-service
spec:
type: LoadBalancer #类型指定为LoadBalancer
loadBalancerIP: 192.168.31.241 #指定负载均衡器地址(可选)
selector:
app: nginx-lb
ports:
- protocol: TCP
port: 80
targetPort: 80
nodePort: 30808 #指定Node所监听的端口,在LoadBalancer类型下,个人建议指定
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deploy
spec:
replicas: 3
selector:
matchLabels:
app: nginx-lb
template:
metadata:
labels:
app: nginx-lb
spec:
containers:
- name: nginx-proxy
image: nginx:latest
ports:
- containerPort: 80
name: http
2.创建Service并查看状态
kubectl apply -f LoadBalancer.yaml
kubectl get svc,deploy,pods -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 33d <none>
service/loadbalancer-service LoadBalancer 10.104.192.216 <pending> 80:30808/TCP 43s app=nginx-lb
NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR
deployment.apps/nginx-deploy 3/3 3 3 43s nginx-proxy nginx:latest app=nginx-lb
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod/nginx-deploy-76cf94c6ff-7ghn7 1/1 Running 0 43s 10.244.2.99 k8s-node02 <none> <none>
pod/nginx-deploy-76cf94c6ff-krpfv 1/1 Running 0 43s 10.244.5.141 k8s-node03 <none> <none>
pod/nginx-deploy-76cf94c6ff-lznpf 1/1 Running 0 43s 10.244.3.20 k8s-node01 <none> <none>
kubectl describe svc loadbalancer-service
Name: loadbalancer-service
Namespace: default
Labels: <none>
Annotations: Selector: app=nginx-lb
Type: LoadBalancer
IP: 10.104.192.216
IP: 192.168.31.241
Port: <unset> 80/TCP
TargetPort: 80/TCP
NodePort: <unset> 30808/TCP
Endpoints: 10.244.2.99:80,10.244.3.20:80,10.244.5.141:80
Session Affinity: None
External Traffic Policy: Cluster
Events: <none>
3.添加均衡器
我这里的负载均衡器就在192.168.31.241上部署一个Nginx来进行对Node上的端口负载均衡进行演示。
192.168.31.241 Nginx配置如下
upstream end-pod {
server 192.168.31.231:30808 max_fails=6 fail_timeout=60s;
server 192.168.31.232:30808 max_fails=6 fail_timeout=60s;
server 192.168.31.233:30808 max_fails=6 fail_timeout=60s;
}
server {
listen 80;
server_name localhost;
location / {
proxy_pass http://end-pod;
}
}
4.测试访问
首先动态显示Service的日志,Service也会记录被调度到后端Pod的日志
kubectl logs -f service/loadbalancer-service
打开浏览器访问192.168.31.241负载均衡器的地址

ExternalName类型使用
ExternalName类型的Service用于将外部的服务器发布到集群中来让Pod中的应用程序访问,因此,Service不需要任何标签选择器来关联Pod对象,但必须要使用spec.externalName
属性定义一个CNAME记录用于返回外部真正提供服务的主机和别名,而后通过CNAME记录值获取到相关主机的IP地址。
kind: Service
apiVersion: v1
metadata:
name: my-service
namespace: default
spec:
type: ExternalName
externalName: k8sops.cn
当访问地址 k8sops.cn.default.svc.cluster.local时,集群的 DNS 服务将返回一个值为 k8sops.cn的 CNAME 记录。访问这个服务的工作方式与其它的相同,唯一不同的是重定向发生在 DNS 层,而且不会进行代理或转发。
除了可以直接通过 externalName 指定外部服务的域名之外,我们还可以通过自定义 Endpoints 来创建 Service,前提是 clusterIP=None,名称要和 Service 保持一致,如下所示:
apiVersion: v1
kind: Service
metadata:
name: etcd-k8s
namespace: kube-system
labels:
k8s-app: etcd
spec:
type: ClusterIP
clusterIP: None
ports:
- name: port
port: 2379
---
apiVersion: v1
kind: Endpoints
metadata:
name: etcd-k8s # 名称必须和 Service 一致
namespace: kube-system
labels:
k8s-app: etcd
subsets:
- addresses:
- ip: 10.151.30.57 # Service 将连接重定向到 endpoint
ports:
- name: port
port: 2379 # endpoint 的目标端口
上面这个服务就是将外部的 etcd 服务引入到 Kubernetes 集群中来。