20200405のAWSに関する記事は16件です。

ローカル(Docker)でDynamoDBの環境構築

概要

  • DynamoDBをローカル環境でテストするためのDockerを用いたDynamoDB localについて解説します
  • AWS CLIの設定などはすでに済んでいる前提なので、もしまだの場合ドキュメントを見てください
  • Dockerの基礎的な知識とDockerの設定などはすでに済んでいる前提なので、もしまだの場合ドキュメントを見てください
  • 対象読者はサクッとDynamoDBをローカルで試してみたいと考えている方です
  • この記事はdynamodb-localを参考にしています

DynamoDB localとは

DynamoDB localは開発者が自身の開発環境で動作しているDynamoDBのバージョンを用いて開発・テストできるように作成されダウンロードが可能にされたもの

DynamoDB localの利点

  • DynamoDB localの新しいDockerイメージでは、全てのDynamoDB localの依存関係とそれに必要な構成要素を組み込んだDockerイメージを用いることで、DynamoDB localを素早く開始することができる
  • DynamoDB localの新しいDockerイメージでは、コンテナ化されたビルドに継続的な統合テストの一部としてDynamoD localを含めることができる
  • DynamoDB localを使うためにインターネット接続は不要で、DynamoDB localは既存のDynamoDB APIで呼び出すことが出来る
  • DynamoDB localではプロビジョニングされたスループット、データストレージ、データ転送のコストがない

DynamoDB localのイメージをプル

$ docker pull amazon/dynamodb-local

立ち上げ

ただ立ち上げるだけ

$ docker run -p 8000:8000 amazon/dynamodb-local
  • ローカルの8000番ポートで立ち上げるだけ
  • Ctr+Cで止めることが出来る
  • 止めたあとは$ docker rm #{CONTAINER_NAME}で削除
    • $ docker ps -aで起動&停止中のコンテナを確認できる
  • コンテナを消すとデータは消える
  • --rmオプションをつけると、Ctr+Cで停止すると削除する必要なく自動で消える
  • -dオプションをつけるとバックグランドで実行してくれる
    • この場合、停止するために$ docker stop #{CONTAINER_NAME}を実行する必要がある

ローカルにデータをマウント(ボリューム)して立ち上げ

$ docker run -p 8000:8000 -v #{LOCAL_DYRECTORY}:/home/dynamodblocal/data amazon/dynamodb-local -jar DynamoDBLocal.jar -dbPath /home/dynamodblocal/data
  • #{LOCAL_DYRECTORY}にはdb情報を保存しておきたいディレクトリ名を指定
  • -dオプションと--rmオプションについては上記同様適宜
  • --rmオプションをつけなければ、停止(docker stop)させておきさえすれば、いつでも呼び出し(docker start)できる
  • #{LOCAL_DYRECTORY}ディレクトリに#{ACCESS_KEY}_#{REGION}.dbファイルが作成されたことを確認する
  • コンテナ名(#{CONTAINER_NAME})を命名したいときは--nameオプションを利用して--name #{CONTAINER_NAME}を追加
    • こうしておけばいちいち$ docker ps -aでコンテナ名を調べなくても$ docker stop #{CONTAINER_NAME}$ docker start #{CONTAINER_NAME}を実行できる

確認

$ aws dynamodb list-tables --endpoint-url http://localhost:8000
  • 上記コマンド(AWS CLI)で以下のようなテーブル情報が出てくれば接続ができている
{
    "TableNames": []
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Amazon API Gateway/ALBのバックエンドで動くLambda関数をJava(Eclipse+maven)で実装する

前提条件

初心者向け。Lambdaは知ってるけどWeb APIどうやって実装するんだろう、といったところから。

先に結論を書いておくと、Web APIなLambda関数をJavaで実装するのは非常に燃費が悪い感があるのでオススメできない。制約が無い限りは、node.jsとかpythonとかを使った方が断然楽だと思った。
※トラブルシューティングの面でも、Javaとその他のスクリプト言語では情報量が全然違うというか、その他のスクリプト言語ってたぶんJavaほどトラブらない気がする……

一応、環境はWindows10+Eclipse+Mavenなので、環境構築からする場合はこの辺の過去記事を見つつセットアップすると導入が早い。

あと、JDKは8か11にしておく。Lambdaは2020年4月時点ではまだJDK13のランタイムに対応していないのである。

サンプルプロジェクトからJarファイルを作成する

Eclipseのメニューで、ファイル→新規→プロジェクトのダイアログで、AWS Lambda Java Projectを選択。
プロジェクト名やMaven configurationは適当に決めて、Lambda Function Handlerの入力タイプでCustomを選択して完了ボタンを押すと、Lambda関数のサンプルプロジェクトが作られる。
サンプルプロジェクトにはJUnitのテストコードの雛形も入っているので便利!

Web APIを実装する場合はこのカスタムハンドラをいじっていくのが手っ取り早いっぽい。
ハンドラの詳細な紹介は以下。
【AWS公式】AWS Lambda における Java 関数ハンドラーの提供インターフェイスの使用
RequestHandlerのインプットとアウトプットを作り込んでいけば良い。

↑のハンドラのサンプルをベースにコードを書いたらプロジェクトを右クリックして、
- Maven clean
- Maven install
する。分かりにくいのでキャプチャを貼っておくと、↓こんな感じ。
キャプチャ.PNG

基本はこれでtargetフォルダ配下にjarファイルができてアップロードして動かせるはずなのだけど、この後の工程でClassNotFoundExceptionが出て上手くいかない場合は、以下を参考にしながらmaven-shade-pluginを使ったパッケージ作成を行う(キャプチャの「Mavenビルド...」のダイアログで、ゴールをpackage shade:shade指定する)。

【AWS公式】Java Lambda 関数を使用した「ClassNotFoundException」エラーのトラブルシューティング方法を教えてください。
【AWS公式】Eclipse を使用した .jar デプロイパッケージの作成

AWS SAM Localは必要か?

SAM Localは、ローカル環境でLambda関数を実行したり、DynamoDBに接続したりできるもの。
今回くらいのお試しだと、SAM Localの起動時間のオーバーヘッドの方が大きいので素直にアップロードしてテスト入力してみる方が早いけど、単体試験で回帰テストやるつもりならあっても良いかも。それとてJUnitでも良い気はするけど……

参考は↓ここ。

【Developers.IO】[新ツール]AWS SAMをローカル環境で実行できるSAM Localがベータリリース

でも罠があって、npmでインストールできるSAM Localはバージョンが古くて、Java11のランタイムに対応していない。Java11ランタイムで動かしたいのであれば、SAM CLIを使う。

【AWS公式】AWS SAM CLI のインストール

Docker Toolboxでインストールしたsam.exeのパスと、↑の手順でインストールしたsam.cmdのパスが違うので、設定を変えておくのを忘れないように。先にインストールしたからか、自分の環境ではDocker Toolboxのパスが優先されてしまうようだ。
Eclipseのメニューのウィンドウ→設定でダイアログを開き、AWSツールキット→AWS SAM Localでパスを設定できる。

キャプチャ2.PNG

前節に書いたClassNotFoundExceptionはここでも起きるので、出た場合は↑のmaven-shade-pluginを試してみる。

また、ローカルのDockerサーバに接続できないようなエラーが出る場合は、環境変数の設定が必要かもしれない。この辺を試しているときには色々なエラーが出ていて、何が効果があったのかが良く分からなくなってしまった。↓参考までに環境変数に関する説明。

【MAGELLAN Dev Center】Docker Toolbox の使い方

Lambda関数の作成

さて、ここまでくればあとは普通にLambda関数を作るだけ。
「一から作成」で、ランタイムをJava8か11か使っている方にして、適切なロール(S3とCloudWatchLogsにアクセスできるもの)を設定する。
↑で作成したMavenのパッケージであればzipする必要はないので、そのままS3にアップロードして、「Amazon S3からのファイルのアップロード」でJarファイルのURLを貼り付ける。
ハンドラは、サンプルそのままであるならcom.amazonaws.lambda.demo.LambdaFunctionHandler::handleRequestを設定する。
[ファイルパス][ハンドラのクラスファイル名]::[ハンドラ]な感じだ。

適当にテストを流して、期待した結果が得られたならファーストステップはクリアだ!

Amazon API Gateway/ALBのバックエンドで動かす

で、ここからが問題。
どうやらAPI GatewayもALBも、定型のJSONをレスポンスしないと、Lambdaプロキシを通せなかったり、ALBのヘルスチェックでエラーになってしまう。
テストを通ったとしても、Web APIとしては実用できないのである。

【AWS公式】API ゲートウェイの「不正な Lambda プロキシ応答」エラー、または 502 ステータスコードを解決するにはどうすればよいですか?
【AWS公式】ターゲットとしての Lambda 関数

どちらにも書いてあるように、↓こんな感じのJSONである。

{
    "isBase64Encoded": true|false,
    "statusCode": httpStatusCode,
    "headers": { "headerName": "headerValue", ... },
    "body": "..."
}

しかもこれ、Lambda関数が良い感じにBodyだけ取り出しちゃったりするので、LambdaのテストやSAM Localじゃデバッグできない。API Gatewayの方は適当にPOJOのResponseオブジェクトを作ってみたら動いたが、ALBの方はよりstrictにチェックするらしく、皆目見当もつかない。

こんなのどうしたらええねん……と思って調べてみると、↓こんな記事が。
【cloudpack.media】JavaでLambda関数を書いてSAMでデプロイしてみた

今回はこれを参考に動かしてみて、Amazon API Gateway/ALBのどちらでも動作することが確認できた。

さて、次回からは応用編。このアプリケーションでALB+LambdaのCI/CDパイプラインを作ってみよう。

その他

文字コードに関するwarningがうるさいときに

↓これ読んだ。
【情報科学屋さんを目指す人のメモ】Eclipse+Maven:「Using platform encoding (MS932 actually) to copy filtered resources, …」エラーの対策方法

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

EC2について

EC2とは

インスタンス単位で任意のAZにインスタンスを立ち上げてサーバーとして利用できるサービス

特長

・柔軟性が高い(短時間で使いたいサーバーを立てることができる)
・世界中のリージョンから選択できる
・セキュリティグループでトラフィックの制御できる
・AMIを使うことで同じサーバーをいくつも起動できる

インスタンス作成の流れ

  1. OSイメージの選択
  2. インスタンスタイプの選択
  3. ストレージの選択
  4. セキュリティグループの設定
  5. SSHキーペアの選択

OSイメージの選択

以下のOSから選択可能
・AMI(AWSが提供)
・Windows
・Mac
・Linux
・カスタムAMI(自作)

※データはS3内に保存される

インスタンスタイプの選択

スクリーンショット 2020-04-05 19.40.35.png

タイプの項目のt2,t3はファミリー(種類)と世代(バージョン)とを表しており、後半は大きさを表す。
下に行けば行くほどスペックが良いがその分値段も上がるので用途に適したタイプを選択することが大切

また、インスタンスの細かい設定もできる。
インスタンスの詳細設定の項目で重要度が高いであろう設定項目は以下である。

・ネットワークとサブネット(VPCとの紐付け)
スクリーンショット 2020-04-05 19.50.29.png

※VPCのについては別途記事書きます

スクリーンショット 2020-04-05 20.00.06.png
・終了保護の有効化
 →本番環境で誤ってインスタンスを終了させないため
・モニタリング
 →CloudWatchのモニタリングを有効にすることによってインスタンスの状況を可視可できる

・AMIの設定
スクリーンショット 2020-04-05 20.04.08.png
頻繁に使用するコマンドまたは各インスタンスに共通のソフト(Apache等)を入れたい時に
AMIを設定することによってインスタンスを立ち上げた際に設定したコマンドを実行してくれる。
※別途記事作成予定

ストレージの選択

スクリーンショット 2020-04-05 20.09.01.png

ストレージの種類と容量を選択できる
種類には画像の3パターンある
・汎用SSD(gp2)
 →小〜中規模のDB。仮想デスクトップ等

・プロビジョンド IOPS SSD(io1)
 →高いI/O性能を必要とするもの。大規模DB等

・マグネティック(standard)
 →旧世代ボリューム。基本は使用しない

セキュリティグループの設定

スクリーンショット 2020-04-05 20.13.58.png

初期表示ではSSH接続が許可されており、SSHでログインするにはキーペアが必要になる
また、特定の通信を許可したい場合はセキュリティグループに接続方法とIPアドレスの範囲を指定したルールを追加する

SSHキーペアの選択

スクリーンショット 2020-04-05 20.21.38.png
インスタンスの接続に必要になるキーペアを設定する
無くさないようにしっかり管理することが大切

※インスタンスのログイン・SSHのBASHコマンド操作については別途記事作成予定

関連サービスリンク

・VPC
・データベース各種
・CloudWatch
・AutoScalling
・セキュリティグループ

などなど

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Python] Headless Chrome を AWS Lambda で動かす

  1. serverless-chromeをダウンロードして解凍
  2. chromedriverをダウンロードして解凍

  3. 実行権限付与

    • chmod 777 <ダウンロードしたファイル>
  4. chrome フォルダにダウンロードしたファイルを入れて、zipに固める

  5. Lambdaレイヤーにzipをアップロード

  6. Lambdaレイヤーにseleniumをアップロード

  7. Lambda関数に上記2つのレイヤーを追加

  8. 以下のようなコードで実行できる

from selenium import webdriver
from selenium.webdriver.chrome.options import Options

def lambda_handler(event, context):
    options = Options()
    options.binary_location = '/opt/chrome/headless-chromium'
    options.add_argument('--headless')
    options.add_argument('--no-sandbox')
    options.add_argument('--single-process')
    options.add_argument('--disable-dev-shm-usage')

    browser = webdriver.Chrome('/opt/chrome/chromedriver', chrome_options=options)
    browser.get('https://www.google.com')
    title = browser.title
    browser.close()
    browser.quit()

    return {"title": title}

参考

https://blog.ikedaosushi.com/entry/2018/12/22/231421
https://qiita.com/nabehide/items/754eb7b7e9fff9a1047d

環境

macOS 10.15.4
Python 3.7.7

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PycharmでAWS CLIのプロファイルを切り替える

概要

Pycharm + AWS Toolkitで開発をしている時に、別アカウントの環境(CLIでプロファイル設定済み)に切り替えたいことがありました。ソースに変更を加えずに環境を切り替えたかったのですが、環境変数を設定するやり方が楽だったので紹介します。

方法

Edit Configurations > Environment variables から設定します。
日本語化してる人は、「構成の編集 > 環境変数」です。
スクリーンショット 2020-04-05 19.53.22.png

設定する環境変数は以下の2つです。それぞれに利用したいプロファイル名を設定します。

AWS_DEFAULT_PROFILE
AWS_PROFILE

(例)user01というプロファイル名を使う場合
スクリーンショット 2020-04-05 19.41.20.png

これで、ソースの実行時に環境変数で設定したプロファイルが利用されます。

余談

Pycharmの画面右下にプロファイルが表示されていますが、これをクリックして変更しただけだと切り替わらない?もよう...
これだけで切り替わるとさらに楽になるのですが...
スクリーンショット 2020-04-05 20.02.33.png

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ECRにdockerをプッシュする時に、credentialsのエラーが出たときの対処

AWS ECRにdockerをプッシュする時、テンプレートのコピペだと上手くいかないところがあったのでメモ。

環境

aws-cli/2.0.6
AWSのプロファイル設定済み

エラー内容

テンプレートをそのままコピペすると以下のコマンドになります。

$ aws ecr get-login-password --region [region] | docker login --username AWS --password-stdin [account].dkr.ecr.[region].amazonaws.com/[repositry name]

が、上記のコマンドを実行しても、以下のようなエラーが出てしまいます。

Unable to locate credentials. You can configure credentials by running "aws configure".
Error: Cannot perform an interactive login from a non TTY device

解決方法

AWSのcredentialは設定済みだったのですが、そのプロファイル名を自分で指定してあげる必要がありました。そのためには、コマンド前半の--region [region]--profile [profile name]に変更する必要がありました。

$ aws ecr get-login-password --profile [profile name] | docker login --username AWS --password-stdin [account].dkr.ecr.[region].amazonaws.com/[repositry name]

これで、リポジトリにプッシュできるようになりました。([profile name]には、自分で設定しているプロファイル名を入れます)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Python] pip3のパッケージを AWS Lambda 上でimportできるようにする

AWS Lambda レイヤーにパッケージ用のレイヤーを追加する。

  1. パッケージを配置したフォルダを作成

    pip3 install -t python/lib/python3.7/site-packages <パッケージ名>
    
    • python3.7 の箇所は適宜置き換える
  2. python フォルダをzipに固める

  3. レイヤーにアップロード

  4. パッケージを利用したい関数にレイヤーを追加する

  5. import <パッケージ名> で使えるようになる

参考

https://blog.ikedaosushi.com/entry/2018/12/22/231421

環境

macOS 10.15.4
Python 3.7.7

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

コロナ患者数が都が確保してる病床数超えたから自宅療養する人が増えるだろうから一人で不安だしなんかあった時の為に助けを求めるボット作った。

はじめに

コロナウイルスに感染した患者さんの数が東京都が確保してる病床数を超えたから自宅療養しなければいけない人が増えると思って、見守りボットを作りました。

テストで公開なのでLINEは一旦無料プランなのですが、利用者が万が一増えたら有料にしようかなと思ってます。しかし、なにせ素人なものでサーバー代とLINEプッシュ代でいくらかかるのか・・・。悪戯だけはこないで欲しいと願う。。

とりあえずシェア
775sszex.png

コードとか使い方は追々。。。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Cloudtrailのログを、awsマネージドではないsparkのmetastoreに登録する方法

はじめに

awsマネージドなSpark/hive環境であれば、 com.amazon.emr.hive.serde.CloudTrailSerde あたりを利用してmetastoreにテーブルを登録することができます。
https://docs.aws.amazon.com/ja_jp/athena/latest/ug/cloudtrail-logs.html
しかし、 com.amazon.emr.hive.serde.CloudTrailSerde はawsマネージドではないSpark/hiveでは利用できません。

本記事では、databricksなどの awsマネージドではないSpark環境 において、cloudtrailのログをmetastoreに登録する方法を説明します。

注意

  • region/year/month/dayのpartitionが利用可能なようにcreate tableしています。
    • partitionは手動で追加する必要があります。手順参照。
  • hive上では、本手順では動きません。
    • とはいえ、ちょっとした変更で動くと思う

手順

tableの定義

以下のqueryで CREATE TABLE をします。 {} 内は適宜書き換えてください。
{table_path} は、一般的に s3a://{CloudTrail_bucket_name}/AWSLogs/{Account_ID}/CloudTrail/ の形式になります。

列名/objectのkey名は大文字小文字を適切に指定しないと読み込まないので注意( 本日の落とし穴 )。

sparksql
CREATE TABLE {db}.{table}
(
  Records ARRAY<
    STRUCT<
      additionalEventData:STRING,
      awsRegion:STRING,
      errorCode:STRING,
      errorMessage:STRING,
      eventID:STRING,
      eventName:STRING,
      eventSource:STRING,
      eventTime:STRING,
      eventType:STRING,
      eventVersion:STRING,
      readOnly:STRING,
      recipientAccountId:STRING,
      requestID:STRING,
      requestParameters:STRUCT<
        acl:STRING,
        bucketName:STRING,
        clusterId:STRING,
        databaseName:STRING,
        durationSeconds:INT,
        encryption:STRING,
        encryptionContext:STRING,
        expression:STRING,
        filterSet:STRING,
        instancesSet:STRING,
        name:STRING,
        nextToken:STRING,
        partitionInput:STRING,
        partitionValueList:STRING,
        partitionValues:STRING,
        pattern:STRING,
        policy:STRING,
        resourcesSet:STRING,
        roleArn:STRING,
        roleSessionName:STRING,
        spotInstanceRequestIdSet:STRING,
        stateValue:STRING,
        tableName:STRING,
        tagSet:STRING,
        versioning:STRING,
        volumeSet:STRING
      >,
      resources:ARRAY< STRUCT<
          ARN:STRING,
          accountId:STRING,
          type:STRING
        >
      >,
      responseElements:STRUCT<
        _return:STRING,
        assumedRoleUser:STRUCT<
          arn:STRING,
          assumedRoleId:STRING,
          credentials:STRUCT<
            accessKeyId:STRING,
            expiration:STRING,
            sessionToken:STRING
          >,
          requestId:STRING,
          spotInstanceRequestSet:STRING
        >
      >,
      serviceEventDetails:STRING,
      sharedEventID:STRING,
      sourceIPAddress:STRING,
      userAgent:STRING,
      userIdentity:STRUCT<
        accessKeyId:STRING,
        accountId:STRING,
        arn:STRING,
        invokedBy:STRING,
        principalId:STRING,
        sessionContext:STRING,
        type:STRING,
        userName:STRING
      >,
      vpcEndpointId:STRING
    >
  >,
  region STRING,
  year STRING,
  month STRING,
  day STRING
)
USING json
PARTITIONED BY (region, year, month, day)
LOCATION {table_path}
;

viewの定義

logを直接覗くとわかるのですが、Cloudtrailのlogは以下のようなjsonになっています:

{"Records":[
  {"eventVersion":"1.05",...},
  {"eventVersion":"1.05",...},
]}

これは、Records内にすべての行と列が内包されており、扱いが面倒です。

そこで、この問題を解決するviewを予め定義しましょう。
以下のqueryで CREATE VIEW をします。

sparksql
CREATE OR REPLACE VIEW {db}.{view}
AS
  SELECT 
    Record.*,
    region,
    year,
    month,
    day
  FROM
    (
      SELECT
        explode(Records) AS Record,
        region,
        year,
        month,
        day
      FROM
        {db}.{table}
  )
;

partitionの追加

ディレクトリ(prefix)が {partition_key}={partiton_val} の形式ではないため、 MSCK REPAIR TABLE を利用できません。
そのため、ALTER TABLE ADD PARTITION を使用します。
{partition_path} は、一般的に s3a://{CloudTrail_bucket_name}/AWSLogs/{Account_ID}/CloudTrail/{region}/{year}/{month}/{day}/ の形式になります。

ALTER TABLE {db}.{table} ADD IF NOT EXISTS
  PARTITION (region={region}, year={year}, month={month}, day={day})
  LOCATION {partition_path};
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

S3バケット別概算コスト算出ツールを作ってみた

はじめに

S3って安価なストレージなので、容量気にせずとりあえずなんでも格納して放置なんてことが多いですよね?
将来的に分析するかもってことでデータレイクに蓄積して結果使わないなんてことも多い。(my観測範囲)

で、新型コロナウィルスの影響で先行き不安な状況なので、収束まではどうにか支出を抑えて凌ぎたい。。
という人も多いと考え、S3バケットごとに要否判断(&不要であれば削除)しやすいように、S3バケットごとに
使用量/概算コストを簡単に一覧表示するツールを作ったので、紹介します。

ツール概要

一言で言えば、
全リージョン/全バケット別にオブジェクト数/使用バイト数/概算月額料金を出力するツール
です。(誰かが作ってそうなんですが探してもなかったので作りました)

CloudWatchからバケットサイズなど取得できるようになってるので、それを実行すればawscliでも取得できる
のですが、リージョン指定しないといけないとか、メトリクス/ディメンションを指定するのがダルイので
そこらへんうまくやってくれるツールが欲しいなぁと思ったのがモチベーションです。
あと、請求コンソールだとバケット別の使用量はわからないので。

補足(&免責事項)

  • コスト算出について
    • 実行時点の利用量を1ヶ月間継続した場合の概算請求額であり正確ではありません(ご利用は自己責任で)
    • ストレージ保存量に対して課金される料金が対象であり、それ以外(リクエスト/転送量)のコストは含みません
    • 概算請求額は東京リージョン料金(2020/04時点)で算出します
    • 料金通貨は米ドル
  • バージョニングについて
    • 以前のバージョンのオブジェクトやそのサイズもカウントされます
    • ツール結果とaws s3 ls --recursive等で得られるオブジェクト数は異なります

要はざっくりでいいのでバケットごとにかかる料金を知りたいって人がターゲットです

実行方法

前提

  • AWSクレデンシャル情報(~/.aws/credentials)が設置されていること

Go言語がインストールされている環境の場合

GO111MODULE=off go get -v github.com/miyaz/s3usage
cd $(go env GOPATH)/src/github.com/miyaz/s3usage
go run main.go

それ以外の環境

こちらから環境に応じたzipファイルをダウンロードし
展開後 s3usageというバイナリを実行します

オプション

  • -p {プロファイル名}
    • ~/.aws/credentials に記載されているプロファイル名
    • 指定なし時(デフォルト)はdefault
  • -v
    • ストレージタイプ毎の使用量/概算コストも表示したい場合に指定

実行例

./s3usage -p prod
normal.png

./s3usage -v -p prod
verbose.png

おわりに

コロナに負けるな!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

DevOps

AWS DevOps自分用攻略本作成中

スケーリング

Q:200以上のスレッド処理している際にパフォーマンス低下、対応策は?

A:各インスタンスにスクリプトを追加して、同時セッション数を検出、セッション数が5分で200を超えている場合は、インスタンスにAutoScalingグループの必要な容量を1つ増やす。

Q:コンプライアンス問題を自動的検出、修正する方法は?

A:コンプライアンス変更通知をSNSトピックに発行するCWatchEventルールを作成する。メッセージチャネルHTTPSエンドポイントをSNSトピックにサブスクライブする。非準拠のセキュリティ構成に対処するLambda関数を記述、SNSトピックにサブスクライブする。

Q:VPCで新しいAWSの機能をテストする場合の最短方法は?

A:開発リクエストに応じて、ステージングVPCでEC2を起動し、構成管理を使用してアプリをセットアップ。テストハーネスを実行してアプリの機能を確認して、SNSで開発チームに結果を通知。

Q:DynamoDBからLambdaでデータを読み取る、Lambdaはテストが成功したら開発者によってデプロイされる。

A:CodePipelineを使用してテストOKな後にパイプラインをトリガーするポストコミットフックを設定、CodeDeployを使用して、トラフィックの割合と間隔を指定するカナリアデプロイ構成を作成。

Q:Cforamtionスタックが削除された後にRDBのスナップショットが作成できるようにするには?

A:CFormationテンプレートの削除ポリシーを使用し、RDBのスナップショットが作成されるようにする。

Q:RDS MySQLに読み取り競合がある場合の最良アプローチは?

A:各AZで実行されているECacheインメモリーキャッシュをデプロイする。各AZにRDSリードレプリカを追加する。

Q:アプリログの長期保存が求められている、スタッフはすぐにログを表示させたい。現状はS3に1時間ごとにアーカイブされている、どのような方法で迅速に表示されるようにできる?

A:S3ライフサイクルポリシーを更新して、古いログをGlacier、サービスを使用もしくは作成してCWatchLogsエージェントを使用してアプリログをCWatchLogsにストリーミングする。

Q:OpsWorksでリモートリポジトリから新しいインスタンスごとにアプリとクックブックをデプロイしている。上司がQAで承認されていないコードで異なるアプリバージョンを提供しているインスタンスがあることに気づいた。なぜ?

A:チームの誰かがコードを別々のブランチにコミットし、OpsWorksが新しいインスタンスを起動した。

Q:AWSでカナリアテストをするためには?アプリはAutoScalingグループにデプロイする必要があり、ClassicLBを使う必要がある。

A:ブルーグリーン別にClassicLBとAutoScalingグループを作成し、Route53を使用し、CLBで加重Aレコードを作成する。

Q:サードパーティサプライとの注文を処理する単一EC2がある。注文はSQSキューから取得され、5minごとにバッチ処理、処理の遅延は1h以内というビジネス要件あり。週に3回アプリが失敗し、停止するので手動で再起動している。これの回復力を上げるためには?

A:AutoScaling起動設定を作成して、グループを最小値、最大値1の起動設定を使用する。

A:アプリを変更して、インスタンスIDのディメンションをもつかすたむCWatchメトリクスを送信する。メトリクスが10min間動作しない場合にCWatchアラームを作成し、EC2アクションを実行してインスタンスを終了する。

Q:
A:
Q:
A:
Q:
A:
Q:
A:
Q:
A:
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

DynamoDB queryパラメータまとめ

はじめに

今さらだがDynamoDBのクエリパラメータについてまとめる。
既に他の人が同じような記事を書いているかもしれないが、
AWSのドキュメントを読んでいて、
「分かりづらいから一覧でまとめてくれ!!」と思い
あくまで自分のためのメモとして残す。
他のDB操作パラメータについては必要が出てきたら追記するつもり。

Query Parameters

TableName

  • 文字通りテーブル名を指定する

KeyConditionExpression

  • プライマリーキー(パーティションキー、ソートキー)、セカンダリーインデックスに対する条件式を記述する
  • DynamoDB予約語でなければ、後述のExpressionAttributeNamesでプレースホルダーを指定しなくてもそのまま使える
    • image.png
    • :main, :subはExpressionAttributeValuesで宣言する
    • 属性値のプレースホルダーにはコロンが必要

ExpressionAttributeNames

  • KeyConditionExpressionで使う属性名のプレースホルダーを定義する
  • オブジェクト文字列で指定する
  • #を接頭辞としてつけるのが必須
    • image.png
    • この場合、ViewsがDynamoDBの予約語であるため、別名を指定しなければならないという事情もある

ExpressionAttributeValues

  • KeyConditionExpressionで使う属性値のプレースホルダーを定義する
  • オブジェクトで指定
  • 接頭辞としてコロン:をつけるのが必須
    • image.png
    • 条件式自体が文字列であるため、そのまま書くと文字列か数値かが判別できないため、型定義(S: 文字列、N:数値)が必要なのだと思われる

FilterExpression

  • テーブルからのクエリ実行後に、APIとしての戻り値をフィルタリングするための条件式を書く
  • ExpressionAttributeNames, ExpressionAttributeValuesで定義したプレースホルダーを使って、KeyConditionExpressionと同様にして書ける

ProjectionExpression

  • クエリ結果に対して、出力される属性を指定したい場合に使う
    • image.png

その他

DynamoDB DocumentClient

  • ExpressionAttributeValuesで指定していた型宣言が不要になる
  • 上記の例だと、次のようになる
    • image.png
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS LambdaのCustom Runtimeを使い、Node.js v8などEoLとなったランタイムを動かす

はじめに

Node.js、バージョンアップの足がかなり早いですよね。
AWS Lambdaにおけるランタイムサポート期間も、これにあわせてハイテンポになっています。

ちゃんとバージョンアップをしろというご意見は重々承知の上ではありますが、
Node.js v8.10でLambda Functionを使い続けざるを得ない場合に、カスタムランタイムを使ってEoLとなったランタイムを動かし延命処置を図ります。

動作確認環境

  • Arch Linux (2020.04.04)
  • Docker 19.03.8-ce
  • aws-cli 1.18.36

カスタムランタイムの使い方

カスタムランタイムの仕様については、公式ドキュメントが詳しいので割愛します。

カスタムランタイムを使用するには、デプロイパッケージあるいはLayerに、node実行ファイルと、ハンドラー関数を起動するためのbootstrap実行ファイルを含める必要があります。
今回は既存のLambda Functionを使用することを想定していますので、関数のデプロイパッケージには手を加えずLambda Layerでランタイムを読み込ませます。

Lambda Layerの作成には、以下のリポジトリを活用させていただきます。
https://github.com/lambci/node-custom-lambda

こちらのリポジトリにはNode v10、v12のファイルが含まれています。(2020/04/04現在)
今回はこのリポジトリをフォークし、v8.10用のファイルを追加することでLayerを作成します。

bootstrapについては、CとJavascriptで書かれたものがそれぞれv12.x/bootstrap.cv12.x/bootstrap.jsにあります。(v10.xも同様)

bootstrap.c(をコンパイルしたbootstrap)がまずAWS Lambdaによって起動され、これがbootstrap.jsスクリプトをカスタムランタイムのNodeで実行します。
bootstrap.jsは、AWS Lambda ランタイムインターフェイスから関数の呼び出しイベントの受け取り、デプロイパッケージのスクリプト実行、実行結果のPOSTを行います。

上記リポジトリのbootstrap.jsはNode v8.10でも問題なく動くので、
必要な変更点はLambda Layerに含めるnode実行ファイルをv8.10のものに変更するだけとなります。

カスタムランタイムの作成

前節で紹介したリポジトリをクローンするところからはじめます。
Dockerが必要となります。

$ git clone https://github.com/lambci/node-custom-lambda.git
$ cd node-custom-lambda

v12.xのディレクトリを元に、v8.10のディレクトリを作成します。

$ cp -r v12.x v8.10
$ cd v8.10

v12.xのLayerファイルを削除しておきます。

$ rm layer.zip

このプロジェクトでは、Docker上でbootstrap.cのビルドとNodeのダウンロードを行います。

config.shを編集し、Nodeのバージョンを指定します。

config.sh
< export NODE_VERSION=12.16.1
---
> export NODE_VERSION=8.10.0

ビルドします。

$ ./build.sh

v8.10/layer.zipファイルが出来上がります。
これを解凍すると以下のようなファイルが入っています。

layer
├── bin
│   └── node
├── bootstrap
└── bootstrap.js

Nodeのバージョンを確認しておきます。

$ ./layer/bin/node -v
v8.10.0

テストが用意されていますので、実行してみます。

$ ./test.sh

以上でカスタムランタイムのLayerが完成しました。

カスタムランタイムのデプロイ

リポジトリにはpublish.shが用意されていますが、このスクリプトは全てのリージョンにデプロイされてしまいます。
今回はap-northeast-1にのみデプロイできればよいので、AWS CLIを使って手動でLayerを作成します。

まず、作成したレイヤーのファイル(layer.zip)を任意のS3にアップロードします。

aws s3api put-object --bucket ${BUCKET_NAME} --key nodejs/8.10.0/layer.zip --body layer.zip --output json

次に、Lambda Layerを作成します。
ここではCloudFormationで作成します。

template.yml
AWSTemplateFormatVersion: 2010-09-09

Parameters:
  S3BucketName:
    Description: A S3 bucket name contains layer.zip
    Type: String

Resources:
  Nodejs8Runtime:
    Type: AWS::Lambda::LayerVersion
    Properties:
      Content:
        S3Bucket: !Ref S3BucketName
        S3Key: nodejs/8.10.0/layer.zip
      Description: Layer for Node.js 8.10.0 Custom Runtime
      LayerName: custom-runtime-nodejs-8

Outputs:
  Nodejs8RuntimeLayerARN:
    Description: A lambda layer ARN of Node.js 8.10.0 Custom Runtime
    Value: !Ref Nodejs8Runtime
    Export:
      Name: !Sub ${AWS::StackName}-runtime-nodejs8

Stackを作成。

aws cloudformation create-stack --stack-name dev-lambdalayers-nodejs \
--template-body file://template.yml \
--parameter ParameterKey=S3BucketName,ParameterValue=${S3_BUCKET_NAME}

Layerができていることを確認します。

$ aws lambda list-layer-versions --layer-name custom-runtime-nodejs-8
{
  "LayerVersions": [
    {
      "LayerVersionArn": "arn:aws:lambda:ap-northeast-1:123456789012:layer:custom-runtime-nodejs-8:1",
      "Version": 1,
      "Description": "Layer for Node.js 8.10.0 Custom Runtime",
      "CreatedDate": "2020-04-04T16:59:31.629+0000"
    }
  ]
}

Lambda Functionの作成とテスト

上記で作成したカスタムランタイムをテストします。

Lambda FunctionはServerless Frameworkを使用して作ることにします。
provider.runtimeprovidedを指定することでカスタムランタイムを使用できます。
Lambda Layerは、先程のCloudformation StackのOutputをインポートしてARNを指定します。

serverless.yml
service: test-lambda-function

provider:
  name: aws
  runtime: provided
  stage: dev
  region: ap-northeast-1

functions:
  hello:
    handler: handler.hello
    layers:
      - 'Fn::ImportValue': dev-lambdalayers-nodejs-runtime-nodejs8

関数のコードはシンプルに、実行しているNode.jsのバージョンを返すだけです。

handler.js
module.exports.hello = async event => {
  return process.version;
};

これを実行して、v8.10.0という文字列が帰ってきたら成功です。

$ sls invoke -f hello
"v8.10.0"

注意点

  • AWS公式のNode.jsランタイムにはaws-sdkが含まれていますが、この方法で作成したカスタムランタイムにはいずれのnpmパッケージも含まれていません。

おわりに

以上でNode v8を使用するLambda Functionの延命措置ができました。
同様の方法で、Node v6、v4も動かすことが可能です。

しっかりバージョンアップしていくのがベストであることは言うまでもありませんが、
node-gypなどネイティブモジュールはバージョンアップで動かなくなることも多々ありますので、とりあえずの措置には使えるかと思います。

また今回使用したコードはすべて以下リポジトリにアップしています。
https://github.com/uhey22e/node-custom-lambda

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Amazon Route53 サポートされるDNSレコードタイプ

A(アドレスコード)

  • A(Address)レコードは、IPv4でホスト名とIPアドレスの関連づけを定義するレコードです。「www」などのホスト名を入力し、VALUEにグローバルIPアドレスを入力することによって、ご利用いただくサーバーへの名前解決が行われます。
  • MXレコード、CNAMEレコード、NSレコードを設定するには、あらかじめ各レコードのVALUEに入力するホスト(FQDN)をAレコードとして設定されていることが必要です。

AAAAレコード(IPv6アドレスコード)

  • AAAAレコードは、IPv6でホスト名とIPアドレスの関連づけを定義するレコードです。

CNAMEレコード

  • CNAMEレコードは正規ホスト名に対する別名を定義するレコードです。
  • 特定のホスト名を別のドメイン名に転送する時などに利用します。

※正規ホスト名はAレコードが登録されている必要があります。
※特定のファイルやサブディレクトリを指定する事はできません。
※ホスト名なしのCNAMEレコードは登録することができません。

例)ドメイン名:mugicha.comの場合
【設定可能】
ホスト名:www
VALUE:www.ryokucha.com と入力
http://www.mugicha.com/にアクセスするとhttp://www.ryokucha.com/に転送されます。

【設定不可】
ホスト名:空白
VALUE:www.ryokucha.com と入力
http://mugicha.com/にアクセスしhttp://www.ryokucha.com/に転送される設定はできません。
※ホスト名を空白にて設定したい場合、IPアドレスをご確認のうえ、Aレコード設定をご利用ください。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS CDKを使えばTypeScriptでAWSインフラを定義できるらしい

AWS CDKなるものを使うとコードでインフラを実装できるらしいです。
やってみます。

What Is the AWS CDK?

使い慣れたプログラミング言語でAWSリソースを定義できるもの。

サポート言語は
- TypeScript
- JavaScript
- Python
- Java
- C#/.NET
とのこと。

CDKの利点

個人的な利点は
- プログラムなのでCFnと比べてレビューの敷居が下がる
- 動的なインフラ定義の幅が広がる(予想)

やってみる

というわけで、やってみます。

環境

  • VSCode
    • 1.43.2
  • node.js
    • v12.11.0
      • aws-cdk
        • 1.31.0
      • TypeScrippt
        • 3.8.3
  • aws-cli
    • 1.18.31

環境構築参考:
AWS CDK Intro Workshop > 前提条件

作りたい構成

良さげなサンプルソースがあったので参考にして以下のような構成になればいいなと思ってます。

Untitled Diagram.jpg
aws-samples/aws-cdk-examples/ecs-service-with-advanced-alb-config

準備

プロジェクトを作成します。

cdk init sample-app --language typescript

libディレクトリに構築に使用するファイルが作成されます。

主にこのファイルをいじいじしていくらしいです。

.ts
import * as sns from '@aws-cdk/aws-sns';
import * as subs from '@aws-cdk/aws-sns-subscriptions';
import * as sqs from '@aws-cdk/aws-sqs';
import * as cdk from '@aws-cdk/core';

export class ElbSampleStack extends cdk.Stack {
  constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const queue = new sqs.Queue(this, 'ElbSampleQueue', {
      visibilityTimeout: cdk.Duration.seconds(300)
    });

    const topic = new sns.Topic(this, 'ElbSampleTopic');

    topic.addSubscription(new subs.SqsSubscription(queue));
  }
}

TypeScriptなのでファイル監視をします。

npm run watch

とりあえずきれいにします。

.ts
import * as cdk from '@aws-cdk/core';

export class ElbSampleStack extends cdk.Stack {
  constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
    super(scope, id, props);
  }
}

必要なcdkモジュールをインストールします。

npm i @aws-cdk/aws-elasticloadbalancingv2
npm i @aws-cdk/aws-ecs

実装

.ts
import * as cdk from '@aws-cdk/core';

// 使うモジュール
import * as elb from '@aws-cdk/aws-elasticloadbalancingv2';
import * as ec2 from '@aws-cdk/aws-ec2';
import * as ecs from '@aws-cdk/aws-ecs';

export class ElbSampleStack extends cdk.Stack {
  constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    // Create a cluster
    const vpc = new ec2.Vpc(this, 'cdk-sample-vpc', { maxAzs: 2 });

    const cluster = new ecs.Cluster(this, 'cdk-sample-cluster', { vpc });
    cluster.addCapacity('DefaultAutoScalingGroup', {
      instanceType: ec2.InstanceType.of(
        ec2.InstanceClass.T2,
        ec2.InstanceSize.MICRO
      )
    });

    // Create Task Definition
    const taskDefinition = new ecs.Ec2TaskDefinition(
      this,
      'cdk-sample-taskDef'
    );
    const container = taskDefinition.addContainer('web', {
      image: ecs.ContainerImage.fromRegistry('amazon/amazon-ecs-sample'),
      memoryLimitMiB: 256
    });

    container.addPortMappings({
      containerPort: 80,
      hostPort: 8080,
      protocol: ecs.Protocol.TCP
    });

    // Create Service
    const service = new ecs.Ec2Service(this, 'cdk-sample-service', {
      cluster,
      taskDefinition
    });

    // Create ALB
    const lb = new elb.ApplicationLoadBalancer(this, 'cdk-sample-elb', {
      vpc,
      internetFacing: true
    });
    const listener = lb.addListener('PublicListener', { port: 80, open: true });

    // Attach ALB to ECS Service
    listener.addTargets('ECS', {
      port: 80,
      targets: [
        service.loadBalancerTarget({
          containerName: 'web',
          containerPort: 80
        })
      ],
      // include health check (default is none)
      healthCheck: {
        interval: cdk.Duration.seconds(60),
        path: '/health',
        timeout: cdk.Duration.seconds(5)
      }
    });

    new cdk.CfnOutput(this, 'LoadBalancerDNS', {
      value: lb.loadBalancerDnsName
    });
  }
}

デプロイ

CDKを元にCFnを作成します。

cdk synth

デプロイに使用するS3バケットを作成します。

cdk bootstrap

CloudFormationを使用してS3バケットが作成されるようです。

スクリーンショット 2020-04-05 4.21.27.png

デプロイします。

cdk deploy

デプロイに関する変更の確認を求められます。
スクリーンショット 2020-04-05 4.26.27.png

yでデプロイが実施されるのでログを眺めながら待ちます。

作成されました。
スクリーンショット 2020-04-05 4.34.55.png
AWSコンソールでも確認できます。
スクリーンショット 2020-04-05 4.42.36.png

出力されたDNSにアクセスしてみます。
スクリーンショット 2020-04-05 4.35.49.png

成功しました。

後処理

デプロイしたリソースもコマンドで消せるようなので消しておきましょう。

cdk destroy

スクリーンショット 2020-04-05 4.44.00.png
yで削除が実施されますのでログを見て待ちます。。

削除されたようです。

スクリーンショット 2020-04-05 4.49.01.png

めでたし。

感想

AWS CDKが話題になっていたのでなんとなく試してみましたが、CFnに比べて学習コストが2段階くらい下がったような気がします。

ただ、ソース量を見れば分かる通り定義していないものもうまい具合に作成してくれるようなので、意図しないリソースが作成されないように注意しなければいけないと感じました。

参考

【コードでインフラ定義】CDKという異次元体験をさくっとやるのに便利なAWS公式Workshopの紹介 | Developers.IO

AWS Cloud Development Kit (AWS CDK) ワークショップ

aws-samples/aws-cdk-examples/ecs-service-with-advanced-alb-config

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

EC2 Instance Connectでブラウザベースの接続ができない時の対処

Instance Connectのブラウザベースの接続

普段は自分のPCからsshでEC2のinstanceにログインしていましたが、
Instance Connectのブラウザベース接続というものがあると知りました。
これを使うとインターネット環境があればどこでも自由にEC2のinstanceへログインできるそうです。

そこで以下を参考にして、Instance Connectのブラウザベースで接続を試みました。
EC2 Instance Connect のセットアップ

Screenshot from 2020-04-04 15-26-14.png

すると、画面が黒いまま何も表示されない・・
Screenshot from 2020-04-04 15-26-25.png

そして、結局、接続失敗!!
Screenshot from 2020-04-04 15-49-55.png

ローカルPCからはsshでC2にログインできるのに、
Instance Connectのブラウザベースでは接続が失敗しました。

試行錯誤した結果、この場合の確認ポイントは以下の3点でした。

1.セキュリティグループのインバウンドの設定

対象のEC2インスタンスのセキュリティグループのインバウンドの設定に以下を追加する必要があります。
タイプ:ssh
プロトコル:TCP
ポート範囲:22
ソース:カスタム 3.112.23.0/29
Screenshot from 2020-04-04 10-05-40.png

2.IAM(ユーザ)の設定

consoleにloginするIAM(ユーザ)に、以下のポリシーをアタッチします。

設定項目と設定値
Region:ap-northeast-1
AWS accountID:000000000000
InstanceID:i-99999999999999999
Osuser:ubuntu(AmazonLinux2のデフォルトのユーザー名はec2-user、Ubuntuの場合はubuntu)

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "ec2-instance-connect:SendSSHPublicKey",
            "Resource": "arn:aws:ec2:ap-northeast-1:000000000000:instance/i-99999999999999999",
            "Condition": {
                "StringEquals": {
                    "ec2:osuser": "ubuntu"
                }
            }
        },
        {
            "Effect": "Allow",
            "Action": "ec2:DescribeInstances",
            "Resource": "*"
        }
    ]
}

3.instance側の設定

対象のinstanceにsshでログインして、以下を実行します。
sudo apt-get update
sudo apt-get install ec2-instance-connect

4つの新しいファイルが作成されていることを確認します。

$ ls /usr/share/ec2-instance-connect/
eic_curl_authorized_keys
eic_harvest_hostkeys
eic_parse_authorized_keys
eic_run_authorized_keys

これでブラウザベースのInstance Connectができるはずです!!


これでも解決しない場合

なお、ここまでやっても接続できない場合、
以下のように許可の範囲を一時的に広げて確認してみましょう。

  • セキュリティグループのインバウンドのIPの範囲を「0.0.0.0/0」まで広げてみる
  • consoleにloginするuserのIAMにAmazonEC2FullAccessのポリシーをアタッチしてみる

EC2 Instance Connectはインターネットが繋がれば、どこからでもEC2のinstanceにログインできるため、事前に準備してログインできるようにしておくと何かあった時に役立つと思います。

参考

AWS IP Address Ranges
EC2 Instance Connect の特徴や注意点についてまとめてみる

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む