takafumi blog

日々の勉強メモ

fluent-plugin-s3 設定と IAMロール アクションの関係と注意

環境   fluent-plugin-s3 1.6.0

関係性のまとめ

アクション 関係ある設定 説明
s3:ListBucket check_bucket true trueであればfluent起動時にbucket存在をチェック。存在しなければ起動をリトライする
falseだと起動はするが書き込み時に失敗する
s3:GetObject check_object true trueであれば同じobjectがあると上書きせずエラーになる
falseだと同名objectは上書きされる
s3:PutObject 書き込み全般

つまり check_bucket check_objectfalse ならアクションは最小限の PutObject のみでよい

check_object false の注意

check_object false の時も s3_object_key_format が同名にしない事で不用意な上書きを防ぐ事ができる。

s3_object_key_format を設定していなければ、デフォルト値自体が上書きされにくいフォーマットに自動で変更される。

(s3_object_key_format "%{path}/%{date_slice}_%{hms_slice}.%{file_extension}" になる)

さらに uuid_flush hex_random index hostname などを使えばより重複を防止し安全に扱える。

  • uuid_flush
    • bufferがフラッシュされる際、常にUUIDに置き換えられる
    • 書き込み失敗のリトライ時も新しいUUIDに置き換えられる
  • hostname
    • ホストネームに置き換えられるので、 %s%{hms_slice} などと組み合わせると重複を防止できる
  • hex_random :
    • bufferに使われるhashで置き換えられる
    • 例えば同名エラーでリトライしたときも同じ値が使われる
    • %{index} を組み合わせればリトライ時は %{index} が更新される

fluent-plugin-s3 v0.12 の注意

正確には fluent-plugin-s3 < 1.0.0rc7 のとき。

この時 check_object false だと s3_object_key_format が固定されるので注意

github.com

check_bucket trueAPIリクエストにかかる料金の注意

check_bucket は起動時のチェックなので、 true でもほとんどAPIリクエストはしない。

しかし check_objecttrue だと書き込みの度にAPI GetObject を発行する。

そのため、特に書き込み頻度が高い場合は、APIリクエストが常に GetObject -> PutObject と二重に発生し料金が嵩むので注意。

Scalaでもtry-catchを使う方がよい事もある

環境   Scala 2.13.6

Scalaだと例外を常にTryで処理しがちなので、反省メモ。

もちろん単純に例外出す可能性ある個所をTryで包むのは問題ない。
困るのはTry[Either[L, R]] みたいなネストを避けたい時。

refinedとかでよくある。
例えば以下は単純にStringEither[String, PosInt]に変換している。

import cats.implicits._
import eu.timepit.refined.api.RefType
import eu.timepit.refined.types.numeric.PosInt
import scala.util.Try

def s2pi(s: String): Either[String, PosInt] =
  Try(s.toInt).toEither
    .leftMap(_.getMessage)
    .flatMap(RefType.applyRef[PosInt](_))

val etPiR = s2pi("1") // Right(1)
val etPiL = s2pi("a") // Left(For input string: "a")

読みずらい!
まあ正直言うと、このくらいならまだマシで、実際コード書いているともっと複雑になる事も多々ある。

こういうTryとEitherのネストを避けようとするなら、以下のように書くのが断然読みやすい。

import eu.timepit.refined.api.RefType
import eu.timepit.refined.types.numeric.PosInt
import scala.util.Try

def s2piVer2(s: String): Either[String, PosInt] =
  try RefType.applyRef[PosInt](s.toInt)
  catch { case t: Throwable => Left(t.getMessage) }

val etPiV2R = s2piVer2("1") // Right(1)
val etPiV2L = s2piVer2("a") // Left(For input string: "a")

ドメイン駆動開発(DDD)とCleanArchitectureのエンティティはどう違うか?

双方と共にビジネスモデル定義のためのオブジェクトという点では共通だが、実際には意味は大幅に異なる。

DDDでは

以下のように定義される

「エリック・エヴァンスのドメイン駆動開発 第5章」から

主として同一性によって定義されるオブジェクトはエンティティと呼ばれる。

としている。

つまりDDDではビジネスモデルの中でも、常に同一である事を保証できるオブジェクトととして定義されるものがエンティティである。

CleanArchitectureでは

クリーンアーキテクチャ(The Clean Architecture翻訳)」から

エンティティーは、大規模プロジェクトレベルのビジネスルールをカプセル化する。エンティティは、メソッドを持ったオブジェクトかもしれない、あるいは、データ構造と関数の集合かもしれない。

とある。
これはDDDで言うところの、エンティティ、値オブジェクト、ドメインサービス、リポジトリ(の仕様)、ファクトリ, 集約などのドメインレイヤー全体を指している。

まとめ

回答としては「DDDにおけるエンティティ」と「CleanArchitectureにおけるエンティティ」は全く異なる定義なので、使うアーキテクチャに合わせてコードを書こう。

もっと言えば - 書いているコードのアーキテクチャが、何を採用しているか? - 各用語はどういう定義なのか?

をきちんと理解して書こうという事になる。

プロジェクトによってはCleanArchtectureだけどentityをdomainというpackage名にしたりで、一見DDD風に見える事もあったり、そもそも全く異なる意味で使っている場合もある。
プロジェクトに参加したときはビジネス的な言葉、DDDでいうユビキタス言語だけではなく、こういったコード上の意味合いも慎重に確認する事が大切。

aws sam (Serverless Application Model)に入門する

Infrastructure as Code入門としてaws samでLambdaを作るところからはじめてみる

aws-cliaws-samはinstall済み

sample templateから基本的なアプリケーションをビルト

仕組み的には以下のような順序で作成される

  1. ローカルでsamコマンドでアプリケーションを作成
  2. ローカルからsam deployでcloudformationを呼び出しアプリケーションをデプロイする
  3. S3へテンプレートが保存される
  4. API GatewayとLambdaが作成される

f:id:takafumi-s:20210808235600p:plain

aws samのアプリケーションを作成

$ sam init

# まずはawsのtemplateを選択して使う
# template一覧は以下
Which template source would you like to use?
        1 - AWS Quick Start Templates
        2 - Custom Template Location
Choice: 1


# パッケージはシンプルに
# lambda用のファイルはzipで固め
# cloudformationでS3にアップロードされる
#
# 2のイメージはECR周りの知識が必要なのでそのうち試す
What package type would you like to use?
        1 - Zip (artifact is a zip uploaded to S3)
        2 - Image (artifact is an image uploaded to an ECR image repository)
Package type: 1

# go1.xで作っていく
Which runtime would you like to use?
        1 - nodejs14.x
        2 - python3.8
        3 - ruby2.7
        4 - go1.x
        5 - java11
        6 - dotnetcore3.1
        7 - nodejs12.x
        8 - nodejs10.x
        9 - python3.7
        10 - python3.6
        11 - python2.7
        12 - ruby2.5
        13 - java8.al2
        14 - java8
        15 - dotnetcore2.1
Runtime: 4

# cloudformation, API Gatewayの名前
# S3, Lambdaのprefixとして使われる
Project name [sam-app]:

Cloning from https://github.com/aws/aws-sam-cli-app-templates

# まずは単独のLambdaで作るので1を選択
AWS quick start application templates:
        1 - Hello World Example
        2 - Step Functions Sample App (Stock Trader)
Template selection: 1

    -----------------------
    Generating application:
    -----------------------
    Name: sam-app
    Runtime: go1.x
    Dependency Manager: mod
    Application Template: hello-world
    Output Directory: .

    Next steps can be found in the README file at ./sam-app/README.md

この段階でgolangのLambdaに適した構成が作成される
template.yamlはcloudformationと共通フォーマット
将来的にaws samの範囲より拡張された場合はcloudformationで使う事もできる

$ tree sam-app
sam-app
├── Makefile
├── README.md
├── hello-world
│   ├── go.mod
│   ├── go.sum
│   ├── main.go
│   └── main_test.go
└── template.yaml

1 directory, 7 files

main.goは以下のようにLambdaでGetリクエストを受ける基本的な構成になっている

package main

import (
        "errors"
        "fmt"
        "io/ioutil"
        "net/http"

        "github.com/aws/aws-lambda-go/events"
        "github.com/aws/aws-lambda-go/lambda"
)

var (
        // DefaultHTTPGetAddress Default Address
        DefaultHTTPGetAddress = "https://checkip.amazonaws.com"

        // ErrNoIP No IP found in response
        ErrNoIP = errors.New("No IP in HTTP response")

        // ErrNon200Response non 200 status code in response
        ErrNon200Response = errors.New("Non 200 Response found")
)

func handler(request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
        resp, err := http.Get(DefaultHTTPGetAddress)
        if err != nil {
                return events.APIGatewayProxyResponse{}, err
        }

        if resp.StatusCode != 200 {
                return events.APIGatewayProxyResponse{}, ErrNon200Response
        }

        ip, err := ioutil.ReadAll(resp.Body)
        if err != nil {
                return events.APIGatewayProxyResponse{}, err
        }

        if len(ip) == 0 {
                return events.APIGatewayProxyResponse{}, ErrNoIP
        }

        return events.APIGatewayProxyResponse{
                Body:       fmt.Sprintf("Hello, %v", string(ip)),
                StatusCode: 200,
        }, nil
}

func main() {
        lambda.Start(handler)
}

アプリケーションをbuild

$ sam build
Building codeuri: ./sam-app/hello-world runtime: go1.x metadata: {} functions: ['HelloWorldFunction']
Running GoModulesBuilder:Build

Build Succeeded

Built Artifacts  : .aws-sam/build
Built Template   : .aws-sam/build/template.yaml

Commands you can use next
=========================
[*] Invoke Function: sam local invoke
[*] Deploy: sam deploy --guided

ローカルでアプリケーションを実行

sam local start-apiと実行する事で、ローカルでsamでbuildしたAPIを実行できる

$ sam local start-api
Mounting HelloWorldFunction at http://127.0.0.1:3000/hello [GET]
You can now browse to the above endpoints to invoke your functions. You do not need to restart/reload SAM CLI while working on your functions, changes will be reflected instantly/automatically. You only need to restart SAM CLI if you update your AWS SAM template
2021-08-08 23:37:49  * Running on http://127.0.0.1:3000/ (Press CTRL+C to quit)

この状態でリクエストを投げると、dockerイメージが作成&実行され、レスポンスを返す

$ curl http://127.0.0.1:3000/hello
Hello, 11.111.111.111

sam local start-apiのログ

Invoking hello-world (go1.x)
Image was not found.
Building image......................................................................................................
Skip pulling image and use local one: amazon/aws-sam-cli-emulation-image-go1.x:rapid-1.27.2.

Mounting ./sam-app/.aws-sam/build/HelloWorldFunction as /var/task:ro,delegated inside runtime container
START RequestId: fa806430-6835-4b88-acd6-4e1b857bcd4a Version: $LATEST
END RequestId: fa806430-6835-4b88-acd6-4e1b857bcd4a
REPORT RequestId: fa806430-6835-4b88-acd6-4e1b857bcd4a  Init Duration: 0.07 ms  Duration: 1010.38 ms    Billed Duration: 1100 ms        Memory Size: 128 MB     Max Memory Used: 128 MB
No Content-Type given. Defaulting to 'application/json'.
2021-08-08 23:43:07 127.0.0.1 - - [08/Aug/2021 23:43:07] "GET /hello HTTP/1.1" 200 -
$ docker ps
CONTAINER ID   IMAGE                                                   COMMAND                  CREATED         STATUS        PORTS                      NAMES
fa8dda6a0f15   amazon/aws-sam-cli-emulation-image-go1.x:rapid-1.27.2   "/var/rapid/aws-lamb…"   2 seconds ago   Up 1 second   127.0.0.1:6436->8080/tcp   vigorous_allen

アプリケーションをawsへデプロイする

--guided をつけると会話形式で設定できる

$ sam deploy --guided

Configuring SAM deploy
======================

        Looking for config file [samconfig.toml] :  Not found

        Setting default arguments for 'sam deploy'
        =========================================
        Stack Name [sam-app]:
        AWS Region [ap-northeast-1]:
        #Shows you resources changes to be deployed and require a 'Y' to initiate deploy
        Confirm changes before deploy [y/N]:
        #SAM needs permission to be able to create roles to connect to the resources in your template
        Allow SAM CLI IAM role creation [Y/n]:
        HelloWorldFunction may not have authorization defined, Is this okay? [y/N]: y
        Save arguments to configuration file [Y/n]:
        SAM configuration file [samconfig.toml]:
        SAM configuration environment [default]:

        Looking for resources needed for deployment: Not found.
        Creating the required resources...
        Successfully created!

                Managed S3 bucket: aws-sam-cli-managed-default-samclisourcebucket-xarl3ki5fc62
                A different default S3 bucket can be set in samconfig.toml

        Saved arguments to config file
        Running 'sam deploy' for future deployments will use the parameters saved above.
        The above parameters can be changed by modifying samconfig.toml
        Learn more about samconfig.toml syntax at
        https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-config.html

Uploading to sam-app/7ad10cf1c8bd04b9d2c1ad18dd4297c0  4328737 / 4328737  (100.00%)

        Deploying with following values
        ===============================
        Stack name                   : sam-app
        Region                       : ap-northeast-1
        Confirm changeset            : False
        Deployment s3 bucket         : aws-sam-cli-managed-default-samclisourcebucket-xarl3ki5fc62
        Capabilities                 : ["CAPABILITY_IAM"]
        Parameter overrides          : {}
        Signing Profiles             : {}

Initiating deployment
=====================
Uploading to sam-app/17fb0b7caff4b6a85cdca466c28c1236.template  1154 / 1154  (100.00%)

Waiting for changeset to be created..

CloudFormation stack changeset
----------------------------------------------------------------------------------------------------
Operation    LogicalResourceId                           ResourceType                   Replacement
----------------------------------------------------------------------------------------------------
+ Add        HelloWorldFunctionCatchAllPermissionProd    AWS::Lambda::Permission        N/A
+ Add        HelloWorldFunctionRole                      AWS::IAM::Role                 N/A
+ Add        HelloWorldFunction                          AWS::Lambda::Function          N/A
+ Add        ServerlessRestApiDeployment47fc2d5f9d       AWS::ApiGateway::Deployment    N/A
+ Add        ServerlessRestApiProdStage                  AWS::ApiGateway::Stage         N/A
+ Add        ServerlessRestApi                           AWS::ApiGateway::RestApi       N/A
----------------------------------------------------------------------------------------------------

Changeset created successfully. arn:aws:cloudformation:ap-northeast-1:111111111111:changeSet/samcli-deploy1628426562/db1f0927-131d-4bf1-9b12-aacf949eacda


2021-08-08 21:42:54 - Waiting for stack create/update to complete

CloudFormation events from changeset
------------------------------------------------------------------------------------------------------------------------------
ResourceStatus         ResourceType                   LogicalResourceId                           ResourceStatusReason
------------------------------------------------------------------------------------------------------------------------------
CREATE_IN_PROGRESS     AWS::IAM::Role                 HelloWorldFunctionRole                      -
CREATE_IN_PROGRESS     AWS::IAM::Role                 HelloWorldFunctionRole                      Resource creation Initiated
CREATE_COMPLETE        AWS::IAM::Role                 HelloWorldFunctionRole                      -
CREATE_IN_PROGRESS     AWS::Lambda::Function          HelloWorldFunction                          -
CREATE_IN_PROGRESS     AWS::Lambda::Function          HelloWorldFunction                          Resource creation Initiated
CREATE_COMPLETE        AWS::Lambda::Function          HelloWorldFunction                          -
CREATE_IN_PROGRESS     AWS::ApiGateway::RestApi       ServerlessRestApi                           -
CREATE_IN_PROGRESS     AWS::ApiGateway::RestApi       ServerlessRestApi                           Resource creation Initiated
CREATE_COMPLETE        AWS::ApiGateway::RestApi       ServerlessRestApi                           -
CREATE_IN_PROGRESS     AWS::Lambda::Permission        HelloWorldFunctionCatchAllPermissionProd    -
CREATE_IN_PROGRESS     AWS::Lambda::Permission        HelloWorldFunctionCatchAllPermissionProd    Resource creation Initiated
CREATE_IN_PROGRESS     AWS::ApiGateway::Deployment    ServerlessRestApiDeployment47fc2d5f9d       -
CREATE_COMPLETE        AWS::ApiGateway::Deployment    ServerlessRestApiDeployment47fc2d5f9d       -
CREATE_IN_PROGRESS     AWS::ApiGateway::Deployment    ServerlessRestApiDeployment47fc2d5f9d       Resource creation Initiated
CREATE_IN_PROGRESS     AWS::ApiGateway::Stage         ServerlessRestApiProdStage                  -
CREATE_IN_PROGRESS     AWS::ApiGateway::Stage         ServerlessRestApiProdStage                  Resource creation Initiated
CREATE_COMPLETE        AWS::ApiGateway::Stage         ServerlessRestApiProdStage                  -
CREATE_COMPLETE        AWS::Lambda::Permission        HelloWorldFunctionCatchAllPermissionProd    -
CREATE_COMPLETE        AWS::CloudFormation::Stack     sam-app                                     -
------------------------------------------------------------------------------------------------------------------------------

CloudFormation outputs from deployed stack
-----------------------------------------------------------------------------------------------------------
Outputs
-----------------------------------------------------------------------------------------------------------
Key            HelloWorldFunctionIamRole
Description    Implicit IAM Role created for Hello World function
Value          arn:aws:iam::111111111111:role/sam-app-HelloWorldFunctionRole-MGSTGE9WCIMR

Key            HelloWorldAPI
Description    API Gateway endpoint URL for Prod environment for First Function
Value          https://aaaaaaaaaa.execute-api.ap-northeast-1.amazonaws.com/Prod/hello/

Key            HelloWorldFunction
Description    First Lambda Function ARN
Value          arn:aws:lambda:ap-northeast-1:111111111111:function:sam-app-HelloWorldFunction-pMOSVaxRjbuN
-----------------------------------------------------------------------------------------------------------

Successfully created/updated stack - sam-app in ap-northeast-1

これにより API Gateway経由でLambdaを呼び出せるようところまで作成される

デプロイ結果の確認

OutputsにあるHelloWorldAPIへアクセスしてみると、レスポンスが返ってくる

$ curl - 'https://aaaaaaaaaa.execute-api.ap-northeast-1.amazonaws.com/Prod/hello/'
HTTP/2 200
content-type: application/json
content-length: 22
date: Sun, 08 Aug 2021 13:41:16 GMT

Hello, 11.111.111.111

参考

docs.aws.amazon.com

docs.aws.amazon.com

awsデータストレージまとめ(インスタンスストア・EBS・EFS・S3)

各ストレージ

インスタンス ストア

Elastic Block Store (EBS)

  • ブロックレベルストレージ
  • インスタンスから独立してデータを永続化する
  • 起動しているインスタンスにアタッチ(追加)できる
  • 稼働中にサイズ変更、ボリュームタイプの変更など可能
  • インスタンスとEBSは同じAZにある必要がある

Elastic File System (EFS)

  • マネージドなサーバーレスなファイルレベルストレージ
  • 最大サイズ16TiB(=tebibyte=1024GB)
  • EC2でNFSとしてマウント可能
  • オートスケール
  • リージョン内のAZを跨いでマウント可能
    • ただし複数のVPCに接続はできない

Simple Storage Service (S3)

  • オブジェクトレベルストレージ
  • インターネットストレージとしてURLでアクセス可能
  • 無制限のストレージ容量
  • 各オブジェクトは最大5TB
  • バケットはリージョン単位で保存される
  • 各オブジェクトのバージョニングによる変更追跡
  • 少なくとも3つのAZにデータが保存(バックアップ)される
  • 複数のストレージクラスによる料金プラン
  • 各オブジェクトにURLがあり静的webページとしても使える

ストレージ形式について

ファイルレベルストレージ

  • ファイルストレージ
  • ファイルベース・ストレージ

ファイルによるデータ管理。
階層構造化が容易で、一意のデータパスによりファイルにアクセスする。
データにはファイル名、作成日時などの最低限のメタデータが付与される。

ブロックレべルストレージ

ディスクを論理ボリュームへ分割し、論理ボリュームをさらにブロックへ分割。
データをブロック単位で保存する(ブロックより大きなデータは複数ブロックへ保存される。連続性はない)

ボリューム、ブロック両方にIDが振られボリュームID+ブロックIDでデータを特定する事で高速なデータアクセスが可能。

ブロック単位での差分更新が可能なため、巨大なデータの更新が高速で行える。

メタデータは基本的にはない、もしくは非常に限定的。

オブジェクトレベルストレージ

  • オブジェクトストレージ
  • オブジェクトベース・ストレージ

一つのデータを一つのオブジェクトとして保存する。
オブジェクトは一意のIDを持ち、インデックス化され高速にアクセスできる。

多彩なメタ情報が付与され、セキュリティポリシーやアクセス権限に至るまで様々な情報を持つ事ができる。

通常はフラットな構造になるが、S3のようにバケット入れ子構造にできるサービスもある。

一度書き込まれたオブジェクトは変更できず、オブジェクトは全体を一度で書き込む必要がある。
そのため巨大なデータを更新する場合などは、巨大なデータを再度全て書き込み、過去データを削除(もしくは履歴として残す)ような運用になる。

参考

docs.aws.amazon.com

docs.aws.amazon.com

docs.aws.amazon.com

docs.aws.amazon.com

aws アクセスコントロール系モジュール概要

どのモジュールがどの位置にあるのかを忘れがちなので簡易まとめ

概要図

f:id:takafumi-s:20210803234137p:plain

クラウド単位

  • AWS Cloud
    • ようはAWSの事だが、概要図ではAZの同意味で使われている
    • リージョン
      • 地理的に離れた領域
    • アベイラブルゾーン(AZ)
      • リージョン内で複数の独立した場所、IDC
    • AWS Outputs
      • めったに使わないがオンプレミスなどでAWSのサービスを運用できる仕組み
  • Virtual Private Cloud(VPC)
    • 論理的に区切られた仮想ネットワーク上の空間
    • 通常他のVPCへは直接通信できない
    • AWS Transit Gatewayなどを使ってAZやリージョンをまたぐ複数VPC構成を作る事も可能だが今回はスルー
  • サブネット
    • いわゆるサブネットによるVPCの分割空間
    • EC2はサブネット内に配置する

外部との接続

ACLとセキュリティグループの違い

  • ACL
    • サブネットのファイアウォールとして動作
    • ステートレス
    • インバウンドとアウトバウンドのトラフィックを全てチェックする(=ステートレス)
    • デフォルトでは全てのパケット通行が許可されている
  • セキュリティグループ

つまり以下のようにチェックされる f:id:takafumi-s:20210803233348p:plain

参考

docs.aws.amazon.com

docs.aws.amazon.com

docs.aws.amazon.com

docs.aws.amazon.com

docs.aws.amazon.com

docs.aws.amazon.com

docs.aws.amazon.com