/.ebextensions
文件夹中的任何命令都是在主机上下文中运行,而不是在docker容器中运行。这是正确的吗?我可以在启动后在docker容器内部运行命令以执行迁移,但我如何确保迁移只在单个实例上运行?是否有环境变量或其他方式可以从docker容器中识别出主机机器?
更新: 我在2015年8月6日发布了一个问题,询问如何在Amazon Elastic Beanstalk论坛上运行“来自Docker主机的命令”。你也可以在那里关注这些对话,因为它们很有用。
/.ebextensions
文件夹中的任何命令都是在主机上下文中运行,而不是在docker容器中运行。这是正确的吗?docker exec
。我怀疑您的命令将针对还未停止服务的旧容器执行。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
会自动删除容器。
更新:尽管这个解决方案看起来是正确的,但实际上并没有按照预期工作(虽然一开始似乎是这样的)。原因最好在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
。
在我工作的公司中,我们有一个类似于这行代码的等效代码来启动生产服务器:
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迁移,因为一些应用程序团队不想在部署后运行迁移,而其他团队(如我们的团队)希望在部署后立即运行它们。
如果您有像Delayed job、Sidekiq、Rescue这样的工作者,则可以将它们配置为运行迁移:
bundle exec rake db:migrate && bundle exec sidekiq
因此,首先部署工作者,然后再部署不运行迁移的Web服务器。
例如:只需 bundle exec puma
我同意使用EB钩子处理更复杂的DevOps问题(如为Nginx Web服务器拉取SSL证书),而不是运行迁移。
无论如何,钩子已经在这个SO问题中得到了解决,因此我不会重复解决方案。 我只会引用这篇文章,以帮助您理解它们:
您需要自行决定哪种工具最适合您的应用程序。但是,与Ansible或Kubernetes等工具相比,EB确实是一种非常简单的工具。无论您使用什么工具,只要能正常运行就可以。
对于Rails开发人员,这里有一个更有用的EB链接:
container_commands:
01_write_leader_marker:
command: touch /tmp/is_leader
leader_only: true
jq
实现相同的效果,它似乎已经安装了:env_args=$(/opt/elasticbeanstalk/bin/get-config environment | jq -r 'to_entries | map("--env \(.key)=\(.value)") | join(" ")')
(然后在docker命令中使用$env_args
)。 - Joe Freeman