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 產生的。