如何使用 METEOR_SETTINGS 环境变量在 AWS/EBS 上配置 Meteor

Posted

技术标签:

【中文标题】如何使用 METEOR_SETTINGS 环境变量在 AWS/EBS 上配置 Meteor【英文标题】:How to config Meteor on AWS/EBS using METEOR_SETTINGS environment variable 【发布时间】:2016-04-18 03:00:51 【问题描述】:

尝试在 AWS/EBS(Amazon Web Services,Elastic Beanstalk)环境中设置 Meteor。

Meteor 开发运行可以传递一个命令行标志:--settings settings.json 其中settings.json 是一个包含服务器/客户端键/值配置的文件(格式正确的 JSON)。

Meteor 的部署使用METEOR_SETTINGS 环境变量,而不是在命令行中传递配置文件。如果提供的话,它应该包含一个 json 文档,例如 settings.json 的内容,例如:

$ METEOR_SETTINGS=$(cat settings.json)
$ echo $METEOR_SETTINGS
 "public":  "s3path": "https://d2v4p3rms9rvi3.cloudfront.net"  

问题是当我在 EBS 控制台中将METEOR_SETTINGS 的值设置为这个值时:

AWS/EBS 丢弃引号,转义斜线(如屏幕截图所示),并发送 Meteor:

public:s3path:https:\/\/d2v4p3rms9rvi3.cloudfront.net

如节点启动错误所示:

-------------------------------------
/var/log/nodejs/nodejs.log
-------------------------------------
npm WARN deprecated backwards-incompatible changes made to `npm run-script` and
npm WARN deprecated semver behavior.

> meteor-dev-bundle@0.0.0 start /var/app/current
> node main.js


/var/app/current/programs/server/boot.js:283
).run();
   ^
Error: METEOR_SETTINGS are not valid JSON: public:s3path:https:\/\/d2v4p3rms9rvi3.cloudfront.net
    at packages/meteor/packages/meteor.js:21:1
    at Package (packages/meteor/packages/meteor.js:42:1)
    at /var/app/current/programs/server/packages/meteor.js:1277:4
    at /var/app/current/programs/server/packages/meteor.js:1286:3
    at /var/app/current/programs/server/boot.js:242:10
    at Array.forEach (native)
    at Function._.each._.forEach (/var/app/current/node_modules/underscore/underscore.js:79:11)
    at /var/app/current/programs/server/boot.js:137:5

针对这个问题,我尝试了 value 字段中 JSON 对象的各种变体: 转义引号,用单引号括起整个 json 部分,用单引号替换双引号,以及其他尝试 - 都没有解决它。

问题是:

如何设置 METEOR_SETTINGS 以便 Meteor rcv & 正确解析它?

注意:其中一项要求是将相同的构建部署到开发、登台和生产环境。需要为每个环境单独设置配置,因此如果有另一种方法可以将设置注入 EBS 环境,而无需修改也可以解决该问题的构建。

【问题讨论】:

你能检查这个文件看看你是否可以在那里指定环境变量吗? /opt/elasticbeanstalk/hooks/configdeploy 我发现的唯一其他资源是与 OpWorks 应用程序相关的环境变量:docs.aws.amazon.com/opsworks/latest/userguide/… 或这个:docs.aws.amazon.com/elasticbeanstalk/latest/dg/… 从屏幕截图中可以看出 - 上面的内容不是 1:1 交付到 Meteor 中的内容。所以我希望会发生一些神奇的事情,你将能够以某种方式正确地指定它。我的第一次尝试类似于testJSON = "public": "s3path": "https://d2v4p3rms9rvi3.cloudfront.net" ; var temp = JSON.stringify(testJSON); var result = encodeURIComponent(temp); console.log(temp); "public":"s3path":"d2v4p3rms9rvi3.cloudfront.net" console.log(result); %7B%22public%22%3A%7B% 22s3path%22%3A%22https%3A%2F%2Fd2v4p3rms9rvi3.cloudfront.net%22%7D%7D @RobertSimon 不确定我是否理解你的意思,但确实使用encodeURIComponent 很有用。它对METEOR_SETTINGS 没有帮助,因为meteor 不希望它被url 编码,但它可能适合我提到的第二种解决方法:如果该解决方法到位,可以使用url 设置METEOR_SETTINGS_ENCODED env 变量-编码的json。然后,对其进行解码并在Meteor.startup() 部分中填充Meteor.settings。它仍然是一个 hack,但至少它提供了从 aws/ebs 上的 settings.json 文件以编程方式填充 Meteor.settings 的路径 Meteor 不希望它被编码为 URL,但是在屏幕截图上的值和 Meteor 得到的值之间存在一些可见的解码。所以我认为在网络上提供一些编码/正确转义的字符串最终可能会作为有效的 JSON 字符串提供给流星。如果您按照我的步骤获取该对象的 encodeURIComponent 值并将其粘贴到 web 上的表单,meteor 会收到什么? 【参考方案1】:

在与 AWS 支持讨论这个问题后,我意识到 AWS/EBS 不支持将 JSON 存储在环境变量中。这是因为环境变量作为键/值字符串存储在未编码的 JSON 中(显然是在 CloudFormation 中)。这里的底线有点令人失望:

METEOR_SETTINGS 不能在 AWS/EBS 控制台中使用

这确实很不幸,但是有几个解决方法。

第一个解决方法

将 json 配置移动到 s3 存储桶中,并将以下内容放入 .ebextensions/app.config 文件中:

container_commands: 
  01_setvariable: 
    command: "aws s3 cp s3://<bucket-name>/nodejs.conf /tmp/deployment/config/#etc#init#nodejs.conf 

这将使用从您的 s3 存储桶中检索到的内容完全覆盖 /etc/init/nodejs.conf。自然有机会使用微调/花哨的 bash 脚本来设置/覆盖单个设置。

我最终没有选择这种方法,因为它涉及另一个实体(S3 存储桶),并且开发迭代需要新版本部署,这不是非常快。

第二种解决方法

注意:这是我想出的一个简单的代码破解。似乎不需要太多努力就可以将所有这些混乱抛在脑后。

我最初的需要是将 AWS/EBS 环境变量传播到客户端,因此我决定绕过 METEOR_SETTINGS 变量并直接使用来自节点的 process.env 空间的环境变量填充 Meteor.settings.public。白名单由一个简单的列表管理。添加server/lib/config.js 文件:

Meteor.startup(function () 
    // public settings that need to be exposed to the client can be added here
    var publicEnvs = 
        S3_PATH: 's3path'
    ;
    var modified;
    _.each(publicEnvs, (value, key) => 
        let envValue = process.env[key];
        if (envValue) 
            Meteor.settings.public[value] = envValue;
            modified = true;
        
    );
    if (modified) 
        __meteor_runtime_config__.PUBLIC_SETTINGS = Meteor.settings.public;
    
);

万岁,您的客户可以访问您选择的环境变量!

例如,通过此更改,在 EBS 控制台中定义的 S3_PATH 环境变量可以在客户端上以 Meteor.settings.public.s3path 的形式访问。很简单,而且没有很多活动部件:)

【讨论】:

2º 解决方法的唯一问题是,您不能在根目录 /lib 中使用您的设置,因为它是要加载的第一个目录。我向亚马逊报告了这个错误:forums.aws.amazon.com/thread.jspa?threadID=236405&tstart=0 在这种情况下,如果将代码放在根 /lib 下会怎样,以便它在其他所有内容之前加载,例如在/lib/lib/config.js下?【参考方案2】:

测试了另一种解决方法。

meteor build --directory之后 编辑main.js如下

process.argv.splice(2, 0, 'program.json');
var settingfilename = './settings.json';
if (process.env.METEOR_SETTING_FILE)
  settingfilename = process.env.METEOR_SETTING_FILE;
var settings = require(settingfilename);
if (settings) 
  try 
    process.env.METEOR_SETTINGS = JSON.stringify(settings);
   catch (e) 
    console.error(e);
  

process.chdir(require('path').join(__dirname, 'programs', 'server'));
require('./programs/server/boot.js');

并将settings.json 复制到bundle/eb initeb deploy

您可以通过在 EB 控制台的配置选项卡的环境属性中添加 METEOR_SETTING_FILE 来设置其他设置文件。

每次构建后都需要编辑文件。


添加了要在构建脚本中使用的补丁文件,例如ed - ../build/bundle/main.js &lt; main.js.patch

main.js.patch

8a
var settingfilename = './settings.json';
if (process.env.METEOR_SETTING_FILE)
  settingfilename = process.env.METEOR_SETTING_FILE;
var settings = require(settingfilename);
if (settings) 
  try 
    process.env.METEOR_SETTINGS = JSON.stringify(settings);
   catch (e) 
    console.error(e);
  

// console.log (JSON.stringify(process.env));
.
w

【讨论】:

@AdrianLopez 感谢您的回复。除非我遗漏了什么,否则您的建议涉及更改构建,因此不能回答以下问题:“要求是相同的构建部署到开发、登台和生产环境。需要为每个环境单独设置配置”。 您可以使用相同的构建。正如我之前提到的,通过在环境变量中将METEOR_SETTING_FILE 设置为./settings.dev.json。我在构建脚本中有ed - ../build/bundle/main.js &lt; main.js.patch。我已经在上面添加了补丁文件 不过,您建议的解决方案要求所有部署(开发、登台、生产)的所有配置(密钥等)都在存储库中。这违反了代码和配置之间的分离,并且由于涉及(明显的)安全隐患而通常不鼓励 @tivoni,我同意你的观点。但我在settings.json 中有很多设置。如果不是太长,我不会将我的settings.json 复制到部署中。我会添加一些代码来解析 METEOR_SETTINGS 变量中的 json 字符串,而不是加载 json 文件。【参考方案3】:

新解决方案

这是我能想到的最简单的方法。我制作了一个 bash 脚本,它会自动生成项目的压缩版本并集成设置文件,因此您无需执行任何操作。

在运行此脚本之前

在你的流星项目中创建 ./lib/beanstalk-settings-fix.js

/*==============================================================================
 * Globals
 *============================================================================*/
/*global process*/
/*global Meteor*/
/*global Npm*/


if (Meteor.isProduction)
  var meteorFile = process.env.METEOR_SETTINGS_FILE;
  if(meteorFile == undefined) throw new Error(
    'METEOR_SETTINGS_FILE env variable must be defined in production.')
  var fs = Npm.require('fs');
  var pjsonBuf = fs.readFileSync( meteorFile );
  Meteor.settings = JSON.parse( pjsonBuf.toString().trim());

如何使用它

    将下一段代码复制到一个文本文件并保存为 build.sh 编辑常量。 授予它执行权限并运行它。

您将以类似 project-name.zip 的内容结束,准备将其上传到您的 beanstalk 环境。希望对您有用!

此解决方案基于AWS forums。如果您想查看旧的解决方案,请查看编辑历史记录。

#!/bin/bash

#===============================================================================
# DESCRIPTION:
#===============================================================================
# This script creates a build of the project ready to be uploaded to beanstalk.
# Requires pyton 2.7.x

#===============================================================================
# COMMON ISSUES:
#===============================================================================
# -If you upload the output to a sample application, it will fail.
# -Version format must be 0.0.0

#===============================================================================
# CONSTANTS
#===============================================================================
CURRENT_VERSION="1.0.0"
OUTPUT_NAME="file-name-without-extension"
PRODUCTION_SETTINGS_JSON="./your-project-directory/settings-prod.json"
PROJECT_DIRECTORY="./your-project-directory"
OUTPUT_DIRECTORY="./the-output-directory"
ROOT_URL="http://www.SOMEENVIRONMENT-env.us-west-2.elasticbeanstalk.com"
MONGO_URL="none"

#===============================================================================
# SAY HELLO
#===============================================================================
initial_directory=$(pwd)  #  This file's local path
clear
echo "COOKING OUTPUT"
echo "========================================================="

#===============================================================================
# RAW PROJECT BUILD
#===============================================================================
echo "> BUILDING RAW PROJECT"
cd $initial_directory
cd $PROJECT_DIRECTORY
rm -f -R "../build/bundle"
meteor build --directory ../build/

#===============================================================================
# SET PRODUCTION ENVIRONMENT VARIABLES
#===============================================================================
cd $initial_directory
json=`cat $PRODUCTION_SETTINGS_JSON`
cd $OUTPUT_DIRECTORY/bundle
mkdir -p .ebextensions
echo "option_settings:" >> .ebextensions/environment.config
echo "  - option_name: MONGO_URL" >> .ebextensions/environment.config
echo "    value: $MONGO_URL" >> .ebextensions/environment.config
echo "option_settings:" >> .ebextensions/environment.config
echo "  - option_name: ROOT_URL" >> .ebextensions/environment.config
echo "    value: "$ROOT_URL"" >> .ebextensions/environment.config
echo "files:" >> .ebextensions/environment.config
echo " '/tmp/settings.json':" >> .ebextensions/environment.config
echo "    content : |" >> .ebextensions/environment.config
echo "        "$json >> .ebextensions/environment.config
echo "option_settings:" >> .ebextensions/environment.config
echo "  - namespace:  aws:elasticbeanstalk:application:environment" >> .ebextensions/environment.config
echo "    option_name: METEOR_SETTINGS_FILE" >> .ebextensions/environment.config
echo "    value: '/tmp/settings.json'" >> .ebextensions/environment.config
chmod 444 .ebextensions/environment.config
echo "> ADDING 'settings.json' AS ENV VAR"

#===============================================================================
# CREATE PACKAGE.JSON
#===============================================================================
cd $initial_directory
cd $OUTPUT_DIRECTORY/bundle

# Write base package.json
echo '
  "name": "'$OUTPUT_NAME'",
  "version": "'$CURRENT_VERSION'",
  "scripts": 
    "start": "node main.js"
  ,
  "dependencies": 
  
' > ./package.json

# Add dependencies from meteor in packages.json
# Then add extra dependencies defined by us.
EXTRA_DEPENDENCIES='"forever": "*"'
meteor_packages=$(cat ./programs/server/package.json)
packages=$(cat ./package.json)
packages_updated=`python <<END
import json;
# We cannot operate directly bash variables, so we make a copy.
a = $packages
b = $meteor_packages
a['dependencies'] = b['dependencies']
for key, value in $EXTRA_DEPENDENCIES.iteritems():
    a['dependencies'].update(key: value)
print json.dumps(a, sort_keys=False, indent=4, separators=(',', ': '));
END`
echo "$packages_updated" > ./package.json
chmod 444 ./package.json
echo "> ADDING 'package.json'"

#===============================================================================
# ZIP OUTPUT
#===============================================================================
cd $initial_directory
cd $OUTPUT_DIRECTORY/bundle
zip -FSrq "../$OUTPUT_NAME-$CURRENT_VERSION.zip" .
echo "> ZIP THE OUTPUT"

#===============================================================================
# CLEAN THE HOUSE
#===============================================================================
cd $initial_directory
cd $OUTPUT_DIRECTORY
rm -R -f ./bundle
echo "> CLEAN THE HOUSE"

#===============================================================================
# SAY GOODBYE
#===============================================================================
echo "========================================================="
echo "YOU CAN UPLOAD THE PROJECT TO A BEANSTALK ENVIRONMENT NOW"

一些额外的帮助:如果您想检查一切是否正常,您可以在 /.ebextensions/environment.config 下的压缩输出和 / 下的包文件中找到最终设置包.json

【讨论】:

我在客户端收到以下错误:未捕获错误:METEOR_SETTINGS_FILE 环境变量必须在生产中定义。如前所述,我已经配置了第 1 步。我的设置文件有公共、私有和 kadira 对象。但是,当我在浏览器中执行 Meteor.settings.public 时,即使 /tmp/settings.json 有多个键,它也会给我一个空对象 我还没有更新解决方案,因为我没有机会在生产环境中对其进行测试,但您可能会发现它很有用。 I've made an script that automatically produces a compressed build of your project 可以上传到 beanstalk。您只需打开它,编辑常量。授予它执行权限并运行它。 如您所说,第一步仍需要手动完成,对吗?我现在试试,让你知道这是否有效。我认为 METEOR_SETTINGS_FILE 的环境变量不可用,因为如果我在控制台中执行process.env,我看不到它。 我设法使该解决方案工作,但它不是很直观或优雅。我向你推荐新的。帖子已更新。 我上传了包,但是收到 502 nginx 错误。你能分享你的nginx配置吗?还是在 .ebextensions 内?

以上是关于如何使用 METEOR_SETTINGS 环境变量在 AWS/EBS 上配置 Meteor的主要内容,如果未能解决你的问题,请参考以下文章

如何使用VS2005输出所有环境变量

如何使用环境变量作为其他环境变量的参考?

cmd里面如何分别查看系统环境变量和用户环境变量?

如何持久化docker中的环境变量

如何修改 ubuntu 环境变量

mingw64位,win764位如何安装,环境变量?