环境准备
在使用 service 之前,首先利用 Deployment 创建出 3 个 pod,注意要为 pod 设置 app=nginx-pod
的标签
创建deployment.yaml,内容如下
apiVersion: apps/v1
kind: Deployment # 类型为 deployment
metadata:
name: pc-deployment # deployment 的名称
namespace: zouzou
spec:
replicas: 3 # 三个副本
selector: # 标签选择器
matchLabels:
app: nginx-pod
template:
metadata:
labels: # 标签
app: nginx-pod
spec:
containers:
- name: nginx
image: nginx:1.14
ports: # 暴露容器的 80 端口
创建 deployment
[root@dce-10-6-215-215 tmp]# kubectl create -f deployment.yaml
查看 pod 的详情
[root@dce-10-6-215-215 tmp]# kubectl get pod -n zouzou -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx3-c5d7c9466-vnt9c 1/1 Running 0 24h 172.29.34.135 dce-10-6-215-200 <none> <none>
pc-deployment-5bff7844cb-6n65j 1/1 Running 0 51s 172.29.190.159 dce-10-6-215-190 <none> <none>
pc-deployment-5bff7844cb-vnvhs 1/1 Running 0 51s 172.29.34.223 dce-10-6-215-200 <none> <none>
pc-deployment-5bff7844cb-z5ks7 1/1 Running 0 51s 172.29.34.255 dce-10-6-215-200 <none> <none>
为了方便后面的测试,修改下三台 pod 里 nginx 的 index.html 页面
# 使用下面命令依次更改,注意要换成自己的 pod 名称
kubectl exec -it pc-deployment-5bff7844cb-vnvhs -n zouzou /bin/sh
echo "我是 172.29.34.223 的 pod"
修改完成之后,我们测试一下,看有没有修改成功
ClusterIP 类型的 Service
默认的 Service 类型就是 ClusterIP,它只能在集群内部访问
创建 service-clusterip.yaml 文件
apiVersion: v1
kind: Service # 类型为 Service
metadata:
name: service-clusterip # Service 的名称
namespace: zouzou
spec:
selector: # 标签选择器,会和上面创建的 deployment.yaml 的 pod 关联起来
app: nginx-pod
clusterIP: 172.31.88.88 # service 的 ip 地址,如果不写,默认会生成一个,注意这个地址不能写,有个范围的,我的是 172.31.0.0/16
type: ClusterIP # 类型为 Service 的 ClusterIP
ports:
- port: 8080 # Service 端口,自定义
targetPort: 80 # pod 端口,不写默认是 80
注意:上面的 service-clusterip.yaml 只是创建一个 service,因为里面的选择器为 app:nginx-pod,所以会和 deployment.yaml 里的 pod 关联起来(因为 deployment.yaml 里的标签也是 app:nginx-pod)
创建 service
# 创建 service
[root@dce-10-6-215-215 tmp]# kubectl create -f service-clusterip.yaml
查看 service
# 可以看到 CLUSTER-IP 就是我们设置的 IP 地址,端口是我们写的 8080,svc 是 service 的简写
[root@dce-10-6-215-215 tmp]# kubectl get svc -n zouzou -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
service-clusterip ClusterIP 172.31.88.88 <none> 8080/TCP 2m6s app=nginx-pod
查看 service 的详细信息,可以看到我们的 Selector 选择的是pod 的标签为 app=nginx-pod 的 pod。还有个参数是 Session Affinity(Session 亲和度),这里为 None,后面在说
还有一个很重要的是 Endpoints,里面有三个 ip 地址,意思就是这个 service 找到了三个 pod,当访问 service 的时候,会转发到这三个里面的其中一个上,其实这三个就是我们上面使用 deployment.yaml 创建的三个 pod
# 查看 service 的详细信息
[root@dce-10-6-215-215 tmp]# kubectl describe svc service-clusterip -n zouzou
Name: service-clusterip
Namespace: zouzou
Labels: <none>
Annotations: <none>
Selector: app=nginx-pod
Type: ClusterIP
IP: 172.31.88.88
IPFamily: IPv4
Port: <unset> 8080/TCP
TargetPort: 80/TCP
Endpoints: 172.29.190.159:80,172.29.34.223:80,172.29.34.255:80 # Endpoints 列表,里面就是当前 service 可以负载到的服务入口
Session Affinity: None # session 亲和性
查看 ipvs 的映射规则
我的有好多,这里找到 service 的 ip 对应的 ip。从下面可以看到,当我们访问 172.31.88.88:8080 的时候,会采用轮询的方式访问下面的三个 ip(也就是我们的 pod),rr 是轮询的意思
[root@dce-10-6-215-215 tmp]# ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 127.0.0.1:30001 rr
-> 172.28.116.206:80 Masq 1 0 0
TCP 172.31.88.88:8080 rr # 主要看这个和下面的三个
-> 172.29.34.223:80 Masq 1 0 0
-> 172.29.34.255:80 Masq 1 0 0
-> 172.29.190.159:80 Masq 1 0 0
TCP 172.31.122.40:80 rr
-> 172.28.116.205:80 Masq 1 0 0
循环访问下我们的 service 地址,看下是不是采用轮询的方式
从下面的结果可以看出来,确实是采用轮询的方式来访问了我们的 pod
使用域名进行访问
Service域名格式:$(service name).$(namespace).svc.cluster.local
,其中 cluster.local 为指定的集群的域名
例如上面的就可以写成
service-clusterip.zouzou.svc.cluster.local
Endpoints
Endpoint 是 kubernetes 中的一个资源对象,存储在 etcd 中,用来记录一个 service 对应的所有 pod 的访问地址,它是根据 service 配置文件中 selector 描述产生的。
一个 Service 由一组 Pod 组成,这些 Pod 通过 Endpoints 暴露出来,Endpoints 是实现实际服务的端点集合。换句话说,service 和 pod 之间的联系是通过 endpoints 实现的。
我们可以通过下面的命令查看 Endpoint
# 会返回所有的,因为我这里只有一个 service,所以只有一个
[root@dce-10-6-215-215 tmp]# kubectl get endpoints -n zouzou -o wide
NAME ENDPOINTS AGE
service-clusterip 172.29.190.159:80,172.29.34.223:80,172.29.34.255:80 20m
负载分发策略
对 Service 的访问被分发到了后端的 Pod 上去,目前 kubernetes 提供了两种负载分发策略:
- 如果不定义,默认使用 kube-proxy 的策略,比如随机、轮询
- 基于客户端地址的会话保持模式,即来自同一个客户端发起的所有请求都会转发到固定的一个 Pod 上
此模式可以使在 spec 中添加 sessionAffinity: ClientIP
选项
修改 service-clusterip.yaml ,加上 sessionAffinity: ClientIP
apiVersion: v1
kind: Service # 类型为 Service
metadata:
name: service-clusterip # Service 的名称
namespace: zouzou
spec:
sessionAffinity: ClientIP # 来自同一个客户端发起的所有请求都会转发到固定的一个 Pod 上
selector: # 标签选择器,会和上面创建的 deployment.yaml 的 pod 关联起来
app: nginx-pod
clusterIP: 172.31.88.88 # service 的 ip 地址,如果不写,默认会生成一个
type: ClusterIP # 类型为 Service 的 ClusterIP
ports:
- port: 8080 # Service 端口,自定义
targetPort: 80 # pod 端口
在 apply 一下
# 也可以删除 service,在创建
[root@dce-10-6-215-215 tmp]# kubectl apply -f service-clusterip.yaml
在来查看下 ipvs 的映射规则,可以看到在我们的 service 后面加了个 persistent,表示持久的,即某一个客户端发起的请求都会分给一个 pod 上
[root@dce-10-6-215-215 tmp]# ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 127.0.0.1:30001 rr
-> 172.28.116.206:80 Masq 1 0 0
TCP 172.31.88.88:8080 rr persistent 10800
-> 172.29.34.223:80 Masq 1 0 0
-> 172.29.34.255:80 Masq 1 0 0
-> 172.29.190.159:80 Masq 1 0 0
TCP 172.31.122.40:80 rr
-> 172.28.116.205:80 Masq 1 0 0
在循环访问下 service,看能不能分给一个固定的 pod
[root@dce-10-6-215-215 tmp]# while true;do curl 172.31.88.88:8080; sleep 5; done;
我是 172.29.34.223 的 pod
我是 172.29.34.223 的 pod
我是 172.29.34.223 的 pod
我是 172.29.34.223 的 pod
我是 172.29.34.223 的 pod
我是 172.29.34.223 的 pod
我是 172.29.34.223 的 pod
我是 172.29.34.223 的 pod
我是 172.29.34.223 的 pod
我是 172.29.34.223 的 pod
我是 172.29.34.223 的 pod
我是 172.29.34.223 的 pod
在用我们的 node 节点访问
[root@dce-10-6-215-200 ~]# while true;do curl 172.31.88.88:8080; sleep 5; done;
我是 172.29.34.255 的 pod
我是 172.29.34.255 的 pod
我是 172.29.34.255 的 pod
我是 172.29.34.255 的 pod
我是 172.29.34.255 的 pod
我是 172.29.34.255 的 pod
我是 172.29.34.255 的 pod
我是 172.29.34.255 的 pod
我是 172.29.34.255 的 pod
我是 172.29.34.255 的 pod
我是 172.29.34.255 的 pod
我是 172.29.34.255 的 pod
我是 172.29.34.255 的 pod
从上面的结果可以看出,当我们设置了 sessionAffinity: ClientIP 后,当某个客户端访问 service 的时候,会将这个客户端的请求分给一个固定的 pod
删除 service
通过 yaml 文件删除
kubectl delete -f service-clusterip.yaml
通过 service 的名称删除
kubectl delete svc service-clusterip -n zouzou