在容器化時代,雖然Docker已經很強大了,但是在實際使用上還是有諸多不便,比如集群管理、資源調度、文件管理等等。
不過目前也涌現了很多解決方案,比如 Mesos、Swarm、Kubernetes 等等,其中谷歌開源的 Kubernetes就是其中的王者!
它是基于容器的集群編排引擎,具備擴展集群、滾動升級回滾、彈性伸縮、自動治愈、服務發現等多種特性能力。
學習K8s,需要了解K8s常見的應用場景和核心概念。內容豐富,建議收藏!!
一、K8s集群架構及核心概念
1.1 架構圖

1.2 K8s組件
api service
所有服務訪問統一入口。對外暴露K8s的api接口,是外界進行資源操作的唯一入口,并提供認證、授權、訪問控制、API注冊和發現等機制。
controller manager
負責維護集群的狀態,比如故障檢測、自動擴展、滾動更新等,他們是處理集群中常規任務的后臺線程。
scheduler
負責資源的調度,按照預定的調度策略將Pod調度到響應的機器上;就是監視新創建的Pod,如果沒有分配節點,就選擇一個節點供他們運行,這就是Pod的調度。
etcd
一個可信賴的分布式鍵值存儲服務,能夠為整個分布式集群存儲一些關鍵數據,協助分布式集群運轉。儲存K8s集群所有重要信息(持久化)。
v2版本是基于內存的存儲,v3開始才是序列化到介質。
新版K8s(v1.11以上)已經改用v3版本的etcd。
kubelet
直接跟容器引擎交互實現容器的生命周期管理。
kube-proxy
負責寫入規則至iptables、ipvs實現服務映射訪問。
其中scheduler和controller-manager兩個組件是有leader選舉的,這個選舉機制是K8s對于這兩個組件的高可用保障。Api server是可以水平擴展的。
其他重要插件:
CoreDNS
可以為集群中的SVC創建一個域名IP的對應解析關系。
Dashboard
給K8s集群提供一個B/S結構訪問體系。
Ingress Controller
官方只能實現四層代理,ingress可以實現七層代理。
Federation
提供一個可以跨集群中心多K8s統一管理功能。
Prometheus
提供K8s集群的監控能力。
ELK
提供K8s集群日志統一分析介入平臺。
1.3 K8s資源
K8S中的所有內容都抽象為資源,資源實例化后叫做對象。
在K8S中,一般使用yaml格式的文件來創建符合我們預期的Pod,這種yaml文件一般被稱為資源清單。
K8s中的資源可分為三種:
#1 名稱空間級資源
通過:
kubectl api-resources --namespaced=true查看全部。
K8S是有空間概念的,以下資源是歸屬于某個空間的(沒有指定就是默認空間default)。
同時也可以根據功能分為以下集中:
工作負載型
Pod、ReplicaSet(Replication
Controller在v1.11版本廢棄)、Deployment、StatefulSet、DaemonSet、Job、CronJob
服務發現及負載均衡型
Service、Ingress等
配置與存儲型
Volume、CSI(容器存儲接口,可以擴展各種各樣的第三方存儲卷)
特殊類型的存儲卷
ConfigMap(當做配置中心來使用的資源類型)、Secret(保存敏感數據)、DownwardAPI(把外部環境中的信息輸出給容器)
#2 集群級資源
通過:、
kubectl api-resources --namespaced=false 查看全部。
Namespace、Node、Role、ClusterRole、RoleBinding、ClusterRoleBinding
#3 元數據型資源
HPA、PodTemplate、LimitRange
資源清單常用字段:
yaml文件是K8s資源聲明表達的格式。各種資源通用的常用字段如下:


1.4 工作負載
Node
K8s中節點(node)指的是一個工作機器,曾經叫做minion。
不同的集群中,節點可能是虛擬機,也可能是物理機。每個節點都由master組件管理,并包含了運行Pod(容器組)所需的服務。
這些服務包括:容器引擎、kubelet、kube-proxy。
kubectl get nodes -o wide 查看所有節點的列表。
kubectl describe node 查看節點狀態以及節點的其他詳細信息。
輸出的節點狀態包括以下信息:
Addersses
包括內外部IP、hostname等信息。
Conditions
描述了節點的硬盤、內存、進程數等壓力狀態。
Capacity and Allocatable
描述節點上的可用資源如CPU、內存、該節點可調度的最大pod數等情況。
Info
描述了節點的基本信息,如Linux內核版本、Kubernetes版本、Docker版本、操作系統名稱等。
1.5 節點管理
與Pod和Service不一樣,節點并不是由Kubernetes創建的,節點由云供應商創建。
向Kubernetes中創建節點時,僅僅是創建了一個描述該節點的API對象。
節點API對象創建成功后,Kubernetes將檢查該節點是否有效。
例如,假設創建如下節點信息:

Kubernetes在APIServer上創建一個節點API對象(節點的描述),并且基于metadata.name字段對節點進行健康檢查。
如果節點有效,則可以向該節點調度Pod;否則,該節點API對象將被忽略,直到節點變為有效狀態。
1.6 節點控制器(Node Controller)
這是一個負責管理節點的Kubernetes master組件。在節點的生命周期中,節點控制器起到了許多作用:
-
在注冊節點時為節點分配CIDR地址塊。
-
通過云供應商(cloud-controller-manager)接口,檢查節點列表中每一個節點對象對應的虛擬機是否可用。在云環境中,只要節點狀態異常,節點控制器檢查其虛擬機在云供應商的狀態,如果虛擬機不可用,自動將節點對象從APIServer中刪除。
-
監控節點的健康狀況。當節點變得不可觸達時(例如,由于節點已停機,節點控制器不再收到來自節點的心跳新號),節點控制器將節點API對象的NodeStatusCondition取值從NodeReady更新為Unknown;然后在等待pod-eviction-timeout時間后,將節點上的所有Pod從節點驅逐。
節點自注冊(Self-Registration
如果Kubelet的啟動參數–register-node為true(默認為true),kubelet會嘗試將自己注冊到APIServer。
Kubelet自行注冊時,將使用以下選項:
–kubeconfig:向apiserver進行認證時所用身份信息的路徑
–cloud-provider:向云供應商讀取節點自身元數據
–register-node:自動向API Server祖冊節點
–register-with-taints:注冊節點時,為節點添加污點(逗號分隔,格式為=:)
–node-ip:節點的IP地址
–node-labels:注冊節點時,為節點添加標簽
–node-status-update-frequency:向master節點發送心跳信息的時間間隔
如果 Node authorization mode (opens new window)和 NodeRestriction admission plugin (opens new window)被啟用,kubelet 只擁有創建/修改其自身所對應的節點 API 對象的權限。
1.7 Pod
Pod(容器組)是 Kubernetes 中最小的可部署單元。
一個 Pod(容器組)包含了一個應用程序容器(某些情況下是多個容器)、存儲資源、一個唯一的網絡 IP 地址、以及一些確定容器該如何運行的選項。
Pod 容器組代表了 Kubernetes 中一個獨立的應用程序運行實例,該實例可能由單個容器或者幾個緊耦合在一起的容器組成。
一個Pod是容器環境下的“邏輯主機”,它可能包含一個或者多個緊密相連的應用,這些應用可能是在同一個物理主機或虛擬機上。
Pod 的context可以理解成多個linux命名空間的聯合:
PID 命名空間
同一個Pod中應用可以看到其它進程。
網絡 命名空間
同一個Pod的中的應用對相同的IP地址和端口有權限。
IPC 命名空間
同一個Pod中的應用可以通過VPC或者POSIX進行通信。
UTS 命名空間
同一個Pod中的應用共享一個主機名稱。
注:同一個Pod中的應用可以共享磁盤,磁盤是Pod級的,應用可以通過文件系統調用。
Pod創建流程
創建Pod的整個流程,時序圖如下:

注:
1、用戶提交創建Pod的請求,可以通過API Server的REST API ,也可用Kubectl命令行工具,支持Json和Yaml兩種格式。
2、API Server 處理用戶請求,存儲Pod數據到Etcd。
3、Schedule通過和 API Server的watch機制,查看到新的pod,嘗試為Pod綁定Node。調度器用一組規則過濾掉不符合要求的主機,比如Pod指定了所需要的資源,那么就要過濾掉資源不夠的主機,對上一步篩選出的符合要求的主機進行打分,在主機打分階段,調度器會考慮一些整體優化策略進行調度;選擇打分最高的主機,進行binding操作,結果存儲到Etcd中。
4、kubelet根據調度結果執行Pod創建操作。綁定成功后,會啟動container。scheduler會調用API Server的API在etcd中創建一個bound pod對象,描述在一個工作節點上綁定運行的所有pod信息。運行在每個工作節點上的kubelet也會定期與etcd同步bound pod信息。
Pod的定義
可以通過kubectl explain pod 查看pod yaml定義的頂級屬性,然后通過類似 kubectl explain pod.{頂級屬性}查看二級屬性的細節定義。
下面給出一個pod資源定義的樣例,對pod的描述有一個初步了解。
apiVersion: v1 #與k8s集群版本有關,使用 kubectl api-versions 即可查看當前集群支持的版本
kind: Pod #該配置的類型,我們使用的是Pod
metadata: #譯名為元數據,即 Pod 的一些基本屬性和信息
name: nginx-pod #Pod 的名稱
labels: #標簽,可以靈活定位一個或多個資源,其中key和value均可自定義,可以定義多組,目前不需要理解
app: nginx #為該Deployment設置key為app,value為nginx的標簽
spec: #期望Pod實現的功能(即在pod中部署)
containers: #生成container,與docker中的container是同一種
- name: nginx #container的名稱
image: nginx:1.7.9 #使用鏡像nginx:1.7.9創建container,該container默認80端口可訪問
保存為nginx-pod.yaml文件,通過kubectl apply -f nginx-pod.yaml啟動pod。通過kubectl get pods -o wide查看pod的啟動情況。

二、服務發現、負載均衡、網絡
Kubernetes 中 Service 是一個 API 對象,通過 kubectl + YAML 定義一個 Service,可以將符合 Service 指定條件的 Pod ,作為可通過網絡訪問的服務提供給服務調用者。
Service 是 Kubernetes 中的一種服務發現機制:
Pod 有自己的 IP 地址
Service 被賦予一個唯一的 dns name
Service 通過 label selector 選定一組 Pod
Service 實現負載均衡,可將請求均衡分發到選定這一組 Pod 中
例如,假設您有一組 Pod:
每個 Pod 都監聽 9376 TCP 端口
每個 Pod 都有標簽 app=MyApp
下面文件可用來創建一個 Service(名字為 my-service):
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app: MyApp
ports:
- protocol: TCP
port: 80
targetPort: 9376
Service 從自己的 IP 地址和 port 端口接收請求,并將請求映射到符合條件的 Pod 的 targetPort。
為了方便,默認 targetPort 的取值 與 port 字段相同
Kubernetes 將為該 Service 分配一個 IP 地址(ClusterIP 或 集群內 IP),供 Service Proxy 使用
Kubernetes 將不斷掃描符合該 selector 的 Pod,并將最新的結果更新到與 Service 同名 my-service 的 Endpoint 對象中
2.1 服務代理
Kubernetes 集群中的每個節點都運行了一個 kube-proxy。
負責為 Service(ExternalName 類型的除外)提供虛擬 IP 訪問。
Kubernetes 支持三種 proxy mode(代理模式),他們的版本兼容性如下:

User space 代理模式
kube-proxy 監聽 kubernetes master以獲得添加和移除 Service / Endpoint 的事件
kube-proxy 在其所在的節點(每個節點都有 kube-proxy)上為每一個 Service 打開一個隨機端口
kube-proxy 安裝 iptables 規則,將發送到該 Service 的 ClusterIP(虛擬 IP)/ Port 的請求重定向到該隨機端口
任何發送到該隨機端口的請求將被代理轉發到該 Service 的后端 Pod 上(kube-proxy 從 Endpoint 信息中獲得可用 Pod)
kube-proxy 在決定將請求轉發到后端哪一個 Pod 時,默認使用 round-robin(輪詢)算法,并會考慮到 Service 中的 SessionAffinity 的設定
如下圖所示:

Iptables 代理模式–默認模式
kube-proxy 監聽 kubernetes master 以獲得添加和移除 Service / Endpoint 的事件
kube-proxy 在其所在的節點(每個節點都有 kube-proxy)上為每一個 Service 安裝 iptable 規則
iptables 將發送到 Service 的 ClusterIP / Port 的請求重定向到 Service 的后端 Pod 上
對于 Service 中的每一個 Endpoint,kube-proxy 安裝一個 iptable 規則。默認情況下,kube-proxy 隨機選擇一個 Service 的后端 Pod
如下圖所示:

iptables proxy mode 的優點:
更低的系統開銷
在 linux netfilter 處理請求,無需在 userspace 和 kernel space 之間切換
更穩定
與 user space mode 的差異:
使用 iptables mode 時
如果第一個 Pod 沒有響應,則創建連接失敗
使用 user space mode 時
如果第一個 Pod 沒有響應,kube-proxy 會自動嘗試連接另外一個后端 Pod
您可以配置 Pod 就緒檢查(readiness probe)確保后端 Pod 正常工作。
此時,在 iptables 模式下 kube-proxy 將只使用健康的后端 Pod,從而避免了 kube-proxy 將請求轉發到已經存在問題的 Pod 上。
IPVS 代理模式
kube-proxy 監聽 kubernetes master 以獲得添加和移除 Service / Endpoint 的事件
kube-proxy 根據監聽到的事件,調用 netlink 接口,創建 IPVS 規則;并且將 Service/Endpoint 的變化同步到 IPVS 規則中
當訪問一個 Service 時,IPVS 將請求重定向到后端 Pod
如下圖所示:

IPVS proxy mode 基于 netfilter 的 hook 功能,與 iptables 代理模式相似,但是 IPVS 代理模式使用 hash table 作為底層的數據結構,并在 kernel space 運作。
這就意味著:
IPVS 代理模式可以比 iptables 代理模式有更低的網絡延遲,在同步代理規則時,也有更高的效率
與 user space 代理模式 / iptables 代理模式相比,IPVS 模式可以支持更大的網絡流量
IPVS 提供更多的負載均衡選項:
rr: round-robin
lc: least connection (最小打開的連接數)
dh: destination hashing
sh: source hashing
sed: shortest expected delay
nq: never queue
在所有的代理模式中,發送到 Service 的 IP:Port 的請求,將被轉發到一個合適的后端 Pod,而無需調用者知道任何關于 Kubernetes/Service/Pods 的細節。
2.2 服務發現–DNS
安裝了K8s插件-- DNS 服務,Core DNS (opens new window)。
Kubernetes 集群中就會運行一組 DNS Pod,配置了對應的 Service,并由 kubelete 將 DNS Service 的 IP 地址配置到節點上的容器中以便解析 DNS names。
CoreDNS 監聽 Kubernetes API 上創建和刪除 Service 的事件,并為每一個 Service 創建一條 DNS 記錄。
集群中所有的 Pod 都可以使用 DNS Name 解析到 Service 的 IP 地址。
例如:
名稱空間 my-ns 中的 Service my-service,將對應一條 DNS 記錄 my-service.my-ns。
名稱空間 my-ns 中的Pod可以直接 nslookup my-service (my-service.my-ns 也可以)。
其他名稱空間的 Pod 必須使用 my-service.my-ns。my-service 和 my-service.my-ns 都將被解析到 Service 的 Cluster IP。
Service 類型
Kubernetes 中可以通過不同方式發布 Service,通過 ServiceType 字段指定,該字段的默認值是 ClusterIP。
可選值有:
ClusterIP: 默認值。
通過集群內部的一個Cluster IP 地址暴露 Service,只在集群內部可以訪問。
NodePort
通過每一個節點上的的靜態端口(NodePort)暴露 Service,同時自動創建 ClusterIP 類型的訪問方式。
在集群內部通過(ClusterIP):(Port) 訪問,
在集群外部通過 (NodeIP):(NodePort) 訪問。
LoadBalancer
通過云服務供應商(AWS、Azure、GCE 等)的負載均衡器在集群外部暴露 Service,同時自動創建 NodePort 和 ClusterIP 類型的訪問方式。LoadBalancer是將 .spec.type 字段設置為 LoadBalancer,Kubernetes 將為該Service 自動創建一個負載均衡器。
負載均衡器的創建操作異步完成,您可能要稍等片刻才能真正完成創建,負載均衡器的信息將被回寫到 Service 的.status.loadBalancer 字段。
在集群內部通過 (ClusterIP):(Port) 訪問
在集群外部通過 (NodeIP):(NodePort) 訪問
在集群外部通過 (LoadBalancerIP):(Port) 訪問。
ExternalName
將 Service 映射到 externalName 指定的地址(例如:http://foo.bar.example.com),返回值是一個 CNAME 記錄。不使用任何代理機制。ExternalName 類型的 Service 映射到一個外部的 DNS name,而不是一個 pod label selector。可通過 spec.externalName 字段指定外部 DNS name。
External IP
如果有外部 IP 路由到 Kubernetes 集群的一個或多個節點,Kubernetes Service 可以通過這些 externalIPs 進行訪問。externalIP 需要由集群管理員在 Kubernetes 之外配置。
在 Service 的定義中, externalIPs 可以和任何類型的 .spec.type 一通使用。
Ingress
Ingress 是 Kubernetes 的一種 API 對象,將集群內部的 Service 通過 HTTP/HTTPS 方式暴露到集群外部,并通過規則定義 HTTP/HTTPS 的路由。
Ingress 具備如下特性:集群外部可訪問的 URL、負載均衡、SSL Termination、按域名路由(name-based virtual hosting)。
Ingress 的例子如下所示:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-ingress-for-nginx # Ingress 的名字,僅用于標識
spec:
rules: # Ingress 中定義 L7 路由規則
- host: demo.my-company.com # 根據 virtual hostname 進行路由(請使用您自己的域名)
http:
paths: # 按路徑進行路由- path: /
backend:
serviceName: nginx-service # 指定后端的 Service 為之前創建的 nginx-service
servicePort: 80
- path: /
Ingress Controller 通常是部署在集群中的一個 Deployment,并且通過 NodePort Service 暴露自己的端口,使得用戶可以在集群外通過其 NodePort 訪問到 Ingress Controller。
假設該端口為 32351,并且 http://demo.my-company.com 這個域名被解析到集群中某一個節點的 IP(或者被配置到瀏覽器所在機器的 hosts 文件),則當用戶在瀏覽器中輸入地址 http://demo.my-company.com:32351 時:
請求被轉發到集群某節點的 32351 節點端口。
根據 Service 的定義,請求被轉發到 Ingress Controller 的 Web 端口。
Ingress Controller 根據請求域名 http://demo.my-company.com 以及請求路徑,匹配到 Ingress 定義中該請求應該被路由到 nginx-service 的 80 端口。
Ingress Controller 執行 L7 路由轉發,將請求發送到 nginx-service 的 80 端口。
2.3 K8s網絡
容器網絡
容器網絡是容器選擇連接到其他容器、主機和外部網絡(如Internet)的機制。
CNI意為容器網絡接口,它是一種標準的設計,為了讓用戶在容器創建或銷毀時都能夠更容易地配置容器網絡。
目前最流行的CNI插件是Flannel。Flannel插件既可以確保滿足Kubernetes的網絡要求,又能為Kubernetes集群管理員提供他們所需的某些特定的網絡功能。
容器的Runtime提供了各種網絡模式,每種模式都會產生不同的體驗。
例如,Docker默認情況下可以為容器配置以下網絡:
none
將容器添加到一個容器專門的網絡堆棧中,沒有對外連接。
host
將容器添加到主機的網絡堆棧中,沒有隔離。
default bridge
默認網絡模式。每個容器可以通過IP地址相互連接。
自定義網橋
用戶定義的網橋,具有更多的靈活性、隔離性和其他便利功能。
Docker還可以讓用戶通過其他驅動程序和插件,來配置更高級的網絡(包括多主機覆蓋網絡)。
Flannel插件
Flannel是CoreOs團隊針對kubernetes設計的一個網絡規劃服務。
簡單來說,它的功能是讓集群中的不同節點主機創建的Docker容器都具有全集群唯一的虛擬IP地址。
而且它還能在這些IP之間建立一個覆蓋網絡(Overlay Network),通過這個覆蓋網絡,將數據包原封不動地傳遞到目標容器內。
Flannel監控ETCD中每個Pod的實際地址,并在內存中建立維護Pod節點路由表。

Kubernetes的網絡模型假設了所有Pod都在一個可以直接連通的扁平的網絡空間中,kubernetes假定這個網絡已經存在。
Pod之間的通信存在以下三種情況,對應的通信方式如下:
同一個Pod內的多個容器之間。這個是通過lo回路在機器內尋址
當兩個Pod在同一個節點主機。由網橋直接轉發至對應Pod,不用經過Flannel。
當兩個Pod在不同的節點主機,不同Node之間的通信智能通過宿主機的物理網卡進行。
Pod至Service的網絡,目前基于性能考慮,采用的是LVS方式維護和轉發。
Pod到外網:Pod向外網發送請求,查找路由表,轉發數據包到宿主機的網卡,宿主網卡完成路由選擇后,iptable執行Masquerade,把源IP更改為宿主機網卡的IP,然后向外網服務器發送請求。
外網訪問Pod:是通過Service找到對應的Pod,通過網絡轉換為對應的pod網絡Ip再定位到具體的Pod。