kube-awsでaws-ssm-agentとRunCommandを試してみる

kube-awsとは

AWS上にKubernetesクラスタをつくるツールです。GolangやCloudFormation、Container Linux(旧CoreOS)をベースにしています。

RunCommandとは

Amazon EC2 Systems Managerというサービスの一機能で、AWSのAPI経由でEC2インスタンスにざまざまなコマンドを実行させることができます。

aws-ssm-agentとは

RunCommandで送られたコマンドを実際に実行するデーモンです。

概要

kube-awsクラスタでRunCommandが利用できるようにします。

まず、aws-ssm-agentは公式にはrpmなどで配布されているのですが、基本的にContainer Linuxにはパッケージマネージャ的なものが存在しないのでそれが利用できません。そのため、Container Linux向けにビルドしたaws-ssm-agentをなんとかして手に入れる必要があります。

ありがたいことに、DailyHotelという会社さんがContainer Linux向けにaws-ssm-agentをビルド・公開してくださっているので、それを利用します。

https://github.com/DailyHotel/amazon-ssm-agent

また、kube-awsにはaws-ssm-agent対応があるので、ほぼcluster.yamlという設定ファイルの記述を少しいじるだけでセットアップは完了です。ただし、kube-aws 0.9.8の時点ではaws-ssm-agentの動作に必要なIAMポリシーがそのままだと付与されないというバグ?があるので、その点だけ手動でフォローする必要があります。

手順

kube-aws initkube-aws renderを実行して必要なファイルを生成したあと、cluster.yamlに以下を追記します。

amazonSsmAgent:
  enabled: true
  downloadUrl: https://github.com/DailyHotel/amazon-ssm-agent/releases/download/v2.0.805.1/ssm.linux-amd64.tar.gz
  sha1sum: a6fff8f9839d5905934e7e3df3123b54020a1f5e

clusterNamek8s1ということにします。
その場合、クラスタ名でIAMロールを検索すると下記のように3つでてきます。それぞれ、Controllerノード、Etcdノード、Workerノード用です。

image.png

それぞれに、AWSマネージドなIAMポリシー「AmazonEC2RoleforSSM」を追加します。

image.png

この時点でノードが起動完了してしまっている場合、aws-ssm-agentがエラーを吐き続けているかもしれません。(試した時は少なくともそう見えた)

その場合、aws-ssm-agentを再起動します。

sudo systemctl restart aws-ssm-agent

これで、あとはAWSコンソールから普段どおりRunCommandとAWS-RunShellScriptドキュメントを使ってクラスタ内の任意のEC2インスタンスに対して任意のシェルコマンドを実行することができます。

例えば、クラスタ内の全インスタンスを対象にしたい場合は、クラスタ名のタグが使えます。

image.png

コマンドの実行結果(時刻、成否、標準出力など)は以下のように記録されます。

image.png

CloudTrailなども使うと監査ログをとる目的ではかなり良さそうですね。

続きを読む

【注意】Amazon Aurora(MySQL)でZero Downtime Patch実行時に不具合が発生するケースがある【AWS】

はじめに

先日、Amazon Aurora(MySQL)の必須アップデートの案内が来たのでアップデートの検証作業を実施しておりました。
Zero Downtime Patch(以下、ZDP)が実行され、ダウンタイム無しでアップデートが行われるはずが、不具合が発生してしまいました。

ググっても関連する情報が見つからなかったので、取り急ぎ分かっている情報について記録しておきます。

なお、対象のアップデートは下記のものになります。(Cluster Version 1.14)

Database Engine Updates 2017-08-07
http://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Aurora.DatabaseEngineUpdates.20170807.html

ポイント(TL;DR)

重要なポイントは下記のとおりです。

Aurora(MySQL)に対し、DB Connection Poolを利用し、且つPrepared Statementを利用していると、Zero Downtime Patch実行時に不具合が発生する可能性が高い。
Aurora(MySQL)のアップデートを行う前に、アプリの構成・実装を調べた方が良い。

Zero Downtime Patch(ZDP)とは何か

Auroraを無停止(正確には5秒程度の遅延)でアップデートできる画期的な仕組みです。
アップデート処理中にも、クライアントからのDB接続は維持されます。

今回、Cluster Version 1.14へのアップデートとなりますが、本アップデートに関するドキュメントにも説明があります。
http://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Aurora.DatabaseEngineUpdates.20170807.html

AuroraのCluster Version 1.10で初めて導入されたようです。
ZDPの内容について、下記ドキュメントにも記載があります。

Database Engine Updates 2016-12-14
http://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Aurora.DatabaseEngineUpdates.20161214.html

こちらの記事も情報がまとまっていて参考になります。

Amazon Aurora のアップグレード
https://qiita.com/tonishy/items/542f7dd10cc43fd299ab

事象の内容

Aurora(MySQL)でClusterのアップデートが必要の旨の案内が来たため、AWS Management Consoleからアップデートを行いました。
AuroraのClusterアップデートは無事に完了しましたが、当該Auroraインスタンスを利用しているJavaアプリが軒並みエラーを吐き出し始めました。
その後Javaアプリを再起動し、復旧しました。

AWS Management Console上、WriterとなっているインスタンスのEventに下記のものが表示されました。
従って、Zero Downtime Patchが行われていたことは間違いありません。

The database was successfully patched with a zero downtime patch.

Javaアプリのログ上には、「Unknown prepared statement handler」といったエラーが多数出力されておりました。

事象の原因

当該JavaアプリはJDBC Connection Poolを利用し、DBとの接続を維持する構成になっています。
また、Prepared Statementを利用する実装になっております。

ZDP実行時、MySQLのクライアント(i.e. Javaアプリ)からの接続は維持されるようですが、Prepared Statementは維持されないことが原因のようでした。
時系列的に整理すると、下記のような状況になっていたようです。

  1. Javaアプリ起動時、Auroraに対して幾つかJDBC接続を確立する
  2. リクエストに応じて、JavaアプリからAuroraにクエリを発行する。この際、Prepareを発行してPrepared Statementを作る。
    Javaアプリは、同一のリクエストに対してはPrepared Statementを再利用する。
  3. AuroraのアップデートをZDPで行う。この際、JavaアプリからのJDBC接続は維持されるが、Prepared Statementは全てクリアされる。
  4. Javaアプリに対し、項番2と同様のリクエストを行う。Javaアプリは、コンパイル済みのPrepared Statementが存在しているという認識のまま、これを再利用しようとし、エラーとなる。

同一のアプリに対し、Prepared Statementを利用するもの/しないものを用意してAuroraのアップデートを実施しました。
結果、前者はエラーが発生し、後者はエラーが発生しませんでした。

なお上記の挙動は、独自に調査・検証したものとなります。
現在、AWSの公式見解・ドキュメント等の情報は見つけられておりません。
# 一応、Database Engine Updates 2016-12-14には以下の記載がありますが、これが該当するかどうかは不明です。
# Note that temporary data like that in the performance schema is reset during the upgrade process.

2017/9/26にAWSサポートへの問い合わせを行っておりますが、2017/10/19現在、回答待ちの状況です。

確認した環境・構成

上記事象は、下記構成のJavaアプリで確認しました。

  • Java 8 (Oracle JDK)
  • spring-boot-1.4.0.RELEASE
  • mybatis-spring-boot-starter 1.1.1
  • mariadb-java-client 1.5.5

併せてMyBatisGeneratorも利用しています。
特別な理由がない限り、JavaアプリからはMyBatisGeneratorで生成されたMapperとClassを利用しています。

デフォルトでは、MyBatisGeneratorではPrepared Statementを利用するMapperを生成するようです。

回避策

回避策は概ね以下の3通りになると思います。

1.Prepared Statementを利用しないよう、アプリを変更する。

ソースコード/ORマッパーの改修・設定変更を行い、Prepared Statementを利用しないようにする方法です。
おそらく、この対応は難しいケースがほとんどだと思います。

2.MySQL接続ドライバの設定で、Prepared Statementを利用しないよう変更する。 

今回のケースでは、MariaDB Connector/Jを利用しており、オプションを指定することでPrepared Statementを利用しないように変更することができました。

useServerPrepStmtsオプションをfalseにすることで実現可能です。
ちなみに、1.6.0以降のバージョンはデフォルトでfalseになっているようですので、これ以降のバージョンを利用してる場合は、本件の影響を受けません。

About MariaDB Connector/J -> Optional URL parameters -> Essential options
https://mariadb.com/kb/en/library/about-mariadb-connector-j/

3.ZDPではない、通常のアップデートを実施する

Auroraインスタンスの再起動を伴う、通常のアップデートを実行することで、本件事象を回避することが可能と考えられます(※未検証)
20~30秒程度のダウンタイムが発生します。

下記に、ZDPにならないケースについて記載がありますので、このどれかを満たせば通常のアップデートになると推測できます(※未検証)

Database Engine Updates 2017-08-07 -> Zero-Downtime Patching
http://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Aurora.DatabaseEngineUpdates.20170807.html

  • Long-running queries are in progress
  • Open long-running transactions exist
  • Binary logging is enabled
  • Binary log replication is running
  • Pending parameter changes exist
  • Temporary tables are in use
  • Table locks are in use
  • Open SSL connections exist

まとめ

DBへのConnection Poolを利用し、且つPrepared Statementを利用するようアプリを実装していると、不具合が発生する可能性が高いです。

経験的な感覚ですが、JavaアプリはConnection Poolを利用しているケースが多いかと思います。
Aurora(MySQL)を利用している場合は、チェックをした方が良いかと思います。

検証作業について

古いClusterバージョンのAuroraインスタンスを作ることはできません。AWSサポートにも聞いてみましたが無理なようでした。
従って、運良く過去に構築した古いAuroraインスタンスが存在していれば検証は可能ですが、それ以外のケースでは検証手段がありません。

続きを読む

Terraformでtask-definisionを消したらエラーがで続けた…

task-definitionをコンソールから停止

tfファイルを更新してもtask-definitionが更新されないので試しに現状動いているものを停止してみた

error.sh
$ terraform apply
aws_ecs_service.registry: ServiceNotActiveException: Service was not ACTIVE.
    status code: 400, request id:*****

後に更新されない理由が判明したがエラーが消えてくれない

原因

ECSのtaskは停止しても削除されず、INACTIVE状態で残るためにtfstateファイルから同じものを見続けることが判明
一時的に参照するtask-definitionを動いてるものに指定 => apply => 元に戻す でも効果なし

ecs.tf
resource "aws_ecs_service" "service_redash_blt" {
  name = "${terraform.env}-****"
  cluster = "${aws_ecs_cluster.****.id}"
  task_definition = "${aws_ecs_task_definition.****.arn}"
  desired_count = 1
  deployment_minimum_healthy_percent = 50
  deployment_maximum_percent = 100
  iam_role = "${aws_iam_role.****.arn}"

  load_balancer {
    elb_name = "${aws_elb.****.name}"
    container_name = "nginx"
    container_port = 80
  }
}

解決策

結局task-definitionのfamily自体を変更
terraformで作ったものをコンソールからいじってはいけないと痛感…

続きを読む

DynamoDBの予約語一覧を無理やり動的に取得してみた

DynamoDBの予約語を対処するために一覧が欲しくなったのですが、いちいちプログラム内で定義したくなかったので、AWSの予約語一覧ページから一覧を取得するサービスを作りました。

前提

  1. AWSが公開しているDynamoDB予約語一覧のWebページ( http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/ReservedWords.html )をスクレイピングして予約語一覧を抽出します。
  2. 実装はAWS Lambda (Python3)、Webサービスとして動作させるためにAPI Gatewayを利用します。

結果

https://github.com/kojiisd/dynamodb-reserved-words

DynamoDB予約語一覧ページの確認

今回は以下のページをParseしたいと思います。

http://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/ReservedWords.html

HTMLを見てみると、Parseしたい箇所はcodeタグで囲まれているようで、しかもこのページ内でcodeタグが出現するのは1度だけのようです。

スクリーンショット 2017-10-15 18.03.18.png

これであればすぐにパースできそうです。

HTMLの取得とParse

BeautifulSoupを利用すれば、簡単に実装ができます。BeautifulSoupのインストールは他のページでいくらでも紹介されているので、ここでは省略します。

import sys
import os


sys.path.append(os.path.join(os.path.abspath(os.path.dirname(__file__)), 'lib'))
from bs4 import BeautifulSoup
import requests

URL = os.environ['TARGET_URL']

def run(event, context):

    response = requests.get(URL)
    soup = BeautifulSoup(response.content, "html.parser")

    result_tmp = soup.find("code")
    if result_tmp == None:
        return "No parsing target"

    result = result_tmp.string

    return result.split('n');

とりあえず申し訳程度にパースできない場合(AWSのページがcodeタグを使わなくなった時)を想定してハンドリングしています。

API Gatewayを定義してサービス化

コードが書けてしまえばAPI Gatewayを定義してサービス化するだけです。

設定は非常に単純なので割愛で。

スクリーンショット 2017-10-15 18.12.11_deco.png

アクセスしてみる

実際にアクセスしてみます。

$ curl -X GET https://xxxxxxxxxxx.execute-api.us-east-1.amazonaws.com/prod
["", "ABORT", "ABSOLUTE", "ACTION", "ADD", "AFTER", "AGENT", "AGGREGATE", "ALL", "ALLOCATE", "ALTER", "ANALYZE", "AND", "ANY", "ARCHIVE", "ARE", "ARRAY", "AS", "ASC", "ASCII", "ASENSITIVE", "ASSERTION", "ASYMMETRIC", "AT", "ATOMIC", "ATTACH", "ATTRIBUTE", "AUTH", "AUTHORIZATION", "AUTHORIZE", "AUTO", "AVG", "BACK", "BACKUP", "BASE", "BATCH", "BEFORE", "BEGIN", "BETWEEN", "BIGINT", "BINARY", "BIT", "BLOB", "BLOCK", "BOOLEAN", "BOTH", "BREADTH", "BUCKET", "BULK", "BY", "BYTE", "CALL", "CALLED", "CALLING", "CAPACITY", "CASCADE", "CASCADED", "CASE", "CAST", "CATALOG", "CHAR", "CHARACTER", "CHECK", "CLASS", "CLOB", "CLOSE", "CLUSTER", "CLUSTERED", "CLUSTERING", "CLUSTERS", "COALESCE", "COLLATE", "COLLATION", "COLLECTION", "COLUMN", "COLUMNS", "COMBINE", "COMMENT", "COMMIT", "COMPACT", "COMPILE", "COMPRESS", "CONDITION", "CONFLICT", "CONNECT", "CONNECTION", "CONSISTENCY", "CONSISTENT", "CONSTRAINT", "CONSTRAINTS", "CONSTRUCTOR", "CONSUMED", "CONTINUE", "CONVERT", "COPY", "CORRESPONDING", "COUNT", "COUNTER", "CREATE", "CROSS", "CUBE", "CURRENT", "CURSOR", "CYCLE", "DATA", "DATABASE", "DATE", "DATETIME", "DAY", "DEALLOCATE", "DEC", "DECIMAL", "DECLARE", "DEFAULT", "DEFERRABLE", "DEFERRED", "DEFINE", "DEFINED", "DEFINITION", "DELETE", "DELIMITED", "DEPTH", "DEREF", "DESC", "DESCRIBE", "DESCRIPTOR", "DETACH", "DETERMINISTIC", "DIAGNOSTICS", "DIRECTORIES", "DISABLE", "DISCONNECT", "DISTINCT", "DISTRIBUTE", "DO", "DOMAIN", "DOUBLE", "DROP", "DUMP", "DURATION", "DYNAMIC", "EACH", "ELEMENT", "ELSE", "ELSEIF", "EMPTY", "ENABLE", "END", "EQUAL", "EQUALS", "ERROR", "ESCAPE", "ESCAPED", "EVAL", "EVALUATE", "EXCEEDED", "EXCEPT", "EXCEPTION", "EXCEPTIONS", "EXCLUSIVE", "EXEC", "EXECUTE", "EXISTS", "EXIT", "EXPLAIN", "EXPLODE", "EXPORT", "EXPRESSION", "EXTENDED", "EXTERNAL", "EXTRACT", "FAIL", "FALSE", "FAMILY", "FETCH", "FIELDS", "FILE", "FILTER", "FILTERING", "FINAL", "FINISH", "FIRST", "FIXED", "FLATTERN", "FLOAT", "FOR", "FORCE", "FOREIGN", "FORMAT", "FORWARD", "FOUND", "FREE", "FROM", "FULL", "FUNCTION", "FUNCTIONS", "GENERAL", "GENERATE", "GET", "GLOB", "GLOBAL", "GO", "GOTO", "GRANT", "GREATER", "GROUP", "GROUPING", "HANDLER", "HASH", "HAVE", "HAVING", "HEAP", "HIDDEN", "HOLD", "HOUR", "IDENTIFIED", "IDENTITY", "IF", "IGNORE", "IMMEDIATE", "IMPORT", "IN", "INCLUDING", "INCLUSIVE", "INCREMENT", "INCREMENTAL", "INDEX", "INDEXED", "INDEXES", "INDICATOR", "INFINITE", "INITIALLY", "INLINE", "INNER", "INNTER", "INOUT", "INPUT", "INSENSITIVE", "INSERT", "INSTEAD", "INT", "INTEGER", "INTERSECT", "INTERVAL", "INTO", "INVALIDATE", "IS", "ISOLATION", "ITEM", "ITEMS", "ITERATE", "JOIN", "KEY", "KEYS", "LAG", "LANGUAGE", "LARGE", "LAST", "LATERAL", "LEAD", "LEADING", "LEAVE", "LEFT", "LENGTH", "LESS", "LEVEL", "LIKE", "LIMIT", "LIMITED", "LINES", "LIST", "LOAD", "LOCAL", "LOCALTIME", "LOCALTIMESTAMP", "LOCATION", "LOCATOR", "LOCK", "LOCKS", "LOG", "LOGED", "LONG", "LOOP", "LOWER", "MAP", "MATCH", "MATERIALIZED", "MAX", "MAXLEN", "MEMBER", "MERGE", "METHOD", "METRICS", "MIN", "MINUS", "MINUTE", "MISSING", "MOD", "MODE", "MODIFIES", "MODIFY", "MODULE", "MONTH", "MULTI", "MULTISET", "NAME", "NAMES", "NATIONAL", "NATURAL", "NCHAR", "NCLOB", "NEW", "NEXT", "NO", "NONE", "NOT", "NULL", "NULLIF", "NUMBER", "NUMERIC", "OBJECT", "OF", "OFFLINE", "OFFSET", "OLD", "ON", "ONLINE", "ONLY", "OPAQUE", "OPEN", "OPERATOR", "OPTION", "OR", "ORDER", "ORDINALITY", "OTHER", "OTHERS", "OUT", "OUTER", "OUTPUT", "OVER", "OVERLAPS", "OVERRIDE", "OWNER", "PAD", "PARALLEL", "PARAMETER", "PARAMETERS", "PARTIAL", "PARTITION", "PARTITIONED", "PARTITIONS", "PATH", "PERCENT", "PERCENTILE", "PERMISSION", "PERMISSIONS", "PIPE", "PIPELINED", "PLAN", "POOL", "POSITION", "PRECISION", "PREPARE", "PRESERVE", "PRIMARY", "PRIOR", "PRIVATE", "PRIVILEGES", "PROCEDURE", "PROCESSED", "PROJECT", "PROJECTION", "PROPERTY", "PROVISIONING", "PUBLIC", "PUT", "QUERY", "QUIT", "QUORUM", "RAISE", "RANDOM", "RANGE", "RANK", "RAW", "READ", "READS", "REAL", "REBUILD", "RECORD", "RECURSIVE", "REDUCE", "REF", "REFERENCE", "REFERENCES", "REFERENCING", "REGEXP", "REGION", "REINDEX", "RELATIVE", "RELEASE", "REMAINDER", "RENAME", "REPEAT", "REPLACE", "REQUEST", "RESET", "RESIGNAL", "RESOURCE", "RESPONSE", "RESTORE", "RESTRICT", "RESULT", "RETURN", "RETURNING", "RETURNS", "REVERSE", "REVOKE", "RIGHT", "ROLE", "ROLES", "ROLLBACK", "ROLLUP", "ROUTINE", "ROW", "ROWS", "RULE", "RULES", "SAMPLE", "SATISFIES", "SAVE", "SAVEPOINT", "SCAN", "SCHEMA", "SCOPE", "SCROLL", "SEARCH", "SECOND", "SECTION", "SEGMENT", "SEGMENTS", "SELECT", "SELF", "SEMI", "SENSITIVE", "SEPARATE", "SEQUENCE", "SERIALIZABLE", "SESSION", "SET", "SETS", "SHARD", "SHARE", "SHARED", "SHORT", "SHOW", "SIGNAL", "SIMILAR", "SIZE", "SKEWED", "SMALLINT", "SNAPSHOT", "SOME", "SOURCE", "SPACE", "SPACES", "SPARSE", "SPECIFIC", "SPECIFICTYPE", "SPLIT", "SQL", "SQLCODE", "SQLERROR", "SQLEXCEPTION", "SQLSTATE", "SQLWARNING", "START", "STATE", "STATIC", "STATUS", "STORAGE", "STORE", "STORED", "STREAM", "STRING", "STRUCT", "STYLE", "SUB", "SUBMULTISET", "SUBPARTITION", "SUBSTRING", "SUBTYPE", "SUM", "SUPER", "SYMMETRIC", "SYNONYM", "SYSTEM", "TABLE", "TABLESAMPLE", "TEMP", "TEMPORARY", "TERMINATED", "TEXT", "THAN", "THEN", "THROUGHPUT", "TIME", "TIMESTAMP", "TIMEZONE", "TINYINT", "TO", "TOKEN", "TOTAL", "TOUCH", "TRAILING", "TRANSACTION", "TRANSFORM", "TRANSLATE", "TRANSLATION", "TREAT", "TRIGGER", "TRIM", "TRUE", "TRUNCATE", "TTL", "TUPLE", "TYPE", "UNDER", "UNDO", "UNION", "UNIQUE", "UNIT", "UNKNOWN", "UNLOGGED", "UNNEST", "UNPROCESSED", "UNSIGNED", "UNTIL", "UPDATE", "UPPER", "URL", "USAGE", "USE", "USER", "USERS", "USING", "UUID", "VACUUM", "VALUE", "VALUED", "VALUES", "VARCHAR", "VARIABLE", "VARIANCE", "VARINT", "VARYING", "VIEW", "VIEWS", "VIRTUAL", "VOID", "WAIT", "WHEN", "WHENEVER", "WHERE", "WHILE", "WINDOW", "WITH", "WITHIN", "WITHOUT", "WORK", "WRAPPED", "WRITE", "YEAR", "ZONE "]

うまくできました。

まとめ

思いつきで作成してみましたが、AWSのページが閉鎖されたりHTML構造が変更されない限り、仕様変更などは気にせずに使えそうです。

あとBeautifulSoup初めて使いましたが、かなり便利。

続きを読む

AWSでElastic Stack

はじめに

Elastic Stackについて触る機会があったので、メモを残します。
2017/09月時点の作業です。

Elastic Stackはログの解析・可視化等ができるものなのですが、今回その説明は省かせてもらいますので、公式等をご確認下さい。

作りたい構成

AWSのVPC環境にElasticsearchを仮想マシン3台にインストールしクラスタ化します。
その後、どれか一台にkibanaをインストールしてクラスタの状態をブラウザから確認します。

今回作りたい構成は以下のようなイメージです。
ES01.jpeg

AWS側の設定について

AWS側の設定は以下のようにしました。

VPC:192.100.0.0/24
VPC Subnet:192.100.0.0/27
仮想マシン:Amazon Linux
仮想マシンプラン:m4.large
仮想マシン台数:3台
srv1:192.100.0.4/EIP付き
srv2:192.100.0.5/EIP付き
srv3:192.100.0.6/EIP付き

セキュリティグループ
フルオープンはセキュリティ的に問題があるのである程度絞っています。
・仮想マシン操作用
SSH (22) TCP (6) 22 [sshアクセスするIPアドレス]
・Elasticsearchへのアクセス用
カスタム TCP ルール TCP (6) 9200 [ElasticsearchへアクセスするIPアドレス]
・Elasticsearchの内部通信用
カスタム TCP ルール TCP (6) 9300 192.100.0.0/27
・kibanaへのアクセス用
カスタム TCP ルール TCP (6) 5601 [kibanaへアクセスするIPアドレス]

それでは作ります。

構築作業

1.事前準備

1.1 java8(openJDK)のインストール(仮想マシン3台共通作業)

Amazon Linuxでインストールされているjavaのバージョンは1.7.0がインストールされていますので、1.7.0を削除し、1.8.0をインストールします。
デバッグは使うことは無いと思いますがお作法として入れておきます。

# yum -y remove java-1.7.0-openjdk
# yum -y install java-1.8.0-openjdk-devel
# yum -y install java-1.8.0-openjdk-debuginfo --enablerepo=*debug*

1.2 ElasticsearchをyumでインストールできるようにGPGキーのインポートと、リポジトリの作成(仮想マシン3台共通作業)

# rpm --import https://artifacts.elastic.co/GPG-KEY-elasticsearch
# vi /etc/yum.repos.d/elastic.repo
[elasticsearch-5.x]
name=Elasticsearch repository for 5.x packages
baseurl=https://artifacts.elastic.co/packages/5.x/yum
gpgcheck=1
gpgkey=https://artifacts.elastic.co/GPG-KEY-elasticsearch
enabled=1
autorefresh=1
type=rpm-md

2.Elasticsearchのインストールと起動

Elasticsearchをクラスターモードで起動します。

2-1.Elasticsearchのインストール(仮想マシン3台共通作業)

インストールはありがたいことにとても簡単です。

# yum install -y elasticsearch

インストール後のElasticsearchのディレクトリ状況は以下のような感じになっていました。

/var/run/elasticsearch
/var/log/elasticsearch
/var/lib/elasticsearch
/var/lib/yum/yumdb/e/e7a7bc22f961d4dd3889c0cac8f668512fe3d2c0-elasticsearch-5.6.2-1-noarch
/var/lib/yum/repos/x86_64/latest/elasticsearch-5.x
/var/cache/yum/x86_64/latest/elasticsearch-5.x
/etc/elasticsearch
/etc/elasticsearch/elasticsearch.yml
/etc/sysconfig/elasticsearch
/etc/rc.d/init.d/elasticsearch
/usr/lib/systemd/system/elasticsearch.service
/usr/lib/sysctl.d/elasticsearch.conf
/usr/lib/tmpfiles.d/elasticsearch.conf
/usr/share/elasticsearch
/usr/share/elasticsearch/lib/elasticsearch-5.6.2.jar
/usr/share/elasticsearch/modules/reindex/elasticsearch-rest-client-5.6.2.jar
/usr/share/elasticsearch/bin/elasticsearch
/usr/share/elasticsearch/bin/elasticsearch.in.sh
/usr/share/elasticsearch/bin/elasticsearch-keystore
/usr/share/elasticsearch/bin/elasticsearch-systemd-pre-exec
/usr/share/elasticsearch/bin/elasticsearch-translog
/usr/share/elasticsearch/bin/elasticsearch-plugin

2.2.elasticsearch.ymlの設定(仮想マシン毎の設定)

elasticsearch.ymlはデフォルトの設定は全てコメント化されていますので、
追加したい内容を追記すればOKでした。
追加する内容は以下です。

cluster.name [cluster-name]:クラスタ名。入力しないとデフォルトの名前になる。
node.name [node-name]:elasticsearchノードの名前。自身の識別に利用する。
discovery.zen.ping.unicast.hosts ["IPADDRESS","IPADDRESS","IPADDRESS"]:クラスタを構成する相手ホスト名をユニキャストで検索する。(3台構成なので3つ書く)
network.host: 0.0.0.0:通信許可設定。通信の制御はAWSのセキュリティグループで行う為、こちらは全許可で記載します。

上記の設定を各サーバのelasticsearch.ymlに記載します。

srv1
# vi /etc/elasticsearch/elasticsearch.yml
cluster.name: my-cluster
node.name: node001
network.host: 0.0.0.0
discovery.zen.ping.unicast.hosts: ["192.100.0.4","192.100.0.5","192.100.0.6"]
srv2
# vi /etc/elasticsearch/elasticsearch.yml
cluster.name: my-cluster
node.name: node002
network.host: 0.0.0.0
discovery.zen.ping.unicast.hosts: ["192.100.0.4","192.100.0.5","192.100.0.6"]
srv3
# vi /etc/elasticsearch/elasticsearch.yml
cluster.name: my-cluster
node.name: node003
network.host: 0.0.0.0
discovery.zen.ping.unicast.hosts: ["192.100.0.4","192.100.0.5","192.100.0.6"]

2.3.Elasticsearchの起動(仮想マシン3台共通作業)

各サーバのElasticsearchサービスを起動します。この時同じタイミングで起動コマンドを打つと、ノードの認識がうまくいかない場合があります。サービスの起動完了を待ちながら1台ずつ起動します。

# /etc/init.d/elasticsearch start

2.4.クラスタ状態の確認

仮想マシン3台の内、どのサーバでも構いませんので以下コマンドを発行し、ノードの状態を確認します。

# curl localhost:9200/_cat/nodes
192.100.0.5 8 43 0 0.00 0.00 0.00 mdi - node002
192.100.0.6 7 43 0 0.00 0.02 0.00 mdi - node003
192.100.0.4 8 44 0 0.00 0.01 0.04 mdi * node001
※クラスタ化ができていない場合は自分のノード名しか表示されません。

クラスタ化がとても簡単に成功しました。
他にもログを確認することでノードが追加されたか確認することができます。
以下のようにログが出力されます。

/var/log/elasticsearch/my-cluster.log
[2017-xx-xxTxx:xx:xx,043][INFO ][o.e.g.GatewayService     ] [node001] recovered [0] indices into cluster_state
[2017-xx-xxTxx:xx:xx,815][INFO ][o.e.c.s.ClusterService   ] [node001] added {{node002}{UTINV4wuTd6UfgLByoG4gQ}{E5ptnPZ0SG-xc629ayXK_Q}{192.100.0.5}{192.100.0.5:9300},}, 
reason: zen-disco-node-join[{node002}{UTINV4wuTd6UfgLByoG4gQ}{E5ptnPZ0SG-xc629ayXK_Q}{192.100.0.5}{192.100.0.5:9300}]

2.5.自動起動設定(仮想マシン3台共通作業)

無事クラスタ化ができましたので、Elasticsearchサービスを自動起動するよう設定しておきます。

# chkconfig --add elasticsearch
# chkconfig --list elasticsearch
elasticsearch   0:off   1:off   2:on    3:on    4:on    5:on    6:off

3.Kibanaのインストールとx-packのインストール

次に可視化ツールのKibanaをインストールします。
また、クラスタの状態をモニタリングする為、x-packを導入します。
作業はsrv1で実施しました。

3.1.kibanaのインストール

Kibanaもインストールはとても簡単です。

# yum -y install kibana

3.2.kibana.ymlの編集

kibanaは5.0.0からリモートホストからのアクセスを受け入れない為、リモートホストからの接続が必要な場合は設定ファイル「/etc/kibana/kibana.yml」の「server.host」の修正が必要です。今回はブラウザから確認したいので、修正を行いました。

# vi /etc/kibana/kibana.yml
server.host: "0.0.0.0"

kibana.ymlもデフォルトの設定は全てコメント化されていますので、
追加したい内容を追記すればOKでした。

3.3.Kibanaの起動

# /etc/init.d/kibana start

3.4.kibanaへのアクセス

ブラウザから1号機のURLにアクセスしてみます。

http://[srv1のEIP]/5601

とりあえずはページが表示されれば問題ありません。本来の目的にはまだ届いていない為、画像は割愛します。

3.5.自動起動設定

kibanaも自動起動するようにしておきます。

# chkconfig --add kibana
# chkconfig --list kibana
kibana          0:off   1:off   2:on    3:on    4:on    5:on    6:off

3.6.x-packのインストール(仮想マシン3台共通作業)

今回の目的である、kibanaでクラスタの状態を確認する為にx-packプラグインをインストールしていきます。まずはelasticsearch用のx-packをインストールします。

kibanaを停止後、x-packインストールします。

# /etc/init.d/kibana stop
# /usr/share/elasticsearch/bin/elasticsearch-plugin install x-pack

途中y(yes)の入力が必要な為、全文を乗せておきます。

-> Downloading x-pack from elastic
[=================================================] 100%??
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@     WARNING: plugin requires additional permissions     @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
* java.io.FilePermission \.pipe* read,write
* java.lang.RuntimePermission accessClassInPackage.com.sun.activation.registries
* java.lang.RuntimePermission getClassLoader
* java.lang.RuntimePermission setContextClassLoader
* java.lang.RuntimePermission setFactory
* java.security.SecurityPermission createPolicy.JavaPolicy
* java.security.SecurityPermission getPolicy
* java.security.SecurityPermission putProviderProperty.BC
* java.security.SecurityPermission setPolicy
* java.util.PropertyPermission * read,write
* java.util.PropertyPermission sun.nio.ch.bugLevel write
* javax.net.ssl.SSLPermission setHostnameVerifier
See http://docs.oracle.com/javase/8/docs/technotes/guides/security/permissions.html
for descriptions of what these permissions allow and the associated risks.

Continue with installation? [y/N]y
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@        WARNING: plugin forks a native controller        @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
This plugin launches a native controller that is not subject to the Java
security manager nor to system call filters.

Continue with installation? [y/N]y
-> Installed x-pack

3.7.elasticsearchのx-packのsecurityを無効化する。(仮想マシン3台共通作業)

x-packには様々な機能が付属しており、securityが有効だとちゃんと設定を行わないとKibanaが起動できません。今回の目的には関係が無い為、securityを無効化することで回避します。

# vi /etc/elasticsearch/elasticsearch.yml
xpack.security.enabled: false
※上記を最下行に追記

追記後Elasticsearchを再起動して反映
# /etc/init.d/elasticsearch restart

3.8.kibanaのx-packのインストール

Kibana用x-packをインストールします。

# /usr/share/kibana/bin/kibana-plugin install x-pack
Attempting to transfer from x-pack
Attempting to transfer from https://artifacts.elastic.co/downloads/kibana-plugins/x-pack/x-pack-5.6.2.zip
Transferring 119528829 bytes....................
Transfer complete
Retrieving metadata from plugin archive
Extracting plugin archive
Extraction complete
Optimizing and caching browser bundles...
Plugin installation complete

途中止まっちゃったかな?と思いたくなるような反応が無い時間がありましたが、気にせず放置しておけば大丈夫です。

3.9.Kibanaの起動

設定が完了しましたのでKibanaを起動します。

# /etc/init.d/kibana start

設定後のプラグインの状態とライセンスの状態がどうなっているかメモしておきます。
コマンドは3台のいずれかのサーバで実行すれば確認できます。

プラグイン状態
# curl -XGET -u elastic:changeme 'http://localhost:9200/_cat/plugins?v'
name    component         version
node003 analysis-kuromoji 5.6.2
node003 x-pack            5.6.2
node002 analysis-kuromoji 5.6.2
node002 x-pack            5.6.2
node001 analysis-kuromoji 5.6.2
node001 x-pack            5.6.2
ライセンス状態
# curl -XGET -u elastic:changeme 'http://localhost:9200/_xpack/license'
{
  "license" : {
    "status" : "active",
    "uid" : "7977deb4-d253-4ef4-8fd1-bf01e1d86315",
    "type" : "trial",
    "issue_date" : "2017-xx-xxTxx:xx:xx.537Z",
    "issue_date_in_millis" : 1506590395537,
    "expiry_date" : "2017-xx-xxTxx:xx:xx.537Z",
    "expiry_date_in_millis" : 1509182395537,
    "max_nodes" : 1000,
    "issued_to" : "my-cluster",
    "issuer" : "elasticsearch",
    "start_date_in_millis" : -1
  }
}

4.kibanaからクラスタ状態を確認

それでは最後にkibanaの画面を確認します。

ブラウザから1号機のURLにアクセスします。
http://[srv1のEIP]:5601

表示された画面の左ペインにある、Monitoringを選択します。
es-monitoring.png

遷移後の画面の真ん中にある、Noedsを選択します。
es-monitoring2.png

Kibanaの画面上からクラスタの状態が確認できました。
es-monitoring3.png

Kibanaの画面上から確認できるようになると、すごくできた感があります。
次回は肝心のログデータの取り込みなんかをメモしたいと思います。

続きを読む

ECSでBlue/Greenデプロイ【cloudpack大阪ブログ】

cloudpack大阪の佐々木です。

今扱っているECS環境では、デプロイをCloudFormationでやっています。実際やってみると、アップデート途中で止まるってことが割と頻繁にあって、インプレイスでアップデートするのは怖いなということで、Blue/Greenデプロイ環境をつくってみました。

https://aws.amazon.com/jp/blogs/news/bluegreen-deployments-with-amazon-ecs/

元ネタはこれなんですが、このサンプルだと、CodePipelineとか使って ややこしい いい感じので、もう少し簡単に。

初期設定

まず、Blueを本番、Greenをステージングとして、下記のように設定します。

ALB設定

  • ダイナミックポートマッピングで、Blue、Greenそれぞれのターゲットグループを作成
  • Blue用ターゲットグループをTCP/80(本番用ポート)にマッピング
  • Green用ターゲットグループをTCP/8080(ステージング用ポート)にマッピング

ECS設定

  • Blue用のサービスをつくってBlue用のターゲットグループにマッピング
  • Green用のサービスをつくってGreen用のターゲットグループにマッピング

CFnのテンプレートはこんな感じになります。

elb.yml
Parameters:
  VpcId:
    Type: String

  Subnets:
    Type: List<AWS::EC2::Subnet::Id>

Resources:
  WebSecurityGroup:
    Type: "AWS::EC2::SecurityGroup"
    Properties:
      GroupDescription: web-sg
      SecurityGroupIngress:
        - CidrIp: "0.0.0.0/0"
          IpProtocol: "TCP"
          FromPort: 80
          ToPort: 80
        - CidrIp: "0.0.0.0/0"
          IpProtocol: "TCP"
          FromPort: 8080
          ToPort: 8080
      VpcId: !Ref VpcId


### ALBを作成 ###
  LoadBalancer:
    Type: AWS::ElasticLoadBalancingV2::LoadBalancer
    Properties:
      Name: ecstest-elb
      Subnets: !Ref Subnets
      SecurityGroups:
        - !Ref WebSecurityGroup

### Blue環境用TargetGroup ###
  BlueTargetGroup:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    DependsOn: LoadBalancer
    Properties:
      Name: target-blue
      VpcId: !Ref VpcId
      Port: 80
      Protocol: HTTP

### Green環境用TargetGroup ###
  GreenTargetGroup:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    DependsOn: LoadBalancer
    Properties:
      Name: target-green
      VpcId: !Ref VpcId
      Port: 80
      Protocol: HTTP

### 本番環境用Listner(TCP/80)
  ListenerProd:
    Type: AWS::ElasticLoadBalancingV2::Listener
    Properties:
      LoadBalancerArn: !Ref LoadBalancer
      Port: 80
      Protocol: HTTP
      DefaultActions:
        - Type: forward
          TargetGroupArn: !Ref BlueTargetGroup # <- 本番にBlue環境を紐付け

### ステージング環境用Listner(TCP/8080)
  ListenerStg:
    Type: AWS::ElasticLoadBalancingV2::Listener
    Properties:
      LoadBalancerArn: !Ref LoadBalancer
      Port: 8080
      Protocol: HTTP
      DefaultActions:
        - Type: forward
          TargetGroupArn: !Ref GreenTargetGroup # <- ステージングにGreen環境を紐付け
blue.yml
Parameters:

  Cluster:
    Type: String

  BlueTargetGroupARN:
    Type: String

Resources:

### Role作成 ###
  ECSServiceRole:
    Type: AWS::IAM::Role
    Properties:
      Path: /
      RoleName: role-blue
      AssumeRolePolicyDocument: |
        {
            "Statement": [{
                "Effect": "Allow",
                "Principal": { "Service": [ "ecs.amazonaws.com" ]},
                "Action": [ "sts:AssumeRole" ]
            }]
        }
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceRole

### Blue用サービス ###
  Service:
    Type: AWS::ECS::Service
    Properties:
      ServiceName: service-blue
      Cluster: !Ref Cluster
      Role: !Ref ECSServiceRole
      DesiredCount: 1
      TaskDefinition: !Ref TaskDefinition
      LoadBalancers:
        - ContainerName: nginx
          ContainerPort: 80
          TargetGroupArn: !Ref BlueTargetGroupARN # <- BlueのTargetGroupを指定

### Blue用タスク定義 ###
  TaskDefinition:
    Type: AWS::ECS::TaskDefinition
    Properties:
      Family: ecstest # ← Familyを同じ値にすることでRevisionの変更が可能
      ContainerDefinitions:
        - Name: nginx
          Image: nginx
          Memory: 128
          PortMappings:
            - ContainerPort: 80

green.yml
Parameters:

  Cluster:
    Type: String

  GreenTargetGroupARN:
    Type: String

Resources:

### Role作成 ###
  ECSServiceRole:
    Type: AWS::IAM::Role
    Properties:
      Path: /
      RoleName: role-green
      AssumeRolePolicyDocument: |
        {
            "Statement": [{
                "Effect": "Allow",
                "Principal": { "Service": [ "ecs.amazonaws.com" ]},
                "Action": [ "sts:AssumeRole" ]
            }]
        }
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceRole

### Green用サービス ###
  Service:
    Type: AWS::ECS::Service
    Properties:
      ServiceName: service-green
      Cluster: !Ref Cluster
      Role: !Ref ECSServiceRole
      DesiredCount: 1
      TaskDefinition: !Ref TaskDefinition
      LoadBalancers:
        - ContainerName: nginx
          ContainerPort: 80
          TargetGroupArn: !Ref GreenTargetGroupARN # <- BlueのTargetGroupを指定

### Green用タスク定義 ###
  TaskDefinition:
    Type: AWS::ECS::TaskDefinition
    Properties:
      Family: ecstest # ← Familyを同じ値にすることでRevisionの変更が可能
      ContainerDefinitions:
        - Name: nginx
          Image: nginx
          Memory: 128
          PortMappings:
            - ContainerPort: 80

ELBのリスナーを確認すると、BlueがTCP/80、GreenがTCP/8080になっています。

Kobito.ZPP2rK.png

タスク定義のFamilyの値をBlue/Greenで同じにしておけば、同じタスク定義でRevisionの更新ができます。

Kobito.D5emlt.png

デプロイ

デプロイの時は、こんな感じです。

ECS設定

  • Green用のサービスをアップデートする

ALB設定

  • ポートのマッピングをBlue/Greenで入れ替える

という手順になります。

CFnでアップデートする場合は、下記のようなテンプレートでUpdate Stackします。

elb.yml
Parameters:
  VpcId:
    Type: String

  Subnets:
    Type: List<AWS::EC2::Subnet::Id>

Resources:
  WebSecurityGroup:
    Type: "AWS::EC2::SecurityGroup"
    Properties:
      GroupDescription: web-sg
      SecurityGroupIngress:
        - CidrIp: "0.0.0.0/0"
          IpProtocol: "TCP"
          FromPort: 80
          ToPort: 80
        - CidrIp: "0.0.0.0/0"
          IpProtocol: "TCP"
          FromPort: 8080
          ToPort: 8080
      VpcId: !Ref VpcId


### ALBを作成 ###
  LoadBalancer:
    Type: AWS::ElasticLoadBalancingV2::LoadBalancer
    Properties:
      Name: ecstest-elb
      Subnets: !Ref Subnets
      SecurityGroups:
        - !Ref WebSecurityGroup

### Blue環境用TargetGroup ###
  BlueTargetGroup:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    DependsOn: LoadBalancer
    Properties:
      Name: target-blue
      VpcId: !Ref VpcId
      Port: 80
      Protocol: HTTP

### Green環境用TargetGroup ###
  GreenTargetGroup:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    DependsOn: LoadBalancer
    Properties:
      Name: target-green
      VpcId: !Ref VpcId
      Port: 80
      Protocol: HTTP

### 本番環境用Listner(TCP/80)
  ListenerProd:
    Type: AWS::ElasticLoadBalancingV2::Listener
    Properties:
      LoadBalancerArn: !Ref LoadBalancer
      Port: 80
      Protocol: HTTP
      DefaultActions:
        - Type: forward
          TargetGroupArn: !Ref GreenTargetGroup # <- 本番にGreen環境を紐付け

### ステージング環境用Listner(TCP/8080)
  ListenerStg:
    Type: AWS::ElasticLoadBalancingV2::Listener
    Properties:
      LoadBalancerArn: !Ref LoadBalancer
      Port: 8080
      Protocol: HTTP
      DefaultActions:
        - Type: forward
          TargetGroupArn: !Ref BlueTargetGroup # <- ステージングにBlue環境を紐付け

リスナーのターゲットグループを入れ替えます。

Kobito.LyimFg.png

入れ替わってますね。

まとめ

ALB(or NLB)のターゲットグループを使えばBlue/Green環境が1つのECSクラスタでできるようになります。
これでCFnが止まっても本番環境には影響なく安心ですね。

続きを読む

SparkアプリケーションのデバッグTIPS

Spark UIでスレッドダンプを見る

どこかで詰まってる/デッドロックしているなどを調べたい場合、スレッドダンプを見るのが有効ですが、それだけならSpark UI上でできます。

Screen Shot 2017-09-03 at 10.43.47 PM.png

Screen Shot 2017-09-03 at 10.44.12 PM.png

EMR上でJMXリモートデバッグ

ここではjvisualvmを使うことにします。

jvisualvmの準備

jvisualvmにプラグインを入れておくと捗ります。

Tools > Pluginsからプラグイン管理画面を開き、Threads Inspectorあたりは入れておくと便利です。

ステップを追加する

前提

  • アプリケーションはjarに固めてs3://path/to/app.jarに置いてあるとします
  • command-runner.jarspark-submitする方法でステップを実行します
  • EMRクラスタのmasterおよび各ノードは、22番が空いてるものとします

EMRにステップの追加

以下のようなコマンドラインオプションをつけてステップを追加します。

spark-submit --deploy-mode cluster --class com.example.Main --master yarn-cluster 
  --conf "spark.executor.extraJavaOptions=-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.port=8002 -Dcom.sun.management.jmxremote.rmi.port=8002 -Djava.rmi.server.hostname=127.0.0.1" 
  --conf "spark.driver.extraJavaOptions=-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.port=8003 -Dcom.sun.management.jmxremote.rmi.port=8003 -Djava.rmi.server.hostname=127.0.0.1" 
  --num-executors 3 --driver-memory 2g --executor-memory 5g --executor-cores 2  # このへんはお好みで
  s3://path/to/app.jar

これで、driverプロセスに対しては8003、executorプロセスに対しては8002でjmx接続できるようになります。

今回は、executorプロセスに接続してみます。

※注意

一つのノードで複数のexecutorが割り当てられうる設定で実行した場合、これだと8002がバッティングする可能性があります。

そういった場合はcom.sun.management.jmxremote.port=0として、動的にポートを取得するなどの手段をとる必要があります。

executorプロセスの実行されているノードの特定

YARNクラスタ上のどのノードでexecutorが実行されるかは動的に決まるため、各ノードにSSHで入ってlsofなどで8002番が使われているかどうか確認し、つなぐ先のノードを特定します。

ここでは、ec2-xxx-xxx-xxx-xxx.ap-northeast-1.compute.amazonaws.comだったとします。

SSHトンネルを開く

手元のマシンで以下のコマンドを実行し、8002番のSSHトンネルを開きます。

$ ssh -i key.pem -NL 8002:ec2-xxx-xxx-xxx-xxx.ap-northeast-1.compute.amazonaws.com:8002 hadoop@ec2-xxx-xxx-xxx-xxx.ap-northeast-1.compute.amazonaws.com

jvisualvmから接続

あとは127.0.0.1:8002に対してjvisualvmから接続すればOKです。

IntelliJでステップ実行する(localモード)

IntelliJのリモートデバッグを使えば、Sparkアプリケーションをステップ実行などできます。

ここではlocalモードで行います。(EMR上でもできるはずだけど)

前提

  • sparkバイナリを落として/path/to/spark_homeに解凍しておきます
  • アプリケーションはjarに固めて/path/to/app.jarに置いておきます

IntelliJの前準備

IntelliJを起動し、Run > Edit Configurationsから、リモートデバッグ用のconfigurationを追加しておきます。

Screen Shot 2017-09-03 at 11.23.02 PM.png

+からRemoteを選択すれば勝手にこれが入力されています。

spark-submit

リモートデバッグ用のオプションをつけて、spark-submitします。

$ cd /path/to/spark_home
$ ./bin/spark-submit --master local 
    --driver-java-options "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005" 
    /path/to/app.jar

IntelliJでリモートデバッグを開始

少し経ってSparkアプリケーションが起動したら、さきほど作成したconfigurationを実行すれば、いつものようにブレークポイントを貼ったりスレッドの状態を見たりwatchを仕込んだりできます。

続きを読む

CloudFormation入門2

CloudFormation入門 の続き。

ここではEC2インスタンスをCloudFormation で生成します。
今回作成する環境は下の表になります。

OS Ubuntu16.04
AMI ami-10504277
Instance Type t2.micro
Root Volume 8GiB
VPC IP Range 10.0.0.0/16
Subnet IP Range 10.0.0.0/24
SecurityGroup SSH(port:22) source:0.0.0.0/0
New Key Pair NO

1. サンプルテンプレートの作成

{
  "AWSTemplateFormatVersion" : "2010-09-09",
  "Description" : "AWS CloudFormation Test Template vpc_single_instance_in_subnet.template: Create a VPC and add an EC2 instance with a security group. ",

  "Parameters" : {
    "InstanceType" : {
      "Description" : "EC2 instance type",
      "Type" : "String",
      "Default" : "t2.micro",
      "AllowedValues" : [ "t1.micro","t2.micro","m1.small","m1.medium","m1.large","m1.xlarge","m2.xlarge","m2.2xlarge","m2.4xlarge","m3.xlarge","m3.2xlarge","c1.medium","c1.xlarge","cc1.4xlarge","cc2.8xlarge","cg1.4xlarge"],
      "ConstraintDescription" : "must be a valid EC2 instance type."
    },
    "KeyName": {
      "Description" : "Name of an existing EC2 KeyPair to enable SSH access to the instance",
      "Type": "String",
      "MinLength": "1",
      "MaxLength": "255",
      "AllowedPattern" : "[\x20-\x7E]*",
      "ConstraintDescription" : "can contain only ASCII characters."
    },
    "SSHLocation" : {
      "Description" : " The IP address range that can be used to SSH to the EC2 instances",
      "Type": "String",
      "MinLength": "9",
      "MaxLength": "18",
      "Default": "0.0.0.0/0",
      "AllowedPattern": "(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/(\d{1,2})",
      "ConstraintDescription": "must be a valid IP CIDR range of the form x.x.x.x/x."
    }
  },

  "Resources" : {
    "VPC" : {
      "Type" : "AWS::EC2::VPC",
      "Properties" : {
        "CidrBlock" : "10.0.0.0/16",
        "Tags" : [ {"Key" : "Application", "Value" : { "Ref" : "AWS::StackId"} } ]
      }
    },
    "Subnet" : {
      "Type" : "AWS::EC2::Subnet",
      "Properties" : {
        "VpcId" : { "Ref" : "VPC" },
        "CidrBlock" : "10.0.0.0/24",
        "MapPublicIpOnLaunch" : "True",
        "Tags" : [ {"Key" : "Application", "Value" : { "Ref" : "AWS::StackId"} } ]
      }
    },
    "InternetGateway" : {
      "Type" : "AWS::EC2::InternetGateway",
      "Properties" : {
        "Tags" : [ {"Key" : "Application", "Value" : { "Ref" : "AWS::StackId"} } ]
      }
    },
    "AttachGateway" : {
       "Type" : "AWS::EC2::VPCGatewayAttachment",
       "Properties" : {
         "VpcId" : { "Ref" : "VPC" },
         "InternetGatewayId" : { "Ref" : "InternetGateway" }
       }
    },
    "RouteTable" : {
      "Type" : "AWS::EC2::RouteTable",
      "Properties" : {
        "VpcId" : {"Ref" : "VPC"},
        "Tags" : [ {"Key" : "Application", "Value" : { "Ref" : "AWS::StackId"} } ]
      }
    },
    "Route" : {
      "Type" : "AWS::EC2::Route",
      "DependsOn" : "AttachGateway",
      "Properties" : {
        "RouteTableId" : { "Ref" : "RouteTable" },
        "DestinationCidrBlock" : "0.0.0.0/0",
        "GatewayId" : { "Ref" : "InternetGateway" }
      }
    },
    "SubnetRouteTableAssociation" : {
      "Type" : "AWS::EC2::SubnetRouteTableAssociation",
      "Properties" : {
        "SubnetId" : { "Ref" : "Subnet" },
        "RouteTableId" : { "Ref" : "RouteTable" }
      }
    },
    "NetworkAcl" : {
      "Type" : "AWS::EC2::NetworkAcl",
      "Properties" : {
        "VpcId" : {"Ref" : "VPC"},
        "Tags" : [ {"Key" : "Application", "Value" : { "Ref" : "AWS::StackId"} } ]
      }
    },

    "InboundNetworkAclEntry" : {
      "Type" : "AWS::EC2::NetworkAclEntry",
      "Properties" : {
        "NetworkAclId" : {"Ref" : "NetworkAcl"},
        "RuleNumber" : "100",
        "Protocol" : "-1",
        "RuleAction" : "allow",
        "Egress" : "false",
        "CidrBlock" : "0.0.0.0/0"
      }
    },
    "OutBoundNetworkAclEntry" : {
      "Type" : "AWS::EC2::NetworkAclEntry",
      "Properties" : {
        "NetworkAclId" : {"Ref" : "NetworkAcl"},
        "RuleNumber" : "100",
        "Protocol" : "-1",
        "RuleAction" : "allow",
        "Egress" : "true",
        "CidrBlock" : "0.0.0.0/0"
      }
    },
    "SubnetNetworkAclAssociation" : {
      "Type" : "AWS::EC2::SubnetNetworkAclAssociation",
      "Properties" : {
        "SubnetId" : { "Ref" : "Subnet" },
        "NetworkAclId" : { "Ref" : "NetworkAcl" }
      }
    },
    "InstanceSecurityGroup" : {
      "Type" : "AWS::EC2::SecurityGroup",
      "Properties" : {
        "VpcId" : { "Ref" : "VPC" },
        "GroupDescription" : "Enable SSH access via port 22",
        "SecurityGroupIngress" : [
          {"IpProtocol" : "tcp", "FromPort" : "22", "ToPort" : "22", "CidrIp" : { "Ref" : "SSHLocation"}}
         ]
      }
    },
    "EC2Instance" : {
      "Type" : "AWS::EC2::Instance",
      "Properties" : {
        "ImageId" : "ami-10504277",
        "SecurityGroupIds" : [{ "Ref" : "InstanceSecurityGroup" }],
        "SubnetId" : { "Ref" : "Subnet" },
        "InstanceType" : { "Ref" : "InstanceType" },
        "KeyName" : { "Ref" : "KeyName" },
        "Tags" : [ { "Key" : "Application", "Value" : "string" } ]
         }
      }
   }
}

設定値の解説

AWS::EC2::VPC
・CidrBlock
  VPCのネットワークアドレス
  ここでは 10.0.0.0/16

AWS::EC2::Subnet
・CidrBlock
  サブネットのネットワークアドレス
  ここでは 10.0.0.0/24
・MapPublicIpOnLaunch
  このサブネットで起動されたインスタンスが起動時にパブリック IP アドレスを設定するか。
  初期値は false。ここでは外部からアクセス可能にするので true

CloudFormation実行

スタック作成を実行すると、パラメータの入力欄が表示される。
image1.png

後は同様にスタック作成を行う。
もちろん、全自動にする場合は、パラメータの値をテンプレートに予め埋め込んでおく。

SecurityGroup とNetworkACLの違い

セキュリティグループ ネットワーク ACL
インスタンスレベルで動作します (第1保護レイヤー) サブネットレベルで動作します (第2保護レイヤー)
ルールの許可のみサポート ルールの許可と拒否がサポートされます
ステートフル: ルールに関係なく、返されたトラフィックが自動的に許可されます ステートレス: 返されたトラフィックがルールによって明示的に許可されます
トラフィックを許可するかどうかを決める前に、すべてのルールを評価します トラフィックを許可するかどうかを決めるときに、順番にルールを処理します
インスタンスの起動時に誰かがセキュリティグループを指定した場合、または後でセキュリティグループをインスタンスに関連付けた場合にのみ、インスタンスに適用されます。 関連付けられたサブネット内のすべてのインスタンスに自動的に適用されます (バックアップの保護レイヤーなので、セキュリティグループを指定する人物に依存する必要はありません)

擬似パラメータ

AWS CloudFormation で事前定義されているパラメーター。テンプレートでは宣言しない。
パラメーターと同じように、Ref 関数の引数として使用します。

パラメータ 解説
AWS::AccountId 123456789012 など、スタックが作成されているアカウントの AWS アカウント ID を返します。
AWS::Region us-west-2 など、包括的なリソースが作成されている AWS リージョンを表す文字列を返します。
AWS::StackId arn:aws:cloudformation:us-west-2:123456789012:stack/teststack/51af3dc0-da77-11e4-872e-1234567db123 など、aws cloudformation create-stack コマンドで指定されたようにスタックの ID を返します。
AWS::StackName teststack など、aws cloudformation create-stack コマンドで指定されたようにスタックの名前を返します。

2. テンプレート作成フロー

2-1. VPCを設定する

・AWS::EC2::VPCのリソースを追加
・PropertiesにCIDRを記載(CidrBlock 属性)

"VPC": {
  "Type": "AWS::EC2::VPC",
  "Properties": {
    "CidrBlock": "192.168.10.0/24"
  }
}

2-2. サブネットを設定する

・AWS::EC2::Subnet のリソースを追加
・PropertiesにVpcId(VPCのリソースID),CIDR(CidrBlock 属性),
・MapPublicIpOnLaunch(起動時のパブリックID有効か) を記載

"Subnet": {
  "Type": "AWS::EC2::Subnet",
  "Properties": {
    "VpcId": {
      "Ref": "VPC"
    },
    "CidrBlock": "192.168.10.0/26"
  }
}

2-3. インターネットゲートウェイを設定する

・インターネットゲートウェイをAWS::EC2::InternetGateway のリソースとして追加、
・インターネットゲートウェイとVPCの紐付けを行う矢印は   
  AWS::EC2::VPCGatewayAttachment
 というリソースとして追加します。

"InternetGateway" : {
  "Type" : "AWS::EC2::InternetGateway",
  "Properties" : {
      "Tags" : [ {"Key" : "Application", "Value" : { "Ref" : "AWS::StackId"} } ]
  }
},
"AttachGateway" : {
  "Type" : "AWS::EC2::VPCGatewayAttachment",
  "Properties" : {
      "VpcId" : { "Ref" : "VPC" },
      "InternetGatewayId" : { "Ref" : "InternetGateway" }
  }
}

2-4. ルートテーブルとルートを設定する

・ルートテーブルをAWS::EC2::RouteTable のリソースとして追加、
・ルートテーブルはVPCとの紐付けを指定。
・ルーティング情報は AWS::EC2::Route に記載します。
・RouteTableId は、ルートテーブルのリソースIDを指定。
・インターネット宛てのルートを指定。
  ”DestinationCidrBlock”: “0.0.0.0/0”,
  ”GatewayId”:

・サブネットをルートテーブルに関連付けるには、
  AWS::EC2::SubnetRouteTableAssociation を利用します。

"RouteTable": {
    "Type": "AWS::EC2::RouteTable",
    "Properties": {
        "VpcId": {
            "Ref": "VPC"
        }
    }
},
"Route" : {
  "Type" : "AWS::EC2::Route",
  "DependsOn" : "AttachGateway",
  "Properties" : {
    "RouteTableId" : { "Ref" : "RouteTable" },
    "DestinationCidrBlock" : "0.0.0.0/0",
    "GatewayId" : { "Ref" : "InternetGateway" }
  }
},
"SubnetRouteTableAssociation" : {
  "Type" : "AWS::EC2::SubnetRouteTableAssociation",
  "Properties" : {
    "SubnetId" : { "Ref" : "Subnet" },
    "RouteTableId" : { "Ref" : "RouteTable" }
  }
},

2-5. NetworkACLの設定

AWS::EC2::NetworkAcl
・VpcId:VPCのIDを指定。

AWS::EC2::NetworkAclEntry
・Egress
 サブネットからの送信トラフィックに適用される (true) か、
 サブネットへの受信トラフィックに適用される (false) か。
 初期値は false 。
・Protocol
 ルールが適用される IP プロトコル。-1 またはプロトコル番号を指定する必要があります
・RuleAction
 ルールに一致するトラフィックを許可または拒否するかどうか。有効な値は “allow” または “deny” 。
・RuleNumber
 エントリに割り当てるルール番号 (100 など)。ACL エントリは、ルール番号の昇順で処理されます。
 エントリには同じルール番号を使用できません

"NetworkAcl" : {
  "Type" : "AWS::EC2::NetworkAcl",
  "Properties" : {
    "VpcId" : {"Ref" : "VPC"},
    "Tags" : [ {"Key" : "Application", "Value" : { "Ref" : "AWS::StackId"} } ]
  }
},
"InboundNetworkAclEntry" : {
  "Type" : "AWS::EC2::NetworkAclEntry",
  "Properties" : {
    "NetworkAclId" : {"Ref" : "NetworkAcl"},
    "RuleNumber" : "100",
    "Protocol" : "-1",
    "RuleAction" : "allow",
    "Egress" : "false",
    "CidrBlock" : "0.0.0.0/0"
  }
},
"OutBoundNetworkAclEntry" : {
  "Type" : "AWS::EC2::NetworkAclEntry",
  "Properties" : {
    "NetworkAclId" : {"Ref" : "NetworkAcl"},
    "RuleNumber" : "100",
    "Protocol" : "-1",
    "RuleAction" : "allow",
    "Egress" : "true",
    "CidrBlock" : "0.0.0.0/0"
  }
},
"SubnetNetworkAclAssociation" : {
  "Type" : "AWS::EC2::SubnetNetworkAclAssociation",
  "Properties" : {
    "SubnetId" : { "Ref" : "Subnet" },
    "NetworkAclId" : { "Ref" : "NetworkAcl" }
  }
}

2-6. セキュリティグループを設定する

セキュリティグループをAWS::EC2::SecurityGroup のリソースとして追加。
・VpcId:VPCのIDを指定。
・SecurityGroupIngress
 Amazon EC2 セキュリティグループの Ingress ルールのリスト。

"InstanceSecurityGroup" : {
  "Type" : "AWS::EC2::SecurityGroup",
  "Properties" : {
    "VpcId" : { "Ref" : "VPC" },
    "GroupDescription" : "Enable SSH access via port 22",
    "SecurityGroupIngress" : [
      {"IpProtocol" : "tcp", "FromPort" : "22", "ToPort" : "22", "CidrIp" : { "Ref" : "SSHLocation"}}
     ]
  }
}

2-7. EC2を設定する

EC2のインスタンスは AWS::EC2::Instance のリソースとして追加。
・SubnetId
 インスタンスの起動先となるサブネットの ID
・ImageId
 登録時に割り当てられた Amazon Machine Image (AMI) の一意の ID
・InstanceType
 インスタンスタイプ (t2.micro など)。デフォルトのタイプは「m3.medium」。
・SecurityGroupIds
 Amazon EC2 インスタンスに割り当てる VPC セキュリティグループのセキュリティグループ ID を含んだリスト。

"EC2Instance" : {
  "Type" : "AWS::EC2::Instance",
  "Properties" : {
    "ImageId" : "ami-10504277",
    "SecurityGroupIds" : [{ "Ref" : "InstanceSecurityGroup" }],
    "SubnetId" : { "Ref" : "Subnet" },
    "InstanceType" : { "Ref" : "InstanceType" },
    "KeyName" : { "Ref" : "KeyName" },
    "Tags" : [ { "Key" : "Application", "Value" : "string" } ]
   }
}

Amazon EC2 インスタンスへの既存の Elastic IP の割り当て
参考: Amazon EC2 テンプレートスニペット

"IPAssoc" : {
  "Type" : "AWS::EC2::EIPAssociation",
  "Properties" : {
    "InstanceId" : { "Ref" : "logical name of an AWS::EC2::Instance resource" },
    "EIP" : "existing Elastic IP address"
  }
}

3. パラメータの指定

パラメータ指定は

"Parameters" : {
    (パラメータの指定),
    (パラメータの指定),...
    (パラメータの指定)
},

で指定する。

パラメータ項目 説明
Description 入力欄の右に表示される。
Type Stringなど
MinLength 最小文字数
MaxLength 最長文字数
Default 初期表示
AllowedValues プルダウン表示される選択肢
AllowedPattern 入力可能な文字パターン
ConstraintDescription 文字パターンが不正な場合の表示メッセージ

パラメータ利用シーン

・キーペア等、AWS固有パラメータを利用すると、毎回検証して表示してくれる
・認証情報はテンプレートに直接記載するのではなく、入力パラメータとして使用する

EC2作成時のパラメータ利用例

"Parameters" : {
  "InstanceType" : {
    "Description" : "EC2 instance type",
    "Type" : "String",
    "Default" : "t2.micro",
    "AllowedValues" : [ "t1.micro","t2.micro","m1.small","m1.medium","m1.large","m1.xlarge","m2.xlarge","m2.2xlarge","m2.4xlarge","m3.xlarge","m3.2xlarge","c1.medium","c1.xlarge","cc1.4xlarge","cc2.8xlarge","cg1.4xlarge"],
    "ConstraintDescription" : "must be a valid EC2 instance type."
  },
  "KeyName": {
    "Description" : "Name of an existing EC2 KeyPair to enable SSH access to the instance",
    "Type": "String",
    "MinLength": "1",
    "MaxLength": "255",
    "AllowedPattern" : "[\x20-\x7E]*",
    "ConstraintDescription" : "can contain only ASCII characters."
  },
  "SSHLocation" : {
    "Description" : " The IP address range that can be used to SSH to the EC2 instances",
    "Type": "String",
    "MinLength": "9",
    "MaxLength": "18",
    "Default": "0.0.0.0/0",
    "AllowedPattern": "(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/(\d{1,2})",
    "ConstraintDescription": "must be a valid IP CIDR range of the form x.x.x.x/x."
  }
},

4. 削除ポリシーの指定

スタックを削除すると、スタック内のAWSリソースはすべて削除される。
削除ポリシーにより、削除したくないリソースを削除しないように出来ます。

CloudFormationには [DeletionPolicy] という項目があります。[DeletionPolicy] は削除時にリソースが
どのような動きをするかを定義する項目です。

削除ポリシーの種類

ポリシー 説明
Delete 削除
Retain 保持。消さない。
Snapshot スナップショット

スナップショット可能なリソースは以下のみ。
AWS::EC2::Volume
AWS::RDS::DBInstance
AWS::Redshift::Cluster

  • 例1 S3のバケットを「Retain」指定で作成した場合
"myS3Bucket" : {
  "Type" : "AWS::S3::Bucket",
  "DeletionPolicy" : "Retain"
}

→ スタック削除時にもバケットは保持されてデータは残ります。

例2 RDBを「Snapshotn」指定で作成した場合

"MyDB" : {
  "Type" : "AWS::RDS::DBInstance",
  "Properties" : {
    "DBName" : { "Ref" : "DBName" },
    "AllocatedStorage" : { "Ref" : "DBAllocatedStorage" },
    "DBInstanceClass" : { "Ref" : "DBInstanceClass" },
    "Engine" : "MySQL",
    "EngineVersion" : "5.5",
    "MasterUsername" : { "Ref" : "DBUser" },
    "MasterUserPassword" : { "Ref" : "DBPassword" },
    "Tags" : [ { "Key" : "Name", "Value" : "My SQL Database" } ]
  },
  "DeletionPolicy" : "Snapshot"
}

→スタック削除時にスナップショットが取れます。

5. CloudFormationの便利機能

・スタックのネスト
テンプレートにテンプレートを指定できる。
例えば、ロードバランサのテンプレート、EC2テンプレートを参照するテンプレートを作成できる
→この時に、ロードバランサのテンプレートを更新した場合、それを参照するスタックの更新時に反映できる

・CloudTrailの利用
CloudTrailを利用し、CloudFormationAPI呼び出しを追跡できる。

・CloudFormerの利用
既存のAWSリソースからテンプレートを作成できる。
CloudFormer専用のインスタンスを一時的に起動させ、Webアクセス、対象のリソースを選択、あとは待つだけ。
  ・専用インスタンスは利用が終わったら削除
  ・実際に利用すときは、作成されたテンプレート内容を確認・修正して問題ないことを確認してから利用する(そのまま使わない)
→別途検証を行う予定。

・カスタムリソース
AWSリソース(EC2やS3)ではないリソースをCloudFormationに含めることができる。
→必要時に検証を行う。

・ヘルパースクリプト
ソフトウェアをインストールしたい場合はヘルパースクリプトを利用する。
cfn-initn等
→必要時に検証を行う。

6. ベストプラクティス

・事前検証
テンプレートを利用する前に検証をする
コマンドは以下の通り。

# aws cloudformation validate-template --template-body file:////Users/cloud-formation/aws.template
# aws cloudformation validate-template --template-url https://s3-ap-northeast-1.amazonaws.com/stacks/aws.template

・アクセス制御
CloudFormationの操作(スタックの作成・表示・削除・更新)をIAMの権限で制御できる。
テンプレートに記載されているAWSリソースの操作権限も必要になる
さらに、EC2やS3等のAWSリソースの利用権限も必要
たとえば、ネットワークチームはVPC、運用チームはEC2起動・停止、開発は全ての権限等チームごとにIAMユーザを作成し、権限を設定できる

・バージョン管理
テンプレートをバージョン管理する。バージョン管理することで、
インフラのバージョン管理が可能
任意の過去の状態に戻すことが可能

・作成前の注意
スタック作成前に、VPCやEC2等の作成数上限に達していないか確認する。

・リソース更新時の注意
リソースをマネジメントコンソールで直接変更しない
CloudFomationスタックとリソース情報に不整合が生じエラーになる
リソース変更時は、必ずテンプレート更新、スタック更新する

・スタックポリシーを使用する
重要なスタックリソースを保護する
→スタックポリシーは別途詳細を確認する。

参考資料

【AWS】CloudFormationまとめ
http://qiita.com/iron-breaker/items/a12d1228de12663e7d32

AWS CLIとCloudFormationでVPCを作ってEC2を立ち上げてみた②
~CloudFormation編~
https://recipe.kc-cloud.jp/archives/8090

AWSのSecurityGroupとNetworkACL
http://yuheikagaya.hatenablog.jp/entry/2015/05/21/000000

セキュリティグループとネットワーク ACL の比較
http://docs.aws.amazon.com/ja_jp/AmazonVPC/latest/UserGuide/VPC_Security.html

AWS CloudFormation のベストプラクティス
http://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/best-practices.html

CloudFormationデザイナーをはじめよう
http://dev.classmethod.jp/cloud/aws/cfndesigner/

続きを読む

【AWS】 EMRについて

立てて自動で動作させて、勝手に死んでほしい。
さて、そんなことをやる上で色々調べたので自分の備忘録のために書いておきます

steps

起動コマンドの中でステップの追加をしておくとそのステップにそって処理をしてくれる。

steps
  --steps Type=CUSTOM_JAR,Name="S3DistCp step",ActionOnFailure=CONTINUE,Jar=command-runner.jar,Args=[s3-dist-cp,--s3Endpoint=s3-ap-northeast-1.amazonaws.com,--src=$S3_SRC,--dest=hdfs:///import_tbl] Type=Hive,Name="TABLE SET UP",ActionOnFailure=CONTINUE,Args=[-f,${pathtohivescript}] 

これでS3から元ネタを取ってきて、処理をさせるという2工程が走る。
ポイントは、1工程 2工程 みたいな感じに工程間はスペースで区切ることだった

auto-terminate

コンソールから起動すると勝手には死なないけど、CLI経由で起動させる場合、このオプションを指定すると、全てのステップが完了した後に勝手にEMRが終了するようになる

s3-dist-cp

S3とHDFS間でデータをやり取りできるツール
上記のstepの1工程目でやってるのは、S3から元データを取ってきてHDFSにぶっこんでいる処理

# サンプルスクリプト

サンプル
aws emr create-cluster --name '${clusterName}' 
    --release-label emr-5.7.0 
    --log-uri '${s3LogUrl}' 
    --applications Name=Ganglia Name=Hadoop Name=Hive Name=Hue Name=Mahout Name=Pig Name=Tez 
    --ec2-attributes '{"KeyName":"${KeyName}","InstanceProfile":"EMR_EC2_DefaultRole","SubnetId":"${SubnetId}"}' 
    --instance-groups '[{"InstanceCount":${masterInstanceCount},"InstanceGroupType":"MASTER","InstanceType":"${masterInstanceType}","Name":"Master Instance Group"},{"InstanceCount":${coreInstanceCount},"InstanceGroupType":"CORE","InstanceType":"${coreInstanceType}","Name":"Core Instance Group"}]' 
    --service-role EMR_DefaultRole 
    --steps Type=CUSTOM_JAR,Name="S3DistCp step",ActionOnFailure=CONTINUE,Jar=command-runner.jar,Args=[s3-dist-cp,--s3Endpoint=s3-ap-northeast-1.amazonaws.com,--src=$S3_SRC,--dest=hdfs:///$HDFS_TBL] Type=Hive,Name="HIVE PG",ActionOnFailure=CONTINUE,Args=[-f,${pathtohivescript}] 
    --region ap-northeast-1 
    --profile ${profile}

Hiveで動かすスクリプトはS3に上がっている必要があり、上記サンプル内ではその s3:// でフルパスを指定している。

続きを読む