EMR master node에 Static private IP 할당하기

2020-10-31

.

Data_Engineering_TIL(20201031)

[실습 시 참고자료]

1) 블로그글 “AWS EMR 을 Terraform 으로 관리 할 때 몇가지 팁들” 에서 ‘Tip: AWS EMR 에 고정 Private IP 할당 하기’

** URL : https://www.popit.kr/tips-for-terrafoming-aws-emr/

2) 베스핀글로벌 최정민님 실습자료 “static_private_ip.py”

[실습목표]

아래와 같은 아키텍처를 구현하고, EMR을 구동시킬때 master node에 대한 고정된 프라이빗 아이피 할당한다.

** master node에 고정적으로 private ip를 할당하는 방법

‘elastic network interface’ 라는 기능을 이용해 고정하고자 하는 private ip를 master node에 부여하는 쉘스크립트(bootstrap.sh)를 개발을 했다. 그리고 개발한 쉘스크립트(bootstrap.sh)와 python 파일(static_private_ip.py)을 s3에 업로드를 시키고, EMR 구동시 부트스트랩 액션으로 적용하면 s3로 부터 해당 쉘스크립트를 로딩해 실행하게 된다. 그러면 emr master의 기존 프라이빗 아이피에 추가로 세컨더리 아이피 개념으로 고정아이피가 추가되는 형태이다. 결과적으로 베스천에서 고정하고자 하는 아이피로 접근했을때 EMR master에 접속을 할 수 있게 된다.

image

[실습내용]

step 1) 위에 아키텍처와 같은 형태로 VPC와 subnet을 구성하고, internetgateway와 natgateway도 생성한다. 그런 다음에 s3 endpoint를 생성하여 private subnet과 연결해준다.

라우트 테이블 셋팅도 아래와 같이 해준다.

routetable

step 2) 아래와 같은 스펙으로 bastion을 생성한다.

aws ec2 run-instances --image-id ami-03b42693dc6a7dc35 --count 1 --instance-type t3.micro --key-name myemrkey --security-group-ids sg-xxxxxxx --subnet-id subnet-xxxxxxxxxx --associate-public-ip-address --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=pms-bastion-test},{Key=owner,Value=pms},{Key=expiry-date,Value=2020-10-31}]' --block-device-mappings 'DeviceName=/dev/xvda,Ebs={VolumeSize=10,DeleteOnTermination=true}'

그런다음에 아래와 같이 bastion 서버에 emr에서 쓸 키를 업로드 시킨다.

$ scp -i myemrkey.pem myemrkey.pem ec2-user@3.35.174.28:~/
The authenticity of host '3.35.174.28 (3.35.174.28)' can't be established.
ECDSA key fingerprint is SHA256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '3.35.174.28' (ECDSA) to the list of known hosts.
myemrkey.pem                                                                        100% 1696    37.4KB/s   00:00

$ ssh -i myemrkey.pem ec2-user@3.35.174.28

       __|  __|_  )
       _|  (     /   Amazon Linux 2 AMI
      ___|\___|___|

https://aws.amazon.com/amazon-linux-2/
25 package(s) needed for security, out of 39 available
Run "sudo yum update" to apply all updates.
[ec2-user@ip-10-0-1-11 ~]$ ls
myemrkey.pem

[ec2-user@ip-10-0-1-11 ~]$ chmod 400 myemrkey.pem

step 3) s3에 아래와 같은 내용의 두개의 파일을 업로드한다.

** s3 업로드 location : s3://[your_bucket_name]/staticip/

  • bootstrap.sh
#!/bin/bash
sudo pip3 install ec2_metadata
aws s3 cp s3://[my_bucket_name]/staticip/static_private_ip.py ./
python2 static_private_ip.py $1
  • static_private_ip.py
#!/usr/bin/python

import sys, subprocess
import json
import urllib2

is_master = subprocess.check_output(['cat /emr/instance-controller/lib/info/instance.json | jq .isMaster'], shell=True).strip()
if is_master == "true":
    private_ip = str(sys.argv[1])
    region_name = str(json.loads(urllib2.urlopen('http://169.254.169.254/latest/dynamic/instance-identity/document').read())["region"])
    instance_id = subprocess.check_output(['/usr/bin/curl -s http://169.254.169.254/latest/meta-data/instance-id'], shell=True)
    interface_id = subprocess.check_output(['aws ec2 --region %s describe-instances --instance-ids %s | jq .Reservations[].Instances[].NetworkInterfaces[].NetworkInterfaceId' %(region_name, instance_id)], shell=True).strip().strip('"')
    #Assign private IP to the master instance:
    subprocess.check_call(['aws ec2 --region %s assign-private-ip-addresses --network-interface-id %s --private-ip-addresses %s' %(region_name, interface_id, private_ip)], shell=True)
    subnet_id = subprocess.check_output(['aws ec2 --region %s describe-instances --instance-ids %s | jq .Reservations[].Instances[].NetworkInterfaces[].SubnetId' %(region_name, instance_id)], shell=True).strip().strip('"').strip().strip('"')
    subnet_cidr = subprocess.check_output(['aws ec2 --region %s describe-subnets --subnet-ids %s | jq .Subnets[].CidrBlock' %(region_name, subnet_id)], shell=True).strip().strip('"')
    cidr_prefix = subnet_cidr.split("/")[1]
    #Add the private IP address to the default network interface:
    subprocess.check_call(['sudo ip addr add dev eth0 %s/%s' %(private_ip, cidr_prefix)], shell=True)
    #Configure iptablles rules such that traffic is redirected from the secondary to the primary IP address:
    primary_ip = subprocess.check_output(["/sbin/ifconfig eth0 | grep 'inet' | cut -d: -f2 | awk '{ print $2}'"], shell=True).strip()
    subprocess.check_call(['sudo iptables -t nat -A PREROUTING -d %s -j DNAT --to-destination %s' %(private_ip, primary_ip)], shell=True)
else:
    print "Not the master node"

step 4) 다음과 같은 스펙으로 EMR을 생성한다.

프라이빗 서브넷에 EMR을 생성하고, 부트스트랩 액션으로 s3에 업로드한 bootstrap.sh 을 참조해서 10.0.5.185 를 인자로 받아 마스터노드의 프라이빗 아이피를 10.0.5.185로 고정해서 띄울 것이다.

emr master에 기존에 프라이빗 아이피에 세컨더리 아이피 10.0.5.185가 추가되는 형태다. 그러면 베스천에서 10.0.5.185로 접근했을때 EMR master에 접속하게 된다.

aws emr create-cluster --applications Name=Spark Name=Hadoop Name=Hive --tags 'owner=pms' 'name=pms-EMR-test' 'expiry-date=2020-10-31' --ec2-attributes '{"KeyName":"myemrkey","InstanceProfile":"EMR_EC2_DefaultRole","ServiceAccessSecurityGroup":"sg-xxxxxxxxxxx","SubnetId":"subnet-xxxxxxxxxxxxxxxxxxx","EmrManagedSlaveSecurityGroup":"sg-xxxxxxxxxxxx","EmrManagedMasterSecurityGroup":"sg-0a818b7babcd4f905"}' --release-label emr-6.1.0 --log-uri 's3n://[your_bucket_name]/' --instance-groups '[{"InstanceCount":1,"EbsConfiguration":{"EbsBlockDeviceConfigs":[{"VolumeSpecification":{"SizeInGB":32,"VolumeType":"gp2"},"VolumesPerInstance":4}]},"InstanceGroupType":"MASTER","InstanceType":"m5.xlarge","Name":"Master"},{"InstanceCount":1,"EbsConfiguration":{"EbsBlockDeviceConfigs":[{"VolumeSpecification":{"SizeInGB":32,"VolumeType":"gp2"},"VolumesPerInstance":4}]},"InstanceGroupType":"CORE","InstanceType":"m5.xlarge","Name":"Core"}]' --bootstrap-actions '[{"Path":"s3://[your_bucket_name]/staticip/bootstrap.sh","Args":["10.0.5.185"],"Name":"static_private_ip"}]' --ebs-root-volume-size 10 --service-role EMR_DefaultRole --enable-debugging --name 'pms-EMRtest-test' --scale-down-behavior TERMINATE_AT_TASK_COMPLETION --region ap-northeast-2

** 참고로 콘솔에서 작업한다면 아래 그림과 같이 부스트트랩 액션을 설정해주면 된다.

  • location : bootstrap.sh 및 static_private_ip.py 저장경로

  • optional arguments : 고정하고자 하는 private ip

image

step 5) 베스천에서 10.0.5.185로 접근 시 emr 마스터노드로 접근이 되는지 확인

베스천 서버에서 10.0.5.185 로 접근하면 EMR matser로 접속이 되는 것을 확인할 수 있다.

[ec2-user@ip-10-0-1-11 ~]$ ssh -i myemrkey.pem hadoop@10.0.5.185
Last login: Sat Oct 31 09:59:34 2020

       __|  __|_  )
       _|  (     /   Amazon Linux 2 AMI
      ___|\___|___|

https://aws.amazon.com/amazon-linux-2/
59 package(s) needed for security, out of 88 available
Run "sudo yum update" to apply all updates.

EEEEEEEEEEEEEEEEEEEE MMMMMMMM           MMMMMMMM RRRRRRRRRRRRRRR
E::::::::::::::::::E M:::::::M         M:::::::M R::::::::::::::R
EE:::::EEEEEEEEE:::E M::::::::M       M::::::::M R:::::RRRRRR:::::R
  E::::E       EEEEE M:::::::::M     M:::::::::M RR::::R      R::::R
  E::::E             M::::::M:::M   M:::M::::::M   R:::R      R::::R
  E:::::EEEEEEEEEE   M:::::M M:::M M:::M M:::::M   R:::RRRRRR:::::R
  E::::::::::::::E   M:::::M  M:::M:::M  M:::::M   R:::::::::::RR
  E:::::EEEEEEEEEE   M:::::M   M:::::M   M:::::M   R:::RRRRRR::::R
  E::::E             M:::::M    M:::M    M:::::M   R:::R      R::::R
  E::::E       EEEEE M:::::M     MMM     M:::::M   R:::R      R::::R
EE:::::EEEEEEEE::::E M:::::M             M:::::M   R:::R      R::::R
E::::::::::::::::::E M:::::M             M:::::M RR::::R      R::::R
EEEEEEEEEEEEEEEEEEEE MMMMMMM             MMMMMMM RRRRRRR      RRRRRR

[hadoop@ip-10-0-5-239 ~]$