在Kubernetes集羣環境中,一個完整的應用或服務都會涉及爲數衆多的組件運行,各組件所在的Node及實例數量都是可變的。日誌子系統如果不做集中化管理,則會給系統的運維支撐造成很大的困難,因此有必要在集羣層面對日誌進行統一收集和檢索等工作。
在容器中輸出到控制檯的日誌,都會以*-json.log的命名方式保存在/var/lib/docker/containers/目錄下,這就爲日誌採集和後續處理奠定了基礎。
Kubernetes推薦採用Fluentd+Elasticsearch+Kibana完成對系統和容器日誌的採集、查詢和展現工作。
部署統一的日誌管理系統,需要以下兩個前提條件。
◎ API Server正確配置了CA證書。
◎ DNS服務啓動、運行。
系統部署架構
該系統的邏輯架構如下圖:
在各Node上都運行了一個Fluentd容器,採集本節點/var/log和/var/lib/docker/containers兩個目錄下的日誌進程,將其彙總到Elasticsearch集羣,最終通過Kibana完成和用戶的交互工作。
這裏有一個特殊的需求:Fluentd必須在每個Node上運行,爲了滿足這一需求,我們通過下面幾種方式部署Fluentd。
◎ 直接在Node主機上部署Fluentd。
◎ 利用kubelet的--config參數,爲每個Node都加載Fluentd Pod。
◎ 利用DaemonSet讓Fluentd Pod在每個Node上運行。
創建Elasticsearch RC和Service
Elasticsearch的RC和Service定義如下:
---
apiVersion: v1
kind: ReplicationController
metadata:
name: elasticsearch-logging-v1
namespace: kube-system
labels:
k8s-app: elasticsearch-logging
version: v1
kubernetes.io/cluster-service: "true"
spec:
replicas: 2
selector:
k8s-app: elasticsearch-logging
version: v1
template:
metadata:
labels:
k8s-app: elasticsearch-logging
version: v1
kubernetes.io/cluster-service: "true"
spec:
containers:
- image: gcr.io/google_containers/elasticsearch:1.8
name: elasticsearch-logging
resources:
# keep request = limit to keep this container in guaranteed class
limits:
cpu: 100m
requests:
cpu: 100m
ports:
- containerPort: 9200
name: db
protocol: TCP
- containerPort: 9300
name: transport
protocol: TCP
volumeMounts:
- name: es-persistent-storage
mountPath: /data
volumes:
- name: es-persistent-storage
emptyDir: {}
---
apiVersion: v1
kind: Service
metadata:
name: elasticsearch-logging
namespace: kube-system
labels:
k8s-app: elasticsearch-logging
kubernetes.io/cluster-service: "true"
kubernetes.io/name: "Elasticsearch"
spec:
ports:
- port: 9200
protocol: TCP
targetPort: db
selector:
k8s-app: elasticsearch-logging
執行kubectl create -f elastic-search.yml命令完成創建。
在命令成功執行後,首先驗證Pod的運行情況。通過kubectl get pods--namespaces=kube-system獲取運行中的Pod。
接下來通過Elasticsearch頁面驗證其功能。
首先,執行# kubectl cluster-info命令獲取Elasticsearch服務的地址。
然後,使用# kubectl proxy命令對API Server進行代理,在成功執行後輸出如下內容:
這樣就可以在瀏覽器上訪問URL地址https://20.0.40.51:12567/api/v1/namespaces/kube-system/services/elasticsearch-logging/proxy,來驗證Elasticsearch的運行情況了,返回的內容是一個JSON文檔。
在每個Node上啓動Fluentd
Fluentd的DaemonSet定義如下:
apiVersion: extensions/v1beta1
kind: DaemonSet
metadata:
name: fluentd-cloud-logging
namespace: kube-system
labels:
k8s-app: fluentd-cloud-logging
spec:
template:
metadata:
namespace: kube-system
labels:
k8s-app: fluentd-cloud-logging
spec:
containers:
- name: fluentd-cloud-logging
image: gcr.io/google_containers/fluentd-elasticsearch:1.17
resources:
limits:
cpu: 100m
memory: 200Mi
env:
- name: FLUENTD_ARGS
value: -q
volumeMounts:
- name: varlog
mountPath: /var/log
readOnly: false
- name: containers
mountPath: /var/lib/docker/containers
readOnly: false
volumes:
- name: containers
hostPath:
path: /var/lib/docker/containers
- name: varlog
hostPath:
path: /var/log
通過kubectl create命令創建Fluentd容器:
查看創建的結果:
結果顯示Fluentd DaemonSet正常運行,還啓動了3個Pod,與集羣中的Node數量一致。
接下來使用# kubectl logs fluentd-cloud-logging-7tw9z命令查看Pod的日誌,在Elasticsearch正常工作的情況下,我們會看到類似下面這樣的日誌內容:
# kubectl logs fluentd-cloud-logging-7tw9z
Connection opened to Elasticsearch cluster =>
{:host => "elasticsearch-logging", :port=>9200, :scheme=>"http"}
運行Kibana
至此已經運行了Elasticsearch和Fluentd,數據的採集和匯聚已經完成,接下來使用Kibana展示和操作數據。
Kibana的RC和Service定義如下:
---
apiVersion: v1
kind: ReplicationController
metadata:
name: kibana-logging-v1
namespace: kube-system
labels:
k8s-app: kibana-logging
version: v1
kubernetes.io/cluster-service: "true"
spec:
replicas: 1
selector:
k8s-app: kibana-logging
version: v1
template:
metadata:
labels:
k8s-app: kibana-logging
version: v1
kubernetes.io/cluster-service: "true"
spec:
containers:
- name: kibana-logging
image: gcr.io/google_containers/kibana:1.3
resources:
# keep request = limit to keep this container in guaranteed class
limits:
cpu: 100m
requests:
cpu: 100m
env:
- name: "ELASTICSEARCH_URL"
value: "http://elasticsearch-logging:9200"
ports:
- containerPort: 5601
name: ui
protocol: TCP
---
apiVersion: v1
kind: Service
metadata:
name: kibana-logging
namespace: kube-system
labels:
k8s-app: kibana-logging
kubernetes.io/cluster-service: "true"
kubernetes.io/name: "Kibana"
spec:
ports:
- port: 5601
protocol: TCP
targetPort: ui
selector:
k8s-app: kibana-logging
通過kubectl create -f kibana-rc-svc.yml命令創建Kibana的RC和Service。
查看Kibana的運行情況。
結果表明運行均已成功。通過kubectl cluster-info命令獲取Kibana服務的URL地址。
同樣通過kubectl proxy命令啓動代理,在出現“Starting to serve on 127.0.0.1:8001”字樣之後,用瀏覽器訪問URL地址http://ip:port/api/v1/proxy/namespaces/kube-system/services/kibana-logging即可訪問Kibana頁面。
第1次進入頁面時需要進行一些設置,選擇所需選項後單擊create按鈕。
然後單擊discover按鈕,就可以正常查詢日誌了。
在搜索欄輸入“error”關鍵字,可以搜索出包含該關鍵字的日誌記錄。
同時,通過左邊菜單中Fields相關的內容對查詢的內容進行限定。
至此,Kubernetes集羣範圍內的統一日誌收集和查詢系統就搭建完成了。
Kubernetes的審計機制
Kubernetes爲了加強對集羣操作的安全監管,從1.4版本開始引入審計機制,主要體現爲審計日誌(Audit Log)。審計日誌按照時間順序記錄了與安全相關的各種事件,這些事件有助於系統管理員快速、集中瞭解以下問題:
◎ 發生了什麼事情?
◎ 作用於什麼對象?
◎ 在什麼時間發生?
◎ 誰(從哪兒)觸發的?
◎ 在哪兒觀察到的?
◎ 活動的後繼處理行爲是怎樣的?
下面是兩條Pod操作的審計日誌示例。
第一條:
第二條:
API Server把客戶端的請求(Request)的處理流程視爲一個“鏈條”,這個鏈條上的每個“節點”就是一個狀態(Stage),從開始到結束的所有Request Stage如下。
◎ RequestReceived:在Audit Handler收到請求後生成的狀態。
◎ ResponseStarted:響應的Header已經發送但Body還沒有發送的狀態,僅對長期運行的請求(Long-running Requests)有效,例如Watch。
◎ ResponseComplete:Body已經發送完成。
◎ Panic:嚴重錯誤(Panic)發生時的狀態。
Kubernets從1.7版本開始引入高級審計特性(AdvancedAuditing),可以自定義審計策略(選擇記錄哪些事件)和審計存儲後端(日誌和Webhook)等,開啓方法爲增加kube-apiserver的啓動參數--feature-gates=AdvancedAuditing=true。注意:在開啓AdvancedAuditing後,日誌的格式有一些修改,例如新增了上述Stage信息;從Kubernets 1.8版本開始,該參數默認爲true。
kube-apiserver在收到一個請求後(如創建Pod的請求),會根據Audit Policy(審計策略)對此請求做出相應的處理。
我們可以將Audit Policy視作一組規則,這組規則定義了有哪些事件及數據需要記錄(審計)。當一個事件被處理時,規則列表會依次嘗試匹配該事件,第1個匹配的規則會決定審計日誌的級別(Audit Level),目前定義的幾種級別如下(按級別從低到高排列)。
◎ None:不生成審計日誌。
◎ Metadata:只記錄Request請求的元數據如requesting user、timestamp、resource、verb等,但不記錄請求及響應的具體內容。
◎ Request:記錄Request請求的元數據及請求的具體內容。
◎ RequestResponse:記錄事件的元數據,以及請求與應答的具體內容。
None以上的級別會生成相應的審計日誌並將審計日誌輸出到後端,當前的後端實現如下。
(1)Log backend:以本地日誌文件記錄保存,爲JSON日誌格式,我們需要對API Server的啓動命令設置下列參數。
◎ --audit-log-path:指定日誌文件的保存路徑。
◎ --audit-log-maxage:設定審計日誌文件保留的最大天數。
◎ --audit-log-maxbackup:設定審計日誌文件最多保留多少個。
◎ --audit-log-maxsize:設定審計日誌文件的單個大小,單位爲MB,默認爲100MB。
審計日誌文件以audit-log-maxsize設置的大小爲單位,在寫滿後,kube-apiserver將以時間戳重命名原文件,然後繼續寫入audit-log-path指定的審計日誌文件;audit-log-maxbackup和audit-log-maxage參數則用於kube-apiserver自動刪除舊的審計日誌文件。
(2)Webhook backend:回調外部接口進行通知,審計日誌以JSON格式發送(POST方式)給Webhook Server,支持batch和blocking這兩種通知模式,相關配置參數如下。
◎ --audit-webhook-config-file:指定Webhook backend的配置文件。
◎ --audit-webhook-mode:確定採用哪種模式回調通知。
◎ --audit-webhook-initial-backoff:指定回調失敗後第1次重試的等待時間,後繼重試等待時間則呈指數級遞增。
Webhook backend的配置文件採用了kubeconfig格式,主要內容包括遠程審計服務的地址和相關鑑權參數,配置示例如下:
# clusters refers to the remote service.
clusters:
- name: name-of-remote-audit-service
cluster:
certificate-authority: /path/to/ca.pem # CA for verifying the remote service.
server: https://audit.example.com/audit # URL of remote service to query. Must use 'https'.
# users refers to the API server's webhook configuration.
users:
- name: name-of-api-server
user:
client-certificate: /path/to/cert.pem # cert for the webhook plugin to use
client-key: /path/to/key.pem # key matching the cert
# kubeconfig files require a context. Provide one for the API server.
current-context: webhook
contexts:
- context:
cluster: name-of-remote-audit-service
user: name-of-api-sever
name: webhook
--audit-webhook-mode則包括以下選項。
◎ batch:批量模式,緩存事件並以異步批量方式通知,是默認的工作模式。
◎ blocking:阻塞模式,事件按順序逐個處理,這種模式會阻塞API Server的響應,可能導致性能問題。
◎ blocking-strict:與阻塞模式類似,不同的是當一個Request在RequestReceived階段發生審計失敗時,整個Request請求會被認爲失敗。
(3)Batching Dynamic backend:一種動態配置的Webhook backend,是通過AuditSink API 動態配置的,在Kubernetes 1.13版本中引入。
需要注意的是,開啓審計功能會增加API Server的內存消耗量,因爲此時需要額外的內存來存儲每個請求的審計上下文數據,而增加的內存量與審計功能的配置有關,比如更詳細的審計日誌所需的內存更多。我們可以通過kube-apiserver中的--audit-policy-file參數指定一個Audit Policy文件名來開啓API Server的審計功能。
通常審計日誌可以以本地日誌文件方式保存,然後使用Fluentd作爲Agent採集該日誌並存儲到Elasticsearch,用Kibana等UI界面對日誌進行展示和查詢。
小結: 今天的內容到此結束
謝謝大家的瀏覽。