K8s中的Pod和容器設(shè)計模式
作者 | 中國移動云能力中心PaaS產(chǎn)品部 仇明
?近些年來,容器技術(shù)迅速席卷全球,顛覆了應(yīng)用的開發(fā)、交付和運行模式。容器技術(shù)作為云原生技術(shù)領(lǐng)域的技術(shù)基石,也是現(xiàn)今最熱門的一種服務(wù)器端技術(shù)。容器以及容器編排技術(shù)成為基礎(chǔ)設(shè)施領(lǐng)域最炙手可熱的關(guān)鍵詞,隨著容器及周邊生態(tài)技術(shù)的蓬勃發(fā)展,容器社區(qū)當仁不讓成為開源社區(qū)最活躍的生態(tài)圈之一。同時,以容器技術(shù)為核心的容器生態(tài)圈在云計算、互聯(lián)網(wǎng)等領(lǐng)域得到了廣泛應(yīng)用。
01容器的概念
早在2000年,容器技術(shù)的概念就已經(jīng)出現(xiàn),當初是用在chroot環(huán)境中做進程隔離。2013年,Docker項目正式發(fā)布,讓Linux容器技術(shù)逐步席卷天下。2014 年,Kubernetes項目正式發(fā)布,容器技術(shù)開始和編排系統(tǒng)齊頭并進。
容器的本質(zhì),是一個試圖被隔離,資源受限制的進程。所使用的隔離技術(shù)都是基于Linux內(nèi)核提供,其中namespace用來做資源隔離,cgroups用來做資源限制(CPU、內(nèi)存、存儲、網(wǎng)絡(luò)的使用限制)。隔離技術(shù)中不得不提的是虛擬機。通過如下的虛擬機和容器的對比圖,可以看出,虛擬機工作原理中最重要的是Hypervisor層——進行硬件虛擬化功能,模擬出運行操作系統(tǒng)需要的各種硬件(如 CPU、內(nèi)存、I/O 設(shè)備等),然后,在這些虛擬硬件上安裝操作系統(tǒng)Guest OS。而容器沒有自己的OS,通過Container Daemon直接共享宿主機內(nèi)核,所有對容器進程的限制都是基于宿主機操作系統(tǒng)本身能力進行。故而,容器也顯得很輕量化。
02Pod概念
2.1 操作系統(tǒng)中的例子
首先,在虛擬機中運行一個簡單的java程序,然后通過pstree指令,將會看到這個java程序?qū)嶋H上是由一組(linux中)線程組成,共享資源,共同協(xié)作完成java程序的業(yè)務(wù)工作。
2.2 Pod實現(xiàn)原理
在被譽為云時代的操作系統(tǒng)Kubernetes 中,容器被類比為了進程,那么Pod呢?其實 Pod 只是一個抽象的邏輯概念,它可以類比為前面例子中的java程序一樣,是一組(一個或者多個)容器的集合,這些容器之間共享同一份網(wǎng)絡(luò)(Network Namespace)和存儲(Volume)等資源。
2.2.1 網(wǎng)絡(luò)共享
Pod中首先會創(chuàng)建一個基礎(chǔ)容器(Infra container),然后以join network namespace的方式,將其它容器都與基礎(chǔ)容器的網(wǎng)絡(luò)關(guān)聯(lián)起來,這樣就實現(xiàn)了pod內(nèi)容器間共享網(wǎng)絡(luò)資源的功能。所以基礎(chǔ)容器永遠都是Pod 里面第一個被創(chuàng)建的容器,等待其他容器的加入。Pod的生命周期只跟基礎(chǔ)容器一致?;A(chǔ)容器是一個非常小的鏡像,叫做 k8s.gcr.io/pause,大概 100~200KB 左右,是一個匯編語言寫的、永遠處于“暫?!睜顟B(tài)的容器。
2.2.2 存儲共享
Pod是通過Volume實現(xiàn)共享存儲的。Pod先綁定好Volume,然后Pod里的容器只要聲明掛載這個Volume,就可以共享這個Volume對應(yīng)的宿主機目錄,所有容器看到的這個Volume目錄內(nèi)容都是一樣的。以下yaml提供了單Pod內(nèi)啟兩個容器的示例。
apiVersion: v1
kind: Pod
metadata:
name: one-pod-two-container-demo
spec:
volumes: #Pod先綁定好Volume
- name: shared-logs
emptyDir: {}
containers:
- name: web-nginx
image: nginx
volumeMounts: #容器再掛載這個volume
- name: shared-logs
mountPath: /var/log/nginx
- name: sidecar-container
image: busybox
command: ["sh","-c","while true; do cat /var/log/nginx/access.log /var/log/nginx/error.log; sleep 30; done"]
volumeMounts: #容器再掛載這個volume
- name: shared-logs
mountPath: /var/log/nginx
03容器設(shè)計模式
Kubernetes 社區(qū)結(jié)合了Kubernetes 集群的微服務(wù)模型提出了一系列解決典型分布式系統(tǒng)問題的容器設(shè)計模式,主要可以分三大類:
- 單容器管理模式;
- 單節(jié)點多容器模式;
- 多節(jié)點多容器模式。
3.1 單容器管理模式
Kubernetes 容器設(shè)計模式中,單容器管理模式是最簡單的,單單啟動一個單容器微服務(wù)實例命令與Docker原生命令類似。
$ kubectl run nginx --image=nginx:latest -n qiuqiu
pod/nginx created
$ kubectl get all -n qiuqiu
NAME READY STATUS RESTARTS AGE
pod/nginx 1/1 Running 0 9s
3.2 單節(jié)點多容器模式
在秉持關(guān)注點分離原則(SOC)下,容器化應(yīng)用程序是容器之間的職責分離,每個容器都應(yīng)該只承擔它最擅長業(yè)務(wù),其他額外的任務(wù),應(yīng)該交給其他容器(Sidecar 容器),單節(jié)點多容器模式便是利用了Kubernetes 中Pod內(nèi)所有容器可以共享同一個存儲空間(Volume)和網(wǎng)絡(luò)地址空間(Network Namespace)特性,來充分體現(xiàn)SOC原則。
3.2.1 挎斗模式(Sidecar Pattern)
挎斗模式主要是利用在同一個Pod中的容器可以共享存儲的能力。如下圖應(yīng)用日志收集服務(wù)場景:對于應(yīng)用服務(wù)A(如:Nginx)來說,重心并非日志的持久化存儲與管理(access.log等),而運維人員有時會需要通過日志排查服務(wù)異常問題。因此,日志收集器服務(wù)是一個很有必要的輔助工具。秉持關(guān)注點分離原則(SOC),可以為應(yīng)用服務(wù)的Pod中加入一個日志收集器服務(wù)的Sidecar 容器,應(yīng)用服務(wù)容器作為主容器只需關(guān)注業(yè)務(wù),日志信息由Sidecar 容器進行收集處理。生產(chǎn)環(huán)境中可以使用Filebeat 或者 Logstash 等工具收集日志并分發(fā)給ElasticSearch集群??娑纺J较?,通常作為輔助工具的Sidecar 容器鏡像是標準化可重用的,應(yīng)用服務(wù)不需要每次都帶著輔助工具的執(zhí)行文件一起打包迭代升級。
以下yaml文件定義了一個Pod內(nèi),啟動2個容器(不包含基礎(chǔ)容器),并使用emptyDir卷作為共享存儲。Sidecar 容器通常在定義中排在第二位,因此在執(zhí)行 kubectl 命令時,默認目標是主容器。Nginx 服務(wù)作為主容器,產(chǎn)生的日志寫入共享存儲卷掛載的/var/log/nginx目錄。這樣可以阻止原本將Nginx 服務(wù)日志以容器標準化輸出形式輸出(即阻止被軟連接到/dev/stdout和/dev/stderr,輸出到了容器的前臺日志),并寫入access.log 與 error.log 文件。最后由Sidecar容器(busybox)進行相關(guān)日志處理。
apiVersion: v1
kind: Pod
metadata:
name: app-log-aggregation-server
spec:
volumes:
- name: shared-logs
emptyDir: {}
containers:
- name: nginx
image: nginx
volumeMounts:
- name: shared-logs
mountPath: /var/log/nginx
- name: sidecar-container
image: busybox
command: ["sh","-c","while true; do cat /var/log/nginx/access.log /var/log/nginx/error.log; sleep 30; done"]
volumeMounts:
- name: shared-logs
mountPath: /var/log/nginx
3.2.2 外交官模式(Ambassador Pattern)
外交官模式主要是利用同一個Pod中的容器可以共享網(wǎng)絡(luò)地址空間的特性。外交官模式中存在一個負責代理從應(yīng)用程序容器到其他服務(wù)的連接的邊車容器(SideCar container ),也稱為外交官容器。例如,幾乎所有應(yīng)用程序在某個階段都需要數(shù)據(jù)庫連接。針對應(yīng)用開發(fā)迭代中會存在多種環(huán)境數(shù)據(jù)庫——一個開發(fā)數(shù)據(jù)庫、一個測試數(shù)據(jù)庫和一個生產(chǎn)數(shù)據(jù)庫。在編寫應(yīng)用程序的 Pod 時,開發(fā)/實施人員需要可以通過環(huán)境變量或 configMap 更改數(shù)據(jù)庫連接配置項達到數(shù)據(jù)庫的切換。而這種情況下還可以通過外交官模式,根據(jù)運行的環(huán)境將數(shù)據(jù)庫連接代理到相應(yīng)的數(shù)據(jù)庫。開發(fā)/實施人員無需修改配置項(和使用開發(fā)環(huán)境數(shù)據(jù)庫配置項一樣),當部署到測試或生產(chǎn)環(huán)境時,外交官容器會檢測它在哪個環(huán)境上運行(例如反射方式),并連接到正確的數(shù)據(jù)庫上。
以下yaml給出一個Redis客戶端訪問不同環(huán)境Redis庫樣例。主容器是由redis客戶端服務(wù),外交官容器是基于malexer/twemproxy 鏡像啟動的容器,提供中繼鏈接Redis數(shù)據(jù)服務(wù)。需要說明的malexer/twemproxy 鏡像需要將Redis 實例以環(huán)境變量傳遞(13-15行),格式為address:port:weight。默認情況下,該容器偵聽端口 6380(其他高級配置可查閱github:https://github.com/malexer/docker-twemproxy)。
# Redis client yaml
apiVersion: v1
kind: Pod
metadata:
namespace: qiuqiu
name: ambassador-example
spec:
containers:
- name: redis-client
image: redis
- name: ambassador
image: malexer/twemproxy
env:
- name: REDIS_SERVERS
value: redis-st-0.redis-svc.qiuqiu.svc.cluster.local:6379:1
ports:
- containerPort: 6380
以下圖,演示了應(yīng)用程序(由 Redis 客戶端模擬)向 localhost:6380 發(fā)起請求,外交官容器接收請求并將其中轉(zhuǎn)到其配置中定義的 Redis 服務(wù)器。
3.2.3 適配器模式(Adapter Pattern)
適配器模式主要是以標準化和規(guī)范化應(yīng)用對外暴露服務(wù)接口。每個應(yīng)用程序可能會以各種形式(如JSON、XML、StatsD等),對外輸出監(jiān)控指標數(shù)據(jù)。然而,監(jiān)控系統(tǒng)卻希望收到的是統(tǒng)一數(shù)據(jù)模型的數(shù)據(jù)。這時就可以通過使用適配器模式,將應(yīng)用程序容器,同相應(yīng)的監(jiān)控指標數(shù)據(jù)轉(zhuǎn)換的適配器容器創(chuàng)建在同一個Pod內(nèi),就可做到將不同應(yīng)用的異構(gòu)監(jiān)控指標數(shù)據(jù)轉(zhuǎn)換為單個統(tǒng)一模型數(shù)據(jù)。
以下yaml是以Prometheus 監(jiān)控Nginx 服務(wù)狀態(tài)指標為例。Nginx 有一個用于查詢 Web 服務(wù)器狀態(tài)的接口,只要更改 default.conf 文件配置項即可啟用。接著添加一個適配器容器將此接口的輸出轉(zhuǎn)換為 Prometheus 所需的數(shù)據(jù)格式即可。通過configMap 配置 default.conf 文件內(nèi)容,其中(20-24行)定義了一個接口(/nginx_status),它利用 stub_status 模塊來顯示 Nginx 狀態(tài)信息;接著是創(chuàng)建包含Nginx 容器和適配器容器的Pod。適配器容器使用 nginx/nginx-prometheus-exporter鏡像創(chuàng)建,它可以按照 Prometheus 格式轉(zhuǎn)換 Nginx 在 /nginx_status 接口暴露的指標數(shù)據(jù)格式。
# Nginx config
apiVersion: v1
kind: ConfigMap
metadata:
namespace: qiuqiu
name: nginx-conf
data:
default.conf: |
server {
listen 80;
server_name localhost;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
location /nginx_status {
stub_status;
allow 127.0.0.1; #only allow requests from localhost
deny all; #deny all other hosts
}
}
# Nginx Pod and the adapter container
apiVersion: v1
kind: Pod
metadata:
namespace: qiuqiu
name: webserver
spec:
volumes:
- name: nginx-conf
configMap:
name: nginx-conf
items:
- key: default.conf
path: default.conf
containers:
- name: webserver
image: nginx
ports:
- containerPort: 80
volumeMounts:
- mountPath: /etc/nginx/conf.d
name: nginx-conf
readOnly: true
- name: adapter
image: nginx/nginx-prometheus-exporter:0.4.2
args: ["-nginx.scrape-uri","http://localhost/nginx_status"]
ports:
- containerPort: 9113
以下圖是驗證操作:執(zhí)行yaml創(chuàng)建服務(wù)后,首先登錄到 webserver Pod,安裝 curl 指令(便于發(fā)起 HTTP 請求),接著,檢查 /nginx_status 接口和適配器容器的轉(zhuǎn)換接口(9113/metrics )。
3.3 多節(jié)點多容器模式
3.3.1 選舉模式(Election Pattern)
選舉模式是一種多節(jié)點多容器組合模式,在分布式系統(tǒng)中尤為重要。在分布式系統(tǒng)中,有狀態(tài)應(yīng)用服務(wù)來要解決高可用和水平伸縮問題時,需要依賴外部系統(tǒng)存儲每個實例與狀態(tài)分片數(shù)據(jù)的對應(yīng)關(guān)系(全局信息)。而引入選舉主控節(jié)點機制,就不需要依賴外部的系統(tǒng)來維護自己的狀態(tài)了,由主控節(jié)點完成保存和分發(fā)全局信息任務(wù)——將應(yīng)用服務(wù)容器與可復用的選舉器容器組合起來使用——即選舉模式。
以下yaml提供了一個用來作為應(yīng)用容器檢測選舉結(jié)果的nodejs服務(wù)(具體參考Kubernetes社區(qū)contrib項目election(https://github.com/kubernetes/contrib/tree/master/election))
apiVersion: apps/v1
kind: Deployment
metadata:
name: demo-elector-nodejs
namespace: qiuqiu
spec:
replicas: 3
selector:
matchLabels:
name: leader-elector
template:
metadata:
labels:
name: leader-elector
spec:
containers:
- image: k8s.gcr.io/nodejs-election-client:0.1
imagePullPolicy: IfNotPresent
name: nodejs
ports:
- containerPort: 8080
protocol: TCP
resources:
requests:
cpu: 100m
- image: k8s.gcr.io/leader-elector:0.5
imagePullPolicy: Always
name: elector
args:
- --election=elect-master
- --http=localhost:4040
ports:
- containerPort: 4040
protocol: TCP
resources:
requests:
cpu: 100m
04綜述
以上簡單介紹了容器和Pod的概念,并基于容器和Pod的關(guān)系,引入容器設(shè)計模式。并結(jié)合實例介紹了跨斗模式、外交官模式、適配器模式、選舉模式。以下列表是對以上介紹模式的一個簡單綜述。
鑒于篇幅限制,諸如工作隊列模式(Work queue pattern)、分散收集模式(Scatter/gather pattern)等其他優(yōu)秀的設(shè)計模式,讀者可以按需查詢社區(qū)文檔(https://kubernetes.io/blog)。