's3'에 해당하는 글 5건

AWS CDK

Server/AWS 2022. 2. 13. 23:54

AWS CDK(Cloud Development Kit) 를 이용하면 Java, Python, Script 등을 통해 AWS CloudFormation 을 만들어 AWS 클라우드 리소스를 프로비저닝 할 수 있다. AWS 콘솔 창에서 클릭과 타이핑으로 생성하던 리소스들을 code 화 하여, 반복적인 리소스 생성이 필요할 때 유용하다.

 

쉽게 말해 CDK 에서 지원하는 언어(Java, Python, TypeScript, ...) 로 프로그래밍하고, 커맨드 라인 도구인 CDK 툴킷을 통해 AWS CloudFormation 템플릿을 생성하고, stack 을 배포하여 AWS 의 모든 리소스들을 생성/설정할 수 있다.

 

준비해야 할 것은..

  • AWS cli 설치
  • 자격증명(credentials)
  • cdk 설치...
    > npm install -g aws-cdk@1.140.0

 

현재 CDK 는 v2 버전대가 있지만, 이런저런 이유으로 인하여 v1 을 사용하였다. 차이라면 v2 는 새로나온 리소스등을 계속해서 추가해 줄 것이고, v1 은 점점 deprecated 되는 메소드들이 많아질 것이다. 또 bootstrap 도 v2 에서는 필수로 설치하는 거 같고... 참고로 AWS 는 2023년 6월 1일에 CDK v1 에 대한 지원을 종료한다.

 

준비를 마쳤다면  Java(JDK 8+) 로 S3 버킷을 하나 생성하는 간단한 단계는 다음과 같다.

 

 

1. cdk 프로젝트 자동 생성

 

> cdk init app --language java

 

위 명령은 현재 디렉토리에서 cdk 를 쉽게 개발할 수 있도록 관련 library 들의 설정이 포함된 maven 기반의 빈 프로젝트를 생성해 준다. IDE 로 열어보면 Java 기본 구조로 된 maven 프로젝트를 볼 수 있다.

 

 

2. dependency 추가

 

<dependency>
    <groupId>software.amazon.awscdk</groupId>
    <artifactId>s3</artifactId>
    <version>${cdk.version}</version>
</dependency>

 

v2 는 대부분의 리소스를 기본적으로 지원하는 거 같긴한데... 필요하다면 추가할 artifactId 는 대부분의 AWS 서비스 이름으로 검색하고 dependency 에 추가해야 한다.

 

 

3. java 파일 확인/수정/빌드

 

main 메소드가 들어있는 App.java 파일과, 기본적인 Stack.java 파일이 생성되어 있다. App 에는 하나 이상의 Stack(구체적인 AWS 리소스) 을 정의할 수 있다. Stack 파일을 바탕으로 리소스를 추가하면 되고, main 메소드에는 계정 정보와 함께 새로 생성한 Stack 파일을 로드하면 된다.

 

// App.java
public class App {
    public static void main(final String[] args) {
        App app = new App();

        new MyS3Stack(app, "MyCdkS3Stack", StackProps.builder()
                .env(Environment.builder()
                        .account("111122223333")
                        .region("ap-northeast-2")
                        .build())
                build());

        app.synth();
    }
}

 

// MyS3Stack
public class MyS3Stack extends Stack {
    public MyS3Stack(final Construct scope, final String id) {
        this(scope, id, null);
    }

    public MyS3Stack(final Construct scope, final String id, final StackProps props) {
        super(scope, id, props);

        Bucket.Builder.create(this, "MyFirstBucket")
                .bucketName("cdk-test1")
                .build();
    }
}

 

Stack 의 모든 리소스는 scope, id, props 등으로 구성되는데, scope 는 부모 stack 을 명시한다. id 는 App 내부에서 사용될 id(hash 문자열이 붙음). props 는 해당 리소스의 속성이다.(null 가능) 위 예제에서는 각각 this, MyfirstBucket, bucketName 등이 되겠다. Java에서는 props를 전달하기 위해 Builder가 제공된다. 위 예에서는 BucketProps 나 Bucket 을 사용할 수 있다.

 

// Bucket
Bucket.Builder.create(this, "MyFirstBucket")
    .bucketName("cdk-test1")
    .build();

// BucketProps
new Bucket(this, "MyFirstBucket", new BucketProps.Builder()
    .bucketName("cdk-test1")
    .build());

 

 

4. CDK 병합(synth)

 

코드를 모두 작성했다면 App 의 루트 디렉토리에서 synth / deploy 명령으로 병합/배포해야 한다. cdk synth 를 실행하면 자동으로 빌드(mvn package) 도 되지만, 수동으로 빌드하여 미리 에러 등을 체크할 수 있다. 마찬가지로 deploy 역시 synth 를 자동으로 실행 해주기 때문에 사실상 빌드없이 deploy 만 해도 무방하긴 하다.

 

> cdk synth --profile my
Resources:
  MyFirstBucketB8881111:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: cdk-test1
    UpdateReplacePolicy: Retain
    DeletionPolicy: Retain
    Metadata:
      aws:cdk:path: MyS3Stack/MyFirstBucket/Resource
  CDKMetadata:
    Type: AWS::CDK::Metadata
    Properties:
      Analytics: v2:deflate64:H5sIAAAAAAAA/yWKQQ5EIAwA3+Idq1UfYNbbHvUFpNYEiZBQ0APh70Y8zSQzCNgN0FajvqSm1TaZfGDIS9Rk1eSdxJAoqmlzM4tPgbio55Ue8i+R4bd9Vor651M3iNAC9tUuxtQhuWgOhvnjDSNKfjhxAAAA
    Metadata:
      aws:cdk:path: MyS3Stack/CDKMetadata/Default

 

위와 비슷한 yaml 형식으로 출력되며 cdk.out/MyS3Stack.template.json 파일에 저장되고, 배포시 cdk.out 디렉토리를 기반으로 배포된다.

aws-cli 에 profile 이 여러개일 경우 원하는 profile 로 지정해야 한다. 소스와 aws-cli 의 계정이 일치하지 않는 경우 아래와 같은 에러가 발생한다.

 

Need to perform AWS calls for account 111122223333, but the current credentials are for 444455556666

 

위 오류가 발생해서 profile 을 정상적으로 설정했는데 아래 오류가 또 발생했다.

 

MyCdkS3Stack (MyFirstBucketB8881111) cdk-test1 already exists
The stack named KpsCdkS3Stack failed creation, it may need to be manually deleted from the AWS console: ROLLBACK_COMPLETE

 

S3 를 확인해보니 비어 있는데 웬 cdk-test1 already exists... 처음 profile 오류나면서 잘못된 계정으로 cdk-test1 을 생성하려는 시도가 있었을 것이고, 그 정보가 어딘가에 남아 있는듯...; (이해할 수 없음)

 

 

5. CDK 배포(destroy)

 

deploy 로 배포하고 나면, CloudFormation 과 S3 에서 리소스들을 확인할 수 있다.

 

> cdk deploy --profile my
MyS3Stack: deploying...
MyS3Stack: creating CloudFormation changeset...
  0/3 |오후 12:12:13 | REVIEW_IN_PROGRESS   | AWS::CloudFormation::Stack | MyS3Stack User Initiated
  0/3 |오후 12:12:18 | CREATE_IN_PROGRESS   | AWS::CloudFormation::Stack | MyS3Stack User Initiated
  0/3 |오후 12:12:22 | CREATE_IN_PROGRESS   | AWS::CDK::Metadata | CDKMetadata/Default (CDKMetadata)
  0/3 |오후 12:12:22 | CREATE_IN_PROGRESS   | AWS::S3::Bucket    | MyFirstBucket (MyFirstBucketB8881111)
  0/3 |오후 12:12:24 | CREATE_IN_PROGRESS   | AWS::CDK::Metadata | CDKMetadata/Default (CDKMetadata) Resource creation Initiated
  0/3 |오후 12:12:24 | CREATE_IN_PROGRESS   | AWS::S3::Bucket    | MyFirstBucket (MyFirstBucketB8881111) Resource creation Initiated
  1/3 |오후 12:12:24 | CREATE_COMPLETE      | AWS::CDK::Metadata | CDKMetadata/Default (CDKMetadata)
  2/3 |오후 12:12:45 | CREATE_COMPLETE      | AWS::S3::Bucket    | MyFirstBucket (MyFirstBucketB8881111)
  3/3 |오후 12:12:46 | CREATE_COMPLETE      | AWS::CloudFormation::Stack | MyS3Stack

 ✅  KpsCdkS3Stack

Stack ARN:
arn:aws:cloudformation:ap-northeast-2:111122223333:stack/MyS3Stack/1231dca0-8a2f-12ec-8c62-0618df8ff132

 

cloudformation-cdk

 

s3-cdk

 

여러 스택을 동시 실행하는 것도 가능하다.

 

> cdk deploy Happy Grumpy   # app defines two or more stacks; two are deployed
> cdk synth "Stack?"    # Stack1, StackA, etc.
> cdk deploy "*Stack"   # PipeStack, LambdaStack, etc.

 

 

6. CDK 재배포

 

> cdk diff --profile my

 

위 명령으로 기존 소스와 수정된 소스간의 변경점을 출력해 볼 수 있으며, cdk deploy 로 계속해서 코드의 변경사항을 리소스에 적용할 수 있다. (버전 관리가 유용할 수 있다.)

 

 

7. stack 삭제

 

생성한 stack 을 삭제한다.

 

> cdk ls
MyS3Stack

> cdk destroy MyS3Stack --profile my
Are you sure you want to delete: MyS3Stack (y/n)? y
MyS3Stack: destroying...

 ✅  MyS3Stack: destroyed

 

CloudFormation 의 Stack 은 정상적으로 삭제되었지만, S3 버킷은 삭제되지 않았다. 기본적으로 사용자 데이터가 포함될 수 있는 리소스에는 RETAIN 의 removePolicy 속성이 있으며, 해당 리소스는 생성될 때 스택과 분리되므로 수동으로 삭제해야 한다. Stack 삭제시 비어있는 버킷을 삭제하고자 한다면 removePolicy 설정을 Destroy 변경하면 된다. 또한 버킷이 비어있지 않다면 이 또한 실패하는데, 버킷의 autoDeleteObjects 속성을 true로 설정할 수 있다.

 

.removalPolicy(RemovalPolicy.DESTROY)
.autoDeleteObjects(true)

 

코드 변경하며 여러 버킷을 만들었었는데 마지막 소스에만 국한하지 않고, 모든 버킷을 삭제한 건 조금 굿잡...

 

 

Java 의 더 많은 예제는 요기...
https://github.com/aws-samples/aws-cdk-examples/tree/master/java

 

GitHub - aws-samples/aws-cdk-examples: Example projects using the AWS CDK

Example projects using the AWS CDK. Contribute to aws-samples/aws-cdk-examples development by creating an account on GitHub.

github.com

 

 

약 3일 정도 CDK 를 사용해 봤다. 위에 s3 의 예는 약 100개 이상의 리소스 중에 하나일 뿐이다. API 꾸역꾸역 찾아가며 각종 리소스를 생성해 봤다. Terraform 이든 CDK 든 당연히 누군가에게는 좋은 IaC 도구이다. 본인이 얼마나 많은 리소스를 관리해야 할지에 따라 필요할 수도, 그렇지 않을 수도 있다. 디테일한 설정을 위해서 리소스마다 많은 옵션들을 찾아봐야 하며 정상적으로 세팅이 되는지 테스트해야 하고... 이런 시간들이 만만치 않다. 여러 설정들을 생성하고 수정하고를 테스트해 보았는데 실제 운영중인 리소스들을 과연 CDK 를 이용하여 한번에 컨트롤할 수 있을지는 확신하지 못했다. 그리고 반복적인 작업도 마찬가지로 코드레벨에서 naming 과 ip 대역 등의 무수한 수정들이 동반된다고 할 때 CDK 가 과연 나에게 필요한 것인지에 대해서는 확신할 수 없었다. 반복적인 작업에 대해 시간을 단축하려고 맛을 보긴 했지만, 내가 앞으로 지금까지의 시스템을 복제하는데 3일은 안걸릴 것이라는 확신과 함께 빨리 손을 떼었다. 언젠가 시간이 좀 날때 다시 봐야할 듯...


WRITTEN BY
손가락귀신
정신 못차리면, 벌 받는다.

,

파일 mime-type 체크

Daily/Prog 2020. 5. 14. 18:26



얼마전 s3 에 업로드된 상당량의 이미지 mime-type 이 octet-stream 으로 올라가 있는 것을 확인했다. 또 그 파일들은 모두 확장자가 대문자인 것도 확인했다.


String mimeType = Files.probeContentType(path);


위 부분에서 대문자 확장자 파일들에 대한 처리가 정상적으로 되지 않아 null 을 반환한 것이 문제였다. 확장자로 mimeType 을 얻어오는 과정에서 발생할 예외들을 미리 예상하여 처리할 수도 있지만, 업로드시 파일에서 직접 mimeType 을 확인할 수도 있다. Apache 의  Tika 라이브러리를 사용해 봤다.


compile group: 'org.apache.tika', name: 'tika-core', version: '1.24'

...

String mimeType = new Tika().detect(inputStream);


tika-parsers 는 용량이 어마무시하다. parser 가 꼭 필요한게 아니라면 core 만 받자.


그리고 기존에 s3 에 octet-stream 타입으로 업로드된 이미지 파일들은 aws-cli 로 배치처리 하여 image/jpeg 으로 변경하였다.


> aws s3 cp \

 s3://bucket/path \

 s3://bucket/path \

 --exclude "*" \

 --include "*.JPG" \

 --acl public-read \

 --no-guess-mime-type \

 --content-type "image/jpeg" \

 --metadata-directive "REPLACE" \

 --recursive

 --profile myprofile


아쉽게도 include 에 지정한 패턴이 대소문자를 구분하지 못해서 시간이 꽤 걸렸음...




WRITTEN BY
손가락귀신
정신 못차리면, 벌 받는다.

,

s3 업로드시 발생하는 로그.


Sep 30 03:29:32 ip-172-50-10-72 tomcat8: 2019-09-30 12:29:32.717  WARN 27104 --- [io-8443-exec-65] c.amazonaws.services.s3.AmazonS3Client   : No content length specified for stream data.  Stream contents will be buffered in memory and could result in out of memory errors.


s3 업로드시 setContentLength 를 지정하지 않거나 IOUtils.toByteArray(inputStream) 사용시 inputStream 이 소진되어 발생하는 경고이며 다음과 같이 수정이 필요하다.


byte[] bytes = IOUtils.toByteArray(inputStream);
objectMetadata.setContentLength(bytes.length);
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
 
PutObjectRequest putObjectRequest = new PutObjectRequest(bucket, key, byteArrayInputStream, objectMetadata);
client.putObject(putObjectRequest);
cs




WRITTEN BY
손가락귀신
정신 못차리면, 벌 받는다.

,

간만에 AWS 유지보수 건이 떨어졌다.


AWS Signature Version 4 to replace AWS Signature Version 2 for signing S3 API requests

Amazon S3 will no longer support path-style API requests starting September 30th, 2020



AWS Signature Version 4 to replace AWS Signature Version 2 for signing S3 API requests


2019년 6월 24일 부터는 S3 API 요청시 Signature Version 2 방식(SigV2)의 서명으로는 통신사용이 불가능하다. 기존 코드가 SigV2 서명을 사용 중이었다면 보안이 강화된 SigV4 방식으로 코드를 수정해야 한다. 2014년 1월 이후에 런칭된 리전(서울리전 등...) 에서는 SigV4 만 지원하므로 수정할 필요가 없으나 그 전에 런칭된 리전(도쿄리전 등....) 에서는 SigV2, SigV4 모두 사용이 가능했으므로 어떤 버전의 서명을 사용중인지 체크해야 한다. 개발 프레임워크 로그에서 서명 버전을 확인할 수 있으면 좋겠지만 스프링에서는 확인할 수 없었다. 대신 AWS CloudTrail 로그나 Athena 쿼리로 조회할 수 있다.


SELECT * 

FROM s3_cloudtrail_events_db.cloudtrail_myawsexamplebucket_table

WHERE eventsource='s3.amazonaws.com'

AND json_extract_scalar(additionalEventData, '$.SignatureVersion')='SigV2'

LIMIT 5


SignatureVersion 이 SigV2 인 레코드가 있다면 SDK 를 최신버전으로 바꾸는 것으로 간단하게 해결된다.

사용 언어 확인 : https://docs.aws.amazon.com/ko_kr/AmazonS3/latest/dev/UsingAWSSDK.html

참고로 나는 AWS SDK for JAVA 1.10 버전에서 1.11 로 변경하여 해결하였다. 람다 스무개를 다바꿨다... ㅡㅡ;;



Amazon S3 will no longer support path-style API requests starting September 30th, 2020


S3 주소 지정시에 두가지 형식을 사용할 수 있었다. 


  • //s3.amazonaws.com/<bucketname>/key (Path style)
  • //<bucketname>.s3.amazonaws.com/key) (Virtual-hosted style)


버킷 이름이 도메인 호스트 자리에 들어가느냐 경로(path) 안에 들어가느냐 인데, 경로 방식을 2020년 9월 30일부터 폐기하기로 했다. 경로 방식을 사용 중이라면 미리 준비해 놓는 것이 좋다.



미리 공지주는건 좋은데, 그래도 Deprecate 난 반댈세...





WRITTEN BY
손가락귀신
정신 못차리면, 벌 받는다.

,

S3 Output Plugin

Tool/Fluentd 2017. 4. 13. 23:10

이제 flentd 의 워크플로우와 설정 파일을 수정하는 방법을 보았으니, 내가 하고자 하는 인프라를 구축하고 td-agent.conf 파일을 수정하면 된다.

input -> (filter) -> match 모델로 거의 모든 다양한 데이터 수집이 가능한데, 흔히 쓰이는 것들을 요약하자면...

웹서버 로그 / 시스템 로그 / Rest API 등을 입력받아, Mongo / Elasticsearch / S3 / Treasure Data 등으로 전달하는 일을 주로 한다.

인터넷을 뒤져보면 더 많은 예제도 찾을 수 있다.


내가 해야 하는 작업은 아래와 같다.


1. 웹 어플리케이션에서 fluent-logger 를 통해

2. fluentd 서버로 메시지 전달

3. 수집된 로그를 가공하여 S3 로 전달

4. AWS Athena 에서 S3 데이터 쿼리


이번 포스트에서는 fluentd 에 관련된 2번, 3번을 설정 파일을 수정하여 해결해 보겠다.



S3 플러그인 설치


td-agent-gem 툴을 이용해 S3 플러그인을 설치한다.


# td-agent-gem install fluent-plugin-s3
Fetching: fluent-plugin-s3-0.8.2.gem (100%)
Successfully installed fluent-plugin-s3-0.8.2
Parsing documentation for fluent-plugin-s3-0.8.2
Installing ri documentation for fluent-plugin-s3-0.8.2
Done installing documentation for fluent-plugin-s3 after 0 seconds
cs


혹시나 아래와 같은 오류가 발생한다면...


Fetching: strptime-0.1.9.gem (100%)
Building native extensions.  This could take a while...
ERROR:  Error installing fluent-plugin-s3:
        ERROR: Failed to build gem native extension.
 
    /opt/td-agent/embedded/bin/ruby -r ./siteconf20170405-3324-1dj4g72.rb extconf.rb
checking for rb_timespec_now()... *** extconf.rb failed ***
Could not create Makefile due to some reason, probably lack of necessary
libraries and/or headers.  Check the mkmf.log file for more details.  You may
need configuration options.
cs


Development Tools 을 설치한다.


# yum groupinstall "Development Tools" "Legacy Software Development"
or
# apt-get install build-essential
cs



td-agent.conf 수정


설정 파일에는 fluent-logger 라이브러리를 통해 forward 타입으로 입력받을 것이고, fluentd 서버가 내부망에 있으므로 외부 접근은 걱정 없다.

forward 로부터 입력받은 이벤트 tag 별로, 별도의 디렉토리에 json 으로 메시지를 쌓고, 1시간에 1번씩 AWS S3 에 로그를 업로드 할 것이다.


# vi /etc/td-agent/td-agent.conf
 
<source>
    @type forward
</source>
 
<match fanbook.*>
    @type s3
 
    aws_key_id YOUR_AWS_KEY_ID
    aws_sec_key YOUR_AWS_SECRET_KEY
    s3_bucket YOUR_S3_BUCKET_NAME
    s3_region YOUR_S3_REGION
 
    path logs/${tag[1]}/%Y/%m/%d/
    s3_object_key_format %{path}%{time_slice}_%{index}.%{file_extension}
    store_as json
 
    <buffer tag,time>
        @type file
        path /var/log/td-agent/s3
        timekey 1h
        timekey_wait 10m
        timekey_use_utc true # use utc
    </buffer>
    <format>
        @type json
    </format>
</match>
cs



위 설정 파일을 간단하게 분석해 보자.

<match> 지시자에 out_s3 플러그인을 설정하였고, 그 아래에는 S3 키/버킷/리전 정보를 입력하였다.

path 파라미터에는 해당 버킷에 레코드가 저장될 경로를 입력한다.

태그/년/월/일을 뜻하는 ${tag}/%Y/%m/%d/ 문법은 v0.14 에서만 표시할 수 있다. v0.12 는 path / s3_object_key_format 에 이 문법을 지원하지 않는다.

오로지 이것 때문에 아직 안정화 되지 않은 v0.14 를 선택했다 ㅡ.ㅡ;

s3_object_key_format 은 업로드할 객체 키로 전체 경로를 입력한다.

store_as 는 어떤 파일로 S3 에 업로드 할 것인지를 결정한다. default 는 압축된 gzip 이지만, json 으로 설정했다. 이 값으로 %{file_extension} 가 결정된다.


output 을 담당하는 <match> 지시자 안에는, 이벤트를 버퍼링하도록 <buffer> 섹션을 넣을 수 있다.

버퍼링은 파일로 저장되도록 @type file 을 설정했다. @type 을 지정하지 않으면 defulat 인 memory 가 설정된다.

태그별로 파일을 만들고 시간 당 업로드를 실행할 계획이므로 <buffer tag,time> 라고 지정하였다.

path 는 버퍼링 파일이 저장될 경로이다.

timekey 는 파일을 생성하여 지정한 시간 동안 버퍼링을 받고 시간이 만료되면 새로운 파일로 버퍼링 처리를 반복한다.

timekey_wait 는 버퍼링 된 파일을 ouput 플러그인(s3) 에서 처리할 시기를 결정한다. 일반적으로 Fluentd 가 지연된 이벤트를 받을 수 있도록 대기시키는 시간이다.

timekey 가 1h 이고 timekey_wait 가 10m 라면, 1시간 10분에 업로드를 시작한다.


<format> 섹션은은 이벤트 형식이다. 기본적인 파일 타입은 out_file 로 예제에서 계속 보아왔던 time|tag|record(json) 이지만, time|tag 를 빼고 record 만 넣기 위해 json 으로 변경하였다.



테스트


input : 

echo '{"json":"message01"}' | /opt/td-agent/embedded/bin/fluent-cat test.hongs
echo '{"json":"message02"}' | /opt/td-agent/embedded/bin/fluent-cat test.hongs
cs


output :

S3/logs/hongs/2017/04/11/201704110320_0.json
 
{"json":"message01"}
{"json":"message02"}
cs




WRITTEN BY
손가락귀신
정신 못차리면, 벌 받는다.

,