4-1、service资源
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