前言
在企業落地 K8S 的過程中,私有鏡像庫 (專用鏡像庫) 必不可少,特別是在 Docker Hub 開始對免費用戶限流之後, 越發的體現了搭建私有鏡像庫的重要性。
私有鏡像庫不但可以加速鏡像的拉取還可以避免因特有的"網絡問題"導致鏡像拉取失敗尷尬。
當然部署了私有鏡像庫之後也需要對鏡像庫設置一些安全策略,大部分私有鏡像庫採用 IP訪問策略+認證 (非公開項目) 的方式對鏡像庫進行安全保護。
那麼對於含有認證限制的鏡像庫,在 K8S 中該如何優雅的集成呢?
下文就總結了在 K8S 中使用私有鏡像庫的幾種情況和方式。
在 K8S 中使用私有鏡像庫
首先要確定私有鏡像庫的授權使用方式,在針對不同的使用方式選擇對應的認證配置。
- 針對節點 (Node)
這個應該是企業使用 K8S 時最常用的方式,一般也只要使用這個就夠了,並且該方案幾乎是使用了私有鏡像庫之後必不可少的配置,它可以做到:
在節點環境中進行一定的配置,不需要在 K8S 中進行其它的配置即可享有具體私有庫的權限。
該方案對該節點上的所有 Pods 生效,同時還對非 Pods 鏡像生效,例如: kubelet 的 pause 鏡像,這個非常關鍵。
- 針對服務賬號 (ServiceAccount)、針對命名空間 (Namespace)
配置了該 ServiceAccount 的 Pod 都享有這個 ServiceAccount 所配置的鏡像庫認證設置。
還可以利用 K8S 中 default ServiceAccount 機制,達到對一個具體命名空間中沒有特殊設置的所有 Pod 生效。
- 針對 Pod
針對具體的 Pod 進行認證配置,該 Pod 就會具有私有庫的權限。
Deployment、DaemonSet、StatefulSet、CronJob、Job 等資源都使用了PodTemplate 最終都會以具體的 Pod 資源體驗,所以在 PodTemplate 中配置也算對 Pod 配置。
配置步驟
前提條件
- 一個可用私有鏡像庫 (可用採用 Harbor 搭建)
- 私有鏡像庫的賬號和密碼 (推薦只給只讀權限)
- CRI 基於 Docker (其它的 CRI 暫沒有驗證)
針對節點 (Node) 配置
- 編寫 Docker 配置文件
- 將 Docker 配置文件放在指定位置
- 重啟 kubelet
編寫 Docker 配置文件
首先編寫 Docker 的認證配置文件, 格式如下:
https://files.mdnice.com/point.png); height: 30px; width: 100%; background-size: 40px; background-repeat: no-repeat; background-color: #282c34; margin-bottom: -7px; border-radius: 5px; background-position: 10px 10px;">{
"auths": {
"<HOST>": {
"auth": "<BASIC_AUTHORIZATION>"
}
}
}
<HOST>
為私有鏡像庫的地址, 例如: hub.docker.com
<BASIC_AUTHORIZATION>
為 BASE64(<USERNAME>:<PASSWORD>)
例如: cmVhZGVyOjEyMzQ1Ng==
, 其中賬號是: reader
, 密碼是: 123456
使用 :
拼接後進行 base64
完整的配置文件, 例
https://files.mdnice.com/point.png); height: 30px; width: 100%; background-size: 40px; background-repeat: no-repeat; background-color: #282c34; margin-bottom: -7px; border-radius: 5px; background-position: 10px 10px;">{
"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
。
還可以放在以下位置:
https://files.mdnice.com/point.png); height: 30px; width: 100%; background-size: 40px; background-repeat: no-repeat; background-color: #282c34; margin-bottom: -7px; border-radius: 5px; background-position: 10px 10px;">{--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
參考文檔:
放在 ${HOME} 開頭的位置
需要在 kubelet service 環境中配置 HOME 的路徑, 不然不會生效, 例如: HOME=/root
下面是使用 kubeadm
安裝的環境中可用的腳本, 如果不是請自行配置
https://files.mdnice.com/point.png); height: 30px; width: 100%; background-size: 40px; background-repeat: no-repeat; background-color: #282c34; margin-bottom: -7px; border-radius: 5px; background-position: 10px 10px;">echo "HOME=${HOME}" >> /var/lib/kubelet/kubeadm-flags.env
重啟 kubelet
如果 init 不是 systemd,請自行替換服務重啟的命令
https://files.mdnice.com/point.png); height: 30px; width: 100%; background-size: 40px; background-repeat: no-repeat; background-color: #282c34; margin-bottom: -7px; border-radius: 5px; background-position: 10px 10px;">systemctl daemon-reload; systemctl restart kubelet
針對服務賬號 (ServiceAccount)、針對命名空間 (Namespace)
- 創建一個 Docker 註冊表機密資源
- 設置 ServiceAccount 的 imagePullSecrets
- 將 Pod 的 serviceAccountName 設置為該 ServiceAccount 的名稱
創建一個 Docker 註冊表機密資源
使用 kubectl cli 創建註冊表機密資源
https://files.mdnice.com/point.png); height: 30px; width: 100%; background-size: 40px; background-repeat: no-repeat; background-color: #282c34; margin-bottom: -7px; border-radius: 5px; background-position: 10px 10px;">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>
是命名空間名稱
示例命令如下:
https://files.mdnice.com/point.png); height: 30px; width: 100%; background-size: 40px; background-repeat: no-repeat; background-color: #282c34; margin-bottom: -7px; border-radius: 5px; background-position: 10px 10px;">kubectl create secret docker-registry docker-reader-secret --docker-server=harbor.domain.cn --docker-username=reader --docker-password=123456 -n basic
使用 yaml 創建註冊表機密資源
https://files.mdnice.com/point.png); height: 30px; width: 100%; background-size: 40px; background-repeat: no-repeat; background-color: #282c34; margin-bottom: -7px; border-radius: 5px; background-position: 10px 10px;">apiVersion: v1
data:
.dockerconfigjson: eyJhdXRocyI6eyJET0NLRVJfUkVHSVNUUllfU0VSVkVSIjp7InVzZXJuYW1lIjoiRE9DS0VSX1VTRVIiLCJwYXNzd29yZCI6IkRPQ0tFUl9QQVNTV09SRCIsImF1dGgiOiJSRTlEUzBWU1gxVlRSVkk2UkU5RFMwVlNYMUJCVTFOWFQxSkUifX19
kind: Secret
metadata:
name: docker-reader-secret
namespace: default
type: kubernetes.io/dockerconfigjson
.dockerconfigjson
是base64之後的字符串, 具體內容參考 "編寫 Docker 配置文件" 節中的內容
https://files.mdnice.com/point.png); height: 30px; width: 100%; background-size: 40px; background-repeat: no-repeat; background-color: #282c34; margin-bottom: -7px; border-radius: 5px; background-position: 10px 10px;">kubectl apply -f docker-reader-secret.yaml
設置 ServiceAccount 的 imagePullSecrets
https://files.mdnice.com/point.png); height: 30px; width: 100%; background-size: 40px; background-repeat: no-repeat; background-color: #282c34; margin-bottom: -7px; border-radius: 5px; background-position: 10px 10px;">apiVersion: v1
kind: ServiceAccount
metadata:
name: service1
namespace: basic
secrets:
- name: service1-token-mp4qs
imagePullSecrets:
- name: docker-reader-secret
將資源的 serviceAccountName 設置為該 ServiceAccount 的名稱
https://files.mdnice.com/point.png); height: 30px; width: 100%; background-size: 40px; background-repeat: no-repeat; background-color: #282c34; margin-bottom: -7px; border-radius: 5px; background-position: 10px 10px;">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
- 創建一個 Docker 註冊表機密資源
- 設置 Pod 的 imagePullSecrets
創建一個 Docker 註冊表機密資源
參考 "創建一個 Docker 註冊表機密資源" 節中的內容
一個具體的 Pod
https://files.mdnice.com/point.png); height: 30px; width: 100%; background-size: 40px; background-repeat: no-repeat; background-color: #282c34; margin-bottom: -7px; border-radius: 5px; background-position: 10px 10px;">apiVersion: v1
kind: Pod
metadata:
name: foo
namespace: awesomeapps
spec:
containers:
- name: foo
image: janedoe/awesomeapp:v1
imagePullSecrets:
- name: docker-reader-secret
針對具有 PodTemplate 內容的資源
https://files.mdnice.com/point.png); height: 30px; width: 100%; background-size: 40px; background-repeat: no-repeat; background-color: #282c34; margin-bottom: -7px; border-radius: 5px; background-position: 10px 10px;">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
最後
如果大家的私有鏡像庫還沒有采用認證,就趕緊行動起來吧!
血的教訓,安全問題刻不容緩。