Docker+K8s基礎篇(五)
- service資源介紹
- A:service資源的工作特性
- service的使用
- A:service字段介紹
- B:ClusterIP的簡單使用
- C:NodePort的簡單使用
- D:LoadBalancer和ExternalName
- E:無頭service
♣一:service資源介紹
A:service資源的工作特性:
kubernetes的service資源:
在整個k8s集群的節點中pods資源是最小的對象,這些對象是提供真正服務的重要組成部分,當我們需要通過外部進行訪問的時候因各pod資源提供的訪問端點是不一致的,我們就需要設定一個固定的訪問端點來提供訪問,這個訪問端點,是存在pod至上和控制器之下的中間層,這個中間層將叫service,service會嚴格依賴k8s上的一個重要組件叫coredns(新版本)和kube-dns(老版本1.11之前的版本),所以我們在部署的時候必需要部署coredes或者kube-dns。
kubernetes要想給客戶端提供網絡功能,需要依賴於第三方方案,這種方案在新版本中可以通過cni(容器網絡插件標准接口)來接入任何遵循這種標准的第三方方案,例如我們使用到的flannel。
kubernetes的三類ip地址:
1:node網絡
2:pod網絡
node和pod的地址是實際存在且配置了。
3:cluater(集群地址)或者叫做service地址,這種地址是虛擬的地址(virtual ip),這些地址沒有出現在接口之上,僅僅只是出現在service的規則當中。
在每個節點之上都工作了kube-proxy組件,這個組件將會實時監視service資源中的變動信息,這個監視是由kube-proxy通過一種固有的方式(watch)請求方式來實現的,一旦service的資源發生變動,kube-proxy都要將其轉換為當前節點之上的能夠被service調度的規則之上(這個規則可能是iptables或者ipvs規則,取決於service的實現方式)
service的實現方式在k8s上有三種模型:
1:userspace(用戶空間)
當用戶的訪問請求會先到達service上,由service將其轉換監聽在某個套接字上的用戶空間內的kube-proxy,接下來kube-proxy處理完成之后再轉給service代理至這個service各個相 關聯的pod之上,實現調度。
這種模型效率不高,因為用戶請求要進過工作在內核上的service轉給工作各個“主機”之上用戶空間的kube-proxy,kube-proxy將其封裝成請求報文發送給內核空間的service資源, 有service的規則在調度至各個pod資源上。
2:iptabeles:
當前用戶請求直接請求service的IP,這個請求會被工作在本地內核空間中的service所截取,然后直接調度給相關聯的pod,而整個調度都是基於iptables規則來完成的。
3:ipvs:
和iptables一樣,只不過規則換成了ipvs規則。
在配置k8s的時候設定k8s工作在什么模式之下,就會生成對應模式的規則,1.1之前默認是userspace,1.11默認使用的ipvs,如果ipvs沒有激活,就會降級為iptables。
當集群中的pods資源發生了改變,這個信息會立馬反應到apiservice之上,因為這個改變會直接存儲在apiservice的ectd當中,這種變化也會立即觸發kube-proxy並發送給service資源中將其轉換為iptables或者ipvs規則,這些轉換都是動態且實時的。
♣二:service的使用:
A:service字段介紹:

[root@www kubeadm]# kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 77m 在我們初始化集群的時候已然幫忙創建了一個名稱叫kubernetes的service資源,這個資源很重要,是保證我們service和集群節點之間聯系的,而且10.96.0.1是面向集群內部的地址。 [root@www kubeadm]# kubectl explain svc 也是包含5個一級字段 KIND: Service VERSION: v1 DESCRIPTION: Service is a named abstraction of software service (for example, mysql) consisting of local port (for example 3306) that the proxy listens on, and the selector that determines which pods will answer requests sent through the proxy. FIELDS: apiVersion <string> APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources kind <string> Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds metadata <Object> Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata spec <Object> Spec defines the behavior of a service. https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status status <Object> Most recently observed status of the service. Populated by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status [root@www kubeadm]# kubectl explain svc.spec.ports(ports是用於把那個端口和后端的容器端口建立關聯關系) KIND: Service VERSION: v1 RESOURCE: ports <[]Object> DESCRIPTION: The list of ports that are exposed by this service. More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies ServicePort contains information on service's port. FIELDS: name <string> The name of this port within the service. This must be a DNS_LABEL. All ports within a ServiceSpec must have unique names. This maps to the 'Name' field in EndpointPort objects. Optional if only one ServicePort is defined on this service. nodePort <integer> The port on each node on which this service is exposed when type=NodePort or LoadBalancer. Usually assigned by the system. If specified, it will be allocated to the service if unused or else creation of the service will fail. Default is to auto-allocate a port if the ServiceType of this Service requires one. More info: https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport port <integer> -required- service的端口 The port that will be exposed by this service. protocol <string> node端口 The IP protocol for this port. Supports "TCP", "UDP", and "SCTP". Default is TCP. targetPort <string> pods端口 Number or name of the port to access on the pods targeted by the service. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME. If this is a string, it will be looked up as a named port in the target Pod's container ports. If this is not specified, the value of the 'port' field is used (an identity map). This field is ignored for services with clusterIP=None, and should be omitted or set equal to the 'port' field. More info: https://kubernetes.io/docs/concepts/services-networking/service/#defining-a-service [root@www kubeadm]# kubectl explain svc.spec.selector (我們需要關聯到哪些pods資源上) KIND: Service VERSION: v1 FIELD: selector <map[string]string> DESCRIPTION: Route service traffic to pods with label keys and values matching this selector. If empty or not present, the service is assumed to have an external process managing its endpoints, which Kubernetes will not modify. Only applies to types ClusterIP, NodePort, and LoadBalancer. Ignored if type is ExternalName. More info: https://kubernetes.io/docs/concepts/services-networking/service/ spec.clusterIP(指定固定的ip,創建之后無法改變) [root@www kubeadm]# kubectl explain svc.spec.type (service的類型) KIND: Service VERSION: v1 FIELD: type <string> DESCRIPTION: type determines how the Service is exposed. Defaults to ClusterIP. Valid options are ExternalName, ClusterIP, NodePort, and LoadBalancer. "ExternalName" maps to the specified externalName. "ClusterIP" allocates a cluster-internal IP address for load-balancing to endpoints. Endpoints are determined by the selector or if that is not specified, by manual construction of an Endpoints object. If clusterIP is "None", no virtual IP is allocated and the endpoints are published as a set of endpoints rather than a stable IP. "NodePort" builds on ClusterIP and allocates a port on every node which routes to the clusterIP. "LoadBalancer" builds on NodePort and creates an external load-balancer (if supported in the current cloud) which routes to the clusterIP. More info: https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types
svc.spec.type字段類型:
類型一共分為4種:
1:ExternalName(把集群外部的服務引入到集群內部直接使用);
2:ClusterIP;
3:NodePort(接入外部);
4:LoadBalancer(支持lbaas負載均衡的一鍵調度)
B:ClusterIP的簡單使用:

[root@www TestYaml]# cat redis-svc.yaml apiVersion: v1 kind: Service metadata: name: redis namespace: default spec: selector: app: redis clusterIP: 10.98.98.98 (指定ip創建的時候需要注意網段和地址沖突問題) type: ClusterIP ports: - port: 6379 targetPort: 6379 [root@www TestYaml]# kubectl apply -f redis-svc.yaml service/redis created [root@www TestYaml]# kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 102m redis ClusterIP 10.98.98.98 <none> 6379/TCP 14s 可以看到redis的ip和端口是配置文件指定的ip和端口 [root@www TestYaml]# 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.98.98.98","... Selector: app=redis Type: ClusterIP IP: 10.98.98.98 Port: <unset> 6379/TCP 指定的service端口 TargetPort: 6379/TCP 指定的pod端口 Endpoints: <none> Session Affinity: None Events: <none> 這里需要說明的是service不會直接到pod,而是需要進過中間層Endpoints的,Endpoints也是k8s上標准的對象,再由Endpoints關聯至pods資源上。
C:NodePort的簡單使用:

[root@www TestYaml]# cat NodePort.svc.yaml apiVersion: v1 kind: Service metadata: name: myapp namespace: default spec: selector: app: myapp clusterIP: 10.99.99.99 type: NodePort ports: - port: 8088 targetPort: 8088 nodePort: 30008 從30000到32767之間的都可以,默認是動態分配的 [root@www TestYaml]# kubectl apply -f NodePort.svc.yaml service/myapp created [root@www TestYaml]# kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 125m myapp NodePort 10.99.99.99 <none> 8088:30008/TCP 22s service的8088端口映射成node上的30008 redis ClusterIP 10.98.98.98 <none> 6379/TCP 23m 通過此種方式創建的pod就可以在外部直接訪問了,只不過要進過好幾級轉換,先是port,再是protocol,在轉換到targetPort上。
D:LoadBalancer和ExternalName:
如果購買了阿里雲平台的環境虛擬機(帶ibaas負載均衡服務),你在上面搭建了一共k8s的集群服務,k8s會自動去調用雲平台的ibaas服務,用軟件的方式去把用戶的訪問請求負載調度至后端的任意一個node節點上,在由節點上的service服務再負載調度至后端的任意一個pod之上,整個過程都是自動的,而且是兩級負載均衡調度。
ExternalName:
我們在構建pods的時候本來就是層級關系的,例如db被tomcat訪問,tomcat被nginx訪問,這種被訪問的形式本身就是一種client的存在,那如果是我們的nginx服務在集群外部的某台集群上,想nginx能訪問到集群內部的tomcat就需要使用ExternalName,在創建yaml文件的時候ExternalName是一個真實有效且能被dns所解析的服務名,然后用戶的訪問請求走外部的nginx,nginx在發送請求報文給集群內部的nodeIP到service,有service調度到后端的pod(tomcat),pod在返回給service,最后返回到nginx上,實現訪問,但是此種方式用的不多。

[root@www kubeadm]# kubectl explain svc.spec.externalName (externalName只能是類型為ExternalName的時候才有效) KIND: Service VERSION: v1 FIELD: externalName <string> DESCRIPTION: externalName is the external reference that kubedns or equivalent will return as a CNAME record for this service. No proxying will be involved. Must be a valid RFC-1123 hostname (https://tools.ietf.org/html/rfc1123) and requires Type to be ExternalName. [root@www kubeadm]#

[root@www kubeadm]# kubectl explain svc.spec.sessionAffinity(svc還支持sessionAffinity) KIND: Service VERSION: v1 FIELD: sessionAffinity <string> DESCRIPTION: Supports "ClientIP" and "None". Used to maintain session affinity. Enable client IP based session affinity. Must be ClientIP or None. Defaults to None. More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies 這里的session支持兩種,一個是ClientIP,將來自同一個ip訪問的請求始終調度到同一個pod上。 None就是默認的隨機調度。
E:無頭service:
無頭service,即值創建service的時候不指定clusterIP,此時用戶的訪問請求不再是訪問clusterIP而是轉而訪問pod上的ip

[root@www TestYaml]# kubectl explain svc.spec.clusterIP KIND: Service VERSION: v1 FIELD: clusterIP <string> DESCRIPTION: clusterIP is the IP address of the service and is usually assigned randomly by the master. If an address is specified manually and is not in use by others, it will be allocated to the service; otherwise, creation of the service will fail. This field can not be changed through updates. Valid values are "None", empty string (""), or a valid IP address. "None" can be specified for headless services when proxying is not required. Only applies to types ClusterIP, NodePort, and LoadBalancer. Ignored if type is ExternalName. More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies 在指定clusterIP的時候可以指定為none(格式是""即可) 案例: [root@www TestYaml]# cat NodePort.svc.yaml apiVersion: v1 kind: Service metadata: name: myapp namespace: default spec: selector: app: myapp clusterIP: None 不指定ip ports: - port: 8088 targetPort: 8088 [root@www TestYaml]# kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 81m myapp ClusterIP None <none> 8088/TCP 9s 可以看到ip是none標記 [root@www TestYaml]# 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 83m 我們直接去解析coredns的ip就能直接看到pod ip的解析記錄 [root@www TestYaml]# dig -t A myapp.default.svc.cluster.local. @10.96.0.10 我們直接解析coredns的ip看看下解析記錄 ; <<>> DiG 9.9.4-RedHat-9.9.4-74.el7_6.1 <<>> -t A myapp.default.svc.cluster.local. @10.96.0.10 ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 60422 ;; flags: qr aa rd; QUERY: 1, ANSWER: 3, AUTHORITY: 0, ADDITIONAL: 1 ;; WARNING: recursion requested but not available ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags:; udp: 4096 ;; QUESTION SECTION: ;myapp.default.svc.cluster.local. IN A ;; ANSWER SECTION: myapp.default.svc.cluster.local. 5 IN A 10.244.2.9 可以看到這里有三個記錄,ip分別是2.9,2.8,1.8 myapp.default.svc.cluster.local. 5 IN A 10.244.2.8 myapp.default.svc.cluster.local. 5 IN A 10.244.1.8 ;; Query time: 0 msec ;; SERVER: 10.96.0.10#53(10.96.0.10) ;; WHEN: 日 7月 14 11:29:23 CST 2019 ;; MSG SIZE rcvd: 201 [root@www TestYaml]# kubectl get pods -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES myapp-9758dcb6b-hl957 1/1 Running 0 4m6s 10.244.2.8 www.kubernetes.node1.com <none> <none> myapp-9758dcb6b-z8rk6 1/1 Running 0 4m6s 10.244.2.9 www.kubernetes.node1.com <none> <none> myapp-9758dcb6b-zl5jt 1/1 Running 0 4m6s 10.244.1.8 www.kubernetes.node2.com <none> <none> 上面的2.9,2.8,1.8對應的ip就是pod的ip,這樣我們得出的結論就是當service沒有clusterip的時候就會通過coredns來解析並轉發到后端的pod之上。
service很好用,但是也是存在缺陷的,當面我們創建了service的時候,用戶訪問需要進行兩級轉換,如果是阿里雲的lbaas則是兩級調度,其實我們看到的是兩級轉換,但是落到ipvs或者iptebles之上就是四層調度。因為ipvs和iptables都是四層的,如果我們建立的是https服務的話,例如https對應的名稱是myapp,那么你的每一個myapp都要配置成htpps的主機,因為ipvs或者iptables自身是無法卸載https會話的。
kubernetes還有一種引入外部流量的方式,叫ingress(是一種7層調度器)將外部的流量引入到內部來,但是也是無法脫離service的工作,必需要用pod7層功能的應用來調度,起能提供的應用有nginx,haprxoy等。在kubernetes之上應用比較多的是nginx,當然還有別的應用有各自相應的優勢。