Kubernets屬於主從的分佈式集羣架構,包含Master和Node:php
Master做爲控制節點,調度管理整個系統,包含如下組件:前端
Node是運行節點,運行業務容器,包含如下組件:node
Kubernets使用Etcd做爲存儲和通訊中間件,實現Master和Node的一致性,這是目前比較常見的作法,典型的SOA架構,解耦Master和Node。git
Guestbook示例將會展現如何運行一個應用到Kubernetes上,應用包含
- Web前端
- Redis集羣(一個master,2個slave)
github
若是你熟悉Bosh/Bosh Lite的話,可使用kubernetes-release.本文使用Kubernetes環境:
- master:192.168.3.146
- node1:192.168.3.147
- node2:192.168.3.148
- node3:192.168.3.149web
須要準備配置文件redis-master-controller.yaml,用於描述pod如何運行服務容器:redis
apiVersion: v1
kind: ReplicationController
metadata:
name: redis-master labels: name: redis-master spec: replicas: 1 selector: name: redis-master template: metadata: labels: name: redis-master spec: containers: - name: master image: redis ports: - containerPort: 6379
即便只有一個Redis Master Pod實例,這裏也使用ReplicationController保證Pod持續運行,不然Node掛掉的話,Pod將中止運行。docker
RC與Pod的關聯是經過Label來實現的。Label機制是Kubernetes中的一個重要設計,經過Label進行對象的弱關聯,能夠靈活地進行分類和選擇。對於Pod,須要設置其自身的Label來進行標識,Label是一系列的Key/value對,在Pod-->metadata-->labeks中進行設置。後端
在上面的的yaml文件中定義了該RC的selector中的name爲redis-master,那麼這個RC就會去關注Pod-->metadata-->labeks中label爲redis-master
api
的Pod。修改了對應Pod的Label,就會使Pod脫離RC的控制。一樣,在RC運行正常的時候,若試圖繼續建立一樣Label的Pod,是建立不出來的。由於RC認爲副本數已經正常了,再多起的話會被RC刪掉的。
上面的紅色部分就是RC要控制的pod對象,即紫色部分的pod的名字
建立Pod:
$ kubectl create -f redis-master-controller.yaml
查看ReplicationController:
$ kubectl get rc CONTROLLER CONTAINER(S) IMAGE(S) SELECTOR REPLICAS redis-master master redis name=redis-master 1
查看Pod:
$ kubectl get pods -o wide NAME READY STATUS RESTARTS AGE NODE redis-master-u3fup 1/1 Running 0 2m node1
能夠看到Pod運行在Node1節點,在Node1查看docker容器:
$ docker ps
CONTAINER ID IMAGE ... feb393fbe42b redis:latestminute ago ... d9e934ee55ae gcr.io/google_containers/pause:0.8.0 ...
總共有2個容器正在運行,其中一個Redis Master,另一個是google_containers/pause,它是Netowrk Container, 每啓動一個Pod都會附加啓動這樣一個容器,它的做用就只是簡單的等待,設置Pod的網絡。
若是docker rm -f feb393fbe42b,刪掉Redis Master Container,過一下子就會有新的容器啓動,這說明Kubernetes會保證Pod的容器運行。
$ docker ps
CONTAINER ID IMAGE ... fc3b458d333a redis:latestminute ago ... d9e934ee55ae gcr.io/google_containers/pause:0.8.0 ...
若是把Node1關掉,Pod會遷移到其餘Node上,這是ReplicationController保證Pod運行。
$ kubectl get pods -o wide NAME READY STATUS RESTARTS AGE NODE redis-master-x5kjp 0/1 Running 0 7s node3
上一步已經運行起了一個Redis Master Pod, 即便只有一個Pod,也是有必要使用Service。Kubernetes中Service中起到了負載均衡器的做用,經過Proxy和Selector決定服務請求傳遞給後端提供服務的Pod,對外提供固定的IP,這樣的話Redis Master Pod遷移變化也不會影響。
須要redis-master-service.yaml來描述redis master service:
apiVersion: v1 kind: Service metadata: name: redis-master labels: name: redis-master spec: ports: # the port that this service should serve on - port: 6379 targetPort: 6379 selector: name: redis-master #和redis-master這個pod的Label對應
建立Service:
$ kubectl create -f redis-master-service.yaml
查看Service:
$ kubectl get service
NAME LABELS SELECTOR IP(S) PORT(S)
redis-master name=redis-master name=redis-master 10.254.189.63 6379/TCP
Service同RC同樣,都是經過Label來關聯Pod的。當你在Service的yaml文件中定義了該Service的selector中的label爲app:my-web,那麼這個Service會將Pod-->metadata-->labeks中label爲app:my-web的Pod做爲分發請求的後端。當Pod發生變化時(增長、減小、重建等),Service會及時更新。這樣一來,Service就能夠做爲Pod的訪問入口,起到代理服務器的做用,而對於訪問者來講,經過Service進行訪問,無需直接感知Pod。
須要注意的是,Kubernetes分配給Service的固定IP是一個虛擬IP,並非一個真實的IP,在外部是沒法尋址的。真實的系統實現上,Kubernetes是經過Kube-proxy組件來實現的虛擬IP路由及轉發。因此在以前集羣部署的環節上,咱們在每一個Node上均部署了Proxy這個組件,從而實現了Kubernetes層級的虛擬轉發網絡。
Kubernetes會分配IP(10.254.189.63)給Redis Master Service,這個就是Redis Master Service對外暴露的IP,能夠經過redis-cli訪問:
$ redis-cli -h 10.254.189.63 info
Kubernetes同時提供2種了發現Service的方法:
- 環境變量
當Pod運行的時候,Kubernetes會將以前存在的Service的信息經過環境變量寫到Pod裏面,以Redis Master Service爲例,它的信息會被寫到新的Pod裏面:
"REDIS_MASTER_PORT_6379_TCP=tcp://10.254.189.63:6379", "REDIS_MASTER_PORT_6379_TCP_PROTO=tcp", "REDIS_MASTER_PORT_6379_TCP_ADDR=10.254.189.63", "REDIS_MASTER_SERVICE_PORT=6379", "REDIS_MASTER_SERVICE_HOST=10.254.189.63", "REDIS_MASTER_PORT=tcp://10.254.189.63:6379", "REDIS_MASTER_PORT_6379_TCP_PORT=6379",
這種方法有個比較明顯的缺陷,Pod必須在Service以後啓動,以前啓動的Pod將沒有這些環境變量。那麼下一種方法就沒有這個限制。
- DNS
當有新的Service建立,就會自動生成一條DNS記錄,好比在Kubernetes的Namespace 「my-ns」中有個Service叫」my-service」,那麼就有條DNS記錄」my-service.my-ns」對應Service的IP。以Redis Master Service爲例, 就有條DNS記錄:
redis-master => 10.254.189.63
redis-slave-controller.yaml:
apiVersion: v1
kind: ReplicationController
metadata:
name: redis-slave labels: name: redis-slave spec: replicas: 2 selector: name: redis-slave template: metadata: labels: name: redis-slave spec: containers: - name: worker image: kubernetes/redis-slave:v2 ports: - containerPort: 6379
建立Pod:
$ kubectl create -f redis-slave-controller.yaml $ kubectl get rc CONTROLLER CONTAINER(S) IMAGE(S) SELECTOR REPLICAS redis-master master redis name=redis-master 1 redis-slave worker kubernetes/redis-slave:v2 name=redis-slave 2 $ kubectl get pods -o wide NAME READY STATUS RESTARTS AGE NODE redis-master-x5kjp 1/1 Running 0 1h node3 redis-slave-04o8g 1/1 Running 0 5m node1 redis-slave-llxpk 1/1 Running 0 5m node1
redis-slave-service.yaml:
apiVersion: v1 kind: Service metadata: name: redis-slave labels: name: redis-slave spec: ports: # the port that this service should serve on - port: 6379 selector: name: redis-slave
建立Service:
$ kubectl create -f redis-slave-service.yaml $ kubectl get service NAME LABELS SELECTOR IP(S) PORT(S) redis-master name=redis-master name=redis-master 10.254.189.63 6379/TCP redis-slave name=redis-slave name=redis-slave 10.254.70.184 6379/TCP
frontend-controller.yaml:
apiVersion: v1
kind: ReplicationController
metadata:
name: frontend labels: name: frontend spec: replicas: 3 selector: name: frontend template: metadata: labels: name: frontend spec: containers: - name: php-redis image: kubernetes/example-guestbook-php-redis:v2 ports: - containerPort: 80
建立Pod:
$ kubectl create -f frontend-controller.yaml $ kubectl get rc CONTROLLER CONTAINER(S) IMAGE(S) SELECTOR REPLICAS frontend php-redis kubernetes/example-guestbook-php-redis:v2 name=frontend 3 redis-master master redis name=redis-master 1 redis-slave worker kubernetes/redis-slave:v2 name=redis-slave 2 $ kubectl get pods NAME READY STATUS RESTARTS AGE frontend-7ukb6 1/1 Running 0 45s frontend-8ch4l 1/1 Running 0 45s frontend-n8l7w 1/1 Running 0 45s redis-master-x5kjp 1/1 Running 0 3h redis-slave-04o8g 1/1 Running 0 2h redis-slave-llxpk 1/1 Running 0 2h
frontend-service.yaml:
apiVersion: v1 kind: Service metadata: name: frontend labels: name: frontend spec: ports: # the port that this service should serve on - port: 80 selector: name: frontend
建立Service:
$ kubectl create -f frontend-service.yaml $ kubectl get service NAME LABELS SELECTOR IP(S) PORT(S) frontend name=frontend name=frontend 10.254.58.118 80/TCP redis-master name=redis-master name=redis-master 10.254.189.63 6379/TCP redis-slave name=redis-slave name=redis-slave 10.254.70.184 6379/TCP
Web Frontend是須要對外暴露的,這樣外部網絡才能真正訪問該應用,Kubernetes提供了2種方式暴露Service到外部網絡:
- NodePort
Kubernetes將會在每一個Node上設置一個Port,訪問該Port會被轉發到對應的Service。這支持開發者設置本身的LoadBalancer。
- LoadBalancer
Kubernetes會設置LoadBalancer給Service。
本文采用NodePort方式, 更改frontend-service.yaml:
apiVersion: v1 kind: Service metadata: name: frontend labels: name: frontend spec: type: NodePort ports: # the port that this service should serve on - port: 80 nodePort: 30061 selector: name: frontend
那麼就能夠經過任意節點訪問該應用: