terraformでS3バケットのACLに権限を付与する

CloudFrontのアクセスログをS3に保存させる事はごくごく普通にあると思いますが、terraformで環境を構築しているとS3作成時に特殊なACLは設定できず、CloudFront作成時にログ出力指定をしていると権限不足で落ちてしまいます。

参考: アクセスログ – Amazon CloudFront

terraformだけで解決できれば一番良いのですが、2017年6月現在ではissueが出ているものの反映されるのはまだ先になりそうな感じです。

GUIからぽちぽちしてると勝手にACLを作ってくれるので、手で作った後にterraformに記載するとかいう意味のない作業をしていたのですが、そんな作業やりたくなかったので無理やり気味ではありますがterraform applyを実行するだけでS3にACLを付与し、CloudFrontの構築まで一気通貫で行えるようにちょっとした工夫を加えてみました。

準備するもの

linux/mac前提です。windowsの場合はbash on windowsであれば動作可能です。

必須

  • terraform
  • AWS CLI

推奨

  • direnv

準備するスクリプト

bin/AssignAwsdatafeedsAcl
#!/bin/sh

# 自身のディレクトリパスを取得
CURRENT_DIR=$(cd $(dirname $0);pwd)/

# 引数のチェックを行う
if [ $# -ne 1 ]; then
    exit 1
fi

# バケット名を引数から取得
BUCKET_NAME=$1

TEMP_FILE=$(mktemp)

# S3待ち(waitが無い場合にエラーが発生)
sleep 2

# 現在のバケットACL情報を取得する
aws s3api get-bucket-acl --bucket ${BUCKET_NAME} > ${TEMP_FILE}
if [ $? -ne 0 ]; then
    # テンポラリファイルを削除する
    rm -f ${TEMP_FILE}
    exit 1
fi

# ACLにawsdatafeeds権限が含まれていない場合は追加する
grep awsdatafeeds ${TEMP_FILE}
if [ $? -eq 1 ]; then
    # 追記する文字列を取得
    INPUT_LINES=$(perl -p -e 's/n/\n/' ${CURRENT_DIR}acl.txt)
    if [ $? -ne 0 ]; then
        # テンポラリファイルを削除する
        rm -f ${TEMP_FILE}
        exit 1
    fi

    # 権限情報を追記
    sed -i -e "s/("Grants": *[)/1n${INPUT_LINES}/" ${TEMP_FILE}
    if [ $? -ne 0 ]; then
        # テンポラリファイルを削除する
        rm -f ${TEMP_FILE}
        exit 1
    fi

    # 権限情報をS3バケットに反映
    aws s3api put-bucket-acl --bucket ${BUCKET_NAME} --access-control-policy "$(cat ${TEMP_FILE})"
    if [ $? -ne 0 ]; then
        # テンポラリファイルを削除する
        rm -f ${TEMP_FILE}
        exit 1
    fi
fi

# テンポラリファイルを削除する
rm -f ${TEMP_FILE}

exit 0
bin/acl.txt
        {
            "Permission": "FULL_CONTROL",
            "Grantee": {
                "DisplayName": "awsdatafeeds",
                "ID": "c4c1ede66af53448b93c283ce9448c4ba468c9432aa01d700d3878632f77d2d0",
                "Type": "CanonicalUser"
            }
        },

上記2ファイルをパスが通ったところに配置しておきます。
シェルスクリプトの方にはちゃんと実行権限を付与しましょう。

ちなみにdirenvを使って、terraform実行パスなどでPATHを追加するのが推奨です。

.envrc
export AWS_DEFAULT_PROFILE=[AWSプロファイル名]
export AWS_DEFAULT_REGION=[デフォルトとするリージョン]

export AWS_PROFILE=$AWS_DEFAULT_PROFILE
export AWS_REGION=$AWS_DEFAULT_REGION

PATH_add `pwd`/bin

terraformの記述

s3.tf
resource "aws_s3_bucket" "sample" {
    bucket = "sample-cfn-logs"
    acl    = "private"
    tags {
         Name = "sample-cfn-logs"
    }

    provisioner "local-exec" {
         command = "AssignAwsdatafeedsAcl ${aws_s3_bucket.test.bucket}"
    }
}

概要

terraformのprovisionerにlocal-execという、実行マシンでのコマンド実行機能があります。

これはリソースが作成されたのちに実行されるので、S3リソースが作成された後にシェルスクリプトを実行し、AWS CLIを利用してS3バケットにACLを付与しています。

上記スクリプトでは下記の様な処理を行っています。

  1. aws cli: S3バケットの設定済みACLを取得
  2. shell: 取得したACLにawsdatafeedsの権限が付与されているかチェックする
  3. shell: 権限がない場合、権限の内容を取得したACLに追記する
  4. aws cli: 改変したACL情報をS3バケットに反映する

S3バケットが構築されてからAPIで叩けるまでに微妙なタイムラグがあるらしく、sleepが入っていないとAPIを叩いた時にバケットが存在しないと怒られてしまいます。

ちゃんとするならば、APIのレスポンスを見て待つ処理を入れるのが良いのですが、terraformがMulti ACLに対応するまでの暫定的な対応なのでsleepで濁しています。

配置したスクリプトにパスが通っていないとlocal-execの指定時にわざわざパスを書いてあげる必要があるので、direnv使ってパス通しちゃいましょう。

相対パスがどこからになるのか知らない。

ちなみにacl.txtの内容を変えると好きな権限を入れれます。でもあまり使わないですし変更検知もされないので推奨はしません。

どうしてもという場合はaws_s3_bucketリソースの代わりにnull_resourceリソースを使って毎回スクリプトをキックするようにした上で、前回実行のACLと反映するACLに差分がある時に実行するなど工夫をしてみてください。

結論

はよterraform自体で対応して。

続きを読む

Terraformを利用して、AWS EC2を作成してみた

概要

Terraformを利用してAWSのEC2を作成してみました。
terraformを実行するのはIAM Roleで権限を付与したインスタンスからになります。

ドキュメントを見るといろいろと出来そうですね。
https://www.terraform.io/docs/

コードは下記になります。
同じ階層のディレクトリに変数の設定を入れてあげてください。

ec2_dev.tf

provider "aws" {
  region = "${var.region}"
}

resource "aws_instance" "dev" {
    ami = "${var.ami_id}"
    instance_type = "${var.instance_type}"
    key_name = "${var.key_name}"
    iam_instance_profile = "${var.iam_role}"
    vpc_security_group_ids = [
        "${var.sg_id}"
    ]
    subnet_id = "${var.subnet_id}"
    associate_public_ip_address = "true"
    root_block_device {
        volume_type = "gp2"
        volume_size = "8"
        delete_on_termination = "true"
    }
    tags {
        Name = "${var.tag_name}"
        env = "${var.tag_env}"
    }
}

https://github.com/handa3/study/blob/master/terraform/ec2/ec2_dev.tf

続きを読む

tectonic で kubernetes クラスタを構築・管理する(AWS)

概要

tectonic で AWS に kubernetes クラスタを構築する。

対応プラットフォーム(2017.06.09 時点)
  • AWS
  • Bare Metal
  • Microsoft Azure (alpha)
  • OpenStack (pre-alpha)
料金
  • 10ノードまで無料

環境

インストーラを実行した環境
– Amazon Linux AMI 2017.03
– tectonic 1.6.4

事前準備

セットアップ前に必要な作業

  • CoreOS

    • CoreOS アカウントサインアップ
    • CoreOS License 取得 (tectonic-license.txt)
    • Secret 取得 (config.json)
  • AWS

    • IAM Role 設定 (Policy)
    • Route 53 でパブリックホストゾーンの作成

tectonic のインストーラー取得

ダウンロード・解凍

$ wget https://releases.tectonic.com/tectonic-1.6.4-tectonic.1.tar.gz
$ tar xzvf tectonic-1.6.4-tectonic.1.tar.gz
$ cd tectonic

tectonic セットアップ (GUI)

インストーラー起動

$ ./tectonic-installer/linux/installer -address=0.0.0.0:4444 -open-browser=false
Starting Tectonic Installer on 0.0.0.0:4444

ブラウザで インストーラーを起動したホストIPアドレス:4444 にアクセスして、画面に従いセットアップする

01.png

tectonic セットアップ (terraform)

インストーラーの環境設定

$ export INSTALLER_PATH=$(pwd)/tectonic-installer/linux/installer
$ export PATH=$PATH:$(pwd)/tectonic-installer/linux

terraform 環境設定

$ sed "s|<PATH_TO_INSTALLER>|$INSTALLER_PATH|g" terraformrc.example > .terraformrc
$ export TERRAFORM_CONFIG=$(pwd)/.terraformrc

cluster resources 取得

$ terraform get platforms/aws

credential を環境変数に設定

$ export AWS_ACCESS_KEY_ID=AK***************
$ export AWS_SECRET_ACCESS_KEY=**************************************
$ export AWS_REGION=ap-northeast-1

クラスタ設定

クラスタ名を設定

$ export CLUSTER=cluster-1

クラスタ環境設定

$ mkdir -p build/${CLUSTER}
$ cp examples/terraform.tfvars.aws build/${CLUSTER}/terraform.tfvars

terraform 環境設定

$ vi build/cluster-1/terraform.tfvars

設定内容

tectonic_admin_email = "********************"   #tectonic console ログインID
tectonic_admin_password_hash = "*************"  #ログインパスワード※

tectonic_aws_az_count = "1"
tectonic_aws_region = "ap-northeast-1"
tectonic_aws_ssh_key = "************"           #AWS に登録した Key pair

tectonic_base_domain = "*************"          #Route 53 に登録したドメイン
tectonic_cluster_name = "cluster-1"             #クラスタ名

tectonic_license_path = "/path/to/tectonic-license.txt" #事前に取得した CoreOS Lisence
tectonic_pull_secret_path = "/path/to/config.json"      #事前に取得した CoreOS Secret

tectonic_worker_count = "2"                     #worker ノード数

tectonic_admin_password_hash は bcrypt で hash 化する (bcrypt-hash tool)

$ wget https://github.com/coreos/bcrypt-tool/releases/download/v1.0.0/bcrypt-tool-v1.0.0-linux-amd64.tar.gz
$ tar zxvf bcrypt-tool-v1.0.0-linux-amd64.tar.gz
$ ./bcrypt-tool/bcrypt-tool 
Enter password: 
Re-enter password: 
$2a$10$*****************************************************

plan 実行

$ terraform plan -var-file=build/${CLUSTER}/terraform.tfvars platforms/aws
Plan: 116 to add, 0 to change, 0 to destroy.

クラスタをデプロイ

apply 実行

$ terraform apply -var-file=build/${CLUSTER}/terraform.tfvars platforms/aws

クラスタへのアクセス

作成された kubeconfig を読み込み

$ export KUBECONFIG=generated/auth/kubeconfig

クラスタ確認

$ kubectl cluster-info
Kubernetes master is running at https://cluster-1-api.***.****.jp:443
KubeDNS is running at https://cluster-1-api.***.****.jp:443/api/v1/proxy/namespaces/kube-system/services/kube-dns

ノード確認

$ kubectl get node
NAME                                             STATUS    AGE       VERSION
ip-10-0-22-59.ap-northeast-1.compute.internal    Ready     4m        v1.6.4+coreos.0
ip-10-0-37-60.ap-northeast-1.compute.internal    Ready     2m        v1.6.4+coreos.0
ip-10-0-52-127.ap-northeast-1.compute.internal   Ready     1m        v1.6.4+coreos.0

tectonic console にアクセス

ブラウザで https://{{ tectoniccluster_name }}.{{ tectonic_base_domain }}/ にアクセス

12.png

設定したログインID、パスワードでログインする

13.png

続きを読む

AWS Summit Tokyo 2017 参加レポート Day3 (6/1)

会社の外部研修として『AWS Summit Tokyo 2017』に6月1日(木)~2日(金)の計2日間参加して来ました。

当エントリでは6月1日(Day3)に聴講した内容をレポートします。(Day4のレポートはこちら
※注)記事には個人的なメモや感想が含まれています。予めご承知ください。


基調講演(Key Note) (10:00 ~ 11:30)

ホストトーク:オープニング

by Werner Vogels 氏 (Amazon.com CTO)

  • AWSは急速に成長している

    • 成長率はIT企業の中でもTOP
    • 毎月のユーザー増加数 = 日本では10万以上
    • 各国のリージョン、AZ、エッジロケーションも随時拡大中
  • 本イベントを通してAWSを色々学んで行って欲しい。

気になったワードメモ

  • AWS Activate
  • One Amazon
  • Osaka リージョン開始

ゲストトーク:株式会社ソラコム

by 安川 健太 氏 (株式会社ソラコム 最高技術責任者)

SORACOMのサービス展開

IoTはネットワークセキュリティが課題
>3G/LTE網でInternetに出ないIoTネットワーク(デバイスとクラウドの直接接続)を提供

SORACOM Funnel
>デバイスからクラウドへのデータ転送を疎結合に管理
 利用例:デバイス>Funnel>Kinesis>Lambda>AWS IoT
  ⇒Funnelから先の切り替え(=新サービス追加など)が容易

ホストトーク:AWS – SUPER POWERS 1

by Werner Vogels 氏 (Amazon.com CTO)

「AWSは開発者に『SUPER POWERS※』を与える。」 (※ネタ元:スーパーマン)

SUPER POWER – ‘SPEED’ (超音速)

  • EC2新インスタンス:F1(FPGA利用可)
  • 今の時代 検索は必須>Elastic Search等ですぐに実装可
  • CloudはInfraへの関心を省き、Productに集中させてくれる>開発を加速
  • AWSは色々な選択肢を提供>ビジネスに合わせて適切なものを

ゲストトーク:NTT東日本

by 中村 浩 氏 (東日本電信電話株式会社 取締役)

CloudGateway re:connect
>FLETS光の高速・閉域網でAWSと再接続

社内で開発・利用して便利だったものを社外にも利用してほしい
クラウドと企業間の距離が一番近い国に

ホストトーク:AWS – SUPER POWERS 2

by Werner Vogels 氏 (Amazon.com CTO)

SUPER POWER – ‘INVISIBILITY’ (目に見えない)

⇒開発者がインフラを気にしなくていい ⇒サーバーレス機能群紹介

  • AWS Lambda

    • サーバーレスコンピュート
  • AWS Step Functions
    • ビジュアルワークフローで分散アプリケーションのコンポーネント管理
  • AWS X-Ray
    • 分散アプリケーションの分析とデバッグ
  • Amazon DynamoDB Accelerator(DAX) :new:
    • 完全マネージド型インメモリキャッシュクラスタでクエリ超高速化

ゲストトーク:ソニーモバイルコミュニケーションズ

by 川西 泉 氏 (ソニーモバイルコミュニケーションズ株式会社 取締役 EVP)

SonyモバイルのIoTへの取り組み:『カーリングビジネスの実現』

製品紹介
– XPERIA Ear
– XPERIA Touch
– XPERIA Agent
  >http://av.watch.impress.co.jp/docs/news/1018278.html

スマートホームシステム
 AWS IoTを活用
 製品例:自宅のLED照明>センサーで子供の帰宅感知>仕事(外出)中の親に通知>LED付属のスピーカで会話

ホストトーク:AWS – SUPER POWERS 3

by Werner Vogels 氏 (Amazon.com CTO)

SUPER POWER – ‘FLIGHT’ (飛び立つ)

IoTデバイスはサーバを必要としない

  • AWS Greengrass
     ⇒サーバ側の役目だった処理をローカルで構築可能に

これまでのデータベース=実機やベンダー由来の様々な制約や障害があった
クラウド+オープンソースのDBで”制約”から飛び立とう

  • AWS Database Migration Service
  • Amazon Aurora
    • 完全MySQL互換のRDB
    • PostgreSQL互換も提供開始

ゲストトーク:グリー株式会社

by 藤本 真樹 氏 (グリー株式会社 開発・人事統括 取締役 執行役員常務 CTO)

オンプレで10年運用してきた環境をクラウドへ移行した話

技術選択の判断は難しい、基準には色々な軸がある
敢えて上げるならば、”is that faster?”
>速さは裏切らない(コンピュータが速くて困ることは絶対にない)

⇒ AWS(の機能やサービス展開速度)はこれらに応えられる

ホストトーク:AWS – SUPER POWERS 4

by Werner Vogels 氏 (Amazon.com CTO)

SUPER POWER – ‘X-RAY VISION’ (透視)

⇒ データの集計・可視化

  • Amazon Athena

    • S3に保存されたデータを標準SQLで簡単に分析
  • Amazon EMR
    • Hadoopなどのビッグデータフレームワークを手軽に実行・分析可能
  • Amazon Redshift
    • 複雑なクエリ・超高速パフォーマンスを提供するペタバイト級データウェアハウス
  • Amazon Redshift Spectrum :new:
    • S3のデータを直接クエリ可能
    • エクサバイト規模のクエリ>Hiveで5年想定の処理を155秒で完了

SUPER POWER – ‘PRECOGNITION’ (予見)

⇒ Amazon AI – 人工知能サービス

  • Amazon Machine Learning

    • カスタム予測モデルの学習
  • Amazon Rekognition
    • 画像から顔と感情を認識
  • Amazon Polly
    • 文章をリアルな音声に変換(感情付加など)
  • Amazon Lex
    • 自動音声認識・自然言語理解

SUPER POWER – ‘IMMORTALITY’ (不朽)

⇒ スタートアップ企業生き残りの鍵 =『デジタルトランスフォーメーション』

AWSは『イノベーション』を続ける


AWS 認定試験 (12:30 ~ 13:50)

Summit特設会場でソリューションアーキテクトアソシエイト試験を受験(特典の50%OFFクーポン目当てです ^^; )

試験終了後すぐに認定者限定ラウンジに行ってみましたが、ノベルティの折り畳み傘はとっくに品切れでした;;
やはり一日数量限定は午前中に行かないと無理な模様。( ⇒ Day4で無事GET!!)

限定ラウンジではお菓子や飲み物が無料、座ってPCなどの充電が出来て中々快適でした。
モニタなど設置して講演中のセッションを視聴できればいいなと考えましたが、逆に人が溢れて寛げなくなりそう?


Session:『AWS Shield と AWS Lambda@Edge で構築するセキュアで柔軟性の高いアプリケーション』 (14:20 ~ 15:00)

スピーカー:Prasad Kalyanaraman 氏 (Vice President, AWS Edge Services)

セッション概要(タイトルリンク先より引用)

AWS Edge Services では、Amazon CloudFront に加え、AWS Shield、 AWS Lambda@Edge などの革新的な新サービスを発表しています。本セッションでは、AWS Shield の DDoS 防御機能と、AWS Lambda@Edge の CDN 上でのスクリプト実行により、セキュリティを担保しつつ柔軟でカスタマイズ性の高いアプリケーションの実現方法をご紹介します。

エッジでのセキュリティ活用

セキュアコンテンツ配信にCloudFrontが使える
 >エンドユーザー付近をセキュア通信の終端にする

Lambda@Edge

=Lambda関数のデリバリーサービス
 ・Bot 判定
 ・バリデーション
 ・認証処理
  ⇒エンドユーザ付近で実行できる
 ・ユーザ毎にコンテンツカスタマイズ可能
  e.g.
   ・PC/モバイル判定>アクセス先分岐
   ・エッジでHSTSヘッダを埋込み

DDoS対策 (スクラビングセンター)

 >AWS Shield (全Edgeロケーションで使用可)
  >Standard
   ・エッジで攻撃を検知し、遮断
   ・リアルタイムで悪意のあるトラフィックを検出
   ・無料で自動適用
  >Advanced
   ・攻撃データの可視化、およびイベント後の分析と調査を容易に
   ・DDoS攻撃で請求金額が跳ね上がるのを防ぐ「DDoS コスト保護」
   ・専門サポート
   ・有料

AWS WAF

 >L7(アプリケーションレイヤー)防御
 >Lambdaで独自のセキュリティチェック可 (e.g. 特定のIPを一定時間ブロック等)


Session:『Amazon ECS と SpotFleet を活用した低コストでスケーラブルなジョブワーカーシステム』 (15:20 ~ 16:00)

スピーカー:松田 和樹 氏 (株式会社インティメート・マージャー 開発本部)

セッション概要(タイトルリンク先より引用)

インティメート・マージャーではビジネスの都合上、50 種類ものジョブワーカーを運用しております。増え続けるビジネス要件に対応するため、Amazon ECS と SpotFleet を軸に Amazon S3、Amazon SQS、Autoscaling を組み合わせることで、低コストかつスケーラブルな docker 基盤を構築致しました。本セッションでは、その docker 基盤を中心にお話しします。

公演に使用されたスライドが公開されてます。

構築背景

  • 20以上の社外システムとデータ受渡し

    • 異なるデータ形式
    • 異なる接続方式
  • データ肥大化
    • スケールする仕組みが必要
    • 特定のジョブのみスケールさせたい

第一世代

処理フロー

S3ファイルUP>イベントをSQS通知>Cloud Watchでキュー数監視>EC2のワーカーをAutoScaling (Spot)
 >SQSキュー&S3ファイルを取得し処理>後続処理は別のS3へ

課題

  • スポット高騰時など特定のワーカーが起動不可となる
  • リソース(InstanceType)効率が悪い
  • 待機時間が長い
  • スケーリング設定多く、運用負荷高い

第二世代

変更点

  • 全ワーカーの実行環境をコンテナに
  • docker基盤共用
  • ECS採用
  • spot fleet採用
  • ワーカー毎にコンテナをスケール

Amazon ECS

  • 52のサービスが稼働 ( 50種のワーカー + Mackerel(監視) + Fluentd(ログコレクタ) )
  • コンテナレジストリ ⇒ Amazon ECR

Spot fleet

  • 168 vCPU
  • 自動入札(一つ一つ設定も可能だが手間)
  • InstanceType:7種 主にC4、M4系

運用のポイント

ログのハンドリング

  • 目的で集約先使い分け

    • 常時監視 ⇒logdna
    • 長期分析 ⇒BigQuery
    • 直近データ分析、参照 ⇒Aurora

環境構築・デプロイ

  • Terraform採用
  • CircleCIからAPIでデプロイ
     ⇒docker対応CIサービスがおすすめ

強制ターミネート(Spotインスタンス)

  • コンテナ強制終了
  • 検知する仕組み必要
    • メタデータをポーリング⇒クラスタから退役

AMI

  • ECS-Optimized AMI 使用
  • AMIのカスタマイズはしない
  • 設定はcloud-initで

ECSの課題

  • クラスタ管理

    • agent方式⇒ラグが多々
    • ゾンビワーカー
  • docker全機能は使えない
  • コンソール画面がまだまだ
  • 学習コストそこそこ

ECSの強みは他AWSサービスとの連携


Session:『AWS で実現するセキュリティ・オートメーション』 (16:20 ~ 17:00)

スピーカー:桐山 隼人 氏 (AWSジャパン 技術統括本部 ソリューションアーキテクト)

セッション概要(タイトルリンク先より引用)

クラウドは、今までやってきたことを効率的にするだけでなく、今までできなかったことを可能にします。本セッションでは、セキュリティをクラウド環境で実現することで可能となる、運用統合と自動化(オートメーション)をご紹介します。セキュリティ・オートメーションは、貴社の運用が変わるだけでなく、セキュリティ戦略そのものを見直すきっかけにもなります。オートメーションによる次世代のセキュリティを考えてみませんか?

※スライドは公開されていないようですが、ほぼこれと同じだったかと思います。

クラウド移行に関するアンケート

日:クラウドに移行しない理由 1位 ⇒セキュリティ
米:クラウドに移行した理由 1位 ⇒セキュリティ
 ⇒まだ正確な認識が普及していないせいと思われる

クラウドだからできるセキュリティ

オートメーションとは=戦略策定の基盤
AWSはセキュリティオートメーション前提に設計されている ⇒基盤として活用してほしい

  • 戦略策定

    • SFA、マーケティングに活用
    • やること やらないこと を決められる
  • 何を自動化すべきか? (考える主軸 ⇒)

    • 対策主体(人、組織、技術)
    • 対策対象(サーバ、ネットワーク、クライアント)
    • 対策場所(入口、内部、出口)

ガートナーの『適応型セキュリティアーキテクチャ』

サイクルを回す:
 防御⇒検知⇒対応⇒予測⇒~

 防御:CloudFront、WAF ~
 検知:VPC Logs、Auto Scaling ~
 対応:SNS、Lambda ~
 予測:EC2 Config、3rd party レピュテーション、Inspector ~

実用例

CloudFrontアクセス⇒WAF⇒LambdaでレピュテーションリストDL&判定
 ⇒Lambdaでセキュリティ評価⇒Amazon Inspector⇒SNS⇒LambdaでNACL/SG変更
 ⇒攻撃検知⇒ブロックログ⇒EBSなどsnapshot⇒CloudTrail(操作ログ)
全レイヤー、ノードのログが取れるので
 ⇒VPC Flow Logs/Elastic Search/Kibanaなどで可視化
攻撃者のドメイン生成は自動化されている:Domain Generation Algorithm(DGA)
 ⇒AWS WAF + Amazon MLで怪しいドメインを学習
Machine Learningでリスク分析
 ⇒設定変更などの意思決定まで自動化


Session:『Machine Learning on AWS』 (17:20 ~ 18:00)

スピーカー:志村 誠 氏 (AWSジャパン 技術統括本部 ソリューションアーキテクト)

セッション概要(タイトルリンク先より引用)

AI や機械学習という言葉が話題になる遥か前から、Amazon では機械学習技術を活用したさまざまな取り組みを行ってきました。本セッションでは AWS の上でご利用いただける機械学習サービスについてご紹介するととともに、それらをどのように使い分けるか、また機械学習をどのように皆さまのサービスに役立てて行くかについてご紹介します。

どんなサービスで機械学習を活用するか?

ビジネス主体で考える(使いたい技術から考えない)

  • 良質なデータが継続的に入るか
  • 自動化の価値ある予測か
  • 費用対効果

機械学習はあくまでツール
需要に反し解決していない問題に対して検討する

代表的な活用例>
– レコメンド
– 異常検知
– 画像認識
– クラスタリング(ユーザの分類分けなど)

AWSで提供する機械学習サービス

グルーピング>

  • Service
  • platform
  • Engines
  • Hardware

AI Hardware

  • EC2:P2 instance

    • GPU特化
  • Greengrass
    • デバイスに学習モデル
    • エッジで推論処理

AI Engines

典型的な機械学習フレームワークをインストール済みのAMIを提供

AI platform

  • Amazon Machine Learning
  • Amazon EMR

AI Service

  • Polly
  • Rekognition
  • Lex

その他

Kinesis Analytics >異常検知
Elastic Search >検索を機械学習に利用
Data Pipeline >EMRのジョブをスケジューリング

ゴールを明確に

  • 解決すべき課題
  • アウトプット

続き⇒Day4

続きを読む

Amazon ECSを用いたDocker本番運用の実現

はじめに

現在お手伝いしているアカウンティング・サース・ジャパンにて、ECSを使ったDockerの本番運用を始めたので、その一連の流れについてまとめました。

税理士向け会計システムを扱うアカウンティング・サース・ジャパンでは最近Scalaでの新規プロジェクトが立ち上がってきており、既存のプロジェクトはJavaであったり、Erlangであったりと様々な言語が用いられていますが、インフラ人員が少ないということもあり、なるべくシンプルなインフラ構成を実現する必要がありました。

そういった中、各アプリケーションをDocker化することでインフラとしては共通基盤としてのDockerクラスタのみの管理になり、運用コストが下がるのではないかという仮説からDocker化を進めることになりました。クラスタを実現するに辺りKubenatesなどの選択肢もありましたが、今回はECSを選択し、下記のようにAWSのマネージドサービスを最大限に活用しています。

  • オーケストレーションツール: Amazon EC2 Container Service (ECS)
  • サービスディスカバリ: Application Load Balancer (ALB)
  • Dockerレジストリ: Amazon ECR
  • ログ、メトリクス収集: CloudWatch, CloudWatch Logs
  • 監視: CloudWatch Alarms
  • Infrastructure as Code: CloudFormation
  • CIツール: Jenkins

各技術の選定理由

今回Docker化を行うに辺り、下記を優先的に技術選定を行いました。

  • 運用が楽であること
  • 構成がシンプルで、技術の学習コストが低いこと

まずは、オーケストレーションツールの選定です。候補に上がったのは、Docker Swarm、Kubernetes、ECSです。

DockerのSwarm modeは本番での運用例が技術選定時点であまり見当たらなかったので候補から落としました。次にKubernetesとECSですが、海外の事例などではどちらも多く使われているようです。

今回は多機能さよりも運用に手間がかからない方が良いと考え、マネージドサービスであるECSが第一候補にあがりました。ここは詳細に調査したというよりも、ある種勢いで決めています。その上でやりたいことが実現できるかどうか一つ一つ技術検証を行った上で導入判断を行いました。

同じようにマネージドサービスを優先的に使ったほうが良いという考えで、ログなどでもCloudWatchを使っています。

AWSインフラをコードで記述するものとしてはTerraformが良く取り上げられている気がしますが、個人的にはいくつかの理由でCloudFormationを推しているのでこちらを使っています。

CIツールですが、社内の標準であるJenkinsをそのまま使うことにしました。

全体構成

下記のような構成になっています。

スクリーンショット 2017-05-21 12.46.39.png

ざっくりと説明すると、developmentブランチにプッシュするとGithub HookでJenkinsがDockerイメージをビルドして、ECRにPushします。ユーザはJenkinsでDeployジョブを実行(あるいはBuildの後続ジョブとして自動実行)し、CloudFormationにyamlファイルを適用することでTask, Service, ALB, Route53設定, CloudWatch設定を一通り実行します。またECSのClusterはあらかじめCloudFormationテンプレートを作成して作っておきます。

Task/Serviceの更新についてはCloudFormationを経由しない方がシンプルかとは思いまいしたが、Service毎に管理するRoute53やCloudWatchと合わせて一つのテンプレートにしてしまうのが良いと判断しました。

ここまでやるなら専用のデプロイ管理ツールを作った方がとも思ったのですが、業務委託という立場で自分しかメンテができないものを残すものは躊躇されたため、あくまでAWSとJenkinsの標準的な機能を組み合わせて実現しています。

CloudFormationテンプレートの解説

上記の流れが全てなので理解は難しくないと思いますが、一連の処理で重要なポイントとなるのはCloudFormationテンプレートなのでこれについてだけ触れておきます。長いテンプレートなのでざっくりとだけ雰囲気を掴んでもらえればと思います。

ECSクラスタのテンプレート

cluster作成用のCloudFormationテンプレートは下記のようになっています。

gist:cluster.yaml

一見複雑に見えますが、Amazon EC2 Container Service テンプレートスニペットを参考に作ると簡単に作成できると思います。

(あまりそのまま書くと会社に怒られそうなため)省略していますが、実際にはここにECSクラスタの監視を行うCloudWatch Alarmなどを設定することで、監視設定までこのテンプレートだけで完了します。

ECSクラスタはインフラチーム側であらかじめ用意しておき、リソースが足りなくなったときなどには適宜インスタンス数を変更したりクラスタ自体を別途作ったりしていきます。オートスケーリングを導入すればそれすら必要なくなります(今回はDocker運用が初めてだったので知見がたまるまで手動での対応にしています)。

インフラ側としての責務はここまでで、下記のテンプレートで定義される個別のサービスについてはアプリ開発者側の責務として明確に責任境界を分けました。(もちろん実際にはサポートはかなりの部分でしています。)

これにより全員が今までよりインフラに近い領域まで意識するように個人の意識が変わっていくことを期待しています。

個別サービス用テンプレート

開発環境、ステージング環境、プロダクション環境などそれぞれで同一のテンプレートを使うようにし、パラメータを使用します。そのパラメータをJenkinsのジョブ内で注入することで実現します。VPCなどの環境で決まる値はJenkinsジョブで実行するスクリプト内で定義し、アプリケーションごとの値は environment.yaml というファイルを用意してスクリプトから読み込みます。

environment.yamlは例えば下記のようになっています。アプリケーション開発者は、特殊なことをしない限りは service.yaml をインフラチームが用意したservice.yamlをコピーして、environment.yamlだけ編集すれば良い形になっています。DSLですら無いのでアプリ側のメンバーも心理的な抵抗が少ないようで良かったです。

environment.yaml
images:
- xxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/hoge-image
parameters:
  default:
    TaskMemory: 512
    TaskMaxMemory: 990
    ImageRepositoryUrl: xxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/hoge-image
    ServiceDesiredCount: 1
  dev:
    ClusterName: dev-default
    JavaOpts: "-Xmx256MB"
  stg:
    ClusterName: stg-default
    JavaOpts: "-Xmx256MB"
  prod:
    ClusterName: default
    JavaOpts: "-Xmx1500MB -Xms1500MB"
    TaskMemory: 1990
    TaskMaxMemory: 1990
    ServiceDesiredCount: 2

そして service.yaml は下記のようなファイルです。

gist:service.yaml

これもAmazon EC2 Container Service テンプレートスニペットから作ればすぐにできるのではないかと思います。(もちろん全てのパラメータは一つ一つ値を検討します。)

こちらもCloudWatch周りや重要でないところは削除しています。色々と手で削ってるのでコピペだと動かない可能性大ですが雰囲気だけ掴んで貰えればと思います。

このファイルは全アプリケーションで同一ファイルを使うのではなく、アプリケーションごとにコピー/編集して利用します。全体の変更を行うときには全プロジェクトのファイルを更新しなければいけませんが、共通基盤がアプリケーション側を制約しないように、プロジェクト毎のyamlファイル管理としています。ファイルの配置場所は各Gitリポジトリに配置するのが理想ですが、現状ではDocker運用になれてくるまで全てのyamlファイルを管理するリポジトリを作成してインフラチーム側が主に編集する形を取っています。

デプロイ

あとは、このservice.yamlとenvironment.yamlを組み合わせてデプロイするRubyスクリプトでもJenkinsのPipelineのコードでも適当に書いてJenkinsのJobを登録すれば完了です。(environment.yamlファイルを読み込んで aws cloudformation create-stack でservice.yamlと共にパラメータとして渡すだけなので簡単です!)

新規アプリ開発時も社内標準のservice.yamlとenvironment.yamlをファイルを持ってきて、environment.yamlを修正した上で、Jenkinsにジョブを登録すればすぐにDockerクラスタへのデプロイ準備が整います。しかも、上記のテンプレート例では割愛していますが、テンプレートには監視項目/通知設定まで書かれているので、インフラ側で設定を行う必要もなく監視が開始されます。CloudFormation最高ですね。

おわりに

実際の運用ではミッションクリティカルなアプリケーションならではの品質管理のために、JenkinsのPipeline機能を利用して開発→検証→リリースまでのデプロイメントパイプラインを実現しています。

アプリケーションのSECRETなどコミットしない情報をどう管理するかも検討する必要がありますが、これは管理の仕方はチームによって異なると思ったため割愛しています。

また、ログ解析としてはS3に出されたALBのログをRedash+Amazon Athenaでエラー率やアクセス数を分析できるようにし、CPU使用率やメモリ使用率などのパフォーマンス状況をCloudWatchの内容をGrafanaで可視化しています。これによりログ収集の基盤などを作らずに必要な可視化を実現することができました。ベンチャーでは分析基盤の運用も大きなコストになってしまうため、こういった工夫も必要です。(もちろん重要なKPIについては別途分析する仕組みが整っています。)

今回の構成が最高とは思いませんが、ある程度満足行くところまではできたかなと思います。もっとよくできるよ!とか一緒にやりたいな!とかもっと詳細聞きたいな!いう方はぜひ @miyasakura_ までご一報ください。

続きを読む

Terraform Best Practices in 2017

Terraform Best Practices in 2017

以下のブログをベースにver0.9の新機能のstate environmentsや、backend、remote stateを活用してベストプラクティスを考えた。
細かい話は以下のブログを参照いただき、ver0.9に対応した内容だけ記載します。
Terraformにおけるディレクトリ構造のベストプラクティス | Developers.IO

サンプルコード

サンプルコードを置きましたので、イメージが付かない場合は以下を見てみて下さい。
(適当に作ったので間違えてたらプルリクください)
https://github.com/shogomuranushi/oreno-terraform

ディレクトリ構造

├── environments
│   ├── not_immutable
│   │   ├── provider.tf
│   │   ├── backend.tf
│   │   ├── variable.tf
│   │   ├── main.tf
│   │   └── output.tf
│   └── immutable
│       ├── provider.tf
│       ├── backend.tf
│       ├── variable.tf
│       ├── main.tf
│       └── output.tf
└── modules
    ├── compute
    │   ├── ec2.tf
    │   ├── elb.tf
    │   ├── output.tf
    │   ├── userdata.sh
    │   └── variable.tf
    ├── db
    │   ├── main.tf
    │   ├── output.tf
    │   └── variable.tf
    └── vpc
        ├── main.tf
        ├── output.tf
        └── variable.tf

ディレクトリ構造のポイント

1. environments配下の分け方

  • tfstateファイルで管理する範囲が大きいと問題があった際の影響範囲が大きくなるため実行単位を小さくする
  • 今回の場合は、not_immutableとimmutableで分けて、それぞれの配下でterraformを実行する
  • terraformの実行単位を分けるとterraform間での値の受け渡しが通常とは異なり、 remote state 機能を利用する必要がある
    • 以下のようにdataを定義することで、remote側のoutputを参照できるようになる

    • 注意点
      • 制約としてremote先のmoduleの先のoutputは読み取れないのでmodule直下(root)でoutputを定義する必要がある
      • tfstateの管理方法をs3にした状態でstate environmentsを使った時のs3のprefixは env:/<environment>/ なるため以下のように記述する
remote_state参照方法(backend.tf内に記述)
data "terraform_remote_state" "not_immutable" {
  backend = "s3"
  config {
    bucket = "< backetname >"
    key    = "env:/${terraform.env}/not_immutable/terraform.tfstate"
    region = "< region >"
  }
}
moduleへの渡し方
module "compute" {
    source         = "../../modules/compute"

    vpc            = "${data.terraform_remote_state.not_immutable.vpc}"
}

2. dev/stg/prodなどの環境の分け方

  • ver0.9以前はdev,stg,prodなどはディレクトリを分けることで、tfstateを競合させないようにしていたが、ver0.9で追加された state environments を利用して環境を分ける
  • terraform env new dev を打つことでdevの環境が作られる

    • デフォルトでは直下に terraform.state.d というディレクトリができ、その配下に環境毎にtfstateが管理される
  • terraform env list を打つことで現在のenviromentを参照可能

state_environmentsの実行方法
$ terraform env new dev

$ terraform env list
  default
* dev
  stg
  • その前にtfstateはs3に置いたほうが良いと思うので、以下の記述も入れて terraform init を実行することでtfstateをs3で管理出来る状態になる。その後に terraform apply を実行することでtfstateが生成される

    • なお、s3をbackendにすると /env:/< environment > が補完され < backetname >/env:/< environment >/immutable/terraform.tfstate のように管理される
backend.tf
terraform {
  backend "s3" {
    bucket = "< backet name >"
    key    = "immutable/terraform.tfstate"
    region = "us-west-2"
  }
}
initの実行方法
$ terraform init

state environmentsの活用方法

今まではdevやstg、prodを別のディレクトリで管理していたため、それぞれのディレクトリにvariableを置くような形だったが、state environmentsの登場により1つのディレクトリで複数の環境を扱えるようになった。
そこで如何に効率的に複数の環境を扱えるか考えた結果、以下になった。

  • map関数をガンガン使う

    • environments配下のvariable.tfには以下のようにmapで定義する
  • map関数のkeyの部分をドット区切りでenv情報を入れる
    • 環境毎に値を定義出来る

      • env毎の切り替え方法は、値取得時に "vpc-${lookup(var.common, "${terraform.env}.region", var.common["default.region"])}"
        }
        のように ${terraform.env} にdevやstgが入りvalueとして参照可能になる
    • envの値が無ければdefaultを参照するように定義する方法は以下
      • defaultの指定方法は "vpc-${lookup(var.common, "${terraform.env}.region", var.common["default.region"])}"
        }
        のように ${lookup(key, value, default) で指定可能

それらを踏まえたコードは以下

variable側
variable "common" {
    default = {
        default.region     = "us-west-2"
        default.project    = "oreno-project"

        dev.region         = "us-west-2"
        stg.region         = "us-west-2"
        prd.region         = "ap-northeast-1"
  }
}

# VPC
variable "vpc" {
    type = "map"
    default = {
        default.cidr       = "10.0.0.0/16"
        default.public-a   = "10.0.0.0/24"
        default.public-c   = "10.0.1.0/24"
        default.private-a  = "10.0.2.0/24"
        default.private-c  = "10.0.3.0/24"
    }
}
module呼び出し時
module "vpc" {
    source       = "../../modules/vpc"
    common       = "${var.common}"
    vpc          = "${var.vpc}"
}
module内からvariableの値を取得する時
resource "aws_vpc" "vpc" {
    cidr_block                  = "${lookup(var.vpc, "${terraform.env}.cidr", var.vpc["default.cidr"])}"
    enable_dns_support          = "true"
    enable_dns_hostnames        = "true"
    tags {
        Name                    = "vpc-${lookup(var.common, "${terraform.env}.project", var.common["default.project"])}"
    }
}
参考:こんな感じでproviderでもlookup可能
provider "aws" {
    region = "${lookup(var.common, "${terraform.env}.region", var.common["default.region"])}"
}

まとめ

  1. 影響範囲を小さくするため、terraformの実行単位は小さくしましょう
  2. terraform間の受け渡しは remote state を使いましょう
  3. state environments を使って環境を分けましょう
  4. map関数を使ってmodule等に渡す時などのコードを簡素化しましょう
  5. map関数 & state environments & default定義を使ってvariableを効率化させましょう

以上

続きを読む

Terraformのoutputでmapを利用する方法

やること

Terraformのoutputでmapを利用する

なにが嬉しいか

module間で値を受け渡す時のoutputの定義が短くなる

Befor: 冗長的なoutput

output側の定義
output "vpc_id" {
  value = "${aws_vpc.vpc.id}"
}

output "public-a" {
  value = "${aws_subnet.public-a.id}"
}

output "public-c" {
  value = "${aws_subnet.public-c.id}"
}

output "private-a" {
  value = "${aws_subnet.private-a.id}"
}

output "private-c" {
  value = "${aws_subnet.private-c.id}"
}
moduleから呼び出す時
module "compute" {
  source = "../../xxx"

  vpc_id     = "${module.xxx.vpc_id}"
  public-a   = "${module.xxx.public-a}"
  public-c   = "${module.xxx.public-c}"
  private-a  = "${module.xxx.private-a}"
  private-c  = "${module.xxx.private-c}"
}

After: mapを使ったoutput

outputの際にvalueの値に "${map("key", "value")}" を入れることでmapとして利用可能になる。

output側の定義
output "vpc" {
  value = "${
    map(
      "vpc_id",           "${aws_vpc.vpc.id}",
      "subnet-public-a",  "${aws_subnet.public-a.id}",
      "subnet-public-c",  "${aws_subnet.public-c.id}",
      "subnet-private-a", "${aws_subnet.private-a.id}",
      "subnet-private-c", "${aws_subnet.private-c.id}"
    )
  }"
}
moduleから呼び出す時
module "compute" {
  source = "../../xxx"
  vpc = "${module.xxx.vpc}"
}

スッキリ!!

参照方法

ちなみに以下のようにlookupでkeyを指定することでvalueを参照出来る。

.tf
subnet_ids = ["${lookup(var.vpc, "subnet-private-a")}"]}

続きを読む