2019年7月8日 星期一

MySQL 效能及安全性檢查

先使用別人提供的工具

因為我是自行以 source 安裝 mysql ,所以在本機連線時,需要指定 mysql.socket 的路徑,並且 mysqladmin/mysql 這兩個指令必須在 $PATH 可找到的路徑內,然後帳號密碼是用 mysql_config_editor 儲存,並且必須以 --login-path=client 方式來提供給這個 perl script 使用。所以我執行的參數如下
mysqltuner.pl --socket /MySQL/inst1/mysql.sock --output /tmp/health.txt
如果只想要輸出可能有問題的項目,可以加上參數
mysqltuner.pl --socket /MySQL/inst1/mysql.sock --nogood --noinfo

另外這個 script 執行時會去檢查兩個檔案是否存在,basic_passwords.txt  及vulnerabilities.csv。

前者是用一些常見的帳號密碼組合,來檢查系統中是否有太容易被嘗試就取得登入權限的帳號。後者是針對 mysql 版本的 CVE 記錄來檢查,看起來應該只是單純比對 mysql 是否符合 CVE 所記錄的版本號。

如果要用這個 perl script 作為定期檢查的工具的話,可能需要定期調整這兩個輔助檔案的內容,才比較能發揮效果。

在跑出來的建議裡,有幾個項目引起我的注意。第一個是
innodb_log_file_size should be (=256M) if possible, so InnoDB total log files size equals to 25% of buffer pool size

這句話關係到三個參數:
* innodb_log_file_size
* innodb_log_files_in_group
* innodb_buffer_pool_size。

這三個參數都可以從 SHOW VARIABLES like "%innodb%" 取得,其中關鍵是 innodb_log_file_size 這個值。

我這邊 my.cnf 裡相關的設定值是
innodb_buffer_pool_size = 2048M
innodb_log_file_size = 2048M
innodb_log_files_in_group 則是維持系統預設值 2
而這個 perl script 提出的建議公式是
log file size * log files group / buffer pool size = 0.25
=> log file size = buffer pool size * 0.25 / log files group
=> log file size = 2048M * 0.25 / 2 = 256M
所以得出上面那句敘述。
那這樣的公式是否有任何有效的憑據? 我查了幾篇文章

官網 (針對 version 5.7) 上的並沒有任何建議的公式,主要提供如下說明
log file size 如果設定的太小,會造成太多非必要的檔案寫入動作,如果設定太大,則會造成在災後復原的時間太久。
關鍵性的一句是
Make your redo log files big, even as big as the buffer pool.


一篇在這個原則下,採取以下計算方式作為建議。
從 show engine innodb status 取得 Log sequence number,然後看一分鐘的時間內,寫了多少 log size。文章的案例是一小時大約寫入 110MB,於是作者建議 log file size 只需要 128MB 就足夠,因為 log files group 是 2 ,所以最後 log file size 設定為 64MB。
這種計算方式沒有考慮 buffer pool size,純粹以 log 寫入的速度,及盡可能縮短災後復原的時間來做考量。

另外一篇則是認為,log file size 設定大一點不是問題,文章中的案例是 pool size 為 64G,log file size 設定為 15G, log files group 則是預設值 2 。以這樣的參數來看,比前面 perl script 建議的 0.25 高 (這篇算起來是  15 * 2 / 64 約等於 0.5)。這篇另外提供一個不錯的參考數值, 10GB 的 log file size 大約需要使用 20min 來復原,25GB 的 log file size 則是使用約 45min 來復原。也就是說,如果這個復原所需的時間是可以接受的,那 log file size 可以設定大一點。

所以綜合以上幾篇來說,我覺得可以不依循 perl script 的建議,官網都說了盡可能讓 log file size 大一些,甚至和 buff pool size 一樣大。

另一個引起我注意的建議項目是
innodb_buffer_pool_instances(=2)
關於這個參數的解釋可以看這篇,而我直接去看這個 perl script 的 source ,它的計算建議方式很簡單,如果 pool size 小於 1G,那就設為 1 個 instance,否則估算每一個 instance 可以有 1G 的 pool size 可以使用,也就是說 pool size 設為多少 GB (上限 64GB),就設定多少個 pool instances 。因為我這邊的 pool size 設定為 2GB,所以 pool instances 數量也就建議為 2 了。不過這應該還要搭配其他數據來看,而且從其他文章來看,當 pool size 足夠大的時候,這個設定值對效能的影響就沒有很明顯。

其他一些建議值,像是 query cache 的部分,因為這台 mysql server 是 lab 測試用的,平常沒有資料在上面跑,所以可能要找運作中的系統來跑測試,看看建議值是否會有所不同。

2019年7月5日 星期五

istio 的安裝設定調整 (4)

istio 在服務安全性的部分有個機制叫 mtls (mutual TLS) ,也就是服務之間的雙向 TLS 認證。

要打開 mtls 支援很簡單,把 values.yaml 裡的 mtls 設為 enable 就好,然後就可以用 istio 提供的工具 istioctl 驗證一下服務之間是否真的有 mtls
$ istioctl -n bookinfo authn tls-check sleep-7d457d69b5-zlt65 httpbin.bookinfo.svc.cluster.local
HOST:PORT                                   STATUS     SERVER     CLIENT     AUTHN POLICY     DESTINATION RULE
httpbin.bookinfo.svc.cluster.local:8000     OK         mTLS       mTLS       default/         default/istio-system 

httpbin 這個 sample app 我是裝在 bookinfo 這個 namespace ,這個要打完整的 FQDN 才能正確查詢。

如果沒使用 cert-manager ,TLS 使用的是 citadel 自行產生的憑證,也可以設定用其他憑證來取代 citadel 產生的,這部分可以參考官網。如果有使用 cert-manager,也就是使用 SDS 來取得憑證,那在 istio-proxy 這個 container 的 /etc/certs 就沒有 citadel 產生的憑證,這部分一樣可以參考官網

enable mtls 很簡單,不過後續要考慮的問題就變多了。例如外部要來使用服務,但不支援 TLS 的時候,或是要使用到外部的服務,但是對方不支援 TLS 的時候,都會造成問題。

因為有可能並非整個 kubernetes cluster 都在 istio 的 service mesh 內,因此上面那段比較正確的講法應該是,"非 istio service" 與 "istio service" 之間的溝通。

當 "istio service" 往 "非 istio service" 請求時,可以設定一個 DestinationRule 來 disable 這條路徑的 mtls。事實上像 kuberentes apiserver 就沒有 istio 的 sidecar,所以預設會有一個 DestinationRule 是把通往 apiserver 的 mtls 給 disable ,這部分在官網有蠻清楚的說明。

對於已經上線的系統,可以透過 permissive mode來測試新增的 policy。
permissive mode 意思是該項 policy 並不會真的生效,但是可以透過適當的設定,使其在 telemetry 的 log 中觀察出區別。以上面官網提供的例子來說,在 rabcsampelog 這個 instance resource 裡,區分了 responseCode 和 permissiveResponseCode,前者是實際回應的 http response code,後者是 permissive 設定情況下應該會有的 http response code。也就是說,可以從 istio-telemetry 這個 pod 的 log 裡觀察 permissiveResponseCode ,就可以知道設定是否正確。

當 "非 istio service" 往 "istio service" 請求時,可以設定 service 同時接受 TLS 與明文的請求,也是使用 permissive mode

從上面範例裡的 rbac-permissive-telemetry.yaml 這個檔案中,可以看到幾個新面孔的 CRD,像是 instance、handler 等,可以參考這篇的說明。

mtls 相關的部分,在官網寫得還算蠻清楚的,這部分我比較沒有實際動手操作確認,只有透過 istioctl 確認 mtls 運作正確,然後把幾個範例中的 yaml 掃過一遍,以及針對比較陌生的 CRD 稍微查了下資料。

為了 mtls ,把官網 tasks 裡的 security 項目整個掃過一遍,這是我始料未及的,裡面有很多我沒想到過的層面。雖然沒有整個流程實作過,但大致上有個初步的了解,希望日後若有機會運用到的話,不會手忙腳亂  QQ

2019年7月3日 星期三

修改 prometheus-operator rules

上週開始,prometheus-operator 開始狂發 etcdHighNumberOfFailedGRPCRequests alert ,然後 slack 就一直狂叫,有時一天數百個訊息。

而另一個也有裝 prometheus-operator 的 kubernetes cluster 卻沒有這情況,原本以為是 cluster 的問題,後來查了一下網路發現這可能是這條 rule 的 bug,不過這無法解釋為什麼另一個 kubernetes cluster 就不會產生 alert。

不過決定暫時先手動拿掉這條 rule ,因為是 operator 的架構,可以很直覺的去修改 rule 內容
kubectl -n monitoring edit prometheusrules.monitoring.coreos.com prometheus-prometheus-oper-etcd

把有關 etcdHighNumberOfFailedGRPCRequests 的 rule 移除掉即可 (我其實是 comment rule,但是儲存更新後,就自動把 comment 掉的 rule 給移除了)

順便補充一個連結,說明如何新增自己的 rule,同樣是因為 operator 的架構,讓新增 rule 的方式也變得很有彈性。

istio 的安裝設定調整 (3)

來整理一下 istio 裡 tls、ingress 與 cert-manager 的設定,有些地方還沒弄清楚,不過先簡單做個筆記。

話說上週安裝時,istio 才更新到 1.2.0 ,這禮拜又變成了 1.2.2 ....

在講 istio 之前,先講一下 cert-manager 。

簡單來說,cert-manager 是一個可以自動產生或取得 ssl 憑證的套件。如果用 helm 安裝 cert-manager 的話,記得要先安裝 cert-manager 的 CRD (官網上有寫)。

這篇文章對於安裝設定 cert-manger 蠻完整的,如果用 letsencrypt 的話,差不多就是這樣了。不過很不幸的是,我用的是自己虛擬出來的 domain name,沒辦法透過 letsencrypt 取得憑證,所以要用自行產生憑證的方式來設定。

cert-manager 建立的 CRD 主要有 Issuer / ClusterIssuer / Certificates,前兩個是設定取得憑證的方式,最後一個則是要產生哪些 domain 的憑證。建立和產生憑證的流程稍後再談,先回頭看 istio。

istio 如果不搭配 cert-manager 而是自行產生憑證來使用的話,可以直接參考官網的做法。
不過每個網站都要自行產生憑證太麻煩了,既然 istio 支援搭配 cert-manager 的方式,那就來試看看吧。

搭配 cert-manager 的設定方式,可先參考官網

首先是要變更 values.yaml 裡的設定,主要在於 enable istio 的 ingress 和 cert-manager。
相關變更過的設定如下:
gateways:
  enabled: true
  istio-ingressgateway:
    sds:
      enabled: true
  istio-egressgateway:
    enabled: false
certmanager:
  enabled: true
  email: duan@example.com
global:
  k8sIngress:
    enabled: true
    gatewayName: ingressgateway
    enableHttps: true

上面這設定花不少時間測試,目前在 istio 1.2.2 是可以 work 的。重點是要 enable ingressgateway 的 sds ,而 istio-egressgateway 預設是沒啟用的,不過我在測試過程中曾經 enable 而造成問題,所以修改成 enabled: false

設定更新上去後,會發現 istio-ingressgateway 這個 pod 裡的 containeer 由原本的一個變成兩個,多了一個名稱為 ingress-sds 的 container。

sds 全名是 secret discovery service ,這是官網的介紹
這邊有一個坑,不過不會馬上發現,我是到所有東西都設定好後,發現連不上 https 網頁,去查資料才發現的。

檢查 istio-system namespace 裡的 CRD gateways.networking.istio.io 的 istio-autogenerated-k8s-ingress 的設定裡,有 https 的相關設定。我參考這篇文章後,把 https 的部分移除後,原本的問題就解決了。
kubectl -n istio-system edit gateways.networking.istio.io istio-autogenerated-k8s-ingress
# 移除以下部分
  - hosts:
    - '*'
    port:
      name: https-default
      number: 443
      protocol: HTTPS
    tls:
      mode: SIMPLE
      privateKey: /etc/istio/ingress-certs/tls.key
      serverCertificate: /etc/istio/ingress-certs/tls.crt
基礎建設的部分都好了之後,可以開始著手實際的設定了,首先設定 cert-manager 的 Issuer。如前面提到的,因為是虛擬的 domain name,所以要設定為自行產生的方式。
apiVersion: certmanager.k8s.io/v1alpha1
kind: Issuer
metadata:
  name: test-selfsigned
  namespace: istio-system
spec:
  selfSigned: {}

然後設定要產生哪個 domain name 的憑證,假設叫 bookinfo.example.com
apiVersion: certmanager.k8s.io/v1alpha1
kind: Certificate
metadata:
  name: test-certificate
  namespace: istio-system
spec:
  secretName: test-certificate
  issuerRef:
    name: test-selfsigned
  commonName: bookinfo.example.com
  dnsNames:
  - bookinfo.example.com
  acme:
    config:
    - http01:
        ingressClass: istio
      domains:
      - bookinfo.example.com
設定完以後,檢查一下憑證是否有產生
# 檢查 status
kubectl -n istio-system describe certificates 
# 檢查是否將產生的金鑰放在指定的 secret 裡
kubectl -n istio-system get secrets test-certificate 
接下來就可以拿 istio 提供的 sample 來驗證一下了。先用 sample 裡的 httpbin ,然後替 httpbin 建立 gateway 及 virtualservice 。

apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: httpbin-gateway
spec:
  selector:
    istio: ingressgateway
  servers:
  - hosts:
    - bookinfo.example.com
    port:
      name: http
      number: 80
      protocol: HTTP
  - hosts:
    - bookinfo.example.com
    port:
      name: https-default
      number: 443
      protocol: HTTPS
    tls:
      credentialName: test-certificate
      mode: SIMPLE
      privateKey: sds
      serverCertificate: sds
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: httpbin
spec:
  hosts:
  - "bookinfo.example.com"
  gateways:
  - httpbin-gateway
  http:
  - match:
    - uri:
        prefix: /status
    - uri:
        prefix: /delay
    route:
    - destination:
        host: httpbin
        port:
          number: 8000

在這邊簡單講一下 gateway、virtualservice 與 kubernetes 本身的 service 元件之間的關係。httpbin 這個 sample application 本身是 listen 80 port ,而它建立的 svc 是 listen 8000 port ,然後轉給 pods 的 80 port。

上面的 virtualservice 則是設定說 "bookinfo.example.com" 這個 url 會連到 cluster 內部名稱為 httpbin 的 host (實際上就是 svc name) 的 8000 port。

透過這樣的機制,讓後端的 app (以這個案例來說就是 httpbin) 本身不需要提供 tls/https ,而是在 gateway 的部分就處理掉了,也就是 ssl termination。

都設定完之後,可以用 curl 來驗證一下 https 的情況了
curl -k  https://bookinfo.example.com/status/418

用瀏覽器的話驗證的話,可以確認一下看到的憑證是不是 cert-manager 產生的。


2019年6月28日 星期五

istio 的安裝設定調整 (2)

istio 預設選項裡 kiali 和 tracing 是 disable 的,不過這次安裝我想測試這部分的功能,所以把 values.yaml 這兩個選項都 enable 起來試看看。另外為了測試 tracing 功能,所以安裝了 istio 提供的範例程式 bookinfo。其中 bookinfo 的瀏覽網址會是 istio ingress 的 ip 加上 /productpage ,例如 http://10.10.10.185/productpage

裝好後,應該會有兩個 UI ,分別來自 jaeger 和 kiali。為了方便測試,我把這兩個 UI 相關的 svc 的 type 都改成 LoadBalancer,這兩個 svc 名稱分別為 jaeger-query 和 kiali。

然後開始瀏覽 bookinfo 的 page ,發現 jaeger-query 的網頁完全沒有 bookinfo 的紀錄。
這邊是我遇到的第一個坑。

jaeger 分成三個元件,query (即 web UI)、agent、collector,從 istio 安裝的 namespace 裡可以看到三個對應的 svc ,即 jaeger-query、jaeger-agent、jaeger-collector。不過仔細一看,這三個 service 都對應到同一個 labels 的 pods (app: jaeger),也就是 istio-tracing。

這是因為 istio 預設使用的是 all-in-one 的 jqeger image,所以這三個 svc 其實都是同一個 pod 在處理 (其實不止這三個,zipkin 這個 svc 也是指向 istio-tracing 這個 pod) 。

不過問題不在這,問題在於 istio 預設的 tracing 採樣率設定為 1 ...%,也就是 100 個 requests 裡只取樣 1 次。這對於實際上線時是有必要的,但對測試 istio 本身的功能是很不方便的。所以要調整一下取樣率,來把它設定為 100%。

查了一下設定的地方,是在 istio-sidecar-injector 這個 configMap 裡的 traceSampling。不過因為是以 json 格式的方式儲存,所以直接修改 confgMap 不太方便。

比較簡單的設定方式,就是修改 values.yaml ,不過 values.yaml 看不出來設定的地方,所以要參考 istio 提供的 values-istio-demo.yaml 裡面 pilot 的部分,把 traceSampling 設定為 100.0,然後更新設定即可。

更新後,每次瀏覽 bookinfo 後,都可以在 jaeger 的 UI 上看到資料了。
接下來看 kiali 的 UI,在登入的部分遇到第二個坑。

進入到 kiali UI 遇到的第一個問題,它會顯示沒有設定帳號密碼之類的訊息。
kiali 會檢查有沒有 kiali 這個 secret,有的話會掛載起來當帳號密碼來源。所以最簡單的解決方式就是手動建立
kubectl create secret generic kiali -n istio-system --from-literal "username=root" --from-literal "passphrase=1234"

建立完之後,刪除 kiali pods ,讓它自動重建一個 pod ,這樣才會把 secret 掛載起來,然後就可以用設定好的資料登入了。

在處理完上面這些後,才發現在 values-istio-demo.yaml 的 kiali 的部分,有一個 createDemoSecret 的選項,所以看起來如果有先設定這個話,是可以自動建立好登入用的帳號密碼。
2019/07/03 補充:如果設定為 createDemonSecret,會自動建立帳號密碼都為 admin 的 secret。

登入後會出現訊息說找不到 grafana 和 jaeger service。官網是這樣說明的

This task does not discuss Jaeger and Grafana. If you already installed them in your cluster and you want to see how Kiali integrates with them, you must pass additional arguments to the helm command, for example:
$ helm template \ --set kiali.enabled=true \ --set "kiali.dashboard.jaegerURL=http://jaeger-query:16686" \ --set "kiali.dashboard.grafanaURL=http://grafana:3000" \ install/kubernetes/helm/istio \ --name istio --namespace istio-system > $HOME/istio.yaml $ kubectl apply -f $HOME/istio.yaml
不過上面沒有說明到的部分是,kiali 並不是直接取得 jaeger 和 grafana 的資料再顯示到瀏覽器上,而是提供網址讓瀏覽器自己用 ajax 的方式去 jaeger 和 grafana 要資料。

也就是說,上面設定的兩個網址,必須是瀏覽器認得的,而不是 kuberentes 內部的 cluster name or ip。

因為我把 jaeger 和 grafana 的 svc type 都改成 LoadBalancer,所以要設定一個讓瀏覽器可以認得的 url ,例如 http://10.10.10.190:16686/jaeger

修改完後,可以在 kiali 上看到 jaeger 產生的圖了,但是 grafana 還是不行。
查了又查,改了又改,發現 kiali 裡設定的 grafana url 怎麼改,都不會改變瀏覽器去取得 grafana 資料的路徑。看到網路上有人說這是 istio 裡 kiali 的 bug,所以這部分就暫時先不去管了。
2019/07/03 補充:雖然 kiali 都會出現找不到 grafana service 的訊息,但只要有設定好,還是可以出現圖。

另外,如果像前一篇提到的方式,沒有安裝 istio 本身的 prometheus 的話,kiali 也會出現連不到 prometheus 之類的訊息,所以要去設定 kiali 的 configMap,加上 prometheus 的 url。不過這邊是 kiali 直接去抓 prometheus 的資料,所以只要設定 cluster 內部認得的 domain name 即可,例如
    external_services:
      prometheus:
        url: http://prometheus-prometheus-oper-prometheus.monitoring:9090

istio 的安裝設定調整 (1)

關於 istio 的安裝設定,先從外圍的 prometheus 和 grafana 講起,這次安裝的版本是 istio 1.2.0

istio 官方文件的安裝方式裡,預設會安裝一份 prometheus ,也可以調整選項把 grafana 的安裝給 enable。

因為已經安裝了 prometheus-operator ,希望能夠整合到一起。
比較簡單的方式,就是讓 istio 依照預設也安裝一份 prometheus ,然後從 prometheus-operator 的 granfana 新增一個 data source 即可。
不過最好的情況,當然還是維持只有一個 prometheus 是比較好的做法。

這邊有一篇文章說明了如何用 prometheus-oprator 來收集 isito 的資訊。
我是設定好後才看到上面那篇文章,做法上大致相同,只是我用的方式比較刻苦一點。

先取得 istio 裡的 prometheus 的 config ,最簡單的方法是先依預設方式安裝好一份 istio ,然後從裝好的 prometheus 裡取出設定檔,或者依照上面那篇文章提供的設定內容。

原本以為直接修改 prometheus  secrets 裡的設定就好,發現 secrets 裡的設定檔是壓縮過的格式 prometheus.yaml.gz 。不過想說這也不是什麼大問題,取出來解壓縮,修改完在壓縮後更新回去就好。
kubectl -n monitoring get secrets prometheus-prometheus-prometheus-oper-prometheus -o jsonpath='{.data.prometheus\.yaml\.gz}' | base64 -d | gunzip > /tmp/prometheus.yaml

然後把前面取出的設定整合到 /tmp/prometheus.yaml ,然後 gzip、base64 最後 kubectl edit 貼回 secret 就搞定。

咦?不 work 。仔細一看,kubectl edit 修改完儲存後,再進去又變回原本的內容。

仔細查看 prometheus-operator 的機制,是要透過另外的 secrets 來新增 scrape config
最簡單的方式,就是在 prometheus-operator values.yaml 的 additionalScrapeConfigs 加上自訂的 scrape config

如果之前沒有設定過 additionalScrapeConfigs ,那在這次設定完之後,會發現安裝的 namespace 下多了一個 secrets prometheus-prometheus-oper-prometheus-scrape-confg,新增的設定就在這個 secret 裡
kubectl -n monitoring get secrets prometheus-prometheus-oper-prometheus-scrape-confg -o jsonpath='{.data.additional-scrape-configs\.yaml}' | base64 -d


這個沒有 gzip 壓縮過了,之後如果要再新增 scrape config,可以直接修改這個 secret,就可以不用再透過 values.yaml 了。雖然透過 helm 來更新還是比較方便的,不過經常是因為在測試設定的方式來做更新,一直透過 helm 會讓安裝版本記錄更動太多。比較好的方式是測試設定正確後,再修改 values.yaml 做更新,這樣可以確保 helm history 裡的安裝歷史紀錄的版本都是可以運作的。

把 prometheus 搞定後,剩下的 grafana 就比較簡單了。

我是直接把 istio 目錄下的 charts/grafana/dashboards/ 裡的所有 json file ,一個一個從 grafana UI 裡做 import 。如果有些 dashboard 沒資料,可能是因為 promethues scape config 沒設定好的關係,不過至少 gallary pilot mixer 三個 dashboard 應該是有資料的。

簡單的部分先到這邊了。

2019年6月20日 星期四

讓 prometheus-operator 監控 cluster 外部的 metrics

故事的緣起是在 enable 公司的 ceph dashboard plugin 時,發現 ceph 還有一個 prometheus 的 plugin ,這個 plugin 是一個 prometheus exporter ,也就是 enable 這個 plugin 後,就可以從
http://[ceph ip]:9283/metrics

取得 ceph 的 metrics data。那要如何設定讓 kubernetes 內的 prometheus 可以抓到 ceph 的 metrics ?故事就是這樣開始的...

我是用 prometheus-operator 的方式安裝的,這篇對 prometheus-operator 的架構有很不錯的介紹。從這邊可以知道,要新增 metrics 的關鍵在於 servicemonitor 這個 resource ,這是安裝 prometheus-operator 新增的 CRD resource 。

所以新增一個 servicemonitor 去設定要拉 metrics 的來源 service 就好了,簡單吧?
事情好像沒想像中那麼美好,ceph 是在 kubernetes 外部,cluster 內部沒有 ceph 相關的 svc 設定,這段要自己搞定。

凡事問 google ,找到這篇,節省了非常多的時間。

建立 ep => 建立 svc => 建立 servicemonitor 
打完收工。

喔,不,文章中有一些小地方需要修正一下。
最主要的關鍵是文章裡 "BOOM" 那行找到的 label ,和現在 prometheus-operator 的設定不一樣,這應該是版本的關係。

修正 label 的部分,以及一些必要的修改 (像 ip 之類的),剩下的還有一些小錯誤要修正,調整完後就差不多真的完工了。

kubectl apply 上面設定好的 yaml 後,就可以從 prometheus 的 UI 來驗證一下
* 從 Status 的 target 和 service discovery 可以看到新增的 servicemonitor 名稱
* 從資料篩選的地方輸入 ceph 就會自動跳出很多符合的名稱可以篩選,例如 ceph_bluefs_bytes_written_slow

prometheus 這邊搞定後,就往 grafana 前進,隨便找個 for ceph 的 dashboard ,例如編號 7056,從 grafana 匯入這個 dashboard ,就可以從 grafana 看到 ceph 的資料了。

就數值的部分和 ceph 本身的 dashboard 看起來都差不多,不過有一些小地方可能有點問題。畢竟是用別人現成的 dashboard,這個微調的部分就暫時不管了。

這樣對 prometheus-operator 的瞭解又多了一點,之後要拉其他的 application 的 metrics 就比較有把握了。

Log collect by Loki

比較常看到的 log collect 會使用 ELK 或 EFK 的架構,因為之前有安裝過 ELK 覺得太笨重,所以想試看看 Loki ,這樣 metrics (Prometheus) 和 logging (Loki) 就可以共用一套前端 (Grafana) 來處理。

Loki 基本上非常好安裝,用 helm 安裝的話甚至連 values.yaml 都不用修改。
helm repo add loki https://grafana.github.io/loki/charts
helm repo update
helm install -n loki --namespace logging loki/loki-stack

然後因為我原本就已經有安裝 prometheus 和 grafana ,所以直接到 grafana 的 UI 去設定。新增一個 data source ,選擇 Loki。在設定 loki url 的地方,如果 loki 安裝的 namespace 和 grafana 一樣,就可以直接照官方設定為 http://loki:3100/

因為我是設定在不同的 namespace,所以要修改為
http://loki.logging:3100

這個只要略懂 kubernetes 內部 DNS 運作方式的話,應該很好理解和判斷。
設定好之後,到 grafana 選單的 Explore 選項,就可以看到 log data 慢慢出現了。

loki 是用 promtail 來收集 log ,所以會看到 promtail 這個 DaemonSet 在每一個 node 上。而 promtail 預設是收集 container 的 log  ,也就是 /var/lib/docker/contaibners 及 /var/log/pods 裡的 log。 promtail 的設定可以參考 helm chart 裡的 charts/promtail/values.yaml 這個檔案。

如果要將前一篇的 audit log (或者連 host 的 syslog 一起) 也放進 loki 的話,理論上應該是改 promtail 設定是最適合的。 不過對 helm chart 不熟,對 promtail 設定的語法也不熟 (語法和 prometheus 一樣) ,而且也想嘗試用其他方式餵資料給 loki 看看,所以選擇用 fluentd 來做。

我是直接找一個現成的專案來試,這個專案把 loki plugin for fluentd 包進去,只要改幾個地方就可以運作了。

先修改 values.yaml,設定 loki 的 url ,因為我預定安裝在和 loki 一樣的 namesapce ,所以只要設定
http://loki:3100

還有設定 source 的 log path ,例如
path /var/log/kubernetes/kube-audit

另外要修改 templates/05-deployment.yaml 這個檔案,裡面有一個 nodeSelector 設定,目的是篩選 master node 來 deploy (因為只有 master node 上會有 audit log)。
但是預設的 nodeSelector 的篩選條件 (role: master) 和我現有的環境不符合,我也不想為了這個去設定 node 的 label ,所以修改這邊的設定為
node-role.kubernetes.io/master:
另外因為我這邊是多台 master node ,如果以 deployment 方式部署較不適合,所以把 Deployment 改為 DaemonSet ,在這份 yaml 中,直接這樣修改是沒問題的。
都修改完之後,就可以 deploy 了。
helm install --name=fluentd-loki ./ --namespace=logging

然後就可以到 grafana 的 UI 去做驗證了。
選擇 label 為 {env="dev"} 就可以看到透過 fluentd 取得的 audit log 了。
那這個 label 是哪來的? 

回頭看 values.yaml 的設定裡,有一個 extra_labels ,就是設定透過 fluentd 傳進去的 log 會有什麼 label 。

接著可以參考 grafana 官網,看看怎樣篩選需要的 log ,例如要找出 audit log 裡,verb 是 create 類型的,可以這樣篩選
{env="dev"} verb="create"


到此先告一段落囉。

2019年6月19日 星期三

kubernetes 內建的 audit 機制

官方資料裡有關於 audit 尚稱完整的說明,不過在實際上與設定上還是有一些需要摸索的地方,在這邊做個紀錄。

audit 的功能,是透過 kubernetes API Server 來做,所以首先是要修改 apiserver 的設定。如果是用 kubeadm 安裝,設定檔是在 /etc/kubernetes/manifests/kube-apiserver.yaml

不過在修改設定檔之前,要先設定好 audit 相關的 policy file 提供給 apiserver ,因為 apiserver 是屬於 static pod ,一修改設定就會直接重啟 pod。

Policy 設定的方式稍後再說,先用官方提供的最簡範例
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
- level: Metadata

為了方便測試 (稍後說明),將上面內容存放在 /etc/kubernetes/pki/audit-policy.yaml ,然後就可以開始修改 apiserver 的設定檔了。

修改的內容最主要是開啟 audit 及相關參數,這邊先只設定必要的參數,分別是 policy file 的路徑、audit log 的路徑、以及 log 的格式
    - --audit-policy-file=/etc/kubernetes/pki/audit-policy.yaml
    - --audit-log-path=/var/log/kube-audit
    - --audit-log-format=json

前面提到 policy file 放置的路徑是為了方便,原因在於 apiserver 是一個 pod , pods 裡是看不到 host 的檔案的,而觀察一下 apiserver 的 yaml 設定,可以發現 /etc/kubernetes/pki 這個路徑是有掛載在 pod 裡,所以為了方便,把 policy file 放在這邊就不需要額外設定,就可以讓 apiserver 取得。如前所述,修改完設定檔後,apiserver pod 會自動重啟。

接下來用 kubectl exec 的方式進到 apiserver pod ,確認一下 pod 裡的 /var/log/kube-audit 是否有產生。以上面設定的 policy 內容來說,會把所有事件的 metadata 都記錄下來,所以如果有設定正確的話,應該會看到 audit log 如雪片般的飛來。

不過把 log file 放在 pod 裡,這樣不容易管理與稽核,這時有四種做法。

第一種是把 audit log 改送到 stdout (修改 log path 的設定變成 --audit-log-path=-),這樣就會和其他的 container 一樣,會把 log 寫在 host 的 /var/lib/docker/containers/ 裡。不過這種做法,會因為 apiserver 的 log format 是 json ,要和其他 container log 一起管理會產生差異,所以我放棄這種方式。

第二種方式是透過 sidecar。有些 app/image 沒有把 log 丟到 stdout/stderr ,就要透過 sidecar 的方式去收集 log ,常見的做法是把 fluentd 作為 sidecar 來搜集 log 。不過因為 apiserver 是 static pod,我也不太傾向用 sidecar 的方式來處理。

第三種是把 audit log 設定為 webhook ,把 log 送到指定的 url 。因為 apiserver static pod ,這種方式需要在 cluster 外部設定好可以收 log 的 webhook ,如 logstash 或 fluentd 等。因為我目標是把 log 整合在 cluster  內部的 app ,所以也沒採取這種方式。

最後一種方式就是直接把 host 上的路徑掛載到 apiserver 上產生 log 的路徑下。
例如我規劃把 audit log 會寫到 host 的 /var/log/kubernetes/ 下,就修改 apiserver 設定檔,增加內容如下
# 指定 host 路徑
  - hostPath:
      path: /var/log/kubernetes
      type: DirectoryOrCreate
    name: k8s-audit
# 指定掛載路徑
    - mountPath: /var/log
      name: k8s-audit
設定正確的話,就可以看到 host 的 /var/log/kuberentes/kube-audit 裡的 audit log 如雪片般的飛來。

在思考如何把 audit log 和其他 log 一起管理之前,先來調整一下 policy file ,否則 log file 成長的會非常快。

官網上對於 policy file 有一些基本的介紹,不過這篇寫得比較完整

設定檔裡面的幾個主要的項目:

* omitStages
  指 audit log 發出來的時機
  選項有 RequestReceived / ResponseStarted / ResponseComplete / Panic

* level
  記錄 log 內容的完整程度,None 就是不紀錄
  選項有 None / Metadata / Request / RequestResponse

然後參考 rules 裡的其他設定項,如 verbs、users、resources 等,仔細看會發現和 kuberntes RBAC 的設定方式一樣,例如 verbs 的選項有
["get", "list", "watch", "create", "update", "patch", "delete"]


所以假設今天要設定的規則是,只記錄 namespace 的 create 和 delete,其他通通不記錄,那 rules 的寫法會如下
apiVersion: audit.k8s.io/v1beta1
kind: Policy
omitStages:
  - "RequestReceived"
rules:
  - level: Request
    verbs: ["create", "delete"]
    resources:
    - group: ""
      resources: ["namespaces"]
  - level: None

到這邊可能遇到兩個問題

* 修改 policy file 並不會讓 apiserver pod 重啟,如果用 kubectl delete pod 的方式對 static pod 也是無效的,所以最簡單的方式,就是把 apiserver 的設定檔移到別的路徑下再移回來,就可以讓 apiserver pod 重啟了。

* 如果 master node 不止一台,而且有用 haproxy 之類的擋在前面做 load balance ,就會遇到和我一樣的問題。因為對 api server 的 request 是會輪詢的,所以要測試時會出現非預期的結果。我一開始沒注意到這個問題,想說一樣的 kubectl create ns 指令,為什麼有時候有產生 audit log ,有時候沒有。後來修改 kubectl context 直接指向有變更設定的 apiserver ,就可以很正常的依測試產生 audit log 了。


audit 的部份到這邊告一段落,接下來把 host 上面的 audit log 和其他 log 整合一起,就是另一個故事了。


2019年6月13日 星期四

alertmanager 初探

環境是延續之前安裝的 prometheus-operator

alertmanager 裡的設定主要分為幾個項目:

* receivers : 設定有哪些可以接收 alert 的接收端
* routes: 設定哪些 rules 要送到哪些 receiver
* templates: alert 的內容可以套用預先定義好的 template

prometheus-operator 裡預設的 receivers 是 null ,也就是完全不會發通知出去,這設定可以從 alertmanager 的 UI 的 Status 看到。

如果要從 command line 看 alertmanager 的設定,可以用以下指令
kubectl -n monitoring get secrets alertmanager-prometheus-prometheus-oper-alertmanager -o jsonpath='{.data.alertmanager\.yaml}' | base64 -d



首先替 alertmanager 加上 receiver 的設定,一樣還是修改 values.yaml 再用 helm upgrade 更新會比較方便。找到 values.yaml 關於 alertmanaget 的 config 的地方,修改成如下 (以通知到 slack channel 為例) :
config:
  global:
    slack_api_url: SLACK_HOOK_URL  // 置換成自己設定好的 slack hook url
  route:
    group_by: ['alertname', 'datacenter', 'app', 'job']
    receiver: 'slack-notifications'
  receivers:
    - name: 'slack-notifications'
      slack_configs:
      - channel: '#k8s-alertmanager'
         text: '{{ template "slack.k8sbridge.txt" . }}'

最後那行是說 text 要套用指定的 template,所以同樣在 values.yaml 設定 template 的部分
templates:
  - '*.tmpl'
這兩行是告訴 alertmanager 要 include 預設路徑下的所有 .tmpl 檔案
然後把後面 templatesFiles 那一大段都 uncomment,只修改 define 的 tempalte name,要對應前面設定的 text 裡的 template name,例如:
  templateFiles:
    template_1.tmpl: |-
        {{ define "cluster" }}{{ .ExternalURL | reReplaceAll ".*alertmanager\\.(.*)" "$1" }}{{ end }}
        {{ define "slack.k8sbridge.tet" }}

設定完以後,用 helm upgrade ,沒問題的話就會看到指定的 slack channel 裡出現 alertmanager 送出來的訊息。


2019-09-21 補充:
helm 安裝的 prometheus-operator 的 values.yaml 裡的 templateFiles 裡的範例,少了兩個最後的 {{ end }}
這個 template 語法和 go-template 是一樣的,所以 {{ define "xxxx" }} 和 {{ range }} 後面都要配對一個 {{ end }}

之前設定時,正好去忙別的事,沒有 check log 驗證設定是否正確。這兩天看到 alertmanager log 裡有錯誤訊息,抽時間來修正一下。

2019年6月11日 星期二

helm 的零碎小知識

記錄一下 helm 常使用的指令及一些小細節

安裝 helm 時,會先用 helm init 建立和 kubernetes cluster 的關係,也就是會 deploy 一個叫 tiller 的 deployment 到 kubernetes (預設在 namespace kube-system) 。

後續所有套件安裝的資訊以及將套件 deploy 到 kubernetes 都是透過 tiller 進行,因此 tiller 需要足夠的權限,而 tiller 的權限則是在 helm init 決定]。所以在 helm init 前,需要在 namespace kube-system 先建立好一個 service account,並且給予足夠的權限。然後在 helm init 時,用 --service-account 指定建立好的 service account。

helm 基本指令的使用都還蠻直覺的,不過在看指令前,先簡單了解一下 helm 的套件運作方式。

在 kubernetes 裡,部署、設定、管理之類的動作都是透過 yaml 進行,而一個軟體的部署可能會用到很多個 yaml 檔來設定,而且其中可能很多設定內容都是重複的。helm 做的事情就是把要讓使用者設定的項目提取出來,放在 values.yaml 裡,然後在安裝時,把 values.yaml 裡的設定內容轉化成真正的 yaml 檔案後,再部署到 kubernetes 裡。

所以用 helm 安裝套件時,大部分的情況都是會需要變更 values.yaml 的設定後再部署。也就是說大部分的安裝指令會類似如下:
helm install -f values.yaml -n pm --namespace monitoring stable/prometheus

會指定安裝後的名稱、要安裝到哪個 namespace、以及要讀取哪個 yaml 檔裡的設定。

但是一般來說,除非對要安裝的套件很熟了,否則不會知道 values.yaml 裡設定了什麼,所以要先透過 helm 指令,將套件抓下來後,取得裡面的 values.yaml 來修改。
helm fetch stable/prometheus

這樣就可以把 promethues 這個套件下載回來,下載的套件是一個 tarball ,解開來後,裡面就會有一個 values.yaml ,可以修改完後,用上述的指令進行安裝。

然而通常情況是,很多套件一開始使用時,values.yaml 設定的結果還需要調整。
有些小地方,可能直接用 kubectl 指令做一些調整就好,但是有些設定可能需要修改 values.yaml 再跑 upgrade 會方便一些。
helm upgrade -f values.yaml pm stable/prometheus

在刪除套件時,因為我大部分都是測試性質,不太會有 rollback 需求,所以通常都會完整的移除套件。有沒有加 --purge 參數的差別可以看這篇
helm delete pm --purge

另外較常用的指令大約就是
helm ls  #查看已安裝的套件
helm status pm # 查看已安裝套件名稱為 pm 的安裝資訊

最後是關於 private repo for helm
我是用 harbor 當 helm 的 repo,在啟動 harbor 時,加上 --with-chartmuseum 參數,harbor 就可以做為 helm 的 private repo。

要讓 helm 使用 private repo ,要先進行一些設定

* 新增 repo 的 url 到 helm,用 helm repo add 指令
* 安裝 helm plugin ,後續才能用 helm push 把套件推送到 repo 上。

關於這部分,這篇有蠻完整的介紹。
我個人覺得這部分的使用有點麻煩,因為我架設的 harbor 有使用 ssl ,因此要 helm repo add 或 helm push 時,都要指定一堆憑證路徑的參數 (--ca-file --cert-file --key-file)。
雖然這可以透過一些方式來簡化,但要自動化處理時,要把這些加入流程又要注意 security 會有點繁瑣。

2019年6月8日 星期六

CKA 小記

距離當初考 CKA 三個月了,簡單記錄一下考試的經驗。

題目共 24 題,其中 6~9 分的大約 4 題,其他 20 題則是 1~4 分。

在分數比較多的 4 題裡,大致上是如下題型:

1. 提供 ca 等憑證的路徑,建立起 master node ,也就是官網裡的 tls bootstrapping 的部分

2. secrets 的設定使用的情境

3. cluster 內的 DNS 查詢,也就是會先要求建立一個 pod 和 service ,然後用 nslookup 分別作正反查

4. 用 etcdctl 對 etcd 做備份

剩下的 20 題裡,大約有 2 題是要求找出 cluster 運作出問題的地方 (一題是針對 worker node ,另一題是針對 master node)。剩下的大部分都是比較基本的 kubectl 操作,從最簡單的 "找出 cpu 使用最高的 pod",到比較複雜一點的 "deployment 的 rollout history and rollback",不會有太刁鑽的題目。

雖然考試會用到 5~6 個 cluster ,但前 18 題左右都是在第一個 cluster 裡面考基本的題型,然後後面 3 個 cluster 則是建立或除錯 cluster,最後可能有一個是要設定 storage 相關的題目。

也就是說,即使放棄掉比較偏系統維運方面的題目,大致上也還有 80 分上下是可以透過 minikube 之類的環境就可以輕易做練習來準備的範圍。

所以如果單純以準備考試來說,基本操作的熟練度要夠,以及針對已知題型的練習也足夠,那要低空飛過只求及格是不會太難的。當然臨場的反應、細心還有緊張程度都會有些影響,這就只能靠個人去控制了。

如果去網路上查 CKA 如何準備,十篇大概會有七八篇會推薦 kubernetes the hard way ,主要是針對 tls bootstrapping 那一題。

不過我個人是覺得,如果是新手,為了準備這題,所花費準備的時間可能是其他所有題目的好幾倍,而且可能會相當挫折。把 kubernetes the hard way 從頭理解一遍當然是不錯,但是就準備考試本身來說,反而可能會是個不利的因素。除非已經是有一定熟悉度,目標往滿分邁進,否則我會建議掌握基本分和基本題型會輕鬆許多。

如果希望事先了解一下考試的介面,可以參考 arush 提供的測試環境
雖然和實際考試的有所差異,但可以作為參考和練習。

另外當初為了準備考試,有用 asciinema 錄製一些操作記錄,有興趣也可以參考。


就和所有的認證一樣,考過不代表高手,高手也不是一定就能考過。準備是必要的 (對大多數人而言),而考過只是代表一個起步而已。起步早晚不是重點,能一步步走下去才是考驗。



2019年6月6日 星期四

Prometheus-Operator 安裝設定

用 helm 安裝的 prometheus-operator 預設就有 mixin 的相關設定及 grafana dashboard ,會比用 helm 安裝的 prometheus 方便許多 (要自己安裝 grafana 及相關設定,如果要安裝 mixin 還要設定 prometheus rules 及 grafana dashboard 等)

用 helm 安裝的話,好像沒什麼好寫的,呃
helm install stable/prometheus-operator --name prometheus --namespace monitoring

搞定了!
先把 promthues 和 grafana 的 service type 改為 Loadbalancer ,來看看安裝結果。

咦?首先從 prometheus 的 UI -> Status -> Targets 看到 etcd 的 endpoints 狀態是 down
cluster 正常的情況下, etcd 的狀態不可能是 down 的,所以這應該是 prometheus 設定問題。

查了一下資料,這是因為 etcd 預設是需要憑證才能 access (如果是用 kubeadm 安裝的話)。就好像用 etcdctl 的話,要指定 ca crt key 等憑證的參數才能 access etcd 一樣。

從查到的資料來看,文章裡的解決方法,已經不適合目前安裝的版本 (prometheus-operator-5.7.0)。

目前的版本已經考慮到這個情況,在 values.html 已經提供可參考的設定方式。
helm fetch stable/prometheus-operator
tar zxvf prometheus-operator-5.7.0.tgz

然後修改 prometheus-operator/values.yaml 及相關設定,流程大致如下:

1. 尋找 etcd 相關的設定,可以看到有提供相關的設定,在 serviceMonitor 的後面調整為
schema: https
caFile: "/etc/prometheus/secrets/etcd-certs/ca.crt"
certFile: "/etc/prometheus/secrets/etcd-certs/healthcheck-client.crt"
keyFile: "/etc/prometheus/secrets/etcd-certs/healthcheck-client.key"

2. 尋找 secrets 的設定,調整為
secrets:
   - etcd-certs

3. 先還不要更新 helm package ,上面第二個設定的意思是,要把能夠 access etcd  的幾個憑證檔放到 etcd-certs 這個 secrets (這個 secrets 不存在,是要自行建立的)
kubectl -n monitoring create secret generic etcd-certs --from-file=/etc/kubernetes/pki/etcd/healthcheck-client.crt --from-file=/etc/kubernetes/pki/etcd/healthcheck-client.key --from-file=/etc/kubernetes/pki/etcd/ca.crt
   注意,這是要在 master node 上執行的,因為如果是用 kubeadm 安裝的話,這幾個憑證在 master node 才有。如果 etcd 是自行安裝,或是透過其他方式如 kubespray 安裝的話,就要自行調整產生 secrets 的方法了。

4. 可以更新 helm package 了
helm upgrade -f prometheus-operator/values.yaml prometheus stable/prometheus-operator

到這邊算是修改完成了,從 prometheus UI 上已經可以看到 etcd dashboard 裡有資料了。

遇到問題的話,可以檢查幾個地方

1. 檢查 servicemonitors 裡 etcd 相關的設定, servicemonitors 是 operator 建立的 CRD
kubectl -n monitoring edit servicemonitors prometheus-prometheus-oper-kube-etcd
   看裡面的設定是否有包含 values.yaml 裡有關憑證的設定

2. 檢查 pods 路徑下是否有確實把指定的 secrets 掛載起來
#kubectl -n monitoring exec -it prometheus-prometheus-prometheus-oper-prometheus-0
$ ls /etc/prometheus/secrets/etcd-certs/