Service
Kubernetes
集群为我们提供了这样的一个对象 Service
,Service
是一种抽象的对象,它定义了一组Pod
的逻辑集合和一个用于访问它们的策略,其实这个概念和微服务非常类似。一个Serivce
下面包含的Pod
集合一般是由Label Selector
来决定的。
环境
192.168.48.101 master01
192.168.48.201 node01
192.168.48.202 node0
三种IP
在继续往下学习Service
之前,我们需要先弄明白Kubernetes
系统中的三种IP这个问题,因为经常有同学混乱。
- Node IP:
Node
节点的IP
地址 - Pod IP:
Pod
的IP地址 - Cluster IP:
Service
的IP
地址
首先,Node IP
是Kubernetes
集群中节点的物理网卡IP
地址(一般为内网),所有属于这个网络的服务器之间都可以直接通信,所以Kubernetes
集群外要想访问Kubernetes
集群内部的某个节点或者服务,肯定得通过Node IP
进行通信(这个时候一般是通过外网IP
了)
然后Pod IP
是每个Pod
的IP
地址,它是Docker Engine
根据docker0
网桥的IP
地址段进行分配的(我们这里使用的是flannel
这种网络插件保证所有节点的Pod IP
不会冲突)
最后Cluster IP
是一个虚拟的IP
,仅仅作用于Kubernetes Service
这个对象,由Kubernetes
自己来进行管理和分配地址,当然我们也无法ping
这个地址,他没有一个真正的实体对象来响应,他只能结合Service Port
来组成一个可以通信的服务。
定义Service
先创建一个deployment
vim myapp-deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp-deploy
namespace: default
labels:
app: myapp
type: deploy
spec:
replicas: 2
selector:
matchLabels:
app: myapp
type: deploy
template:
metadata:
name: myapp-pod
labels:
app: myapp
type: deploy
spec:
containers:
- name: myapp
image: ikubernetes/myapp:v1
ports:
- name: http
containerPort: 80
[root@master01 deploy_yaml]# kubectl apply -f myapp-deploy.yaml
deployment.apps/myapp-deploy created
[root@master01 deploy_yaml]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
myapp-deploy-8675c97685-6br95 1/1 Running 0 21s 10.244.1.51 node01 <none> <none>
myapp-deploy-8675c97685-d9jh4 1/1 Running 0 21s 10.244.2.42 node02 <none> <none>
直接访问pod试试
[root@master01 deploy_yaml]# curl 10.244.1.51
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
[root@master01 deploy_yaml]# curl 10.244.2.42
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
[root@master01 deploy_yaml]#
创建service
Cluster IP
vim myapp-svc.yaml
apiVersion: v1
kind: Service
metadata:
name: myapp-svc
namespace: default
labels:
type: svc
app: myapp
spec:
selector:
app: myapp
type: deploy
type: ClusterIP
ports:
- port: 80
targetPort: 80
protocol: TCP
name: http
[root@master01 svc_yaml]# kubectl apply -f myapp-svc.yaml
service/myapp-svc created
[root@master01 svc_yaml]# kubectl get svc -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 10d <none>
myapp-svc ClusterIP 10.102.65.215 <none> 80/TCP 39s app=myapp,type=deploy
Service
能够将一个接收端口映射到任意的targetPort
。 默认情况下,targetPort
将被设置为与port
字段相同的值。 Service
能够支持 TCP 和 UDP 协议,默认是 TCP 协议。
测试试试
[root@master01 svc_yaml]# curl 10.102.65.215
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
NodePort
vim myapp-svc.yaml
apiVersion: v1
kind: Service
metadata:
name: myapp-svc
namespace: default
labels:
type: svc
app: myapp
spec:
selector:
app: myapp
type: deploy
type: NodePort
ports:
- port: 80
targetPort: 80
nodePort: 30080
protocol: TCP
name: http
[root@master01 svc_yaml]# kubectl apply -f myapp-svc.yaml
service/myapp-svc configured
[root@master01 svc_yaml]# kubectl get svc -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 10d <none>
myapp-svc NodePort 10.102.65.215 <none> 80:30080/TCP 6m25s app=myapp,type=deploy
测试
[root@master01 svc_yaml]# curl 10.102.65.215
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
[root@master01 svc_yaml]# curl 192.168.48.200:30080
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
[root@master01 svc_yaml]# curl 192.168.48.201:30080
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
[root@master01 svc_yaml]# curl 192.168.48.202:30080
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
默认调度是轮训
[root@master01 svc_yaml]# while true;do curl 192.168.48.200:30080/hostname.html;sleep 1;done
myapp-deploy-8675c97685-6br95
myapp-deploy-8675c97685-d9jh4
myapp-deploy-8675c97685-d9jh4
myapp-deploy-8675c97685-6br95
myapp-deploy-8675c97685-d9jh4
myapp-deploy-8675c97685-6br95
myapp-deploy-8675c97685-d9jh4
sessionAffinity: ClientIP
也可以pod绑定
vim myapp-svc.yaml
apiVersion: v1
kind: Service
metadata:
name: myapp-svc
namespace: default
labels:
type: svc
app: myapp
spec:
selector:
app: myapp
type: deploy
type: NodePort
sessionAffinity: ClientIP
ports:
- port: 80
targetPort: 80
nodePort: 30080
protocol: TCP
name: http
[root@master01 svc_yaml]# kubectl apply -f myapp-svc.yaml
service/myapp-svc configured
[root@master01 svc_yaml]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 10d
myapp-svc NodePort 10.102.65.215 <none> 80:30080/TCP 16m
测试试试
[root@master01 svc_yaml]# while true;do curl 192.168.48.200:30080/hostname.html;sleep 1;done
myapp-deploy-8675c97685-d9jh4
myapp-deploy-8675c97685-d9jh4
myapp-deploy-8675c97685-d9jh4
myapp-deploy-8675c97685-d9jh4
myapp-deploy-8675c97685-d9jh4
无头service
vim myapp-svc.yaml
apiVersion: v1
kind: Service
metadata:
name: myapp-svc
namespace: default
labels:
type: svc
app: myapp
spec:
selector:
app: myapp
type: deploy
clusterIP: None
ports:
- port: 80
targetPort: 80
protocol: TCP
name: http
[root@master01 svc_yaml]# kubectl apply -f myapp-svc.yaml
service/myapp-svc created
[root@master01 svc_yaml]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 10d
myapp-svc ClusterIP None <none> 80/TCP 28s
解析测试下
[root@master01 svc_yaml]# yum install bind-utils -y
[root@master01 svc_yaml]# kubectl get svc -n kube-system
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
heapster ClusterIP 10.108.106.25 <none> 80/TCP 2d
kube-dns ClusterIP 10.96.0.10 <none> 53/UDP,53/TCP,9153/TCP 10d
kubernetes-dashboard NodePort 10.103.36.52 <none> 443:30001/TCP 2d2h
metrics-server ClusterIP 10.100.95.210 <none> 443/TCP 2d1h
[root@master01 svc_yaml]# dig -t A myapp-svc.default.svc.cluster.local @10.96.0.10
; <<>> DiG 9.9.4-RedHat-9.9.4-73.el7_6 <<>> -t A myapp-svc.default.svc.cluster.local @10.96.0.10
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 55803
;; flags: qr aa rd; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;myapp-svc.default.svc.cluster.local. IN A
;; ANSWER SECTION:
myapp-svc.default.svc.cluster.local. 5 IN A 10.244.2.42
myapp-svc.default.svc.cluster.local. 5 IN A 10.244.1.51
;; Query time: 0 msec
;; SERVER: 10.96.0.10#53(10.96.0.10)
;; WHEN: Wed Apr 10 21:51:25 CST 2019
;; MSG SIZE rcvd: 166
ExternalName
ExternalName
是 Service 的特例,它没有 selector,也没有定义任何的端口和 Endpoint。 对于运行在集群外部的服务,它通过返回该外部服务的别名这种方式来提供服务。
kind: Service
apiVersion: v1
metadata:
name: my-service
namespace: prod
spec:
type: ExternalName
externalName: my.database.example.com
当查询主机 my-service.prod.svc.cluster.local (后面服务发现的时候我们会再深入讲解)时,集群的 DNS 服务将返回一个值为 my.database.example.com 的 CNAME 记录。 访问这个服务的工作方式与其它的相同,唯一不同的是重定向发生在 DNS 层,而且不会进行代理或转发。