【问题标题】:What's the best way to store a config file for a Docker container on AWS?在 AWS 上存储 Docker 容器的配置文件的最佳方式是什么?
【发布时间】:2020-10-28 15:29:42
【问题描述】:

我有一个 node.js 应用程序,它有一个包含敏感数据的配置文件。我想通过 AWS ECS(集群)作为 docker 容器启动它。

存储敏感配置数据的最佳方式是什么?

我目前正在通过 Docker 容器的任务定义中的环境变量发送它们,但必须有更好的方法来做到这一点。

这样的最佳做法是什么?我应该使用 Docker Secrets 还是 Amazon Secrets Manager?

我知道这是一个非常笼统的问题,但我找不到答案,所以也许有人有建议?

【问题讨论】:

    标签: amazon-web-services docker environment-variables containers amazon-ecs


    【解决方案1】:

    存储敏感配置数据的最佳方式是什么?

    通常您会使用SSM Parameter StoreAWS Secrets Manager。它们之间的一个区别是SSM Parameter Store 是免费的(标准类型),而AWS Secrets Manager 不是。

    然后,您将使用任务定义中的环境变量将“链接”传递给商店或经理中的秘密。

    为此,您的应用程序需要使用 AWS SDK 来获取容器中的机密。还需要设置task role,以便您的容器可以访问机密

    该任务还需要能够访问SSM Parameter Store 或AWS Secrets Manager。因此,如果您在没有 Internet 的私有子集中运行它们,则需要为 SSM 和 Secret Manager(取决于您使用什么)设置 NAT gatewayVPC interface endpoints 以允许访问。

    更多详情如下:

    【讨论】:

    • 谢谢你,马辛!您能否告诉我在哪里可以阅读更多关于使用 NAT 网关和 VPC 接口端点以便能够在没有互联网的情况下访问这些秘密的信息?我不确定我是否在关注最后一部分...谢谢!
    • @Aerodynamika 嗨,没问题。我添加了答案的链接。这仅适用于您的任务在没有互联网的私有子网中运行的情况。如果您的任务已经可以访问 Internet,则不必担心 :-)
    • — 所以您的意思是您认为最好的方法是使用 AWS 机密管理器或 SSM 参数存储,并在我有互联网时将它们作为“链接”简单地访问,如果我没有没有互联网然后我将它们作为环境参数传递?谢谢!
    • @Aerodynamika 在这两种情况下,您都使用环境变量传递它们。但如果没有互联网,您将不得不做一些额外的设置。这是必需的,因为您的任务需要连接到 SSM 服务本身才能获取密钥的实际值。我添加了新链接以提供更多详细信息。希望它会有用:-)
    【解决方案2】:

    很好的问题。我同意 SSM Parameter Store 或 AWS Secrets Manager 是这项工作的推荐工具,但如果您遵循基础设施即代码原则,我认为它们并不那么有用。

    因此,我将我的配置文件保存在同一个 Git 存储库中的 Terraform 脚本旁边,并在运行时将实际敏感数据(密码、密钥等)从 SSM 参数存储注入到特定的目标文件中。

    与使用 AWS 提供的解决方案相比,这有几个优势:

    • 没有大小限制(在免费版本中,SSM Parameter Store 将您限制为 4 KB)。
    • 非敏感配置受版本控制,具有随之而来的所有优势。
    • 我可以轻松查看当前使用的配置并更新它们,而无需登录 AWS 控制台等。

    这是我使用 Terraform / AWS ECS 管理配置/机密的方法

    基本思路如下:我们将没有敏感数据的配置文件存储在 Terraform 旁边的存储库中。秘密存储在 AWS 参数存储中。为了在运行时将数据放入我们的容器中,我们修改入口点。我们当然可以只创建修改后的图像,但在我看来这会产生很大的维护开销。使用入口点方法,我们可以继续使用原始图像。

    缺点是我必须创建自定义入口点脚本。这意味着我必须找到我感兴趣的镜像的 Dockerfile 并提取用于启动镜像中运行的实际进程的命令。

    我有一个这样的 git 存储库:

    ├── files
    │   └── promstack
    │       ├── grafana
    │       │   ├── default-datasources.yml
    │       │   ├── grafana.ini
    │       │   └── run.sh
    │       ├── loki
    │       │   └── run.sh
    │       ├── nginx
    │       │   ├── nginx.conf
    │       │   └── run.sh
    │       └── prometheus
    │           ├── prometheus.yml
    │           ├── rules-alerting.yml
    │           ├── rules-recording.yml
    │           └── run.sh
    ├── myscript.tf
    └── variables.tf
    

    run.sh 脚本代表入口点。这是一个典型的run.sh

    #!/bin/sh
    
    set -x
    
    require () {
        if [ ! "$1" ]; then 
            echo "ERROR: var not found"
            exit 1 
        fi 
    }
    
    expand () {
        var_name="${1}"
        file="${2}"
    
        eval var="\$$var_name"
    
        sed -i "s+\${${var_name}}+${var}+g" ${file}
        sed -i "s+\$${var_name}+${var}+g" ${file}
    }
    
    require ${GRAFANA_INI}
    require ${DEFAULT_DATASOURCES_YML}
    require ${DOMAIN}
    
    echo ${GRAFANA_INI} | base64 -d > /etc/grafana/grafana.ini
    chmod 666 /etc/grafana/grafana.ini
    
    expand DOMAIN /etc/grafana/grafana.ini
    
    echo ${DEFAULT_DATASOURCES_YML} | base64 -d > /etc/grafana/provisioning/datasources/default.yml
    chmod 666 /etc/grafana/provisioning/datasources/default.yml
    
    su -s "/bin/sh" -c "/run.sh" grafana
    

    这里是 Terraform 脚本的一部分(准确地说是 ECS 容器任务定义):

    {
      name: "grafana",
      image: "grafana/grafana:7.0.5",
      portMappings: [{
        containerPort : 3000,
        hostPort: 0,
        protocol: "tcp"
      }],
      user: "0",
      entryPoint: [ "/bin/sh", "-c", join(" ", [
        "export DEFAULT_DATASOURCES_YML=${base64encode(file("${path.module}/files/promstack/grafana/default-datasources.yml"))};",
        "export GRAFANA_INI=${base64encode(file("${path.module}/files/promstack/grafana/grafana.ini"))};",
        "echo '${base64encode(file("${path.module}/files/promstack/grafana/run.sh"))}' | base64 -d | sh;"
      ])],
      secrets: [
        {
          name: "DOMAIN",
          valueFrom: "<my ssm parameter>"
        }
      ]
    },
    

    【讨论】:

    • 感谢您的回复!你能告诉我 Terraform 脚本是什么吗?这是您用来将某些参数传递给您的应用的特殊基础架构吗?
    • 或者更准确地说@trailnag——你如何在运行时注入敏感数据?你会为此使用简单的 shell 脚本还是 Terraform?
    • 这是一个有点“hacky”的解决方案,但它在 prod 中工作了很长时间,没有任何问题。我将用一些摘录编辑我的评论
    • 因此,当您运行 terraform apply 时,ecs 任务定义将被部署到 AWS,并将编码为 base64 字符串的文件填充到入口点中,包括 run.sh 本身。当容器启动时,run.sh base64 字符串被解码并在 shell 中运行,然后(初始)脚本将从环境变量中解码其他 base64 字符串并将它们具体化为 grafana 期望的文件。同时,来自 SSM 的 DOMAIN secret 也已提供给容器 env var,您可以使用 sed“扩展”到 grafani.ini。我理解对了吗?非常整洁,但令人兴奋!
    猜你喜欢
    • 2016-07-21
    • 2011-03-05
    • 2010-11-24
    • 1970-01-01
    • 2011-07-30
    • 2013-03-28
    • 2013-07-22
    • 1970-01-01
    相关资源
    最近更新 更多