service资源
一个service刻意看作一组提供相同服务的Pod的对外的访问接口;
service作用于哪些pod时通过标签选择器来定义的;
service强依赖dns解析服务:coredns,kube-dns(k8s1.1以前版本)
https://cloud.tencent.com/developer/article/1718429
service工作模式:userspace,iptables,ipvs
userspace:kubernetes 1.1-
iptables:kubernetes 1.10-
ipvs:kubernetes1.11+
userspace代理模式
这种模式,kube-proxy 会监视 Kubernetes master 对 Service 对象和 Endpoints 对象的添加和移除。 对每个 Service,它会在本地 Node 上打开一个端口(随机选择)。 任何连接到“代理端口”的请求,都会被代理到 Service 的backend Pods 中的某个上面(如 Endpoints 所报告的一样)。 使用哪个 backend Pod,是 kube-proxy 基于 SessionAffinity 来确定的。
最后,它配置 iptables 规则,捕获到达该 Service 的 clusterIP(是虚拟 IP)和 Port 的请求,并重定向到代理端口,代理端口再代理请求到 backend Pod。
默认情况下,userspace模式下的kube-proxy通过循环算法选择后端。
默认的策略是,通过 round-robin 算法来选择 backend Pod。
iptables 代理模式
这种模式,kube-proxy 会监视Kubernetes控制节点对Service对象和Endpoints对象的添加和移除。对每个Service,它会配置iptables规则,从而捕获到达该Service的clusterIP和端口的请求,进而将请求重定向到Service的一组backend中的某个上面。对于每个Endpoints对象,它也会配置 iptables 规则,这个规则会选择一个backend组合。
默认的策略是,kube-proxy在iptables模式下随机选择一个backend。
使用 iptables 处理流量具有较低的系统开销,因为流量由Linux netfilter 处理,而无需在用户空间和内核空间之间切换。 这种方法也可能更可靠。
如果kube-proxy在 iptables模式下运行,并且所选的第一个 Pod 没有响应,则连接失败。 这与userspace模式不同:在这种情况下,kube-proxy 将检测到与第一个 Pod 的连接已失败,并会自动使用其他后端Pod重试。
我们可以使用 Pod readiness 探测器验证后端 Pod 是否可以正常工作,以便 iptables 模式下的 kube-proxy 仅看到测试正常的后端。这样做意味着可以避免将流量通过 kube-proxy 发送到已知已失败的Pod。
IPVS 代理模式
在 ipvs 模式下,kube-proxy监视Kubernetes服务(Service)和端点(Endpoints),调用 netlink 接口相应地创建 IPVS 规则, 并定期将 IPVS 规则与 Kubernetes服务(Service)和端点(Endpoints)同步。该控制循环可确保 IPVS 状态与所需状态匹配。访问服务(Service)时,IPVS 将流量定向到后端Pod之一。
IPVS代理模式基于类似于 iptables 模式的 netfilter 挂钩函数,但是使用哈希表作为基础数据结构,并且在内核空间中工作。 这意味着,与 iptables 模式下的 kube-proxy 相比,IPVS 模式下的 kube-proxy 重定向通信的延迟要短,并且在同步代理规则时具有更好的性能。与其他代理模式相比,IPVS 模式还支持更高的网络流量吞吐量。
IPVS提供了更多选项来平衡后端Pod的流量。这些是:
rr: round-robin
lc: least connection (smallest number of open connections)
dh: destination hashing
sh: source hashing
sed: shortest expected delay
nq: never queue
注意:要在 IPVS 模式下运行 kube-proxy,必须在启动 kube-proxy 之前使 IPVS Linux 在节点上可用。 当 kube-proxy 以 IPVS 代理模式启动时,它将验证 IPVS 内核模块是否可用。 如果未检测到 IPVS 内核模块,则 kube-proxy 将退回到以 iptables 代理模式运行。
service服务类型:
ClusterIP:默认类型,自动分配一个仅Cluster内部可以访问的虚拟IP
NodePort:通过每个 Node 上的 IP 和静态端口(NodePort)暴露服务。以ClusterIP为基础,NodePort 服务会路由到 ClusterIP 服务。通过请求 <NodeIP>:<NodePort>,可以从集群的外部访问一个集群内部的 NodePort 服务。
LoadBalancer:使用云提供商的负载均衡器,可以向外部暴露服务。外部的负载均衡器可以路由到 NodePort 服务和 ClusterIP 服务。
ExternalName:通过返回 CNAME 和它的值,可以将服务映射到 externalName 字段的内容(例如,foo.bar.example.com)。没有任何类型代理被创建。
headless:
需要注意的是:Service 能够将一个接收port映射到任意的targetPort。默认情况下,targetPort将被设置为与port字段相同的值。
Service域名格式:(servicename).(namespace).svc.cluster.local,其中cluster.local为指定的集群的域名
service主要包含:apiVsersion,kind,metadata,spec几个字段
kubectl explain svc
spec.ports字段:
nodePort:节点上面的端口,该端口一定不能被占用
port:service的端口,提供该服务的端口
targetPort:容器上的端口
命令式:
kubectl expose deployment goweb --name=gowebsvc --port=80 --target-port=8000
声名式:yaml
资源记录:
SERVIC_NAME.NAMESPACE_NAME.DOMAIN>LTD
svc.cluster.local. 如:redis.default.svc.cluster.local.
service -->endpoint-->pod
ClusterIP类型:
# vi redis-svc.yaml
apiVersion: v1
kind: Service
metadata:
name: redis
namespace: default
spec:
selector:
app: redis
role: logstor
clusterIP: 10.97.97.97
type: ClusterIP
ports:
- port: 6397
targetPort: 6397
# kubectl apply -f redis-svc.yaml
# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 28h
redis ClusterIP 10.97.97.97 <none> 6397/TCP 8s
# kubectl describe svc redis
Name: redis
Namespace: default
Labels: <none>
Annotations: kubectl.kubernetes.io/last-applied-configuration:
{"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"name":"redis","namespace":"default"},"spec":{"clusterIP":"10.97.97.97","...
Selector: app=redis,role=logstor
Type: ClusterIP
IP: 10.97.97.97
Port: <unset> 6397/TCP
TargetPort: 6397/TCP
Endpoints: <none>
Session Affinity: None
Events: <none>
NodePort类型:
# vi myapp-np-svc.yaml
apiVersion: v1
kind: Service
metadata:
name: myapp-np
namespace: default
spec:
selector:
app: myapp-np
release: canary
clusterIP: 10.98.98.98
type: NodePort
ports:
- port: 800
targetPort: 800
nodePort: 30800
# kubectl apply -f myapp-np-svc.yaml
# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 28h
myapp-np NodePort 10.98.98.98 <none> 800:30800/TCP 32m
# kubectl describe svc myapp-np
Name: myapp-np
Namespace: default
Labels: <none>
Annotations: kubectl.kubernetes.io/last-applied-configuration:
{"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"name":"myapp-np","namespace":"default"},"spec":{"clusterIP":"10.98.98.98...
Selector: app=myapp-np,release=canary
Type: NodePort
IP: 10.98.98.98
Port: <unset> 800/TCP
TargetPort: 800/TCP
NodePort: <unset> 30800/TCP
Endpoints: <none>
Session Affinity: None
External Traffic Policy: Cluster
Events: <none>
# curl 192.168.31.11:30800/hostname.html #轮询
# kubectl path svc myapp-np -p '{"spec":{"sessionAffinity":"ClientIP"}}' #打补丁直接生效,会话保持
# curl 192.168.31.11:30800/hostname.html
# kubectl get svc -n kube-system
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kube-dns ClusterIP 10.96.0.10 <none> 53/UDP,53/TCP,9153/TCP 28h
查看解析:
# dig -t A myapp-np.default.svc.cluster.local. @10.96.0.10
; <<>> DiG 9.9.4-RedHat-9.9.4-50.el7 <<>> -t A myapp-np.default.svc.cluster.local. @10.96.0.10
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 9942
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;myapp-np.default.svc.cluster.local. IN A
;; ANSWER SECTION:
myapp-np.default.svc.cluster.local. 5 IN A 10.98.98.98
;; Query time: 41 msec
;; SERVER: 10.96.0.10#53(10.96.0.10)
;; WHEN: Sun Apr 07 00:22:36 CST 2019
;; MSG SIZE rcvd: 113
------------------------
Deployment的yaml信息
cat myapp-deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp-deploy
namespace: default
spec:
replicas: 3
selector:
matchLabels:
app: myapp
release: v1
template:
metadata:
labels:
app: myapp
release: v1
env: test
spec:
containers:
- name: myapp
image: registry.cn-beijing.aliyuncs.com/google_registry/myapp:v1
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 80
# kubectl apply -f myapp-deploy.yaml
# kubectl get pod -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
myapp-deploy-577fbb58c6-j45bg 1/1 Running 0 35s 10.244.1.30 vm2 <none> <none>
myapp-deploy-577fbb58c6-pdcpr 1/1 Running 0 35s 10.244.2.3 vm3 <none> <none>
myapp-deploy-577fbb58c6-wb45k 1/1 Running 0 35s 10.244.2.2 vm3 <none> <none>
[root@vm1 manifest]# kubectl get rs
NAME DESIRED CURRENT READY AGE
myapp-deploy-577fbb58c6 3 3 3 38s
# curl 10.244.1.30
# curl 10.244.1.30/hostname.html
ClusterIP类型示例
yaml文件
# cat myapp-svc-ClusterIP.yaml
apiVersion: v1
kind: Service
metadata:
name: myapp-clusterip
namespace: default
spec:
type: ClusterIP # 可以不写,为默认类型
selector:
app: myapp
release: v1
ports:
- name: http
port: 80
targetPort: 80
# kubectl apply -f myapp-svc-ClusterIP.yaml
# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.1.0.1 <none> 443/TCP 42d
myapp-clusterip ClusterIP 10.1.186.57 <none> 80/TCP 5s
# curl 10.1.186.57
# curl 10.1.186.57/hostname.html
Headless Services
有时不需要或不想要负载均衡,以及单独的Service IP。遇到这种情况,可以通过指定 Cluster IP(spec.clusterIP)的值为 “None” 来创建 Headless Service。
这对headless Service并不会分配 Cluster IP,kube-proxy不会处理它们,而且平台也不会为它们进行负载均衡和路由。
使用场景:
第一种:自主选择权,有时候client想自己来决定使用哪个Real Server,可以通过查询DNS来获取Real Server的信息。
第二种:Headless Services还有一个用处(PS:也就是我们需要的那个特性)。Headless Service对应的每一个Endpoints,即每一个Pod,都会有对应的DNS域名;这样Pod之间就可以互相访问。【结合statefulset有状态服务使用,如Web、MySQL集群】
# cat myapp-svc-headless.yaml
apiVersion: v1
kind: Service
metadata:
name: myapp-headless
namespace: default
spec:
selector:
app: myapp
release: v1
clusterIP: "None"
ports:
- port: 80
targetPort: 80
# kubectl apply -f myapp-svc-headless.yaml
# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.1.0.1 <none> 443/TCP 42d
myapp-headless ClusterIP None <none> 80/TCP 16s
# kubectl describe svc myapp-headless
Name: myapp-headless
Namespace: default
Labels: <none>
Annotations: <none>
Selector: app=myapp,release=v1
Type: ClusterIP
IP Families: <none>
IP: None
IPs: <none>
Port: <unset> 80/TCP
TargetPort: 80/TCP
Endpoints: 10.244.1.30:80,10.244.2.2:80,10.244.2.3:80
Session Affinity: None
Events: <none>
service只要创建成功就会写入到coredns。得到coredns IP的命令如下:
kubectl get pod -o wide -A | grep 'coredns'
kube-system coredns-7ff77c879f-5w99f 0/1 running 9 28m 10.244.0.6 vm1 <none> <none>
kube-system coredns-7ff77c879f-f5vtn 0/1 running 249 28h 10.244.1.26 vm2 <none> <none>
coredns记录信息如下
# 其中 10.244.0.61 为 coredns IP
# myapp-headless.default.svc.cluster.local 为Headless Service域名。格式为:$(service name).$(namespace).svc.cluster.local,其中 cluster.local 指定的集群的域名
# nslookup myapp-headless.default.svc.cluster.local 10.244.0.61
或
# dig -t A myapp-headless.default.svc.cluster.local. @10.244.0.61
NodePort类型示例
如果将 type 字段设置为 NodePort,则 Kubernetes 控制层面将在 --service-node-port-range 标志指定的范围内分配端口(默认值:30000-32767)。
# cat myapp-svc-NodePort.yaml
apiVersion: v1
kind: Service
metadata:
name: myapp-nodeport
namespace: default
spec:
type: NodePort
selector:
app: myapp
release: v1
ports:
- name: http
# 默认情况下,为了方便起见,`targetPort` 被设置为与 `port` 字段相同的值。
port: 80 # Service对外提供服务端口
targetPort: 80 # 请求转发后端Pod使用的端口
nodePort: 31682 # 可选字段,默认情况下,为了方便起见,Kubernetes 控制层面会从某个范围内分配一个端口号(默认:30000-32767)
# kubectl apply -f myapp-svc-NodePort.yaml
# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.1.0.1 <none> 443/TCP 42d
myapp-nodeport NodePort 10.1.122.197 <none> 80:31682/TCP 15s
端口查看,可见在本地宿主机监听了相应的端口(备注:集群所有机器都监听了该端口)
tcp 0 0 0.0.0.0:31682 0.0.0.0:* LISTEN 2534/kube-proxy
curl通过节点IP访问
# curl 192.168.1.85:31682
# curl 192.168.1.85:31682/hostname.html
ExternalName类型示例
这种类型的Service通过返回CNAME和它的值,可以将服务映射到externalName字段的内容(例如:my.k8s.example.com;可以实现跨namespace名称空间访问)。ExternalName Service是Service的特例,它没有selector,也没有定义任何的端口和Endpoint。相反的,对于运行在集群外部的服务,它通过返回该外部服务的别名这种方式提供服务。
# cat myapp-svc-ExternalName.yaml
apiVersion: v1
kind: Service
metadata:
name: myapp-externalname
namespace: default
spec:
type: ExternalName
externalName: my.k8s.example.com
# kubectl apply -f myapp-svc-ExternalName.yaml