一、kubernetes服务发布Service
1.1 Service存在的意义
在 kubernetes 中,Pod 是有生命周期的,如果 Pod 重启它的 IP 很有可能会发生变化。如果我们的服务都是将 Pod 的 IP 地址写死,Pod 挂掉或者重启,和刚才重启的 pod 相关联的其他服务将会找不到它所关联的Pod,为了解决这个问题,在 kubernetes 中定义了 Service 资源对象,Service 定义了一个服务访问的入口,客户端通过这个入口即可访问服务背后的应用集群实例,service 是一组 Pod 的逻辑集合,这一组 Pod 能够被 Service 访问到,通常是通过 Label Selector 实现的。
1.2 Service概述
service 是一个固定接入层,客户端可以通过访问 service 的 ip 和端口访问到 service 关联的后端pod,这个 service 工作依赖于在kubernetes 集群之上部署的一个附件,就是 kubernetes 的 dns 服务(不同 kubernetes 版本的 dns 默认使用的也是不一样的,1.11 之前的版本使用的kubeDNs,较新的版本使用的是 coredns),service 的名称解析是依赖于 dns 附件的,因此在部署完 k8s 之后需要再部署 dns 附件,kubernetes 要想给客户端提供网络功能,需要依赖第三方的网络插件(flannel,calico 等)。每个 K8s 节点上都有一个组件叫做 kube-proxy,kube-proxy 这个组件将始终监视着apiserver 中有关 service 资源的变动信息,需要跟 master 之上的 apiserver 交互,随时连接到apiserver 上获取任何一个与 service 资源相关的资源变动状态,这种是通过 kubernetes 中固有的一种请求方法 watch(监视)来实现的,一旦有 service 资源的内容发生变动(如创建,删除),kube-proxy 都会将它转化成当前节点之上的能够实现 service 资源调度,把我们请求调度到后端特定的 pod资源之上的规则,这个规则可能是 iptables,也可能是 ipvs,取决于 service 的实现方式。
userspace代理模式:服务(Service) | Kubernetes
iptables 代理模式:服务(Service) | Kubernetes
ipvs代理模式:服务(Service) | Kubernetes
1.3 Service原理
k8s 在创建 Service 时,会根据标签选择器 selector(lable selector)来查找 Pod,据此创建与Service 同名的 endpoint 对象,当 Pod 地址发生变化时,endpoint 也会随之发生变化,service 接收前端 client 请求的时候,就会通过 endpoint,找到转发到哪个 Pod 进行访问的地址。(至于转发到哪个节点的 Pod,由负载均衡 kube-proxy 决定)
1.4 Service 的四种类型
序号 | Service类型 | 用途及原理 |
---|---|---|
1 | ExternalName | 适用于 k8s 集群内部容器访问外部资源,没有 selector,也没有定义任何的端口和 Endpoint。 应用场景容器之间跨名称空间访问。 |
2 | ClusterIP | 通过 k8s 集群内部 IP 暴露服务,选择该值,服务只能够在集群内部访问,默认的ServiceType。 |
3 | NodePort | 通过每个 Node 节点上的 IP 和静态端口暴露 k8s 集群内部的服务。通过请求 (NodeIP) NodePort)可以把请求代理到内部的 pod。应用场景服务外部发布。Client>NodeIP:NodePort> Service Ip:ServicePort> PodIP:ContainerPort |
4 | LoadBalancer | 使用云提供商的负载局衡器,可以向外部暴露服务。外部的负载均衡器可以路由到 NodePort 服务 和 ClusterIP 服务。应用场景服务外部发布。 |
1.5 创建 Service 服务 type 类型 ClusterIP
创建nginx服务,副本数为2
[root@master01 ~]#vim pod_clusterIp.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-nginx
spec:
selector:
matchLabels:
run: my-nginx
replicas: 2 #副本数
template:
metadata:
labels:
run : my-nginx
spec:
containers:
- name: my-nginx
image: nginx:latest
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80 #pod中容器的端口
查看pod的ip信息
[root@master01 ~]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE
my-nginx-65fc647b5d-27f44 1/1 Running 0 15m 10.244.196.131 node01
my-nginx-65fc647b5d-xpl8b 1/1 Running 0 15m 10.244.196.129 node01
删除一个pod,查看pod的ip信息
[root@master01 ~]# kubectl delete pod my-nginx-65fc647b5d-27f44
pod "my-nginx-65fc647b5d-27f44" deleted
[root@master01 ~]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE
my-nginx-65fc647b5d-h8v85 1/1 Running 0 54s 10.244.196.134 node01 #ip和name都发生了变化
my-nginx-65fc647b5d-xpl8b 1/1 Running 0 25m 10.244.196.129 node01
创建Service为ClusterIP类型的
[root@master01 ~]# vim svc_clusterIp.yaml
apiVersion: v1
kind: Service
metadata:
name: my-nginx
labels:
run: my-nginx
spec:
type: ClusterIP
ports:
- port: 80 #service 的端口,暴露给 k8s 集群内部服务访问
protocol: TCP
targetPort: 80 #pod 容器中定义的端口
selector:
run: my-nginx #选择拥有 run=my-nginx 标签的 pod
$$
查看创建的svc和endpoints资源
[root@master01 ~]# kubectl get svc -l run=my-nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
my-nginx ClusterIP 10.100.165.243 <none> 80/TCP 2m23s
[root@master01 ~]# kubectl get ep my-nginx
NAME ENDPOINTS AGE
my-nginx 10.244.196.129:80,10.244.196.134:80 3m1s
管理节点集群内部访问
[root@master01 ~]# curl 10.100.165.243
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
service 可以对外提供统一固定的 ip 地址,并将请求重定向至集群中的 pod。其中“将请求重定向至集群中的 pod”就是通过 endpoint 与 selector 协同工作实现。selector 是用于选择 pod,由selector 选择出来的 pod 的 ip 地址和端口号,将会被记录在 endpoint 中。endpoint 便记录了所有pod 的 ip 地址和端口号。当一个请求访问到 service 的 ip 地址时,就会从 endpoint 中选择出一个 ip地址和端口号,然后将请求重定向至 pod 中。具体把请求代理到哪个 pod,需要的就是 kube-proxy 的轮询实现的。service 不会直接到 pod,service 是直接到 endpoint 资源,就是地址加端口,再由endpoint 再关联到 pod。
service 只要创建完成,我们就可以直接解析它的服务名,每一个服务创建完成后都会在集群 dns 中动态添加一个资源记录,添加完成后我们就可以解析了,资源记录格式是:
service的域名格式:
- SVC_NAME.NS_NAME.DOMAIN.LTD
- 服务名.命名空间.域名后缀
- 集群默认的域名后缀是 svc.cluster.local
控制器创建的pod的域名格式:
- controller_NAME.SVC_NAME.NS_NAME.DOMAIN.LTD
- 控制器名称.服务名.命名空间.域名后缀
- 进入pod中请求svc的域名
[root@master01 ~]# kubectl exec -it my-nginx-65fc647b5d-h8v85 -- /bin/bash
root@my-nginx-65fc647b5d-h8v85:/#
root@my-nginx-65fc647b5d-h8v85:/# curl my-nginx.default.svc.cluster.local
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
- 解析svc的域名
root@my-nginx-65fc647b5d-h8v85:/# nslookup my-nginx.default.svc.cluster.local
Server: 10.96.0.10
Address: 10.96.0.10#53
Name: my-nginx.default.svc.cluster.local
Address: 10.100.165.243
-
headless services
headless services是一种类型为ClusterIP特殊的service,这种svc是没有IP地址的,也称为无头服务,一般搭配StatefulSet控制器有状态服务的使用。
[root@master01 ~]# vim mysql-service.yaml
apiVersion: v1
kind: Service
metadata:
name: mysql-balance-svc
namespace: mysql-space
labels:
name: mysql-balance-svc
spec:
ports:
port: 3308
protocol: TCP
targetPort: 3306
clusterIP: None #clusterIP为None 即为headless services
selector:
name: mysql-balance-pod
- 查看此类型svc
[root@master01 ~]# kubectl get svc -n mysql-space
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
mysql-balance-svc ClusterIP None <none> 3308/TCP 51s2
1.6 创建 Service 服务 type 类型 NodePort
- 创建pod资源
vim pod_nodePort.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-nginx-nodeport
spec:
selector:
matchLabels:
run: my-nginx-nodeport
replicas: 2 #副本数
template:
metadata:
labels:
run : my-nginx-nodeport
spec:
containers:
- name: my-nginx-nodeport
image: nginx:latest
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80 #pod中容器的端口
- 创建Service的type类型为NodePort
[root@master01 ~]# vim svc_nodePort.yaml
apiVersion: v1
kind: Service
metadata:
name: my-nginx-nodeport
labels:
run: my-nginx-nodeport
spec:
type: NodePort
ports:
port: 80 #service 的端口,暴露给 k8s 集群内部服务访问
protocol: TCP
targetPort: 80 #pod 容器中定义的端口
nodePort: 30080 #宿主机暴露的端口
selector:
run: my-nginx-nodeport #选择拥有 run=my-nginx 标签的 pod
-
查看Service信息
$$
[root@master01 ~]# kubectl get svc -l run=my-nginx-nodeport
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S)
my-nginx-nodeport NodePort 10.98.93.195 <none> 80:30080/TCP
$$
- 访问集群内Service
$$
[root@master01 ~]# curl 10.98.93.195 #集群内访问
<h1>Welcome to nginx!</h1>
root@my-nginx-nodeport-689f6d5464-4qdxs:/# curl 10.98.93.195 #pod中访问
<h1>Welcome to nginx!</h1>
$$
- 访问集群外Service
[root@master01 ~]# curl 192.168.1.11:30080 #集群外访问
<h1>Welcome to nginx!</h1>
服务请求走向:
-
Client-→node ip:30080->service ip:80-→pod ip:container port
-
Client ->192.168.1.10:30080->10.98.93.195:80->10.244.196.132:80
1.7 创建 Service服务type类型ExternalName
应用场景:跨名称空间访问
需求:default 名称空间下的 client 服务想要访问 nginx-ns 名称空间下的 nginx-svc 服务
- 创建client的pod资源
[root@master01 ~]# vim pod_client.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: client
spec:
replicas: 1
selector:
matchLabels:
app: busybox
template:
metadata:
labels:
app: busybox
spec:
containers:
- name: busybox
image: busybox
imagePullPolicy: IfNotPresent
command: ["/bin/sh","-c","sleep 36000"]
- 创建client的svc
[root@master01 ~]# vim client_svc.yaml
apiVersion: v1
kind: Service
metadata:
name: client-svc
spec:
type: ExternalName
externalName: nginx-svc.nginx-ns.svc.cluster.local # 相当于nginx-svc的软链
ports:
name: http
port: 80
targetPort: 80
-
创建server的Pod资源
[root@master01 ~]# vim server_nginx.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
namespace: nginx-ns # nginx-ns的名称空间
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:latest
imagePullPolicy: IfNotPresent
创建server的svc
[root@master01 ~]# vim nginx_svc.yaml
apiVersion: v1
kind: Service
metadata:
name: nginx-svc
namespace: nginx-ns
spec:
selector:
app: nginx
ports:
name: http
protocol: TCP
port: 80
targetPort: 80
-
进入client-pod中访问
[root@master01 ~]# kubectl exec -it client-7d8bfb6fcf-8xmp7 -- /bin/sh
/ # wget -q -O - client-svc.default.svc.cluster.local #访问自己的svc域名
<h1>Welcome to nginx!</h1>
/ # wget -q -O - nginx-svc.nginx-ns.svc.cluster.local #访问nginx-svc的域名
<h1>Welcome to nginx!</h1>
1.8 创建Service服务type类型LoadBalancer
apiVersion: v1
kind: Service
metadata:
name: example-service
spec:
selector:
app: example
ports:
- port: 8765 #svc暴露的端口
targetPort: 9376 #后端代理的pod端口
type: LoadBalancer
1.9 k8s 最佳实践-映射外部服务案例分享
- 在master02节点上安装mysql,映射外部mysql数据库到集群内部,供容器访问
[root@master01 ~]# vim mysql_service.yaml
apiVersion: v1
kind: Service
metadata:
name: mysql
spec:
type: ClusterIP
ports:
port: 3306
-
查看SVC
[root@master01 ~]# kubectl get svc | grep mysql
mysql ClusterIP 10.107.115.192 <none> 3306/TCP
[root@master01 ~]# kubectl describe svc mysql
Name: mysql
Namespace: default
Labels: <none>
Annotations: <none>
Selector: <none>
Type: ClusterIP
IP Families: <none>
IP: 10.107.115.192
IPs: 10.107.115.192
Port: <unset> 3306/TCP
TargetPort: 3306/TCP
Endpoints: <none> #目前没有关联资源
Session Affinity: None
Events: <none>
添加endpoint资源
[root@master01 ~]# vim mysql_endpoint.yaml
apiVersion: v1
kind: Endpoints
metadata:
name: mysql
subsets:
addresses:
ip: 192.168.1.11
ports:
port: 3306
-
再次查看SVC
[root@master01 ~]# kubectl describe svc mysql
Name: mysql
Namespace: default
Labels: <none>
Annotations: <none>
Selector: <none>
Type: ClusterIP
IP Families: <none>
IP: 10.107.115.192
IPs: 10.107.115.192
Port: <unset> 3306/TCP
TargetPort: 3306/TCP
Endpoints: 192.168.1.11:3306 #已经关联到了mysql的资源
Session Affinity: None
Events: <none>