【问题标题】:Inject env variable into build stage of image将环境变量注入图像的构建阶段
【发布时间】:2020-04-28 14:18:15
【问题描述】:

为基于 Django 的 API 开发 CI/CD 管道。我需要在构建阶段注入环境变量才能成功构建映像。

  • python manage.py collectstatic 在构建镜像时在Dockerfile 中运行
  • collectstatic 需要 settings.py 中的 SECRET_KEY 才能运行
  • SECRET_KEY 在这个阶段是空的,因为它是用os.environ['SECRET_KEY] 读入的
  • 因为是空的,所以构建失败
  • 因此,我需要设置环境来拥有这个变量

我无法将这么多的东西放在一起,所以想看看是否有人可以帮助我。

这是我目前所拥有的:

Azure 管道

azure-pipelines.yml

trigger:
  branches:
    include:
    - master

resources:
- repo: self

variables:
  # Container registry service connection established during pipeline creation
  secretKey: $(SECRET_KEY)

  # Agent VM image name
  vmImageName: 'ubuntu-latest'

stages:
- stage: Build
  displayName: Build and push stage
  jobs:  
  - job: Build
    displayName: Build
    pool:
      vmImage: $(vmImageName)
    steps:
    - bash:
      env:
        SECRET_KEY: $(secretKey)
    - task: Docker@2
      displayName: Build and push api image to container registry
      inputs:
        command: buildAndPush
        repository: $(imageRepository)-api
        dockerfile: $(dockerfilePath)/api/Dockerfile
        containerRegistry: $(dockerRegistryServiceConnection)
        tags: |
          $(tag)
    - upload: manifests
      artifact: manifests

Dockerfile

FROM python:3.7-slim
ENV PYTHONUNBUFFERED 1
WORKDIR /app
EXPOSE 5000
COPY requirements*.txt ./
RUN pip install -r requirements.txt
COPY . .
RUN python manage.py collectstatic
CMD ["gunicorn", "-b", ":5000", "--log-level", "info", "config.wsgi:application"]

构建失败日志(可能没有帮助,因为我知道它失败的原因...SECRET_KEY 不在环境变量中)

Step 8/18 : RUN python manage.py collectstatic
 ---> Running in 1f42a5c062aa
Traceback (most recent call last):
  File "manage.py", line 21, in <module>
    main()
  File "manage.py", line 17, in main
    execute_from_command_line(sys.argv)
  File "/usr/local/lib/python3.7/site-packages/django/core/management/__init__.py", line 381, in execute_from_command_line
    utility.execute()
  File "/usr/local/lib/python3.7/site-packages/django/core/management/__init__.py", line 325, in execute
    settings.INSTALLED_APPS
  File "/usr/local/lib/python3.7/site-packages/django/conf/__init__.py", line 79, in __getattr__
    self._setup(name)
  File "/usr/local/lib/python3.7/site-packages/django/conf/__init__.py", line 66, in _setup
    self._wrapped = Settings(settings_module)
  File "/usr/local/lib/python3.7/site-packages/django/conf/__init__.py", line 157, in __init__
    mod = importlib.import_module(self.SETTINGS_MODULE)
  File "/usr/local/lib/python3.7/importlib/__init__.py", line 127, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 1006, in _gcd_import
  File "<frozen importlib._bootstrap>", line 983, in _find_and_load
  File "<frozen importlib._bootstrap>", line 967, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 677, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 728, in exec_module
  File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
  File "/app/config/settings.py", line 26, in <module>
    SECRET_KEY = os.environ['SECRET_KEY']
  File "/usr/local/lib/python3.7/os.py", line 679, in __getitem__
    raise KeyError(key) from None
KeyError: 'SECRET_KEY'
The command '/bin/sh -c python manage.py collectstatic' returned a non-zero code: 1
##[error]The command '/bin/sh -c python manage.py collectstatic' returned a non-zero code: 1
##[error]The process '/usr/bin/docker' failed with exit code 1

我只是不确定如何在其中获取环境变量。我的方法可能从一开始就完全错误。

那我该怎么办:

  1. 安全地声明管道中的环境变量?
  2. 将上述环境变量传递到构建阶段?

谢谢!

【问题讨论】:

  • 无法获取最新信息,该解决方法对您有帮助吗?或者,如果您有任何疑问,请随时在此处分享。
  • @HughLin-MSFT 好吧,我希望有一种方法可以使用 azure-pipelines.yml 和 Azure DevOps Pipelines 来做到这一点。如果这不可行,下面提供的选项 1 是我的想法。
  • @HughLin-MSFT arguments: --secret 是在azure-pipelines.yml 中处理此问题的方法吗?图像中嵌入的机密是否存在与之相关的安全问题?
  • @HughLin-MSFT 这对 Azure Pipelines 和 azure-pipelines.yml 中的变量不可行吗?又搞砸了几个小时,但没有运气。

标签: azure docker azure-devops dockerfile azure-pipelines


【解决方案1】:

您可以在Dockerfile 中使用--build-arg 和相应的ARG 命令注入环境变量,但这不安全。密钥将嵌入图像中!

如果您想安全,您的选择:

  1. 使用 Docker 构建的密钥外部下载您需要的任何内容,然后将其复制进去。
  2. 为 Docker 使用(实验性的,但错误较少)BuildKit 后端,它支持安全地传递秘密。
  3. 在另一个容器中运行网络服务器,使用--network container:&lt;thatcontainerid&gt; 运行您的 Docker 构建,现在您可以向该其他容器发送查询以获取机密信息。或者类似地在主机上运行网络服务器,--network host

更长的文章,其中还提到了其他一些替代方案,例如通过多阶段构建进行复制:https://pythonspeed.com/articles/docker-build-secrets/

【讨论】:

    【解决方案2】:

    您的python manage.py collectstatic 正在docker 容器中运行,它的os.environ['SECRET_KEY] 将尝试获取它正在运行的容器的环境变量。但是您在管道中设置的环境变量 SECRET_KEY 用于构建代理。

    您可以尝试按照以下步骤将管道环境变量传递给 docker 容器。

    1,我在你的 dockerfile 中添加了一个ARG 和一个ENV ARG SECRET; ENV SECRET_KEY $SECRET

    ENV SECRET_KEY指的是ARG SECRET的值

    FROM python:3.7-slim
    
    ARG SECRET
    ENV SECRET_KEY $SECRET
    
    ENV PYTHONUNBUFFERED 1
    WORKDIR /app
    EXPOSE 5000
    COPY requirements*.txt ./
    RUN pip install -r requirements.txt
    COPY . .
    RUN python manage.py collectstatic
    CMD ["gunicorn", "-b", ":5000", "--log-level", "info", "config.wsgi:application"]
    

    2,我将 docker buildandpush 任务分离为停靠构建和停靠推送,因为 buildandpush 命令不能接受参数。

    在 docker build 任务中。我将变量secretKey 传递给arguments 字段--build-arg SECRET=$(secretKey)。这样当 docker run build 时,ARG SECRET 将被 secretKey 替换。它将被传递给上面 dockerfile 中定义的ENV SECRET_KEY。这样 SECRET_KEY 将被设置为 docker 容器的环境变量。

    那么你的python代码应该能够使用os.environ['SECRET_KEY]获取环境变量的值

    stages:
    - stage: Build
      displayName: Build and push stage
      jobs:  
      - job: Build
        displayName: Build
        pool:
          vmImage: $(vmImageName)
        steps:
        - task: Docker@2
          displayName: Build
          inputs:
            command: build
            repository: $(imageRepository)-api
            dockerfile: $(dockerfilePath)/api/Dockerfile
            containerRegistry: $(dockerRegistryServiceConnection)
    
            arguments: --build-arg SECRET=$(secretKey)
    
            tags: |
              $(tag)
    
        - task: Docker@2
          displayName: Push
          inputs:
            command: push
            repository: $(imageRepository)-api
            containerRegistry: $(dockerRegistryServiceConnection)
            tags: |
              $(tag)
    
    
    - upload: manifests
      artifact: manifests
    

    【讨论】:

    • 这确实有效!但是,我现在遇到的问题是安全性。虽然执行RUN echo ${SECRET_KEY} 之类的操作不会打印出值,但当我拉取图像并运行docker history &lt;image id&gt; 时,它会打印出SECRET_KEY 的一部分。我没有使用arguments: --build-arg:,而是尝试使用arguments: --secret,但是这些值没有传递到Dockerfile中。有没有比使用--build-arg 更安全的方法?
    • 如果您想使用 docker secrets,您可能需要使用 docker compose 并将 secret 设置为容器的 env 变量。可以参考this example。您也可以使用 powershell 任务在纯 powershell 命令中执行 docker 文件中的 docker 命令,并使用 powershell 脚本将管道密码设置为容器的 env 变量。
    • 完美运行!谢谢
    • @cjones 是的。使用 BuildKit 和秘密。请参阅下面的答案。
    【解决方案3】:

    在 Docker 18.09+ 中安全地执行此操作的方法是使用 Buildkitsecrets

    如下修改您的管道:

    variables:
      DOCKER_BUILDKIT: 1
    
    steps:
      - bash: |
          echo ${SECRET_KEY} > ${PIPELINE_WORKSPACE}/secret_key.txt
        displayName: Store secret
        env:
          SECRET_KEY: $(secretKey)
    
      - task: Docker@2
        displayName: Build
        inputs:
          command: build
          arguments: --secret id=secret_key,src=$(Pipeline.Workspace)/secret_key.txt
    

    在你的 Dockerfile 中:

    RUN --mount=type=secret,id=secret_key \
      export SECRET_KEY=$(cat /run/secrets/secret_key) && \
      python manage.py collectstatic
    CMD ["gunicorn", "-b", ":5000", "--log-level", "info", "config.wsgi:application"]
    

    【讨论】:

      猜你喜欢
      • 2018-04-20
      • 2021-03-05
      • 1970-01-01
      • 2014-02-06
      • 1970-01-01
      • 2019-09-14
      • 2017-06-04
      • 1970-01-01
      • 2019-06-10
      相关资源
      最近更新 更多