在 AWS 上存储 Docker 容器的配置文件的最佳方式是啥?

Posted

技术标签:

【中文标题】在 AWS 上存储 Docker 容器的配置文件的最佳方式是啥?【英文标题】: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?

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

【问题讨论】:

【参考方案1】:

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

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

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

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

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

更多详情如下:

Managing Secrets for Amazon ECS Applications Using Parameter Store and IAM Roles for Tasks

【讨论】:

谢谢你,马辛!您能否告诉我在哪里可以阅读更多关于使用 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。我理解对了吗?非常整洁,但令人兴奋!

以上是关于在 AWS 上存储 Docker 容器的配置文件的最佳方式是啥?的主要内容,如果未能解决你的问题,请参考以下文章

AWS Elastic Beanstalk 多容器 Docker 配置上的 HTTPS/SSL 问题

Docker 容器在使用 AWS ECR 的 AWS ECS 中不起作用

AWS 上的多容器 Docker

AWS beanstalk 中 docker 容器中的 JVM 内存设置

如何使用非默认运行参数在 AWS Elastic Beanstalk 中运行 Docker 容器?

在单个 docker 容器环境中配置 nginx (AWS ElasticBeanstalk)