Docker CLI 允许标记,但 Docker Python API 引发 API 错误

Docker CLI allows tag, but Docker Python API raises APIError

我正在尝试使用 Docker Python API 将本地 Docker 图像推送到 ECR。作为该过程的一部分,我需要以某种方式标记图像。当我在 CLI 上这样做时,它起作用了:

docker tag foo/bar '{user_id}.dkr.ecr.us-east-1.amazonaws.com/foo/bar'

然而,当我尝试使用 Docker Python SDK 中的 docker.images.Image.tag 函数做同样的事情时,它失败了:

import docker
(docker.client.from_env().images.get('foo/bar')
 .tag('foo/bar', 
      '{user-id}.dkr.ecr.us-east-1.amazonaws.com/foo/bar'
     )
)

(将上面代码示例中的 user_id 替换为 AWS 用户 ID 值,例如 717171717171;出于这个问题的目的,我在此处对其进行了混淆处理)

出现以下错误:

In [10]: docker.client.from_env().images.get('foo/bar').ta
    ...: g('foo/bar', '{user_id}.dkr.ecr.us-east-1.amaz
    ...: onaws.com/foo/bar')                              
---------------------------------------------------------------------------
HTTPError                                 Traceback (most recent call last)
~/miniconda3/envs/alekseylearn-dev/lib/python3.6/site-packages/docker/api/client.py in _raise_for_status(self, response)
    255         try:
--> 256             response.raise_for_status()
    257         except requests.exceptions.HTTPError as e:

~/miniconda3/envs/alekseylearn-dev/lib/python3.6/site-packages/requests/models.py in raise_for_status(self)
    939         if http_error_msg:
--> 940             raise HTTPError(http_error_msg, response=self)
    941 

HTTPError: 500 Server Error: Internal Server Error for url: http+docker://localhost/v1.35/images/sha256:afe07035bce72b6c496878a7e3960bedffd46c1bedc79f1bd2b89619e8457194/tag?tag={user_id}.dkr.ecr.us-east-1.amazonaws.com%2Ffoo%2Fbar&repo=foo%2Fbar&force=0

During handling of the above exception, another exception occurred:

APIError                                  Traceback (most recent call last)
<ipython-input-10-5bb015d17409> in <module>
----> 1 docker.client.from_env().images.get('alekseylearn-example/build').tag('foo/bar', '{user_id}.dkr.ecr.us-east-1.amazonaws.com/foo/bar')

~/miniconda3/envs/alekseylearn-dev/lib/python3.6/site-packages/docker/models/images.py in tag(self, repository, tag, **kwargs)
    120             (bool): ``True`` if successful
    121         """
--> 122         return self.client.api.tag(self.id, repository, tag=tag, **kwargs)
    123 
    124 

~/miniconda3/envs/alekseylearn-dev/lib/python3.6/site-packages/docker/utils/decorators.py in wrapped(self, resource_id, *args, **kwargs)
     17                     'Resource ID was not provided'
     18                 )
---> 19             return f(self, resource_id, *args, **kwargs)
     20         return wrapped
     21     return decorator

~/miniconda3/envs/alekseylearn-dev/lib/python3.6/site-packages/docker/api/image.py in tag(self, image, repository, tag, force)
    531         url = self._url("/images/{0}/tag", image)
    532         res = self._post(url, params=params)
--> 533         self._raise_for_status(res)
    534         return res.status_code == 201
    535 

~/miniconda3/envs/alekseylearn-dev/lib/python3.6/site-packages/docker/api/client.py in _raise_for_status(self, response)
    256             response.raise_for_status()
    257         except requests.exceptions.HTTPError as e:
--> 258             raise create_api_error_from_http_exception(e)
    259 
    260     def _result(self, response, json=False, binary=False):

~/miniconda3/envs/alekseylearn-dev/lib/python3.6/site-packages/docker/errors.py in create_api_error_from_http_exception(e)
     29         else:
     30             cls = NotFound
---> 31     raise cls(e, response=response, explanation=explanation)
     32 
     33 

APIError: 500 Server Error: Internal Server Error ("invalid tag format")

为什么CLI命令成功而Python API命令失败?

在详细的 Docker API 术语中,像 123456789012.dkr.ecr.us-east-1.amazon.aws.com/foo/bar:baz 这样的图像名称被分成 repository(在冒号之前)和a tag(冒号后)。存储库名称的主机名部分是 注册表 。如果指定 none,则默认标记值是文字 latest.

在你的例子中,你已经有了一个 Image 对象,所以你需要应用第二个参数的两个 "halves":

docker.client.from_env().images.get('foo/bar')
 .tag('{user-id}.dkr.ecr.us-east-1.amazonaws.com/foo/bar',
      'latest'
     )

(在许多实际情况下,使用 latest 标签并不是一个好主意;时间戳或源代码控制提交 ID 之类的东西可以更好地识别图像并有助于向 ECS 或 EKS 或普通 Kubernetes 等服务指示他们需要进行更新。此外,虽然 ECR 图像 ID 有点长得不切实际,但在脚本上下文中没有什么能阻止您直接使用它们;例如,您可以 docker build -t 12345...amazonaws.com/foo/bar:abcdef0 并跳过中间 docker tag 如果你愿意,请走一步。)