如何在K8S中優雅的使用私有映象庫 (Docker版)

KAnts發表於2020-12-31

前言

在企業落地 K8S 的過程中,私有映象庫 (專用映象庫) 必不可少,特別是在 Docker Hub 開始對免費使用者限流之後, 越發的體現了搭建私有映象庫的重要性。

私有映象庫不但可以加速映象的拉取還可以避免因特有的"網路問題"導致映象拉取失敗尷尬。

當然部署了私有映象庫之後也需要對映象庫設定一些安全策略,大部分私有映象庫採用 IP訪問策略+認證 (非公開專案) 的方式對映象庫進行安全保護。

那麼對於含有認證限制的映象庫,在 K8S 中該如何優雅的整合呢?
下文就總結了在 K8S 中使用私有映象庫的幾種情況和方式。

在 K8S 中使用私有映象庫

首先要確定私有映象庫的授權使用方式,在針對不同的使用方式選擇對應的認證配置。

  1. 針對節點 (Node)

    這個應該是企業使用 K8S 時最常用的方式,一般也只要使用這個就夠了,並且該方案几乎是使用了私有映象庫之後必不可少的配置,它可以做到:
    在節點環境中進行一定的配置,不需要在 K8S 中進行其它的配置即可享有具體私有庫的許可權。
    該方案對該節點上的所有 Pods 生效,同時還對非 Pods 映象生效,例如: kubelet 的 pause 映象,這個非常關鍵

  2. 針對服務賬號 (ServiceAccount)、針對名稱空間 (Namespace)

    配置了該 ServiceAccount 的 Pod 都享有這個 ServiceAccount 所配置的映象庫認證設定。
    還可以利用 K8S 中 default ServiceAccount 機制,達到對一個具體名稱空間中沒有特殊設定的所有 Pod 生效。

  3. 針對 Pod

    針對具體的 Pod 進行認證配置,該 Pod 就會具有私有庫的許可權。

    Deployment、DaemonSet、StatefulSet、CronJob、Job 等資源都使用了PodTemplate 最終都會以具體的 Pod 資源體驗,所以在 PodTemplate 中配置也算對 Pod 配置。

配置步驟

前提條件

  1. 一個可用私有映象庫 (可用採用 Harbor 搭建)
  2. 私有映象庫的賬號和密碼 (推薦只給只讀許可權)
  3. CRI 基於 Docker (其它的 CRI 暫沒有驗證)

針對節點 (Node) 配置

  1. 編寫 Docker 配置檔案
  2. 將 Docker 配置檔案放在指定位置
  3. 重啟 kubelet

編寫 Docker 配置檔案

首先編寫 Docker 的認證配置檔案, 格式如下:

{
  "auths": {
    "<HOST>": {
      "auth": "<BASIC_AUTHORIZATION>"
    }
  }
}

<HOST> 為私有映象庫的地址, 例如: hub.docker.com
<BASIC_AUTHORIZATION>BASE64(<USERNAME>:<PASSWORD>)
例如: cmVhZGVyOjEyMzQ1Ng==, 其中賬號是: reader, 密碼是: 123456
使用 : 拼接後進行 base64

完整的配置檔案, 例

{
  "auths": {
    "hub.docker.com": {
      "auth": "cmVhZGVyOjEyMzQ1Ng=="
    },
    "harbor.domain.cn": {
      "auth": "cmVhZGVyOiFAIzQ1Ng=="
    }
  }
}

如有多個映象庫在 auths 節中進行新增即可。

將 Docker 配置檔案放在指定位置

推薦放在 kubelet 根目錄中, 配置檔案需以 config.json 命名。
預設的 kubelet 根目錄一般為 /var/lib/kubelet (如有修改進行替換即可)
也就是需要放置在 /var/lib/kubelet/config.json

還可以放在以下位置:

{--root-dir:-/var/lib/kubelet}/config.json
{cwd of kubelet}/config.json
${HOME}/.docker/config.json
/.docker/config.json
{--root-dir:-/var/lib/kubelet}/.dockercfg
{cwd of kubelet}/.dockercfg
${HOME}/.dockercfg
/.dockercfg

參考文件:
https://kubernetes.io/docs/concepts/containers/images/#configuring-nodes-to-authenticate-to-a-private-registry

放在 ${HOME} 開頭的位置

需要在 kubelet service 環境中配置 HOME 的路徑, 不然不會生效, 例如: HOME=/root
下面是使用 kubeadm 安裝的環境中可用的指令碼, 如果不是請自行配置

echo "HOME=${HOME}" >> /var/lib/kubelet/kubeadm-flags.env

重啟 kubelet

如果 init 不是 systemd,請自行替換服務重啟的命令

systemctl daemon-reload; systemctl restart kubelet

針對服務賬號 (ServiceAccount)、針對名稱空間 (Namespace)

  1. 建立一個 Docker 登錄檔機密資源
  2. 設定 ServiceAccount 的 imagePullSecrets
  3. 將 Pod 的 serviceAccountName 設定為該 ServiceAccount 的名稱

建立一個 Docker 登錄檔機密資源

使用 kubectl cli 建立登錄檔機密資源

kubectl create secret docker-registry <SECRET_NAME> --docker-server=<DOCKER_REGISTRY_SERVER> --docker-username=<DOCKER_USER> --docker-password=<DOCKER_PASSWORD> -n <NAMESPACE>

其中
<SECRET_NAME> 是機密資源的名稱, 在編輯 sa 資源的時需要引用
<DOCKER_REGISTRY_SERVER> 是私有映象庫的伺服器地址
<DOCKER_USER> 是私有映象庫認證的賬號
<DOCKER_PASSWORD> 是私有映象庫認證的密碼
<NAMESPACE> 是名稱空間名稱

示例命令如下:

kubectl create secret docker-registry docker-reader-secret --docker-server=harbor.domain.cn --docker-username=reader --docker-password=123456 -n basic

使用 yaml 建立登錄檔機密資源

apiVersion: v1
data:
  .dockerconfigjson: eyJhdXRocyI6eyJET0NLRVJfUkVHSVNUUllfU0VSVkVSIjp7InVzZXJuYW1lIjoiRE9DS0VSX1VTRVIiLCJwYXNzd29yZCI6IkRPQ0tFUl9QQVNTV09SRCIsImF1dGgiOiJSRTlEUzBWU1gxVlRSVkk2UkU5RFMwVlNYMUJCVTFOWFQxSkUifX19
kind: Secret
metadata:
  name: docker-reader-secret 
  namespace: default
type: kubernetes.io/dockerconfigjson

.dockerconfigjson 是base64之後的字串, 具體內容參考 "編寫 Docker 配置檔案" 節中的內容

kubectl apply -f docker-reader-secret.yaml

設定 ServiceAccount 的 imagePullSecrets

apiVersion: v1
kind: ServiceAccount
metadata:
  name: service1
  namespace: basic
secrets:
- name: service1-token-mp4qs
imagePullSecrets:
- name: docker-reader-secret

將資源的 serviceAccountName 設定為該 ServiceAccount 的名稱

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
      serviceAccountName: service1

如何針對名稱空間內的所有Pod?

K8S 中有個預設的機制,會在名稱空間中建立一個名稱為 default 的 ServiceAccount (sa) 資源。
並且在資源沒有單獨指定 serviceAccountName 時, 預設使用 default 作為serviceAccountName。
所以我們只需設定 default ServiceAccount 的 imagePullSecrets 即可對該名稱空間中沒有特殊指定 serviceAccountName 欄位的 Pod 生效了。

針對 Pod

  1. 建立一個 Docker 登錄檔機密資源
  2. 設定 Pod 的 imagePullSecrets

建立一個 Docker 登錄檔機密資源

參考 "建立一個 Docker 登錄檔機密資源" 節中的內容

一個具體的 Pod

apiVersion: v1
kind: Pod
metadata:
  name: foo
  namespace: awesomeapps
spec:
  containers:
    - name: foo
      image: janedoe/awesomeapp:v1
  imagePullSecrets:
    - name: docker-reader-secret

針對具有 PodTemplate 內容的資源

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
      imagePullSecrets:
      - name: docker-reader-secret

最後

如果大家的私有映象庫還沒有采用認證,就趕緊行動起來吧!
血的教訓,安全問題刻不容緩。

相關文章