Kubernetes应用部署模型解析(原理篇)
Kubernetes架构
Kubernetes代理节点Kubelet和Kube-proxy运行在代理节点上。他们监听服务节点的信息来启动容器和实现Kubernetes网络和其它业务模型,比如Service、Pod等。当然每个代理节点都运行Docker。Docker负责下载容器镜像和运行容器。
Kubelet
Kube-ProxyKube-proxy是一个简单的网络代理和负载均衡器。它具体实现Service模型,每个Service都会在所有的Kube-proxy节点上体现。根据Service的selector所覆盖的Pods, Kube-proxy会对这些Pods做负载均衡来服务于Service的访问者。
Kubernetes服务节点Kubernetes服务组件形成了Kubernetes的控制平面,目前他们运行在单一节点上,但是将来会分开来部署,以支持高可用性。
etcd所有的持久性状态都保存在etcd中。Etcd同时支持watch,这样组件很容易得到系统状态的变化,从而快速响应和协调工作。
Kubernetes API Server这个组件提供对API的支持,响应REST操作,验证API模型和更新etcd中的相应对象。
Scheduler通过访问Kubernetes中/binding API, Scheduler负责Pods在各个节点上的分配。Scheduler是插件式的,Kubernetes将来可以支持用户自定义的scheduler。
Kubernetes Controller Manager ServerController Manager Server负责所有其它的功能,比如endpoints控制器负责Endpoints对象的创建,更新。node控制器负责节点的发现,管理和监控。将来可能会把这些控制器拆分并且提供插件式的实现。
Kubernetes模型Kubernetes的伟大之处就在于它的应用部署模型,主要包括Pod、Replication controller、Label和Service。
PodKubernetes的最小部署单元是Pod而不是容器。作为First class API公民,Pods能被创建,调度和管理。简单地来说,像一个豌豆荚中的豌豆一样,一个Pod中的应用容器同享同一个上下文:
- PID 名字空间。但是在docker中不支持
- 网络名字空间,在同一Pod中的多个容器访问同一个IP和端口空间。
- IPC名字空间,同一个Pod中的应用能够使用SystemV IPC和POSIX消息队列进行通信。
- UTS名字空间,同一个Pod中的应用共享一个主机名。
- Pod中的各个容器应用还可以访问Pod级别定义的共享卷。
Replication controller复制控制器确保Pod的一定数量的份数(replica)在运行。如果超过这个数量,控制器会杀死一些,如果少了,控制器会启动一些。控制器也会在节点失效、维护的时候来保证这个数量。所以强烈建议即使我们的份数是1,也要使用复制控制器,而不是直接创建Pod。
ServiceService定义了一个Pod的逻辑集合和访问这个集合的策略。集合是通过定义Service时提供的Label选择器完成的。举个例子,我们假定有3个Pod的备份来完成一个图像处理的后端。这些后端备份逻辑上是相同的,前端不关心哪个后端在给它提供服务。虽然组成这个后端的实际Pod可能变化,前端客户端不会意识到这个变化,也不会跟踪后端。Service就是用来实现这种分离的抽象。
Service Cluster IP和 kuber proxy每个代理节点都运行了一个kube-proxy进程。这个进程从服务进程那边拿到Service和Endpoint对象的变化。
对每一个Service, 它在本地打开一个端口。 到这个端口的任意连接都会代理到后端Pod集合中的一个Pod
IP和端口。在创建了服务后,服务Endpoint模型会体现后端Pod的
IP和端口列表,kube-proxy就是从这个endpoint维护的列表中选择服务后端的。另外Service对象的sessionAffinity属性也会帮助kube-proxy来选择哪个具体的后端。缺省情况下,后端Pod的选择是随机的。可以设置service.spec.sessionAffinity
成"ClientIP"来指定同一个ClientIP的流量代理到同一个后端。在实现上,kube-proxy会用IPtables规则把访问Service的Cluster
IP和端口的流量重定向到这个本地端口。下面的部分会讲什么是service的Cluster IP。
内部使用者的服务发现Kubernetes在一个集群内创建的对象或者在代理集群节点上发出访问的客户端我们称之为内部使用者。要把服务暴露给内部使用者,Kubernetes支持两种方式:环境变量和DNS。
环境变量当kubelet在某个节点上启动一个Pod时,它会给这个Pod的容器为当前运行的Service设置一系列环境变量,这样Pod就可以访问这些Service了。一般地情况是{SVCNAME}_SERVICE_HOSTh和{SVCNAME}_SERVICE_PORT变量,
其中{SVCNAME}是Service名字变成大写,中划线变成下划线。比如Service "Redis-master",它的端口是 TCP 6379,分配到的Cluster IP地址是 10.0.0.11,kubelet可能会产生下面的变量给新创建的Pod容器:
REDIS_MASTER_SERVICE_PORT=6379
REDIS_MASTER_PORT=tcp://10.0.0.11:6379
REDIS_MASTER_PORT_6379_TCP=tcp://10.0.0.11:6379
REDIS_MASTER_PORT_6379_TCP_PROTO=tcp
REDIS_MASTER_PORT_6379_TCP_PORT=6379
REDIS_MASTER_PORT_6379_TCP_ADDR= 10.0.0.11
DNS一个可选的Kubernetes附件(强烈建议用户使用)是DNS服务。它跟踪集群中Service对象,为每个Service对象创建DNS记录。这样所有的Pod就可以通过DNS访问服务了。
Pod IP and Service Cluster IPPod IP
地址是实际存在于某个网卡(可以是虚拟设备)上的,但Service Cluster
IP就不一样了,没有网络设备为这个地址负责。它是由kube-proxy使用Iptables规则重新定向到其本地端口,再均衡到后端Pod的。我们前面说的Service环境变量和DNS都使用Service的Cluster
IP和端口。
外部访问ServiceService对象在Cluster IP range池中分配到的IP只能在内部访问,如果服务作为一个应用程序内部的层次,还是很合适的。如果这个Service作为前端服务,准备为集群外的客户提供业务,我们就需要给这个服务提供公共IP了。
Label和Label selectorLabel标签在Kubernetes模型中占着非常重要的作用。Label表现为key/value对,附加到Kubernetes管理的对象上,典型的就是Pods。它们定义了这些对象的识别属性,用来组织和选择这些对象。Label可以在对象创建时附加在对象上,也可以对象存在时通过API管理对象的Label。
environment = productiontier != frontendenvironment = production,tier != frontend
environment in (production, qa)tier notin (frontend, backend)partition
一个简单的应用
部署Kubernetes集群
- 所有的节点安装了docker version 1.2+ 和 bridge-utils
- 如果没有本地的docker registry, 要确保节点能访问互联网gcr.io
- 确保管理节点能够ssh 访问所有节点。比如ssh gongysh@192.168.0.201 ls
$ git clone https://github.com/GoogleCloudPlatform/kubernetes.git
cd kubernetes./build/run.sh hack/build-do.sh
gongysh@fedora20:~/git/kubernetes/cluster/ubuntu$ cat config-default.sh#!/bin/bash# Define all your cluster nodes, MASTER node comes first"# And separated with blank space like <user_1@ip_1> <user_2@ip_2> <user_3@ip_3>export nodes="gongysh@192.168.0.201 gongysh@192.168.0.202 gongysh@192.168.0.203"# Define all your nodes role: a(master) or i(minion) or ai(both master and minion), must be the order sameexport roles=("ai" "i" "i")# Define minion numbersexport NUM_MINIONS=${NUM_MINIONS:-3}# define the IP range used for service portal.# according to rfc 1918 ref: https://tools.ietf.org/html/rfc1918 choose a private ip range here.export SERVICE_CLUSTER_IP_RANGE=192.168.3.0/24# define the IP range used for flannel overlay network, should not conflict with above SERVICE_CLUSTER_IP_RANGE rangeexport FLANNEL_NET=172.16.0.0/16....
$ cd cluster$ KUBERNETES_PROVIDER=ubuntu ./kube-up.sh
Kubernetes cluster is running. The master is running at: http://192.168.0.201 ... calling validate-cluster Found 3 nodes. 1 NAME LABELS STATUS 2 192.168.0.201 <none> Ready 3 192.168.0.202 <none> Ready 4 192.168.0.203 <none> Ready Validate output: Cluster validation succeeded Done, listing cluster services: Kubernetes master is running at http://192.168.0.201:8080
部署nginx pod 和复制器
$ cat nginx-rc.yaml apiVersion: v1 kind: ReplicationController
metadata: name: nginx-controller spec: replicas: 2 selector:
name: nginx template: metadata: labels: name:
nginx spec: containers: - name: nginx image:
nginx ports: - containerPort: 80
$ kubectl -s http://192.168.0.201:8080 create -f nginx-rc.yaml
$ kubectl -s http://192.168.0.201:8080
get podsNAME READY REASON RESTARTS
AGEnginx-controller-6zr34 1/1 Running 0
48mnginx-controller-njlgt 1/1 Running 0 48m
$ $ kubectl -s http://192.168.0.201:8080
describe pod nginx-controller-6zr34 2>/dev/null | grep Node:Node:
192.168.0.203/192.168.0.203$ kubectl -s http://192.168.0.201:8080 describe pod nginx-controller-njlgt 2>/dev/null | grep Node:Node: 192.168.0.201/192.168.0.201
部署节点内部可访问的nginx service
$ cat nginx-service-clusterip.yaml apiVersion: v1 kind: Service
metadata: name: nginx-service-clusterip spec: ports: - port:
8001 targetPort: 80 protocol: TCP selector: name:
nginx
$ kubectl -s http://192.168.0.201:8080 create -f ./nginx-service-clusterip.yaml services/nginx-service $ kubectl -s http://192.168.0.201:8080
get serviceNAME LABELS
SELECTOR IP(S) PORT(S)kubernetes
component=apiserver,provider=kubernetes <none>
192.168.3.1 443/TCPnginx-service-clusterip <none>
name=nginx 192.168.3.91 8001/TCP
$ ssh 192.168.0.202 curl -s 192.168.3.91:8001 <!DOCTYPE html>
<html> <head> <title>Welcome to nginx!</title>
<style> body { 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style> </head> <body> <h1>Welcome to
nginx!</h1> <p>If you see this page, the nginx web server is
successfully installed and working. Further configuration is
required.</p> <p>For online documentation and support
please refer to <a
href="http://nginx.org/">nginx.org</a>.<br/> Commercial
support is available at <a
href="http://nginx.com/">nginx.com</a>.</p> <p><em>Thank
you for using nginx.</em></p> </body> </html>
部署外部可访问的nginx service
$ cat nginx-service-nodeport.yaml apiVersion: v1 kind: Service
metadata: name: nginx-service-nodeport spec: ports: - port:
8000 targetPort: 80 protocol: TCP type:
NodePort selector: name: nginx
$ kubectl -s http://192.168.0.201:8080 create -f ./nginx-service-nodeport.yaml services/nginx-service-nodeport $ kubectl -s http://192.168.0.201:8080
get serviceNAME LABELS
SELECTOR IP(S) PORT(S)kubernetes
component=apiserver,provider=kubernetes <none>
192.168.3.1 443/TCPnginx-service-clusterip <none>
name=nginx 192.168.3.91
8001/TCPnginx-service-nodeport <none>
name=nginx 192.168.3.84 8000/TCP
$ kubectl -s http://192.168.0.201:8080
describe service nginx-service-nodeport 2>/dev/null | grep
NodePortType: NodePortNodePort:
<unnamed> 32606/TCP
$ curl 192.168.0.201:32606 <!DOCTYPE html> <html>
<head> <title>Welcome to nginx!</title> <style>
body { 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; } </style>
</head> <body> <h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully
installed and working. Further configuration is
required.</p> <p>For online documentation and support
please refer to <a
href="http://nginx.org/">nginx.org</a>.<br/> Commercial
support is available at <a
href="http://nginx.com/">nginx.com</a>.</p> <p><em>Thank
you for using nginx.</em></p> </body> </html>
代理节点上的IP tables规则解析
-A KUBE-PORTALS-CONTAINER -d 192.168.3.84/32 -p tcp -m comment --comment
"default/nginx-service-nodeport:" -m tcp --dport 8000 -j REDIRECT
--to-ports 43981
-A KUBE-NODEPORT-CONTAINER -p tcp -m comment --comment "default/nginx-service-nodeport:" -m tcp --dport 32606 -j REDIRECT --to-ports 43981
-A KUBE-PORTALS-HOST -d 192.168.3.84/32 -p tcp -m comment --comment
"default/nginx-service-nodeport:" -m tcp --dport 8000 -j DNAT
--to-destination 192.168.0.201:43981
-A
KUBE-NODEPORT-HOST -p tcp -m comment --comment
"default/nginx-service-nodeport:" -m tcp --dport 30975 -j DNAT
--to-destination 192.168.0.201:43981
总结