이제 막 시작한 스타트업을 위한 AWS 아키텍처 - AWS를 더 AWS 답게



AWS는 Amazon Web Services 의 약자로 Microsoft Azure, Google Cloud Platform 과 함께 세계 3대 클라우드 서비스 입니다. 국내에는 KT 클라우드와, Naver Cloud Platform 역시 선전 하고 있지만, 규모면에서 세계 3대장을 따라갈 수 없는 상황 입니다.

가장 먼저 서비스를 시작한 AWS 는 본인들의 인프라를 구축하던 노하우를 기반으로 웹서비스를 시작했고, 구글은 본인들의 각종 서비스와 API를 편하게 사용하는 것을 기반으로 많은 고개층을 확보하고 있으며 .NET 개발자들에게 MS-SQL을 빵빵하게 지원하며 신세계를 선보이던 Microsoft Azure 역시 마소 주가(실제 주식 가격을 말합니다) 를 고공비행 하게 만들고 있습니다. 


개발을 할땐 몰랐지만, 점점 걱정되는 보안 구멍과 튼튼한 VPC 디자인의 니즈는 점점 갈 수록 커집니다.


이 글은 우수한 보안과 회사에서 제공하는 상용 플랫폼이 처음으로 출시된 상황에서 회사의 표준을 만드는 것부터 실제로 사용에 편리한 클라우드 환경을 제공하는데 가장 큰 목적이 있었습니다. 직접 만들어서 사용해 보면, 각 인스턴스 간의 데이터 흐름을 파악하는데 상당한 도움이 됩니다. 아래는 우리가 만들어 볼 구성입니다. 꼭 직접 만들어보세요. 만들고 바로 지우면 돈 천원이면 경험해 보실 수 있습니다. 





요약


1. 데이터 흐름

2. VPC 생성

3. Subnet 생성

4. Internet Gateway, NAT Gateway 생성

5. Routing Table, Network ACL 설정

6. AWS Lambda를 Private Subnet에서 구성





#1 데이터 흐름


유저, 데브옵스, Lambda가 VPC와 그 내부의 리소스에 접근하는 순서 입니다.

User

1. CloudFront를 통해 S3에서 호스팅 되는 React App 접속 
2. Route53을 통해 Internet Gateway로 이동
3. ALB를 통해 Public Subnet 라우팅 테이블로 이동
4. 라우팅 테이블에서 ALB 를 통해 접근한 트래픽은 어플리케이션으로 트래픽을 보냄
5. 정상적이지 않은 접근일 경우 다시 Internet Gateway로 포워딩
6. EB 어플리케이션은 내부 IP를 사용하여 MySQL에 접근


DevOps

1. EC2 접근 최소화
2. 굳이 접근 해야 한다면, Bastion 서버를 중간에 두고 접근
3. 로그 등은 반드시 언제든 확인할 수 있는 시스템을 갖추어야 한다.
3. 데이터 베이스 접근 역시 Bastion 서버를 중간에 두고 접근
4. 굳이 Bastion 서버가 아니더라도 VPN 등을 두고 접근을 실시한다.


Lambda

1. Lambda가 Private Subnet 에서 생성됨 
2. 인터넷을 사용해야 할 경우 Public Subnet 에 있는 NAT Gateway를 통해 인터넷에 접근함
3. 데이터베이스 사용시 내부IP를 이용하여 데이터베이스에 접근






#2 VPC 생성


AWS VPC (Virtual Private Cloud) 란? 

논리적으로 생성되는 가상 네트워크로 AWS 거의 모든 리소스들이 VPC 안에서 실행됩니다. VPC는 사용자 계정 전용 이며, 다른 AWS 계정 유저는 접근할 수 없습니다. AWS는 계정 생성과 동시에 기본적으로 구성된 EC2-VPC를 제공하지만, 우리는 새로운 VPC를 구성하여 진행 할 것입니다. VPC나 VPC에 연결되는 네트워크 리소스 들은 리전별로 생성 가능한 제한이 있으므로, 이를 미리 확인해 두는 것이 좋습니다. 

VPC는 하나의 리전 내에서만 생성이 가능하고 여러 리전을 걸쳐 생성될 수는 없습니다. 하지만, 한 리전에서 복수의 가용영역(AZ)을 지원하기 때문에, 이 AZ에 걸쳐서 생성할 수 있습니다. VPC가 가질 수 있는 IP 주소는 65535(2^16)개로 제한됩니다.



AWS VPC 등 네트워크 리소스 생성 제한 개수

VPC

5 per Region

Subnet

200 per VPC

IPv4 CIDR Block

5 per VPC (50개 까지 증가 가능)

IPv6 CIDR Block

1 (증가 불가)

Elastic IP

5 per Region

Egress-only internet gateways

5 per Region

Internet gateways

5 per Region (1VPC = 1 IGW)

NAT gateways

5 per AZ (가용 영역당 5개)

Network ACLs

200 per VPC

Rules

20 per Network ACL

Route tables

200 per VPC

Routes

50 per Route Table

VPC security groups

2500 per Regions

Inbound or outbound rules

60 per Security Group





위와 같이 VPC를 생성할 수 있습니다. 

IPv4 CIDR 블록인경우 2^16 개 까지 밖에 만들 수 없으므로, 위 처럼 설정하였습니다. 필요에 따라 설정하면 되고, 2^16 개가 전부 필요 없을 경우 172.16.0.0/24 등 256개만 설정할 수 있습니다. 172.16.0.0/24 로 설정하면 2^(32-24) 개 만큼만 할당 가능한 IP CIDR 블록이 생성됩니다.




#3 Subnet 생성


Subnet 생성도 VPC 생성 만큼 간단합니다. 우리는 Elastic Beantalk를 사용할 예정이고, Lambda와 RDS를 프라이빗 서브넷에서 실행할 예정이기 때문에 퍼블릭 서브넷 2개, 프라이빗 서브넷 2개 해서 총 4개의 서브넷을 생성할 것 입니다.


공개여부

Subnet 이름

IPv4 CIDR

가용영역

Public

Public-Subnet-1

10.0.0.0/24

ap-northeast-2a

Public

Public-Subnet-1

10.0.1.0/24

ap-northeast-2b

Private

Private-Subnet-1

10.0.255.0/24

ap-northeast-2a

Private

Private-Subnet-1

10.0.254.0/24

ap-northeast-2b



방금 생성한 VPC를 선택하여 위와 같이 4개의 서브넷을 생성합니다. IPv4 CIDR 설정은 자유 이지만, VPC CIDR을 벗어날 수는 없습니다. 위처럼 설정하게 되면, 해당 서브넷에서 생성되는 리소스는 10.0.4.* 과 같은 IP를 가지게 됩니다.





#4 Internet Gateway, NAT Gateway  생성


VPC와 Subnet을 생성하였습니다. 각 서브넷의 라우팅 테이블과 Network ACLs을 설정하기 위해 IGW와 NAT GW를 먼저 생성해 줍니다. 생성은 매우 간단합니다.


Internet Gateway


인터넷 게이트웨이를 생성하면, 조금 전에 생성한 VPC에 인터넷 게이트웨이를 아래와 같이 연결해 줘야 합니다.



NAT Gateway

NAT Gateway는 AWS가 Fully Manage 하는 서비스 이지만, 직접 구성하는 것보다 3배 정도 가격입니다. NAT 인스턴스는 failover가 되지 않고 싱글 AZ에 있어야 함으로 장애 발생시 NAT에 의존하는 모든 인스턴스들의 퍼블릭 엑세스가 막힙니다. 이것은 NAT의 구조적인 한계 이기에 무엇이 더 나은지 생각해 보아야 합니다.


NAT Gateway는 Elastic IP가 필요합니다. 내부IP만 가지고 있는 리소스들이 외부 인터넷 접속을 할때 NAT 게이트웨이의 Elastic IP(외부 아이피 입니다) 를 가지고 외부로 나갈 수 있습니다.



Internet Gateway


NAT Gateway

  • 인터넷 게이트웨이는 AWS VPC와 인터넷을 연결해주는 논리적인 커넥션
  • 1 VPC에 1 IGW 만 할당할 수 있고 통신 대역폭의 제한은 없음
  • VPC에 인터넷 게이트웨이가 연결되어 있지 않으면, 외부에서 VPC 내부로 접근 할 수 없음
  • 적어도 1개의 퍼블릭 서브넷이 IGW로 접근하는 것을 라우팅 테이블에서 허용해 줘야 함
  • 라우팅 테이블에서 트래픽이 인터넷 게이트웨이를 향하게 된다면, 퍼블릭 서브넷으로 간주함.
  • 프라이빗 서브넷안에 있는 리소스들이 인터넷에 접속할 수 있도록 해줌 
  • 명시적으로 허용하지 않는 이상 일방통행
  • 외부 트래픽은 NAT Gateway를 통해 들어올 수 없음.
  • Private Subnet 에서 생성된 Lambda 도 NAT Gateway를 지나 외부 인터넷과 통신할 수 있음


NAT Gateway는 Subnet 을 지정하는데 어떻게 HA를 보장 하나요?

보장 하지 못합니다. 각 Zone 마다 NAT Gateway를 생성해야 합니다.
If you have resources in multiple Availability Zones and they share one NAT gateway, and if the NAT gateway’s Availability Zone is down, resources in the other Availability Zones lose internet access. To create an Availability Zone-independent architecture, create a NAT gateway in each Availability Zone and configure your routing to ensure that resources use the NAT gateway in the same Availability Zone.

AWS 공식문서 보기




#5 Route Tables, Network ACLs 설정


라우트 테이블은 특정 IP Address 에 라우팅 경로를 설정하는 기능입니다. 서브넷에서 외부(외부 인터넷이 아닌 서브넷의 외부)로 나가는 아웃바운드 트래픽에 대한 경로 정의 입니다. 우리는 퍼블릭 서브넷과 프라이빗 서브넷이 있으므로, 각각의 라우트 테이블을 설정해 줄 것입니다. VPC에게는 암시적인 기본 라우팅 테이블(하단 참조)이 있습니다. 그래서 명시적으로 라우팅을 설정 하려면, 특정 라우팅 테이블과 연결해야 합니다. 아니면, 기본 라우팅 테이블과 암시적으로 연결되며, 1개의 서브넷은 1개의 라우팅 테이블과 연결됩니다.



라우팅 테이블 생성

 


라우팅 테이블의 주요 개념을 알고 진행해야 편리합니다.

기본 라우팅 테이블

VPC와 함께 자동으로 제공되는 라우팅 테이블입니다. 다른 라우팅 테이블과 명시적으로 연결되지 않은 모든 서브넷의 라우팅을 제어합니다.

사용자 지정 라우팅 테이블

VPC에 대해 생성하는 커스텀 라우팅 테이블입니다.

서브넷 라우팅 테이블

서브넷과 연결된 라우팅 테이블입니다.

엣지 연결

인바운드 VPC 트래픽을 어플라이언스로 라우팅하는 데 사용하는 라우팅 테이블입니다. 라우팅 테이블을 인터넷 게이트웨이 또는 가상 프라이빗 게이트웨이와 연결하고 어플라이언스의 네트워크 인터페이스를 VPC 트래픽의 대상으로 지정합니다.

게이트웨이 라우팅 테이블

인터넷 게이트웨이 또는 가상 프라이빗 게이트웨이와 연결된 라우팅 테이블입니다.

로컬 게이트웨이 라우팅 테이블

Outposts 로컬 게이트웨이와 연결된 라우팅 테이블입니다.

대상

트래픽을 이동할 대상 IP 주소의 범위입니다(대상 CIDR). 예를 들어, 172.16.0.0/12 CIDR이 있는 외부 회사 네트워크입니다.

전파

라우팅 전파를 사용하면 가상 프라이빗 게이트웨이가 라우팅 테이블에 라우팅을 자동으로 전파할 수 있습니다. 즉, 라우팅 테이블에 VPN 라우팅을 수동으로 입력할 필요가 없습니다. 

대상

대상 트래픽을 전송할 때 사용할 게이트웨이, 네트워크 인터페이스 또는 연결입니다(예: 인터넷 게이트웨이).

로컬 라우팅

VPC 내 통신을 위한 기본 라우팅입니다.



Private Subnet Route Table 예시


Public Subnet Route Table 예시

 


라우팅 우선순위 

라우팅 IP 주소가 같은 설정이라면 아래와 같은 정적 라우팅 테이블이 우선됩니다. 

  • NAT 게이트웨이 
  • 네트워크 인터페이스 
  • 인스턴스 ID 
  • 인터넷 게이트웨이 
  • 게이트웨이 VPC 엔드포인트 
  • 전송 게이트웨이 
  • VPC 피어링 연결

정적 라우팅과 전파된 라우팅의 대상이 동일한 경우 정적 라우팅이 우선 적용됩니다.
전파된 라우팅 의 예: 가상 사설 네트워크(VPN 게이트웨이)



Network ACLs 

Security Groups(보안그룹)은 Allow 로직만 설정이 가능합니다. 이 와 다르게 Network ACLs는 인바운드, 아웃바운드, ALLOW, DENY 로직으로 세밀하게 서브넷 접근 제어가 가능합니다.





Network ACLs 팁

규칙 번호

번호가 가장 낮은 규칙 부터 평가됩니다. 규칙에 일치하는 트래픽이 있으면 이와 모순되는 상위 규칙이 있더라도 적용됩니다.

유형

트래픽 유형(예: SSH)입니다. 모든 트래픽 또는 사용자 지정 범위를 지정할 수도 있습니다.

프로토콜

표준 프로토콜 번호를 가진 어떤 프로토콜이든 지정할 수 있습니다. 

포트 범위

트래픽에 대한 수신 포트 또는 포트 범위입니다. 예를 들어, HTTP 트래픽의 경우 80입니다.

소스

[인바운드 규칙만 해당] 트래픽의 소스(CIDR 범위)입니다.

대상

[아웃바운드 규칙만 해당] 트래픽의 대상(CIDR 범위)입니다.

허용 여부

ALLOW / DENY

룰 갯수

Network ACL 당 인바운드 20개, 아웃바운드 20개

Network ACLs 갯수

VPC 당 200개

IPv6 자동추가

기본 네트워크 ACL의 인바운드 규칙을 수정한 경우에는, IPv6 블록을 VPC에 연결할 때 인바운드 IPv6 트래픽에 대한 허용 규칙이 자동으로 추가되지는 않습니다. 이와 마찬가지로 아웃바운드 규칙을 수정한 경우에는 아웃바운드 IPv6 트래픽에 대한 허용 규칙이 자동으로 추가되지 않습니다.

Asterisk 규칙

각 네트워크 ACL에는 규칙 번호가 asterisk로 된 기본 규칙이 포함되어 있습니다. 이 규칙은 패킷이 다른 어떤 규칙과 도 일치하지 않을 경우에는 거부되도록 되어 있습니다. 이 규칙을 수정하거나 제거할 수 없습니다.





#6 Lambda를 프라이빗 서브넷에서 실행


우선 [서비스]-[Lambda] 로 이동하여 새로운 함수를 생성하고 하단에 VPC 설정으로 가서 아래와 같이 설정을 해봅니다. Lambda가 VPC를 통해 외부 인터넷에 접속해야 한다면 반드시 NAT Gateway를 통해야 하며, 라우팅테이블에서 NAT Gateway가 정의된 프라이빗 서브넷에서 만들어져야 합니다.


위 처럼 세팅을 해 주고 함수내용을 아래와 같이 해서 실행을 하면 Lambda 는 실행되지 않습니다. 해당 설정만으로는 실행이 불가하고 람다에게 아래와 같은 역할을 주어야 합니다.



가장 필요한 권한은 AWSLambdaVPCAccessExecutionRole 이고, Lever에서 작동하는 Lambda는 S3와 SQS 권한도 필요하여 임시로 주었습니다. 추후, FullAccess권한은 빼는것이 당연히 맞습니다.


테스트 람다 언어는 Python3 입니다.

import urllib3


def lambda_handler(event, context):
    http = urllib3.PoolManager()
    url = "https://google.com"
    req = http.request('GET', url)
    data = req.status
    return data


응답 :

Response:
200

Request ID:
"6d99128e-686d-4c8f-b942-9c55ad7e8bf4"

Function logs:
START RequestId: 6d99128e-686d-4c8f-b942-9c55ad7e8bf4 Version: $LATEST
END RequestId: 6d99128e-686d-4c8f-b942-9c55ad7e8bf4
REPORT RequestId: 6d99128e-686d-4c8f-b942-9c55ad7e8bf4    Duration: 725.37 ms    Billed Duration: 800 ms    Memory Size: 128 MB    Max Memory Used: 56 MB    Init Duration: 158.89 ms    

응답이 잘 오는 것을 볼 수 있습니다. 


DB 접속을 잘 하는지 테스트 하기 위해 우선 아래와 같이 로컬에서 작업합니다.

~ % mkdir lambda
~ % cd lambda
~ % vi lambda_function.py

#====================== 함수 코드를 붙여 넣습니다. =======================
import pymysql


host = 'private-subnet-db-host.com'
pw = 'db_pw'
name = 'db_user'
db_name = 'db_name'



def lambda_handler(event, context):
    db = pymysql.connect(host, user=name, password=pw, db=db_name)
    print(db)
    return db
#==================================================================

~ % pip install pymysql -t .
~ % zip -r lambda.zip *

만들어진 lambda.zip을 람다에 업로드 하고 실행해 봅니다. 혹시 DB 연결이 되지 않으면, Lambda가 속해있는 Security Group이 RDS의 Security Group에서 허용하고 있는지 확인합니다. 


SAM으로 배포할 경우 추가해야 하는 옵션 

YAML 설정 파일로 SAM을 통해 배포하는 경우 아래와 같은 내용을 추가 해야 합니다.

Role: "arn:aws:iam::1234567890:role/lambda-vpc-access"
VpcConfig:
  SecurityGroupIds:
    - sgGroup: sg-보안그룹ID
  SubnetIds:
    - subnet1: subnet-프라이빗서브넷-1-ID
    - subnet2: subnet-프라이빗서브넷-2-ID




마치며..

너무나 긴 포스팅이라 따라오시기 조차 쉽지 않지만, 한 번만 해보면, 네트워크 아키텍처라는 것을 무엇인지 알게 되실겁니다. 절대 어렵지 않아요. 백엔드 서버를 Public Subnet 에 둔 것은 개발 편의를 위한 것이며, 개발과 운영 인력이 나누어져 있다면, Private Subnet에 두는 것을 고려해 보세요. 

이제 막 서비스를 시작한 스타트업에게 적용할만한 현실적인 방법이라 생각합니다!




  • [[a.original_name]] ([[a.file_size | fileSizer]])
좋아요[[ postLike | likePlus ]]
공유
라이언

“Lead Python Engineer”

댓글 [[totalCommentCount]]
[[ comment.author__nick_name ]] [[ comment.datetime_updated | formatDate]] (수정됨)

[블라인드 처리된 글 입니다.]

답장
[[ sub.author__nick_name ]] [[ sub.datetime_created | formatDate ]] (수정됨)

취소
댓글을 남겨주세요.
'데브옵스' 관련 최신 포스트
[[ post.title ]]
[[ post.datetime_published_from | DateOnly ]]