需要Service的原因,我觉得主要从两个方面考虑:
所以就需要一个稳定的地址来暴露服务给用户,用户完全不需要考虑podip是多少,这个就是服务发现 service。
watch
请求方式,然后与master节点的api-server交互,获取任何一个与service有关的变动情况;然后kube-proxy都要把它转换为当前node之上能够调度到后端pod的规则(iptables/ipvs,取决于kube-proxy中设置的规则)。通过kubectl expose 创建service,通过 kubectl expose -h 查询详细具体内容;
kubectl expose可以使用的对象:
pod (po), service (svc), replicationcontroller (rc), deployment (deploy), replicaset (rs)
需要注意:
expose pod:前提要求pod必须有label,否则出错如下:
$ kubectl expose po httpd-manual --port=8080 --target-port=80
error: couldn't retrieve selectors via --selector flag or introspection: the pod has no labels and cannot be exposed
expose controller:前提是.spec.selector
必须设置。
几个expose实例:
Expose svc: kubectl expose service -n nginx nginx --name=nginx1 --port=80 --type=NodePort
Expose pod: kubectl expose po httpd-manual --port=8080 --target-port=80
Expose controller: kubectl expose replicationcontroller -n controller-test kubia --name rc-expose --port=8080 --target-port=8080
Expose from yaml: kubectl expose -f ReplicationContraller.yaml -n controller-test --name=rc-expose2 --port=8080 --target-port=8080
这个是根据yaml文件中的
.spec.selector
来创建service
关于Service的详细介绍:
kubectl explain svc.spec
一个简单的Service yaml文件
apiVersion: v1
kind: Service
metadata:
name: kubia
spec:
ports:
- port: 80
targetPort: 8080
selector:
app: kubia
通过配置service的.spec.sessionAffinity: ClientIP
:实现从同一个clientIP的所有请求,均转发到同一个pod。
该参数只有两个可选项,默认为none。
kind: Service
apiVersion: v1
metadata:
name: my-service
spec:
selector:
app: MyApp
ports:
- name: http
protocol: TCP
port: 80
targetPort: 9376
- name: https
protocol: TCP
port: 443
targetPort: 9377
**为什么要使用命名端口的方式:**最大的好处就是即使更换containerPort,也不需要改变service的targetPort。
比如现在你的pod中containerPort是8080,但你想更改他为80,使用命名的端口的话你只需要修改pod.spec.ports.containerPort为80,而不需要你去再修改service。
使用命名的端口,需要首先在pod中指定名字:
kind: Pod
metedata:
name: kubia
spec:
containers:
ports:
- name: httpname1
containerPort: 8080
- name: httpsname2
containerPort: 8443
由此可以明确的看到:tagetPort=containerPort 这样就不会混淆了。
apiVersion: v1
kind: Service
metadata:
name: kubia
spec:
ports:
- name: http
port: 80
targetPort: httpname1
- name: https
port: 443
targetPort: httpsname2
selector:
app: kubia
查询容器内部的环境变量。
kubectl exec -n nginx nginx-56b5c7ff74-vmt6t -- env
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=nginx-56b5c7ff74-vmt6t
NGINX_VERSION=1.7.9-1~wheezy
KUBERNETES_SERVICE_HOST=10.43.0.1
KUBERNETES_SERVICE_PORT_HTTPS=443
NGINX_PORT_80_TCP_PORT=80
KUBERNETES_PORT_443_TCP_PROTO=tcp
KUBERNETES_PORT_443_TCP_PORT=443
KUBERNETES_PORT_443_TCP_ADDR=10.43.0.1
NGINX_PORT_80_TCP_PROTO=tcp
NGINX_PORT_80_TCP_ADDR=10.43.210.231
KUBERNETES_SERVICE_PORT=443
KUBERNETES_PORT_443_TCP=tcp://10.43.0.1:443
NGINX_SERVICE_HOST=10.43.210.231 # servicIP
NGINX_SERVICE_PORT=80 # servicePort
KUBERNETES_PORT=tcp://10.43.0.1:443
NGINX_PORT=tcp://10.43.210.231:80
NGINX_PORT_80_TCP=tcp://10.43.210.231:80
HOME=/root
注意服务名称中的横杠被转换为下画线,并且当服务名称用作环境变量名称中的前缀时,所有的字母都是大写的。
每一个服务创建以后,都会在DNS中自动添加资源记录;kubernetes通过修改每个容器的/etc/resolve.conf文件实现;pod是否使用内部的DNS服务器是根据pod.spec.dnsPolicy属性来决定的。
客户端Pod在知道Service的名称后可以通过全限定域名(FQDN)来访问,而不是查询环境变量。
域名格式为:SVC_NAME.NS_NAME.DOMAIN.LTD
,默认的域名后缀为:svc.cluster.local
$ kubectl get svc -n nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx NodePort 10.43.210.231 > 80:30088/TCP 12d
$ nslookup nginx.nginx.svc.cluster.local 10.43.0.10 # coredns的地址
Server: 10.43.0.10
Address: 10.43.0.10:53
Name: nginx.nginx.svc.cluster.local
Address: 10.43.210.231
Headless Services是一种特殊的service,其spec.clusterIP表示为None,这样在实际运行时就不会被分配ClusterIP
$ kubectl explain svc.spec.clusterIP
KIND: Service
VERSION: v1
FIELD: clusterIP >
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
创建一个headless的Service
apiVersion: v1
kind: Service
metadata:
name: nginx-headless
namespace: nginx
spec:
clusterIP: None
ports:
- port: 80
targetPort: 80
selector:
app: nginx
现象,svc的CLUSTER-IP是none,也就是没有ServiceIP,我们用集群内域名解析他的全限定域名FQDN,解析到的是他的Pod的IP:
kubectl get svc -n nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx-headless ClusterIP None 80/TCP 4m51s
# dig -t A nginx-headless.nginx.svc.cluster.local. @10.1.0.10
...
;; ANSWER SECTION:
nginx-headless.nginx.svc.cluster.local. 30 IN A 10.244.2.46
...
# kubectl get pods -n nginx -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-8475b6dc48-p8hd9 1/1 Running 0 19d 10.244.2.46 node1
statefulSet.spec.serviceName
,当serviceName配置成与headless service的Name 相同的时候,可以通过 {hostName}.{headless service}.{namespace}.svc.cluster.local
解析出PodIP;# nslookup prometheus-cluster-monitoring-0.prometheus-operated.cattle-prometheus.svc.cluster.local 10.43.0.10
Server: 10.43.0.10
Address 1: 10.43.0.10 kube-dns.kube-system.svc.cluster.local
Name: prometheus-cluster-monitoring-0.prometheus-operated.cattle-prometheus.svc.cluster.local
Address 1: 10.42.6.63 10-42-6-63.access-prometheus.cattle-prometheus.svc.cluster.local
Headless Service使用场景
kind: Service
apiVersion: v1
metadata:
name: search
namespace: default
spec:
type: ExternalName
externalName: www.baidu.com
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
search ExternalName <none> www.baidu.com <none> 9s
解析一下域名:
$ nslookup search.default.svc.cluster.local 10.43.0.10
Server: 10.43.0.10
Address: 10.43.0.10:53
search.default.svc.cluster.local canonical name = www.baidu.com
www.baidu.com canonical name = www.a.shifen.com
Name: www.a.shifen.com
Address: 39.156.66.14
Name: www.a.shifen.com
Address: 39.156.66.18
search.default.svc.cluster.local canonical name = www.baidu.com
www.baidu.com canonical name = www.a.shifen.com
Name: www.a.shifen.com
Address: 2409:8c00:6c21:104c:0:ff:b02c:156c
Name: www.a.shifen.com
Address: 2409:8c00:6c21:104f:0:ff:b03f:3ae
-----------------------------------------------------
$ nslookup www.baidu.com
Server: 172.24.1.1
Address: 172.24.1.1:53
Non-authoritative answer:
www.baidu.com canonical name = www.a.shifen.com
Name: www.a.shifen.com
Address: 39.156.66.18
Name: www.a.shifen.com
Address: 39.156.66.14
Non-authoritative answer:
www.baidu.com canonical name = www.a.shifen.com
Name: www.a.shifen.com
Address: 2409:8c00:6c21:104f:0:ff:b03f:3ae
Name: www.a.shifen.com
Address: 2409:8c00:6c21:104c:0:ff:b02c:156c
可以很明显看到,ExternalName仅在DNS级别实施的,换言之就是针对有域名的外部服务,只是为外部服务在集群内的DNS服务器上,也就是coredns创建了个简单的CNAME DNS记录。search.default.svc.cluster.local canonical name = www.baidu.com
CNAME记录指向全限定域名而不是IP地址
Service和pod之间并不是直接关联的,他们中间存在一个介质endpoints;
# kubectl describe svc -n kubernetes-dashboard kubernetes-dashboard
Name: kubernetes-dashboard
Namespace: kubernetes-dashboard
Labels: k8s-app=kubernetes-dashboard
Selector: k8s-app=kubernetes-dashboard
Type: NodePort
IP: 10.1.192.5
Port: <unset> 443/TCP
TargetPort: 8443/TCP
NodePort: <unset> 30000/TCP
Endpoints: 10.244.0.10:8443,10.244.0.13:8443
Session Affinity: None
External Traffic Policy: Cluster
Events: <none>
Selector:通过这个来确定哪些pod与这个endpoints连接
Endpoints:Service endpoint对应的pod的IP和端口列表
endpoints里面是一个endpoint列表:
# kubectl get endpoints -n kubernetes-dashboard kubernetes-dashboard
NAME ENDPOINTS AGE
kubernetes-dashboard 10.244.0.10:8443,10.244.0.13:8443 56d
Endpoints对应的就是PodIP和端口
关于endpoints需要注意的地方:
也可以称为外挂式部署。
首先理解所谓服务(应用层),无非就是IP+端口。
所谓外挂式,就是把集群外的服务,挂载到集群里面,看上去和部署在集群里面的服务一样。
关键在于创建了一个endpoint
如果创建了不包含pod selector(在集群里见到selector就是Pod的selector,比如节点选择器就要指明NodeSelector)的service,kubernetes就不会创建endpoint(毕竟缺少选择器,将不会知道service中包含哪些pod)。这样就需要手动创建Endpoints资源来指定endpoint列表。
首先创建一个没有selector的service
apiVersion: v1
kind: Service
metadata:
name: external-service
spec:
ports:
- port: 80
这里的port只是集群内部访问的端口,跟真正外部服务的端口没有关系,简单来说就是在集群内部访问这个端口转发到endpoints上指定的外部服务的真正端口
手动创建endpoints
apiVersion: v1
kind: Endpoints
metadata:
name: external-service
subsets:
- addresses:
- ip: 11.11.11.11
- ip: 22.22.22.22
ports:
- port: 32322
地址可以指定多个实现负载均衡;这里的port就是真正服务的端口。
NodePort将集群内部的服务映射为NodeIP+NodePort,外部客户端访问服务只需要
一个典型nodeport.yaml例子:
apiVersion: v1
kind: Service
metadata:
name: kube-node-service
labels:
name: kube-node-service
spec:
type: NodePort
ports:
- port: 80
targetPort: 8081
protocol: TCP
nodePort: 32143
selector:
app: web
一个简单的LoadBalancer yaml
apiVersion: v1
kind: Service
metadata:
name: kubia-loadbalancer
spec:
type: LoadBalancer
ports:
- port: 80
targetPort: 8080
selector:
app: kubia