分享的內容包括
- k8s demo部署示例
- k8s基本架構和原理
- k8s資源物件
- k8s網路模型
- 總結
k8s就像圖中的貨船,管理各種集裝箱(容器)
一. k8s demo部署示例
說明
- 先通過一個hello world程式直觀感受一下k8s
- 程式部署在宿主機,容器和k8s三種環境,對比他們的差異
- 程式碼大致是這個樣子
@RestController public class K8sDemoController { @GetMapping("/hello") public String hello(){ return "hello k8s demo."; } } 複製程式碼
1. 宿主機上如何執行
- mvn編譯程式碼打成jar包
- 執行
java -jar k8s-demo.jar &
- 瀏覽器中輸入 http://10.1.69.101:8080/hello 地址訪問服務
2. Docker容器上如何執行
- mvn編譯程式碼打成jar包
- 將jar包打成docker映象
- 執行
docker run --name k8s-demo -d -p 8080:8080 k8s-demo:0.0.1-SNAPSHOT
- 瀏覽器中輸入 http://10.1.69.101:8080/hello 地址訪問服務
3. 在k8s中如何執行
這一步大致感受一下yaml的樣子,不需要關心指令碼的細節,後面介紹資源時會細說。只需要有個大體的印象,部署一個k8s服務的基本流程。
- mvn編譯程式碼打成jar包
- 將jar包打成docker映象
- 構建一個執行服務的yaml檔案
- 執行kubectl apply -f k8s-demo.yaml
- 瀏覽器中輸入 www.k8s-demo.com/hello 地址訪問服務
-
yaml檔案內容
# 檔名為k8s-demo.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: k8s-demo
namespace: spring-test
spec:
replicas: 3
template:
metadata:
labels:
app: k8s-demo
spec:
containers:
- name: k8s-demo
image: k8s-demo:0.0.1-SNAPSHOT
ports:
- containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: k8s-demo
namespace: spring-test
spec:
type: NodePort
selector:
app: k8s-demo
ports:
- protocol: TCP
port: 8888
targetPort: 8080
nodePort: 30003
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: k8s-demo
namespace: spring-test
spec:
rules:
- host: www.k8s-demo.com
http:
paths:
- path: /hello
backend:
serviceName: k8s-demo
servicePort: 8888
複製程式碼
4. 總結三種部署方式
為了減少部署複雜度,程式碼並沒有使用redis,只是例項圖增加了redis
5. 流程越來越複雜,為什麼不直接部署在宿主機?
5.1 容器比宿主機優勢在哪?
- 可移植性:容器提供了執行應用程式的基本包裝,可以在任何支援容器的雲上部署
- 高效率:啟動一個容器只需一個映象,且啟動時間非常短
- 隔離性:宿主機上往往安裝很多服務,且各自依賴不一樣。而一個容器只跑一個服務
- 版本控制:方便追溯不同版本差異,方便快速回滾,只需替換映象版本,無需宿主機上一套複雜的流程
- 低成本:小巧輕便,不需要像宿主機或虛擬機器一樣佔用很多資源
5.2 k8s的出現又解決了容器的什麼問題?
- 自動編排排程:大量容器劇增後,如何管理、如何排程的問題
- 分散式解決方案:節點可水平擴充套件,容器可方便擴縮容
- 自愈能力:故障自動發現,並進行自我修復
我們說容器實現了單個應用程式的基本包裝實現可移植。上圖中,宿主機部署的方式如果加上一個nginx做反向代理,就和k8s中ingress的部署方式是一樣的。也就是k8s實現了整套分散式應用的可移植
二. 基本架構和原理
1. Master節點的元件
apiServer
- 提供資源操作的唯一入口,提供api註冊、發現、認證、訪問控制等功能
etcd
- 一個key-value資料庫
- 儲存整個機器的狀態
controller-manager
- 負責維護機器狀態,比如:自動擴容、故障檢查、滾動更新
- 實現叢集自動化的關鍵元件
scheduler
- 負責資源排程
- 將未分配節點的pod排程到合適的節點上
2. Node節點的元件
kubelet
- 負責容器生命週期管理,比如:建立、刪除
- 同時負責Volume,網路的管理
kube-proxy
- 負責為Service提供負載均衡、服務發現
Container Runtime
- 容器執行環境
- 預設是Docker,同時還支援其他容器引擎
三. 資源物件
概述
- k8s中大部分概念,如Node,Pod,Service都可以看做一種資源物件
- 資源的描述:yaml檔案或json檔案
- 資源的操作:物件可以通過kubectl(或者api)執行增、刪、改、查
- 資源的儲存:資訊在etcd中持久化
k8s通過對比資源的“實際狀態”和etcd中的“期望狀態”,實現自動化控制
1. Pod
- Pod是k8s中最重要最基本的資源
- pod是在容器之外又封裝的一層概念
- pod是容器排程的基本單元(不是docker容器)
- 每個pod包含一個特殊的根容器:Pause容器,和一個或多個業務容器
- 每個pod有唯一的ip,pod內的容器可通過localhost通訊
為什麼要新增pod這個概念?
- 一組容器作為一個單元,很難判斷整體狀態,以及對整體進行管控。新增業務無關的pause容器,用於管控整體
- 簡化了關聯容器通訊和共享的問題
2. Deployment
- 實現Pod自動編排:建立、刪除、擴容、縮容
- 通過replicas控制pod數量,template控制要建立的pod的模板
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: k8s-demo
namespace: spring-test
spec:
replicas: 3
template:
metadata:
labels:
app: k8s-demo
spec:
containers:
- name: k8s-demo
image: k8s-demo:0.0.1-SNAPSHOT
ports:
- containerPort: 8080
複製程式碼
3. Service
- pod異常時,可能會被排程到另一臺機器,導致pod的ip改變,使用ip訪問服務不可靠
3.1概述
- k8s裡最核心的資源之一,類似微服務架構中的“微服務”
- 前端應用通過入口地址訪問服務,服務通過label對接到後端的pod,即使pod的ip變了
- kube-proxy負責把service請求轉發到後端,並做負載均衡
- service整個生命週期內,ClusterIp不會變,對外提供的服務地址也就不會變
apiVersion: v1
kind: Service
metadata:
name: k8s-demo
namespace: spring-test
spec:
type: NodePort
selector:
app: k8s-demo
ports:
- protocol: TCP
port: 8888
targetPort: 8080
nodePort: 30003
複製程式碼
4. Ingress
service提供了ip:port的訪問方式,即工作在tcp/ip層,而http服務需要將不同的url對應到不同的後端服務,service是無法實現這一功能的。
- Ingress提供http層的負載分發功能
- Ingress可以實現不同的請求,分發到不同的後端服務
- Ingress定義後,需要結合Ingress Controller,才能形成完整的功能
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: k8s-demo
namespace: spring-test
spec:
rules:
- host: www.k8s-demo.com
http:
paths:
- path: /hello
backend:
serviceName: k8s-demo
servicePort: 8888
複製程式碼
4.1 Ingress Controller定義
- 可以使用公有云提供的Ingress Controller
- 也可以使用google提供的Ingress Controller,以pod形式執行,功能如下:
- 監聽apiserver,獲取ingress的定義
- 基於ingress定義,生成nginx的配置檔案的內容
- 執行nginx -s reload,重新載入配置
4.2 Ingress定義
- 建立型別為Ingress的yaml檔案
- 配置spec.rules,指定hostname中url和service的對應關係
四. k8s網路模型
前面hello world程式中,對於如何訪問到服務,有必要了解一下k8s的網路模型,在這之前先介紹docker的網路模型
1. Docker網路模型
- docker第一次啟動時,會建立虛擬網橋docker0
- 為docker0分配一個子網
- docker建立每個容器時,會建立veth裝置對,一端關聯到網橋上,另一端使用linux的網路名稱空間技術連線到容器內,並給容器內eth0裝置分配一個ip地址
2. Docker網路的侷限性
- Docker網路模型沒有考慮到多主機互聯的網路解決方案,崇尚簡單為美
- 同一機器內的容器之間可以直接通訊,但是不同機器之間的容器無法通訊
- 為了跨節點通訊,必須在主機的地址上分配埠,通過埠路由或代理到容器
- 分配和管理容器特別困難,特別是水平擴充套件時
3. k8s網路模型概述
3.1 k8s網路模型的原則:
- 每個pod都擁有唯一個獨立的ip地址,稱IP-Per-Pod模型
- 所有pod都在一個可連通的網路環境中
- 不管是否在同一個node,都可以通過ip直接通訊
- pod被看作一臺獨立的物理機或虛擬機器
目前原生docker和kubernetes還不能打通多節點容器與容器的通訊,要支援該模型,必須依靠第三方網路外掛實現,比如:flannel
3.2 設計這個原則的原因:
- 使用者不需要額外考慮如何建立pod之間的連線
- 使用者不需要考慮將容器埠對映到主機埠的問題
- 可以相容過去跑在宿主機和KVM的應用
3.3 IP-Per-Pod與Docker埠對映的區別
- docker埠對映到宿主機會引入埠管理的複雜性
- docker最終被訪問的ip和埠,與提供的不一致,引起配置的複雜性
4. k8s網路模型詳解
4.1 容器與容器的通訊
- 同一個容器的pod直接共享同一個linux協議棧
- 就像在同一臺機器上,可通過localhost訪問
- 可類比一個物理機上不同應用程式的情況
4.2 pod與pod的通訊
同一Node內的pod之間通訊
- 同一Node內的pod都是通過veth連線在同一個docker0網橋上,地址段相同,所以可以直接通訊
不同Node的pod之間通訊
- docker0網段與宿主機不在同一個網段,所以不同pod之間的pod不能直接通訊
- 不同node之間通訊只能通過宿主機物理網路卡
- 前面說過k8s網路模型需要不同的pod之間能通訊,所以ip不能重複,這就要求k8s部署時要規劃好docker0的網段
- 同時,要記錄每個pod的ip地址掛在哪個具體的node上
- 為了達到這個目的,有很多開源軟體增強了docker和k8s的網路
4. 開源網路元件Flannel
4.1 實現的功能
- 協助k8s給每個Node上的docker容器分配互不衝突的ip地址
- 能在這些ip地址之間建立覆蓋網路(Overlay Network),將資料傳遞到目標容器
4.2 底層原理
- Flannel建立名為flannel0的網橋
- flannel0網橋一端連線docker0網橋,另一端連線flanneld程式
- flanneld程式一端連線etcd,利用etcd管理分配的ip地址資源,同時監控pod地址,建立pod節點路由表
- flanneld程式一端連線docker0和物理網路,配合路由表,完成資料包投遞,完成pod之間通訊
4.3 缺點
- 引入多個網路元件,帶來網路時延和損耗
- 預設使用udp作為底層傳輸協議,具有不可靠性
五. 總結
- 本文先通過一個demo,部署在不同的環境中,直觀感受了如何使用k8s。這個過程我們需要思考k8s誕生解決了什麼問題?核心的提供了一個平臺,主要負責容器的自動排程和編排
- 對k8s有直觀感受後,介紹了下k8s的基本架構。各個元件是如何互動的,在使用k8s過程中不斷回想架構圖,能加深對k8s的瞭解
- 掌握了基本架構後,想要將服務部署到k8s中,需要對常用的資源物件有一定了解,因此接著介紹了主要的資源物件。k8s中其他的資源物件可類比學習
- 當你把一個服務部署到k8s之後,如何做驗證呢,如何暴露你的服務呢?需要對k8s的網路模型有一定了解,才能真正掌握它暴露服務的方式。因此最後部分著重介紹了k8s的網路模型
參考
- 《kubernetes權威指南》