boto3 를 이용하여, AWS 로컬스택 아키텍처 구성해보기

반응형

근 두달가까이 소식이 없다가, 그동안 쌓인 내용들을 부랴부랴 올리고 있습니다.

 

사실 파이썬3에 대한 이용능력을 좀 높일 필요가 있어서, 해커랭크에서 좀 살다 왔습니다.

신기한게 자격증도 주어지더라구요... (어느정도 문제 풀어보니)

 

한번 들어가셔서 확인해보시기 바랍니다 :) 

www.hackerrank.com

 

HackerRank

HackerRank is the market-leading technical assessment and remote interview solution for hiring developers. Learn how to hire technical talent from anywhere!

www.hackerrank.com

 

자 무튼, 한동안 소식이 없기도 했고, 그 소식 없는 기간동안 잠깐 과제를 진행하기도 했습니다.

로컬스택을 이용해, 로컬 환경에서의 AWS 아키텍처링을 테스팅해볼 수 있는 도구입니다.

 

로컬스택에 대한 설명은 조금 후일담으로 미루어둘게요. (클라우드 개발자이기에 코드를 집중적으로 판다 실시)

(아참, 환경마다 다를 순 있겠지만, 파이썬3를 이용해 로컬스택 설치하지 마시구, 도커 & 도커컴포즈파일 이용해서 설치하세요. 파이썬3 이용해서 설치할 때, 디펜던시 계속 깨지는 오류가 발생합니다. 트러블 슈팅 귀찮으니 도커/컴포즈 로 가는 방법 추천드립니다.)

 

* 로컬스택 main

https://hub.docker.com/r/localstack/localstack/

 

Docker Hub

 

hub.docker.com

* 로컬스택 설치 가이드

https://medium.com/better-programming/dont-be-intimidated-learn-how-to-run-aws-on-your-local-machine-with-localstack-2f3448462254

 

Don’t Be Intimidated — Learn How to Run AWS on Your Local Machine With LocalStack

A frugal programmer’s guide to leveraging AWS APIs without breaking your bank account

medium.com

 

과제로 주어진, 구성해야할 아키텍처는 다음과 같습니다.

로컬스택 환경이 모두 구성되었고, 로컬스택 컨테이너가 돌아가고 있다고 가정하겠습니다.

 

이제 저 모든 구성요소를 의존성 순서대로 주입하여, AWS boto3 & python3 스크립트를 돌리게 할 겁니다.

 

한편으로는 테라폼 IAC로도 되긴 하지만, 충분히 SDK로도 됩니다..!!!!! 라는 것을 보여주고 싶었어요 ㄷㄷㄷㄷ.... (그래봤자 100% 치환은 되진 않겠지만..)

 

import json
import boto3
import subprocess

"""
로컬스택 AWS 클라우드 인프라 메이커 (Python3 Script with boto3)

@desc 
	POC 수행을 위한, AWS 서비스 구성요소
	본 파이썬 스크립트 실행 시, 하기 구성요소 생성
	
	IAM : 루트유저 급 마스터 Role 
	IGW : 신규 인터넷 게이트웨이
	VPC : 신규 VPC (10.0.0.0/16)
	Subnets Of VPC : 신규 VPC의 서브넷
	RouteTable (RT) : 생성된 서브넷을 위한 라우트테이블
	Route of RT : 라우트 테이블의 네트워크 경로
	Security Group : 모든 대역 / 모든 포트 Open (AnyOpen) (In/Out 포함)
	EC2 Key Pair : EC2 머신을 위한 키페어
	EC2 Instance Profile : EC2 머신이 사용할 IAM 프로필
	EC2 Instance : Nginx가 기동될 EC2 머신
	Kinesis : EC2 머신이 로그를 전송할 키네시스 데이터스트림
	Lambda : 키네시스 데이터스트림의 이벤트 트리거를 받아 처리할 람다
	Lambda Trigger : 키네시스 DS와 람다 이벤트 매핑
	S3 : 람다가 정제한 로그를 쌓을 버킷
@author 클라우드 개발자 앙몬드
@since 2020-09-17
"""

print("[System] 로컬스택 AWS 클라우드 인프라 메이커 시작")
print("=" * 30)

PROFILE_NAME = "localstack"			# aws API와 통신하기 위한 크리덴셜 프로필
REGION_NAME = "ap-northeast-2"			# 로컬스턱 리전 고정 (서울)
ENDPOINT_URL = "http://localhost:4566/"		# 로컬스택 Fake AWS API 엔드포인트

session = boto3.session.Session(profile_name=PROFILE_NAME, region_name=REGION_NAME)	# boto3 세션 팩토리
iam_client = session.client("iam", endpoint_url=ENDPOINT_URL)		# iam 클라이언트 
ec2_resource = session.resource("ec2", endpoint_url=ENDPOINT_URL)		# ec2 리소스
ec2_client = session.client("ec2", endpoint_url=ENDPOINT_URL)		# ec2 클라이언트
kinesis_client = session.client('kinesis', endpoint_url=ENDPOINT_URL)	# kinesis 클라이언트
lambda_client = session.client('lambda', endpoint_url=ENDPOINT_URL)	# lambda 클라이언트
s3_client = session.client("s3", endpoint_url=ENDPOINT_URL)			# s3 클라이언트

LOCALSTACK_LOG_S3_BUCKET_NAME = "localstack-fake-log-bucket"		# s3 버킷 이름
LOCALSTACK_KINESIS_DS_NAME = "localstack-fake-kinesis-ds-for-log"		# 키네시스 이름
LOCALSTACK_LAMBDA_NAME = "localstack-fake-lambda-for-log-processing"	# 람다함수 이름
print("[System] 기본 환경변수 설정 완료")

"""
루트유저 급 마스터 Role 생성
"""
# IAM Policy 생성
managed_policy = {
    "Version": "2012-10-17",
    "Statement": [{
        "Sid": "VisualEditor0",
        "Effect": "Allow",
        "Action": "*",
        "Resource": "*"}]}
response_create_policy = iam_client.create_policy(
    PolicyName='localstack-fake-master-policy',
    PolicyDocument=json.dumps(managed_policy))
arn_fake_master_policy = response_create_policy["Policy"]["Arn"]

# IAM Role 생성
trust_entity_policy = {
    "Version": "2012-10-17",
    "Statement": [{
        "Effect": "Allow",
        "Principal": {
            "Service": [
                "lambda.amazonaws.com",
                "s3.amazonaws.com",
                "kinesis.amazonaws.com",
                "ec2.amazonaws.com"
            ]
        },
        "Action": "sts:AssumeRole"}]}
response = iam_client.create_role(
    Path="/",
    RoleName="localstack-fake-master-role",
    AssumeRolePolicyDocument=json.dumps(trust_entity_policy),
    Description="localstack-fake-master-role",
    MaxSessionDuration=3600)
iam_role_arn = response["Role"]["Arn"]

# IAM Role에 Policy 탑재
# arn_fake_master_policy 은, create_policy 의 리턴값으로 획득 가능
iam_client.attach_role_policy(
    PolicyArn=arn_fake_master_policy,
    RoleName='localstack-fake-master-role')
print("[System] AWS 계정 루트유저급 IAM Role 생성 완료 - 루트유저급 Role 사용 가능 엔티티 : EC2 / Lambda / S3 / Kinesis")

"""
인터넷 게이트웨이 / VPC 생성 & IGW 탑재
"""
igw = ec2_resource.create_internet_gateway()
vpc = ec2_resource.create_vpc(CidrBlock='10.0.0.0/16')
ec2_client.modify_vpc_attribute(VpcId=vpc.id, EnableDnsSupport={'Value': True})
ec2_client.modify_vpc_attribute(VpcId=vpc.id, EnableDnsHostnames={'Value': True})
vpc.attach_internet_gateway(InternetGatewayId=igw.id)
print("[System] IGW/VPC 생성 & 신규 생성 VPC에 IGW 링크 완료")

"""
VPC의 서브넷 생성 (AZ 이중화를 위해 2개로 분할 생성), RT 생성 & 네트워크 라우터 탑재
	-> nginx 인스턴스는 이중화되지 않으므로, 서브넷은 이중화하였으며, 실제 라우터는 한 곳만 설정
"""
routetable = vpc.create_route_table()
route_public = routetable.create_route(DestinationCidrBlock='0.0.0.0/0', GatewayId=igw.id)
subnet_a = ec2_resource.create_subnet(CidrBlock='10.0.0.0/24', VpcId=vpc.id, AvailabilityZone="ap-northeast-2a")
subnet_c = ec2_resource.create_subnet(CidrBlock='10.0.1.0/24', VpcId=vpc.id, AvailabilityZone="ap-northeast-2c")
routetable.associate_with_subnet(SubnetId=subnet_a.id)
print("[System] Route Table 및 요청 네트워크 라우팅 설정 완료")
print("[System] VPC IP대역 중, 이중화를 위한 subnet C클래스 분할 & AZ-A 서브넷에 라우트 탑재 완료")

"""
인/아웃바운드 전 대역 전 포트 오픈 Security Group  생성
"""
security_group = ec2_resource.create_security_group(GroupName='sg-localstack', Description='sg-localstack', VpcId=vpc.id)
security_group.authorize_ingress(
    IpPermissions=[
        {
            'FromPort': 0,
            'IpProtocol': 'tcp',
            'IpRanges': [
                {
                    'CidrIp': '0.0.0.0/0',
                    'Description': 'all_any_open'
                },
            ],
            'ToPort': 65535
        },
        {
		'FromPort': 0,
		'ToPort': 65535,
		'IpProtocol': 'udp',
		'IpRanges': [
			{
				'CidrIp': '0.0.0.0/0',
				'Description': 'all_any_open'
			},
		], 
    }])
print("[System] 시큐리티그룹 네트워크 애니오픈 생성 완료")

"""
EC2에 접근하기 위한 Key Pair 생성
"""
LOCAL_STACK_FAKE_KEYPAIR_NAME = "fake-keypair"
fake_keypair = open('fake-keypair.pem', 'w')
key_pair_response = ec2_resource.create_key_pair(KeyName=LOCAL_STACK_FAKE_KEYPAIR_NAME)
KeyPairOut = str(key_pair_response.key_material)
fake_keypair.write(KeyPairOut)
fake_keypair.close()
print("[System] EC2 머신 접근을 위한 키페어 생성 완료 > ./fake-keypair.pem 확인")


"""
nginx 웹서버를 위한 EC2 인스턴스 생성 
"""
instance_profile = iam_client.create_instance_profile(InstanceProfileName='ec2-nginx-instance-profile')
arn_of_instance_profile = instance_profile["InstanceProfile"]["Arn"]
response = iam_client.add_role_to_instance_profile(
    InstanceProfileName='ec2-nginx-instance-profile',
    RoleName='localstack-fake-master-role'
)
print("[System] EC2 머신을 위한 IAM 인스턴스 프로필 생성 완료")

instances = ec2_resource.create_instances(
    ImageId='ami-06c2d58d267b830df',
    InstanceType='t2.micro',
    KeyName=LOCAL_STACK_FAKE_KEYPAIR_NAME,
    MinCount=1,
    MaxCount=1,
    Placement={'AvailabilityZone': 'ap-northeast-2a' },
    NetworkInterfaces=[{
        'SubnetId': subnet_a.id,
        'DeviceIndex': 0,
        'AssociatePublicIpAddress': True,
        'Groups': [security_group.group_id]  }],
    IamInstanceProfile={
        'Arn': arn_of_instance_profile,
        'Name': 'ec2-nginx-instance-profile' }
)
print("[System] EC2 머신 생성 완료")

"""
키네시스 생성 시, 요청에 대한 응답은 200 정상으로 리턴되지만, 실제 생성되지 않는 이슈가 발생
"""
#subprocess.run('aws kinesis create-stream --stream-name "localstack-fake-kinesis-ds-for-log" --shard-count 1 --profile localstack --endpoint http://localhost:4566', shell=True, check=True)
response = kinesis_client.create_stream(StreamName=LOCALSTACK_KINESIS_DS_NAME, ShardCount=1)
#response2 = kinesis_client.list_streams( Limit=100 )
print(f"[DEBUG] Kinesis Creation Response : {response}")
print(f"[System] 키네시스 DS 생성 완료 (응답 Success / 실제 구현은 안되는 버그 존재)")
print("[Warn] 로컬스택 키네시스 ARN : arn:aws:kinesis:ap-northeast-2:000000000000:stream/localstack-kinesis-fake-ds")



"""
람다 생성
"""
response_lambda = lambda_client.create_function(
	FunctionName=LOCALSTACK_LAMBDA_NAME,
	Runtime='python3.8',
	Role=iam_role_arn,
	Handler = 'lambda_function.lambda_handler',
	Code={'ZipFile': open('./localstack-logs-process-fake-lambda.zip', 'rb').read()},
	Description='localstack-lambda',
	Timeout=900,
	MemorySize=1024,
	Publish=True,
	VpcConfig={
		   'SubnetIds': [subnet_a.id, subnet_c.id ],
		   'SecurityGroupIds': [security_group.group_id ] } )
print("[System] 람다 생성 완료")

response_lambda_trigger = lambda_client.create_event_source_mapping(
    EventSourceArn='arn:aws:kinesis:ap-northeast-2:000000000000:stream/localstack-kinesis-fake-ds',
    FunctionName=LOCALSTACK_LAMBDA_NAME,
    Enabled=True,
    BatchSize=100,
    MaximumBatchingWindowInSeconds=60,
    ParallelizationFactor=1,
    MaximumRecordAgeInSeconds= -1,
    BisectBatchOnFunctionError=False,
    MaximumRetryAttempts=0,
)
print("[System] 람다 이벤트 트리거 매핑 완료 <- Kinesis Fake Stream of Bugs")

"""
s3 버킷 생성 
"""
response_s3_create = s3_client.create_bucket(
    ACL='private',
    Bucket=LOCALSTACK_LOG_S3_BUCKET_NAME,
    CreateBucketConfiguration={'LocationConstraint': 'ap-northeast-2' },
)
print(f"[System] S3 버킷 생성 완료")
print("=" * 30)
print("[System] 로컬스택 AWS 클라우드 인프라 메이커 종료")

반응형

설정

트랙백

댓글