簡體   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