Kubernetes基本原理與示例

1. Kubernetes介紹

基本概念

  • Pod 
    Pod是Kubernetes的基本操做單元,把相關的一個或多個容器構成一個Pod,一般Pod裏的容器運行相同的應用。Pod包含的容器運行在同一個Node(Host)上,看做一個統一管理單元,共享相同的volumes和network namespace/IP和Port空間。
  • Replication Controller 
    Replication Controller確保任什麼時候候Kubernetes集羣中有指定數量的pod副本(replicas)在運行, 若是少於指定數量的pod副本(replicas),Replication Controller會啓動新的Container,反之會殺死多餘的以保證數量不變。Replication Controller使用預先定義的pod模板建立pods,一旦建立成功,pod 模板和建立的pods沒有任何關聯,能夠修改pod 模板而不會對已建立pods有任何影響,也能夠直接更新經過Replication Controller建立的pods。對於利用pod 模板建立的pods,Replication Controller根據label selector來關聯,經過修改pods的label能夠刪除對應的pods。
  • Service 
    Service也是Kubernetes的基本操做單元,是真實應用服務的抽象,每個服務後面都有不少對應的容器來支持,經過Proxy的port和服務selector決定服務請求傳遞給後端提供服務的容器,對外表現爲一個單一訪問接口,外部不須要了解後端如何運行,這給擴展或維護後端帶來很大的好處。
  • Label 
    Label是用於區分Pod、Service、Replication Controller的key/value鍵值對,Pod、Service、 Replication Controller能夠有多個label,可是每一個label的key只能對應一個value。Labels是Service和Replication Controller運行的基礎,爲了將訪問Service的請求轉發給後端提供服務的多個容器,正是經過標識容器的labels來選擇正確的容器。一樣,Replication Controller也使用labels來管理經過pod 模板建立的一組容器,這樣Replication Controller能夠更加容易,方便地管理多個容器,不管有多少容器。

架構

這裏寫圖片描述
Kubernets屬於主從的分佈式集羣架構,包含Master和Node:php

  1. Master做爲控制節點,調度管理整個系統,包含如下組件:前端

    • API Server做爲kubernetes系統的入口,封裝了核心對象的增刪改查操做,以RESTFul接口方式提供給外部客戶和內部組件調用。它維護的REST對象將持久化到etcd(一個分佈式強一致性的key/value存儲)。
    • Scheduler:負責集羣的資源調度,爲新建的pod分配機器。這部分工做分出來變成一個組件,意味着能夠很方便地替換成其餘的調度器。
    • Controller Manager:負責執行各類控制器,目前有兩類:(1)Endpoint Controller:按期關聯service和pod(關聯信息由endpoint對象維護),保證service到pod的映射老是最新的。(2)Replication Controller:按期關聯replicationController和pod,保證replicationController定義的複製數量與實際運行pod的數量老是一致的。
  2. Node是運行節點,運行業務容器,包含如下組件:node

    • Kubelet:責管控docker容器,如啓動/中止、監控運行狀態等。它會按期從etcd獲取分配到本機的pod,並根據pod信息啓動或中止相應的容器。同時,它也會接收apiserver的HTTP請求,彙報pod的運行狀態。
    • Kube Proxy:負責爲pod提供代理。它會按期從etcd獲取全部的service,並根據service信息建立代理。當某個客戶pod要訪問其餘pod時,訪問請求會通過本機proxy作轉發。

    Kubernets使用Etcd做爲存儲和通訊中間件,實現Master和Node的一致性,這是目前比較常見的作法,典型的SOA架構,解耦Master和Node。git

2. Guestbook示例

Guestbook示例將會展現如何運行一個應用到Kubernetes上,應用包含 
- Web前端 
Redis集羣(一個master,2個slave) 
這裏寫圖片描述github

1) 部署Kubernetes

若是你熟悉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

2)啓動Redis Master

須要準備配置文件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-masterapi

的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

3)啓動Redis Slave

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

4)啓動Web Frontend

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

那麼就能夠經過任意節點訪問該應用: 
這裏寫圖片描述

相關文章
相關標籤/搜索