如何在 K8S 集群範圍使用 imagePullSecret?

在這篇文章中,我將向你展示如何在 Kubernetes 中使用 imagePullSecrets。

imagePullSecrets 簡介

Kubernetes 在每個 Pod 或每個 Namespace 的基礎上使用 imagePullSecrets 對私有容器註冊表進行身份驗證。要做到這一點,你需要創建一個秘密與憑據:

{% note warning %}
⚠️ 警告

現在隨著公共鏡像倉庫(如:docker.io 等)開始對匿名用戶進行限流,配置公共倉庫的身份認證也變得有必要。
{% endnote %}

kubectl create secret docker-registry image-pull-secret \
  -n <your-namespace> \
  --docker-server=<your-registry-server> \
  --docker-username=<your-name> \
  --docker-password=<your-password> \
  --docker-email=<your-email>

例如配置 docker.io 的 pull secret:

kubectl create secret docker-registry image-pull-secret-src \
        -n imagepullsecret-patcher \
        --docker-server=docker.io \
        --docker-username=caseycui \
        --docker-password=c874d654-xxxx-40c6-xxxx-xxxxxxxx89c2 \
        [email protected]

{% note info %}
ℹ️ 資訊

如果 docker.io 啟用了「2 階段認證」,可能需要創建 Access Token(對應上面的 docker-password,創建鏈接在這裡:帳號 -> 安全
{% endnote %}

現在我們可以在一個 pod 中使用這個 secret 來下載 docker 鏡像:

apiVersion: v1
kind: Pod
metadata:
  name: busybox
  namespace: private-registry-test
spec:
  containers:
    - name: my-app
      image: my-private-registry.infra/busybox:v1
  imagePullSecrets:
    - name: image-pull-secret

另一種方法是將它添加到命名空間的默認 ServiceAccount 中:

kubectl patch serviceaccount default \
  -p "{\"imagePullSecrets\": [{\"name\": \"image-pull-secret\"}]}" \
  -n <your-namespace>

在 K8S 集群範圍使用 imagePullSecrets

我找到了一個叫做 imagepullsecret-patch 的工具,它可以在你所有的命名空間上做這個:

wget //raw.githubusercontent.com/titansoft-pte-ltd/imagepullsecret-patcher/185aec934bd01fa9b6ade2c44624e5f2023e2784/deploy-example/kubernetes-manifest/1_rbac.yaml
wget //raw.githubusercontent.com/titansoft-pte-ltd/imagepullsecret-patcher/master/deploy-example/kubernetes-manifest/2_deployment.yaml

kubectl create ns imagepullsecret-patcher

編輯下載的文件,一般需要修改image-pull-secret-src的內容,這個 pull secret 就會應用到 K8S 集群範圍。

nano 1_rbac.yaml
nano 2_deployment.yaml
kubectl apply -f 1_rbac.yaml
kubectl apply -f 2_deployment.yaml

這裡背後創建的資源有:

  1. NameSpace
  2. RBAC 許可權相關:
    1. imagepullsecret-patcher ServiceAccount
    2. imagepullsecret-patcher ClusterRole,具有對 service account 和 secret 的所有許可權
    3. imagepullsecret-patcher ClusterRoleBinding,為 imagepullsecret-patcher ServiceAccount 賦予 imagepullsecret-patcher ClusterRole 的許可權。
  3. 全局 pull secret image-pull-secret-src,裡面是你的 K8S 全局包含的所有的鏡像庫地址和認證資訊。
  4. Deployment imagepullsecret-patcher,指定 ServiceAccount 是 imagepullsecret-patcher 就有了操作 service account 和 secret 的所有許可權,並將上面的 secret 掛載到 Deployment pod 內。

可以包含多個鏡像庫地址和認證資訊,如:

{
    "auths": {
        "docker.io": {
            "username": "caseycui",
            "password": "c874xxxxxxxxxxxxxxxx1f89c2",
            "email": "[email protected]",
            "auth": "Y2FzxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxWMy"
        },
        "quay.io": {
            "auth": "ZWFzdxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxlXWmpNPQ==",
            "email": ""
        }
    }
}

base64 編碼後寫到 secret 的 .dockerconfigjson 欄位即可:

apiVersion: v1
kind: Secret
metadata:
  name: image-pull-secret-src
  namespace: imagepullsecret-patcher
data:
  .dockerconfigjson: >-
    eyJhdXRocyI6eyJkb2NrZXIuaW8iOnsidXNlcm5hbWUiOiJjYXNleWN1aSIsInB.............................................IiwiZW1haWwiOiIifX19
type: kubernetes.io/dockerconfigjson

啟動後的 pod 會在所有 NameSpace 下創建 image-pull-secret secret(內容來自於image-pull-secret-src) 並把它 patch 到 default service account 及該 K8S 集群的所有 ServiceAccount 里,日誌如下:

time="2022-01-12T16:07:30Z" level=info msg="Application started"
time="2022-01-12T16:07:30Z" level=info msg="[default] Created secret"
time="2022-01-12T16:07:30Z" level=info msg="[default] Patched imagePullSecrets to service account [default]"
time="2022-01-12T16:07:30Z" level=info msg="[kube-system] Created secret"
time="2022-01-12T16:07:31Z" level=info msg="[kube-system] Patched imagePullSecrets to service account [node-controller]"
...
time="2022-01-12T16:07:37Z" level=info msg="[kube-public] Created secret"
time="2022-01-12T16:07:37Z" level=info msg="[kube-public] Patched imagePullSecrets to service account [default]"
time="2022-01-12T16:07:38Z" level=info msg="[kube-node-lease] Created secret"
time="2022-01-12T16:07:38Z" level=info msg="[kube-node-lease] Patched imagePullSecrets to service account [default]"
time="2022-01-12T16:07:38Z" level=info msg="[prometheus] Created secret"
time="2022-01-12T16:07:39Z" level=info msg="[prometheus] Patched imagePullSecrets to service account [default]"
...
time="2022-01-12T16:07:41Z" level=info msg="[imagepullsecret-patcher] Created secret"
time="2022-01-12T16:07:41Z" level=info msg="[imagepullsecret-patcher] Patched imagePullSecrets to service account [default]"
time="2022-01-12T16:07:41Z" level=info msg="[imagepullsecret-patcher] Patched imagePullSecrets to service account [imagepullsecret-patcher]"

今後我們只需要更新 image-pull-secret-src 這一個即可了。👍️👍️👍️

Kyverno policy

Kyverno policy 可以實現同樣的效果:

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: sync-secret
spec:
  background: false
  rules:
  - name: sync-image-pull-secret
    match:
      resources:
        kinds:
        - Namespace
    generate:
      kind: Secret
      name: image-pull-secret
      namespace: "{{request.object.metadata.name}}"
      synchronize: true
      clone:
        namespace: default
        name: image-pull-secret
---
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: mutate-imagepullsecret
spec:
  rules:
    - name: mutate-imagepullsecret
      match:
        resources:
          kinds:
          - Pod
      mutate:
        patchStrategicMerge:
          spec:
            imagePullSecrets:
            - name: image-pull-secret  ## imagePullSecret that you created with docker hub pro account
            (containers):
            - (image): "*" ## match all container images