嘿,我正在尝试创建一个秘密,用于从我的注册表中提取图像,并且除 gcr 之外的所有其他注册表都在工作,下面是创建的秘密,
.dockerconfigjson: '{"auths":{"us-south1-docker.pkg.dev":{"username": "_json_key","password":"{\"type\":\"service_account\",\"project_id\":\"....\",\"private_key_id\":\"...\",\"private_key\":\"...\\n\",\"client_email\":\"...\",\"client_id\":\"....\",\"auth_uri\":\"https://accounts.google.com/o/oauth2/auth\",\"token_uri\":\"https://oauth2.googleapis.com/token\",\"auth_provider_x509_cert_url\":\"https://www.googleapis.com/oauth2/v1/certs\",\"client_x509_cert_url\":\"https://www.googleapis.com/robot/v1/metadata/x509/...\",\"universe_domain\":\"googleapis.com\"}"}}}'
下面是处理 gcp 的代码,
case "gcr", "gcp":
url = "gcr.io"
if image.URL != "" {
url = image.URL
}
username = "_json_key"
// as the gcp credentials are in the form of a json interface, we need to convert it to a json object
// so we can save the file temporarily and then read it for creating the secret
var gcpCredentials GcpCredentials
// gcp is in interface format, so we need to convert it to json
gcpCredentialsJson, err := json.Marshal(image.GCPCredentials)
if err != nil {
logrus.Errorf("Could not marshal the gcp credentials: %s", err)
return err
}
err = json.Unmarshal(gcpCredentialsJson, &gcpCredentials)
if err != nil {
logrus.Errorf("Could not unmarshal the gcp credentials: %s", err)
return err
}
// create a json file with the gcp credentials
err = os.WriteFile("/tmp/gcp_credentials.json", gcpCredentialsJson, 0644)
if err != nil {
logrus.Errorf("Could not write the gcp credentials file: %s", err)
return err
}
// read the file and get the contents
contents, err := os.ReadFile("/tmp/gcp_credentials.json")
if err != nil {
logrus.Errorf("Could not read the gcp credentials file: %s", err)
return err
}
password = string(contents)
dockerConfigJson := fmt.Sprintf(`{"auths":{"%s":{"username": "%s","password":"%s"}}}`, url, username, password)
logrus.Infof("Creating docker config secret: %s", dockerConfigJson)
secret := corev1.Secret{
ObjectMeta: metav1.ObjectMeta{Name: fmt.Sprintf("%s-%s", secretName, strings.ToLower(image.Provider))},
Type: corev1.SecretTypeDockerConfigJson,
Data: map[string][]byte{".dockerconfigjson": []byte(dockerConfigJson)},
}
err := createSecret(&secret, clientset, namespace)
但是每次我尝试使用令牌提取图像时,都会出现以下错误,
Failed to pull image "us-south1-docker.pkg.dev/...": rpc error: code = Unknown desc = failed to pull and unpack image "...": failed to resolve reference "...": failed to authorize: failed to fetch oauth token: unexpected status from GET request to https://us-south1-docker.pkg.dev/v2/token?scope=repository%3A...Apull&service=us-south1-docker.pkg.dev: 401 Unauthorized
Warning Failed 29m (x4 over 31m) kubelet Error: ErrImagePull
Warning Failed 29m (x6 over 31m) kubelet Error: ImagePullBackOff
我宁愿直接在内存中处理 JSON,而不是写入和读取 JSON 文件:您可以避免文件权限、文件系统延迟或写入/读取文件错误等潜在问题,这些问题在容器化环境中或在以下情况下尤其可能出现问题:使用 Kubernetes 节点中的临时存储。
package main
import (
"encoding/json"
"fmt"
"log"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
func createGCRSecret(clientset *Clientset, image Image, secretName, namespace string) error {
username := "_json_key"
var gcpCredentialsJson []byte
var err error
// Assuming 'GCPCredentials' is a valid JSON string already
gcpCredentialsJson, err = json.Marshal(image.GCPCredentials)
if err != nil {
log.Fatalf("Could not marshal the gcp credentials: %v", err)
return err
}
dockerConfigJson := fmt.Sprintf(`{"auths":{"%s":{"username":"%s","password":"%s"}}}`,
image.URL, username, string(gcpCredentialsJson))
secret := corev1.Secret{
ObjectMeta: metav1.ObjectMeta{Name: fmt.Sprintf("%s-%s", secretName, strings.ToLower(image.Provider))},
Type: corev1.SecretTypeDockerConfigJson,
Data: map[string][]byte{".dockerconfigjson": []byte(dockerConfigJson)},
}
err = createSecret(&secret, clientset, namespace)
if err != nil {
log.Fatalf("Failed to create Kubernetes secret: %v", err)
return err
}
log.Printf("Successfully created docker config secret: %s", secretName)
return nil
}
但是您的 GCPCredentials 必须已经是有效的 JSON 字符串:如果 JSON 密钥格式不正确,则编组/解组步骤可能会在到达 Kubernetes 秘密创建之前失败。
401 Unauthorized
错误通常指向以下问题:
因此请检查与 JSON 凭据关联的服务帐户是否具有必要的角色。对于 GCR(ArtifactRegistry,因为原始的 ContainerRegistry 已被弃用),通常需要像
roles/storage.objectViewer
这样的角色来拉取镜像(请参阅“Cloud Storage 的 IAM 角色”)。
有时部分凭证会被更新或更改。仔细检查服务帐户 JSON 密钥中的整套凭据是否正确且有效。
确保注册表 URL (
image.URL
) 设置正确。注册表 URL 中的错误可能会导致身份验证问题,因为凭据可能与预期的注册表端点不匹配。
Kubernetes 秘密是命名空间资源。这意味着它们只能在创建它们的名称空间内访问。当您创建用于从私有注册表中提取映像的密钥时,您必须确保在与将使用它的 Pod 相同的命名空间中创建该密钥。
创建密钥时,指定运行工作负载(Pod)的命名空间。这是通过在秘密定义的
namespace
中设置 ObjectMeta
参数来完成的:
secret := corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: fmt.Sprintf("%s-%s", secretName, strings.ToLower(image.Provider)),
Namespace: namespace, // Make sure this is the correct namespace
},
Type: corev1.SecretTypeDockerConfigJson,
Data: map[string][]byte{".dockerconfigjson": []byte(dockerConfigJson)},
}
当您调用函数在 Kubernetes API 中创建密钥时,请确保它引用预期的命名空间。这通常涉及使用正确配置的 Kubernetes 客户端集来定位正确的命名空间。
在 pod 的 YAML 配置中,在
imagePullSecrets
部分指定密钥。这告诉 Kubernetes 在拉取容器镜像时使用指定的密钥:
apiVersion: v1
kind: Pod
metadata:
name: example-pod
spec:
containers:
- name: example-container
image: us-south1-docker.pkg.dev/project-id/example-image:tag
imagePullSecrets:
- name: [your-secret-name]
有时,您可能对不同的 pod 有特定的服务帐户,尤其是在更复杂的设置或多租户集群中。确保与 pod 关联的服务帐户具有读取密钥的权限。这通常通过基于角色的访问控制 (RBAC) 设置进行管理。
例如,此角色将允许服务帐户读取特定命名空间中的机密:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: [your-namespace]
name: secret-reader
rules:
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get", "watch", "list"]
以及对应的RoleBinding:
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: read-secrets
namespace: [your-namespace]
subjects:
- kind: ServiceAccount
name: [your-service-account]
namespace: [your-namespace]
roleRef:
kind: Role
name: secret-reader
apiGroup: rbac.authorization.k8s.io