--- title: 镜像 content_template: templates/concept weight: 10 --- {{% capture overview %}} 创建 Docker 镜像并将其推送到仓库,然后在 Kubernetes pod 中引用它。 容器的 `image` 属性支持与 `docker` 命令相同的语法,包括私有仓库和标签。 {{% /capture %}} {{% capture body %}} ## 升级镜像 默认的镜像拉取策略是 `IfNotPresent`,在镜像已经存在的情况下,kubelet 将不再去拉取镜像。如果总是想要拉取镜像,您可以执行以下操作: - 设置容器的 `imagePullPolicy` 为 `Always`。 - 省略 `imagePullPolicy`,并使用 `:latest` 作为要使用的镜像的标签。 - 省略 `imagePullPolicy` 和要使用的镜像标签。 - 启用 [AlwaysPullImages](/docs/reference/access-authn-authz/admission-controllers/#alwayspullimages) 准入控制器(admission controller)。 注意应避免使用 `:latest` 标签,参见[配置镜像最佳实践](/docs/concepts/configuration/overview/#container-images) 获取更多信息。 ## 使用清单(manifest)构建多架构镜像 Docker CLI 现在支持以下命令 `docker manifest` 以及 `create`、`annotate`、`push` 等子命令。这些命令可用于构建和推送清单。您可以使用 `docker manifest inspect` 来查看清单。 请在此处查看 docker 清单文档: https://docs.docker.com/edge/engine/reference/commandline/manifest/ 查看有关如何在构建工具中使用清单的示例: https://cs.k8s.io/?q=docker%20manifest%20(create%7Cpush%7Cannotate)&i=nope&files=&repos= 这些命令依赖于 Docker CLI 并仅在 Docker CLI 上实现。需要编辑 `$HOME/.docker/config.json` 并将 `experimental` 设置为 `enabled`,或者仅在调用 CLI 命令时将 `DOCKER_CLI_EXPERIMENTAL` 环境变量设置为 `enabled`。 {{< note >}} 请使用 Docker *18.06 或更高版本*,低版本存在错误或不支持实验性命令行选项。导致容器问题示例 https://github.com/docker/cli/issues/1135。 {{< /note >}} 如果在上传旧清单时遇到麻烦,只需删除 `$HOME/.docker/manifests` 中旧的清单即可重新开始。 对于 Kubernetes,通常使用带有后缀 `-$(ARCH)` 的镜像。为了向后兼容,请生成带有后缀的旧镜像。想法是生成具有所有 arch(es) 清单的 `pause` 镜像,并生成 `pause-amd64` 镜像,该镜像向后兼容较早的配置或者可能已对带有后缀的镜像进行硬编码的 YAML 文件。 ## 使用私有仓库 从私有仓库读取镜像时可能需要密钥。 凭证可以用以下方式提供: - 使用 Google Container Registry - 每个集群 - 在 Google Compute Engine 或 Google Kubernetes Engine 上自动配置 - 所有 Pod 均可读取项目的私有仓库 - 使用 Amazon Elastic Container Registry(ECR) - 使用 IAM 角色和策略来控制对 ECR 仓库的访问 - 自动刷新 ECR 登录凭据 - 使用 Oracle Cloud Infrastructure Registry(OCIR) - 使用 IAM 角色和策略来控制对 OCIR 仓库的访问 - 使用 Azure Container Registry (ACR) - 使用 IBM Cloud Container Registry - 配置节点用于私有仓库进行身份验证 - 所有 Pod 均可读取任何已配置的私有仓库 - 需要集群管理员配置节点 - 预拉镜像 - 所有 Pod 都可以使用节点上缓存的任何镜像 - 需要所有节点的 root 访问权限才能进行设置 - 在 Pod 上指定 ImagePullSecrets - 只有提供自己密钥的 Pod 才能访问私有仓库 下面将详细描述每一项。 ### 使用 Google Container Registry Kuberetes 运行在 Google Compute Engine (GCE) 时原生支持 [Google Container Registry (GCR)](https://cloud.google.com/tools/container-registry/)。如果 kubernetes 集群运行在 GCE 或者 Google Kubernetes Engine,使用镜像全名(例如 gcr.io/my_project/image:tag) 即可。 集群中所有 pod 都会有读取这个仓库镜像的权限。 kubelet 将使用实例的 Google service account 向 GCR 认证。实例的 Google service account 拥有 `https://www.googleapis.com/auth/devstorage.read_only`,所以它可以从项目的 GCR 拉取,但不能推送。 ### 使用 Amazon Elastic Container Registry 当 Node 是 AWS EC2 实例时,Kubernetes 原生支持 [Amazon Elastic Container Registry](https://aws.amazon.com/ecr/)。 在 pod 定义中,使用镜像全名即可 (例如 `ACCOUNT.dkr.ecr.REGION.amazonaws.com/imagename:tag`) 集群中所有可以创建 Pod 的用户都将能够运行使用 ECR 仓库中任何镜像的 Pod。 kubelet 将获取并定期刷新 ECR 凭据。它需要以下权限才能执行此操作: - `ecr:GetAuthorizationToken` - `ecr:BatchCheckLayerAvailability` - `ecr:GetDownloadUrlForLayer` - `ecr:GetRepositoryPolicy` - `ecr:DescribeRepositories` - `ecr:ListImages` - `ecr:BatchGetImage` 要求: - 必须使用 kubelet `v1.2.0` 及以上版本。(例如 运行 `/usr/bin/kubelet --version=true`)。 - 如果 Node 在区域 A,而镜像仓库在另一个区域 B,需要 `v1.3.0` 及以上版本。 - 区域中必须提供 ECR。 故障排除: - 验证是否满足以上要求。 - 获取工作站的 $REGION (例如 `us-west-2`) 凭证,使用凭证 SSH 到主机手动运行 Docker。它行得通吗? - 验证 kubelet 是否使用参数 `--cloud-provider=aws` 运行。 - 检查 kubelet 日志(例如 `journalctl -u kubelet`)是否有类似的行: - `plugins.go:56] Registering credential provider: aws-ecr-key` - `provider.go:91] Refreshing cache for provider: *aws_credentials.ecrProvider` ### 使用 Azure Container Registry (ACR) 当使用 [Azure Container Registry](https://azure.microsoft.com/en-us/services/container-registry/) 时,可以使用管理员用户或者 service principal 进行身份验证。任何一种情况,认证都通过标准的 Docker 授权完成。本指南假设使用 [azure-cli](https://github.com/azure/azure-cli) 命令行工具。 首先,需要创建仓库并获取凭证,完整的文档请参考 [Azure container registry 文档](https://docs.microsoft.com/en-us/azure/container-registry/container-registry-get-started-azure-cli)。 创建好容器仓库后,可以使用以下凭证登录: * `DOCKER_USER` : service principal,或管理员用户名称 * `DOCKER_PASSWORD`: service principal 密码,或管理员用户密码 * `DOCKER_REGISTRY_SERVER`: `${some-registry-name}.azurecr.io` * `DOCKER_EMAIL`: `${some-email-address}` 填写以上变量后,就可以 [配置 Kubernetes Secret 并使用它来部署 Pod](/docs/concepts/containers/images/#specifying-imagepullsecrets-on-a-pod)。 ### 使用 IBM Cloud Container Registry IBM Cloud Container Registry 提供了一个多租户私有镜像仓库,可以使用它来安全地存储和共享 Docker 仓库。默认情况下,集成的 Vulnerability Advisor 会扫描私有仓库中的镜像,以检测安全问题和潜在的漏洞。IBM Cloud 帐户中的用户可以访问您的镜像,也可以创建令牌来授予对仓库命名空间的访问权限。 要安装 IBM Cloud Container Registry CLI 插件并为镜像创建命名空间,请参阅 [IBM Cloud Container Registry 入门](https://cloud.ibm.com/docs/services/Registry?topic=registry-index#index)。 可以使用 IBM Cloud Container Registry 将容器从 [IBM Cloud 公共镜像](https://cloud.ibm.com/docs/services/Registry?topic=registry-public_images#public_images) 和私有镜像部署到 IBM Cloud Kubernetes Service 集群的默认命名空间。要将容器部署到其他命名空间,或使用来自其他 IBM Cloud Container 的仓库区域或 IBM Cloud 帐户的镜像,请创建 Kubernetes `imagePullSecret`。有关更多信息,请参阅[从镜像构建容器](https://cloud.ibm.com/docs/containers?topic=containers-images#images)。 ### 配置 Node 对私有仓库认证 {{< note >}} 如果在 Google Kubernetes Engine 上运行集群,每个节点上都会有 `.dockercfg` 文件,它包含 Google Container Registry 的凭证。不需要使用以下方法。 {{< /note >}} {{< note >}} 如果在 AWS EC2 上运行集群且准备使用 EC2 Container Registry (ECR),每个 node 上的 kubelet 会管理和更新 ECR 的登录凭证。不需要使用以下方法。 {{< /note >}} {{< note >}} 该方法适用于能够对节点进行配置的情况。该方法在 GCE 及在其它能自动配置节点的云平台上并不适合。 {{< /note >}} {{< note >}} 截至目前,Kubernetes 仅支持 docker config 的 `auths` 和 `HttpHeaders` 部分。这意味着不支持凭据助手(`credHelpers` 或 `credsStore`)。 {{< /note >}} Docker 将私有仓库的密钥存放在 `$HOME/.dockercfg` 或 `$HOME/.docker/config.json` 文件中。Kubelet 上,docker 会使用 root 用户 `$HOME` 路径下的密钥。 * `{--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` {{< note >}} 可能必须在环境变量文件中为 kubelet 显式设置 `HOME=/root`。 {{< /note >}} 推荐如下步骤来为 node 配置私有仓库。以下示例在 PC 或笔记本电脑中操作: 1. 对于想要使用的每一种凭证,运行 `docker login [server]`,它会更新 `$HOME/.docker/config.json`。 2. 使用编辑器查看 `$HOME/.docker/config.json`,保证文件中包含了想要使用的凭证。 3. 获取 node 列表,例如 - 如果想要 node 名称,`nodes=$(kubectl get nodes -o jsonpath='{range.items[*].metadata}{.name} {end}')` - 如果想要 node IP ,`nodes=$(kubectl get nodes -o jsonpath='{range .items[*].status.addresses[?(@.type=="ExternalIP")]}{.address} {end}')` 4. 将本地的 `.docker/config.json` 拷贝到每个节点 root 用户目录下 - 例如: `for n in $nodes; do scp ~/.docker/config.json root@$n:/root/.docker/config.json; done` 创建使用私有仓库的 pod 来验证,例如: ```yaml kubectl apply -f - < 如果一切正常,一段时间后,可以看到: ```shell kubectl logs private-image-test-1 SUCCESS ``` 如果失败,则可以看到: ```shell kubectl describe pods/private-image-test-1 | grep "Failed" Fri, 26 Jun 2015 15:36:13 -0700 Fri, 26 Jun 2015 15:39:13 -0700 19 {kubelet node-i2hq} spec.containers{uses-private-image} failed Failed to pull image "user/privaterepo:v1": Error: image user/privaterepo:v1 not found ``` 必须保证集群中所有的节点都有相同的 `.docker/config.json` 文件。否则, pod 会在一些节点上正常运行而在另一些节点上无法启动。例如,如果使用 node 自动缩放,那么每个实例模板都需要包含 `.docker/config.json`,或者挂载一个包含这个文件的驱动器。 在 `.docker/config.json` 中配置了私有仓库密钥后,所有 pod 都会能读取私有仓库中的镜像。 ### 提前拉取镜像 {{< note >}} 如果在 Google Kubernetes Engine 上运行集群,每个节点上都会有 `.dockercfg` 文件,它包含 Google Container Registry 的凭证。不需要使用以下方法。 {{< /note >}} {{< note >}} 该方法适用于能够对节点进行配置的情况。该方法在 GCE 及在其它能自动配置节点的云平台上并不适合。 {{< /note >}} 默认情况下,kubelet 会尝试从指定的仓库拉取每一个镜像。但是,如果容器属性 `imagePullPolicy` 设置为 `IfNotPresent `或者 `Never`,则会使用本地镜像(优先、唯一、分别)。 如果依赖提前拉取镜像代替仓库认证,必须保证集群所有的节点提前拉取的镜像是相同的。 可以用于提前载入指定的镜像以提高速度,或者作为私有仓库认证的一种替代方案。 所有的 pod 都可以使用 node 上缓存的镜像。 ### 在 pod 上指定 ImagePullSecrets {{< note >}} Google Kubernetes Engine、GCE 及其他自动创建 node 的云平台上,推荐使用本方法。 {{< /note >}} Kubernetes 支持在 pod 中指定仓库密钥。 #### 使用 Docker Config 创建 Secret 运行以下命令,将大写字母代替为合适的值: ```shell kubectl create secret docker-registry --docker-server=DOCKER_REGISTRY_SERVER --docker-username=DOCKER_USER --docker-password=DOCKER_PASSWORD --docker-email=DOCKER_EMAIL ``` 如果已经有 Docker 凭证文件,则可以将凭证文件作为 Kubernetes secret 导入而不是使用上面的命令。[根据现有 Docker 凭证创建 Secret](/docs/tasks/configure-pod-container/pull-image-private-registry/#registry-secret-existing-credentials) 解释了如何安装。如果使用多个私有容器仓库,这将特别有用,因为 `kubectl create secret docker-registry` 创建了一个仅适用于单个私有仓库的 Secret。 {{< note >}} Pod 只能引用和它相同命名空间的 ImagePullSecrets,所以需要为每一个命名空间做配置。 {{< /note >}} #### 引用 Pod 上的 imagePullSecrets 现在,在创建 pod 时,可以在 pod 定义中增加 `imagePullSecrets` 部分来引用 secret。 ```shell cat < pod.yaml apiVersion: v1 kind: Pod metadata: name: foo namespace: awesomeapps spec: containers: - name: foo image: janedoe/awesomeapp:v1 imagePullSecrets: - name: myregistrykey EOF cat <> ./kustomization.yaml resources: - pod.yaml EOF ``` 对每一个使用私有仓库的 pod,都需要做以上操作。 但是,可以通过在 [serviceAccount](/docs/user-guide/service-accounts) 资源中设置 imagePullSecrets 来自动设置 `imagePullSecrets`。检查 [将 ImagePullSecrets 添加 Service Account](/docs/tasks/configure-pod-container/configure-service-account/#add-imagepullsecrets-to-a-service-account) 以获取详细说明。 可以将其与每个节点 `.docker/config.json` 结合使用。凭据将被合并。这种方法适用于 Google Kubernetes Engine。 ### 使用场景 配置私有仓库有多种方案,以下是一些常用场景和建议的解决方案。 1. 集群运行非专有(例如 开源镜像)镜像。镜像不需要隐藏。 - 使用 Docker hub 上的公有镜像 - 无需配置 - 在 GCE/GKE 上会自动使用高稳定性和高速的 Docker hub 的本地 mirror 2. 集群运行一些专有镜像,这些镜像对外部公司需要隐藏,对集群用户可见 - 使用自主的私有 [Docker registry](https://docs.docker.com/registry/)。 - 可以放置在 [Docker Hub](https://hub.docker.com/account/signup/),或者其他地方。 - 按照上面的描述,在每个节点手动配置 .docker/config.json。 - 或者,在防火墙内运行一个内置的私有仓库,并开放读取权限。 - 不需要配置 Kubenretes。 - 或者,在 GCE/GKE 上时,使用项目的 Google Container Registry。 - 使用集群自动伸缩比手动配置 node 工作的更好。 - 或者,在更改集群 node 配置不方便时,使用 `imagePullSecrets`。 3. 使用专有镜像的集群,有更严格的访问控制。 - 保证开启 [AlwaysPullImages admission controller](/docs/reference/access-authn-authz/admission-controllers/#alwayspullimages)。否则,所有的 pod 都可以使用镜像。 - 将敏感数据存储在 "Secret" 资源中,而不是打包在镜像里。 4. 多租户集群下,每个租户需要自己的私有仓库。 - 开启保证 [AlwaysPullImages admission controller](/docs/reference/access-authn-authz/admission-controllers/#alwayspullimages)。否则,所有租户的所有的 pod 都可以使用镜像。 - 私有仓库开启认证。 - 为每个租户获取仓库凭证,放置在 secret 中,并发布到每个租户的命名空间下。 - 租户将 secret 增加到每个命名空间下的 imagePullSecrets 中。 {{% /capture %}} 如果需要访问多个仓库,则可以为每个仓库创建一个 secret。Kubelet 将任何 `imagePullSecrets` 合并为单个虚拟 `.docker/config.json` 文件。