DynamoDBのデータからcsvを作ってS3に上げるLambda関数

経緯

仕事でDynamoDBを使うことになり、その中で「日次でDynamoDBのデータをtxtファイルにエクスポートして所定のS3バケットに置く(日ごとにファイルを分ける)」という要件があり、やり方を調べて実装してみた。

全体設計

CloudWatchEventを使ったよくある日次処理の構成。
Dynamoの項目が日付をもっているためその日付を条件にその日エクスポートするデータをQueryしてくる。
構成.jpg

調べたところ他にDynamoDB Streamをトリガにする方法があるっぽいが、テーブルが更新されるたびにデータを取ってきてファイルにアペンドするより1日の終わりにバッチ一発でやったほうがコスト的にもよくね?と思ったので今回の方法をとった。
このあたり実際のところはどうなのかは分からないので、詳しい方は是非おしえてください( ^)o(^ )

テーブル

こんな感じ。
DynamoではQueryの条件にできるのはkey項目のみらしい。
ところが条件に指定したかったdateがkey項目ではなかったためdateをキーにしたインデックス(GSI)を作成した。

id(Hash key) count(Range Key) date
a1 1 20170101
a1 2 20170101
a1 3 20170101
b1 1 20170102
b1 2 20170102
c1 1 20170102

Lambda関数

こんな感じ。
IAMロールの設定とかは割愛。いい感じにやりました。

index.js
'use strict'
const AWS = require('aws-sdk');
const moment = require('moment');
const fs = require('fs');

AWS.config.update({ region: 'ap-northeast-1' });

const docClient = new AWS.DynamoDB.DocumentClient();
const s3 = new AWS.S3();

exports.handler = function (event, context, callback) {

    let params = {
        TableName: "dynamo_toS3",
        IndexName: "date-count-index",
        KeyConditionExpression: "#date = :date",
        ExpressionAttributeNames: {
            "#date": "date"
        },
        ExpressionAttributeValues: {
            ":date": moment().format('YYYYMMDD') //バッチ実行日に作成されたレコードのみ取得
        }
    };

    docClient.query(params).promise().then(data => {

        let logArray =[];

        data.Items.forEach(element => {

            logArray.push(JSON.stringify(element));

        });

        return new Promise(function(resolve, reject) {
      
       //Lambdaでは一時的なファイルの出力先に/tmpが使える
            fs.writeFile('/tmp/tmp.txt', logArray.join('n'), function(err) { 
                if (err) reject(err);
                else resolve(data);
            });
        });

    }).then(() => {

        let s3Params = {
            Bucket: "my-bucket",
            Key: `log_${moment().format('YYYYMMDD')}.txt`,
            Body: fs.readFileSync('/tmp/tmp.txt')
        }

        return s3.putObject(s3Params).promise();

    }).then(data => {

        console.log(data);

    })
    .catch(err => {

        console.log(err);
    })

};

まとめというか所感

普段RDBに慣れ親しんでいる身からすると、Dynamoはやや癖があるように感じる。
こちらの記事にもある通りSDKを使う場合はAWS.DynamoDBよりもAWS.DynamoDB.DocumentClientを使った方が良い。
次はDynamoDB Streamをトリガにしたものも試してみたい。

続きを読む

AWSの暗号化されたEBSスナップショットを持つAMIを他のアカウントと共有

1. はじめに

AWSで、EC2インスタンスのバックアップから、新しいEC2インスタンスをローンチするといったことが良く行われます。このバックアップとなる、AMI(Amazon Machine Image)は、AWSコンソールでマウスクリックして簡単に作成できます。さらに、このAMIをAWSの他のアカウントで使いたい場合は、AWSコンソールでAWSアカウントIDを許可すれば、簡単に共有できます。

  • AMI(Amazon Machine Image)でEC2インスタンスまるごとバックアップ
  • AMI(Amazon Machine Image)から、新しいEC2インスタンスをローンチ

ただし、EC2インスタンスのEBSが暗号化されていて、AMIを別AWSアカウントで共有したい場合は、やることが一気に増えます。

AWS Blogで、暗号化されたEBSスナップショットを持つAMIを作成し他のアカウントやリージョンで共有する方法が参考になりますが、複雑な手順が必要です。ポイント等について、ご紹介します。

2. 前提

  • EC2インスタンスのEBSが暗号化されている
  • EBS-Backed なインスタンス
  • 暗号化の鍵はデフォルト aws/ebsが指定されている
  • AMIは別アカウントで共有
  • リージョンは東京で同一

別リージョンでも可能ですが、今回の例は同一リージョンになります。

3. ポイント

暗号化されたスナップショットを含むAMI自体を、そのままターゲットアカウントと共有することはできないので、ソースアカウント側のEBSボリューム単位の暗号化されたプライベートスナップショットを、ターゲットアカウントと共有します。ソースアカウント側の鍵を共有して、ターゲットアカウント側の鍵で再度暗号化したスナップショットからAMIを作成します。

awsami.png

暗号化でデフォルトの aws/ebsが指定されている場合、AWS Blogに以下注意点が記述されています。最初は、これに気づかなくて、悩まされました。

※Note: デフォルトEBS CMKで暗号化されたスナップショットは別のアカウントに共有することはできません。サービスデフォルトCMKで暗号化されたリソースは同じアカウントでのみ共有できます。デフォルトEBS CMK (キーエリアスはaws/ebs)による暗号化されたスナップショットから始める場合は、スナップショットをコピーし、KMSで作成したカスタムCMKにて再暗号化してください。外部のアカウントIDがアクセスできるようにカスタムCMKの鍵ポリシーを変更することが出来ます。

4. 手順

4.1 事前準備

  • ソースアカウント AWSアカウント番号 111111111111

    • 事前にカスタムCMK(鍵#2)を作成しておきます。この鍵は、ターゲットアカウントの共有を許可します。
  • ターゲットアカウントAWSアカウント番号 999999999999
    • 事前にカスタムCMK(鍵#3)を作成しておきます

暗号鍵の作成は、 5. KMS作成をご参考ください。この手順も、手間がかかります。

4.2 ソースアカウントで操作

① AMIの作成 インスタンスの作成⇒イメージの作成

通常の操作と同じです。

② AMIのコピー 鍵 #2

デフォルトEBS CMKで暗号化されたスナップショットは別のアカウントに共有できないので、カスタムCMK(鍵#2)で暗号化します。

amicopy.png

③ プライベートスナップショット アクセス許可

プライベートスナップショットをターゲットアカウント(999999999999)に対してアクセス許可します。

snapshotaccess.png

4.3 ターゲットアカウント

④ スナップショットのコピー 鍵 #3

共有されたスナップショットをターゲットアカウントで作成したカスタムCMK(鍵#3)でコピーします。

snapshotcopy.png

⑤ EBSスナップショットからイメージを作成

EBSスナップショットからAMIを作成します。EBSボリュームがたくさんあると、大変です。

注意 paravirtualの場合は、カーネルIDを忘れないように引き継いで指定します

snaptoimage.png

⑥ AMIからインスタンス起動

通常の操作と同じです。

5. KMS作成

IAMの項目に、暗号化キーメニューがあります。

5.1 暗号化キー

リージョンを選択するメニュー

makekey.png

暗号化キーの一覧画面

keylist.png

キーマテリアル

keymaterial.png

5.1 パブリックキーおよびインポートトークン

パブリックキーおよびインポートトークンのダウンロード (AWS マネジメントコンソール)

  • RSAES_OAEP_SHA_1 を選択
  • ラップキーとインポートトークンのダウンロード
    • ファイル名が wrappingKey_CMK_key_ID_timestamp のラップキー (パブリックキー)
    • ファイル名が importToken_CMK_key_ID_timestamp インポートトークン

5.2 キーマテリアルを暗号化

例: OpenSSL でキーマテリアルを暗号化する

  • バイナリキーマテリアルを生成し、AWS KMS へのインポート用に暗号化
  • コンソールからパブリックキーをダウンロード

たとえば、wrappingKey_f44c4e20-f83c-48f4-adc6-a1ef38829760_0809092909

① 次のコマンドを使用して、256 ビット対称キーを生成し、PlaintextKeyMaterial.bin という名前のファイルに保存します。

$ openssl rand -out PlaintextKeyMaterial.bin 32

②パブリックキーをPublicKey.binというファイル名で保存

$ cp wrappingKey_f44c4e20-f83c-48f4-adc6-a1ef38829760_0809092909 PublicKey.bin

③ ダウンロードしたパブリックキーでキーマテリアルを暗号化して 「EncryptedKeyMaterial.bin」という名前のファイルに保存し

$ openssl rsautl -encrypt 
    -in PlaintextKeyMaterial.bin 
    -oaep 
    -inkey PublicKey.bin 
    -keyform DER 
    -pubin 
    -out EncryptedKeyMaterial.bin

外部アカウント

暗号化キーを外部アカウントと共有します。

outaccount.png

6. おわりに

いかがだったでしょうか?EBSが暗号化されてると、アカウント間の共有はとても敷居が高くなります。コード化してる人はいたりするでしょうか…

7. 参考

続きを読む

AWS re:Invent 2017 Security re:Cap レポート

こんにちは、ひろかずです。
今日は、12/14にアマゾン目黒オフィス(東京)で行われた AWS re:Invent 2017 Security re:Capに行ってきたので一筆書きます。

サブタイトルに re:Invent 2017のセキュリティを振り返る会 と題されていました。
年末にも関わらず、19時前に会場が満席!
AWS熱が依然高いことを感じさせます。

テーマはセキュリティ
ボルテージは否応がなく高まります。

今回の目的は…

  • re:Invent 2017前後で発表されたAWSセキュリティ関連情報の収集・共有の場の提供
  • AWSセキュリティを 企業組織内で活用できるか考える
  • re:Invent2018に参加する、そして登壇する

資料は公開されるので、話を中心に書いていきます。

例によってリアルタイム執筆ですので、誤字脱字、記載漏れ、表記ゆれはご容赦ください。

スティーブ・シュミットのメッセージ

  • 人とデータは切り離す(自動化している)
  • セキュリティの判断は人の判定が必要であるので、その瞬間に1人が稼働している。
  • いわゆるステレオタイプのSOCは持っていない

AWSセキュリティサービスアップデート①

AWS Single Sign-On

AWSソリューションアーキテクト
辻 義一

権限とコストを分ける方法

  • IAMユーザー/ポリシー
  • コストタグを付ける

要件によっては、分離できないこともある。
その場合は…

  • AWSアカウントを分ける
  • アカウント超えのデータ転送やピアリング接続ができる

そうすると権限の管理やログインが分離してしまって手間
シングルサインオンが求められる

  • SSOソリューションがあるが…
  • マネコンにアクセスするだけなのに高くて難しいのは辛い

AWS SSO

ユーザーポータルの提供
アプリのポータルも提供
既存のADを活用
簡単に実現

料金とリージョンと条件

SSO自体は無料
MSADやAD Connectorの費用がかかる
Virginiaのみ
AWS Organizationが必要

ユーザーリポジトリは、AD

  • Simple ADには対応していない

RADIUSによるワンタイムパスワード

ログイン先はAWS Organizationに参加しているAWSアカウント
SAML2.0に対応しているアプリケーション

アカウント毎に複数の権限を設定できる

  • 対象組織を選定し、パーミッションセットを設定できる(IAMポリシーと同じ文法)

AWSコンソールのログインフロー

SSOポータルから
ADの認証情報を使ってログイン
パー民ションセットに基いてあぷりが 表示され
選択したアカウントのIAM権限が割り当てられる

Cloudtrailで監査できる

アプリを登録

事前定義のアプリやカスタムアプリに、アプリのメタデータを登録する
SaaS側の定義手順も掲載されている
自分たちのアプリケーションにも実装することもできる

関連サービス

IAM

  • STSが裏で動いている

AWS Directory Service

  • 機能は似ているが、SSOはSAMLも追加

Cognito

  • モバイルアプリのSSOにも使える

ADとAWS ADの連携

パターン1

  • 既存ADとAWS MSADと信頼関係を結ぶ

パターン2

  • 既存AD環境とVPN接続して、AD Connectorで接続

日本から使うには?

パターン1

  • インターネットVPN
  • Direct Connect Gateway

パターン2

  • インターネットVPNサーバを経由する

パターン3(東京リージョンは来年…)

  • VPCのリージョン間接続

QA

LDAPが話せればいいなら、Azure ADと接続できる?

  • 信頼関係を結べるか謎

エンドユーザーセッション①

株式会社ドワンゴ
第二サービス開発本部
Dwango Cloud Service部 Consultingセクション
第二プロダクト開発部 第一セクション
鈴木 栄伍

スライドはニコナレに公開してます。
ニコナレは、AWSで構築されています。

re:Inventと今後のdwangoでのAWSセキュリティについて

re:Invent参加の背景

2016年から参加
初回は、最新動向把握、EBC(Executive Briefing Center)参加のため

  • 社内コンサルやAWSで構築したサービスのインフラエンジニアをやっていた流れ

参加して良かったこと

EBCにてAWS開発者と意見交換できた

  • 今年はAWS OrganizationやECSホウエンと会話した
  • サービスの利用感を感じることができるまたとない機会

Brakeoutセッションへの参加

  • 自分がやっていることと同じようなことを、世界の人たちがどうやっているか
  • 自分の方向性が合っているか分かる
  • DisneyとMarvel Studioのセッションが良かった

社内の困りごとと構想

アカウント(AWS, IAM)管理関連

  • rootアカウントフローが手動
  • Organizationでコマンド発行したい
  • AWS SSOが待ち遠しい

AWS上で構築されたシステムのNWセキュリティ

  • 何かあったときに後手後手に回る
  • FlowLogsとCloudTrailのログをMaisyとGuardDutyで検知できるようにしたい

AWSセキュリティの期待

  • 少数精鋭チームなので、マネジメントサービスで効率化したい
  • System ManagerとGuardDutyの連携
  • Amazon Inspectorとの連携もしてくれるといいなぁ

AWSセキュリティサービスアップデート②

Amazon GuardDuty AWSパートナーソリューションアーキテクト
酒徳 知明

副題:AWSクラウドの脅威検知の実装

DevSecOps

  • セキュリティベースラインがある中でDevOpsを回すのがいい

Amazon GuardDuty

脅威リスクを検知するサービススコープ

  • 脅威リスクのモデルは、膨大なデータサンプルが必要
  • データが多ければモデルの精度が上がる
  • 何をリスクと定義するか?
  • Bad IPや異常検出、機械学習で統合分析している

利用リージョン

  • 東京リージョン使えます!

対象

  • EC2またはIAMに置ける脅威を検出
  • エージェントレス、センサーレス、アプライアンスレス
  • 30日間の無料枠(課金状況を見て高かったら止められる)

高機能なので、無料枠で是非試して!

インプット

  • VPC Flow Logs
  • Cloud Trail
  • DNS Log

アラート

リスクのタイプ(Detection Type)

  • 悪意のあるスキャン(ポートスキャンとか)
  • インスタンスへの脅威(BitCoin掘り掘りとか)
  • アカウントへの脅威(通常と異なるAPIの発行とか)

サービスデリバリーコンセプト

− いかに簡単に有効にできるか
– CloudTrailが有効になってないとかの排除

既知の脅威リストのカスタマイズ

  • ユーザー独自の脅威IPリストとのギャップを埋めるTrusted IP ListThreat IP List

検知の閲覧は2系統

  • AWS Management Console
  • API/JSON

重要度

  • 0~3.9(青)
  • 4.0~6.9(黄)
  • 7.0~10.0(赤)

検知時アクション

  • CloudWatchでアラートでもOK

検知内容は

課金形態

  • イベントあたり(CloudTrailは1億イベントあたり)
  • ログデータあたり(FlowLogs/DNSログは、GBあたり)
  • とりあえず有効化して、無料枠で見極めて!

展開方式

  • CloudFormation -Stacks- で、全リージョン、全アカウントに同じセキュリティベースラインを展開できる
  • アカウントに対してやOrganizationのヒエラルキーを対象にできる

パートナー

  • 多数のセキュリティベンダーが取り扱う
  • パートナーインテグレーションは、パートナーがGuardDutyのデータを持っていく方式
  • 各ベンダー、無料トライアルを提供しているのでぜひ使ってみて!

エンドユーザーセッション②

株式会社リクルートテクノロジーズ
ITソリューション統括部 サイバーセキュリティエンジニアリング部
セキュリティオペレーションセンター
安東 美穂 様

リアルタイム執筆はご遠慮をーとのことです。
残念

LT①

三井物産セキュアディレクション株式会社
プロフェッショナルサービス事業部
マネージャー
佐藤 裕貴 様

サイバーセキュリティ専門会社のre:Invent振り返り

Keynoteの中でDynamoを使っているサービス
Tinder
出会い系アプリ
研究用途で使ってます

セキュリティ系セッション会場は遠かったので行かなかった。

  • 複数名で担当を分けて行くのが良い

Managed Rule for AWS WAF

  • AlertLogicがパートナーとして参加
  • Wordpress向けの仮想パッチを提供
  • Shareが大きいところに投入
  • SQL/SQLiが攻撃ボリュームが大きい
  • Rule作成から開放されよう!

Amazon GuardDuty

  • AWS不正操作、悪意のあるアクセス元の検知
  • サードPartyベンダーのセキュリティサービスも利用して、網羅的なセキュリティを実現

LT②

トレンドマイクロ株式会社
セキュリティエキスパート本部
パートナービジネスSE部 パートナーSE課
姜 貴日 様

Deep Securityを出典してきました。
8カテゴリの中でトレンドマイクロがセキュリティ部門一位(実質Deep Security)

Amazon GuardDutyとは?

  • Deep Securityとはかぶらない(一部かぶる)
  • GuardDutyは、フルマネージドと検出
  • Deep Securityは、検知と防御
  • ShildもWAFも出た時は問い合わせがあった
  • Deep Securityは、サーバー内部で攻撃を遮断するので根本的に違う

Amazon GuardDutyとDeep Securityの連携

  • 検知内容に基いて、対象IPを遮断
  • OSS扱いでGitに公開中

ManagedRule

  • マーケットプレイスで購入すると、サポートは米国になる
  • トレンド提供のRuleは、Apache SuiteとCMS向け

LT③

AWS WAF Partner Managed Rules
AWSソリューションアーキテクト
藤倉 和明

今日はデモ
帰ったら有効にしたくなる話をします

WAF管理画面にMarket Placeが増えてるので、そこから有効化

  • 間を取ってFortinetを選択(笑)
  • No overrideは防御モード。
  • まずは、Override on Counntで慣熟運用すること。

とにかく最高なので、Countではじめよう!

大盛り上がりで終わりました!
今日はここまでです。
お疲れ様でした。

続きを読む

CodeCommitのリポジトリをghqでクローンする

使ったもの

AWS CodeCommit

AWSの提供するソースコントルーロサービス、GithubやBitbucketのようにGitリポジトリを管理できます。
https://aws.amazon.com/jp/codecommit/

小規模であれば無料枠で収まりそうなのと、CodePipelineなどのサービスとの連携が楽そうなので使ってみることにしました。
https://aws.amazon.com/jp/codecommit/pricing/
http://docs.aws.amazon.com/ja_jp/codecommit/latest/userguide/integrations.html

ghq

ghqはリポジトリの管理を楽にしてくれるツール、pecoやfzfと合わせて使うのがおすすめです。
https://github.com/motemen/ghq

細かい紹介はしませんが気になる方は他の記事をどうぞ。
https://qiita.com/search?q=ghq&sort=stock

やったこと

CodeCommitのリポジトリを作成

コンソールからリポジトリ名を決めて作成するだけ。

もしくはcliから aws codecommit create-repository --repository-name hoge としてもOK。
プロファイルで設定しているリージョンにリポジトリが作成されます。
http://docs.aws.amazon.com/cli/latest/reference/codecommit/create-repository.html

CodeCommitに接続するためにIAMユーザーにsshキーを設定

sshキーを作成しパブリックキーをIAMユーザーに登録します。
このIAMユーザーはCodeCommitにアクセスできるようポリシーを設定しておく必要があります。
http://docs.aws.amazon.com/ja_jp/codecommit/latest/userguide/setting-up-ssh-unixes.html

コンソールから作業していれば以下のように接続方法が表示されるのでわかりやすいと思います。

スクリーンショット 2017-12-14 14.15.13.png

ここまで終わればsshでリポジトリに接続できるようになります。

ghqでリポジトリをクローン

CodeCommitのリポジトリをクローンするにあたって上にあるように git clone でもいいのですが、せっかくなので普段から使っているghqを使いたいですね。

ghqでは以下のようにすればクローンできました。

$ ghq get ssh://git-codecommit.REGION.amazonaws.com/v1/repos/REPO

ここで注意するのは -p オプションをつけないことです。
-p オプションをつけるとsshのユーザーがgitになってしまい、CodeCommit用のユーザーと違うので Please make sure you have the correct access rights となってしまいます。

コマンドを実行してもらうとわかりますが -p をつけた場合 ssh://git@ となっています。

# 成功
$ ghq get ssh://git-codecommit.REGION.amazonaws.com/v1/repos/REPO
git clone ssh://git-codecommit.REGION.amazonaws.com/v1/repos/REPO
# 失敗
$ ghq get -p ssh://git-codecommit.REGION.amazonaws.com/v1/repos/REPO
git clone ssh://git@git-codecommit.REGION.amazonaws.com/v1/repos/REPO
# これも失敗
$ ghq get -p https://git-codecommit.REGION.amazonaws.com/v1/repos/REPO
git clone ssh://git@git-codecommit.REGION.amazonaws.com/v1/repos/REPO

あるいはIAMユーザーにHTTPS Git 認証情報を設定しておけばユーザー名とパスワードでも接続できます。

$ ghq get https://git-codecommit.REGION.amazonaws.com/v1/repos/REPO
Username for 'xxxx':
Password for 'xxxx':

error Could not find version control system というエラーが出る場合はREADMEにあるように .gitconfig に以下の記述をしておけばOKです。

[ghq "CODECOMMIT-URL"]
  vcs = git

続きを読む

AWS CodePipelineがECSへのデプロイをサポートしたのでやってみる

AWS CodePipeline が Amazon ECSへのデプロイをサポートしました。(先日発表されたばかりのFargateも)
https://aws.amazon.com/jp/about-aws/whats-new/2017/12/aws-codepipeline-adds-support-for-amazon-ecs-and-aws-fargate/
これによりコンテナ上で稼働するアプリケーションの継続的デリバリーがより簡単に実装できるようになります。

早速チャレンジしてみました。

ECSでテスト用のコンテナを起動する

ECR の設定

Elastic Container Registry に今回のテスト用のリポジトリを作成します。
後ほどリポジトリのURIを使用しますので、メモしておきます。

image.png

Dockerfileの作成

今回は公式ドキュメントのhello-worldを流用しました。
基本的にはこちらの手順を踏襲します。

FROM ubuntu:12.04

# Install dependencies
RUN apt-get update -y
RUN apt-get install -y apache2

# Install apache and write hello world message
RUN echo "Hello World!" > /var/www/index.html

# Configure apache
RUN a2enmod rewrite
RUN chown -R www-data:www-data /var/www
ENV APACHE_RUN_USER www-data
ENV APACHE_RUN_GROUP www-data
ENV APACHE_LOG_DIR /var/log/apache2

EXPOSE 80

CMD ["/usr/sbin/apache2", "-D",  "FOREGROUND"]

イメージをビルド

Dockerfileをビルドし、ECRにプッシュします

$ docker build -t ecs-test-repo .
$ docker tag ecs-test-repo:latest 012345678910.dkr.ecr.ap-northeast-1.amazonaws.com/ecs-test-repo:latest
$ docker push 012345678910.dkr.ecr.ap-northeast-1.amazonaws.com/ecs-test-repo:latest

ECSへのデプロイ

以下のようなタスク定義ファイルを作成します。

imagedefinitions.json
{
    "family": "ecs-hello-world",
    "containerDefinitions": [
        {
            "name": "ecs-hello-world",
            "image": "012345678910.dkr.ecr.ap-northeast-1.amazonaws.com/ecs-test-repo:latest",
            "cpu": 10,
            "memory": 128,
            "portMappings": [
                {
                    "containerPort": 80,
                    "hostPort": 80
                }
            ],
            "entryPoint": [
                "/usr/sbin/apache2",
                "-D",
                "FOREGROUND"
            ],
            "essential": true
        }
    ]
}

AWS CLIでタスク定義の登録を行います。

$ aws ecs register-task-definition --cli-input-json ./imagedefinitions.json

コンソールでタスク定義が登録されていることを確認します。

image.png

新規サービスを作成し、登録したタスクを起動します。

image.png
image.png

クラスタインスタンスのパブリックIPにアクセスすると、Hello Worldが表示されます。
長くなりましたが、以上で事前準備が完了しました。

image.png

AWS CodeCommitの設定

DockerイメージをECRにプッシュするための buildspec.yml を作成し、CodeCommitのリポジトリに追加します。

リポジトリの作成

リポジトリ名を指定して、新規作成します。

image.png

今回、Eメール通知の設定はスキップします。

image.png

リポジトリへの接続

ローカルに作成したリポジトリをcloneします。
リポジトリへの接続には適切な権限をもったIAMユーザおよびGit認証情報が必要ですので
あらかじめ作成しておきます。

$ git clone https://git-codecommit.ap-northeast-1.amazonaws.com/v1/repos/ecs-test-repo
Cloning into 'ecs-test-repo'...
warning: You appear to have cloned an empty repository.

buildspec.ymlの作成

REPOSITORY_URIにはECRのURIを設定します。
またイメージタグにソースコードのコミットIDを追加するようにします。

buildspec.yml

version: 0.2

phases:
  pre_build:
    commands:
      - echo Logging in to Amazon ECR...
      - aws --version
      - $(aws ecr get-login --region $AWS_DEFAULT_REGION)
      - REPOSITORY_URI=012345678910.dkr.ecr.ap-northeast-1.amazonaws.com/ecs-test-repo
      - IMAGE_TAG=$(echo $CODEBUILD_RESOLVED_SOURCE_VERSION | cut -c 1-7)
  build:
    commands:
      - echo Build started on `date`
      - echo Building the Docker image...          
      - docker build -t $REPOSITORY_URI:latest .
      - docker tag $REPOSITORY_URI:latest $REPOSITORY_URI:$IMAGE_TAG
  post_build:
    commands:
      - echo Build completed on `date`
      - echo Pushing the Docker images...
      - docker push $REPOSITORY_URI:latest
      - docker push $REPOSITORY_URI:$IMAGE_TAG
      - echo Writing image definitions file...
      - printf '[{"name":"ecs-hello-world","imageUri":"%s"}]' $REPOSITORY_URI:$IMAGE_TAG > imagedefinitions.json
artifacts:
    files: imagedefinitions.json

artifacts として事前準備で作成したタスク定義ファイルを指定します。
パイプラインの中でタスク定義の新しいリビジョンが作成され、サービスが更新されます。

CodeCommitへプッシュ

Dockerfile, buildspec.yml, imagedefinitions.json をCodeCommitへプッシュします。
Dockerfileの中身を少しだけ編集しています。

RUN echo "Hello World! by CodePipeline" > /var/www/index.html
$ git add.
$ git commit -m "Adding build specification."
$ git push

image.png

Piepelineの設定

Pipelineの作成

マネージドコンソールの AWS CodePipelineから新規パイプラインを作成します。

image.png

ソースの設定でソースプロバイダを CodeCommitに設定し、先程作成したリポジトリを指定します。

image.png

ビルドの設定で以下のように、選択/入力し、ビルドプロジェクトを保存します。

  • ビルドプロパイダでAWS CodeBuildを選択
  • 新しいビルドプロジェクトを作成 を選択し、任意のプロジェクト名を入力
  • 環境イメージで AWS CodeBuildマネージド型イメージの使用を選択し、OS:Ubuntu、ランタイム:Dockerとする

image.png

デプロイの設定で、プロバイダに Amazon ECS を選択し、展開先のクラスタ名、サービス名を指定します。

image.png

AWS サービスロールを選択します。必要に応じて新規作成もできます。

image.png

レビュー画面で設定値を確認し、パイプラインを作成します。

パイプラインの実行

作成直後にパイプラインが起動します。
パイプライン作成ウィザードの中で、CodeBuildのビルドプロジェクト用のサービスロールを作成していますが、
初期の状態ではECRに対する権限が不足しているため、以下のようにビルドが失敗します。

image.png

そのためIAM管理画面で、作成したサービスロールに対し、AmazonEC2ContainerRegistryPowerUserポリシーを追加します。

image.png

Buildステージを再実行すると今度はステージングまで処理が完了します。
image.png

結果確認

タスク定義を確認すると新しいリビジョンが作成されています。
※途中メモリの設定などを誤り複数回デプロイしているため、以下の画面ショットではリビジョンが4まで増えています。。。

image.png

同様にサービスも新しいタスク定義のリビジョンで更新されています。

image.png

最後にブラウザで確認します。Hello worldのメッセージが更新されていることを確認できました!

image.png

最後に

以上の手順でCodePipelineでECSへのデプロイを行うことができました。
参考になれば幸いです。

続きを読む

AWS CodeCommit + Git (https) を OSX から SourceTreeで使う

はじめに

AWSでWEBサービスを作るにあたり、CodeCommit を使ってソースを管理しようと思い、AWS CodeCommit を利用することにしたのでその準備方法などを整理&メモとして残す。サーバ側を python3 で開発するつもりなので、python 周辺のツールを色々と使っているけど、pythonとか使いたく無い…って人は、別の道を探した方が良いと思う。

前提

ほとんど開発用のツール・ライブラリが入っていないことを前提にしているのだけど、まっさらな OSX を用意したわけで無いので、エラーが発生するような場合は、それぞれ解決が必要。たぶん、pip とか…。

CodeCommit の作成など

下記のサイトとかを参考に、CodeCommit上にGitリポジトリを作り、アクセス用のアカウントを準備する。非常にざっくり・簡単に言えば、(1)AWS IAM でユーザを作って、(2)CodeCommit でリポジトリを作って、(3)AWS IAM で認証情報を生成する…といった流れ。

AWS Code Commitでgitリポジトリを作る

実は、この状態で OSXのコマンドプロンプトから git clone コマンドを叩くと、ユーザID・パスワードを入力すれば普通にクローンできる。だけど、コマンドは何かと不便なので、SourceTreeを使うためにおまじないが必要。

AWS CLI をインストール

下記のサイトなどを見る限り、SourceTree が認証をパスするためには、awsコマンドが存在しないといけない模様。なので、awscli をインストールするのだけど…ついでに、pythonのバージョン更新も行う。

AWS CODECOMMIT WITH SOURCETREE

OSX:python2.7 から python3 に変更

下記のサイトを参考に、OSX の python を python3.6.3 に変更。特に必要性は無いのだけど、サーバ側のバージョンを 3.6系にするつもりなので、環境を一致させる意味でも変更する。

MacOSとHomebrewとpyenvで快適python環境を。

AWS CLI のインストール

python (pip) の環境が揃っているなら非常に簡単。以下のコマンドでインストール。エラー(不足ライブラリなど)は適当に処理すべし。

sudo pip install awscli

AWS接続用の資格情報を作成

awsコマンドを使って、接続用の資格情報を作成。AWS IAM で作ったユーザの資格情報が必要になるので準備しておく。

aws configure --profile << 適当な名称 >>

AWS Access Key ID [None]: << ユーザー名 >>
AWS Secret Access Key [None]: << パスワード >>
Default region name [None]: << リージョン名 >>
Default output format [None]: 

#上記の << ... >> は、適宜置き換え

SourceTreeで Clone から Push まで

ひとまず、SourceTreeを使って、リポジトリを clone する。認証情報を聞いてきたり、KeyChain へのアクセス許可を聞いてきたりと若干忙しいけど、特に問題ない…はず。

ただし、このままでは ローカルへの commit はできても、サーバへの pull も push もできないので、config ファイルに追記する。

configへの追記
[credential]    
    helper = !aws --profile << 適当な名称 >> codecommit credential-helper $@
    UseHttpPath = true

# << 適当な名称 >> は、aws configure で使った名称。

まとめ

ssh でやれば良かった… なんて野暮なことは言ったらダメなのだと思うけど、https 経由でも無事に SourceTree が使える模様。

続きを読む

AWS Step Functionsを使ってみた

ワークフローシステムではTresure Data社のDigdagを利用したことがあったのですが、Step Functionsの提案なんかもあったので、ちょっと使ってみました。使い道としてはLambdaは5分制限があるので、それを回避することも考えられますね。

やりたいこと

・Step Functionsを使う
・Lambdaを呼ぶ

前提

私のように初心者が初めて作るという方を前提としています。
Lambdaはある程度わかっていること。ちなみに私は実案件で使ったことないので、その程度で大丈夫です。

すごく単純なフロー

こちらを作成します。単純に1Lambdaを実行させるだけです。
image.png

1.LambdaのIAMロールを作成

Lambdaを作成するにはLambda用のロールを作成しとかないといけないので、まずは作成します。すでにロールがある場合は不要です。
IAMのサービスを開き、
image.png

ロールタブからロールの作成をクリック。
image.png

AWSサービスをクリックして、このロールを使用するサービスを選択で、Lambdaを選んで次のステップとします。
image.png

ポリシータイプは何も選択せずに次へのステップとします。
image.png

ロールの名前は適当に決めて、OKです。
image.png

2.Lambdaを作成

Lambda関数を作成します。
image.png

関数の作成をクリック
image.png

関数の名前はなんでもよいです。ランタイムはNode.js 6.10、ロールはデフォルトの既存のロールのままで、既存のロールに上で作成したロールを設定します。
image.png

Lambdaの画面ですが、トリガーは何も設定しません。
image.png

下の方のコードを以下のように書き換えます。
image.png

3.Step Functionsの作成

Step Functionsのステートマシンを作成します。ステートマシンとはまぁ簡単にいうとワークフローです。ワークフローを作成しますといっても同じです。JSONベースで出来ています。

検索からサービスを選択します。
image.png

ステートマシンを初めて作る場合はこのような画面が表示されるので、「今すぐ始める」ボタンを選択します。
image.png

ステートマシンの名前を付けます。ワークフローの名前ですね。とりあえず「HelloWorldStateMachine」と付けました。
image.png

設計図では「Hello World」を選択します。
image.png

コードは以下のように設定します。
・Type→Taskに変更
・Result→Resourceに変更
・値を先ほどlambdaで作ったarnに変更
image.png
arnはResultをResourceに変更して、値のところをクリックすると候補がいくつか出てくるので、関数名で判断できます。
image.png

ビジュアルワークフローは以下のようになります。
今回は関数を呼び出しているだけのシンプルなワークフローです。
AWS Glueでも思ったのですが、ビジュアルワークフローからコードが生成されるとかなり使いやすいんですけどね。
image.png

すべて完了したら「ステートマシンの作成」ボタンを選択します。
実行ロールが自動作成されるため、デフォルトでOKを押します。
image.png

4.実行

新しい実行を選択します。
image.png
入力を修正して実行!
image.png
実行の詳細で出力タブを確認すると、Lambdaで設定したcallbackが返ってきていることがわかります。
image.png

まとめ

今回は単純なフローを作成しましたが、次は分岐があるフローやもう少し意味のあるLambdaで試してみたいです。
参考:
チュートリアル:Lambda ステートマシンを作成する
AWS Step Functionsチュートリアル実践:Lambdaステートマシンの作成と実行 #reinvent

続きを読む

Cloud9 on Fargate など模索の経過報告

こんなの書いていきます

  • 利用例
  • 小ネタ

利用例

この辺に使えないかと模索しています

  • ECS のあふれたタスク処理
  • 踏み台サーバ
  • Cloud9 リモートサーバ

ECS のあふれたタスク処理

aws ecs describe-clusters--include "STATISTICS" を付与すると返ってくる pendingEC2TasksCount が 1 以上であれば run-task--launch-type FARGATE を指定。
とはいえスケーリングの設定やアプリケーションの作り次第では必ずしもこの負荷の逃がし方が適切でもなく、目下思案中。

踏み台サーバ

多段 SSH するときだけ起動する EC2 はありませんか?
Fargate にしてしまいましょう。

これくらいあれば最低限動きそうです。
https://github.com/pottava/fargate-shell/tree/master/serverless-bastion/docker

  • sshd に -d をつけて起動 することで、セッションが切れたら Fargate も落ちる
  • パスワード、または公開鍵認証(鍵は S3 を経由して配布)
  • sudo させるかどうかを環境変数 ENABLE_SUDO で制御

Docker イメージとして踏み台を管理できれば、これをベースに作業内容をログに残したり、渡す IAM タスクロールで AWS-CLI の利用できるコマンドを制限したりもある程度自由にカスタマイズできそうです。

Cloud9 リモートサーバ

Fargate で Cloud9 のリモートサーバを管理すれば、Docker イメージで作業者の環境を管理できそうです。ベースイメージはこんな感じ。
https://github.com/pottava/fargate-shell/blob/master/serverless-cloud9/docker/Dockerfile

  • AmazonLinux に必要なミドルウェアを入れたもの
  • Cloud9 からの SSH 接続に必要な鍵は S3 を経由して連携

その上で、開発に必要なミドルウェアを載せて、Cloud9 のリモートサーバに指定すれば IDE が起動します。例えば go v1.9.2 ならこんな感じ。
https://github.com/pottava/fargate-shell/tree/master/samples/cloud9-go1.9

ただし・・

  • docker in docker できないため、Cloud9 の
    IDE に c9.ide.lambda.docker はインストールできない
  • 作業が終了したら Fargate を明示的に停止する必要がある

のが惜しい感じになりました。AWS さんのネイティブ対応が待たれます。それにしても Fargate、ハンズオンや Jupyter notebook を配るといったことにも応用できそうです。

小ネタ

(以下 2017/12/12 時点のものであり、また仕様やドキュメントとして記載がないものも取り上げているため、機能追加や特にアナウンスなく変更が入る可能性も十分にありえます)

渡ってくる環境変数

  • AWS_DEFAULT_REGION
  • AWS_REGION
  • AWS_CONTAINER_CREDENTIALS_RELATIVE_URI

最後の変数でタスクに割り当てた IAM ロールを確認できる ものの
コンテナ内であればそのロールは Assume された状態なので
例えば以下のコマンドも同等の情報を返してくれます。普通。
$ aws sts get-caller-identity

渡せない環境変数

EC2 ホストがないので ECS Agent にオプションが渡せない。例えば ECS_ENABLE_CONTAINER_METADATAtrue にしたいけどできない。ECS_CONTAINER_METADATA_FILE が渡ってくると地味に便利なんですが・・

タスク定義の制約

とある理由で docker in docker がしたかったのですが、おそらくセキュリティ上の理由から ECS では設定できる以下の項目が使えません。まあ、はい。

  • linuxParameters/capabilities
  • privileged

awsvpc の制約

Fargate の注意点というわけではないものの、Fargate である以上 awsvpc が避けられないので。
ECS ではコンテナの定義として containerPorthostPort を別にすることができましたが、awsvpc ではそれが許されません。異なったポートを定義すると register-task-definition で弾かれます。

An error occurred (ClientException) when calling the RegisterTaskDefinition operation: When networkMode=awsvpc, the host ports and container ports in port mappings must match.

ENI の上限

起動するコンテナごとに一つ消費されていくので、初期リージョン上限である 350 個が一つのハードルでしょうか。Lambda ほどスケールしてくれませんが、まあ、ユースケース違うしね。ところでいつか awsvpc でない起動方法は追加されるんでしょうか。されない気もしますね・・

続きを読む

AnsibleでAWS環境(RDS)を自動構築する

はじめに

AWSの無料枠範囲で遊んでいます。

無料枠を超えないようにするため、作っては削除。作っては削除。をコンソールでやるのがめんどくさくなりました。

そのため、AnsibleでAWSの環境構築を自動構築できるようにしよう!ということで、
Ansible Playbookのサンプルを作成してます。

この記事で作成したplaybookはgithubで公開しています。

https://github.com/rednes/ansible-aws-sample
(AWS02フォルダ)

AWSの何を作るか

以下の内容をAnsibleで自動構築できるようにします。

  • VPCの作成
  • Internet Gatewayの作成
  • サブネットの作成
  • ルートテーブルの作成
  • セキュリティグループの作成
  • EC2インスタンスの作成
  • サブネットグループの作成
  • RDSの作成
  • EC2インスタンスにWordPress環境の構築

前提

  • ansible, botoをインストールすること
  • AWSのサーバー構築に使用するIAMユーザが作成されていること
  • AWSマネジメントコンソールでキーペア登録していること

AWS構成図

今回Ansibleで構築するAWSの構成図はこんな感じです。

AWS構成図

ディレクトリ構成

├── ansible.cfg
├── group_vars
│   └── all.yml
├── host_vars
│   └── localhost.yml
├── hosts
│   ├── ec2.ini
│   └── ec2.py
├── roles
│   ├── ec2
│   │   └── tasks
│   │       ├── ec2.yml
│   │       ├── main.yml
│   │       ├── security_group.yml
│   │       └── vpc.yml
│   ├── rds
│   │   └── tasks
│   │       ├── main.yml
│   │       └── rds.yml
│   ├── wordpress
│   │   ├── defaults
│   │   │   └── main.yml
│   │   └── tasks
│   │       └── main.yml
│   └── yum
│       ├── defaults
│       │   └── main.yml
│       └── tasks
│           └── main.yml
└── site.yml

Playbookの解説

AnsibleでAWS環境(RDS以外)を構築する内容については、過去の記事を参考にしてください。
今回はRDSの構築についてだけ説明します。

RDSの環境はrdsのroleで作成しています。

roles/rds/tasks/main.yml
---
- include_tasks: rds.yml

main.ymlでは単純にrds.ymlをインクルードしているだけです。
rds.ymlでRDSインスタンスを作成しています。

サブネットとセキュリティグループはec2のroleであわせて作成しています。

1. 異なるAZでサブネットを二つ作成

ec2_vpc_subnetモジュールでサブネットを構築します。
サブネットではVPC, Availability Zone, CIDRを指定します。

RDSでは単独のAZでしか使用しない場合でも、複数のAZにまたがったサブネットグループを作成する必要があるため、
今回は使用していませんがわざわざ使用しないAZにサブネットを作成しています。

サブネットグループ作成時に使用するサブネットを識別するため、タグに「route: private」を設定しています。

roles/ec2/tasks/vpc.ymlの一部
- name: subnet作成
  ec2_vpc_subnet:
    region: "{{ aws.common.region }}"
    state: present
    vpc_id: "{{ vpc_net.vpc.id }}"
    az: "{{ aws.common.region }}{{ item.value.zone }}"
    cidr: "{{ item.value.cidr }}"
    map_public: "{{ item.value.map_public|default(True) }}"
    tags: "{{ item.value.tags }}"
  with_dict: "{{ aws.vpc.subnet }}"
  register: vpc_subnet
host_vars/localhost.ymlの一部
aws:
  common:
    region: ap-northeast-1
  vpc:
    subnet:
      subnet1:
        tags:
          Name: public-a
          route: public
        cidr: 10.0.1.0/24
        zone: a
      subnet2:
        tags:
          Name: private-a
          route: private
        cidr: 10.0.2.0/24
        map_public: False
        zone: a
      subnet3:
        tags:
          Name: private-c
          route: private
        cidr: 10.0.3.0/24
        map_public: False
        zone: c

2. セキュリティグループを作成

ec2_groupモジュールでセキュリティグループを構築します。
セキュリティグループでは主にVPCとインバウンドルールを指定します。

AnsibleDBという名称のセキュリティグループを作成して、
EC2からのみ接続できるように設定しています。

roles/ec2/tasks/security_group.ymlの一部
- name: security group作成
  ec2_group:
    name: "{{ item.value.name }}"
    description: "{{ item.value.description }}"
    tags:
      Name: "{{ item.value.name }}"
      vpc_id: "{{ vpc_net_fact.vpcs[0].id }}"
    region: "{{ aws.common.region }}"
    purge_rules: "{{ item.value.purge_rules|default(False) }}"
    rules: "{{ item.value.rules }}"
  with_dict: "{{ aws.vpc.security_group }}"
  register: security_group
host_vars/localhost.ymlの一部
aws:
  common:
    region: ap-northeast-1
  vpc:
    security_group:
      security_group1:
        name: AnsibleWeb
        description: EC2 group
        rules:
          - proto: tcp
            ports:
              - 22
            cidr_ip: 0.0.0.0/0
          - proto: tcp
            ports:
              - 80
              - 443
            cidr_ip: 0.0.0.0/0
      security_group2:
        name: AnsibleDB
        description: EC2 group
        rules:
          - proto: tcp
            ports:
              - 3306
            cidr_ip: 10.0.1.0/24

3. サブネットグループを作成

rds_subnet_groupモジュールでサブネットグループを構築します。
サブネットグループでは主にsubnet idを指定します。

ec2_vpc_subnet_factsモジュールでタグが「route: private」である全てのサブネットを取得して、
一つのサブネットグループを作成しています。

roles/rds/tasks/rds.ymlの一部
- name: subnet取得
  ec2_vpc_subnet_facts:
    region: "{{ aws.common.region }}"
    filters:
      "tag:route": private
  register: ec2_subnet_facts
  check_mode: no

- name: rds-subnet-group作成
  rds_subnet_group:
    name: "{{ aws.vpc.rds.subnet_group.name }}"
    region: "{{ aws.common.region }}"
    state: present
    description: "{{ aws.vpc.rds.subnet_group.description }}"
    subnets: "{{ ec2_subnet_facts.subnets | map(attribute='id') | list }}"
  register: rds_subnet_group
host_vars/localhost.ymlの一部
aws:
  common:
    region: ap-northeast-1
  vpc:
    rds:
      subnet_group:
        name: wp-dbsubnet
        description: WordPress DB Subnet

4. RDSを作成

rdsモジュールでRDSインスタンスを構築します。
セキュリティグループはec2_group_factsモジュールを利用して、
名称からIDを引っ張ってきて定義しています。

roles/rds/tasks/rds.ymlの一部
- name: RDS作成
  rds:
    command: create
    instance_name: "{{ aws.vpc.rds.db_name }}"
    username: "{{ aws.vpc.rds.db_user }}"
    password: "{{ aws.vpc.rds.db_password }}"
    db_name: wordpress
    region: "{{ aws.common.region }}"
    subnet: "{{ aws.vpc.rds.subnet_group.name }}"
    vpc_security_groups: "{{ ec2_group_facts.security_groups[0].group_id }}"
    db_engine: "{{ aws.vpc.rds.db_engine }}"
    engine_version: "{{ aws.vpc.rds.engine_version }}"
    license_model: "{{ aws.vpc.rds.license_model }}"
    instance_type: "{{ aws.vpc.rds.instance }}"
    size: "{{ aws.vpc.rds.size }}"
    tags:
      Name: "{{ aws.vpc.rds.db_name }}"
  register: rds
host_vars/localhost.ymlの一部
aws:
  common:
    region: ap-northeast-1
  vpc:
    rds:
      db_name: wordpressdb
      db_user: root
      db_password: password
      db_engine: MySQL
      engine_version: 5.6.37
      license_model: general-public-license
      instance: db.t2.micro
      size: 20

注意点

RDSのエンドポイントがRDSを作成するまでわからないため、まず最初にRDSインスタンスまで作成した後に
RDSのエンドポイントをgroup_vars/all.ymlに追記する必要があります。

group_vars/all.ymlの一部
---
my_vars:
  rds:
    endpoint: XXXX # RDSで作成したDBのエンドポイントを入力

RDS作成が完了したら以下の様にawscli等を使用してエンドポイントを取得し、
上述のall.ymlにエンドポイントを記載するとEC2からRDSのmysqlに接続してWordPress環境を構築できます。

$ aws rds describe-db-instances --query="DBInstances[].Endpoint"

続きを読む