一、四层代理service
k8s中每一个pod都是有生命周期的,当pod重启或者挂掉之后,再次启动pod时,它的ip就会发生变化,那么此时由于ip发生变化,其他所关联的pod将可能无法发现或找到该pod运行的服务,为了解决该问题,就需要定义service资源,service定义了一个服务访问的入口,是一组pod的逻辑集合,这一组pod能够被service访问到,客户端通过这个入口即可访问到该服务背后的应用集群,这种定义方式通常时通过label selector实现的。
创建service资源可以通过如下命令查看字段资源信息:kubectl explain service 如下图所示
1.1 service字段类型
#查看字段类型使用:kubectl explain service.spec.type
#ClusterIP
//默认支持的类型,动态分配的地址,也可以自己在创建的时候指定,创建之后就改不了了
#ExternalName
//适用于k8s的容器访问外部资源,也就是跨命名空间访问,他没有selector,也没有定义任何的端口和Endpoint。
#NodePort
//它可以通过访问物理机的ip+创建service时映射的端口,可以请求访问到service关联的pod
(注意,再次创建时映射的端口需要更改,否则将提示被占用,而service在物理机映射的端口,默认在 30000-32767 之间)
二、使用字段类型创建service
2.1 ClusterIP类型创建service
创建service之前先创建一个deployment的资源用于service关联
2.1.1 创建deployment资源
#创建deployment资源
[root@k8s-master ~]# vim pod-01.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: dep01
spec:
selector:
matchLabels:
a01: a02
replicas: 2
template:
metadata:
labels:
a01: a02
spec:
containers:
- name: mynginx
image: nginx
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
startupProbe: #定义启动探测
periodSeconds: 5
initialDelaySeconds: 20
timeoutSeconds: 10
httpGet:
scheme: HTTP
port: 80
path: /
livenessProbe: #定义存活探测
periodSeconds: 5
initialDelaySeconds: 20
timeoutSeconds: 10
httpGet:
scheme: HTTP
port: 80
path: /
readinessProbe: #定义就绪探测
periodSeconds: 5
initialDelaySeconds: 20
timeoutSeconds: 10
httpGet:
scheme: HTTP
port: 80
path: /
#创建
[root@k8s-master ~]# kubectl apply -f pod-01.yaml
deployment.apps/dep01 created
#查看 (定义了启动探测 20s ,过了20s之后pid运行完毕)
[root@k8s-master ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
dep01-66bb7cbd54-8wlpw 0/1 Running 0 6s
dep01-66bb7cbd54-px5bn 0/1 Running 0 6s
[root@k8s-master ~]# kubectl get pods -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
dep01-66bb7cbd54-8wlpw 1/1 Running 0 2m18s 10.244.194.66 k8s-worker1 <none> <none>
dep01-66bb7cbd54-px5bn 1/1 Running 0 2m18s 10.244.126.1 k8s-worker2 <none> <none>
[root@k8s-master ~]# kubectl get deploy
NAME READY UP-TO-DATE AVAILABLE AGE
dep01 2/2 2 2 105s
#测试同集群内pod之间的访问
[root@k8s-master ~]# kubectl exec -it dep01-66bb7cbd54-8wlpw -- /bin/bash //进入其中一个pod
root@dep01-66bb7cbd54-8wlpw:/# curl 10.244.126.1:80 //访问请求集群内另个pod的80端口(正常)
<title>Welcome to nginx!</title>
#删除一个pod(deployment会再次自动创建一个新的出来)
[root@k8s-master ~]# kubectl delete pods dep01-66bb7cbd54-8wlpw
pod "dep01-66bb7cbd54-8wlpw" deleted
[root@k8s-master ~]# kubectl get pods -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
dep01-66bb7cbd54-9thnm 0/1 Running 0 12s 10.244.194.67 k8s-worker1 <none> <none>
dep01-66bb7cbd54-px5bn 1/1 Running 0 13m 10.244.126.1 k8s-worker2 <none> <none>
#查看标签
[root@k8s-master ~]# kubectl get pods --show-labels
NAME READY STATUS RESTARTS AGE LABELS
dep01-66bb7cbd54-9thnm 1/1 Running 0 8m30s a01=a02,pod-template-hash=66bb7cbd54
dep01-66bb7cbd54-px5bn 1/1 Running 0 21m a01=a02,pod-template-hash=66bb7cbd54
2.1.2 创建service
上方2.1.1 最后删除其中一个pod之后,由于该pod资源由deployment进行管理的,且pod副本数为2,会自动在创建一个,但是新创建的pod ip会发生改变,而此时与该被删除pod的ip进行连接的服务将无法连接,所以需要创建一个service进行关联。
由于上方pod是由deploymen进行管理创建的,定义的标签为“a01: a02”,所以由deploymen控制器创建的pod都具有该标签,
所以创建service时关联该标签即可。
service 的完整名称(域名)
创建service完成之后,就可以直接解析它的服务名,每一个服务创建完成之后都会在集群dns中动态添加几个资源记录,也就是service的域名 ,域名格式 service的名称+所在命名空间+域名后缀,如service.default.svc.cluster.local,集群默认域名后缀为:svc.cluster.local
#创建service
[root@k8s-master ~]# vim service-01.yaml
apiVersion: v1
kind: Service
metadata:
name: service01 #service 的name
labels:
ser01: ser02 #service 的标签
spec:
type: ClusterIP #定义创建service的类型
ports:
- port: 80 #定义servic的端口,与被关联的pod的端口保持一致
protocol: TCP #定义tcp协议 (只有tcp、sctp、udp,四层代理不支持https)
targetPort: 80 #需要与被关联pod里面运行的容器端口保持一致,service找到关联的pod之后,通过targetPort定义的80端口,找到pod中对应启>动80端口的容器。(本次关联pod对象里面的容器是nginx 端口是80)
selector: #标签选择器,定义通过标签关联pod
a01: a02 #填写被关联对象pod的标签
#创建 并查看
[root@k8s-master ~]# kubectl apply -f service-01.yaml
service/service01 created
[root@k8s-master ~]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service01 ClusterIP 10.96.18.233 <none> 80/TCP 7s
#查看service的详细信息
[root@k8s-master ~]# kubectl describe svc service01
Name: service01
Namespace: default
Labels: ser01=ser02
Annotations: <none>
Selector: a01=a02
Type: ClusterIP
IP Family Policy: SingleStack
IP Families: IPv4
IP: 10.96.18.233
IPs: 10.96.18.233
Port: <unset> 80/TCP
TargetPort: 80/TCP
Endpoints: 10.244.126.1:80,10.244.194.67:80 //Endpoints通过标签关联到的pod对象的IP及端口
Session Affinity: None
Events: <none>
#也就是说创建service时,会创建一个跟service同名的Endpoints资源,
#这个资源里面保存了通过创建service时定义的selector关联对象的ip和端口然后将它们保存到Endpoints列表里面,
#而且也会将service的ip和pod的ip加入到防火墙规则里面
#单独查看Endpoint信息
[root@k8s-master ~]# kubectl get ep
NAME ENDPOINTS AGE
service01 10.244.126.1:80,10.244.194.67:80 9m31s
#查看防火墙规则
[root@k8s-master ~]# ipvsadm -Ln | grep -A 2 10.96.18.233 (过滤一下service的ip)
TCP 10.96.18.233:80 rr //表示会将访问10.96.18.233:80的请求转发到下面两个ip上
-> 10.244.126.1:80 Masq 1 0 0
-> 10.244.194.67:80 Masq 1 0 0
#测试访问(PS:k8s集群内都可访问,集群外不行)
[root@k8s-master ~]# curl 10.96.18.233:80
<h1>Welcome to nginx!</h1>
#域名访问验证(集群所在物理机无法访问该域名,需要进入到集群pod中进行访问)
#进入其中一个pod 容器内
[root@k8s-master ~]# kubectl exec -it dep01-66bb7cbd54-9thnm -- /bin/bash
root@dep01-66bb7cbd54-9thnm:/# curl service01.default.svc.cluster.local //请求域名
<h1>Welcome to nginx!</h1>
PS:这种方式就是说其他客户端想要访问这个service代理的pod,不用写service的IP,直接写域名也可以。
2.1.3 测试pod删除service效果
如上方2.1.1所示 删除其中一个pod之后,由于该pod资源由deployment进行管理的,且pod副本数为2,会自动在创建一个,但是新创建的pod ip会发生改变,如下演示关联service代理之后的效果。
#删除前确认当前pod 及 service ip信息
[root@k8s-master ~]# kubectl get pods -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
dep01-66bb7cbd54-9thnm 1/1 Running 0 151m 10.244.194.67 k8s-worker1 <none> <none>
dep01-66bb7cbd54-px5bn 1/1 Running 0 165m 10.244.126.1 k8s-worker2 <none> <none>
[root@k8s-master ~]# kubectl describe svc service01
Name: service01
Namespace: default
Labels: ser01=ser02
Annotations: <none>
Selector: a01=a02
Type: ClusterIP
IP Family Policy: SingleStack
IP Families: IPv4
IP: 10.96.18.233
IPs: 10.96.18.233
Port: <unset> 80/TCP
TargetPort: 80/TCP
Endpoints: 10.244.126.1:80,10.244.194.67:80
Session Affinity: None
Events: <none>
#删除其中一个pod,确认第一个pod ip已发生改变
[root@k8s-master ~]# kubectl delete pods dep01-66bb7cbd54-9thnm
pod "dep01-66bb7cbd54-9thnm" deleted
#由于上方yaml文件中定义了创建pod时需要探测,所以pod被创建出来,还是属于未就绪状态“0/1”
[root@k8s-master ~]# kubectl get pods -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
dep01-66bb7cbd54-n6h98 0/1 Running 0 17s 10.244.194.68 k8s-worker1 <none> <none>
dep01-66bb7cbd54-px5bn 1/1 Running 0 171m 10.244.126.1 k8s-worker2 <none> <none>
#service 服务中的Endpoints 关联的pod也会同步更新,由于pod刚创建正在探测中,所以这里IP只有一个
[root@k8s-master ~]# kubectl describe svc service01 | grep Endpoints
Endpoints: 10.244.126.1:80
#等探测完成之后,查看验证
[root@k8s-master ~]# kubectl get pods -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
dep01-66bb7cbd54-n6h98 1/1 Running 0 4m31s 10.244.194.68 k8s-worker1 <none> <none>
dep01-66bb7cbd54-px5bn 1/1 Running 0 175m 10.244.126.1 k8s-worker2 <none> <none>
#service 服务也同步更新了Endpoints
[root@k8s-master ~]# kubectl describe svc service01 | grep Endpoints
Endpoints: 10.244.126.1:80,10.244.194.68:80
2.2 NodePort类型创建service
ps: 为了保持测试时清晰可观,先將之前创建的pod、service及deploymen刪除掉。
2.2.1 创建deploymen资源
#首先创建一个deployment控制器来管理pod
[root@k8s-master ~]# vim deploy-02.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy-02
spec:
selector:
matchLabels:
aa02: bb02
replicas: 2
template:
metadata:
labels:
aa02: bb02
spec:
containers:
- name: mynginx
image: nginx
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
#创建
[root@k8s-master ~]# kubectl apply -f deplou-02.yaml
deployment.apps/deploy-02 created
#查看
[root@k8s-master ~]# kubectl get deploy
NAME READY UP-TO-DATE AVAILABLE AGE
deploy-02 2/2 2 2 70s
[root@k8s-master ~]# kubectl get pods -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
deploy-02-6db744fc95-6kxwl 1/1 Running 0 55s 10.244.194.66 k8s-worker1 <none> <none>
deploy-02-6db744fc95-vzsbz 1/1 Running 0 55s 10.244.126.1 k8s-worker2 <none> <none>
2.2.2 创建service
创建一个NodePort类型的service资源,给上方的pod做代理
#创建service
[root@k8s-master ~]# vim service-02.yaml
apiVersion: v1
kind: Service
metadata:
name: service02
labels:
aaaa02: bbbb02
spec:
type: NodePort
ports:
- port: 80
protocol: TCP
targetPort: 80
nodePort: 30330 #创建service时在物理机上映射的端口
selector:
aa02: bb02 #匹配到具有该标签的pod
#创建
[root@k8s-master ~]# kubectl apply -f service-02.yaml
service/service02 created
[root@k8s-master ~]# kubectl get svc | grep -Ei "name|service"
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service02 NodePort 10.97.227.88 <none> 80:30330/TCP 5s
#查看关联pod的ip
[root@k8s-master ~]# kubectl describe svc service02 | grep -i endpoints
Endpoints: 10.244.126.1:80,10.244.194.66:80
2.2.3 测试访问pod
#测试请求 service的地址
[root@k8s-master ~]# curl 10.97.227.88:80
<h1>Welcome to nginx!</h1>
#测试访问node节点物理机ip + 映射的端口
[root@k8s-master ~]# curl 192.168.57.132:30330
<title>Welcome to nginx!</title>
#浏览器访问也ok,如下图
#数据转发流量走向: 访问k8s集群控制\工作任意一个节点的物理机ip+映射的端口,都可以访问到该pod,
#请求ip+端口时,请求代理给 docker0 虚拟网卡(172.17.0.1:30330),然后由该网卡转发给它所代理的两个pod的ip上
#查看docker0 网卡ip
[root@k8s-master ~]# ip a | grep docker0
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
#查看docker0网卡 ip 代理的流量走向
[root@k8s-master ~]# ipvsadm -Ln | grep -A 2 172.17.0.1
TCP 172.17.0.1:30330 rr
-> 10.244.126.1:80 Masq 1 0 0
-> 10.244.194.66:80 Masq 1 0 0
2.3 ExternalName类型创建service
ps: 为了保持测试时清晰可观,先將之前创建的pod、service及deploymen刪除掉。
场景:跨名称空间访问
需求:default空间下的client服务想要访问 nginx-01 空间下的 nginx 服务。
2.3.1 创建命名空间及deploymen
#创建命名空间 nginx-01
[root@k8s-master ~]# kubectl create ns nginx-01
namespace/nginx-01 created
[root@k8s-master ~]# kubectl get ns | grep nginx
nginx-01 Active 10s
2.3.2 在nginx-01空间下创建deploymen
#在nginx-01空间下创建deployment
[root@k8s-master ~]# vim deploy-03.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy-03
namespace: nginx-01
spec:
selector:
matchLabels:
aa03: bb03
replicas: 1
template:
metadata:
labels:
aa03: bb03
spec:
containers:
- name: mynginx
image: nginx
imagePullPolicy: IfNotPresent
#创建
[root@k8s-master ~]# kubectl apply -f deploy-03.yaml
deployment.apps/deploy-03 created
#查看nginx-01空间下的pod
[root@k8s-master ~]# kubectl get pods -owide -n nginx-01
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
deploy-03-778f9d4bb4-d2v7v 1/1 Running 0 13s 10.244.126.2 k8s-worker2 <none> <none>
2.3.3 在nginx-01空间下创建service
[root@k8s-master ~]# vim service-03.yaml
apiVersion: v1
kind: Service
metadata:
name: service03
namespace: nginx-01 #指定关联nginx-01空间下的pod
spec:
selector:
aa03: bb03 #指定关联nginx-01空间下具有该标签的pod
ports:
- name: myservice
port: 80
protocol: TCP
targetPort: 80
#创建
[root@k8s-master ~]# kubectl apply -f service-03.yaml
service/service03 created
#查看nginx-01空间下的service
[root@k8s-master ~]# kubectl get svc -n nginx-01
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service03 ClusterIP 10.111.121.83 <none> 80/TCP 22s
#查看该servic的详细信息
[root@k8s-master ~]# kubectl describe svc service03 -n nginx-01
Name: service03
Namespace: nginx-01
Labels: <none>
Annotations: <none>
Selector: aa03=bb03
Type: ClusterIP
IP Family Policy: SingleStack
IP Families: IPv4
IP: 10.111.121.83
IPs: 10.111.121.83
Port: myservice 80/TCP
TargetPort: 80/TCP
Endpoints: 10.244.126.2:80 #这里绑定了nginx-01空间下的pod所在节点的ip
Session Affinity: None
Events: <none>
#到这里相当完成了在nginx-01空间下创建了一个pod及一个
2.3.4 在默认空间下创建deployment
[root@k8s-master ~]# vim deploy-04.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: client
spec:
selector:
matchLabels:
app: busybox
replicas: 1
template:
metadata:
labels:
app: busybox
spec:
containers:
- name: mybusybox
image: busybox
imagePullPolicy: IfNotPresent
command: ["/bin/sh","-c","sleep 36000" ]
#创建并查看
[root@k8s-master ~]# kubectl apply -f deploy-04.yaml
deployment.apps/client created
[root@k8s-master ~]# kubectl get pods -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
client-5844498fcb-9g7pq 1/1 Running 0 2s 10.244.194.67 k8s-worker1 <none> <none>
2.3.4 在默认空间下创建service
[root@k8s-master ~]# vim service-04.yaml
apiVersion: v1
kind: Service
metadata:
name: service-04
spec:
type: ExternalName
externalName: service03.nginx-01.svc.cluster.local #指定关联到nginx-01空间下的servic(这里写nginx-01空间下的service的域名)
ports:
- name: svc04
port: 80
targetPort: 80
#创建
[root@k8s-master ~]# kubectl apply -f service-04.yaml
service/service-04 created
#查看(这里是没有ip的只能看到它所关联的域名)
[root@k8s-master ~]# kubectl get svc | grep -Ei "name|servoce-04"
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service-04 ExternalName <none> service03.nginx-01.svc.cluster.local 80/TCP 35s
#而此时,如果想要在默认空间下的busybox里面访问nginx-01空间下的nginx pod服务,只需要访问默认空间下的service-04就可以了,因为它关联了nginx-01空间下的service域名
2.3.5 验证访问
由默认空间下的busybox pod 访问nginx-01空间下的nginx pod。
#进入到默认空间下的busybox pod中
[root@k8s-master ~]# kubectl exec -it client-5844498fcb-9g7pq -- /bin/sh
/ # wget -q -O - service-04 #小写Q 大写O
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
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>
#或者写访问完整的nginx-01空间下的service域名,效果也是一样
/ # wget -q -O - service03.nginx-01.svc.cluster.local
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
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>
#大白话总结:nginx-01空间下的servcer03 关联了nginx-01空间下的pod,默认空间下的service04关联了nginx-01空间下的service03,
#默认空间下的pod关联了默认空间下的service04
#即:由busybox pod --> service04 --> service03 --> nginx pod
三、自定义Endpoints
3.1映射外部服务
场景:k8s集群引用外部的MariaDB数据库
数据库服务器
IP:192.168.57.133 | 数据库:MariaDB |
3.1.1 安装MariaDB
#找到一台机器安装数据库
yum -y install mariadb-server.x86_64
systemctl restart mariadb
3.1.2 创建service
该servic创建时yaml文件中并没有引用定义标签选择器,也就是说,创建的该service不会关联任何pod,
当然service的endpoins也会为空。
#创建service
[root@k8s-master ~]# vim mysql-service.yaml
apiVersion: v1
kind: Service
metadata:
name: mysql
spec:
type: ClusterIP
ports:
- port: 3306
#创建
[root@k8s-master ~]# kubectl apply -f mysql-service.yaml
service/mysql created
[root@k8s-master ~]# kubectl get svc | grep -Ei "name|mysql"
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
mysql ClusterIP 10.107.176.53 <none> 3306/TCP 22s
#查看endpoints信息(空)
[root@k8s-master ~]# kubectl describe svc mysql
Name: mysql
Namespace: default
Labels: <none>
Annotations: <none>
Selector: <none>
Type: ClusterIP
IP Family Policy: SingleStack
IP Families: IPv4
IP: 10.107.176.53
IPs: 10.107.176.53
Port: <unset> 3306/TCP
TargetPort: 3306/TCP
Endpoints: <none> //空
Session Affinity: None
Events: <none>
3.1.3 创建endpoints 资源关联数据库
创建endpoints时 可以使用帮助命令 kubectl explain ep 查看相关字段信息。
如下图所示:
#创建endpoints的yaml文件
[root@k8s-master ~]# vim mysql-endpoints.yaml
apiVersion: v1
kind: Endpoints
metadata:
name: mysql #注意,这里定义的名称要与servic的名字一样,这样service才能找到对应的endpoints是谁。
subsets:
- addresses:
- ip: 192.168.57.133 #这里定义的就是数据库所在服务器的IP地址
ports:
- port: 3306 #定义连接的数据库端口
#创建
[root@k8s-master ~]# kubectl apply -f mysql-endpoints.yaml
endpoints/mysql created
[root@k8s-master ~]# kubectl get ep | grep -Ei "mysql|name" //(ep是简写,也可以写全称)
NAME ENDPOINTS AGE
mysql 192.168.57.133:3306 27s //这里就能看到endpoints注册的ip资源就是数据库所在的服务器IP和端口
#验证servic的endpoints 是否已经更新并绑定成功
[root@k8s-master ~]# kubectl describe svc mysql
Name: mysql
Namespace: default
Labels: <none>
Annotations: <none>
Selector: <none>
Type: ClusterIP
IP Family Policy: SingleStack
IP Families: IPv4
IP: 10.107.176.53
IPs: 10.107.176.53
Port: <unset> 3306/TCP
TargetPort: 3306/TCP
Endpoints: 192.168.57.133:3306 //已经由“none”更改绑定注册成功
Session Affinity: None
Events: <none>
#这时k8s集群内的机器就可以通过访问 mysql 这个servic 来访问到外部的数据库服务器了。