본문 바로가기

Compute/Lambda

AWS Lambda 활용 EIP 변경

방화벽 문제 등이나 기타 이유로 Elastic IP를 사용하는 서비스를 운영할 때 SPOF 발생

(과거 NAT Gateway가 생기기 전에 NAT Instance 단일 장애 지점 문제와 같은...ELB는 아직 EIP를 지원하지 않음) 

1. 필요한 Policy

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "Stmt1491964068000",
            "Effect": "Allow",
            "Action": [
                "ec2:AssociateAddress",
                "ec2:DescribeAddresses"
            ],
            "Resource": [
                "*"
            ]
        }
    ]
}
 

2. Role 생성(Trust relationsships)

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "lambda.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

 

 

3. Lambda 구성

Lambda의 경우 세 가지 방법으로 Source를 등록할 수 있음. 직접, S3를 통해, ZIP 파일을 올리는 세 가지 방식 사용 가능.

node_module이 필요한 경우 ZIP 파일 형태로 Upload 해야 함. (Lambda 실행 파일은 index.js)

[ip, port, ec2Array 수정 필요]

 

leedoing/eip-swap

Contribute to leedoing/eip-swap development by creating an account on GitHub.

github.com

 

3-1. Script(node v4.x)

config.json

{
        "eip" : "13.124.48.32",
        "port" : "8080",
        "ec2Ids" : ["i-0a62787523bb4cdf1",
                    "i-0e57c7b10e4137a6d"],
        "threshold" : 3
}

 

eip-swap.js (람다 핸들러 추가 필요 및 아직 Lambda에서는 ES6 지원하지 않음)

'use strict'
var AWS = require('aws-sdk');
AWS.config.region = 'ap-northeast-2'
var ec2 = new AWS.EC2();
var tcpp = require('tcp-ping');
var waterfall = require('async-waterfall');
var CronJob = require('cron').CronJob;
var sleep = require('system-sleep');
var async = require('async');
var config = require('../config/config.json');

var ec2Describe = function(eip, callback){
        const params = {
        }
        ec2.describeAddresses(params, function(err, data){
                if(err) console.log(err, err.stack);
                else{
                        for(var i = 0;  i < data.Addresses.length; i++){
                                if(data.Addresses[i].PublicIp == eip){
                                                callback(data.Addresses[i]);
                                }
                        }
                }
        });
}

var tcpChecker = function(eip, port, callback){
        tcpp.probe(eip, port, function(err, result){
                callback(result);
        });
}

var ec2AssociateAddress = function(allId, ec2Ids, ec2Id){
        var ec2TargetId = '';
        (ec2Id == ec2Ids[0]) ? ec2TargetId = ec2Ids[1] : ec2TargetId = ec2Ids[0]
        var params = {
                AllocationId: allId,
                InstanceId: ec2TargetId
        }
        ec2.associateAddress(params, function(err, data) {
                if (err) console.log(err, err.stack); // an error occurred
                else     console.log(data);           // successful response
                });
}

waterfall(
        [
                function(callback){
                        const ec2info = config;
                        callback(null, ec2info);
                },
                function(ec2info, callback){
                        var tcpCheckNum = 0;
                        for(var i=0; i < ec2info.threshold; i++){
                                tcpChecker(ec2info.eip, ec2info.port, function(result){;
                                        result ? null : tcpCheckNum++;
                                });
                                sleep(10000); //tcp module default timeout 5s, sleep time > 5s
                        }
                                console.log(tcpCheckNum);
                        (tcpCheckNum == ec2info.threshold ) ? callback(null, ec2info) : callback(null);
                },
                function(ec2info, callback){
                        ec2Describe(ec2info.eip, function(data){
                                callback(null, ec2info, data);
                        });
                },
                function(ec2info, ec2Address, callback){
                        ec2AssociateAddress(ec2Address.AllocationId, ec2info.ec2Ids, ec2Address.InstanceId);
                        callback(null, 'done');
                }
        ], function(err, result){
                if(err) console.log(err);
                else console.log(result);
        }
)

 

 

3-2. Lambda Configuration

 

3-3. Lambda trigger(5분마다 실행) / (CloudWatch Event Rules은 미리 생성)

 

3-4. Test

10초 주기로 3번 체크 후 3번 모두 실패했기 때문에 eip-swap 진행. 실제 EC2 Instance를 확인하면 EIP가 swap 됌.

Log는 CloudWatch Logs에서 확인 가능.

 

4. 정리

Lambda를 통해 AWS API를 호출하고 AWS 서비스 관리가 가능. (apex, node-lambda 와 같은 lambda 배포/테스트 도구 및 모듈이 존재)

참고로 리매핑 비용(월 최초 100번 이상)이 $0.10 이니 요금 폭탄을 안 맞도록 조심...)

 

 

추가로 Lambda에 VPC를 지정하면 Internet 구간 통신 불가. (AWS API 호출 및 Public IP TCP Check 불가능) 

no vpc 혹은 VPC 내의 NAT Gateway를 통해 가능.

 

AWS Lambda 함수에서 VPC내 자원 접근 기능 출시 | Amazon Web Services

몇 달 전에 곧 AWS Lambda 함수에서 VPC내 자원을 접근할 수 있을 거라고 예고해 드렸습니다. 오늘 부터 이 기능을 사용하실 수 있습니다. Lambda 함수를 통해 Amazon Redshift 데이터웨어 하우스, Amazon ElastiCache 클러스터, Amazon Relational Database Service (RDS) 인스턴스 및 특정 VPC 내에서만 접근할 수 있는 서비스 엔드 포인트에 접근할 수 있습니다. 이를 위해 자신의 VPC를

aws.amazon.com