728x90
- 어제 코드에서 맨 아래 암호키 추가를 하게 되면 로컬사용이 가능한것같다(나는 성공하지 못함..)
import io import os import uuid import boto3 from boto3.s3.transfer import S3UploadFailedError from botocore.exceptions import ClientError def do_scenario(s3_resource): print("-" * 88) print("Welcome to the Amazon S3 getting started demo!") print("-" * 88) bucket_name = f"doc-example-bucket-{uuid.uuid4()}" bucket = s3_resource.Bucket(bucket_name) try: bucket.create( CreateBucketConfiguration={ "LocationConstraint": s3_resource.meta.client.meta.region_name } ) print(f"Created demo bucket named {bucket.name}.") except ClientError as err: print(f"Tried and failed to create demo bucket {bucket_name}.") print(f"\t{err.response['Error']['Code']}:{err.response['Error']['Message']}") print(f"\nCan't continue the demo without a bucket!") return file_name = None while file_name is None: file_name = input("\nEnter a file you want to upload to your bucket: ") if not os.path.exists(file_name): print(f"Couldn't find file {file_name}. Are you sure it exists?") file_name = None obj = bucket.Object(os.path.basename(file_name)) try: obj.upload_file(file_name) print( f"Uploaded file {file_name} into bucket {bucket.name} with key {obj.key}." ) except S3UploadFailedError as err: print(f"Couldn't upload file {file_name} to {bucket.name}.") print(f"\t{err}") answer = input(f"\nDo you want to download {obj.key} into memory (y/n)? ") if answer.lower() == "y": data = io.BytesIO() try: obj.download_fileobj(data) data.seek(0) print(f"Got your object. Here are the first 20 bytes:\n") print(f"\t{data.read(20)}") except ClientError as err: print(f"Couldn't download {obj.key}.") print( f"\t{err.response['Error']['Code']}:{err.response['Error']['Message']}" ) answer = input( f"\nDo you want to copy {obj.key} to a subfolder in your bucket (y/n)? " ) if answer.lower() == "y": dest_obj = bucket.Object(f"demo-folder/{obj.key}") try: dest_obj.copy({"Bucket": bucket.name, "Key": obj.key}) print(f"Copied {obj.key} to {dest_obj.key}.") except ClientError as err: print(f"Couldn't copy {obj.key} to {dest_obj.key}.") print( f"\t{err.response['Error']['Code']}:{err.response['Error']['Message']}" ) print("\nYour bucket contains the following objects:") try: for o in bucket.objects.all(): print(f"\t{o.key}") except ClientError as err: print(f"Couldn't list the objects in bucket {bucket.name}.") print(f"\t{err.response['Error']['Code']}:{err.response['Error']['Message']}") answer = input( "\nDo you want to delete all of the objects as well as the bucket (y/n)? " ) if answer.lower() == "y": try: bucket.objects.delete() bucket.delete() print(f"Emptied and deleted bucket {bucket.name}.\n") except ClientError as err: print(f"Couldn't empty and delete bucket {bucket.name}.") print( f"\t{err.response['Error']['Code']}:{err.response['Error']['Message']}" ) print("Thanks for watching!") print("-" * 88) # if __name__ == "__main__": # do_scenario(boto3.resource("s3")) if __name__ == "__main__": session = boto3.Session( aws_access_key_id = "**********", aws_secret_access_key = "*****************************************", region_name = "ap-northeast-2" ) do_scenario(session.resource('s3')) |
- 암호키랑 리전네임(서울)을 넣어주니까 동작되는 것을 확인
- https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/programming-with-python.html
- 클라이언트 인터페이스를 사용하여 항목 삽입하기
- dynamodb에서 테이블을 생성해서 진행(test 라는 테이블) import boto3 dynamodb = boto3.client('dynamodb') dynamodb.put_item( TableName='YourTableName', # 우리의 경우 test Item={ 'pk': {'S': 'id#1'}, 'sk': {'S': 'cart#123'}, 'name': {'S': 'SomeName'}, 'inventory': {'N': '500'}, # ... more attributes ... } ) |
- 리소스 인터페이스를 사용하여 항목 삽입하기
- 이번에는 ubuntu에서 nano를 사용해서 에디터를 대신함 - 이번에는 클라이언트가 아닌 테이블 리소스에서 가져와 그 안에 집어 넣는 것으로 진행함 import boto3 dynamodb = boto3.resource('dynamodb') table = dynamodb.Table('YourTableName') # 우리의 경우 test table.put_item( Item={ 'pk': 'id#1', 'sk': 'cart#123', 'name': 'SomeName', 'inventory': 500, # ... more attributes ... } ) |
- nano의 경우 ctrl 5를 눌러야 저장이 됨
- 위와같이 저장된것을 확인
- 함께 제공된 및 클래스를 사용하여 일반 JSON과 DynamoDB JSON 간에 변환하기
def dynamo_to_python(dynamo_object: dict) -> dict: deserializer = TypeDeserializer() return { k: deserializer.deserialize(v) for k, v in dynamo_object.items() } def python_to_dynamo(python_object: dict) -> dict: serializer = TypeSerializer() return { k: serializer.serialize(v) for k, v in python_object.items() } |
- 클라이언트 인터페이스를 사용하여 쿼리 수행하기
import boto3 client = boto3.client('dynamodb') # Construct the query response = client.query( TableName='YourTableName', KeyConditionExpression='pk = :pk_val AND begins_with(sk, :sk_val)', FilterExpression='#name = :name_val', ExpressionAttributeValues={ ':pk_val': {'S': 'id#1'}, ':sk_val': {'S': 'cart#'}, ':name_val': {'S': 'SomeName'}, }, ExpressionAttributeNames={ '#name': 'name', } ) |
- 위 내용도 동일하게 nano로 진행
- 리소스 인터페이스를 사용하여 쿼리 수행하기
import boto3 from boto3.dynamodb.conditions import Key, Attr dynamodb = boto3.resource('dynamodb') table = dynamodb.Table('YourTableName') response = table.query( KeyConditionExpression=Key('pk').eq('id#1') & Key('sk').begins_with('cart#'), FilterExpression=Attr('name').eq('SomeName') ) |
- 위 다른것들이랑 형태가 조금 다른것을 확인함
- 클라이언트 인터페이스를 사용하여 테이블의 대략적인 크기 얻기
import boto3 dynamodb = boto3.client('dynamodb') response = dynamodb.describe_table(TableName='YourTableName') size = response['Table']['TableSizeBytes'] print(f"Table size: {size}") |
- 리소스 인터페이스를 사용하여 테이블의 대략적인 크기 얻기
import boto3 dynamodb = boto3.resource('dynamodb') table = dynamodb.Table('YourTableName') size = table.table_size_bytes print(f"Table size: {size}") |
from boto3 import Session from botocore.exceptions import BotoCoreError, ClientError from contextlib import closing import os import sys import subprocess from tempfile import gettempdir # AWS 자격 증명 파일 (~/.aws/credentials)의 [관리자] 섹션에 정의 된 # 자격 증명 및 영역을 사용하여 클라이언트를 만듦(~/.aws/credentials). session = Session(profile_name="adminuser") polly = session.client("polly") try: # 음성 합성 요청 response = polly.synthesize_speech(Text="Hello world!", OutputFormat="mp3", VoiceId="Joanna") except (BotoCoreError, ClientError) as error: # 서비스는 오류를 반환하고 종료 print(error) sys.exit(-1) # 응답으로부터 오디오 스트림에 액세스 if "AudioStream" in response: # 참고 : 서비스가 병렬 연결 수에 조절되기 때문에 스트림을 닫는 것이 중요함. # 여기서는 contextLib.closing을 사용하여 스트림 객체의 닫기 메소드가 명령문의 # 범위 끝에 자동으로 호출되도록 함. with closing(response["AudioStream"]) as stream: output = os.path.join(gettempdir(), "speech.mp3") try: # 바이너리 스트림으로 출력을 쓰기위한 파일 열기 with open(output, "wb") as file: file.write(stream.read()) except IOError as error: # 파일에 쓸 수 없었음. 종료 print(error) sys.exit(-1) else: # 응답에 오디오 데이터가 포함되어 있지 않았음. 종료 print("Could not stream audio") sys.exit(-1) # 플랫폼의 기본 플레이어를 사용하여 오디오 재생 # 윈도우의 경우 if sys.platform == "win32": os.startfile(output) else: # MacOS 및 Linux의 경우(Darwin = Mac, XDG-OPEN = Linux) opener = "open" if sys.platform == "darwin" else "xdg-open" subprocess.call([opener, output]) |
- 리눅스 쪽에서는 재생은 안되지만 파일은 생성됨
- 권한을 만들어서 진행하는것이 좋아보임
- 물리적으로 존재하는 컴퓨터가 아닌, 다른 컴퓨터가 만들어내는 가상의 컴퓨터
- 호스트 OS에 하이퍼바이저를 설치하고 그 위에 게스트 OS를 작동시키는 형태로 동작
- 실제 머신의 하드웨어의 기능을 에뮬레이팅함으로써 동작을 지원
- 에뮬레이션으로만 가상머신을 만들면 매우 느려지기 때문에 가상 시스템에서 실행되는 명령어 중 원래 시스템에서 그대로 실행해도 무방한 명령어는 땡겨와서 사용(그대로 CPU에서 실행), 그렇지 않은 명령어만 에뮬레이션함
- 에뮬레이션 : 모든 것을 소프트웨어적으로 구현
- 가상화 : 주요 부품의 구현에서 하드웨어적 지원을 받음
- 에뮬레이션으로만 가상머신을 만들면 매우 느려지기 때문에 가상 시스템에서 실행되는 명령어 중 원래 시스템에서 그대로 실행해도 무방한 명령어는 땡겨와서 사용(그대로 CPU에서 실행), 그렇지 않은 명령어만 에뮬레이션함
- 컨테이너는 코드와 모든 관련이 있는 라이브러리의 종속성을 포함해서 만든 표준 단위
- 프로그램을 동작할 수 있도록 만든 환경에서 빠르고 신뢰성 있게 다른 환경으로 쉽게 이식할 수 있게 만든 것
- 호스트 OS의 커널을 공유하면서 분리된 프로레스로서 실행해 마치 가상머신이 움직이는 것처럼 보이게 하는 기술이다.
- 호스트 OS에 컨테이너 런타임을 올리고 그 위에 프로세스로서 컨테이너를 동작시킨다.
- 컨테이너의 실체는 단순한 프로세스이므로 가상 머신에 비해 매우 가볍고 빠르게 동작할 수 있다.
- 가상머신 vs 컨테이너 방식
- 개요
- 컨테이너라는 경량 단위로 애플리케이션을 실행하는 기능을 제공하는 플랫폼
- 컨테이너를 동작시키기 위한 엔진의 하나
- 도커가 빠르게 확산한 이유
- 컨테이너 기술 자체는 새로운 기술이 아니었지만 도커는 아래 이유로 단기간에 개발자들의 지지를 얻음
- 컨테이너 관리 방식
- 도커에서는 Dockerfile이라는 정의 파일을 작성하여 동일한 컨테이너 이미지를 간단히 만들 수 있음
- 이는 IaC(Infrastructure as Code)를 구현하는데 매우 적합한 소프트웨어의 형태임
- 또한 컨테이너의 이미지에는 애플리케이션과 그 실행 환경 설정이 포함되어 있기 때문에 도커 엔진만 설치되어 있다면 언제 어디서든 그 애플리케이션의 동작이 보장되는 장점도 있음(이전까지는 환경에 영향으로 프로젝트 실행이 안되는 경우가 많이 있었음)
- 에코 시스템
- 컨테이너 이미지를 저장, 공유하기 위한 에코 시스템이 초기부터 준비되어 있었음
- 생성한 컨테이너 이미지는 각 환경에 확실히 배포할 수 있어야 의미가 있으며, 도커에서는 도커 허브(Docker Hub)라는 컨테이너 이미지를 저장, 공유할 수 있는 컨테이너 저장소(Repository)가 제공됨
- docker push/pull 명령으로 간단히 도커 허브에 컨테이너 이미지를 전송하거나 다운로드 할 수 있음
- 이를 통해 애플리케이션을 배포할 때 환경 설정 차이로 인해 발생하기 쉬운 문제를 해결할 수 있음
- 즉, 개발 환경에서 스테이징 환경, 서비스 환경으로 동일한 컨테이너 이미지를 배포할 수 있으므로 테스트를 거친 컨테이너 이미지를 서비스 환경에 안정적으로 배포할 수 있음
- 컨테이너 기술 자체는 새로운 기술이 아니었지만 도커는 아래 이유로 단기간에 개발자들의 지지를 얻음
- 동일한 컨테이너 이미지를 배포함으로써 애플리케이션 실행이 보장됨
- 도커를 통한 조직의 문제 해결
- 도커를 이용하여 컨테이너화를 수행하면 조직의 문제도 해결이 가능하다.
- 큰 조직, 회사 의 경우 일반적으로 인프라팀과 애플리케이션 팀으로 구분된다.
- 어떤 시스템을 구축할 경우
- 인프라팀: 서버 준비, 미들웨어나 프레임워크 등의 실치 및 설정
- 애플리케이션팀 : 인프라팀이 준비한 환경에서 개발 수행
- 기업의 규모가 클 수록 단계별 분업을 진행
- 이렇게 분업을 하게 되더라도 업무에 문제가 발생할 수는 있음
- 예시
- 인프라 초기 구축 이후 미들웨어 설정 변경 등이 필요하면 애플리케이션 팀은 항상 인프라팀에게 작업을 의뢰해야함.
- 인프라팀은 많은 서버를 관리하고 있으므로 애플리케이션이 요구하는 환경을 제공할 때까지의 시간(리드타임)이 길어짐
- 이 경우 애플리케이션이 Dockerfile로 미들웨어를 포함한 설정을 관리한다면 필요에 따라 빠르게 설정을 변경할 수 있음
- 예시
- 미들웨어 관리를 애플리케이션 팀이 담당할 경우 장점
- 인프라팀과 애플리케이션팀이 서로의 장점만을 얻는 관계로 변화
- 도커의 구성 요소
- 도커파일(Dockerfile)
- 운영체제, 언어, 환경 변수, 파일 위치, 네트워크 포트, 이를 실행하는 데 필요한 여타 컴포넌트를 포함하는 도커 이미지를 구축할 수 있는 일련의 명령을 제공하는 텍스트 파일
- 각 도커 컨테이너는 도커파일과 함께 시작함
- 도커파일(Dockerfile)
- 도커 이미지(Docker image, Container image)
- VM 환경의 스냅샷과 유사하며 이식 가능하고 읽기 전용의 실행 파일
- 컨테이너를 생성하기 위한 명령, 그리고 컨테이너가 어떤 소프트웨어 컴포넌트를 어떻게 실행할 것인가에 대한 내역이 담겨 있음
- 도커 실행 유틸리티(Docker run utility)
- 컨테이너를 시작하는 명령
- 각 컨테이너는 이미지 인스턴스이고, 동일 이미지의 다수의 인스턴스가 동시에 실행될 수 있음
- 도커 허브(Docker Hub)
- 컨테이너 이미지가 저장되고 공유되고 관리될 수 있는 저장소(Repository)
- 컨테이너에 특화된 깃허브 도커 버전이라고 생각할 수 있음
- 도커 엔진(Docker Engine)
- 도커의 핵심
- 컨테이너를 생성하고 실행하는 클라이언트-서버 기술이다. 도커 엔진은 컨테이너를 관리하는 이른바 ‘도커 대몬(Docker daemon)’이라는 장시간 실행되는 데몬 프로세스, 도커 대몬과 프로그램 사이의 통신을 담당하는 API, 명령줄 인터페이스를 포함한다.
- 도커 컴포즈(Docker Compose)
- YAML 파일을 이용하는 명령줄 도구이며 멀티 컨테이너 도커 애플리케이션을 정의하고 실행함
- 이에 의해 사용자는 사용자의 구성 환경으로부터 모든 서비스를 생성하고 시작하고 정지하고 재구축할 수 있으며, 모든 실행 서비스의 현황 및 로그 출력을 열람할 수 있음
- 도커 데스크톱(Docker Desktop)
- 위에서 다룬 제반 컴포넌트는 도커 데스크톱 애플리케이션으로 래핑 됨
- 이는 컨테이너화 된 애플리케이션과 마이크로서비스를 구축하고 공유하는 사용자 친화적 방식을 제공함
- 도커의 장점
- 도커 컨테이너는 이전의 방법보다 더 쉽게 조립하고, 유지관리하고, 이동시킬 수 있는 애플리케이션 제작 방법을 제공
- 소프트웨어 개발자에게 아래와 같은 혜택을 제공한다.
- 최소의 요소로 구성되어 높은 이식성을 가짐
- 도커는 애플리케이션과 이의 환경을 격리시킴으로써 이들을 정연하고 최소한으로 유지할 수 있게 해줌
- 이에 의해 미시적 제어와 이식성이 높아짐
- 컴포저블(composability) 특성 보유
- 컨테이너는 개발자가 애플리케이션의 구성 요소를 쉽게 호환되는 부분들로 된 모듈식 유닛으로 더 쉽게 조합할 수 있게 해줌
- 이는 개발 주기, 기능 배포, 버그 수정의 속도를 높일 수 있음
- 오케스트레이션과 스케일링 용이
- 컨테이너는 경량이기 때문에 개발자는 많은 수의 컨테이너를 시작해 서비스 스케일링을 향상시킬 수 있음
- 컨테이너 클러스터는 당연히 오케스트레이션이 필요하고 여기서 쿠버네티스가 등장
- 최소의 요소로 구성되어 높은 이식성을 가짐
- 도커의 단점
- 도커는 가상머신이 아니다. 일종에 프레임워크라 할 수 있음
- 컨테이너는 수많은 문제를 해결하지만 개발자가 가진 모든 난제를 해결하지는 못함
- 도커 컨테이너는 VM이 아니다.
- VM과 달리 컨테이너는 호스트 운영체제 리소스의 제어된 부분을 이용함
- 따라서 요소들이 VM 수준으로 엄격히 격리되지 않음
- 도커 컨테이너는 베어-메탈 속도를 제공하지 않는다.(베어메탈 이란? 하드웨어 상에 어떤 소프트웨어도 설치되어 있지 않은 상태를 의미함, 베어메탈 서버는 가상화를 위한 하이퍼바이저 OS없이 물리 서버를 그대로 제공하는 것을 말하며, 따라서 하드웨어에 대한 직접 제어 및 OS설정이 가능하다.)
- 컨테이너는 VM보다 더 경량이고 베어 메탈에 더 가까움
- 그러나 이는 성능 오버헤드를 초래함
- 워크로드가 베어-메탈 속도를 요구한다면 컨테이너는 이에 근접한 속도를 제공하지만 동일한 속도는 아님
- 도커 컨테이너는 스테이트리스(stateless)이며, 불변적(immutable)이다.
- 컨테이너는 해당 콘텐츠를 담은 이미지로부터 시작되고 실행됨
- 이미지는 기본적으로 일단 생성되면 변경되지 않음
- 그러나 컨테이너 인스턴스는 일시적(transient)임
- 인스턴스가 시스템 메모리로부터 제거되면 이는 영원히 사라짐
- VM처럼 컨테이너를 세션들에 걸쳐 지속시키려면 지속성을 위한 설계가 필요함
- 도커의 과제와 오케스트레이션 도구의 필요성
- 단일 컨테이너가 아닌 여러 개의 컨테이너를 실행할 때 발생하는 문제의 해결
- 보통 시스템의 구성이 커지면 컨테이너 여러 개를 연결해 서비스를 하나 만들게 되는데, 이런 구성일 때 컨테이너 사이의 통신과 가용성 확보에 대한 문제가 있음
- 오케스트레이션
- 여러 개의 컴퓨터 시스템, 애플리케이션 및/또는 서비스를 조율하고 관리하는 것
- 여러 개의 작업을 함께 연결하여 크기가 큰 워크플로나 프로세스를 실행하는 방식을 취하며 이러한 프로세스는 여러 개의 자동화된 작업으로 구성될 수 있으며 관련되는 시스템도 여러 개일 수 있음
- 오케스트레이션의 목표
- 빈도가 높고 반복할 수 있는 프로세스의 실행을 간소화 및 최적화하여 데이터 팀이 복잡한 작업과 워크플로를 간편하게 관리하도록 돕는 것
- 프로세스를 반복할 수 있고 작업을 자동화할 수 있다면 오케스트레이션을 사용하여 시간을 절약하고 효율성을 증대하고 중복성을 없앨 수 있다. ( 작업 오케스트레이션을 통해 데이터 및 머신 러닝을 간소화할 수 있음 )
- 2014년 구글에서 발표한 오케스트레이션 도구
- 컨테이너화된 워크로드와 서비스를 관리하기 위한 이식할 수 있고, 확장 가능한 오픈소스 플랫폼 선언적 구성과 자동화를 모두 지원함
- 데이터 플레인이라고 불리는 서버를 여러 대 실행시켜 그 위에 가상 오케스트레이션 계층을 구축하고 거기에서 컨테이너가 동작함
- 컨테이너 이용자는 이를 통해 컨테이너 그룹을 하나의 큰 머신 리소스로 볼 수 있게 되어 인프라를 추상화할 수 있음
- 인프라의 관리 측면에서 볼 때, 여러 대의 서버로 구성이 가능하므로 단일 장애점을 배제할 수 있는 장점을 가짐
- 어떤 가상 머신에서 어느 정도의 컨테이너를 동작시킬지를 관리하거나 새로운 컨테이너를 배포할 때 어떤 가상 머신에 배포하면 좋을지 등을 자동으로 판단함
- 장애가 발생한 컨테이너를 정지시키고 재시작하는 구조를 가짐
- 이러한 기능은 컨트롤 플레인이라는 마스터 노드 그룹에서 구현됨
- 이 외에도 수많은 기능으로 구성됨(쿠버네티스가 어려워지는 이유)
- 쿠버네티스의 구성
- 쿠버네티스의 필요성
- 컨테이너는 애플리케이션을 포장하고 실행하는 좋은 방법임
- 그러나 프로덕션 환경에서는 애플리케이션을 실행하는 컨테이너를 관리하고 가동 중지 시간이 없는지 확인해야 함
- 예: 컨테이너가 다운되면 다른 컨테이너를 다시 시작해야 함
- 이 문제를 시스템에 의해 처리한다면 더 쉽지 않을까? → 쿠버네티스가 필요한 이유
- 쿠버네티스가 할 수 있는 일
- 분산 시스템을 탄력적으로 실행하기 위한 프레임 워크를 제공
- 애플리케이션의 확장과 장애 조치를 처리하고, 배포 패턴 등을 제공
- 서비스 디스커버리와 로드 밸런싱(컨테이너에 대한 트래픽이 많으면, 쿠버네티스는 네트워크 트래픽을 로드밸런싱하고 배포하여 배포가 안정적이게 됨)
- 스토리지 오케스트레이션(로컬 저장소, 공용 클라우드 공급자 등과 같이 원하는 저장소 시스템을 자동으로 탑재 가능)
- 자동화된 롤아웃과 롤백(쿠버네티스를 사용하여 배포된 컨테이너의 원하는 상태를 서술 가은)
- 자동화된 빈 패킹(bin packing) (컨테이너화된 작업을 실행하는데 사용할 수 있는 쿠버네티스 클러스터 노드를 제공)
- 자동화된 복구(self-healing) (실패한 컨테이너를 다시 시작하고, 컨테이너를 교체하며, '사용자 정의 상태 검사'에 응답하지 않는 컨테이너를 죽이고, 서비스 준비가 끝날 때까지 그러한 과정을 클라이언트에 보여주지 않음)
- 시크릿과 구성 관리(암호, OAuth 토큰 및 SSH 키와 같은 중요한 정보를 저장하고 관리 가능하며, 컨테이너 이미지 재구성 없이, 스택 구성에 시크릿 노출 않고도 시크릿 및 애플리 케이션 구성을 배포 및 업데이트 할 수 있음)
- 참고: https://kubernetes.io/ko/docs/concepts/overview/working-with-objects/
<오늘의 회고>
728x90
'PYTHON-BACK' 카테고리의 다른 글
# 24.10.18_ 마켓컬리 클론코딩2_적립금 (3) | 2024.10.18 |
---|---|
# 24.10.17_ 마켓컬리 클론코딩_회원정보 수정 (1) | 2024.10.17 |
#파이썬 24.09.25_AWS CloudFront 실습2 (0) | 2024.09.26 |
#파이썬 24.09.25_AWS AWS Free Tier 실습 (3) | 2024.09.25 |
#파이썬 24.09.24_AWS 기초 서비스 이론 (5) | 2024.09.24 |