Kubernetes排程流程與安全(七)

MXC肖某某發表於2020-09-17

一、Kubernetes中的排程流程

1,介紹

  Scheduler 是 k8s 中的排程器,主要的任務是把定義的 Pod 分配到叢集的節點上。Scheduler 是作為一個單獨的程式執行的,啟動之後會一直監聽 apiserver。聽起來很簡單,但有很多要考慮的問題:

  • 公平:如何保證每個節點都能被分配資源。
  • 資源利用率高:叢集所有資源最大化被使用。
  • 效率:排程的效能要好,能夠儘快的對大批量的 Pod 完成排程工作。
  • 靈活:允許使用者根據自己的需求控制排程邏輯。

  排程過程分為幾個部分:

  1. 首先是過濾掉不滿足條件的節點,這個過程稱為 predicate
  2. 然後對滿足條件的節點按照優先順序排序,這個是 priority
  3. 最後從中選擇優先順序最高的節點。

2,親和性

  親和性策略,可以設定 Pod 在建立時更傾向建立在哪個節點上。其有兩個維度可設定:

  • 節點親和性:設定節點的相關資訊,在建立 Pod 的時候與節點資訊進行匹配。
  • Pod 親和性:設定 Pod 的相關資訊,在建立 Pod 的時候與已存在的 Pod 資訊進行匹配。

  不管是節點親和性還是 Pod 親和性,其設定策略中都有 軟策略 和 硬策略 兩種方案。

  • 軟策略(preferredDuringSchedulingIgnoredDuringExecution):Pod 傾向於建立在某個節點上,如果條件不符合,可以建立在其它節點上。
  • 硬策略(requiredDuringSchedulingIgnoredDuringExecution):Pod 必須被建立某個節點上,如果條件不符合,那麼 Pod 會一直處於 pending 狀態。

  a)節點親和性

軟策略示例

apiVersion: v1
kind: Pod
metadata:
  name: node-pre-pod
spec:
  containers:
  - name: nginx-container
    image: hub.xcc.com/my-xcc/my-nginx:v1
    ports:
    - containerPort: 80
  affinity:
    nodeAffinity:               #親和性
      preferredDuringSchedulingIgnoredDuringExecution: #軟策略
      - weight: 1               #權重值
        preference:
          matchExpressions:     #匹配表示式
          - key: kubernetes.io/hostname #節點標籤名
            operator: In        #操作運算關係符
            values:             #標籤值
            - worker01

硬策略示例

apiVersion: v1
kind: Pod
metadata:
  name: node-req-pod
spec:
  containers:
  - name: nginx-container
    image: hub.xcc.com/my-xcc/my-nginx:v1
    ports:
    - containerPort: 80
  affinity:
    nodeAffinity:            #節點親和性
      requiredDuringSchedulingIgnoredDuringExecution: #硬策略
        nodeSelectorTerms:
        - matchExpressions:
          - key: kubernetes.io/hostname
            operator: NotIn
            values:
            -worker01

兩個策略組合。這樣的Pod會先匹配符合硬策略的節點,然後在匹配符合軟策略的節點。

apiVersion: v1
kind: Pod
metadata:
  name: node-pod
spec:
  containers:
  - name: nginx-container
    image: hub.xcc.com/my-xcc/my-nginx:v1
    ports:
    - containerPort: 80
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: kubernetes.io/hostname
            operator: NotIn
            values:
            - worker01
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 1
        preference:
          matchExpressions:
          - key: kubernetes.io/hostname
            operator: In
            values:
            - worker02

  b)Pod親和性

  • podAffinity:Pod 與指定的 Pod 在同一拓撲域。
  • podAntiAffinity:Pod 與指定的 Pod 不在同一拓撲域。

  拓撲域:使用 topologyKey 屬性(下方示例中有)定義的值,通俗來講就是節點的標籤。

示例

apiVersion: v1
kind: Pod
metadata:
  name: pod-aff-pod
spec:
  containers:
  - name: nginx-container
    image: hub.xcc.com/my-xcc/my-nginx:v1
    ports:
    - containerPort: 80
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: pod-name
            operator: In
            values:
            - nginx-pod
        topologyKey: kubernetes.io/hostname
    podAntiAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 1
        podAffinityTerm:
          labelSelector:
            matchExpressions:
            - key: pod-name
              operator: In
              valuses:
              - nginx-pod
        topologyKey: kubernetes.io/hostname
排程策略匹配標籤操作符拓撲域支援排程目標
nodeAffinity 主機 In、NotIn、Exists、DoesNotExist、Gt、Lt 指定主機
podAffinity Pod In、NotIn、Exists、DoesNotExist Pod 與指定 Pod 在同一拓撲域
podAnitAffinity Pod In、NotIn、Exists、DoesNotExist Pod 與指定 Pod 不在同一拓撲域

3,汙點和容忍

  汙點(taint)和容忍(toleration)相互相互配合,可以用來避免 Pod 被分配到不適合的節點上。每個節點可以有0個或者多個 taint,這表示對於那些不能容忍這些 taint 的 Pod,是不會被建立在這些節點上的。如果將 toleration 應用於 Pod 上,則表示這些 Pod 可以被排程到具有匹配 taint 的節點上。

  通俗來講,就是節點上可以設定汙點(taint),如果建立 Pod 時不設定 Pod 的容忍(toleration)的話,那麼該 Pod 就不可能被建立在有汙點的節點上。master 節點預設有一個汙點,這也就是為什麼前面我們建立的 Pod 一直沒有被建立在 master 節點上的原因。

kubectl get node
kubectl describe node master01
......
Taints:             node-role.kubernetes.io/master:NoSchedule
......

  a)汙點

key=value:effect
  • key=value:每一個汙點都有一個 key 和 value 作為 汙點的標籤,其中 value 可以為空,即 key:effect。我們上面列出的 master 預設汙點就是這種格式。

  • effect:描述汙點的作用,支援以下三種選項:

    • NoSchedule:k8s 不會將 Pod 排程到具有該汙點的 Node 上。
    • PreferNoSchedule:k8s 儘量避免將 Pod 排程到具有該汙點的 Node 上。
    • NoExecute:k8s 不會將 Pod 排程到具有該汙點的 Node 上,同時會將 Node 上已存在的 Pod 驅逐出去。

    注意:上面描述的前提是所有 Pod 是沒有設定容忍的。

設定、去除汙點

# 設定汙點
kubectl taint nodes node01 check=test:NoSchedule
# 去除汙點,只需要在最後加一個 '-'
kubectl taint nodes node01 check=test:NoSchedule-
# 檢視某個node的汙點,其結果中的 Taints 欄位便是汙點
kubectl describe node node01

設定多master防止資源浪費

kubectl taint nodes <node-name> node-role.kubernetes.io/master=:PreferNoSchedule

  b)容忍

  設定了汙點的 node 將根據汙點的作用(effect)與 Pod 之間產生互斥的關係。但是我們可以在建立 Pod 時為 Pod 設定容忍,意味著 Pod 在建立時可以容忍汙點的存在,可以被排程到存在汙點的 node 上。

spec:
  tolerations:
  - effect: "NoSchedule"
    key: "key"
    operator: "Exists"
    tolerationSeconds: 3600
    value: "value"
  • key、value、effect:需要與 node 上的汙點標籤資訊一致。

  • operator:表示 key 與 value 的關係。

    • Exists:忽略 value 的值,只要 key 匹配上即可。
    • Equal:預設為 Equal。
  • tolerationSeconds:如果 effect 的值為 Noexecute,那麼 tolerationSeconds 表示 Pod 在被驅逐之前還可以保留執行的時間。

#當不指定 key 時,表示容忍所有的汙點 key
spec:
  tolerations:
  - operator: "Exists"
#當不指定 effect 時,表示容忍所有的汙點作用
spec:
  tolerations:
  - key: "key"
    operator: "Exists"

4,指定排程節點

  如果你想要指定 Pod 被排程到具體的 node 上,那麼你可以這樣做

kind: Pod
spec:
  nodeName: node01  #將 Pod 直接排程到指定的 node 節點上,會跳過 Scheduler 的排程策略,是強制匹配
# 或者
spec:
  nodeSelector:  #通過 k8s 的 label-selector 機制選擇節點,由排程器策略匹配 label,而後排程 Pod 到目標節點上,該匹配規則屬於強制約束。
    kubernetes.io/hostname: node01

二、Kubernetes中的認證、鑑權和准入控制

1,機制說明

  Kubernetes作為一個分散式叢集的管理工具,保證叢集的安全性是一個重要的任務。API Server是叢集內部各元件通訊的中介,也是外部控制的入口。所以Kubernetes的安全機制基本就是圍繞保護API Server來設計的。Kubernetes使用了認證(Authetication)、鑑權(Authorization)、准入控制(Admission Control)三步來保證API Server的安全。

2,認證(Authentication)

  • HTTP Token 認證:通過一個 Token 來識別合法使用者
  • HTTP Base 認證:通過 使用者名稱+密碼 的方式認證(base64加密)
  • 最嚴格的 HTTPS 證書認證:基於 CA 根證書籤名的客戶端身份認證方式 

  a)HTTPS 證書認證:

                   Kubernetes排程流程與安全(七)

 

  b)需要認證的節點

 兩種型別
  • Kubenetes 元件對 API Server 的訪問:kubectl、Controller Manager、Scheduler、kubelet、kube-proxy
  • Kubernetes 管理的 Pod 對容器的訪問:Pod(dashborad 也是以 Pod 形式執行)
 安全性說明
  • Controller Manager、Scheduler 與 API Server 在同一臺機器,所以直接使用 API Server 的非安全埠訪問, --insecure-bind-address=127.0.0.1
  • kubectl、kubelet、kube-proxy 訪問 API Server 就都需要證書進行 HTTPS 雙向認證
 證書頒發
  • 手動簽發:通過 k8s 叢集的跟 ca 進行簽發 HTTPS 證書
  • 自動簽發:kubelet 首次訪問 API Server 時,使用 token 做認證,通過後,Controller Manager 會為kubelet 生成一個證書,以後的訪問都是用證書做認證了 

  c)kubeconfig

  kubeconfifig 檔案包含叢集引數(CA證書、API Server地址),客戶端引數(上面生成的證書和私鑰),叢集context 資訊(叢集名稱、使用者名稱)。Kubenetes 元件通過啟動時指定不同的 kubeconfifig 檔案可以切換到不同的叢集。

  d)ServiceAccount

  Pod中的容器訪問API Server。因為Pod的建立、銷燬是動態的,所以要為它手動生成證書就不可行了。Kubenetes使用了Service Account解決Pod 訪問API Server的認證問題。

  e)Secret 與 SA 的關係

  Kubernetes 設計了一種資源物件叫做 Secret,分為兩類,一種是用於 ServiceAccount 的 service-account-token, 另一種是用於儲存使用者自定義保密資訊的 Opaque。ServiceAccount 中用到包含三個部分:Token、ca.crt、namespace
  • token是使用 API Server 私鑰簽名的 JWT。用於訪問API Server時,Server端認證
  • ca.crt,根證書。用於Client端驗證API Server傳送的證書
  • namespace, 標識這個service-account-token的作用域名空間
kubectl get secret --all-namespaces 
kubectl describe secret default-token-5gm9r --namespace=kube-system
  預設情況下,每個 namespace 都會有一個 ServiceAccount,如果 Pod 在建立時沒有指定 ServiceAccount,就會使用 Pod 所屬的 namespace 的 ServiceAccount。

           Kubernetes排程流程與安全(七)

3,授權(Authorization)

  上面認證過程,只是確認通訊的雙方都確認了對方是可信的,可以相互通訊。而鑑權是確定請求方有哪些資源的許可權。API Server 目前支援以下幾種授權策略 (通過 API Server 的啟動引數 “--authorization-mode” 設定)。

  • AlwaysDeny:表示拒絕所有的請求,一般用於測試
  • AlwaysAllow:允許接收所有請求,如果叢集不需要授權流程,則可以採用該策略
  • ABAC(Attribute-Based Access Control):基於屬性的訪問控制,表示使用使用者配置的授權規則對使用者請求進行匹配和控制
  • Webbook:通過呼叫外部 REST 服務對使用者進行授權
  • RBAC(Role-Based Access Control):基於角色的訪問控制,現行預設規則 

4,RBAC授權模式

  RBAC(Role-Based Access Control)基於角色的訪問控制,在 Kubernetes 1.5 中引入,現行版本成為預設標準。相對其它訪問控制方式,擁有以下優勢:
  • 對叢集中的資源和非資源均擁有完整的覆蓋
  • 整個 RBAC 完全由幾個 API 物件完成,同其它 API 物件一樣,可以用 kubectl 或 API 進行操作
  • 可以在執行時進行調整,無需重啟 API Server

  a)RBAC 的 API 資源物件說明

  RBAC 引入了 4 個新的頂級資源物件:Role、ClusterRole、RoleBinding、ClusterRoleBinding,4 種物件型別均可以通過 kubectl 與 API 操作 

  b)Role和ClusterRole

  在 RBAC API 中,Role 表示一組規則許可權,許可權只會增加(累加許可權),不存在一個資源一開始就有很多許可權而通過RBAC 對其進行減少的操作;Role 可以定義在一個 namespace 中,如果想要跨 namespace 則可以建立ClusterRole。
kind: Role 
apiVersion: rbac.authorization.k8s.io/v1beta1 
metadata: 
  namespace: default 
  name: pod-reader 
rules: 
- apiGroups: [""] # "" indicates the core API group 
  resources: ["pods"] 
  verbs: ["get", "watch", "list"]

  ClusterRole 具有與 Role 相同的許可權角色控制能力,不同的是 ClusterRole 是叢集級別的,ClusterRole 可以用於:

  • 叢集級別的資源控制( 例如 node 訪問許可權 )
  • 非資源型 endpoints( 例如 /healthz 訪問 )
  • 所有名稱空間資源控制(例如 pods ) 
kind: ClusterRole 
apiVersion: rbac.authorization.k8s.io/v1beta1 
metadata: 
  # "namespace" omitted since ClusterRoles are not namespaced 
  name: secret-reader 
rules: 
- apiGroups: [""] 
  resources: ["secrets"] 
  verbs: ["get", "watch", "list"]

  c)RoleBinding和ClusterRoleBinding

  RoloBinding 可以將角色中定義的許可權授予使用者或使用者組,RoleBinding 包含一組許可權列表(subjects),許可權列表中包含有不同形式的待授予許可權資源型別(users, groups, or service accounts);RoloBinding 同樣包含對被Bind 的 Role 引用;RoleBinding 適用於某個名稱空間內授權,而 ClusterRoleBinding 適用於叢集範圍內的授權。
  將 default 名稱空間的 pod-reader  Role 授予 jane 使用者,此後 jane 使用者在 default 名稱空間中將具有 pod-reader 的許可權 。
kind: RoleBinding 
apiVersion: rbac.authorization.k8s.io/v1beta1 
metadata: 
  name: read-pods 
  namespace: default 
subjects: 
- kind: User 
  name: jane 
  apiGroup: rbac.authorization.k8s.io 
roleRef: 
  kind: Role 
  name: pod-reader 
  apiGroup: rbac.authorization.k8s.io
  RoleBinding 同樣可以引用 ClusterRole 來對當前 namespace 內使用者、使用者組或 ServiceAccount 進行授權,這種操作允許叢集管理員在整個叢集內定義一些通用的 ClusterRole,然後在不同的 namespace 中使用RoleBinding 來引用。
kind: RoleBinding 
apiVersion: rbac.authorization.k8s.io/v1beta1 
metadata:
  name: read-secrets 
  namespace: development             # 只能訪問 development 空間中的 secrets(因為 RoleBinding 定義在 development 名稱空間)
subjects: 
- kind: User 
  name: dave 
  apiGroup: rbac.authorization.k8s.io 
roleRef: 
  kind: ClusterRole                  # RoleBinding 引用了一個 ClusterRole
  name: secret-reader                #這個 ClusterRole 具有整個叢集內對 secrets 的訪問許可權
  apiGroup: rbac.authorization.k8s.io

  使用 ClusterRoleBinding 可以對整個叢集中的所有名稱空間資源許可權進行授權;以下 ClusterRoleBinding 樣例展示了授權 manager 組內所有使用者在全部名稱空間中對 secrets 進行訪問 。

#允許在manger的Group組中的每個人都可以去讀取任意namespace下的secrets
kind: ClusterRoleBinding 
apiVersion: rbac.authorization.k8s.io/v1beta1 
metadata: 
  name: read-secrets-global 
subjects: 
- kind: Group 
  name: manager 
  apiGroup: rbac.authorization.k8s.io 
roleRef: 
  kind: ClusterRole 
  name: secret-reader #餘ClusterRole對應
  apiGroup: rbac.authorization.k8s.io

  d)Resources

  Kubernetes 叢集內一些資源一般以其名稱字串來表示,這些字串一般會在 API 的 URL 地址中出現;同時某些資源也會包含子資源,例如 logs 資源就屬於 pods 的子資源,API 中 URL 樣例如下 

GET /api/v1/namespaces/{namespace}/pods/{name}/log
  如果要在 RBAC 授權模型中控制這些子資源的訪問許可權,可以通過 / 分隔符來實現,以下是一個定義 pods 資資源logs 訪問許可權的 Role 定義樣例 
kind: Role 
apiVersion: rbac.authorization.k8s.io/v1beta1 
metadata: 
  namespace: default
  name: pod-and-pod-logs-reader 
rules: 
- apiGroups: [""] 
  resources: ["pods/log"] 
  verbs: ["get", "list"]

5,實踐(建立使用者管理dev空間)

devuser-csr.json
{
  "CN": "devuser",     #定義使用者
  "hosts": [
    
  ],
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "CN",
      "ST": "BeiJing",
      "L": "BeiJing",
      "O": "k8s",       #定義Group組
      "OU": "System"
    }
  ]
}

下載證書工具

wget https://pkg.cfssl.org/R1.2/cfssl_linux-amd64 
mv cfssl_linux-amd64 /usr/local/bin/cfssl
wget https://pkg.cfssl.org/R1.2/cfssljson_linux-amd64
mv cfssljson_linux-amd64 /usr/local/bin/cfssljson
wget https://pkg.cfssl.org/R1.2/cfssl-certinfo_linux-amd64 mv cfssl-certinfo_linux-amd64 /usr/local/bin/cfssl-certinfo
#建立使用定義的json檔案 cfssl gencert -ca=ca.crt -ca-key=ca.key -profile=kubernetes /root/devuser-csr.json | cfssljson -bare devuser

設定叢集引數

export KUBE_APISERVER="https://172.20.0.113:6443" 
kubectl config set-cluster kubernetes \ 
--certificate-authority=/etc/kubernetes/ssl/ca.pem \ 
--embed-certs=true \ 
--server=${KUBE_APISERVER} \ 
--kubeconfig=devuser.kubeconfig
設定客戶端認證引數 
kubectl config set-credentials devuser \ 
--client-certificate=/etc/kubernetes/ssl/devuser.pem \ 
--client-key=/etc/kubernetes/ssl/devuser-key.pem \ 
--embed-certs=true \ 
--kubeconfig=devuser.kubeconfig 

設定上下文引數 

kubectl config set-context kubernetes \ 
--cluster=kubernetes \ 
--user=devuser \ 
--namespace=dev \ 
--kubeconfig=devuser.kubeconfig 

設定預設上下文

kubectl config use-context kubernetes --kubeconfig=devuser.kubeconfig 
cp -f ./devuser.kubeconfig /root/.kube/config 

建立rolebinding繫結

# 在名為”dev”的名字空間中將admin ClusterRole授予使用者”devuser”
kubectl create rolebinding devuser-admin-binding --clusterrole=admin --user=devuser -- namespace=dev

6,准入控制(Admission Control)

  准入控制是API Server的外掛集合,通過新增不同的外掛,實現額外的准入控制規則。甚至於API Server的一些主要的功能都需要通過 Admission Controllers 實現,比如 ServiceAccount 。

  官方文件上有一份針對不同版本的准入控制器推薦列表,其中最新的 1.14 的推薦列表是:

NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,DefaultTolerationSeconds,Mutat ingAdmissionWebhook,ValidatingAdmissionWebhook,ResourceQuota
列舉幾個外掛的功能:
  • NamespaceLifecycle: 防止在不存在的 namespace 上建立物件,防止刪除系統預置 namespace,刪除
  • namespace 時,連帶刪除它的所有資源物件。
  • LimitRanger:確保請求的資源不會超過資源所在 Namespace LimitRange 的限制。
  • ServiceAccount: 實現了自動化新增 ServiceAccount
  • ResourceQuota:確保請求的資源不會超過資源的 ResourceQuota 限制。

 

相關文章