發佈日期:

Prometheus Operator​

Prometheus Operator​介紹

官方網站: https://prometheus-operator.dev/

Prometheus Operator 提供Kubernetes原生部署和管理Prometheus及相關監控組件。該項目的目的是為 Kubernetes 集群簡化和自動化基於 Prometheus 的監控堆棧的配置。

Prometheus算子包括但不限於以下特性:

  • Kubernetes 自定義資源:使用 Kubernetes 自定義資源部署和管理 Prometheus、Alertmanager 及相關組件。
  • 簡化的部署配置:配置 Prometheus 的基礎知識,例如版本、持久性、保留策略和本地 Kubernetes 資源的副本。
  • Prometheus Target Configuration:根據熟悉的Kubernetes標籤查詢,自動生成監控目標配置;無需學習普羅米修斯特定的配置語言。

Prometheus Operator優點​

下面是一個最原始的普羅米修斯的設定範例,若是這樣設定,每一個監控目標都需要手動設定,當Pod自動增加/減少時,會需要有人幫忙更改監控對象,所以Prometheus Operator就出現了。

主要功能是去做Services Discovery,例如我們可以用Deployment去管理Pod的產生,用Service去管理Pod之間的互連,而Operator可以透過Service Monitor去發現需要監控的Service,並且可以隨著Pod的增減動態改變。​
Prometheus Operator裡面有一個叫做prometheus-config-reloader的,可以透過ServiceMonitor產生新的prometheus.yml

Prometheus Operator能做什麼​

要了解Prometheus Operator能做什麼,其實就是要了解Prometheus Operator為我們提供了哪些自定義的Kubernetes資源,列出了Prometheus Operator目前提供的️4類資源:​

  • Prometheus:聲明式創建和管理Prometheus Server實例;​
  • ServiceMonitor:負責聲明式的管理監控配置;​
  • PrometheusRule:負責聲明式的管理告警配置;​
  • Alertmanager:聲明式的創建和管理Alertmanager實例。​

簡言之,Prometheus Operator能夠幫助用戶自動化的創建以及管理Prometheus Server以及其相應的配置。​

Prometheus Operator的架構示意圖

發佈日期:

在K8S裡為Prometheus增加exporter: 以pushgateway為例

PUSHGATEWAY介紹

Prometheus Pushgateway 的存在是為了允許臨時和批處理作業將其指標公開給 Prometheus。由於這類工作存在的時間可能不夠長,無法被抓取,因此他們可以將指標推送到 Pushgateway。Pushgateway 然後將這些指標公開給 Prometheus。

何時使用 PUSHGATEWAY

我們只建議在某些有限的情況下使用 Pushgateway。盲目地使用 Pushgateway 而不是 Prometheus 通常的 pull 模型來進行一般指標收集時,有幾個陷阱:

  • 當通過單個 Pushgateway 監控多個實例時,Pushgateway 既成為單點故障又成為潛在的瓶頸。
  • up 你失去了普羅米修斯通過指標(在每次抓取時生成)的自動實例健康監控。
  • Pushgateway 永遠不會忘記推送給它的系列,並將它們永遠暴露給 Prometheus,除非這些系列是通過 Pushgateway 的 API 手動刪除的。

instance當作業的多個實例通過標籤或類似物在 Pushgateway 中區分它們的指標時,後一點尤其重要。即使原始實例被重命名或刪除,實例的指標也會保留在 Pushgateway 中。這是因為作為指標緩存的 Pushgateway 的生命週期從根本上獨立於將指標推送給它的進程的生命週期。將此與普羅米修斯通常的拉式監控進行對比:當一個實例消失時(有意或無意),其指標將隨之自動消失。使用 Pushgateway 時,情況並非如此,您現在必須手動刪除任何陳舊的指標或自己自動執行此生命週期同步。

通常,Pushgateway 的唯一有效用例是捕獲服務級批處理作業的結果。“服務級”批處理作業是在語義上與特定機器或作業實例不相關的作業(例如,為整個服務刪除多個用戶的批處理作業)。此類作業的指標不應包含機器或實例標籤,以將特定機器或實例的生命週期與推送的指標分離。這減少了在 Pushgateway 中管理陳舊指標的負擔。

取得pushgateway的image

官方檔案: https://hub.docker.com/r/prom/pushgateway
或在cmd輸入
docker pull prom/pushgateway

建立一個含有pushgateway的pod

為pushgateway寫Deployments

apiVersion: apps/v1​
kind: Deployment​
metadata:​
  labels:​
    app: pushgateway​
  name: pushgateway​
  namespace: default​
spec:​
  replicas: 1​
  template:​
    metadata:​
      labels:​
        app: pushgateway​
    spec:​
      containers:​
      - image: prom/pushgateway
        imagePullPolicy: Always​
        name: pushgateway​
        ports:​
        - containerPort: 9091​
          name: pushgateway​
          protocol: TCP​
      dnsPolicy: ClusterFirst​
      restartPolicy: Always​

為pushgateway的POD產生一個Headless Services​

將Service指到對應的Pod​

接著到同域名的容器打
echo “some_metric 3.14” | curl –data-binary @- http://pushgateway:9091/metrics/job/some_job
然後就可以用下面指令看資料
curl http://pushgateway:9091/metrics

發佈日期:

Prometheus Exporter

資料提供端​在架構圖的哪邊呢

資料提供端的資料長怎樣呢

  • Counter: 代表一個單調遞增的計數器​
  • Gauge: 表示可以任意上下的單個數值​
  • Histogram:直方圖對觀察結果進行採樣(通常是請求持續時間或響應大小等),並將它們計入可配置的存儲桶中。它還提供所有觀察值的總和。​
  • Summary: 與histogram類似,摘要對觀察結果進行採樣(通常是請求持續時間和響應大小等)。雖然它還提供了觀察總數和所有觀察值的總和,但它計算了滑動時間窗口上的可配置分位數。

查看現有的資料提供端提供了那些資訊

  • 打開Prometheus面板的Targets


  • 選擇要查看的目標的endpoint連結,除了node-exporter外,都會需要在k8s的內網去讀取資料,以json-exporter來說,可在namespace內部使用下面指令查看:​

    curl “http://127.0.0.1:7979/probe?module=default&target=http://127.0.0.1:1985/api/v1/streams/”​

  • 但是同時我們也會發現有許多的網址是無法連上的,因為部分的exporter若是有需要使用密鑰​

取得pod-exporter所提供的資料​

​rancher-monitoring-kubelet可以取得在所有node裡面的Pods的運行狀態,但是在k8s取得Pods的狀態需要認證,因此需要在yaml裡面設定所需要的Secrets,指令如下:

# 取得該namespace的所有密鑰​
kubectl get secret -n cattle-monitoring-system​
# 取得密鑰的內容​
kubectl -n cattle-monitoring-system get secret rancher-monitoring-prometheus-token-hvlqt -o jsonpath={.data.token} | base64 –d​

# 將pod-exporter的網址後面加上-H並帶入密鑰
curl https://172.17.2.22:10250/metrics/cadvisor-k -H “Authorization: Bearer ${TOKEN}”

更多資訊請見:Accessing the Kubernetes API from a Pod

了解資料提供端的樣子的重要性​

可了解要怎麼在Grafana搜尋目標資料,並了解有哪些資料是可以取得的

上面的資料可用以下的PromQL來撈出,sum代表所有串流的數字加總,並以pod label做資料加總分組的依據。
(sum(stream_clients_clients{namespace=~”namespace_name”, pod=~”pod_name.+”, name=~”.+”}) by (pod))
因此了解有哪些資料,才能夠使用PromQL撈出所需資料

如何產生這些資料​

官方提供許多各種語言可使用的函式庫​
https://prometheus.io/docs/instrumenting/clientlibs/​

以下為幾個我嘗試過的exporter:​

不論使用上面哪個方法,最終都需要有一個類似這個頁面的產出,一個靜態的純文字頁面,上面有著我們要觀察的值​

發佈日期:

Prometheus 介紹

Prometheus 簡介

我們在 SoundCloud 的官方博客中可以找到一篇關於他們爲什麼需要新開發一個監控系統的文章 Prometheus: Monitoring at SoundCloud,在這篇文章中,他們介紹到,他們需要的監控系統必須滿足下面四個特性:

簡單來說,就是下面四個特性:

  • 多維度數據模型
  • 方便的部署和維護
  • 靈活的數據採集
  • 強大的查詢語言

實際上,多維度數據模型和強大的查詢語言這兩個特性,正是時序數據庫所要求的,所以 Prometheus 不僅僅是一個監控系統,同時也是一個時序數據庫。那爲什麼 Prometheus 不直接使用現有的時序數據庫作爲後端存儲呢?這是因爲 SoundCloud 不僅希望他們的監控系統有着時序數據庫的特點,而且還需要部署和維護非常方便。

此外,Prometheus 數據採集方式也非常靈活。要採集目標的監控數據,首先需要在目標處安裝數據採集組件,這被稱之爲 Exporter,它會在目標處收集監控數據,並暴露出一個 HTTP 接口供 Prometheus 查詢,Prometheus 通過 Pull 的方式來採集數據,這和傳統的 Push 模式不同。

不過 Prometheus 也提供了一種方式來支持 Push 模式,你可以將你的數據推送到 Push Gateway,Prometheus 通過 Pull 的方式從 Push Gateway 獲取數據。目前的 Exporter 已經可以採集絕大多數的第三方數據,比如 Docker、HAProxy、StatsD、JMX 等等,官網有一份 Exporter 的列表。

Prometheus 的整體架構圖


從上圖可以看出,Prometheus 生態系統包含了幾個關鍵的組件:Prometheus server、Pushgateway、Alertmanager、Web UI 等,但是大多數組件都不是必需的,其中最核心的組件當然是 Prometheus server,它負責收集和存儲指標數據,支持表達式查詢,和告警的生成。

發佈日期:

使用 Prometheus自定義指標為 Kubernetes 做 HPA 縮放

使用套件

  1. Prometheus
  2. Prometheus Operator
  3. K8S
  4. Rancher

步驟一、設定自訂義指標

1. 設定Exporter,這邊有許多官方提供的函式庫
https://prometheus.io/docs/instrumenting/clientlibs/
2. 設定該export的service名為my-export

apiVersion: v1
kind: Service
metadata:
  labels:
    app: my-export
  name: my-export
  namespace: default
spec:
  clusterIP: None
  clusterIPs:
  - None
  ports:
  - name: my-export
    port: 7979
    protocol: TCP
    targetPort: 7979
  selector:
    prometheus-customized-metrix: my-export
  type: ClusterIP

設定Service Monitors

apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  labels:
    app: my-export
  name: my-export
  namespace: default
spec:
  endpoints:
  - interval: 30s
    params:
      module:
      - default
      target:
      - http://127.0.0.1:1985/api/v1/streams/
    path: /probe
    port: my-export
  jobLabel: jobLabel
  namespaceSelector:
    matchNames:
    - default
  selector:
    matchLabels:
      app: my-export

步驟二、設定自訂義規則

增加Prometheus Rules

apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  labels:
    app: rancher-monitoring
    app.kubernetes.io/instance: rancher-monitoring
    app.kubernetes.io/managed-by: Helm
    app.kubernetes.io/part-of: rancher-monitoring
    app.kubernetes.io/version: 16.6.1_up16.6.0
    chart: rancher-monitoring-16.6.1_up16.6.0
    heritage: Helm
    release: rancher-monitoring
  name: my-data
  namespace: default
spec:
  groups:
  - name: my-data
    rules:
    - expr: sum(mydata{container="my-container", name=~".+",namespace=~"default"})
        by (pod)
      labels:
        namespace: default
        service: eventqueue
      record: mydata

設定HPA

在HorizontalPodAutoscaler增加一個設定如下

apiVersion: autoscaling/v2beta2
kind: HorizontalPodAutoscaler
metadata:
  name: my-hpa
  namespace: default
spec:
  behavior:
    scaleDown:
      policies:
      - periodSeconds: 60
        type: Pods
        value: 1
      selectPolicy: Max
      stabilizationWindowSeconds: 300
    scaleUp:
      policies:
      - periodSeconds: 60
        type: Pods
        value: 1
      selectPolicy: Max
      stabilizationWindowSeconds: 300
  maxReplicas: 2
  metrics:
  - object:
      describedObject:
        apiVersion: v1
        kind: Service
        name: eventqueue
      metric:
        name: mydata
      target:
        type: Value
        value: 1k
    type: Object
  minReplicas: 1
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: my-container

參考資料

  1. https://docs.openshift.com/container-platform/4.9/nodes/pods/nodes-pods-autoscaling.html
  2. https://www.padok.fr/en/blog/scaling-prometheus-rabbitmq
發佈日期:

K8S裡CPU和MEMORY的計算單位

如何獲取和讀取 K8s 的資源利用率指標

在K8S裡面我們可以對資源做resource isolation,限制pod所使用的資源或者設立HPA去決定何時要自動擴展/縮小

所以要了解一下K8S裡面所用的度量單位的意義

使用kubectl下指令讀取pod內資源使用狀況

kubectl top pod srs-core1-dbbb776bd-5s9rz -n srs3
會得到下面的回應
NAME CPU(cores) MEMORY(bytes)
srs-core1-dbbb776bd-5s9rz 3m 73Mi

確認Pod裡面不同的Container所使用的資源
kubectl top pod srs-core1-dbbb776bd-5s9rz -n srs3 –containers
會得到以下回應
POD NAME CPU(cores) MEMORY(bytes)
srs-core1-dbbb776bd-5s9rz filebeat 1m 60Mi
srs-core1-dbbb776bd-5s9rz json-exporter 2m 7Mi
srs-core1-dbbb776bd-5s9rz logrotate 1m 0Mi
srs-core1-dbbb776bd-5s9rz srs-core1 1m 5Mi

取得Node資訊

kubectl top node qatk8sworker01 -n srs3
會得到以下回應
NAME CPU(cores) CPU% MEMORY(bytes) MEMORY%
qatk8sworker01 1280m 32% 3713Mi 48%

了解指標所代表的意思 – CPU

其中設定的單位是 m,每 1000m = 1 vCore,也可以使用分數,因此設定的方式可以是:

  • 1 (相當於 1000m)
  • 0.5 (相當於 500m)
  • 300m (相當於 0.3)

從上面取得node的直可以看到CPU的資訊是
1280m 32%
代表總共有4000m = 4 Core CPU
PS:設定 1m 是不被允許的,官方建議最低從 100m 開始

了解指標所代表的意思 – Memory

Memory 設定的單位最低則是從 byte 開始,而使用的單位可以是單一字母的 E, P, T, G, M, K,也可以是雙字母的 Ei, Pi, Ti, Gi, Mi, Ki(比較常見),以下是幾個設定範例:

  • 104857600 (相當於 100 MB = 10010241024)
  • 100M
  • 100Mi

128974848 = 129e6 = 129M = 123Mi
123Mi * 1024 * 1024 = 128974848 bytes
129 MB * 1000 * 1000 = 128974848 bytes
怎麼將Mi轉成Mb?
3713Mi * 1024 * 1024 / 1000 / 1000 = 3893MB

發佈日期:

prometheus執行時更新config的方式

參考官方文件的教學如下:
https://prometheus.io/docs/prometheus/latest/configuration/configuration/

Prometheus can reload its configuration at runtime. If the new configuration is not well-formed, the changes will not be applied. A configuration reload is triggered by sending a SIGHUP to the Prometheus process or sending a HTTP POST request to the /-/reload endpoint (when the –web.enable-lifecycle flag is enabled). This will also reload any configured rule files.

而實際的應用方式則是:

  • 第一種,向prometheus進行發信號
    kill -HUP pid
  • 第二種,向prometheus發送HTTP請求
    curl -XPOST http://127.0.0.1:9090/-/reload
    但是會需要先設定--web.enable-lifecycle參數

     template:
        metadata:
          labels:
            ...
            prometheus: rancher-monitoring-prometheus
        spec:
          containers:
          - args:
            - --web.enable-lifecycle
            image: rancher/mirrored-prometheus-prometheus:v2.27.1
            imagePullPolicy: IfNotPresent
            name: prometheus
            ....
發佈日期:

Dockerfile簡單範例

如何製作docker image

  1. 到docker hub尋找適合的基礎容器或官方提供的映像檔
  2. 自行撰寫需要客製化的部分
  3. 撰寫Dockerfile內容
  4. 執行command line,指向專案資料夾
  5. docker build –tag NAME .

  6. 上傳映像檔至儲存庫

Dockerfile的指令介紹

範例:

  1. FROM: 從某個別人建好的容器開始製作自己的容器。如centos:6.7
  2. ENV: 定義一些變數後面可使用
  3. ARG: 在BUILD IMAGE時可帶入參數
    docker build –build-arg SCRIPT=tmp.js .
  4. WORKDIR: 指定下指令的位置
  5. RUN: 於CONTAINER下指令
  6. COPY: 複製檔案進去
  7. CMD: 最後要開始主程式的指令
  8. ENTRYPOINT:與CMD相同,差在不會被docker run時帶的指令蓋掉,而是相加
發佈日期:

[LeetCode] Maximum Value of K Coins From Piles

Maximum Value of K Coins From Piles

這是遞迴方式的寫法, 但我可能存太多垃圾資訊,當k值變大之後,會發生heap allocation錯誤的問題,也受限於遞迴的限制當k變大效率非常差…..
看來又要動態規劃(哭), 晚點再來補上改善版

/**
* @param {number[][]} piles
* @param {number} k
* @return {number}
*/
var maxValueOfCoins = function (piles, k) {
let rootNode = new node(piles.length, k);
let maxResult = addAllChildrenForThisNode(rootNode, piles, 0);
console.log(rootNode.toObject())
return maxResult
};
function addAllChildrenForThisNode(node, piles, maxResult) {
for (let i = 0; i < node.pilesPointer.length; i++) {
if (node.pilesPointer[i] + 1 < piles[i].length) {
let y = node.pilesPointer[i] + 1;
let newNode = node.addChild(piles[i][y], i, y);
console.log(newNode.deep)
if(newNode.deep < newNode.maxSelectNum){
maxResult = addAllChildrenForThisNode(newNode, piles, maxResult)
}else{
maxResult = Math.max(newNode.currentValue, maxResult)
}
}
}
return maxResult
}
function node(pilesNum, maxSelectNum) {
this.currentValue = 0;
//樹的鏈結資料
this.deep = 0;
this.parent = undefined;
this.children = [];
//x代表在哪個piles, y代表在該piles的深度
this.indexX = undefined;
this.indexY = undefined;
//用來儲存這個node位置所有的piles的y座標
this.pilesPointer = new Array(pilesNum).fill(-1);
this.maxSelectNum = maxSelectNum;
this.addChild = function (num, indexX, indexY) {
let child = new node(this.pilesPointer.length, this.maxSelectNum);
child.deep = this.deep + 1;
child.parent = this;
child.currentValue = this.currentValue + num;
child.indexX = indexX;
child.indexY = indexY;
child.pilesPointer = this.pilesPointer.slice(0);
child.pilesPointer[child.indexX] = indexY;
this.children.push(child);
return child
}
this.toObject = function () {
let children = [];
for (let child of this.children) {
children.push(child.toObject())
}
return {deep:this.deep, value: this.currentValue , children: children}
}
}

console.log(maxValueOfCoins([[80,62,78,78,40,59,98,35],[79,19,100,15],[79,2,27,73,12,13,11,37,27,55,54,55,87,10,97,26,78,20,75,23,46,94,56,32,14,70,70,37,60,46,1,53]],5))
發佈日期:

[LeetCode] Coin Change 2

Coin Change2

這也是一題動態規劃的題目,但是比起上一題,上一題每一個暫時儲存的東西是有意義的,我們可以了解說每一個的暫時狀態是某個數字用所給的硬幣列表裡,能用最少數量達成答案的數目,但是這一題的中間狀態所儲存的真的就是『中間的狀態』,我是看著別人的答案去回推理論的,真的很難想像在遇到這樣的問題時,該用什麼角度去把複雜的問題拆分,並找尋出重複步驟並找到可暫存狀態來計算出最終結果。

這是最後的結果

var change = function(amount, coins) {
let dp = new Array(amount+1).fill(0);
dp[0]=1;
for(let coin of coins){
for(let i=coin;i<=amount;i++){ dp[i]+=dp[i-coin]; } } return dp[amount] }; [/code] 大概解釋一下解題的思緒,首先就是最重要的,先設定一個暫存動態編程的序列

dp

,這裡面的值只是儲存一個暫時的狀態,讓我們在計算下一個值時可以拿前一個運算到一半的值來繼續運算,長度為amount+1(這樣才存的到

dp[amount]

)。

接下來很重要的是把

dp[0]=1

, 這是所有計算的起點, 代表說在一個分解狀態下,若自己可以被自己整除,那就可以有一種解法

若題目是coins=[2,5,8], amount=16時:

我們可以先觀察在

coins:[2], amount:16

時,
這樣的半狀態會得到:

dp = [1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1]

這個的意思是,若是coins只有2時,可以用2組成0, 2, 4, 6, 8, 10, 12, 14, 16,皆只有一種組法。

接下來我們再把5加進去,但是因為以coins=[2,5]來說,我們可以把任務拆細,若是要組成16,我們要避免掉重覆的狀態,也就是說
我們可以思考,因為5,5,2,2,2和2,2,5,5,2其實是相同的答案,以2,2,2,5,5來說,其實是單純用2coin*3個=6和單純用5coin*2個=10,
那麼就可以開始計算,用5和2共同組成的狀況下,五的分佈是如何呢?

amount為16,則5有可能有分成:1個5(這時會需要單純用2組成11)和2個5(這時會需要用2組成6)和3個5(這時會需要用2組成1)三種可能性,這時參照上面coins=[2]的dp,會知道用2組成11的狀況和用2組成1狀況是0個,代表只會增加2個5的狀況。

但是動態編程當然沒這麼容易思考(真哭),因為這邊的dp存的是一個暫時半狀態的對照表,會需要算出從5~16所有可用[2,5]組成的,需要較完整的對照表。

首先很重要的,為什麼要從五開始,因為一定要大於五才有可能可以用五組成結果,並且如同dp[0]一樣,dp[5]因為coins有5,一定會多一種5的組合法。
從上面的推論我們可以觀查到要知道有沒有機會使用5這個coins來組成結果,主要要觀察『要組成總數』減掉現有coin的面額的數字,能不能用2組成

所以若要知道6能不能用[5,2]組成,就要查coins:[2]所暫存的表的

dp[6(要組數字)-5(現有硬幣)]=dp[1](能不能用2組成1) = 0

,可得知是0種可能,以此類推。
這邊非常重要的是一定要從小到大,因為才可以讓狀態累加,我們先得知5可以組成5,把dp[5]=1(本來只有coins:2時是0),而在算10時,才能得到dp[10-5]=1(得知可以使用5來組成10),外加單純用2組成10的可能性(當coins=[2]時dp[10]為1),可知10這個數字可單純用5組成也可用[2,5]組成,所以共有2種可能性,然後把dp[10]更新為2

接著在算dp[15]時才能夠利用dp[15-5]=dp[10]=2,再加上當coins=[2]時的dp[15]=0,得知dp[15]為2

以下為coins=[2,5]時的dp對照表

dp= [ 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 2, 2, 2]

接下來就是再相同的步驟再把coin 8加進coins列表裡。以同樣的步驟和概念,就可以得到

dp=[1, 0, 1, 0, 1, 1, 1, 1, 2, 1, 3, 1, 3, 2, 3, 3, 4]

最後推得用[2,5,8要組成16有四種可能性]