概述 Pod的IP是在docker0网段动态分配的,当发生重启,扩容等操作时,IP地址会随之变化。当某个Pod(frontend)需要去访问其依赖的另外一组Pod(backend)时,如果backend的IP发生变化时,如何保证fronted到backend的正常通信变的非常重要。由此,引出了Service的概念。 这里docker0是一个网桥,docker daemon启动container时会根据docker0的网段来划粉container的IP地址Docker网络 在实际生产环境中,对Service的访问可能会有两种来源:Kubernetes集群内部的程序(Pod)和Kubernetes集群外部,为了满足上述的场景,Kubernetes service有以下三种类型:
ClusterIP:提供一个集群内部的虚拟IP以供Pod访问。
NodePort:在每个Node上打开一个端口以供外部访问。
LoadBalancer:通过外部的负载均衡器来访问。
那么它是怎么实现的,通过下面的示例来理解。(创建方式可以是通过yaml文件或者是命令行方式,这里为了理解我先用命令行方式创建,如果不合适的地方我们通过kubectl edit (RESOURCE/NAME | -f FILENAME) [options]这种方式先修改)
其次,还需要理解NodePort,TargetPort以及port他们的区别:NodePort 外部机器可访问的端口。 比如一个Web应用需要被其他用户访问,那么需要配置type=NodePort,而且配置nodePort=30001,那么其他机器就可以通过浏览器访问scheme://node:30001访问到该服务,例如http://node:30001。 例如MySQL数据库可能不需要被外界访问,只需被内部服务访问,那么不必设置NodePort
TargetPort 容器的端口(最根本的端口入口),与制作容器时暴露的端口一致(DockerFile中EXPOSE),例如docker.io官方的nginx暴露的是80端口。 docker.io官方的nginx容器的DockerFile参考https://github.com/nginxinc/docker-nginx
1 2 3 4 5 6 7 8 9 10 11 12 apiVersion: v1kind: Servicemetadata: name: nginx-service spec: type: NodePort ports: - port: 30080 targetPort: 80 nodePort: 30001 selector: name: nginx-pod
port kubernetes中的服务之间访问的端口,尽管mysql容器暴露了3306端口(参考https://github.com/docker-library/mysql/ 的DockerFile),但是集群内其他容器需要通过33306端口访问该服务,外部机器不能访问mysql服务,因为他没有配置NodePort类型
1 2 3 4 5 6 7 8 9 10 apiVersion: v1 kind: Service metadata: name: mysql-service spec: ports: - port: 33306 targetPort: 3306 selector: name: mysql-pod
一、Create service (type: ClusterIP) 此模式会提供一个集群内部的虚拟IP(与Pod不在同一网段),以供集群内部的pod之间通信使用。 ClusterIP也是Kubernetes service的默认类型。
为了实现图上的功能主要需要以下几个组件的协同工作
apiserver 用户通过kubectl命令向apiserver发送创建service的命令,apiserver接收到请求以后将数据存储到etcd中。
kube-proxy kubernetes的每个节点中都有一个叫做kube-proxy的进程,这个进程负责感知service,pod的变化,并将变化的信息写入本地的iptables中。
iptables 使用NAT等技术将virtualIP的流量转至endpoint中。
下面我们实际发布一个Service,能够更清晰的了解到Service是如何工作的。
1. 通过命令行方式创建service,类型为clusterip 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 $ kubectl create service clusterip my-svc-cp - -tcp= 8080 :80 $ kubectl describe service/my-svc-cp Name: my-svc-cpNamespace: defaultLabels: app= my-svc-cpAnnotations: <none> Selector: app= my-svc-cpType: ClusterIP IP: 10.247 .52.210 Port: 8 0-8080 8080 /TCP TargetPort: 80 /TCP Endpoints: <none> Session Affinity: None Events: <none> $ kubectl get svc - o wide my-svc-cp ClusterIP 10.247 .52.210 <none> 80 /TCP 6 s app= my-svc-cp $ kubectl edit svc my-svc-cp apiVersion: v1kind: Servicemetadata: creationTimestamp: 201 8-1 2-12 T17:41 :54 Z labels: app: my-svc-cp name: my-svc-cp namespace: default resourceVersion: "32742" selfLink: /api/v1/namespaces/default/services/my-svc-cp uid: 372 f6f2c-fe35-11 e8-b967-fa163e874e90 spec: clusterIP: 10.247 .52.210 ports: - name: 8 0-8080 port: 8080 protocol: TCP targetPort: 80 selector: app: my-svc-cp-pod sessionAffinity: None type: ClusterIP status: loadBalancer: {}
2.创建pod 注意selector label
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 $ vim my-svc-cp-pod.yaml apiVersion: v1kind: Podmetadata: name: my-svc-cp-pod labels: app: my-svc-cp-pod spec: containers: - image: nginx:latest imagePullPolicy: Always name: nginx affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: kubernetes.io/hostname operator: In values: - 192.168 .253.183 restartPolicy: Always schedulerName: default-scheduler $ kubectl create - f my-svc-cp-pod.yaml $ kubectl get pods,svc,endpoints,deployment - o wide NAME READY STATUS RESTARTS AGE IP NODE po/my-svc-cp-pod 1 /1 Running 0 36 m 172.16 .0.36 192.168 .253.183 NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR svc/kubernetes ClusterIP 10.247 .0.1 <none> 443 /TCP 1 h <none> svc/my-svc-cp ClusterIP 10.247 .52.210 <none> 8080 /TCP 3 m app= my-svc-cp-pod NAME ENDPOINTS AGE ep/kubernetes 192.168 .103.50 :5444 ,192.168 .174.46 :5444 ,192.168 .236.124 :5444 1 h ep/my-svc-cp 172.16 .0.36 :80 3 m $ kubectl describe ep/my-svc-cp Name: my-svc-cpNamespace: defaultLabels: app= my-svc-cpAnnotations: <none> Subsets: Addresses: 172.16 .0.36 NotReadyAddresses: <none> Ports: Name Port Protocol - --- - --- - ------- 8 0-8080 80 TCP Events: <none> $ curl - I 10.247 .52.210 :8080 HTTP/1.1 200 OK Server: nginx/1.15.5 Date: Wed, 12 Dec 2018 17 :48 :56 GMTContent-Type: text/html Content-Length: 612 Last-Modified: Tue, 02 Oct 2018 14 :49 :27 GMTConnection: keep-aliveETag: "5bb38577-264" Accept-Ranges: bytes$ curl - I 172.16 .0.36 HTTP/1.1 200 OK Server: nginx/1.15.5 Date: Wed, 12 Dec 2018 17 :47 :52 GMTContent-Type: text/html Content-Length: 612 Last-Modified: Tue, 02 Oct 2018 14 :49 :27 GMTConnection: keep-aliveETag: "5bb38577-264" Accept-Ranges: bytes
二、Create service (type: NodePort) 1.通过命令行方式创建service,类型为nodeport Cluster service 的 IP 地址是虚拟的,因此,只能从node节点上使用该IP 地址访问应用。为了从集群外访问应用,K8S 提供了使用 node 节点的IP 地址访问应用的方式。
基本上,NodePort 服务与普通的 “ClusterIP” 服务 YAML 定义有两点区别。 首先,type 是 “NodePort”。还有一个称为 nodePort 的附加端口,指定在节点上打开哪个端口。 如果你不指定这个端口,它会选择一个随机端口。
下图中是 32591. 该端口号的范围是 kube-apiserver 的启动参数 –service-node-port-range指定的,在当前测试环境中其值是 30000-32767。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 $ kubectl create service nodeport my-svc-np - -tcp= 8080 :80 [root@linux-node1 ~] Name: my-svc-npNamespace: defaultLabels: app= my-svc-npAnnotations: <none> Selector: app= my-svc-npType: NodePort IP: 10.103 .115.169 Port: 808 0-80 8080 /TCP TargetPort: 80 /TCP NodePort: 808 0-80 32591 /TCP Endpoints: <none> Session Affinity: None External Traffic Policy: Cluster Events: <none> $ kubectl edit svc my-svc-np ...... ...... externalTrafficPolicy: Cluster ports: - name: 808 0-80 nodePort: 32591 port: 8080 protocol: TCP targetPort: 80 selector: app: my-svc-np-pod sessionAffinity: None type: NodePort status: loadBalancer: {} ...... ......
2.创建pod与之建立关系 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 $ kubectl run nginx-deployment - -image= nginx - -replicas= 3 - -dry-run - o yaml > nginx-deployment.yaml $ cat > > nginx-deployment.yaml < < EOF apiVersion: apps/v1beta1 kind: Deploymentmetadata: creationTimestamp: null labels: run: nginx-deployment name: nginx-deployment spec: replicas: 3 selector: matchLabels: app: my-svc-np-pod strategy: {} template: metadata: creationTimestamp: null labels: app: my-svc-np-pod spec: containers: - image: nginx name: nginx-deployment resources: {} status: {}EOF $ kubectl create - f nginx-deployment.yaml deployment.apps/nginx-deployment created $ kubectl get pod - o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE nginx-deployment-6794 c779fb-8 x92g 1 /1 Running 0 4 m 10.244 .1.19 k8s-m2 <none> nginx-deployment-6794 c779fb-rf8qj 1 /1 Running 0 4 m 10.244 .0.19 k8s-m3 <none> nginx-deployment-6794 c779fb-tdscx 1 /1 Running 0 4 m 10.244 .2.35 k8s-m1 <none> $ kubectl describe svc my-svc-np Name: my-svc-npNamespace: defaultLabels: app= my-svc-npAnnotations: <none> Selector: app= my-svc-np-podType: NodePortIP: 10.103 .115.169 Port: 808 0-80 8080 /TCP TargetPort: 80 /TCP NodePort: 808 0-80 32591 /TCP Endpoints: 10.244 .0.19 :80 ,10.244 .1.19 :80 ,10.244 .2.35 :80 Session Affinity: None External Traffic Policy: Cluster Events: <none> $ kubectl get svc my-svc-np NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE my-svc-np NodePort 10.103 .115.169 <none> 8080 :32591 /TCP 42 m $ curl - I 10.244 .0.19 HTTP/1.1 200 OK Server: nginx/1.15.7 Date: Thu, 13 Dec 2018 08 :43 :25 GMTContent-Type: text/html Content-Length: 612 Last-Modified: Tue, 27 Nov 2018 12 :31 :56 GMTConnection: keep-aliveETag: "5bfd393c-264" Accept-Ranges: bytes$ curl - I 10.103 .115.169 :8080 HTTP/1.1 200 OK Server: nginx/1.15.7 Date: Thu, 13 Dec 2018 08 :42 :53 GMTContent-Type: text/html Content-Length: 612 Last-Modified: Tue, 27 Nov 2018 12 :31 :56 GMTConnection: keep-aliveETag: "5bfd393c-264" Accept-Ranges: bytescurl - I 192.168 .56.111 :32591 HTTP/1.1 200 OK Server: nginx/1.15.7 Date: Thu, 13 Dec 2018 08 :45 :39 GMTContent-Type: text/html Content-Length: 612 Last-Modified: Tue, 27 Nov 2018 12 :31 :56 GMTConnection: keep-aliveETag: "5bfd393c-264" Accept-Ranges: bytes
以上就是nodeport网络类型的简单实现
三、Create service (type: HeadlessClusterIP) 所谓的HeadlessClusterIP 就是我们在创建这种网络类型的时候将 spec.clusterIP 设置成 None,这样k8s就不会给service分配clusterIp了。但指定了selector,那么endpoints controller还是会创建Endpoints的,会创建一个新的DNS记录直接指向这个service描述的后端pod。否则,不会创建Endpoints记录。这种ClusterIP,kube-proxy 并不处理此类服务,因为没有load balancing或 proxy 代理设置,在访问服务的时候回返回后端的全部的Pods IP地址,主要用于开发者自己根据pods进行负载均衡器的开发(设置了selector)。
下面通过实践理解
1.通过命令行方式创建svc,类型为headlessCluserIP 1 2 3 4 5 6 7 8 9 10 11 12 13 $ kubectl create svc clusterip my-svc-headless - -clusterip= "None" - -tcp= 8080 :80 service/my-svc-headless created $ kubectl describe svc my-svc-headless Name: my-svc-headlessNamespace: defaultLabels: app= my-svc-headlessAnnotations: <none> Selector: app= my-svc-headlessType: ClusterIPIP: NoneSession Affinity: None Events: <none>
2. 创建pod与之关联: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 $ cat nginx-deployment.yaml apiVersion: apps/v1beta1 kind: Deploymentmetadata: creationTimestamp: null labels: run: nginx-deployment name: nginx-deployment spec: replicas: 3 selector: matchLabels: app: my-svc-headless strategy: {} template: metadata: creationTimestamp: null labels: app: my-svc-headless spec: containers: - image: nginx name: nginx-deployment resources: {} status: {}$ kubectl create - f nginx-deployment.yaml deployment.apps/nginx-deployment created $ [root@k8s-m1 ~] NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE nginx-deployment-54 bfc79477-b78c6 1 /1 Running 0 40 s 10.244 .1.21 k8s-m2 <none> nginx-deployment-54 bfc79477-dvp2t 1 /1 Running 0 40 s 10.244 .0.22 k8s-m3 <none> nginx-deployment-54 bfc79477-x6v7s 1 /1 Running 0 40 s 10.244 .2.38 k8s-m1 <none> $ kubectl describe svc my-svc-headless Name: my-svc-headlessNamespace: defaultLabels: app= my-svc-headlessAnnotations: <none> Selector: app= my-svc-headlessType: ClusterIPIP: NonePort: 808 0-80 8080 /TCP TargetPort: 80 /TCP Endpoints: 10.244 .0.20 :80 ,10.244 .1.20 :80 ,10.244 .2.36 :80 Session Affinity: None Events: <none> kube-dns ClusterIP 10.96 .0.10 <none> 53 / UDP,53 /TCP 13 d $ kubectl exec - it nginx-deployment-54 bfc79477-b78c6 - - cat /etc/resolv.conf nameserver 10.96 .0.10 search default.svc.cluster.local svc.cluster.local cluster.local options ndots:5 $ dig @10.96 .0.10 my-svc-headless.default.svc.cluster.local ; < < > > DiG 9.9 .4 - RedHat-9.9 .4 - 72 .el7 < < > > @10.96 .0.10 my-svc-headless.default.svc.cluster.local ; (1 server found) ;; global options: + cmd ;; Got answer: ;; -> > HEADER< < - opcode: QUERY, status: NOERROR, id: 41743 ;; flags: qr aa rd ra; QUERY: 1 , ANSWER: 3 , AUTHORITY: 0 , ADDITIONAL: 0 ;; QUESTION SECTION: ;my-svc-headless.default.svc.cluster.local. IN A ;; ANSWER SECTION: my-svc-headless.default.svc.cluster.local. 30 IN A 10.244 .0.22 my-svc-headless.default.svc.cluster.local. 30 IN A 10.244 .1.21 my-svc-headless.default.svc.cluster.local. 30 IN A 10.244 .2.38 ;; Query time: 29 msec ;; SERVER: 10.96 .0.10 ;; WHEN: Thu Dec 13 04 :37 :13 EST 2018 ;; MSG SIZE rcvd: 107
由上可见 dns服务为service和pod生成不同格式的DNS记录 Service
A记录:生成my-svc.my-namespace.svc.cluster.local域名,解析成 IP 地址,分为两种情况: 1.普通 Service:解析成 ClusterIP 2.Headless Service:解析为指定 Pod的IP列表(上述示例就是headless)
SRV记录:为命名的端口(普通 Service 或 Headless Service)生成_my-port-name._my-port-protocol.my-svc.my-namespace.svc.cluster.local的域名
Pod
A记录:生成域名 pod-ip.my-namespace.pod.cluster.local
上述示例个人理解是 这种无头ClusterIP类型 在其集群内部直接可以通过该域名去访问pod, 并且该域名也起到了通过dns做负载的能力。
其他访问法方式可查阅官网学习,此篇博文主要是再次学习巩固一下知识~ 诸如ingress这种访问方式之前也在学习实践过程中已经实现过了,感兴趣可以撩我