Category: AWS CLI


AWS CLI를 사용한 리전간 DB 파라미터 그룹 복사하기

Amazon Relational Database Service (RDS) 는 클라우드에서 관계형 데이터베이스를 손쉽게 설정, 운영 및 확장할 수 있는 관리형 서비스입니다. RDS를 사용하면 데이터베이스 패치 작업, 백업, 특정 시점으로의 복원 등의 복잡한 관리 절차를 자동화 할 수 있습니다.

AWS에서 글로벌 서비스를 진행할 때, 하나의 리전(Region)에서 운영 중인 서비스를  전 세계로 확장하기 위해 데이터베이스를 다른 리전으로 복사해야 하는 경우가 있습니다. 이때 스냅샷(Snapshot)을 생성하여 대상 리전에 복사 후 해당 스냅샷으로부터 새로운 데이터베이스 인스턴스를 만들면 글로벌 서비스가 가능합니다.

RDS의 Snapshot을 다른 리전으로 옮긴 후, 복원하여 사용하시기 위해서는 몇 가지 사전 작업이 필요합니다.

  • 보안 그룹 (Security Groups)생성
  • DB 옵션 그룹 생성
  • DB 서브넷 그룹 생성
  • DB 파라미터 그룹 생성

위의 정보들은 특정 리전에만 국한된 것이기에, 복사하려는 대상 리전에 미리 생성하여 준비 시켜두어야 합니다.

특히, 이중에 기존에 운영 중인 서비스와 동일한 환경과 성능을 보장하기 위해서는 파라미터 그룹을 일치화 시켜야 합니다. 대상이 MySQL의 경우에는 콘솔에서 설정할 수 있는 파라미터들이 300여개에 달하기에 이를 하나하나 비교해가며 수동으로 작업하는 것은 쉽지 않습니다.

(이 글이 작성되는 시점에는 아직 리전간 파라미터 그룹 복사 기능이 지원되지 않으므로) AWS Command-Line Interface(CLI)를 활용하여 다른 리전에서 복사한 스냅샷으로부터 데이터베이스를 생성 후 기존에 사용 중이던 파라미터 그룹과 일치화 시키는 방법에 대해서 알아보고자 합니다.

참고로 한번의 명령으로 최대 20개의 파라미터 값만 변경 가능합니다. 따라서, 설명의 편의를 위해 20개 이하의 파라미터 값 변경을 기준으로 진행하도록 하겠습니다. 20개 이상의 파라미터 값 변경은 반복 실행하는 스크립트 작성을 통해 가능합니다.

1. 환경 설정
우리는 AWS CLI를 활용한 DB 파라미터 그룹 복사 방법을 확인 하기 위해 동경(ap-northeast-1) 리전의 RDS에서 파라미터 그룹을 ‘src-pg’ 라는 이름으로 생성한 뒤 다음과 같이 파라미터를 수정하도록 하겠습니다.

Parameter src-pg default.mysql5.6
innodb_flush_log_at_trx_commit 0 <engine-default>
slow_query_log 1 <engine-default>
innodb_support_xa 0 <engine-default>
long_query_time 60 <engine-default>
character_set_server utf8 <engine-default>

2. 복사 대상 DB 파라미터 그룹 정보 확인
우리의 아이디어는 describe-db-parameters를 통해 파라미터 그룹의 정보를 출력 후, 대상 리전의 RDS에서 생성한 파라미터 그룹에 앞서 출력한 원본 파라미터 그룹의 정보를 modify-db-parameter-group를 통하여 업데이트 하는 것입니다.

수정된 파라미터 중 하나를 선택하여 어떠한 포맷과 형태로 정보들이 출력 되는지 확인 해 봅니다.

$ aws rds describe-db-parameters --db-parameter-group-name src-pg --query "Parameters [? ParameterName == 'long_query_time']"

결과는 JSON 포맷으로 다음과 같이 나옵니다.

[
    {
        "Description": "Defines what MySQL considers long queries",
        "DataType": "float",
        "IsModifiable": true,
        "AllowedValues": "0-31536000",
        "Source": "user",
        "ParameterValue": "60",
        "ParameterName": "long_query_time",
        "ApplyType": "dynamic"
    }
]

파라미터 정보들을 업데이트 하기 위해서는 modify-db-parameter-group명령을 사용합니다. 이 때 --generate-cli-skeleton 옵션을 주게 되면 업데이트를 위해 입력하여야 하는 데이터의 구조를 확인할 수 있습니다.

$ aws rds modify-db-parameter-group --generate-cli-skeleton

결과는 아래와 같이 파라미터를 업데이트 하기 위한 기본 구조가 출력 됩니다.

{
    "DBParameterGroupName": "",
    "Parameters": [
        {
            "ParameterName": "",
            "ParameterValue": "",
            "Description": "",
            "Source": "",
            "ApplyType": "",
            "DataType": "",
            "AllowedValues": "",
            "IsModifiable": true,
            "MinimumEngineVersion": "",
            "ApplyMethod": ""
        }
    ]
}

앞서 describe-db-parameters를 통하여 출력된 정보들과 비교해보면 거의 동일하며, MinimumEngineVersion과 ApplyMethod 가 추가되어 있는것을 확인할 수 있습니다. ApplyMethod 는 꼭 필요한 정보이며 MinimumEngineVersion 은 선택사항 입니다.

이를 통해 기존 출력된 정보에 ApplyMethod 만 추가하여 업데이트 한다면 원하는 파라미터 그룹을 다른 리전에서도 그대로 사용할 수 있다는 것을 알 수 있습니다.

ApplyMethod는 immediate와 pending-reboot 값 중 하나를 가질 수 있습니다. immediate는 변경 사항을 RDS 인스턴스에 즉시 적용하는 것을 의미하며, pending-reboot 은 인스턴스가 재시작 될 때 적용되는 것을 의미 합니다. 각 정보는 파라미터 종류와 운영 환경에 맞게 결정되어야 하겠지만, 이 글에서는 immediate로 지정하여 진행하도록 하겠습니다.

3. 복사하려는 파라미터 그룹 정보 출력
대상 파라미터 그룹에 업데이트 하기 위한, 복사하려는 파라미터 그룹 정보를 파일에 출력해 보겠습니다. 다만, 불필요하게 모든 파라미터 정보를 출력하지 않고, 수정된 파라미터만 출력하기 위해 --source 옵션을 사용하도록 하겠습니다.

$ aws rds describe-db-parameters --db-parameter-group-name src-pg --source user > src-pg.json

아래와 같은 결과가 src-pg.json 파일에 출력됩니다.

{
    "Parameters": [
        {
            "Description": "The server's default character set.",
            "DataType": "string",
            "IsModifiable": true,
            "AllowedValues": "big5,dec8,cp850,hp8,koi8r,latin1,latin2,swe7,ascii,ujis,sjis,hebrew,tis620,euckr,koi8u,gb2312,greek,cp1250,gbk,latin5,armscii8,utf8,ucs2,cp866,keybcs2,macce,macroman,cp852,latin7,utf8mb4,cp1251,utf16,cp1256,cp1257,utf32,binary,geostd8,cp932,eucjpms",
            "Source": "user",
            "ParameterValue": "utf8",
            "ParameterName": "character_set_server",
            "ApplyType": "dynamic"
        },
...

4. 파라미터 정보 추가하기
앞서 살펴본바와 같이 ApplyMethod는 필수이기 때문에 각 파라미터 정보에 추가하도록 하겠습니다. 먼저 파이썬(Python)을 활용한 방법입니다.

$ cat src-pg.json | python -c 'import sys,json; data=json.loads(sys.stdin.read()); map(lambda x: x.update({"ApplyMethod":"immediate"}), data["Parameters"]); print json.dumps(data)' | python -m json.tool > dest-pg.json

두번째는 jq를 사용(설치 필요) 하는 방법입니다.

$ cat src-pg.json | jq '.Parameters[] |= . + {"ApplyMethod": "immediate"}' > dest-pg.json

이제 dest-pg.json 파일을 출력해보면 다음과 같이 ApplyMethod 가 추가된 것을 확인할 수 있습니다.

{
    "Parameters": [
        {
            "AllowedValues": "big5,dec8,cp850,hp8,koi8r,latin1,latin2,swe7,ascii,ujis,sjis,hebrew,tis620,euckr,koi8u,gb2312,greek,cp1250,gbk,latin5,armscii8,utf8,ucs2,cp866,keybcs2,macce,macroman,cp852,latin7,utf8mb4,cp1251,utf16,cp1256,cp1257,utf32,binary,geostd8,cp932,eucjpms",
            "ApplyMethod": "immediate",
            "ApplyType": "dynamic",
            "DataType": "string",
            "Description": "The server's default character set.",
            "IsModifiable": true,
            "ParameterName": "character_set_server",
            "ParameterValue": "utf8",
            "Source": "user"
        },
....

5. 대상 리전에 파라미터 그룹 생성 및 업데이트
이제 복사할 대상 리전에 dest-pg 라는 이름의 기본 값을 가진 파라미터 그룹을 생성하겠습니다.

$ aws rds create-db-parameter-group --db-parameter-group-family MySQL5.6 --db-parameter-group-name dest-pg --description "This is a destination parameter group" --region us-east-1

결과는 다음과 같이 파라미터 그룹명을 포함한 정보가 출력 됩니다.

{
    "DBParameterGroup": {
        "DBParameterGroupName": "dest-pg",
        "DBParameterGroupFamily": "mysql5.6",
        "Description": "This is a destination parameter group"
    }
}

여기서는 명시적으로 –-region 이라는 옵션을 통하여 파라미터 그룹이 생성될 리전(us-east-1, Virginia)을 지정하였습니다.

앞서 만든 대상 리전의 파라미터 그룹에 원본 파라미터 그룹 정보를 업데이트를 합니다. --cli-input-json 옵션을 사용하여 앞서 원본 파라미터 그룹 정보에ApplyMethod 를 추가한 json 파일을 지정합니다.

$ aws rds modify-db-parameter-group --region us-east-1 --db-parameter-group-name dest-pg --cli-input-json file://dest-pg.json

정상적으로 완료되면 파라미터 그룹명이 출력 됩니다.

{
    "DBParameterGroupName": "dest-pg"
}

원본 파라미터 그룹과 대상 파라미터 그룹이 동일한지 diff를 사용하여 확인할 수 있습니다.

$ diff -s <(aws rds describe-db-parameters --db-parameter-group-name src-pg --source user) <(aws rds describe-db-parameters --region us-east-1 --db-parameter-group-name dest-pg --source user)

동일하다면, 다음과 같은 결과가 출력되며 완료된 것을 확인할 수 있습니다.

$ Files /dev/fd/?? and /dev/fd/??? are identical

지금까지 AWS CLI를 사용하여 리전간 RDS 파라미터 그룹을 복사하는 방법에 대해서 알아 보았습니다. 이외에도 다양한 방법으로 가능하겠지만, 앞서 살펴본 바와 같이 CLI를 활용한다면 몇 가지 명령어를 사용해 어렵지 않게 수행 할 수 있을 뿐만 아니라 자동화 할 수 있음을 확인 할 수 있습니다.

AWS CLI는 지속적으로 업데이트 되고 있으며, 새롭게 추가되는 새로운 명령어들과 기능들을 업무에 적용함으로써 작업 능률 향상을 꾀할 수 있을 것입니다. 이러한 점이 AWS의 매력 중 하나가 아닐까 생각합니다.

본 글은 아마존웹서비스 코리아의 솔루션즈 아키텍트가 국내 고객을 위해 전해 드리는 AWS 활용 기술 팁을 보내드리는 코너로서, 이번 글은 김필중 솔루션즈 아키텍트께서 작성해주셨습니다.

DevOps를 위한 AWS CLI 활용팁

AWS 클라우드를 사용하는 개발자 및 운영자라면 웹 기반 관리 콘솔 사용에 익숙하실 것입니다. 하지만, 좀 더 다양한 기능을 수행하거나 반복 작업 그리고 프로그래밍 언어를 통한 체계적인 관리가 필요하다면 AWS 명령줄 인터페이스(Command-line Interface, CLI)를 사용하는 것이 필요합니다.

AWS CLI 를 이용하면 다양한 AWS 서비스를 스크립트로 작성을 해서 여러 작업을 한번에 동작 시킬 수 있으며, 각종 관리 자동화를 수행하는데 유리합니다. AWS의 모든 서비스 기능은 API를 호출함으로서 가능하며, CLI 역시 간단한 명령줄을 통해 이러한 광범위한 API 기능을 수행할 수 있습니다. 이번 글에서는 여러분이 알아두시면 좋은 편리한 몇 가지 CLI 팁을 설명 드리겠습니다.

AWS CLI를 설치하고 나서 처음 해야 할 일은 Access Key 및 Secret Access Key 그리고 기본 리전 및 출력 결과를 설정하는 것입니다. (이 때, 접근 키들은 Root 권한의 키가 아니라, IAM(Identity & Access Management)을 통해 개별 서비스 권한 사용자를 생성하고 만든 키를 사용하는 것이 안전합니다. 더 자세한 사항은 IAM 사용자 가이드를 참고하세요.)

$ aws configure
AWS Access Key ID [****************YOJQ]: 
AWS Secret Access Key [****************5Mhd]:   
Default region name [ap-northeast-1]:
Default output format [json]:

Tip 1. AWS CLI 지원 도구를 활용하라.
AWS CLI의 대부분의 명령어는 $ aws help를 실행해보시면 알아보실 수 있습니다. 하지만, 매번 명령어를 기억하기는 쉽지 않습니다. 손쉽게 각 CLI 명령어를 자동 완성을 통해 실행하고자 한다면 aws shell를 설치해서 사용하시는 것을 권장합니다.

본 기능은 Donne Martin이 만든 SAWS라는 CLI 지원용 오픈소스 도구를 AWS CLI팀과 함께 통합한 것으로 다양한 상위 커맨드와 하위 커맨드를 자동 완성을 지원 합니다.

손쉽게 명령어를 입력 중에도 인라인 명령어 도움말을 바로 보실 수도 있습니다.

명령어 입력 중 AWS 리소스 자원, 예를 들어 Amazon EC2 instance IDs, Amazon SQS queue URL, Amazon SNS 토픽명) 등을 자동 완성 할 수도 있습니다.

그밖에 명령어 실행 기록 및 내보내기, 각종 기능의 도구바 지원 명령어 추천 기능 지원 등 다양한 기능도 제공합니다. 더 자세한 것은 aws-shell 기본 사용법Super-Charge Your AWS Command-Line Experience with aws-shell 블로그 글을 참고하시기 바랍니다.

Tip 2. aws configure 서브 명령어를 사용하라.
AWS CLI 설정을 위한 여러 서브 명령어가 존재합니다. 다음은 설정과 관련된 서브 명령어 입니다.

    $ aws configure
    list – 설정 관련 소스들을 보여준다.
    get – 하나의 설정 변수 값을 가져온다.
    set – 하나의 설정 변수 값을 저장한다.

예를 들어, 리전을 설정하는 것은 다음과 같이 하면 됩니다.

$ aws configure set profile.prod.region us-west-2

좀 더 자세히 살펴봐야 하는 것은, 설정 파일은 두개의 파일로 구성됩니다.

위치 기능
~/.aws/credentials 모든 AWS SDK에서 지원. 오직 인증 정보만 담고 있다.
~/.aws/config CLI에서만 사용되는 정보. 인증 정보를 담을 수도 있다.

설정 파일이 나뉜 것은 위와 같이 각기 수행하는 기능의 목적이 다르기 때문입니다. 구체적으로 명령 예를 들면 다음과 같습니다.

Access Key 아이디 설정

$ aws configure set profile.prod.aws_access_key_id foo
~/.aws/credentials ~/.aws/config
[prod]
aws_access_key_id = foo
 

Secret Access Key 아이디 설정

$ aws configure set profile.prod.aws_secret_access_key bar
~/.aws/credentials ~/.aws/config
[prod]
aws_access_key_id = foo
aws_secret_access_key = bar
 

리전 및 아웃풋 형식 설정

$ aws configure set profile.prod.region ap-northeast-1
$ aws configure set profile.prod.output text
~/.aws/credentials ~/.aws/config
[prod]
aws_access_key_id = foo
aws_secret_access_key = bar
[profile prod]
region = ap-northeast-1
output = text

이런 설정 정보를 알고 싶으면 list 서브 명령어를 사용해서 정보를 가져오면 됩니다.

$ aws configure list
      Name                    Value             Type    Location
      ----                    -----             ----    --------
   profile                             None    None
access_key     ****************YOJQ shared-credentials-file    
secret_key     ****************5Mhd shared-credentials-file    
    region           ap-northeast-1      config-file    

Tip 3. waiter를 적극 활용하라.
AWS CLI를 활용하면 쉘스크립트를 통해 프로그래밍 방식으로 여러 명령어를 하나로 처리할 수 있습니다. 예를 들어, EC2와 관련된 작업을 하는 경우, 대상이 되는 EC2가 동작하게 되고 나서 다른 동작을 처리해야 하는 경우가 생깁니다. 아래 그림은 일반적인 EC2 라이프 사이클입니다.

인스턴스가 새롭게 생성될 때 까지 기다렸다가 그 다음 동작을 해야 한다면, AWS CLI를 이용하여 아래와 같은 스크립트를 작성할 수 있을 것입니다. (예시 코드이며, 리눅스 쉘 스크립트를 기본으로 했습니다. 윈도우 사용자라면 파워셀 등을 이용하셔서 작성하시면 됩니다)

#!/bin/bash
instance_id=$(aws ec2 run-instances --image-id ami-12345 \
        --query Instances[].InstanceId \
        --output text)
instance_state=$(aws ec2 describe-instances --instance-ids $instance_id \
        --query 'Reservations[].Instances[].State.Name')
while [ "$instance_state" != "running" ]
do
    sleep 1
    instance_state=$(aws ec2 describe-instances --instance-ids $instance_id \
        --query 'Reservations[].Instances[].State.Name')
done

위의 코드는 단순 합니다. 이미지 아이디를 통해서 인스턴스 아이디와 인스턴스 상태 정보를 가져옵니다. 그리고 인스턴스 상태가 아직 “running”이 아니면, 무한 반복을 하면서 1초마다 확인해서 반복 루프를 도는 것입니다.

그런데, 이렇게 작성을 하게 되면 타임아웃 설정이 없어서, 대상 인스턴스가 뜨지 않은 경우 무한 반복을 하게 되고, 실패처리 로직이 없다는 문제가 발생합니다. 또한, 매우 코드가 길어지게 되는 단점이 있습니다. 이러한 문제를 해결하기 위해 존재하는 것이 “waiter”입니다. 이것을 활용한 명령어는 다음과 같이 됩니다.

...
instance_id=$(aws ec2 run-instances --image-id ami-12345 \
        --query Instances[].InstanceId \
        --output text)
instance_state=$(aws ec2 wait instance-running --instance-ids $instance_id \
...

EC2에 대해 wait 서브 명령어 다음에 나오는 것은 “대기 이름(waiter name)”입니다. 여기서는 instance-running이 됩니다. 위 명령으로 해당 인스턴스가 “러닝”상태가 될 때 까지 기다립니다.

사용 가능한 “대기 이름”은 help 명령어를 통해서 확인 할 수 있습니다. 또한, 이러한 wait 명령어는 EC2만이 아니라, 다른 서비스에도 적용할 수 있으며, 아래 코드는 Amazon DynamoDB 사례입니다.

$ aws dynamodb wait table-exists
$ aws dynamodb wait table-not-exists

AWS CLI는 AWS 서비스를 손쉽게 조작하는데 있어서 매우 중요한 도구입니다. 명령어 기반의 인터페이스로서 콘솔에서 하기 어려운 조작을 쉽고 편리하게 처리할 수 있습니다. AWS CLI를 사용해서 보다 강력한 AWS 서비스를 사용할 수 있으며, 다양한 활용 팁을 AWS CLI 영문 블로그 혹은 아래 한국팀에서 만든 AWS CLI 활용 사례 예제 코드도 한번 익혀보시기 바랍니다.

본 글은 아마존웹서비스 코리아의 솔루션즈 아키텍트가 국내 고객을 위해 전해 드리는 AWS 활용 기술 팁을 보내드리는 코너로서, 이번 글은 박선용 솔루션즈 아키텍트께서 작성해주셨습니다.

Amazon S3 멀티 파트 다운로드 이용하기

Amazon S3는 내구성 및 확장성을 요구하는 업무에 활발하게 이용되고 있는 오브젝트 스토리지입니다. 현재까지 꾸준히 기능이 추가 및 개선되고 있으며, AWS 모든 서비스의 맏형격으로 다양한 워크로드와 서비스들의 중앙에 위치해서 정적인 멀티미디어 컨텐츠부터 로그 데이터 저장, 백업과 아카이빙, 빅데이터 분석 및 재난 복구 등을 위해 널리 활용되고 있습니다.

Amazon S3에 저장된 오브젝트는 HTTP/HTTPS프로토콜을 통해 일반적으로 인터넷을 경유해서 이동하게 됩니다. 작은 크기의 파일은 짧은 시간 내에 전송되겠지만, 큰 크기의 파일을 하나의 작업으로 전송하는 것은 시간적인 측면에서 이용자에게 적잖은 부담으로 남게 됩니다. AWS는 하나의 큰 오브젝트를 Amazon S3상에 빠르게 올릴 수 있는 방법을 제공하고 있습니다.

일명 멀티파트 업로드. 기본적으로 Amazon S3에 단일 작업으로 올릴 수 있는 객체 크기는 최대 5GB입니다. 따라서 5GB보다 큰 오브젝트는 멀티파트 업로드를 반드시 이용해서 업로드해야하며, 최대 5TB 크기 오브젝트를 업로드할 수 있습니다.

일반적으로 오브젝트 크기가 100MB를 넘게되면 멀티파트 업로드를 권장하며, 멀티파트 업로드 API를 이용해서 큰 오브젝트를 여러 조각으로 나눠서 병렬적으로 빠르게 업로드 할 수 있습니다. 또한 AWS 명령줄 인터페이스를 이용하여 쉽게 멀티 파트 업로드할 수 있는 환경도 제공하고 있습니다.

그러면, Amazon S3상에서 있는 큰 크기의 오브젝트를 내려받는 것은 어떨까요? 아마도 ‘멀티파트 다운로드할 수 있는 API를 동일하게 제공하겠지?’라고 생각할 수 있습니다. 하지만 멀티파드 업로드 API처럼 오브젝트를 간편하게 멀티파트로 다운로드하는 API는 존재하지 않습니다. 다행히 AWS 명령줄 인터페이스의 s3 cp명령은 사용자에게 투명하게 오브젝트를 멀티파트로 내려받을 수 있도록 설계되어있습니다.

$ time aws s3 cp s3://shared/sample.zip sample.zip
download: s3://shared/sample.zip to ./sample.zip
real       3m46.765s
user       0m18.265s
sys        0m10.454s

기본적으로8MB 크기로 구성된 파트들로 멀티파트 다운로드하게 되며, 아래 예처럼 aws 명령줄 인터페이스 구성파일(~/.aws/credentials)을 수정해서 사용자화할 수 있습니다.

[profile development]
aws_access_key_id = xxxxxxxxxxxxxxxxxxxx
aws_secret_access_key = yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
max_concurrent_requests = 20
max_queue_size = 10000
multipart_threshold = 64MB
multipart_chunksize = 16MB

이 블로그 포스팅의 목적은 AWS 명령줄 인터페이스의 s3명령을 통해서 멀티 파트 다운로드 받는 방법을 소개하려는 것은 아닙니다. 사용자가 직접 간단하게 구현할 수 있는 멀티 파트로 다운로드 방법을 소개하고자 합니다. 이 방법을 이용하면 자신만의 코드로 작성된 앱 내에서도 멀티 파트 다운로드를 간단히 구현할 수 있고, 경우에 따라서는 이어받기 기능까지 응용할 수 있습니다.

원리는 W3 표준인 RANGE GET를 활용하는 것입니다. AWS에서 제공하는 GET Object REST API를 이용해서 RANGE 헤더를 사용할 수 있고, AWS 명령줄 인터페이스의 s3api 명령을 이용해서도 RANGE 헤더를 사용할 수 있습니다.

아래는 두번째 방법인 AWS 명령줄 인터페이스의 s3api 명령을 사용해서 멀티파트 다운로드 스크립트를 간단히 구현해 보고, 멀티파트 다운로드에 대한 기본 아이디어와 실제로 다운로드 속도가 어느정도 개선되는지 살펴보도록 하겠습니다. (참고로 AWS 명령줄 인터페이스에서 내부적으로 투명하게 제공되는 멀티파트 다운로드 기능은 s3 명령에서만 지원되고, s3api 명령에서는 지원되지 않습니다.)

사전 준비 사항
스크립트를 작성하기 위해서는 아래 AWS 명령줄 인터페이스가 준비되어야 합니다. AWS 명령줄 인터페이스는 여기를 클릭하시면 다운로드 받을 수 있으며, 설치 가이드와 사용법도 확인할 수 있습니다.

스크립트 작성하기
위 선행 작업이 완료되었다면, 이제 스크립트를 작성할 준비가 되었습니다. 멀티파트 다운로드 스트립트의 작업 순서를 아래와 같이 아주 단순합니다:

  1. 다운로드 하려는 S3 상의 오브젝트 크기 확인
  2. 사용자가 원하는 분할 개수에 맞도록 파트 크기 계산
  3. RANGE GET을 이용해서 오브젝트의 파트들을 병렬적으로 다운로드
  4. 모든 파트들이 다운로드 될 때까지 대기
  5. 다운로드된 모든 파트들을 하나로 묶기

우선 특정 버킷내에 있는 오브젝트 목록 및 속성을 얻는 명령을 알아보도록 하겠습니다. 이는 첫번째 단계인 오브젝트 크기를 확인하는데 사용됩니다. AWS 명령줄 인터페이스의 s3api list-objects 명령을 이용하면 오브젝트에 대한 속성을 알 수 있습니다. 아래는 shared 버킷내에 있는 모든 오브젝트의 정보를 얻을 수 있는 예입니다.

$ aws s3api list-objects –bucket shared

하지만, 실제로 필요한 값은 원하는 특정 오브젝트의 크기 정보입니다. 이는 --query 옵션을 통해서 가능하고, 오브젝트 이름은 Key, 크기는 Size 속성으로 참조할 수 있습니다. 아래 명령줄을 통해서 shared 버킷 내에 있는 sample.zip 오브젝트에대한 크기만 추출해보겠습니다.

$ aws s3api list-objects --bucket shared --query "Contents[?Key==`sample.zip`]|[0].Size"

위 명령어에서 `sample.zip`을 둘러싸고 있는 것은 백쿼터(`…`)인 것에 유념해야 합니다. 그리고 파이프 연산자를 이용해서 파이프 앞에 있는 쿼리 결과를 뒤로 전달하도록 하였으며, [0].Size를 통해서 오브젝트 크기만 추출합니다.

위에서 얻은 오브젝트의 크기를 분할하고 싶은 파트 개수로 나누게 되면 각 파트의 크기를 얻을 수 있습니다. 그리고 W3 표준인 RANGE GET에서 소개된 형식을 바탕으로 각 파트의 순서에 맞도록 BYTE RANGE 연산자를 활용하여 다운로드할 각 파트의 바이트 범위를 명시합니다. AWS 명령줄 인터페이스에서는 --range 옵션을 사용합니다. 아래는 몇가지 사용 예입니다:

$ aws s3api get-object --bucket shared --key sample.zip --range bytes=0-33554431 sample.zip.part.1

위 예는 sample.zip 오브젝트의 첫번째 파트입니다. 파트 크기를 첫 바이트부터 총 32MB(bytes=0-33554431) 크기로 내려받아서 sample.zip.part.1로 저장합니다.

$ aws s3api get-object --bucket shared --key sample.zip --range bytes=33554432-67108863 sample.zip.part.2

sample.zip 오브젝트의 두번째 파트는 이후 두번째 32MB(33554432-67108863)를 내려받아서 sample.zip.part.2에 저장합니다.

$ aws s3api get-object --bucket shared --key sample.zip --range bytes=268435456- sample.zip.part.9

그리고 sample.zip 오브젝트의 마지막 파트 부분으로 나머지 부분을 모두 받게 됩니다. 위 예는bytes=268435456-으로 표현되어 있는데요, 256MB지점부터 끝까지 내려받아서 sample.zip.part.9에 저장한 예입니다.

BASH 쉘 상에서 내려받는 명령어들은 모두 백그라운드로 수행시켜서 병렬로 다운로드 받게합니다. 그리고 이런 백그라운드 프로세스들이 모두 종료될 때까지 wait(1) 명령어를 이용해서 대기합니다.

끝으로 모든 파트를 다운로드하면 파트 순서에 맞춰서 한 파일로 함께 연결하면 됩니다.

지금까지 스크립트를 구성하는 주요 내용에 대해 설명드렸습니다. 아래 스크립트는 앞서 설명드린 내용들을 기반으로 작성한 간단한 bash 쉘 스크립트입니다.

#!/bin/bash
#
[ $# -ne 6 ] && exit 1

while getopts ":b:k:s:" OPT; do
  case $OPT in
    b)
      BUCKET=$OPTARG
      ;;
    k)
      KEY=$OPTARG
      ;;
    s)
      SPLIT=$OPTARG
      ;;
    *)
      echo "Invalid option: -$OPTARG" >&2
      exit 2
      ;;
  esac
done

# MAIN
readonly THRESHOLD_PART_SIZE=8388608   # 8MB (8*1024*1024)
readonly TMP_DIR="./tmp.$$";

OBJECT_SIZE=$(aws s3api list-objects --bucket $BUCKET --query "Contents[?Key==\`${KEY}\`]|[0].Size")

let PART_SIZE=OBJECT_SIZE/SPLIT
[ $PART_SIZE -lt $THRESHOLD_PART_SIZE ] && echo "Part size is $PART_SIZE. Each part size except last one must be larger than $((${THRESHOLD_PART_SIZE}/1024/1024))MB" &&  exit 3
[ ! -d "${TMP_DIR}" ] && mkdir ${TMP_DIR}

I=1
START=0
let END=PART_SIZE-1
while [ $I -le $SPLIT ]
do
   aws s3api get-object --bucket $BUCKET --key $KEY   --range bytes=${START}-${END}  ${TMP_DIR}/${KEY}.${I} > /dev/null 2>&1 &
   let I=I+1
   let START=START+PART_SIZE
   let END=END+PART_SIZE; [ $I -eq $SPLIT ] && END=''
done

wait > /dev/null 2>&1

[ -f $KEY ] && rm $KEY
I=1
while [ $I -le $SPLIT ]
do
   cat ${TMP_DIR}/${KEY}.${I} >> $KEY
   let I=I+1
done
rm -rf $TMP_DIR

스크립트의 간결성을 유지하기 위해서 대부분의 에러처리 루틴을 배제하였습니다. 따라서 범용적으로 사용하기 위해서는 적절한 에러 처리 루틴들이 추가되어야 하고 충분한 테스트를 거친 후 사용하시길 권장합니다.

아래 두 예는 S3에 저장된 약 700MB sample.zip 파일을 위 스크립트를 활용해서 20개로 나눠 다운로드했을 때와 한번의 작업으로 다운로드 받을 때의 결과를 보여주고 있습니다.

$ time ./s3md.sh -b shared -k sample.zip -s 20
real       2m12.268s
user       0m26.010s
sys        0m11.952s

700MB의 단일 파일을 20개로 나눠서 받았을 때, 약 2분 12초 정도 시간이 소요되었습니다. 그리고 이어지는 AWS 명령줄 인터페이스는 단일 작업으로 해당 오브젝트를 내려받는 결과입니다.

$ time aws s3api get-object --bucket shared --key sample.zip sample.zip > /dev/null
real       4m17.417s
user       0m19.199s
sys        0m9.284s

단일 작업으로 다운로드 하였을 경우 약 4분 17초정도 소요되었습니다. 위 예에서는 멀티 파트로 다운로드 받을 경우 약 50%정도의 시간이 단축된 것을 확인할 수 있었습니다. 다만 고려 해야 할 것은 인터넷 환경 및 클라이언드 환경에 따라 위 결과와 다르게 나올 수 있습니다.

끝으로 Amazon S3상에서 큰 데이터를 자주 조작 하시는 IT팀이나 앱 내에서 S3로 파일을 주고 받아야 하는 경우, 오늘 소개해드린 멀티 파트 다운로드 방법이 조금이라도 아이디어를 제공해드렸기를 기대해봅니다.

본 글은 아마존웹서비스 코리아의 솔루션즈 아키텍트가 국내 고객을 위해 전해 드리는 AWS 활용 기술 팁을 보내드리는 코너로서, 이번 글은 박철수 솔루션즈 아키텍트께서 작성해주셨습니다.

AWS CLI를 통한 빠른 인스턴스 타입 변경 방법

클라우드 컴퓨팅 장점 중의 하나는 민첩성(Agility)입니다. 전통적인 IT환경에서 서버의 CPU나 메모리 또는 NIC 등의 부품 구성을 변경하는 작업은 운영자의 수작업이 동반됩니다. 하지만, AWS 환경에서는 웹 관리 콘솔을 통해서 몇 번의 클릭만으로 관련 작업을 신속하고 간단히 수행할 수 있습니다.

그러나, 수 십대 또는 수 백대의 인스턴스들을 운영하고 또 한정된 점검 시간 내에 관련 작업을 수행 해야한다면 이 작업 또한 그리 쉬운 일은 아닙니다. 이런 경우 AWS에서 제공하는 API나 CLI를 이용해서 간단한 프로그램이나 스크립트를 작성하면, 반복적인 작업을 일괄 처리 수행하여 쉽고 빠르게 목표한 결과를 얻을 수 있습니다.

이 글에서는 c3.large 타입의 인스턴스들을 새로운 세대인 c4.large 타입으로 한꺼번에 변경해야 하는 상황을 가정해서, 일괄 처리하는 스크립트를 작성하는 예를 소개합니다.

사전 준비 사항
스크립트를 작성하기 위해서는 아래 AWS 명령줄 인터페이스가 준비되어야 합니다. AWS 명령줄 인터페이스는 여기를 클릭하시면 다운로드 받을 수 있으며, 설치 가이드와 사용법도 확인할 수 있습니다.

스크립트 작성하기
위의 CLI 설치 작업이 완료되었다면, 이제 스크립트를 작성할 준비가 되었습니다. 인스턴스 타입을 변경하는 작업은 인스턴스가 정지된 상태서만 가능하기 때문에, 원하는 작업을 성공적으로 하려면 다음의 작업 순서를 따라야 합니다:

  1. 대상 인스턴스 목록 추출
  2. 인스턴스 정지
  3. 인스턴스 타입 변경
  4. 인스턴스 시작

1번에서 획득한 각 인스턴스를 대상으로 2, 3, 4번을 순차적으로 반복 수행하게 됩니다.

우선 인스턴스 목록을 얻기 위해서 describe-instances 명령을 이용합니다. 아래 명령어는 AWS 계정에서 운영되는 모든 인스턴스의 정보를 얻을 수 있습니다.

$ aws ec2 describe-instances 

작업 대상인 c3.large 타입의 인스턴스들에 대한 정보만 얻기 위해서는 필터 기능을 활용해야 합니다. --filters 옵션을 사용해서 instance-type이 c3.large인 인스턴스들만 추출합니다.

$ aws ec2 describe-instances  --filters "Name=instance-type,Values=c3.large"

위 명령어를 통해서 c3.large 타입의 인스턴스에 대한 모든 속성 정보를 볼 수 있습니다. 응답된 데이터에서 실제로 필요한 인스턴스 ID 정보만 얻기 위해서는 --query 옵션을 사용하고 그 값으로 Reservations[].Instances[].InstanceId 문자열을 사용합니다. 또한 스크립트에서 사용하기 용이하도록 출력되는 형태를 JSON 형태가 아닌 일반 텍스트로 출력되도록 변경합니다. 이를 위해서 --output 옵션과 옶션값으로 text 값을 선택합니다.

출력되는 결과의 필드 구분자는 탭문자(‘\t’)입니다. 즉, 인스턴스 ID들이 탭문자로 구분되어서 한 라인에 길게 나열되게 됩니다. tr(1)명령어를 이용해서 탭문자를 개행문자(‘\n’)로 대체시켜 한 줄에 하나의 인스턴스 ID가 출력되도록 변경합니다.

$ aws ec2 describe-instances  --filters "Name=instance-type,Values=c3.large" --query "Reservations[].Instances[].InstanceId"  --output text |  tr "\t" "\n"

이제 앞에서 얻은 인스턴스 ID 목록과 아래에 소개할 명령어들을 이용해서 인스턴스를 하나씩 c3.large에서 c4.large로 변경할 수 있습니다.

먼저 인스턴스 하나를 정지시킵니다.

$ aws ec2 stop-instances --instance-id=${INSTANCE_ID} 

위 명령어는 비동기 명령이기때문에 해당 인스턴스에 정지 명령을 실행시키고 결과에 관계없이 바로 리턴됩니다. 이 명령어 다음에 바로 인스턴스 타입을 변경시키면 에러가 발생할 수 있습니다.

따라서 인스턴스 상태를 주기적으로 체크하여 인스턴스가 정지(STOPPED)된 상태로 변경되었을 때 타입을 변경해야 합니다. 비동기 명령을 내릴 때마다 체크하는 부분을 스크립트 내에 코드로 작성한다면 다소 번거롭고 귀찮은 일입니다. 하지만 AWS 명령줄 인터페이스는 친절하게도 이를 대신 수행하는 명령을 제공합니다. 비동기 명령어가 수행되고 원하는 상태가 되었을 때까지 기다릴 수 있도록 wait라는 추가 명령 옵션을 제공하고 있습니다.

$ aws ec2 wait instance-stopped --instance-ids=${INSTANCE_ID}

위 명령어를 통해서 인스턴스가 실제로 정지될 때까지 스크립트는 다음 단계를 수행하지 않고 멈춰 있습니다. wait명령을 통해서 인스턴스가 정지할 때까지 아래 명령어를 수행하지 않고 대기합니다.

인스턴스 타입 변경은 modify-instance-attribute 명령을 이용해서 간단히 수행합니다.

$ aws ec2 modify-instance-attribute --instance-type=c4.large --instance-id=${INSTANCE_ID} 

타입 변경이 끝났다면, 이제 인스턴스를 다시 시작하기만 하면 됩니다.

$ aws ec2 start-instances --instance-id=${INSTANCE_ID}

인스턴스를 시작하는 명령도 비동기 명령입니다. 그렇기 때문에 인스턴스에 시작 명령을 내리고 실제 인스턴스가 정상적으로 실행되었는지와는 관계없이 바로 리턴됩니다.
아래와 같이 wait 명령어를 통해서 해당 인스턴스 상태가 정상적으로 시작된 상태인지를 확인한 후 다음 인스턴스에 대한 타입 변경 작업을 수행할 수 있습니다. 이 부분은 선택사항입니다.

$ aws ec2 wait instance-status-ok --instance-ids=${INSTANCE_ID} 

지금까지 스크립트를 구성하는 주요 부분에 대해서 설명드렸습니다. 아래 스크립트는 앞서 설명드린 내용들을 기반으로 작성한 간단한 bash 쉘 스크립트입니다.

#!/bin/bash
#
while getopts ":s:d:" OPT; do
  case $OPT in
    s)
      SRC_TYPE=$OPTARG
      ;;
    d)
      DEST_TYPE=$OPTARG
      ;;
    *)
      echo "Invalid option: -$OPTARG" >&2
      exit 1
      ;;
  esac
done

aws ec2 describe-instances  --filters "Name=instance-type,Values=${SRC_TYPE}" --query 'Reservations[].Instances[].InstanceId' --output text |tr "\t" "\n"| while read INSTID
do
    echo "Changing instance type for ${INSTID}... $(date)"
    aws ec2 stop-instances --instance-id=${INSTID} > /dev/null 2>&1
    aws ec2 wait instance-stopped --instance-ids=${INSTID} > /dev/null 2>&1
    aws ec2 modify-instance-attribute --instance-type=${DEST_TYPE} --instance-id=${INSTID} > /dev/null 2>&1
    aws ec2 start-instances --instance-id=${INSTID} > /dev/null 2>&1
    aws ec2 wait instance-status-ok --instance-ids=${INSTID} > /dev/null 2>&1
done
echo  "All done! $(date)"

스크립트의 간결성을 유지하기 위해서 모든 에러처리 루틴을 배제하였습니다. 따라서 범용적으로 사용하기 위해서는 적절한 에러 처리 루틴들이 추가되어야 하고 충분한 테스트를 거친 후 사용하시길 권장합니다.

아래 예는 실제로 두 개의 c3.large 리눅스 인스턴스를 c4.large로 변경하였을 때의 실행 결과를 캡처한 내용입니다.

$ ./migration.sh –s c3.large –d c4.large
Changing instance type for i-353294c7... 2015년 7월 20일 월요일 09시 27분 01초 KST
Changing instance type for i-253593d7... 2015년 7월 20일 월요일 09시 30분 22초 KST
All done! 2015년 7월 20일 월요일 09시 34분 44초 KST

이 스크립트를 통해 대규모 EC2 인스턴스 운영 환경에서 인스턴스 타입을 변경할 뿐만 아니다 각종 정보를 변경하는 등 다양한 운영 작업에 활용하시는데 도움이 되었으면 합니다. 감사합니다.

본 글은 아마존웹서비스 코리아의 솔루션즈 아키텍트가 국내 고객을 위해 전해 드리는 AWS 활용 기술 팁을 보내드리는 코너로서, 이번 글은 박철수 솔루션즈 아키텍트께서 작성해주셨습니다.