使用Kubernetes演示金絲雀釋出
為了更直觀的看出金絲雀釋出的效果,我們這裡使用了Prometheus監控來觀察這個過程。不知道怎麼使用Prometheus的同學請看使用Prometheus監控Kubernetes叢集,另外我們這裡使用一個Python程式來作為我們要釋出的程式。
如何讓Prometheus監控自定義程式
要想讓Prometheus監控你的程式,你的程式執行在容器裡,而容器有被POD這種資源形式所管理,那麼監控程式就是監控POD,所以首先你就需要在POD檔案中宣告該POD需要被Prometheus抓取,這就需要透過一個標識來完成。
在Prometheus的配置檔案中由於使用了Kubernetes自動發現,那麼它會有這麼一端配置內容,
global:
scrape_interval: 10s
evaluation_interval: 30s
scrape_configs:
......
# 抓取POD進行監控
- job_name: 'kubernetes-pods'
kubernetes_sd_configs:
- role: pod
relabel_configs:
# POD的 annotation 中含有"prometheus.io/scrape: true" 的則保留,
# 意思就是會被Prometheus抓取,不具有這個的POD則不會被抓取
- action: keep
regex: true
source_labels:
- __meta_kubernetes_pod_annotation_prometheus_io_scrape
# 獲取POD的 annotation 中定義的"prometheus.io/path: XXX"定義的值,
# 這個值就是你的程式暴露符合prometheus規範的metrics的地址,如果你的
# metrics的地址不是 /metrics 的話,透過這個標籤說,那麼這裡就會把這個
# 值賦值給 __metrics_path__這個變數,因為prometheus是透過這個變數
# 獲取路徑然後進行拼接出來一個完整的URL,並透過這個URL來獲取metrics值的,
# 因為prometheus預設使用的就是 http(s)://X.X.X.X/metrics
# 這樣一個路徑來獲取的。
- action: replace
regex: (.+)
source_labels:
- __meta_kubernetes_pod_annotation_prometheus_io_path
target_label: __metrics_path__
# 這裡是埠資訊,因為你的程式很有可能在容器中並不是以80埠執行的,
# 那麼就需要做一個拼接http(s)://x.x.x.x:xx/metrics
# __address__在prometheus中代表的就是例項的IP地址,
# 而POD中的annotation 中定義的"prometheus.io/port: XX"就是你程式
# 被訪問到的埠,最終在prometheus中將會被顯示為 instance=X.X.X.X:XX這樣
- action: replace
regex: ([^:]+)(?::\d+)?;(\d+)
replacement: $1:$2
source_labels:
- __address__
- __meta_kubernetes_pod_annotation_prometheus_io_port
target_label: __address__
- action: labelmap
regex: __meta_kubernetes_pod_label_(.+)
- source_labels: [__meta_kubernetes_namespace]
action: replace
target_label: kubernetes_namespace
- source_labels: [__meta_kubernetes_pod_name]
action: replace
target_label: kubernetes_pod_name
所以最關鍵的就是在POD中配置上如下內容:
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "your port"
# 如果的metrics的路徑就是 /metrics的話就不用配置下面的內容
prometheus.io/path: "your path"
但很多概念不清的人會發現你在POD中配置了這樣的設定prometheus獲取過來會報錯,比如我這裡使用了一個標準的tomcat映象來啟動2個POD,下面是deployment配置清單檔案:
apiVersion: v1
kind: Service
metadata:
name: myapp-svc
labels:
appname: myapp
spec:
type: ClusterIP
ports:
- name: tomcat-http
port: 8080
targetPort: 8080
selector:
appname: myapp
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: tomcat-deploy-v1.11.1
labels:
appname: myapp
spec:
replicas: 2
selector:
matchLabels:
appname: myapp
release: 1.11.1
template:
metadata:
name: myapp
labels:
appname: myapp
release: 1.11.1
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "8080"
spec:
containers:
- name: myapp
image: tomcat:8.5.38-jre8
imagePullPolicy: IfNotPresent
resources:
requests:
cpu: "250m"
memory: "128Mi"
limits:
cpu: "500m"
memory: "256Mi"
ports:
- name: http
containerPort: 8080
protocol: TCP
livenessProbe:
httpGet:
path: /
port: http
initialDelaySeconds: 20
periodSeconds: 10
timeoutSeconds: 2
readinessProbe:
httpGet:
path: /
port: http
initialDelaySeconds: 20
periodSeconds: 10
revisionHistoryLimit: 10
strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
type: RollingUpdate
然後我應用這個清單檔案,如下圖:
然後在Pormetheus中可以看到那2個POD已經被抓取了,但是狀態為DOWN,如下圖:
查詢指標也是沒有的
其原因就是因為標準Tomcat自帶的應用沒有/metrics這個路徑,prometheus獲取不到它能識別的格式資料,而指標資料就是從/metrics這裡獲取的。所以我們使用標準Tomcat不行或者你就算有這個/metrics這個路徑,但是返回的格式不符合prometheus的規範也是不行的。
金絲雀釋出我這裡為什麼非要讓prometheus來監控呢?其實金絲雀釋出和prometheus沒關係,弄2個配置清單檔案就可以了,但是為了更加直觀的看出釋出過程的流量情況我這裡才使用了prometheus來監控這個過程,否則你很難有一個更加直觀的認識,而且在實際工作中監控也是必須的尤其是對核心應用的升級。那麼下面我們就自己製作一個符合prometheus指標規範的程式。
製作自定義程式
1. 設定目錄結構
下面是目錄結構
下面是myapp.py檔案的內容
import prometheus_client
from prometheus_client import Counter, Gauge
from prometheus_client import Summary, CollectorRegistry
from flask import Response, Flask
import time
import random
import os
app = Flask(__name__)
# 定義一個註冊器,註冊器可以把指標都收集起來,然後最後返回註冊器資料
REGISTRY = CollectorRegistry(auto_describe=False)
# 定義一個Counter型別的變數,這個變數不是指標名稱,這種Counter型別只增加
# 不減少,程式重啟的時候會被重新設定為0,建構函式第一個引數是定義 指標名稱,
# 第二個是定義HELP中顯示的內容,都屬於文字
# 第三個引數是標籤列表,也就是給這個指標加labels,這個也可以不設定
http_requests_total = Counter("http_requests", "Total request cout of the host", ['method', 'endpoint'], registry=REGISTRY)
# Summary型別,它可以統計2個時間
# request_processing_seconds_count 該函式被呼叫的數量
# request_processing_seconds_sum 該函式執行所花的時長
request_time = Summary('request_processing_seconds', 'Time spent processing request', registry=REGISTRY)
@app.route("/metrics")
def requests_count():
"""
當訪問/metrics這個URL的時候就執行這個方法,並返回相關資訊。
:return:
"""
return Response(prometheus_client.generate_latest(REGISTRY),
mimetype="text/plain")
# 這個是健康檢查用的
@app.route('/healthy')
def healthy():
return "healthy"
@app.route('/')
@request_time.time() # 這個必須要放在app.route的下面
def hello_world():
# .inc()表示增加,預設是加1,你可以設定為加1.5,比如.inc(1.5)
# http_requests_total.inc()
# 下面這種寫法就是為這個指標加上標籤,但是這裡的method和endpoint
# 都在Counter初始化的時候放進去的。
# 你想統計那個ULR的訪問量就把這個放在哪裡
http_requests_total.labels(method="get", endpoint="/").inc()
# 這裡設定0-1之間隨機數用於模擬頁面響應時長
time.sleep(random.random())
html = "Hello World!" \
"App Version: {version}"
# 這裡我會讀取一個叫做VERSION的環境變數,
# 這個變數會隨Dockerfile設定到映象中
return html.format(version=os.getenv("VERSION", "888"))
if __name__ == '__main__':
app.run(host="0.0.0.0", port="5555")
下面是requirements.txt檔案內容
Flask
prometheus_client
下面是Dockerfile檔案的內容
# 使用官方提供的 Python 開發映象作為基礎映象
FROM python:3.7.3-slim
# 建立目錄
RUN mkdir /app
# 將工作目錄切換為 /app 該目錄為容器中的目錄,相當於cd進入這個目錄
WORKDIR /app
# 將Dockerfile所在目錄下的這兩個檔案複製到 /app 下
ADD myapp.py requirements.txt /app/
# 使用 pip 命令安裝這個應用所需要的依賴,這裡透過-r指定依賴包的名稱檔案
RUN pip install --trusted-host mirrors.aliyun.com -r requirements.txt
# 允許外界訪問容器的 5555 埠
EXPOSE 5555
# 設定版本號
ENV VERSION 1.0
# 設定容器程式為:python myapp.py,即:這個 Python 應用的啟動命令
CMD ["python", "myapp.py"]
2. 使用Dockerfile製作映象
使用下面的命令構建映象docker build -t myapp:v1.0 .
打完包,如下圖
使用docker save -o myapp.tar myapp:v1.0
命令匯出該映象,然後複製到Kubernetes叢集中所有node節點上,然後使用這個命令進行匯入docker load -i ./myapp.tar
。
3. 編寫Kubernetes的配置清單檔案
其實這個配置清單檔案我就是用上面那個Tomcat的檔案修改的。
apiVersion: v1
kind: Service
metadata:
name: myapp-svc
labels:
appname: myapp-svc
spec:
type: ClusterIP
ports:
- name: http
port: 5555
targetPort: 5555
selector:
appname: myapp
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp-deploy-v1.0
labels:
appname: myapp
spec:
replicas: 4
selector:
matchLabels:
appname: myapp
release: 1.0.0
template:
metadata:
name: myapp
labels:
appname: myapp
release: 1.0.0
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "5555"
spec:
containers:
- name: myapp
image: myapp:v1.0
imagePullPolicy: IfNotPresent
resources:
requests:
cpu: "250m"
memory: "128Mi"
limits:
cpu: "500m"
memory: "256Mi"
ports:
- name: http
containerPort: 5555
protocol: TCP
livenessProbe:
httpGet:
path: /healthy
port: http
initialDelaySeconds: 20
periodSeconds: 10
timeoutSeconds: 2
readinessProbe:
httpGet:
path: /healthy
port: http
initialDelaySeconds: 20
periodSeconds: 10
revisionHistoryLimit: 10
strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
type: RollingUpdate
應用檔案
現在檢視Prometheus的監控你可以看到你的程式了
獲取指標資料
4. 構建監控
這裡主要是在Grafana中建立一個圖表來監控釋出過程。
首先建立一個Graph的圖表,然後按照下面的引數設定
使用這個公式sum(rate(http_requests_total{appname="myapp"}[5m])) by (release)
再設定一個名字
最後儲存圖表就可以了
演示金絲雀釋出
編寫v2.0版本的配置清單檔案,這裡不需要設定service,且副本數量為1,我這裡沒有修改程式,只是傳遞了一個環境變數進去表示是2.0版本。
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp-deploy-v2.0
labels:
appname: myapp
spec:
replicas: 1
selector:
matchLabels:
appname: myapp
release: 2.0.0
template:
metadata:
name: myapp
labels:
appname: myapp
release: 2.0.0
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "5555"
spec:
containers:
- name: myapp
image: myapp:v1.0
imagePullPolicy: IfNotPresent
resources:
requests:
cpu: "250m"
memory: "128Mi"
limits:
cpu: "500m"
memory: "256Mi"
ports:
- name: http
containerPort: 5555
protocol: TCP
env:
- name: VERSION
value: v2.0.0
livenessProbe:
httpGet:
path: /healthy
port: http
initialDelaySeconds: 20
periodSeconds: 10
timeoutSeconds: 2
readinessProbe:
httpGet:
path: /healthy
port: http
initialDelaySeconds: 20
periodSeconds: 10
revisionHistoryLimit: 10
strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
type: RollingUpdate
應用該配置清單檔案
檢視監控,黃色表示v2.0.0版本已經有請求了
我們V1.0版本有4個副本,V2.0有1個副本,這就意味著30%的流量會進入到v2.0版本上。如果沒有問題,我們就會進行擴充套件V2.0的應用。其實你從圖上看流量也只有30%,如下圖:
下面進行擴充套件
kubectl scale --replicas=4 deploy myapp-deploy-v2.0
刪除版本V1.0的deployment,注意這裡是刪除的deployment而並沒有刪除那個service
kubectl delete deploy myapp-deploy-v1.0
之後你就可以看到V2.0版本已經接管了所有流量
總結
步驟:
-
4個副本的V1.0應用
-
部署1個V2.0的應用
-
觀察一段時間確認沒有問題
-
擴充套件V2.0的應用數量和V1.0一致
-
擴充套件完成後,刪除V1.0版本應用
這個實驗過程參考了一篇文章Kubernetes deployment strategies,它裡面還有很多部署方式,大家可以練習。另外我這裡之所以選擇自己構建映象而不使用它提供的,是因為我想說明一下如何在Prometheus中監控自己的應用,因為這種需求在工作中會有,即便運維不會遇到,但是執行公司業務的程式也會有這種需求,prometheus提供了Java版本的客戶端來讓Java使用。其實我現在對Python版本的客戶端也不是很瞭解,只是參考官網簡單使用而已。