将 Google 服务帐户凭据传递给 Docker

bla*_*fci 7 google-cloud-storage google-compute-engine docker google-cloud-platform google-cloud-sdk

我的用例与此问题的其他用例略有不同,因此有一点预先描述:

我在 Google Cloud 上工作并且有一个“dockerized”的 Django 应用程序。部分应用程序依赖于使用 gsutil 将文件移入/移出 Google Storage 存储分区。由于各种原因,我们不想使用 Google Container Engine 来管理我们的容器。相反,我们希望通过启动额外的 Google Compute VM 来水平扩展,这些 VM 将依次运行这个 Docker 容器。与https://cloud.google.com/python/tutorials/bookshelf-on-compute-engine类似,除了我们将使用容器而不是拉取 git 存储库。

VM 将从基本的 Debian 映像构建,并且依赖项(例如 Docker 本身)的启动和安装将使用启动脚本(例如gcloud compute instances create some-instance --metadata-from-file startup-script=/path/to/startup.sh)进行编排。

如果我手动创建一个虚拟机,迅速提升用sudo -s,运行gsutil config -f(这将创建在/root/.boto凭证文件),然后运行我的泊坞窗容器(见下文Dockerfile)与

docker run -v /root/.boto:/root/.boto username/gs gsutil ls gs://my-test-bucket

那么它的工作原理。但是,这需要我的交互才能创建 boto 文件。

我的问题是:如何将默认服务凭据传递给将在该新 VM 中启动的 Docker 容器?

gsutil 甚至可以在“新鲜”的 Debian VM 上开箱即用,因为它使用所有 VM 加载的默认计算引擎凭据。有没有办法使用这些凭据并将它们传递给 docker 容器?在新的 VM 上第一次调用 gsutil 后,我注意到它创建了 ~/.gsutil 和 ~/.config 文件夹。不幸的是,在 Docker 中安装这两个

docker run -v ~/.config/:/root/.config -v ~/.gsutil:/root/.gsutil username/gs gsutil ls gs://my-test-bucket

不能解决我的问题。它告诉我:

ServiceException: 401 Anonymous users does not have storage.objects.list access to bucket my-test-bucket.

最小的 gsutil Dockerfile(不是我的):

FROM alpine
#install deps and install gsutil
RUN apk add --update \
    python \
    py-pip \
    py-cffi \
    py-cryptography \
  && pip install --upgrade pip \
  && apk add --virtual build-deps \
    gcc \
    libffi-dev \
    python-dev \
    linux-headers \
    musl-dev \
    openssl-dev \
  && pip install gsutil \
  && apk del build-deps \
  && rm -rf /var/cache/apk/*
CMD ["gsutil"]
Run Code Online (Sandbox Code Playgroud)

补充:一个解决方法:

我已经解决了我的问题,但它非常迂回,所以如果可能的,我仍然对更简单的方法感兴趣。所有详细信息如下:

首先,说明:我首先在 web 控制台中创建了一个服务帐户。然后我将 JSON 密钥文件(称为凭据.json)保存到存储桶中。在 GCE VM 的启动脚本中,我将该密钥文件复制到本地文件系统 ( gsutil cp gs://<bucket>/credentials.json /gs_credentials/)。然后我启动我的 docker 容器,挂载该本地目录。然后,当 docker 容器启动时,它会运行一个脚本来验证凭据。

以下是一个小型工作示例的文件:

Dockerfile:

FROM alpine
#install deps and install gsutil
RUN apk add --update \
    python \
    py-pip \
    py-cffi \
    py-cryptography \
    bash \
    curl \
  && pip install --upgrade pip \
  && apk add --virtual build-deps \
    gcc \
    libffi-dev \
    python-dev \
    linux-headers \
    musl-dev \
    openssl-dev \
  && pip install gsutil \
  && apk del build-deps \
  && rm -rf /var/cache/apk/*

# install the gcloud SDK- 
# this allows us to use gcloud auth inside the container
RUN curl -sSL https://sdk.cloud.google.com > /tmp/gcl \
      && bash /tmp/gcl --install-dir=~/gcloud --disable-prompts

RUN mkdir /startup
ADD gsutil_docker_startup.sh /startup/gsutil_docker_startup.sh
ADD get_account_name.py /startup/get_account_name.py

ENTRYPOINT ["/startup/gsutil_docker_startup.sh"]
Run Code Online (Sandbox Code Playgroud)

gsutil_docker_startup.sh: 采用单个参数,即 JSON 格式的服务帐户凭据文件的路径。该文件存在是因为主机上的目录已安装在容器中。

#!/bin/bash

CRED_FILE_PATH=$1

mkdir /results

# List the bucket, see that it gives a "ServiceException:401"
gsutil ls gs://<input bucket> > /results/before.txt

# authenticate the credentials- this creates a .boto file:
/root/gcloud/google-cloud-sdk/bin/gcloud auth activate-service-account --key-file=$CRED_FILE_PATH

# need to extract the service account which is like:
# <service acct ID>@<google project>.iam.gserviceaccount.com"
SERVICE_ACCOUNT=$(python /startup/get_account_name.py $CRED_FILE_PATH)

# with that service account, we can locate the .boto file:
export BOTO_PATH=/root/.config/gcloud/legacy_credentials/$SERVICE_ACCOUNT/.boto

# List the bucket and copy the file to an output bucket for good measure
gsutil ls gs://<input bucket> > /results/after.txt
gsutil cp /results/*.txt gs://<output bucket>/
Run Code Online (Sandbox Code Playgroud)

get_account_name.py:

import json
import sys
j = json.load(open(sys.argv[1]))
sys.stdout.write(j['client_email'])
Run Code Online (Sandbox Code Playgroud)

然后,GCE 启动脚本(在 VM 启动时自动执行)是:

#!/bin/bash

# <SNIP>
# Install docker, other dependencies
# </SNIP>

# pull docker image
docker pull userName/containerName

# get credential file:
mkdir /cloud_credentials
gsutil cp gs://<bucket>/credentials.json /cloud_credentials/creds.json

# run container
# mount the host machine directory where the credentials were saved.
# Note that the container expects a single arg, 
# which is the path to the credential file IN THE CONTAINER
docker run -v /cloud_credentials:/cloud_credentials \ 
    userName/containerName /cloud_credentials/creds.json
Run Code Online (Sandbox Code Playgroud)

Yan*_*leu 3

您可以将特定服务帐户分配给您的实例,然后在代码中使用应用程序默认凭据。请在测试前验证这些要点。

  • 将实例访问范围设置为:“允许完全访问所有云 API”,因为它们并不是真正的安全功能
  • 为您的服务帐户设置正确的角色:“存储对象查看者”

身份验证令牌由应用程序默认凭据通过Google 元数据服务器自动检索,该服务器也可从您的实例和 Docker 容器中获取。无需管理任何凭证。

def implicit():
  from google.cloud import storage

  # If you don't specify credentials when constructing the client, the
  # client library will look for credentials in the environment.
  storage_client = storage.Client()

  # Make an authenticated API request
  buckets = list(storage_client.list_buckets())
  print(buckets)
Run Code Online (Sandbox Code Playgroud)

我也很快用 docker 进行了测试,并且工作得很好

yann@test:~$ gsutil cat gs://my-test-bucket/hw.txt
Hello World
yann@test:~$ docker run --rm google/cloud-sdk gsutil cat gs://my-test-bucket/hw.txt
Hello World
Run Code Online (Sandbox Code Playgroud)