CloudFormation であら便利に静的サイトを作る

cloudpack あら便利カレンダー 2017 の18日目です。

AWS で静的ウェブサイトといえば S3 + CloudFront ですが、その構成を CloudFormation で作る際にあら便利な Tips を入れていこうという記事です。

とりあえず起動してみる

AWS コンソールにログインした状態で、このリンクから Stack を起動できます。

Stack が起動したら、 OutputsURL にアクセスすれば CloudFront/S3 に置かれた静的ページが閲覧できるはずです。

このテンプレートが置いてあるバケットは誰でも List/Get できるので、

$ aws s3 sync s3://cfn-staticweb-example/ .

すれば CloudFormation テンプレートとカスタムリソース用の Lambda ファンクションのソースが入った zip が落ちてきます。そのあとに aws cloudformation create-stack コマンドで起動するもよし、内容を見るも改造するもよしです。

さらに、このファイル一式を生成しているコードは y13i/cfn-staticweb-example にあります。 Serverless Framework を使いつつ、 serverless deploy はせず、 serverless package して出力されたファイルを一部改変して S3 バケットに上げるということをしています。

CFn でどんなあら便利があるの?

CloudFront の利用する・しないの切り替え

CloudFront Distribution は作成にかなり時間が掛かります。このテンプレートでは CloudFront を使わないことも選べるようにしました。

CloudFormation の Conditions を使い、 UseCloudFront という Parametertrue の時のみ Distribution を作成します。さらに S3 Bucket Policy で読み取りを許可する参照元は

  • UseCloudFronttrue

    • CloudFront からのみ可(Origin Access Identity を使用)
  • UseCloudFrontfalse

    • どこからでも可

切り替わるようにしています。

CloudFront の各種パラメーターの切り替え

ACM (AWS Certificate Manager) で発行した証明書を AcmCertificateArn パラメーター指定した場合は Distribution で ViewerCertificate として使用するようにしています。

指定しない場合(値が空文字)の場合は AWS::NoValue 擬似要素を使って、そもそも ViewerCertificate が指定されていないように振る舞うようになっています。

同様に、 Distribution Alias も Parameter で指定された場合のみ設定するテンプレートになっています。

Origin Access Identity の作成

Origin Access Identity は普通には CloudFormation で作成できません!

なんで対応していないのか疑問に思うところですが、文句を言ってもしかたないので Custom Resource使って Lambda Function に作成させています。

インデックスドキュメントの生成

CloudFormation でサポートされている S3 のリソースは BucketBucketPolicy のみです。バケットの内容物は CloudFormation では管理できません。

しかし、前項と同様にカスタムリソースを使えばどうにでもなります。

ここでは、 Parameter で受け付けた IndexDocumentTitleIndexDocumentBodyejs テンプレートに流し込むという処理をして HTML を生成し、バケットに置くという処理をさせてみました

まとめ

Lambda とその周辺環境がそこそこ枯れてきたことで、 Lambda-backed Custom Resource がだいぶ使いやすくなったと感じます。つまり、 CloudFormation の使い勝手も増しているということです。

地味ながら進化を続ける CFn をあら便利に使っていきましょう。

続きを読む

CodeStarバンザイ!数クリックで始めるCI/CDパイプライン

CodeStarを使ってCI/CDパイプラインを構築してみたので、紹介します。

CodeStarとは?

AWSのマネージドサービスであるCodePipeline、CodeCommit、CodeBuild、CodeDeployを使ってCI/CDパイプライン+実行環境をさくっと構築してくれるサービスらしい。

https://aws.amazon.com/jp/codestar/

よしっ、動かしてみるぞ!!

1.プロジェクト作成

マネジメントコンソールからCodeStarを選び、「start a project」をポチッ。
するとプロジェクトのテンプレートを選択する画面が表示される。

カテゴリは、Web application、Web service、Alexa Skill、Static Websiteから、
言語は、Ruby、Node.js、Java、Python、PHP、HTML 5から、
実行環境は、Beanstalk、EC2、Lambdaから選択できる。
※もちろん存在しない組み合わせもあります。

今の時代っぽいWeb Application×Node.js×Lambdaなんてのも選択できるんですね。

うーん、ここはCodeBuildも使ってみたいし「Web Application×Java Spring×EC2」を選択。

使う1.png

そして、プロジェクト名を入力。インスタンスタイプを設定して。ポチッ、ポチッ。

使う2.png

。。。
はいっ、CI/CDパイプライン構築の作業はこれで終わり。

そして、待つこと10分。
CodePipeline、CodeCommit、CodeBuild、CodeDeployと、これを統合的に確認するためのダッシュボードがいい感じにできちゃいました。
もちろんjavaのwebアプリケーションも起動しています。

ダッシュボード

使う4修正.png

CodePipeline

使う8.png

CodeCommit

使う5.png

CodeBuild

使う6.png

CodeDeploy

使う7.png

デプロイされたjavaのwebアプリケーション

12.png

2.CI/CDパイプライン実行

ここからが本番。
gitへの接続情報をIAMで確認(ユーザ⇒認証情報⇒AWS CodeCommit の HTTPS Git 認証情報「生成」)し、code commit上のソースコードをcloneする。
するとこんなものが落ちてきます。

tree
.
├── README.md
├── appspec.yml
├── buildspec.yml
├── pom.xml
├── scripts
│   ├── install_dependencies
│   └── start_server
└── src
    └── main
        ├── java
        │   └── com
        │       └── aws
        │           └── codestar
        │               └── projecttemplates
        │                   ├── HelloWorldAppInitializer.java
        │                   ├── configuration
        │                   │   ├── ApplicationConfig.java
        │                   │   └── MvcConfig.java
        │                   └── controller
        │                       └── HelloWorldController.java
        ├── resources
        │   └── application.properties
        └── webapp
            ├── WEB-INF
            │   └── views
            │       └── index.jsp
            └── resources
                ├── gradients.css
                ├── set-background.js
                ├── styles.css
                └── tweet.svg

ふむふむ、なるほど。
ここは手っ取り早くCI/CDパイプラインを確認するため、index.jspをちょこっと修正。
そして、code commitにpush。
code commit上で、変更した内容も確認できます。

使う11.png
すると。。。

使う9.png

パイプラインが動き出したーーー
どうやら動きとしては、こんなことをやっているみたい。

  • Source : 新たしいコードがpushされると、code commitからソースコードを取得しS3に格納。
  • Build : S3からソースコードを取得しビルド。そしてビルドしたモジュールをS3に格納。
  • Application : S3に格納されたモジュールをEC2にデプロイ。

待つこと5分。デプロイまで成功。
そして、先程の画面を確認してみると。。。

使う10.png

変わった!
簡単!!

これを使えば

  • Java、Rubyなどメジャーな言語を利用したCI/CDパイプラインを爆速で構築できる。
  • Jenkinsから開放される。

がしかし。。

  • 東京リージョンにはまだ来ていない。
  • CodeStarというかcode commit側の問題になるが、pull requestが使えない。。

本番用のアプリケーション開発環境・実行環境として利用するのは、まだまだ難しいような気もしますが、
pocくらいであればこれで十分かもしれませんね。

続きを読む

Amazon Kinesis Streamの監視方法

Kinesis Streamのシャードには以下のような制限がある。

  • 書き込み 1シャード 最大1秒あたり1,000レコードまたは1MBまで
  • 読み込み 1シャード 最大1秒あたり5回の読み込みまたは2MBまで

シャードへの流入・流出量を上記の値を超えないように・または超えたことを検知できるように
監視していかないといけない。

そのため、CloudWatchでのKinesisの監視項目をまとめる。

どのメトリクスを見るべきか

CloudWatchでどういうメトリクスが見れるかは公式ドキュメントに記載がある。
http://docs.aws.amazon.com/ja_jp/streams/latest/dev/monitoring-with-cloudwatch.html

その中でも以下の項目を見ると、シャードを増やすしきい値が見えてきそう。

WriteProvisionedThroughputExceeded

シャードの書き込み上限の超過が発生しているか否かを判定するのなら、この項目を見ればよい。
これは指定された期間中にストリームの上限超過が発生した件数が出力される。

これが定常的に1以上になった場合は、シャードを増やしたほうが良さそう。

AWS CLIでの取得方法

profile=hoge
stream_name=hoge

aws --profile ${profile} cloudwatch get-metric-statistics 
--namespace AWS/Kinesis 
--metric-name WriteProvisionedThroughputExceeded 
--start-time 2017-06-20T12:00:00 
--end-time 2017-06-20T13:00:00 
--period 60 
--statistics Maximum 
--dimensions Name=StreamName,Value=${stream_name}

ReadProvisionedThroughputExceeded

これは上記に記載した「WriteProvisionedThroughputExceeded」の読み込み版。
こちらも合わせて見たい。

PutRecords.Bytes / PutRecords.Records

これは指定期間内にストリームに送信されたバイト/レコード数を確認することが出来る項目。
周期の最小単位が1分となっているため、指標として使うならこの値を使って1秒あたりの数値に計算しなおすとわかりやすい。

この項目を見ることにより、シャードの上限の超過の発生前に事前にシャードを増やすことができる。

PutRecordというのもあるが、KPLを使用している場合はPutRecordsを使う。

AWS CLIでの取得方法

profile=hoge
stream_name=hoge

aws --profile ${profile} cloudwatch get-metric-statistics 
--namespace AWS/Kinesis 
--metric-name PutRecords.Bytes 
--start-time 2017-06-22T12:00:00 
--end-time 2017-06-22T12:05:00 
--period 60 
--statistics Sum 
--dimensions Name=StreamName,Value=${stream_name} | jq .
response
{
  "Datapoints": [
    {
      "Unit": "Bytes",
      "Timestamp": "2017-06-22T12:02:00Z",
      "Sum": 4777677
    },
    {
      "Unit": "Bytes",
      "Timestamp": "2017-06-22T12:01:00Z",
      "Sum": 4241130
    },
    {
      "Unit": "Bytes",
      "Timestamp": "2017-06-22T12:00:00Z",
      "Sum": 5064734
    },
    {
      "Unit": "Bytes",
      "Timestamp": "2017-06-22T12:04:00Z",
      "Sum": 4647186
    },
    {
      "Unit": "Bytes",
      "Timestamp": "2017-06-22T12:03:00Z",
      "Sum": 4581718
    }
  ],
  "Label": "PutRecords.Bytes"
}
# 1分周期で取っているので、60(秒)で割って、1秒あたりのByteを計算
profile=hoge
stream_name=hoge

aws --profile ${profile} cloudwatch get-metric-statistics 
--namespace AWS/Kinesis 
--metric-name PutRecords.Bytes 
--start-time 2017-06-22T12:00:00 
--end-time 2017-06-22T12:05:00 
--period 60 
--statistics Sum 
--dimensions Name=StreamName,Value=${stream_name} | jq -r '.Datapoints[].Sum' | xargs -i expr {} / 60
response
79627
70685
84412
77453
76361

GetRecords.Bytes / GetRecords.Records

上記の「PutRecords.Bytes / PutRecords.Records」の読み込み版。
こちらも合わせて見たい。

続きを読む

AWS独学メモ

頑張って学んでいきます。

サービス俯瞰

コンピューティング関連

サービス名 概要
EC2 仮想サーバー
EC2 Container Service Doker(アプリ実行環境構築ツール)運用サービス
EC2 Container Regstry Dokerイメージ保存・共有サービス。
Elastic Beanstalk .NET/PHP/Python/Ruby/Node.jsアプリを自動でAWSにデプロイ。
Lambda クライアントからのリクエスト発生時に任意プログラミング起動。イベント駆動型サービス。
Auto Scaling CPU使用率等、事前決定条件に応じ、EC2インスタンス増減
Elastic Load Balancing トラフィックに応じ、複数EC2インスタンスに負荷分散

ストレージ・コンテンツ配信

サービス名 概要
S3 ファイルサーバ。画像格納したり。
CloudFront コンテンツ配信ネットワーク。利用者から近い場所から効率よく配信
EBS EC2データを保持するストレージ。EC2のHDD,SSDのような役割。
Elastic File System EC2共有ファイルストレージ
Glacier 低価格ストレージ。仕様頻度低いけど長期保存のバックアップ用。
Import / Export Snowball ペタバイト級の大容量転送サービス。
Storage Gateway オンプレミスとAWSを接続

DB関連

サービス名 概要
RDS DB(MySQL/Oracle/SQL Server/PostgreSQL/Aurora)が利用できる
Database Migration Service 最小限停止時間でDBを移行。オンプレミスのDBサーバからの移行等に用いる
DynamoDB NoSQLデータベスサービス構築/運用。
ElastiCache クラウドでのメモり内キャッシュの管理サービス
Redshift ビッグデータを分析

ネットワーク

サービス名 概要
VPC プライベートネットワーク構築サービス。
Direct Connect オンプレミスのネットワークとAWSのVPCネットワークを直接接続。
Route 53 DNS(ドメイン名とIPアドレスを対応)

開発者用ツール

サービス名 概要
CodeCommit プライベートGit
CodeDeploy 開発アプリを実行環境に自動配置
CodePipeline 継続的デリバリ使用したアプリのリリース

開発ツール

サービス名 概要
CloudWatch AWSリソース監視サービス
CloudFormation テンプレート利用したリソースの作成と管理
CloudTrail ユーザアクティビティとAPI使用状況確認
Config リソースのイベントリ変更の追跡
OpsWorks Chef利用し操作の自動化
Service Catalog 標準化製品の作成と使用
Trusted Advisor パフォーマンスとせきゅりてぃの最適化

セキュリティ

サービス名 概要
IAM AWS認証
Directory Service Active Directoryのホスティングと管理
Inspector アプリのセキュリティ分析
CloudHSM 暗号鍵管理の専用ハードウェア
Key Management Service 暗号鍵作成と管理
WAF 攻撃から保護するファイアウォール

分析

サービス名 概要
EMR Hadoopフレームワーク
Data Pipeline オーケストレーションサービス
Kinesis リアルタイムストリーミングデータとの連携
Machine Learning 機械学習
QuickSight 高速ビジネスインテリジェンスサービス

モバイルサービス

サービス名 概要
Mobile Hub モバイルアプリの構築/テスト/監視
API Gateway RESTful APIの構築/管理
Cofnito ユーザID及びアプリデータの同期
Device Farm iOS/Android/FireOSアプリのテスト
Mobile Analytics アプリ分析の収集/表示/エクスポート
Mobile SDK モバイルソフトウェアの開発キット

アプリケーションサービス

サービス名 概要
AppStream ストリーミングサービス
CloudSearch マネージド型検索サービス
Elastic Transcorder メディアと動画変換
SES Eメール送受信
SNS プッシュ通知サービス
SQS メッセージキューサービス
SWF アプリ同士を連携ワークフローサービス

大企業向け

サービス名 概要
WorkSpaces クラウド上仮想デスクトップパソコンサービス
WorkMail セキュリティ保護、企業向けEメール及びカレンダー
WorkDocs ファイル共有サービス

S3について

用語

用語 意味
バケット データの入れ物
オブジェクト 格納ファイル

ステップ

  1. バケット作成
  2. オブジェクト格納

EC2について

用語

用語 意味
EC2 仮想サーバ。オンプレミスのWindowsサーバやUNIXサーバに相当。
インスタンス 1台の仮想サーバ
EBS(Elastic Block Store) サーバのHDDに相当する仮想ディスク
AMI(Amazon Machine Image) サーバにインストールするOSやミドルウェアやアプリのイメージ。新インスタンスを複数生成時、AMIを利用。
yum パッケージ管理システム
scp(secure copy) SSH機能を用いて、安全にファイル転送する

EC2にSSH接続した

参考ページ1
参考ページ2

ミドルウェアをインストール

yum更新
$ sudo yum -y update
httpdインストール
$ sudo yum  install -y httpd
httpd起動
$ sudo service httpd start
httpd自動起動を確認
$ sudo chkconfig --list httpd
httpd自動起動を設定
$ sudo chkconfig  httpd on
$ sudo chkconfig  httpd off

scp(コンテンツをアップロードする)

【現在ここで躓き中!】
→ 突破!!

参考ページ1

HTTPコンテンツをコピー

HTTPコンテンツのコピー

$ sudo cp /home/ec2-user/index.html /var/www/html/

【現在ここで躓き中!】index.htmlへアクセスできない

続きを読む

カスタムメトリクスのインスタンスIDがちがうときは

cloudwatchでカスタムメトリクスを取りたいとき

pushはできてるようだが、cloudwatchの画面では・・・

  • いつまでもインスタンス名がでない、というかインスタンスIDが違う
  • インスタンスIDをコマンドラインで指定するのか、否そんなパラメータはない

なんと

  • これで解決します
  • AMIとったときのキャッシュが残っていてそれをずっと参照するから
qiita.sh
rm -rf /var/tmp/aws-mon/*

実は公式ページの一番下に書いてあるんだなぁ

  • はい、みなさんドキュメントはちゃんと読みましょう・・・

    • もうしわけございまんでしたm(_ _)m
  • 誰か同じようにハマらないことを切に願う

続きを読む

Cloud Formationで簡単に全リージョンのCloudTrailを有効化する

サクっとできるので、アカウント作ったらとりあえずやっておくといいかもしれません:grinning:

以下は、全リージョンのCloudTrailを有効にするCloudFormationテンプレートのペライチと、使い方になります。

テンプレートは、公式ドキュメントをベースに以下の修正を加えています。

  • 全てのリージョンで CloudTrail 証跡を有効にするように変更
  • グローバルサービス (IAM など) からのイベントをログファイルに発行するように変更
  • OutputにSNS TopicのARNと、S3バケット名を出力するように変更
  • SNSのサブスクリプションは指定しない → サブスクリプションは後から自由に設定したいので、SNS Topicだけ作っておく

CloudFormation テンプレート

---
AWSTemplateFormatVersion: "2010-09-09"
Resources:
  S3Bucket:
    DeletionPolicy: Retain
    Type: "AWS::S3::Bucket"
    Properties: {}
  BucketPolicy:
    Type: "AWS::S3::BucketPolicy"
    Properties:
      Bucket:
        Ref: S3Bucket
      PolicyDocument:
        Version: "2012-10-17"
        Statement:
          -
            Sid: "AWSCloudTrailAclCheck"
            Effect: "Allow"
            Principal:
              Service: "cloudtrail.amazonaws.com"
            Action: "s3:GetBucketAcl"
            Resource:
              !Sub |-
                arn:aws:s3:::${S3Bucket}
          -
            Sid: "AWSCloudTrailWrite"
            Effect: "Allow"
            Principal:
              Service: "cloudtrail.amazonaws.com"
            Action: "s3:PutObject"
            Resource:
              !Sub |-
                arn:aws:s3:::${S3Bucket}/AWSLogs/${AWS::AccountId}/*
            Condition:
              StringEquals:
                s3:x-amz-acl: "bucket-owner-full-control"
  Topic:
    Type: "AWS::SNS::Topic"
  TopicPolicy:
    Type: "AWS::SNS::TopicPolicy"
    Properties:
      Topics:
        - Ref: "Topic"
      PolicyDocument:
        Version: "2008-10-17"
        Statement:
          -
            Sid: "AWSCloudTrailSNSPolicy"
            Effect: "Allow"
            Principal:
              Service: "cloudtrail.amazonaws.com"
            Resource: "*"
            Action: "SNS:Publish"
  myTrail:
    DependsOn:
        - BucketPolicy
        - TopicPolicy
    Type: "AWS::CloudTrail::Trail"
    Properties:
      S3BucketName:
        Ref: S3Bucket
      SnsTopicName:
        !GetAtt Topic.TopicName
      IsLogging: true
      IsMultiRegionTrail: true
      IncludeGlobalServiceEvents: true
Outputs:
  SNSTopicArn:
    Value: !Ref Topic
  S3BucketName:
    Value: !Ref S3Bucket

使い方

どんなリソースが作られるのか視覚的にわかりやすいので、CloudFormation Designerを使ってテンプレートを読み込んでみます。(慣れている方は、aws cliとかを使ってもいいと思います)

まずは、CloudFormationのコンソールを開いて、「テンプレートのデザイン」を押します。

001.png

CloudFormation Designerの画面が開いたら、

  1. テンプレートの言語の選択をYAMLに変えます
  2. テンプレートタブに上記テンプレをコピペします
  3. テンプレをvalidateします
  4. 画面右上の矢印から画面のリフレッシュを行います

003.png

すると、リソースがどのようにつくられるか可視化されます:clap:

あとは、画面上部左上にある:cloud:のアイコンからスタックを作成する画面に行って、適当に名前をつけて作成します。

スクリーンショット 2017-06-20 13.34.31.png

簡単。うまくいくと状況がCREATE_COMPLETEになります。

スクリーンショット 2017-06-20 13.41.49.png

まとめ

正直CloudTrailのコンソールからでもポチポチやれば簡単に設定できるのですが、なるべくリソース作成や変更はコード化しておくと、後からいろいろ見直しやすいのでおすすめです:thumbsup:

参考

http://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-cloudtrail-trail.html

続きを読む

AWS Lambda + Serverless Framework + Slackで作るカスタマイズ可能なfeedリーダー その1

概要

AWS Lambda + Serverless Framework + Slackを使ってカスタマイズ出来るfeedリーダーを作ろうという記事のその1です。
(最初は一つの記事で完結させようとしていたのですがやることが多すぎました…なお続編があるかどうかは不明)

こんなの作ったよ系の記事なので、ツールとかフレームワークの説明は少なめかも。

feedの例:
slack.png

そもそもfeedリーダーを作ろうと思った動機

実はfeedの機能自体はSlackにあって、各チャンネルで

/feed subscribe https://hoge/feed 

のように打つとそのチャンネルに定期的にフィードの記事が流れてきます。ただ

  • 一つ一つの記事のdescriptionがとても長い
  • feedによっては大きいサイズの画像が表示される

などの理由から、流れてくる記事をカスタマイズしたいなーという思いから作り始めました。

Serverless Frameworkの説明

Serverless Frameworkは、AWS Lambdaなどの、関数単位で提供されているサービスの環境準備やデプロイ、パッケージング、バージョン管理、ローカルでのデバッグなどをやってくれるフレームワーク・ツールです。
3か4つぐらいのコマンドさえ覚えておけばなんとかなるので、非常に使いやすいです。
詳しくは以下の記事が分かりやすいかと思います。

具体的なデプロイのコマンドなどは後述のhttps://github.com/yudetamago/my_rss_to_slack_service
を参照のこと。

今回の記事で出来たもの

とりあえず

  • Serverless Frameworkを使ったデプロイ
  • デプロイした関数をデバッグ実行するとfeed(今のところRSSのみ)を取ってきてSlackのwebhookに送信する

という最低限feedの内容をSlackに引っ張ってこれるところまで出来ました。
コードではサンプルとしてAmazon Web Services ブログのfeedを取ってきています。

コードの仕組み・説明

出来たもの
https://github.com/yudetamago/my_rss_to_slack_service

全体としてはfeedをHTTP GETで取ってきて、Slack通知用に変換して送信しているだけだったりします。
(今回は最低限のところしか作っていないので、自分で関数を呼び出してfeed内容を取得してくるだけです。。)

Lambda Functionの機能自体に関連する部分

まず serverless.yml で指定した feedToSlack という関数を handler.jsmodule.exports で定義します。そして後述するfeedの処理を終えてレスポンスを返すときには、引数の callbackにレスポンス内容を入れて callback(null, response) のように呼び出すとLambdaの処理は終了します。

feedの処理

今回はfeedParserというライブラリを使っていて、リクエストをそのまま pipe でパーサーのほうに流しています。記事を読み込むと parser.on('readable') のコールバック関数が呼ばれるので、そこで読み込んだ記事を items に入れたあと、slackのattachmentsに入れています。

これからやること/やりたいこと

  • descriptionにHTMLタグが入っているときの処理

    • 現状ではタグがそのままplain textで表示されるので、どうしようかな…という気持ち。
  • RSSやAtomなどフィードの種類に依存しないようにする
    • item["rss:description"]["#"] が依存している部分だけれど、これは正直頑張るしかない。
  • 同じitemが2回以上送られないよう重複管理
    • これだけはステートフルになってしまうので、何らかの方法で楽したい…
  • 定期的にフィードを取ってくるようにする
  • 複数のフィードに対応する
    • 1つのLambda Functionで並列にHTTP GETする(そもそも出来るの?)か、フィードごとにLambda Function作るかみたいなところから検討する感じ。

まとめ

とりあえず最低限のところまでは出来たので、気力があれば続き作ります!

続きを読む

Go言語でDynamoDB接続を試みたときの備忘録

前提

最近、Go言語を触り始めた。

GoからDynamoDBに接続するにあたって、 aws-sdk-go/aws/service/dynamodb を使おうとし、ドキュメントを読んでたら結構難しそうだったので、 guregu/dynamo を使うことにした。

また、DynamoDBも触ったことがなかった。

ゴールは、GoでDynamoDBに読み書きすること!

DynamoDBの設定

まず、AWSコンソール画面からDynamoDBのテーブルを作ります。

スクリーンショット 2017-06-17 16.47.12.png

上記の画面で、「テーブル名」を Test とします。

「プライマリーキー」を user_id とします。型は「数値」にします。

他のattributeにかんしては、時間に関する値を想定してますが、実際にGoからItemをPUTするときに足されるので、定義する必要はありません。(そもそも登録もできない。)

(キャメルケースがいいのか、パスカルケースがいいのか等の命名規則もあるかと思いますが、一旦これでいきます。)

これでAWS側の用意は出来ました。

あと、もう一つ大事なこととして
IAMで、DynamoDBにアクセスできる権限をもったユーザーを作成し、

  • アクセスキー
  • シークレットキー

を入手しましょう。

Go言語をかく!

user_dynamo.goという名前でファイルを作成します。

必要なモジュール

以下のリアブラリをimportします。

user_dynamo.go
import (
  "fmt"
  "time"
  "github.com/aws/aws-sdk-go/aws"
  "github.com/aws/aws-sdk-go/aws/session"
  "github.com/aws/aws-sdk-go/aws/credentials"
  "github.com/guregu/dynamo"
)
ライブラリ名 説明
fmt 標準出力に使う。
time 現在時刻を取得するのに使う。
github.com/aws/aws-sdk-go/aws configに関するオブジェクト作成につかう
github.com/aws/aws-sdk-go/aws/session sessionに関するオブジェクト作成につかう
github.com/aws/aws-sdk-go/aws/credentials awsへのアクセス情報に関するオブジェクト作成につかう
github.com/guregu/dynamo github.com/aws/aws-sdk-go/aws/service/dynamodbに対する操作を便利にしてくれるライブラリ

DynamoDBのスキーマ定義

この言い回しが正しいかわかりませんが、DynamoDBのUserテーブルに登録する予定の項目を構造体として定義します。

user_dynamo.go
type User struct {
  UseId int `dynamo:"use_id"`
  CreatTime time.Time `dynamo:"created_time"`
}

inttime.Time のあとのバッククウォートで囲まれた部分はGoで「タグ情報」と呼ばれるものになります。

DynamoDBのUserテーブルにアクセス

user_dynamo.go
func main(){
  c := credentials.NewStaticCredentials("<アクセスキー>", "<シークレットキー>", "") // 最後の引数は[セッショントークン]

  db := dynamo.New(session.New(), &aws.Config{
      Credentials: c,
      Region: aws.String("<region名>"), // "ap-northeast-1"等
  })
  table := db.Table("User")

 // 続く
}

のように書きます。

データをPUTする

user_dynamo.go
func main(){
  // 続き

  u := User{UserId: 3, CreatTime: time.Now().UTC()}
  fmt.Println(u)

  if err := table.Put(u).Run(); err != nil {
    fmt.Println("err")
    panic(err.Error())
  }

上記をまとめると

user_dynamo.go
package main

import (
  "fmt"
  "time"
  "github.com/aws/aws-sdk-go/aws"
  "github.com/aws/aws-sdk-go/aws/session"
  "github.com/aws/aws-sdk-go/aws/credentials"
  "github.com/guregu/dynamo"
)


type User struct {
  UseId int `dynamo:"use_id"`
  CreatedTime time.Time `dynamo:"created_time"`
}


func main(){
  cred := credentials.NewStaticCredentials("<アクセスキー>", "<シークレットキー>", "") // 最後の引数は[セッショントークン]

  db := dynamo.New(session.New(), &aws.Config{
      Credentials: cred,
      Region: aws.String("<region名>"), // "ap-northeast-1"等
  })

  table := db.Table("User")

  u := User{UserId: 100, CreatedTime: time.Now().UTC()}
  fmt.Println(u)

  if err := table.Put(u).Run(); err != nil {
    fmt.Println("err")
    panic(err.Error())
  }

それではterminalから

$ go run user_dynamo.go

とうってみてください。DynamoDBに

user_id created_time
100 2017-06-02T09:25:56.000147134Z

と登録されたかと思います。これで、DynamoDBへのデータ登録は完了です。

データの読み取り

DynamoDBより

「user_idが100のデータ全て」

データを読み取る場合は以下のようになります。

user_dynamo.go
func main(){
  // 続き
  var users []User
  err := table.Get("user_id", 100).All(&users)
  if err != nil {
    fmt.Println("err")
    panic(err.Error())
  }

  fmt.Println(users) // [{100 2017-06-02T09:25:56.000147134 +0000 UTC}]と出力される。

  for i, _ := range users {
    fmt.Println(users[i]) // {100 2017-06-02T09:25:56.000147134 +0000 UTC}
  }
}

また全データを取得したい場合は

err := table.Get("user_id", 1).All(&users)

というところを

err := table.Scan().All(&users)

とすれば大丈夫です。

さらに created_time によって、絞りたい場合は以下のようにすれば大丈夫です。

err := table.Get("user_id", 1).Filter("created_time >= ?", "2017-06-01 00:00:00").All(&users)

注意点

DynamoDBでは、プライマリーキーは、DBで一つしか存在できないので、本コードでは書き込みをするたびに書き換わります。

参考にしたサイト

http://www.geocities.jp/m_hiroi/golang/abcgo07.html
https://github.com/guregu/dynamo
http://qiita.com/guregu/items/2e9ac305791935b86f5d
https://developers.eure.jp/tech/aws-sdk-go-tutorial-sqs-dynamodb/

続きを読む

AWS ECSを使用する前にDockerを理解しなきゃ

◎はじめに

・Amazon EC2 Container Service (ECS)ってなんじゃらほい?
というわけでざっくり調べてみたところ、Dockerコンテナを管理するものとのこと。
『えっ、、Dockerってクジラさんのアイコンのヤツでしょ。VMの一つの。。』
とまたまたITリテラシーの低さを露呈した私、
ECSを触る前にまずDockerを理解する必要が出てきました。

1. Dockerとは

docker.png

Wikipediaより

Docker(ドッカー)はソフトウェアコンテナ内のアプリケーションのデプロイメントを自動化するオープンソースソフトウェアである。
Linuxカーネルにおける「libcontainer」と呼ばれるLinuxコンテナ技術とaufsのような特殊なファイルシステムを利用してコンテナ型の仮想化を行う。

はい。まだよくわかりません。。コンテナってなんなのさ。。。

1-1. コンテナとは

■目的
・デプロイ作業を簡易にする
→ 開発環境をそのまま本番環境として利用できる
→ アプリケーション開発環境(ライブラリ、環境変数など)をまるごとパッケージングし本番環境にデプロイすることで環境に依存することがなくなる。

■仮想化との差異

タイプ    概要
仮想化 複数のオペレーティングシステムが単一のシステム(ゲストOS)で同時に実行できるようになる
コンテナ 同じオペレーティング・システム・カーネルを共有し、アプリケーションプロセスをシステムの他の部分から独立させる

▽補足
・Hypervisor上に複数のOSを実行する仮想化は負荷が大きい。
一方、コンテナはホストOS、ライブラリを共有している。
そのため少ディスクかつ少メモリでビルドもデプロイも高速、オーバーヘッドも少ない。
それでいて仮想化のためプラットフォームやハードウェアからは隔離された環境となっている。

・1コンテナ=1プロセス

▽参考サイト:
今からでも間に合うDockerの基礎。コンテナとは何か、Dockerfileとは何か。Docker Meetup Tokyo #2

1-2. 取り敢えず触ってみよう

■参考サイト
・公式サイト
https://docs.docker.com/

・Docker ドキュメント日本語化プロジェクト
http://docs.docker.jp/index.html

■環境
・最新のAmazon linux AMIからlaunchしたインスタンスに導入してみました。

[ec2-user@mitzi_dev02 ~]$ uname -r
4.9.27-14.31.amzn1.x86_64

■余談
・amzn linuxへのインストール方法が公式サイト上で見つからなかったため、CentOSの手順でやってみたところ
ライブラリが足りないなどのエラーが多発し撃沈。。そこで上述の日本語化されたサイトを見たところAWS公式DocumentationのECSの項にDockerの説明・導入方法の記載がありました。親切ね

Docker の基本

■インストールそしてHello World

・パッケージとパッケージキャッシュを更新
$ sudo yum update -y

・Dockerをインストール
$ sudo yum install -y docker

・Dockerサービスを開始する
$ sudo service docker start
Starting cgconfig service:                                 [  OK  ]
Starting docker:  .                                  [  OK  ]

・ Dokerの環境情報表示でコマンド実行できることを確認
$ sudo docker info
・Hell world コンテナを実行

[ec2-user@mitzi_dev02 ~]$ docker run ubuntu:14.04 /bin/echo 'Hello world'

docker: Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Post http://%2Fvar%2Frun%2Fdocker.sock/v1.27/containers/create: dial unix /var/run/docker.sock: connect: permission denied.
See 'docker run --help'.
[ec2-user@mitzi_dev02 ~]$ sudo docker run ubuntu:14.04 /bin/echo 'Hello world'
Unable to find image 'ubuntu:14.04' locally
14.04: Pulling from library/ubuntu
1d8592394ba1: Pull complete
01aa7f61ccd1: Pull complete
5dd2552a960e: Pull complete
7cbe941c5e3e: Pull complete
2549ecfb14c6: Pull complete
Digest: sha256:30204139c6ab96ebd75d72f34db390f28c4decd5e563488b4e485bf979397b67
Status: Downloaded newer image for ubuntu:14.04
Hello world

公式サイトより、ここでやっていることの説明を引用

  • docker run でコンテナを実行。
  • ubuntu は実行するイメージです。この例では Ubuntu オペレーティング・システムのイメージです。イメージを指定したら、Docker はまずホスト上にイメージがあるかどうか確認します。イメージがローカルに存在していなければ、パブリック・イメージ・レジストリである Docker Hub からイメージを取得(pull)します。
  • /bin/echo は新しいコンテナ内で実行するコマンドです。

コンテナを起動するとは、Docker が新しい Ubuntu 環境を作成し、その中で /bin/echo コマンドを実行し、その結果を出力します。

なんとなくイメージが湧いてきました。
ここでは簡単なコマンドを実行しただけですが、これを応用していくことで前述の目的を達成するんだろうな。という何となくなイメージが

とりあえず今回はココまでになります。
引き続きDockerを触ってゆき、その先にあるECSを使いこなせるようにしていきたく思います。

続きを読む

WebpackでAWS SDKをバンドルするとサイズが大きいのでダイエットする

経緯

サーバレス、流行ってますね。
ウェブホスティングしたS3上にReactやRiot.jsなどでSPAを作って、認証はCognito UserPoolというのがテッパン構成でしょう。

そして、SPAを作るならWebpackでES6で書いたソースをトランスパイルして、バンドル(ひとつの*.jsファイルにまとめること)して…というのが通例ですね。(サーバーサイドの人にはこの辺がツラい)

しかしながら、いざやってみると…でかいよバンドルされたJSファイル。minifyして2MB近くとか…。

何が大きいのかwebpack-bundle-size-analyzerを使って見てみましょう。

$ $(npm bin)/webpack --json | webpack-bundle-size-analyzer 
aws-sdk: 2.2 MB (79.8%)
lodash: 100.65 KB (3.56%)
amazon-cognito-identity-js: 94.85 KB (3.36%)
node-libs-browser: 84.87 KB (3.00%)
  buffer: 47.47 KB (55.9%)
  url: 23.08 KB (27.2%)
  punycode: 14.33 KB (16.9%)
  <self>: 0 B (0.00%)
riot: 80.64 KB (2.85%)
jmespath: 56.94 KB (2.02%)
xmlbuilder: 47.82 KB (1.69%)
util: 16.05 KB (0.568%)
  inherits: 672 B (4.09%)
  <self>: 15.4 KB (95.9%)
crypto-browserify: 14.8 KB (0.524%)
riot-route: 8.92 KB (0.316%)
debug: 8.91 KB (0.316%)
events: 8.13 KB (0.288%)
uuid: 5.54 KB (0.196%)
process: 5.29 KB (0.187%)
querystring-es3: 5.06 KB (0.179%)
obseriot: 4.43 KB (0.157%)
<self>: 27.78 KB (0.983%)

なるほど、AWS SDKですね、やっぱり。

なぜ大きくなっちゃうのか

Webpackによるバンドルではnpm installしたモジュールが全て闇雲にバンドルされるわけではありません。
結構賢くて実際に使われているものだけがバンドルされるように考えられています。具体的にはimport(require)されているモジュールだけを再帰的にたどって1つのJSファイルにまとめていきます。
つまり、importしているファイル数が多ければ多いほどバンドルされたファイルは大きくなります。

import AWS from "aws-sdk"

普通のサンプルだとAWS SDKを使うときはこのようにimportすると思います。これが大きな罠なのです。
AWS SDKのソースを見てみましょう。

lib/aws.js
// Load all service classes
require('../clients/all');

ここがサイズを肥大化させる要因となっています。全てのモジュールを読み込んでいるので使いもしないAWSの機能も含めて全て読み込んでしまっているのです。

どうしたらダイエットできるのか

http://docs.aws.amazon.com/ja_jp/sdk-for-javascript/v2/developer-guide/webpack.html#webpack-importing-services

を見てみると書いてあります。

The require() function specifies the entire SDK. A webpack bundle generated with this code would include the full SDK but the full SDK is not required when only the Amazon S3 client class is used.

require("aws-sdk")と書いてしまうと、たとえS3しか使わなくても全部の機能を読み込んでしまいます。
注) import from "aws-sdk"と書いても同じ意味になります。

その下の方に、

Here is what the same code looks like when it includes only the Amazon S3 portion of the SDK.

// Import the Amazon S3 service client
var S3 = require('aws-sdk/clients/s3');

// Set credentials and region
var s3 = new S3({
    apiVersion: '2006-03-01',
    region: 'us-west-1', 
    credentials: {YOUR_CREDENTIALS}
  });

と答えが載っています。

clientsディレクトリ以下のファイルを個別にimportすることができるようになっているということです。

なるほど、試してみた

https://github.com/NewGyu/riot-sample/compare/cognito-login…fat-cognito-login

$ $(npm bin)/webpack --json | webpack-bundle-size-analyzer 
aws-sdk: 325.01 KB (36.3%)
lodash: 100.65 KB (11.2%)
amazon-cognito-identity-js: 94.85 KB (10.6%)
 :

2.2MB -> 325KB と激ヤセです!

続きを読む