개요
금융권 회사에서 Sagemaker Studio를 Prod 수준으로 구축 한 뒤 내용을 정리해둔다.
필자는 사실 개발자이고 인프라쪽 부서해 속해본적도 devops 부서에 속해본적도 없다.
그러나 서버 아키텍처 관련해서 업무는 꽤 진행해 본 부분이 있고 웹, 빅데이터, AI쪽일을 모두 해봐서 이 경험을 토대로 설계해보았다.
(무엇보다 주안점을 둔 것은 금융권에 부합하는 보안을 확보하기 위해 최대한 고려하였다.)
해당 글에서는 Sagemaker Studio를 구축한 뒤 실제로 사용하기 위한 기본 기능들을 검증하여 소개한다.
플랫폼 구축
설계 및 구현한 아키텍처는 아래와 같다.
플랫폼 구축에 고려한 점
구축에 고려한점은 아래와 같다.
VPC 및 인터넷 통신
- sagemaker studio는 격리된 vpc 환경 하에서 실행되며 필요시 Nat Gateway로 외부망 통신을 한다.
- routing table 관리로 인터넷 여부는 수동 컨트롤 하며 더 고도화 되면 squid proxy의 도입도 가능할듯 하다.
- VPC 내의 모든 네트워크 통신은 VPC Flowlog로 로깅함
Endpoint 사용
- AWS Region 내 vpc 외부 통신은 모두 Endpoint를 통해 효율적으로 통신한다.
- public망으로 다시 나갔다가 들어오는 것은 비용이든 보안이든 비효율적이기 때문
- s3는 endpoint gateway로 만들어 scalability를 확보했다.
S3
- sagemaker studio에서 로컬 작업은 EFS, 영구 저장은 S3 Bucket으로 설계하였다.
KMS를 통한 암호화
- EFS, S3 Bucket은 SSE-CMK를 통해 데이터를 암호화 하였다.
- S3는 부가적으로 Bucket Key를 활성화 하여 KMS 트래픽 및 비용을 최소화 하였다.
- S3는 KMS 암호화 없는 upload가 되지 않도록 policy를 추가하였다.
Enterprise 보안
- Sagemaker IAM을 정의하여 최소한의 권한만 부여
- endpoint에도 보안을 걸어 필요한 최소한의 버킷만 접근가능하게 함
- security group으로 주피터 통신 및 필요한 최소한의 포트만 허용
- s3상에서도 보안을 따로 적용
접속방법
아래와 같은 식으로 접속 할 수 있다.
접속주소 : https://{account_id}.signin.aws.amazon.com/console/ 와 같다.
접속하면 관리 콘솔 화면을 볼 수 있다.
Services
클릭 후 Amazon SageMaker
를 클릭한다.
왼쪽 메뉴에서 Studio
를 클릭하면 SageMaker 도메인 메뉴가 나온다.
이미 생성된 계정 중에 본인 계정의 앱시작
버튼을 누른다.
이중에서 앱 시작
-> Studio
클릭 시 Sagemaker Studio를 시작한다.
스튜디오가 실행되면 노트북 실행이 정상동작하는지 확인해보자.
File
-> New
-> Notebook
을 클릭한다.
노트북 실행 시 사용할 도커이미지 및 커널을 선택하라고 나온다.
회사에서 쓰려면 아래 그림처럼 커스텀한 이미지를 만들어놓고 선택하면 된다.
스튜디오 간의 보안정책
Sagemaker Studio IDE는 본인만 사용할 수 있게 보안설정을 하였다. 이유는 간단한데 상식적으로 각자 사용하는 개발툴을 서로 공유해서 쓰지는 않기 때문이다.
이를 위해 보안 쪽에 트릭을 좀 걸었다. 더 나아가 팀별로 공유하여 사용하는것도 보안정책 튜닝을 통해 가능하다.
만약 협업 필요 시 노트북 공유(읽기전용)를 통해 가능하다. 더불어 코드 저장소는 공동사용 영역이기 때문에 운영에 크게 불편함은 없을듯 하다.
아래와 같이 다른사람의 노트북에 접속 시도 시 접근이 제한된다.
형상관리 연동방법: 기초 설정
git을 제대로 사용하기 위해 주피터나 터미널 창에서 git 기본정보 설정을 수행한다.
더불어 ssh 기반 키 인증이 필요하면 file -> new -> terminal
통해서 직접 ssh key를 심으면 된다.
aws code commit을 통한 git clone을 맛만 보자.
원하는 repo에 HTTPS
아이콘을 클릭하면 git repo 주소를 복사 할 수 있다.
스튜디오 좌측에서 Git모양 아이콘을 클릭 후 Clone a Repository
를 선택한다.
위에서 획득한 HTTPS 주소를 입력하고 CLONE
한다.
클론이 완료되면 /
경로 아래에 클론된 디렉토리가 표시된다.
아래의 신규파일을 Git Commit 후 Push 해보기로 하자.
아래 네모 영역에 마우스를 갖다대서 +
를 하면 stage영역으로 파일이 이동한다.
Stage 영역으로 옮겨진 2개의 파일을 확인 할 수 있다.
커밋메세지를 적고 커밋 버튼을 눌러보자.
정상적으로 커밋한 뒤 네모 아이콘을 눌러 원본 repo로 push한다.
정상적으로 git push가 완료됨을 알려준다.
노트북 공유
필요시 읽기전용 으로 내 노트북을 동료에게 공유할 수 있다.
실제 노트북 공유파일은 sagemaker studio 구축 시 지정한 s3 bucket에 저장된다. 이 부분의 권한이나 KMS 설정이 제대로 되지 않으면 노트북 공유 기능이 정상동작 안할수 있으니 참고하자.
공유를 원하는 노트북에 들어가 Share
를 클릭한다.
필요에 따라 output
포함여부를 선택하고 Create
를 누른다.
노트북 공유용 URL이 생성되었다. 이 링크를 공유하면 된다.
타 계정에서 노트북 공유 링크에 접속해보면 읽기 전용이라는 경고와 함께 노트북을 볼 수 있다.
필요시 노트북을 복사해서 내 스튜디오에서 돌려 볼 수 있다.
S3 사용방법
kms연동한 s3로 CRUD하는 코드를 직접 작성하였다. 혹시 필요한 사람은 참고하자.
import os
import boto3
from botocore.client import Config
from Logger import Logger
class S3Manager(object):
def __init__(self, bucket_name='버킷이름'):
self.logger = Logger.get_logger()
self.cli = boto3.client('s3', config=Config(signature_version='s3v4'))
self.bucket_name = bucket_name
self.s3_kms_id = 'kms'
def list_buckets(self):
response = self.cli.list_buckets()
return response
def put_object(self, key_id, body):
response = self.cli.put_object(Bucket=self.bucket_name,
Key=key_id,
Body=body,
ServerSideEncryption='aws:kms',
SSEKMSKeyId=self.s3_kms_id)
return response
def get_object(self, key_id):
try:
response = self.cli.get_object(Bucket=self.bucket_name,
Key=key_id)
return response['Body'].read()
except self.cli.exceptions.NoSuchKey:
self.logger.info(f'{key_id} does not exist.')
return None
def delete_object(self, key_id):
response = self.cli.delete_object(Bucket=self.bucket_name,
Key=key_id)
return response
def upload_dir(self, local_dir_path, s3_dir_path):
self.logger.info('Uploading results to s3 initiated...')
self.logger.info(f'local_path:{local_dir_path}, s3_path:{s3_dir_path}')
try:
for path, subdirs, files in os.walk(local_dir_path):
for file in files:
dest_path = path.replace(local_dir_path, '')
s3file_path = os.path.normpath(s3_dir_path + '/' + dest_path + '/' + file)
local_file_path = os.path.join(path, file)
self.cli.upload_file(local_file_path,
self.bucket_name,
s3file_path,
ExtraArgs={'ServerSideEncryption': 'aws:kms',
'SSEKMSKeyId': self.s3_kms_id})
self.logger.info(f'upload : {local_file_path} to Target: {s3file_path} Success.')
except Exception as e:
self.logger.info(e)
raise e
def upload_file(self, local_file_path, s3file_path):
try:
self.cli.upload_file(local_file_path,
self.bucket_name,
s3file_path,
ExtraArgs={'ServerSideEncryption': 'aws:kms',
'SSEKMSKeyId': self.s3_kms_id})
self.logger.info(f'upload : {local_file_path} to Target: {s3file_path} Success.')
except Exception as e:
self.logger.info(e)
raise e
def download_dir(self, s3_dir_path, local_dir_path=os.getcwd()):
self.logger.info('Downloading results to s3 initiated...')
self.logger.info(f's3_path:{s3_dir_path}, local_path:{local_dir_path}')
bucket = boto3.resource('s3').Bucket(self.bucket_name)
for obj in bucket.objects.filter(Prefix=s3_dir_path):
local_obj_path = os.path.join(local_dir_path, obj.key)
if not os.path.exists(os.path.dirname(local_obj_path)):
os.makedirs(os.path.dirname(local_obj_path))
bucket.download_file(obj.key, local_obj_path)
self.logger.info(f'download : {obj.key} to Target: {local_obj_path} Success.')
def download_file(self, s3_file_path, local_file_path):
bucket = boto3.resource('s3').Bucket(self.bucket_name)
bucket.download_file(s3_file_path, local_file_path)
self.logger.info(f'download : {s3_file_path} to Target: {local_file_path} Success.')
def list_objects(self, prefix='', delimiter=''):
response = self.cli.list_objects_v2(Bucket=self.bucket_name, Prefix=prefix, Delimiter=delimiter)
return response['Contents'] if 'Contents' in response else None
def list_dir(self, prefix='', delimiter='/'):
response = self.cli.list_objects_v2(Bucket=self.bucket_name, Prefix=prefix, Delimiter=delimiter)
list = []
if 'Contents' in response:
list += [item['Key'] for item in response['Contents'] if 'Key' in item]
if 'CommonPrefixes' in response:
list += [prefix['Prefix'] for prefix in response['CommonPrefixes']]
return list
실행 인스턴스 성능 변경
스튜디오 오른쪽 상단에 cpu 표시된 부분을 클릭하면 실행자원을 변경 할 수 있다.
필요시 GPU 모델링도 가능하다.
과금과 연결되므로 오남용은 주의하여야 한다.