繁体   English   中英

使用 Elastic Beanstalk 部署的应用程序上的 Elastic IP

[英]Elastic IP on application deployed using Elastic Beanstalk

我对亚马逊 Web 服务提供的 Elastic IP 服务的使用有点困惑。 我想主要的想法是我可以按照这个简单的过程切换到 web 应用程序的新版本而无需停机:

  1. 在新的 EC2 实例上部署新版本
  2. 正确配置新版本并使用暂存数据库对其进行测试
  3. 正确测试后,让这个新版本使用实时数据库
  4. 将 Elastic IP 关联到此实例
  5. 终止所有无用的服务(暂存数据库和旧的 EC2 实例)

这是部署新版本 web 应用程序的常用方法吗?

现在,如果应用程序在更多实例上扩展怎么办? 我在 Elastic Beanstalk 设置中配置了自动缩放,这创建了一个负载均衡器(我可以在 AWS 管理控制台的 EC2 部分看到它)。 问题是我显然无法将 Elastic IP 与负载均衡器相关联,我必须将其与现有实例相关联。 我应该将它关联到哪个实例? 我糊涂了...

对不起,如果有些问题听起来很愚蠢,但我只是一名程序员,这是我第一次建立云系统。

谢谢!

Elastic Load Balancing (ELB)不适用于Amazon EC2 弹性 IP 地址,实际上这两个概念根本不适合。

通过 Elastic Load Balancing 实现弹性

相反,ELB 通常通过CNAME 记录使用(但见下文),如果需要,这通过允许别名 DNS 地址更改正在使用的 ELB 的 IP 来提供第一级弹性/可用性。 第二级弹性/可用性由负载均衡器在您注册的 EC2 实例之间分配流量时执行。

可以这样想:CNAME 永远不会改变(就像弹性 IP 地址一样)并且 EC2 实例的替换是通过负载均衡器、 Auto Scaling或您自己(通过注册/取消注册实例)来处理的。

这在 Shlomo Swidler 出色的分析“Elastic Load Balancing”中的“Elastic”:ELB Elasticity and How to Test it中有更详细的解释,这反过来又指最近提供的 AWS评估 Elastic Load Balancing最佳实践,这证实了他的分析并提供了关于Elastic Load Balancing Service架构及其本身的工作原理的良好整体阅读(但缺少 Shlomo 提供的说明性分步示例)。

网站域名

请注意,之前要求 CNAME 的限制同时已通过对Amazon Route 53的相应添加得到解决,以允许也使用根域(或Zone Apex ),请参阅“ Moving Ahead With Amazon Route 53”中的别名和区域 Apex部分了解快速概览和将域名与 Elastic Load Balancing 结合使用以了解详细信息。

通过 Elastic Beanstalk 实现弹性

首先, AWS Elastic Beanstalk如上所述依次使用 Elastic Load Balancing。 最重要的是,它添加了应用程序生命周期管理:

AWS Elastic Beanstalk 是一种更简单的方法,可让您在 AWS 云中快速部署和管理应用程序 您只需上传应用程序,Elastic Beanstalk 就会自动处理容量供应、负载平衡、自动扩展和应用程序运行状况监控的部署细节。 [...] [强调我的]

这是通过将环境的概念添加到组合中来实现的,这在架构概述中进行了解释:

环境是应用程序的核心。 [...] 当您创建环境时,AWS Elastic Beanstalk 会预置运行您的应用程序所需的资源。 为环境创建的 AWS 资源包括一个弹性负载均衡器(图中的 ELB)、一个 Auto Scaling 组和一个或多个 Amazon EC2 实例。

请注意,每个环境都有一个指向负载均衡器的 CNAME (URL) ,即就像单独使用 ELB 一样。

所有这些都集中在管理和配置应用程序和环境中,其中详细讨论了 AWS Elastic Beanstalk 的一些最重要的功能,包括使用 AWS 管理控制台、CLI 和 API 的使用示例

零停机时间

出于说明目的,很难确定最相关的部分,但在零停机时间部署版本准确地解决了您的用例并暗示了所有必需的先前步骤(例如, 创建新的应用程序版本启动新环境),因此阅读AWS 管理控制台部分可能会给您该平台如何运作的最佳整体画面。

祝你好运!

除了 Steffen 的精彩回答中描述的选项之外,如果您不需要 Elastic Load Balancer 的全部功能(例如自动扩展超过一个实例),Elastic Beanstalk 似乎最近启用了弹性 IP作为选项。

我在对类似问题的回答中描述了该选项。 Elastic Beanstalk 现在允许您在两种环境类型之间进行选择,并且单实例选项会创建一个弹性 IP。

带有“单实例”和“负载平衡、自动缩放”选项的下拉菜单。


我认为在大多数情况下使用 ELB 将是更可取的选择,但例如对于登台服务器,最好有一个不那么复杂(而且更便宜)的替代方案。

很抱歉几年后回答了一个帖子,但是对于那些确实需要 ELB 上的一组静态 IP 地址的人,可以很好地要求 AWS 将他们所谓的“稳定 IP”地址添加到 ELB,从而给它静态 IP 地址功能。

他们当然不喜欢这样做 - 但如果你能证明它是合理的(主要的理由是当你的客户通过他们的防火墙对出站连接有 IP 白名单限制并且完全不愿意在这种立场上让步时)。

请注意,基于流量选项的“自动缩放”不再是直截了当的 - AWS 将无法像使用开箱即用的解决方案那样向您的 ELB 动态添加更多 ELB 端点,您必须通过随着时间的推移,向客户开放新 IP 地址的痛苦。

不过,对于最初的问题,EB 使用 ELB 来前置 EC2 实例,其中实际上不需要静态 IP 地址(没有客户端出站防火墙问题)是根据接受的答案的最佳方法。

如果上述解决方案都不起作用,一种替代方法是将 NAT 网关附加到私有子网,并将 EIP 与 NAT 网关关联。 在这种情况下,您可以使用 ELB,使用自动缩放,并拥有一个保留的 EIP。

不过,这有点贵,尤其是对于大吞吐量用例。 此外,通过 SSH 连接到实例进行调试变得有点复杂。

您可以按照已接受的答案中的说明将环境设置为单实例,或者如果您想使用已经创建的 Elastic IP,则可以执行以下操作。

在项目根目录的.ebextensions文件夹中,创建一个名为setup.config的文件并粘贴以下内容:

container_commands:
    00_setup_elastic_ip:
        command: |
            export AWS_ACCESS_KEY_ID={YOUR-ACCESS-KEY-ID}
            export AWS_SECRET_ACCESS_KEY={YOUR-SECRET-ACCESS-KEY}
            export AWS_DEFAULT_REGION=us-east-1    
            INSTANCE_ID=$(ec2-metadata -i)
            words=( $INSTANCE_ID )
            EC2_ID="${words[1]}"
            aws ec2 associate-address --instance-id $EC2_ID --allocation-id {eipalloc-ID-TO-THE-IP}

您所要做的就是替换{}中包含的 3 个部分,您可以使用 go。这会将您的 Elastic Beanstalk 实例的 IP 替换为您选择的 Elastic IP。

{}中包含的部分是(尽管去掉了 {},那只是为了告诉您哪些部分要替换为您的信息):

  1. 您的 AWS 访问密钥 ID
  2. 您的 AWS 秘密访问密钥
  3. 您要分配给 Elastic Beanstalk 环境实例的 Elastic IP 的分配 ID。

我写了一篇文章,描述了如何在启动新实例时使用 Cloudwatch 规则和 lambda 函数来完成此操作。 这是 lambda 函数代码:

const AWS = require('aws-sdk');
const ec2 = new AWS.EC2();
const PROD_ENV_NAME = 'my-prod-env-name';

// Example Event
// {
//   "version": "0",
//   "id": "ee376907-2647-4179-9203-343cfb3017a4",
//   "detail-type": "EC2 Instance State-change Notification",
//   "source": "aws.ec2",
//   "account": "123456789012",
//   "time": "2015-11-11T21:30:34Z",
//   "region": "us-east-1",
//   "resources": [
//     "arn:aws:ec2:us-east-1:123456789012:instance/i-abcd1111"
//   ],
//   "detail": {
//     "instance-id": "i-abcd1111",
//     "state": "running"
//   }
// }

exports.handler = async (event) => {
  console.log("EVENT:", event);

  // The newly launched instance ID.
  const instanceId = event.detail['instance-id'];

  // Fetch info about the newly launched instance
  const result = await ec2.describeInstances({
    Filters: [ { Name: "instance-id", Values: [instanceId] } ]
  }).promise()

  // The instance details are buried in this object
  const instance = result.Reservations[0].Instances[0];
  const isAttached = instance.NetworkInterfaces.find(int => int.Association.IpOwnerId !== 'amazon');

  // Bail if the instance is already attached to another EIP
  if (isAttached) {
    console.log("This instance is already assigned to an elastic IP")
    return { statusCode: 200, body: '' }
  }

  // In elastic beanstalk, the instance name gets assigned to the enviroment name.
  // There is also an environment name tag, which could be used here.
  const name = instance.Tags.find(t => t.Key === 'Name').Value;

  // Only assign EIPs to production instances
  if (name !== PROD_ENV_NAME) {
    console.log('Not a production instance. Not assigning. Instance name:', name)
    return { statusCode: 200, body: ''}
  }

  // Get a list of elastic IP addresses
  const addresses = await ec2.describeAddresses().promise();

  // Filter out addresses already assigned to instances
  const availableAddresses = addresses.Addresses.filter(a => !a.NetworkInterfaceId);

  // Raise an error if we have no more available IP addresses
  if (availableAddresses.length === 0) {
    console.log("ERROR: no available ip addresses");
    return { statusCode: 400, body: JSON.stringify("ERROR: no available ip addresses") }
  }

  const firstAvail = availableAddresses[0]
  try {
    // Associate the instance to the address
    const result = await ec2.associateAddress({
      AllocationId: firstAvail.AllocationId,
      InstanceId: instanceId
    }).promise();

    console.log('allocation result', result)

    return { statusCode: 200, body: JSON.stringify('Associated IP address.') };
  } catch (err) {
      console.log("ERROR: ", err);
  }
};

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM