如何在亚马逊弹性Beanstalk单容器Docker环境中运行Rails迁移和种子数据

10
我正在使用docker将Rails应用程序部署到Elastic Beanstalk,目前一切顺利。现在需要运行数据库的迁移和填充数据,但我无法确定如何继续操作。看起来,/.ebextensions文件夹中的任何命令都是在主机上下文中运行,而不是在docker容器中运行。这是正确的吗?
我可以在启动后在docker容器内部运行命令以执行迁移,但我如何确保迁移只在单个实例上运行?是否有环境变量或其他方式可以从docker容器中识别出主机机器?
更新: 我在2015年8月6日发布了一个问题,询问如何在Amazon Elastic Beanstalk论坛上运行“来自Docker主机的命令”。你也可以在那里关注这些对话,因为它们很有用。
4个回答

8
我不确定你提出的解决方案是否可行。目前EB Docker部署流程运行容器命令之前,新的Docker容器并没有运行起来,这意味着您无法在其上使用docker exec。我怀疑您的命令将针对还未停止服务的旧容器执行。
经过多次尝试和错误,我通过使用shell脚本中的容器命令,使其正常工作。
container_commands:
  01_migrate_db:
    command: ".ebextensions/scripts/migrate_db.sh"
    leader_only: true

并且这个脚本:

if [ "${PROCESS}" = "WEB" ]; then

  . /opt/elasticbeanstalk/hooks/common.sh

  EB_SUPPORT_FILES=$(/opt/elasticbeanstalk/bin/get-config container -k support_files_dir)

  EB_CONFIG_DOCKER_ENV_ARGS=()

  while read -r ENV_VAR; do
    EB_CONFIG_DOCKER_ENV_ARGS+=(--env "$ENV_VAR")
  done < <($EB_SUPPORT_FILES/generate_env)

  echo "Running migrations for aws_beanstalk/staging-app"
  docker run --rm "${EB_CONFIG_DOCKER_ENV_ARGS[@]}" aws_beanstalk/staging-app bundle exec rake db:migrate || echo "The Migrations failed to run."
fi
true

我在整个脚本中加入一个检查,以确保迁移不会在后台工作进程上运行。

然后,我按照EB启动新容器时完全相同的方式构建ENV,以便为迁移设置正确的环境。

最后,我针对已创建但尚未运行的新容器运行命令 - aws_beanstalk/staging-app。它在迁移结束时退出,--rm会自动删除容器。


你说得对!它不起作用...我把它放在那里,因为那是我能找到的最接近的位置(当你通过SSH进入实例时,这些命令可以工作)。实际上,我也问了AWS的人,他们给了我和我发布的相同的答案(我认为他们谷歌搜索并得到了我的答案)。我会尽快测试这个,并将您的解决方案标记为答案。 - King'ori Maina
1
@nmoot,看看这个AWS的人员写的方法。https://forums.aws.amazon.com/message.jspa?messageID=665468#665468 ... 他们更详细地解释了问题。 - King'ori Maina
看起来他们的方法很可行。也许我会在某个时候尝试一下,但考虑到我的方法现在运作良好,我暂时会坚持使用它。他们可能是对的,他们的方法在面对EB部署过程的变化时会更加弹性。我已经因为EB的变化而不得不一次性改变了我的迁移解决方案。祝你好运,无论选择哪种方式。 - nmott
小提示:如果您的镜像使用ENTRYPOINT而不是/和CMD命令,则docker run命令也应该有--entrypoint标志。 - Igor Escobar
1
谢谢!对于其他试图做这件事的人 - 我知道这是EB脚本中构建环境参数的方式,但如果(像我一样)这个bash脚本看起来很可怕,你可以使用jq实现相同的效果,它似乎已经安装了:env_args=$(/opt/elasticbeanstalk/bin/get-config environment | jq -r 'to_entries | map("--env \(.key)=\(.value)") | join(" ")')(然后在docker命令中使用$env_args)。 - Joe Freeman

4

更新:尽管这个解决方案看起来是正确的,但实际上并没有按照预期工作(虽然一开始似乎是这样的)。原因最好在nmott答案中解释。为了纪念留在这里。


我能够使用container_commands通过.ebextensions目录配置文件使其工作。在此处了解有关容器命令的更多信息。引用如下...

container_commands中的命令按名称按字母顺序处理。它们在应用程序和Web服务器设置完毕并提取应用程序版本文件之后运行,但在部署应用程序版本之前运行。它们还可以访问环境变量,例如您的AWS安全凭据。此外,您可以使用leader_only。自动缩放组中选择一个实例作为领导者。如果将leader_only值设置为true,则该命令仅在标记为领导者的实例上运行。

因此,应用该知识...container_commands.config将是...

# .ebextensions/container_commands.config
container_commands:
  01_migrate_db:
    command: docker exec `docker ps -l -q -f 'status=running'` rake db:migrate RAILS_ENV=production
    leader_only: true
    ignoreErrors: false
  02_seed_db:
    command: docker exec `docker ps -l -q -f 'status=running'` rake db:seed RAILS_ENV=production
    leader_only: true
    ignoreErrors: false

先运行迁移,然后再填充数据库。我们使用docker exec [OPTIONS] CONTAINER_ID COMMAND [ARG...],它在现有容器的上下文中运行附加的COMMAND [ARG...](而不是主机)。我们通过运行docker ps -q来获取CONTAINER_ID


2

解决方案1:在启动服务器时运行迁移

在我工作的公司中,我们有一个类似于这行代码的等效代码来启动生产服务器:

 bundle exec rake db:migrate && bundle exec puma -C /app/config/puma.rb

是负载均衡环境(3-12个实例,取决于负载),它们都会执行此脚本(我们通过在部署期间逐个引入实例来进行负载平衡)。

问题是第一批部署(第一个实例)将执行 bundle exec rake db:migrate 并运行迁移(意味着它将运行数据库更改),然后完成后将运行服务器 bundle exec puma -C /app/config/puma.rb

第二批部署(第二个实例)也将运行 bundle exec rake db:migrate,但不会执行任何操作(因为没有未决的迁移)。 它将继续脚本的第二部分 bundle exec puma -C /app/config/puma.rb

所以说,我不认为这是完美的解决方案,但它是实用的并且适用于我们的团队。 我不相信有任何通用的“最佳做法”适用于在EB上运行Rails迁移,因为一些应用程序团队不想在部署后运行迁移,而其他团队(如我们的团队)希望在部署后立即运行它们。

解决方法2:后台工作环境运行迁移

如果您有像Delayed job、Sidekiq、Rescue这样的工作者,则可以将它们配置为运行迁移:

bundle exec rake db:migrate && bundle exec sidekiq

因此,首先部署工作者,然后再部署不运行迁移的Web服务器。

例如:只需 bundle exec puma

解决方法3:Hooks

我同意使用EB钩子处理更复杂的DevOps问题(如为Nginx Web服务器拉取SSL证书),而不是运行迁移。

无论如何,钩子已经在这个SO问题中得到了解决,因此我不会重复解决方案。 我只会引用这篇文章,以帮助您理解它们:

结论

您需要自行决定哪种工具最适合您的应用程序。但是,与Ansible或Kubernetes等工具相比,EB确实是一种非常简单的工具。无论您使用什么工具,只要能正常运行就可以。

对于Rails开发人员,这里有一个更有用的EB链接:


2
使用.ebextensions/01-environment.config文件:
container_commands:
  01_write_leader_marker:
    command: touch /tmp/is_leader
    leader_only: true

现在,在Dockerfile / Dockerrun.aws.json中添加目录/tmp到卷中。
然后,检查并设置所有初始化命令,如数据库迁移在sh脚本中,首先检查文件/tmp/is_leader是否存在,并仅在此情况下执行它们。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接