kubernetes核心概念-pod + service
k8s的部署架構
kubernetes中有兩類資源,分別是master和nodes,master和nodes上跑的服務如下圖,
kube-apiserver | kubelet kube-controller-manager | kube-scheduler | kube-proxy ---------------------- -------------------- k8s master node (non-master)
master
:負責管理整個叢集,例如,對應用進行排程(擴縮)、維護應用期望的狀態、對應用進行釋出等。node
:叢集中的宿主機(可以是物理機也可以是虛擬機器),每個node上都有一個agent,名為kubelet,用於跟master通訊。同時一個node需要有管理容器的工具包,用於管理在node上執行的容器(docker或rkt)。一個k8s叢集至少要有3個節點。
kubelet通過master暴露的API與master通訊,使用者也可以直接呼叫master的API做叢集的管理。
k8s中的物件Objects
pod
k8s中的最小部署單元,不是一個程式/程序,而是一個環境(包括容器、儲存、網路ip:port、容器配置)。其中可以執行1個或多個container(docker或其他容器),在一個pod內部的container共享所有資源,包括共享pod的ip:port和磁碟。
pod是臨時性的,用完即丟棄的,當pod中的程序結束、node故障,或者資源短缺時,pod會被幹掉。基於此,使用者很少直接建立一個獨立的pods,而會通過k8s中的controller來對pod進行管理。
controller通過pod templates來建立pod,pod template是一個靜態模板,創建出來之後的pod就跟模板沒有關係了,模板的修改也不會影響現有的pod。
services
由於pod是臨時性的,pod的ip:port也是動態變化的。這種動態變化在k8s叢集中就涉及到一個問題:如果一組後端pod作為服務提供方,供一組前端的pod所呼叫,那服務呼叫方怎麼自動感知服務提供方。這就引入了k8s中的另外一個核心概念,services.
service是通過apiserver創建出來的物件例項,舉例,
kind: Service apiVersion: v1 metadata: name: my-service spec: selector: app: MyApp ports: - protocol: TCP port: 80 targetPort: 9376
這個配置將創建出來一個新的Service物件,名為my-service,後端是所有包含app=MyApp
的pod,目標埠是9376,同時這個service也會被分配一個ip,被稱為叢集ip,對應的埠是80. 如果不指定targetPort
, 那麼targetPort
與port
相同。關於targetPort
更靈活的設定是,targetPort
可以是一個String型別的名字,該名字對應的真實埠值由各個後端pod自己定義,這樣同一組pod無需保證同一個port,更加靈活。
在某種場景下,可能你不想用label selector,不想通過選擇標籤的方式來獲取所有的後端pod,如測試環境下同一個service name可能指向自己的pod,而非生產環境中的正式pod叢集。或者pod叢集並不在k8s中維護。針對這個場景,k8s也有優雅的方案進行適配。就是在建立service的時候不指定selector的部分,而是先創建出來service,之後手動繫結後端pod的ip和埠。
終於知道為什麼k8s是目前風頭最勁的服務編排技術了,它充分地做了解耦,由於google的業務複雜性,它的元件和元件之間,充分的解耦、靈活,整個系統鬆散且牢固。
services元件與bns不同的一點,bns的節點是自己指定了name和後端的關聯關係,而services是根據pod上的標籤(label)自動生成的,更靈活。ali的group就更別提了,group是隸屬於app的,擴充套件性方面更弱一些。
上文說在建立service的時候,系統為service分配了一個叢集虛IP和埠,服務使用方通過這個vip:port來訪問真實的服務提供方。這裡的vip就是kube-proxy
提供出來的。
虛ip和service proxies
kube-proxy的模式
- userspace: client -> iptables -> kube-proxy -> backend pod(rr), iptables只是把虛ip轉換成kube-proxy的ip,通過kube-proxy自己維護的不同埠來輪詢轉發到後端的pod上。
- iptables: client -> iptables -> backend pod(random),kube-proxy只是監聽master上service的建立,之後動態新增/刪除本機上的iptables規則
- ipvs: client -> ipvs ->backend pod, ipvs是一個核心模組
服務發現
服務使用方如何找到我們定義的Service? 在k8s中用了兩個方案,環境變數 && DNS。
-
環境變數
每當有service被創建出來之後,各個node(宿主機)上的kubelet,就會把service name加到自己宿主機的環境變數中,供所有Pod使用。環境變數的命名規則是{SERVICE_NAME}_SERVICE_HOST, ${SERVICE_NAME}SERVICE_PORT,其中SERVICE_NAME是新serviceName的大寫形式,serviceName中的橫槓-會被替換成下劃線.
使用環境變數有一個隱含的建立順序,即服務使用方在通過環境變數訪問一個service的時候,這個service必須已經存在了。
這麼簡單粗暴的方案...這樣做有個好處,就是省的自己搞名字解析服務,相當於本地的agent做了“域名劫持”。serviceName對應到上文提到的,由kube-proxy
提供的vip:port
上。 -
DNS
這是官方不推薦的做法,推薦用來跟k8s的外部服務進行互動,ExternalName.
Headless services
在建立service的時候,使用者可以給spec.clusterIp指定一個特定的ip。前提是這個ip需要是一個合法的IP,並且要在apiServer定義的service-cluster-ip-range範圍內。
當然,如果使用者有自己的服務發現服務,也可以不用k8s原生的service服務,這時,需要顯式地給spec.clusterIp設定None,這樣k8s就不會給service分配clusterIp了。
如果使用者將spec.cluster設定為None,但指定了selector,那麼endpoints controller還是會建立Endpoints
的,會建立一個新的DNS記錄直接指向這個service描述的後端pod。否則,不會建立Endpoints
記錄。 // 這塊沒看懂
釋出服務,服務型別
- ClusterIp: 預設配置,給service分配一個叢集內部IP,k8s叢集外部不識別。
- NodePort:宿主機埠,叢集外部的服務也訪問,使用nodeIp:nodePort
- LoadBalancer:通過雲負載均衡,將service暴露出去 // 沒理解
- ExternalName:將serviceName與externalName繫結,讓外網可以訪問到。要求
kube-dns
的版本>= 1.7