Lambda + Apex + API Gateway Slackで緊急連絡先を表示するslashコマンドを作ろう

概要

  • Slackで/tel_listと打ったら緊急連絡先が出るようにする
  • serverless実装

構成図

スクリーンショット 2017-02-20 13.59.25.png

  • slackでtel_list打つ
  • api gatewayがPOSTリクエスト受け取る
  • 指定したlambdaを実行。そこでKMSでslackのtoken認証する
  • lambdaでpython実行し、slackに結果を送る

内容

①apexでlambda関数を管理する

  • ここではKMSのtokenがまだできてないので、コードは書きません
  • apexの使い方やインストール等はこちらをご覧ください
$ mkdir apex_slack_tel_list && cd apex_slack_tel_list
$ apex init
Project name: apex_slack_tel_list
Project description: apex_slack_tel_list is able to show TEL list when anyone was in trouble

### 一度deployする
$ cd apex_slack_tel_list
$ apex deploy

②IAM(KMS)の設定

  • IAM->暗号化キー->リージョンを選んでください->キーの作成

    • エイリアス:apex_lambda_slack_tel_list
    • キーマテリアルオリジン:KMS
    • 次へ
      • タグの追加はなしで次のステップへ

        • apex_slack_tel_list_lambda_function
        • キーの管理者が削除できるようにをチェック入れる
        • 次のステップへ
          • apex_slack_tel_list_lambda_function
          • 次のステップへ

スクリーンショット 2017-02-20 14.21.27.png

  • 完了したらここのARN部分を控えてください。
  • arn:aws:kms:リージョン名:数字:key/key_idとなってます。

③IAMのroleにARNに対する権限追加

  • IAM->ロール->apex_slack_tel_list_lambda_function->ポリシー名->編集
        {
            "Effect": "Allow",
            "Action": [
                "kms:Decrypt"
            ],
            "Resource": [
                "arn:aws:kms:ap-northeast-1:xxxxxxxxxx:key/xxxxxxxxxxxxx"
            ]
        }

④SlackでSlashコマンドの登録

  • slackのapp->Apps & Integration->Slashで検索->Add Configuration->/tel_list で登録

スクリーンショット 2017-02-20 14.24.58.png

  • 登録が終わったらtokenを控えてください

⑤Slackのtokenを暗号化

$ aws kms encrypt --key-id arnに書いてるkey_id --plaintext="slackのtoken"
{
    "KeyId": "arn:aws:kms:ap-northeast-1:xxxxxxxx:key/xxxxxxxxxxxx",
    "CiphertextBlob": "暗号化されたtoken"

⑥apexでコードを書いてdeployする

$ vim functions/hello/main.py
# -*- coding: utf-8 -*-
import boto3
from base64 import b64decode
from urlparse import parse_qs
import logging

ENCRYPTED_EXPECTED_TOKEN = "暗号化されたtoken"

kms = boto3.client('kms')
expected_token = kms.decrypt(CiphertextBlob = b64decode(ENCRYPTED_EXPECTED_TOKEN))['Plaintext']

logger = logging.getLogger()
logger.setLevel(logging.INFO)

def handle(event, context):
    req_body = event['body']
    params = parse_qs(req_body)
    token = params['token'][0]
    if token != expected_token:
        logger.error("Request token (%s) does not match exptected", token)
        raise Exception("Invalid request token")
    tel_list = [
        'sion:000-0000-0000',
        'cojp:111-1111-1111',
    ]

    format_tel_list = 'n'.join(tel_list)
    return { "text": "```%s```" % (format_tel_list) }
### いらないファイルを削除し、最終的にはこんなディレクトリになります
$ tree
├── functions
│   └── hello
│       └── main.py
└── project.json

### deploy
$ cd apex_slack_tel_list
$ apex deploy
  • lambdaからテストしてみましょう。成功するはずです。

⑦API GatewayでAPIを作成

  • API GATEWAY->API->APIの作成

    • 新しいAPI
    • API名:apex_slack_tel_list
    • 説明:lambda経由でslackで緊急連絡先を表示させてます
    • 作成を押す

スクリーンショット 2017-02-17 15.51.10.png

⑧リソースを作成する

  • apex_slack_tel_list->アクション->リソースの作成

    • リソース名:slack
    • リソースパス:slack
    • リソースの作成

スクリーンショット 2017-02-20 14.38.34.png

⑨メソッドを作成する

  • POST
  • apex_slack_tel_list->アクション->メソッドの作成
    • Lambda関数
    • region: 選んでください
    • Lambda関数: apex_slack_tel_list_hello
    • 保存を押す
  • slack/->出来上がったPOSTをクリック->統合リクエスト->本文マッピングテンプレート
    • application/x-www-form-urlencoded
    • テンプレートに書きを登録し保存。(今回は不要ですが、urlDecodeで日本語をPOSTしてもエラーが起きないようにしてます)
{ "body": $util.urlDecode($input.json("$")) }

⑩API-Gatewayのdeployする

  • リソース->アクション->APIのdeploy

    • デプロイされるステージにprod
    • デプロイ
  • 作成が終わったら、ステージ->prod->URLの呼び出し のurlを保存。
  • これを先ほどのslackに登録してください。

⑪実際にslackで打ってみよう

/tel_list
  • すると下記のように自分自身だけに緊急連絡先が表示されるかと思います。

スクリーンショット 2017-02-20 14.49.35.png

最後に

  • Hubot用にサーバを立てなくて良いし、serverless便利

続きを読む

初心者がAWS(EC2)にアプリをデプロイする際に対峙したエラーとその対応

はじめに

デプロイの際に遭遇したエラーたちとの奮闘記です。
まとまってませんが、皆さんのエラー解決のヒントになれば幸いです。

Version等

Ruby: 2.3.1
Rails: 5.0.1
AWS(EC2)・Nginx・Unicorn・Mysqlの組み合わせ。

① rails コマンドが使えない

terminal
[ec2-user@ip-XXX-XX-XX-XXX teatapp]$ rails secret
-bash: rails: コマンドが見つかりません

解決策

exitで一回ec2を出て入り直すとコマンドが使えるようになる。
なんか変なこと起きたらとりあえず再起動しよう。ド初歩 …。

② Mysql2::Error: Can’t connect to local MySQL server through socket ‘/tmp/mysql.sock’ (2)

「’/tmp/mysql.sock’のソケットじゃ、MySQLと通信できません」とのエラー。アプリケーションが指定しているソケットと、データベース側のソケットが食い違っているために通信ができない模様。
ソケットに関しては以下でざっくり掴めると思います。
IT用語辞典e-Words
「分かりそう」で「分からない」でも「分かった」気になれるIT用語辞典

terminal
$ mysql_config --socket

このコマンドでmysql側のソケットタイプが調べられるので調べてみると、

terminal
[ec2-user@ip-XXX-XX-XX-XXX ~]$ mysql_config --socket
/var/lib/mysql/mysql.sock

/var/lib/mysql/mysql.sockこういうタイプのソケットらしい。
一方、アプリ側のソケットを調べてみると、

database.yml
default: &default
  adapter: mysql2
  encoding: utf8
  pool: 5
  username: root
  password:
  socket: /tmp/mysql.sock

/tmp/mysql.sockと、Mysql側のソケットとタイプが異なる。

解決策

ということでアプリ側のソケットの記述を以下のように修正。

database.yml
default: &default
  adapter: mysql2
  encoding: utf8
  pool: 5
  username: root
  password:
  socket: /tmp/mysql.sock

 (中略)

production:
  <<: *default
  database: testapp_production
  username: testapp
  password: <%= ENV['TESTAPP_DATABASE_PASSWORD'] %>
  socket: /var/lib/mysql/mysql.sock

default部分の記述は残したままで、productionの方に加筆すればOK。
これで、ソケットタイプが合致してアプリとデータベースが通信できるようになりました。

と、思いきや

③ ERROR — : Access denied for user ‘testapp’@’localhost’ (using password: NO) (Mysql2::Error)

terminal
[ec2-user@ip-XXX-XX-XX-XXX testapp]$ unicorn_rails -c config/unicorn.rb -E production

のコマンドでunicornを作動しても、うまく立ち上がらず。

何が起こっているのか、エラーの詳細を確認するために、以下のコマンドを実行。

terminal
[ec2-user@ip-XXX-XX-XX-XXX testapp]$ less log/unicorn.stderr.log

log/unicorn.stderr.logのログを辿ると、以下のような記述が。

log/unicorn.stderr.log
ERROR -- : Access denied for user 'testapp'@'localhost' (using password: NO) (Mysql2::Error)

using password: NOということで、どうやら環境変数で設定したパスワードが読み込まれていない模様。
①アプリ側の環境変数を読み込んでいるところの記載
②EC2側の環境変数を書き込んでいるところの記載
をチェックしてみると…

database.yml
production:
  <<: *default
  database: testapp_production
  username: testapp
  password: <%= ENV['TESTAPP_DATABASE_PASSWORD'] %>
  socket: /var/lib/mysql/mysql.sock

アプリ側は、password: <%= ENV['TESTAPP_DATABASE_PASSWORD'] %>との記述。
一方EC2側はの記載を以下のコマンドでチェックしてみると、

terminal
$ sudo vim /etc/environment
etc/environment
DATABASE_PASSWORD='XXXXXXXXXXXXXXX'
SECRET_KEY_BASE='XXXXXXXXXXXXXXX'

DATABASE_PASSWORD='XXXXXXXXXXXXXXX'との記述。

変数がアプリ側とEC2側で食い違ってました。

解決策

ということで、アプリ側の記述を以下のように変更。

database.yml
production:
  <<: *default
  database: testapp_production
  username: testapp
  password: <%= ENV['DATABASE_PASSWORD'] %>   # 「TESTAPP_」を削除
  socket: /var/lib/mysql/mysql.sock

これで環境変数が正しく読み込まれるようになりました。
ちなみにですが、
① Mysqlのroot user passwordの先頭が「0」で始まる場合、環境変数の読み込みがうまくいかない場合がある
② 環境変数名に「-」(ハイフン)を含めると、環境変数の読み込みがうまくいかない場合がある
みたいです。

環境変数を

etc/environment
NEW-APP_DATABASE_PASSWORD='01234567'
SECRET_KEY_BASE='XXXXXXXXXXXXXXX'

のように変数名にハイフンを含めており、パスワードの先頭を0で始めていて、大っ変痛い目に会いました。

ちなみのちなみに、環境変数がちゃんと読み込まれているか確認するには、

terminal
[ec2-user@ip-XXX-XX-XX-XXX testapp]$ rails c

でコンソールを開いて、

terminal
irb(main):001:0> ENV['DATABASE_PASSWORD']
=> "XXXXXXXX"

と環境変数を打ってあげればちゃんと読み込めているか確認できます。

さて、これでunicornが使えるかと思いきやまたMysqlのエラー。

④ ERROR — : Access denied for user ‘testapp’@’localhost’ (using password: YES) (Mysql2::Error)

ソケットも合わせて、
環境変数も読み込めるようにして、
でもエラー。エラー文を見てみると、なんかユーザーのところが「testapp」になっている。ここが原因。

database.yml
default: &default
  adapter: mysql2
  encoding: utf8
  pool: 5
  username: root
  password:
  socket: /tmp/mysql.sock

 (中略)

production:
  <<: *default
  database: testapp_production
  password: <%= ENV['TESTAPP_DATABASE_PASSWORD'] %>
                                                                                   # productionのuser部分を削除。
  socket: /var/lib/mysql/mysql.sock

解決策

productionのuser部分を削除(するとdefaultのusername、つまりrootがproductionでも読み込まれるようになる)。

これで、

terminal
$ unicorn_rails -c config/unicorn.rb -E production

をしてあげるとunicornが立ち上がりました。Mysqlとの通信もうまくいっている模様。でも、

⑤ Unicornは動いているのにページが表示されない

http:// ~Elastic IP~ :3000/でページにアクセスしても、ロードが続くだけで表示されず。

スクリーンショット 2017-02-16 18.28.12.png

と出てきてしまう。ううう…。

これは原因は単純でポートの開け忘れでした。

スクリーンショット 2017-02-16 18.29.40.png

AWSでポート3000を開けてあげて、

ページが表示されました。

参考サイト

IT用語辞典e-Words
「分かりそう」で「分からない」でも「分かった」気になれるIT用語辞典

続きを読む

zabbixを使ってRDS for Auroraの内部ステータスを取得してみました。

目的

Aurora のステータス情報をzabbixで取得したい

手法

Auroraををzabbixで直接参照することはできない(RDSにzabbix-Agentは入らないので)
代替えのインスタンスを起動して、EC2のAgentを使ってAWS Auroraから値を取得する。

検証環境

・Aurora 5.6.10a
・Auroraエンドポイント用のEC2インスタンス(zabbixサーバへ登録済み)
・zabbixサーバ

詳しい手順

Auroraへzabbix監視ユーザの登録

grant process on *.* to 'zabbix'@'%' identified by 'zbxpass';

EC2インスタンスに.my.cnfの作成

$ sudo mkdir /var/lib/zabbix
$ sudo vim /var/lib/zabbix/.my.cnf
$ sudo chmod 400 /var/lib/zabbix/.my.cnf
$ sudo chown -R zabbix:zabbix /var/lib/zabbix

.my.cnfの中身

[client]
user = zabbix
password= zbxpass

/etc/zabbix/zabbix_agentd.d/userparameter_mysql.confの編集

変更前
UserParameter=mysql.status[*],echo "show global status where Variable_name='$1';" | HOME=/var/lib/zabbix mysql -N | awk '{print $$2}'
変更後
UserParameter=mysql.status[*],echo "show global status where Variable_name='$1';" | HOME=/var/lib/zabbix mysql -hxxxxxxx.cluster-xxxxxxx.ap-northeast-1.rds.amazonaws.com -N | awk '{print $$2}'
変更前
UserParameter=mysql.size[*],bash -c 'echo "select sum($(case "$3" in both|"") echo "data_length+index_length";; data|index) echo "$3_length";; free) echo "data_free";; esac)) from information_schema.tables$([[ "$1" = "all" || ! "$1" ]] || echo " where table_schema="$1"")$([[ "$2" = "all" || ! "$2" ]] || echo "and table_name="$2"");" | HOME=/var/lib/zabbix mysql -N'
変更後
UserParameter=mysql.size[*],bash -c 'echo "select sum($(case "$3" in both|"") echo "data_length+index_length";; data|index) echo "$3_length";; free) echo "data_free";; esac)) from information_schema.tables$([[ "$1" = "all" || ! "$1" ]] || echo " where table_schema="$1"")$([[ "$2" = "all" || ! "$2" ]] || echo "and table_name="$2"");" | HOME=/var/lib/zabbix mysql -hxxxxxxx.cluster-xxxxxxx.ap-northeast-1.rds.amazonaws.com -N'
変更前
serParameter=mysql.ping,HOME=/var/lib/zabbix mysqladmin ping | grep -c alive
変更後
serParameter=mysql.ping,HOME=/var/lib/zabbix mysqladmin -hxxxxxxx.cluster-xxxxxxx.ap-northeast-1.rds.amazonaws.com ping | grep -c alive

これで疑似的にEC2上で動くMySQLとして認識されるのでRDSでTemplate App MySQLを利用することができる。

クラスタエンドポイントや、読み込みエンドポイントをそれぞれ指定すれば
両方のステータス値が取得できる。

デメリットとして、RDSが複数ある場合、その台数分の代替えインスタンスが必要になるが
zabbix-agentを動かすだけなら、t2.nanoでも十分かも。

続きを読む

AWS EC2において管理をしやすくするためにChef-zeroを使えるようにする

正直、サーバの管理方法をいい加減確立しないといけないと思うので、覚書。

構成として、EC2にはCentOS7ないしRHEL7を使う

Chef Development Kitインストール

chefの構成要素を一通り含まれているChef Development Kit(以降chefDK)をインストール。
chefDKをインストールすればchef-zero(Chef Client Local Mode)を使用することが可能。
※Chef soloとChef zeroの違いは、スタンドアロン専用のsoloとは異なり、フルバージョンのサーバ/クライアントが動いているので、展開が容易なところ。
chefDKを公開しているサイト( https://downloads.chef.io/ )からrpmを取得してインストール。

rpm -Uvh https://packages.chef.io/files/stable/chefdk/1.2.22/el/7/chefdk-1.2.22-1.el7.x86_64.rpm

knife-zeroインストール

kinfe-zeroはchefDKに含まれていないのでchefコマンドでインストール。
※気を付けないといけないのが、先にknife-zeroをgemでインストールしようとすると通常のchefが入ってしまうので、chefDKのインストールが完了していることを確認する。

chef gem install knife-zero --no-document

Chefのリポジトリ作成する

リポジトリを作成したいディレクトリでコマンドを実行。
※他のパッケージ管理ツールと名称やパスが被らないように注意する。
特に、yumのレポジトリとコマンドが似ているので、うわのそらで実行して失敗したりしないように注意

chef generate repo chef-repo

kinfe-zeroを使用するための設定

ローカルモードを有効とする設定ファイルを作成。

cd chef-repo
mkdir .chef
vim .chef/knife.rb

knife.rbには、動かすだけであれば
local_mode true
だけ、記述すればいいが、正直それだとBootstrapした場合、ノード情報が多すぎるので、ホワイトリストを利用して必要な情報だけ取得するようにする。

knife.rb
local_mode true
knife[:automatic_attribute_whitelist] = [
  "fqdn/",
  "os/",
  "os_version/",
  "hostname",
  "ipaddress/",
  "roles/",
  "recipes/",
  "ipaddress/",
  "platform/",
  "platform_version/",
  "platform_version/",
  "cloud/",
  "cloud_v2/",
  "ec2/ami_id/",
  "ec2/instance_id/",
  "ec2/instance_type/",
  "ec2/placement_availability_zone/",
  "chef_packages/"
]

正直、これでも多いけど、これより減らすと管理が面倒になるのでこのくらいが妥当

リモートサーバに対してbootstrap実行

リモートサーバをノードに追加。
追加されたノードをnode listコマンドで確認可能。

knife zero bootstrap infra -x ec2-user --hint ec2 --sudo
knife zero bootstrap {サーバー名 or IP} -x ec2-user -i {秘密鍵のパス} --hint ec2 -N {サーバーの名称}
knife node list

bootstrapが完了するとnodeディレクトリ以下にjsonファイルが生成される。
ノード追加時に-Nオプションをつけるとノード名を指定することが可能。

クックブックの作成

サーバを構築するためのクックブックを作成。

knife cookbook create {クックブック名}

完了するとcookbook以下にクックブック名のディレクトリが作成される。
デフォルトはcookbook/recipes/

レシピの作成

例としてApache+PHP7のレシピを作成。
apacheはphpの依存でインストールされるのでインストールは特に記述していない。
cookbook/recipes/default.rbのファイルを次のように修正する。

default.rb

package 'git' do
  action :install
end

package 'epel-release.noarch' do
  action :install
end

remote_file "#{Chef::Config[:file_cache_path]}/remi-release-7.rpm" do
    source "http://rpms.famillecollet.com/enterprise/remi-release-7.rpm"
    not_if "rpm -qa | grep -q '^remi-release'"
    action :create
    notifies :install, "rpm_package[remi-release]", :immediately
end

rpm_package "remi-release" do
    source "#{Chef::Config[:file_cache_path]}/remi-release-7.rpm"
    action :nothing
end

package 'php' do
  flush_cache [:before]
  action :install
  options "--enablerepo=remi --enablerepo=remi-php70"
end

%w(php-openssl php-common php-mbstring php-xml).each do |pkg|
  package pkg do
    action :install
    options "--enablerepo=remi --enablerepo=remi-php70"
  end
end

bash 'install_composer' do
  not_if { File.exists?("/usr/local/bin/composer") }
  user 'root'
  cwd '/tmp'
  code <<-EOH
    curl -sS https://getcomposer.org/installer | php -- --install-dir=/tmp
    mv /tmp/composer.phar /usr/local/bin/composer
  EOH
end

packages = %w(unzip fontconfig-devel)
packages.each do |pkg|
  package pkg do
    action :install
  end
end

ノードにクックブックを登録

レシピを実行するためにノードにクックブックを登録する。

knife node run_list add {ノード名} {クックブック名}

レシピの実行

レシピを実行する。

knife zero converge '{ノード名}' --attribute ec2.local_ipv4 -x ec2-user --sudo -i {鍵のパス}

正常終了するとリモート先のサーバにApache+PHP7がインストールされる。

続きを読む

Apache Sparkによる大規模データの分散処理による機械学習(回帰分析) by Amazon EMR

EMRでApache Sparkを使用するに当たって、必要なデータを処理するためのコードと入力データを用意するだけで、面倒な環境構築を行わず、わずかばかりの設定を加えるだけですぐに使用することができます。

ここでは、最初にScalaによるEMR上でのSpark処理、次に他のサービスのトリガーをきっかけに動くJavaによるLambdaでEMRを呼び出す手順を注意点も含めて詳細なメモを書きます。

Scalaによる回帰分析を行う実行ファイルの作成

環境構築

scalaをインストール。

$ cd /tmp
$ curl -O http://downloads.typesafe.com/scala/2.11.8/scala-2.11.8.tgz
$ tar xzf scala-2.11.8.tgz
$ mkdir -p /usr/local/src
$ mv scala-2.11.8 /usr/local/src/scala

$ export PATH=$PATH:/usr/local/src/scala/bin
$ export SCALA_HOME=/usr/local/src/scala

$ which scala

Scalaのパッケージマネージャであるsbtをインストール。

$ brew install sbt
$ which sbt

プロジェクトの作成

プロジェクトを置くディレクトリを作成。

mkdir SparkExampleApp

ビルドの設定ファイルの作成。

vim build.sbt

以下のように記述する。

name := "Spark Sample Project"
version := "1.0"
scalaVersion := "2.11.8"

libraryDependencies ++= Seq(
  "org.apache.spark" %% "spark-core" % "2.0.0",
  "org.apache.spark" %% "spark-mllib" % "2.0.0",
  "org.apache.spark" %% "spark-sql" % "2.0.0",
  "com.databricks" %% "spark-csv" % "1.4.0"
)

実行ファイルの作成。

mkdir -p src/main/scala
vim src/main/scala/SparkExampleApp.scala

SparkExampleApp.scalaは以下のものを使用。

import org.apache.spark.sql.SparkSession
import org.apache.spark.ml.{Pipeline, PipelineModel}
import org.apache.spark.ml.feature._
import org.apache.spark.ml.regression.LinearRegression

object SparkExampleApp {

  def main(args: Array[String]) {
    val spark = SparkSession
      .builder()
      .appName("Spark Sample App")
      .getOrCreate()

    import spark.implicits._

    spark.sparkContext.hadoopConfiguration.set("fs.s3n.awsAccessKeyId", "AKIA****************")
    spark.sparkContext.hadoopConfiguration.set("fs.s3n.awsSecretAccessKey", "****************************************")

    val filePath = args(0)
    val df = spark.sqlContext.read
        .format("com.databricks.spark.csv")
        .option("header", "true")
        .option("inferSchema", "true")
        .load(filePath)

    val assembler = new VectorAssembler()
      .setInputCols(Array("x"))
      .setOutputCol("features")

    val polynomialExpansion = new PolynomialExpansion()
      .setInputCol(assembler.getOutputCol)
      .setOutputCol("polyFeatures")
      .setDegree(4)

    val linearRegression = new LinearRegression()
      .setLabelCol("y")
      .setFeaturesCol(polynomialExpansion.getOutputCol)
      .setMaxIter(100)
      .setRegParam(0.0)

    val pipeline = new Pipeline()
      .setStages(Array(assembler, polynomialExpansion, linearRegression))


    val Array(trainingData, testData) = df.randomSplit(Array(0.7, 0.3))
    val model = pipeline.fit(trainingData)

    val outputFilePath = args(1)
    model.transform(testData)
      .select("x", "prediction")
      .write
      .format("com.databricks.spark.csv")
      .option("header", "false")
      .save(outputFilePath)

  }
}

jarファイルの作成のために以下のコマンドを実行。

sbt package

生成されたtarget/scala-2.11/spark-sample-project_2.11-1.0.jarをS3のsample-bucket/sample-emr/srcにアップロードしておく。

Apache Sparkの設定

http://spark.apache.org/downloads.html
上のURL上で以下のように入力してApache Sparkのデータをダウンロードしてくる。

  1. Choose a Spark release: 2.0.1(Oct 03 2016)
  2. Choose a package type: Pre-build for Hadoop 2.7 and later
  3. Choose a download type: Direct download
  4. Download Spark: spark-2.0.1-bin-hadoop2.7.tgz

ダウンロードしたファイルに移動する。

cd ~/Downloads/spark-2.0.1-bin-hadoop2.7

conf以下にspark-defaults.confというファイルを以下の内容で作成する。

vim conf/spark-defaults.conf
spark-defaults.conf
spark.jars.packages  com.amazonaws:aws-java-sdk:1.7.4,org.apache.hadoop:hadoop-aws:2.7.1

実験用データの作成

ruby -e 'puts "x,y";Range.new(0,5).step(0.05).each {|i| puts "#{i},#{Math.sin(i)+Random.rand(-0.3..0.3)}"}' > data.csv

sample.jpg

グラフの表示

brew install gnuplot
gnuplot
gnuplot> set datafile separator ","
gnuplot> set terminal jpeg
gnuplot> set output "sample.jpg"
gnuplot> plot "data.csv" every ::1
qlmanage -p sample.jpg

実行

S3にバケットを作成しておき、以下のようなバケットポリシーを作成しておく。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "GetObject",
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::sample-bucket/*"
        },
        {
            "Sid": "ListBucket",
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:ListBucket",
            "Resource": "arn:aws:s3:::sample-bucket"
        }
    ]
}

Sparkの実行

./bin/spark-submit --class SparkExampleApp --master local ~/workspace/SparkExampleApp/target/scala-2.11/spark-sample-project_2.11-1.0.jar s3n://sample-bucket/sample-emr/data.csv s3n://sample-bucket/sample-emr/output.csv

実行後の結果は以下のようになる。

sample.jpg

EMRの実行

1.マネジメントコンソールからEMRを選択。
2.[クラスターを作成]を選択して以下のように記述する。

クラスター名: SampleCluster
ログ記録: チェック
S3フォルダー: s3://sample-bucket/sample-emr/logs
起動モード: ステップ実行

ステップタイプ: Spark アプリケーション

[設定]を選択し、ステップを追加の設定を行う。

3.ステップを追加で以下のように入力して、[追加]を選択。

名前: SparkApplication
デプロイモード: クラスター
Spark-submitオプション: –class SparkExampleApp
アプリケーションの場所: s3://sample-bucket/sample-emr/src/spark-sample-project_2.11-1.0.jar
引数: s3n://sample-bucket/sample-emr/data.csv s3n://sample-bucket/sample-emr/output/output.csv
失敗時の操作: クラスターを終了。

ベンダー: Amazon
リリース: emr-5.0.3
アプリケーション: Hadoop 2.7.3, Spark 2.0.1

インスタンスタイプ: m3.xlarge
インスタンス数: 2

アクセス権限: デフォルト
EMRロール: EMR_DefaultRole
EC2 インスタンスプロファイル: EMR_EC2_DefaultRole

4.[クラスターを作成]を選択。

5.デプロイモードはクライアントとクラスターのうちのクラスターを選択。
6.失敗時の操作は、次へ、キャンセルして待機、クラスターを終了からクラスターを終了を選択。

7.起動モードはクラスター、ステップ実行のうちのステップ実行を選択。
8.ステップタイプはストリーミングプログラミング、Hiveプログラム、Pigプログラム、Spark アプリケーション、カスタムJARからSpark アプリケーションを選択。

しばらくすると、EMRのクラスターが終了し、出力先にファイルが生成されていることが確認できる。

JavaによるLambdaからのEMR呼び出し

Eclipseでプロジェクト作成

Apache Mavenを利用してプロジェクト管理する場合。

1.File > New > Other…からMaven > Maven Project
デフォルト設定のまま進めるので、3回[Next]を選択。

Group Id: com.sample.lambda
Artifact Id: com.sapmle.lambda
Version: 0.0.1-SNAPSHOT
Package: com.sample.lambda

[Finish]を選択。

2.pom.xmlを選択してOverviewタブ中の以下を編集。
Name: lambda-java

3.Dependenciesタブを選択。
[Add…]を選択し、以下のように記述して、aws-lambda-java-core: 1.0.0aws-lambda-java-eventsaws-java-sdk-coreaws-java-sdk-emrを追加する。

Group Id: com.amazonaws
Artifact Id: aws-lambda-java-core
Version: 1.0.0
Group Id: com.amazonaws
Artifact Id: aws-lambda-java-events
Version: 1.0.0
Group Id: com.amazonaws
Artifact Id: aws-java-sdk-core
Version: 1.11.49
Group Id: com.amazonaws
Artifact Id: aws-java-sdk-emr
Version: 1.11.49

次に重要なのが

pom.xmlを右クリックして、Maven > Add Plugin を選択し、以下のように入力する。

Group Id: org.apache.maven.plugins
Artifact Id: maven-shade-plugin
バージョン: 2.3

プロジェクトをビルドするために、Package Exploreからcom.sample.lambdaを右クリックして、Run As > Maven Build を実行する。

作成したpom.xmlは以下のようになる。

pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.sample.lambda</groupId>
  <artifactId>com.sample.lambda</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>jar</packaging>

  <url>http://maven.apache.org</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>com.amazonaws</groupId>
        <artifactId>aws-lambda-java-core</artifactId>
        <version>1.0.0</version>
    </dependency>
    <dependency>
        <groupId>com.amazonaws</groupId>
        <artifactId>aws-lambda-java-events</artifactId>
        <version>1.0.0</version>
    </dependency>
    <dependency>
        <groupId>com.amazonaws</groupId>
        <artifactId>aws-java-sdk-emr</artifactId>
        <version>1.11.49</version>
    </dependency>
    <dependency>
        <groupId>com.amazonaws</groupId>
        <artifactId>aws-java-sdk-core</artifactId>
        <version>1.11.49</version>
    </dependency>
  </dependencies>
  <name>lambda-java</name>
  <build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-shade-plugin</artifactId>
            <version>2.3</version>
        </plugin>
    </plugins>
  </build>
</project>

4.src/main/java中のcom.sample.lambdaのSampleCreateEMRClusterHandler.javaを以下のように作成する。

SampleCreateEMRClusterHandler.java
package com.sample.lambda;

import java.util.ArrayList;
import java.util.List;

import com.amazonaws.auth.AWSCredentialsProvider;
import com.amazonaws.auth.EnvironmentVariableCredentialsProvider;
import com.amazonaws.regions.Region;
import com.amazonaws.regions.Regions;

import com.amazonaws.services.elasticmapreduce.AmazonElasticMapReduceClient;
import com.amazonaws.services.elasticmapreduce.model.ActionOnFailure;
import com.amazonaws.services.elasticmapreduce.model.Application;
import com.amazonaws.services.elasticmapreduce.model.HadoopJarStepConfig;
import com.amazonaws.services.elasticmapreduce.model.InstanceGroupConfig;
import com.amazonaws.services.elasticmapreduce.model.InstanceRoleType;
import com.amazonaws.services.elasticmapreduce.model.JobFlowInstancesConfig;
import com.amazonaws.services.elasticmapreduce.model.RunJobFlowRequest;
import com.amazonaws.services.elasticmapreduce.model.RunJobFlowResult;
import com.amazonaws.services.elasticmapreduce.model.StepConfig;
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;

public class SampleCreateEMRClusterHandler implements RequestHandler<Object, String> {

    public String handleRequest(Object event, Context context) {
        AWSCredentialsProvider cp = new EnvironmentVariableCredentialsProvider();
        AmazonElasticMapReduceClient emr = new AmazonElasticMapReduceClient(cp);
        emr.setRegion(Region.getRegion(Regions.US_EAST_1));

        JobFlowInstancesConfig instanceConfig = new JobFlowInstancesConfig()
                .withKeepJobFlowAliveWhenNoSteps(false)
                .withInstanceGroups(buildInstanceGroupConfigs());

        RunJobFlowRequest request = new RunJobFlowRequest()
                .withName("SampleCluster")
                .withReleaseLabel("emr-5.0.3")
                .withLogUri("s3://sample-bucket/sample-emr/logs/")
                .withServiceRole("EMR_DefaultRole")
                .withJobFlowRole("EMR_EC2_DefaultRole")
                .withVisibleToAllUsers(true)
                .withInstances(instanceConfig)
                .withApplications(buildApplications())
                .withSteps(buildStepConfigs());

        RunJobFlowResult result = emr.runJobFlow(request);
        return "Process complete.";
    }

    private List<Application> buildApplications() {
        List<Application> apps = new ArrayList<Application>();
        apps.add(new Application().withName("Hadoop"));
        apps.add(new Application().withName("Spark"));
        return apps;
    }

    private List<InstanceGroupConfig> buildInstanceGroupConfigs() {
        List<InstanceGroupConfig> result = new ArrayList<InstanceGroupConfig>();
        InstanceGroupConfig masterInstance = new InstanceGroupConfig()
                .withName("MasterNode")
                .withInstanceRole(InstanceRoleType.MASTER)
                .withInstanceCount(1)
                .withInstanceType("m3.xlarge");
        result.add(masterInstance);

        InstanceGroupConfig coreInsetance = new InstanceGroupConfig()
                .withName("CoreNode")
                .withInstanceRole(InstanceRoleType.CORE)
                .withInstanceCount(1)
                .withInstanceType("m3.xlarge");
        result.add(coreInsetance);

        return result;
    }

    private List<StepConfig> buildStepConfigs() {
        List<StepConfig> result = new ArrayList<StepConfig>();

        final String[] args = {
                "spark-submit",
                "--deploy-mode", "cluster",
                "--class", "SparkExampleApp",
                "s3://sample-bucket/sample-emr/src/spark-sample-project_2.11-1.0.jar", 
                "s3n://sample-bucket/sample-emr/data.csv",
                "s3n://sample-bucket/sample-emr/output/output.csv"
        };

        final StepConfig sparkStep = new StepConfig()
                .withName("SparkProcess")
                .withActionOnFailure(ActionOnFailure.TERMINATE_CLUSTER)
                .withHadoopJarStep(new HadoopJarStepConfig()
                        .withJar("command-runner.jar")
                        .withArgs(args));
        result.add(sparkStep);

        return result;
    }

}

以上実行コードの完成。プロジェクトを再度ビルドする。

(追記) TerminalからのMavenによるビルド方法

Mavenをインストールしていない場合は以下の手順でインストールする。

Mavenを以下のURLからダウンロードしてくる。
http://maven.apache.org/download.cgi

mv apache-maven-3.3.9 /usr/local

.zshrcに以下を追加

export M3_HOME=/usr/local/apache-maven-3.3.9
M3=$M3_HOME/bin
export PATH=$M3:$PATH
source ~/.zshrc

プロジェクト直下にterminalで移動し、以下を実行する。

bashを使用している場合は、~/.zshrc~/.bashrcに置き換えて考える。

mvn package

target以下にcom.sapmle.lambda-0.0.1-SNAPSHOT.jarが生成される。こちらがLambdaにアップロードするものとなる。

IAMロールの作成

マネジメントコンソールからIdentity and Access Management
[ロール]から[新しいロールの作成]を選択。

手順 1: ロール名の設定

以下のように入力して[次のステップ]を選択。
ロール名: sampleRole

手順 2: ロールタイプの選択

AWS Lambdaを選択。

手順 3: 信頼性の確立

信頼関係はLambdaに設定されて飛ばされる。

手順 4: ポリシーのアタッチ

AmazonElasticMapReduceRoleを選択して、[次のステップ]を選択。

手順 5: 確認

内容を確認したら、[ロールの作成]を選択。

Lambdaの作成

マネジメントコンソールからLamdaを選択。
[Create a Lambda function]を選択。

Select blueprint

Blank functionを選択。

Configure triggers

ここでは何も設定しないが、必要に応じてLambdaを呼び出す元となるサービスを指定する。
[Next]を選択。

Configure function

Name: SampleLambda
Runtime*: Java8

Code entry type: Upload a .ZIP or JAR file
Function package: [先ほど作成したJARファイルを指定]

Handler*: com.sample.lambda.SampleCreateEMRClusterHandler::handleRequest
Role*: Choose an existing role
Existing role: sampleRole

[Next]を選択。

Review

最後に設定内容を確認して、実際にTestを実行してみてエラーが出ないか確認してみる。

SNSによるLambdaのトリガー処理

SNSから先程作成したLambdaをトリガーするTopicを生成する。
ここでは、Node.jsから呼び出す例を取り上げます。

node.js
var AWS = require('aws-sdk');
AWS.config.update({
        accessKeyId: 'AKIAXXXXXXXXXXXXXXXX',
        secretAccessKey: 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
        region: 'us-east-1'
});

publishSNSTopic();

const publishSNSTopic = () => {
    var sns = new AWS.SNS({
        apiVersion: "2010-03-31",
        region: "us-east-1"
    });

    sns.publish({
        Message: 'Start Job by triggering lambda for EMR',
        Subject: "Trigger Lambda for EMR",
        TopicArn: "arn:aws:sns:us-east-1:xxxxxxxxxxxx:<トピック名>"
    }, function(err, data) {
        if (err) console.log("Failure to publish topic by SNS");
    });
}
$ node index.js

注意点

  • URI schemeはs3ではなくs3nを指定。
    s3: S3ブロックファイルシステム
    s3n: S3ネイティブファイルシステム

以前はs3nを使っていたが、今はs3を推奨。
サービス提供者のドキュメントを読んでそれに合わせることが大事。

  • EMRでログ記録はチェックするようにする
    エラーが出た時のデバッグの根拠になるから。

  • EMR: 出力先にファイルがあるとエラーになる。(output.csv/)

  • com/amazonaws/auth/AWSCredentialsProvider: class java.lang.NoClassDefFoundError

maven-shade-pluginを忘れずに追加することが重要。
https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/java-create-jar-pkg-maven-and-eclipse.html
https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/java-create-jar-pkg-maven-no-ide.html

  • java.lang.ClassNotFoundException: com.amazonaws.ClientConfigurationFactory

aws-java-sdk-coreaws-java-sdk-emrのバージョンを1.11.49に統一

http://stackoverflow.com/questions/36796268/java-lang-classnotfoundexception-com-amazonaws-clientconfigurationfactory

  • spark-submitコマンド実行時の注意点
spark-submit --deploy-mode cluster --class SparkExampleApp s3n://sample-bucket/sample-emr/src/spark-sample-project_2.11-1.0.jar s3n://sample-bucket/sample-emr/data.csv s3n://sample-bucket/sample-emr/output/output.csv

じゃなくて

spark-submit --deploy-mode cluster --class SparkExampleApp s3://sample-bucket/sample-emr/src/spark-sample-project_2.11-1.0.jar s3n://sample-bucket/sample-emr/data.csv s3n://sample-bucket/sample-emr/output/output.csv

原因は、EMRでS3を呼び出すときに、昔はs3nを使っており、今はs3を推奨しており、ScalaのAWSをライブラリはs3nで認識するのに対して、EMRはs3じゃないと受け付けないことが原因。

参考

Scalaをsbtでビルド
http://qiita.com/kanuma1984/items/6f599c815cc8f9232228

EMRでSpark
http://www.atmarkit.co.jp/ait/articles/1609/27/news018.html

JavaによるLambda作成
http://docs.aws.amazon.com/ja_jp/ElasticMapReduce/latest/DeveloperGuide/calling-emr-with-java-sdk.html

Lambda+Java
http://qiita.com/Keisuke69/items/fc39a2f464d14480a432

Lambda+EMR
http://qiita.com/yskazuma/items/b67b1f3f8c39a7a19051
http://qiita.com/RyujiKawazoe/items/534f4b069ebea2f7d26c

続きを読む

DarkforestをUbuntu14.04で動かしてみた

Darkforestを動かしていくまでを記録してみましたので、参考になればと思います
今回はAmazon EC2のg2.2xlergeを使っていきます
こちらは有料なので、使われる場合は金額をご確認ください
インスタンスの使用数に制限があり、デフォルトではg2.2xlargeは0(!)になっているかと思いますので、制限緩和のリクエストをしなければならないので、先にリクエストしておきましょう

OSはUbuntu14.04LTSを選択しました
DarkforestのGithubはこちら
https://github.com/facebookresearch/darkforestGo

gitインストール
$sudo apt-get install git

vimインストール
$sudo apt-get install vim

$makeインストール

$sudo apt-get install -y build-essential cmake

パスワード設定
$sudo passwd (username)

パッケージのアップデート
$sudo aptitude update

$sudo aptitude -y full-upgrade

torchインストール
$git clone https://github.com/torch/distro.git ~/torch –recursive

$cd ~/torch; bash install-deps;

$./install.sh

$source ~/.bashrc

デフォルトのグラフィックドライバを停止
$sudo vim /etc/modprobe.d/blacklist-nouveau.conf
以下入力
blacklist nouveau
blacklist lbm-nouveau
options nouveau modeset=0
alias nouveau off
alias lbm-nouveau off
sudo vim /etc/modprobe.d/nouveau-kms.conf
options nouveau modeset=0

$sudo update-initramfs -u

$sudo reboot

CUDAインストール
NVIDIA公式サイトにてCUDAをダウンロードする(最新版で良いと思います)
$sudo dpkg -i cuda-repo-ubuntu1404_8.0.44-1_amd64.deb

$sudo apt-get update

$sudo apt-get install cuda(長い)

$echo “export PATH=/usr/local/cuda/bin/:$PATH; export

$LD_LIBRARY_PATH=/usr/local/cuda/lib64/:$LD_LIBRARY_PATH; ” >>~/.bashrc && source ~/.bashrc

cuDNNインストール
公式サイトよりcudaのverに合わせてダウンロードする(こちらも最新版で良いと思います)
$tar -xvf cudnn-8.0-linux-x64-v5.1.tgz

$sudo cp cuda/include/*.h /usr/local/cuda/include

$sudo cp cuda/lib64/.so /usr/local/cuda/lib64

パッケージのインストール
githubでは以下だけだが、cunnも必要なようです(現段階では入れなくてもよい)
$sudo apt-get install luarocks

$luarocks install class

$luarocks install image

$luarocks install tds

$luarocks install cudnn

darkforestをインストール
$git clone https://github.com/facebookresearch/darkforestGo.git ~/darkforest –recursive

$cd ~/darkforest

$sh ./compile.sh

モデルファイルを作る
$mkdir ~/darkforest/models

全部モデルファイルに入れておく
$cd models/

$wget https://www.dropbox.com/sh/6nm8g8z163omb9f/AAAOsp9WzSQrITY9veSmOA1pa/df1.bin?dl=0 -O df1.bin

$wget https://www.dropbox.com/sh/6nm8g8z163omb9f/AACZwAeb0OOCReoeIWPRgILta/df2.bin?dl=0 -O df2.bin

$wget https://www.dropbox.com/sh/6nm8g8z163omb9f/AABrO3wRZ5hLOk70gmu3rK7Ja/LICENSE?dl=0 -O LICENSE

$wget https://www.dropbox.com/sh/6nm8g8z163omb9f/AABcYJKMOl6-Uol98boGa7n5a/playout-model.bin?dl=0 -O playout-model.bin

pipeファイル用ディレクトリ
$mkdir ~/df_pipe

動かす
$cd ~/darkforest/local_evaluator

$sh cnn_evaluator.sh 1 ~/df_pipe

(output path = /~/df_pipe
数字)で処理が止まってしまった場合は、df_pipeの中身を確認
私の場合はcnn_eval-1.logに(cunn)が見つからないといわれていたので、luarocks install cunnで入れました

$cd ~/darkforest/cnnPlayerV2

$th cnnPlayerMCTSV2.lua –pipe_path ~/df_pipe (–option)

これでGTPコマンドでの操作が可能です(公式では以下)
clear_board (盤面の初期化)
genmove b (Darkforestに黒番で次の一手を打たせる)
play w Q4 (自分が白番で指定の座標に打つ) ← genmove w Q4 をお勧めします
quit (終了)

genmove b
qiita_genmove.PNG

goguiでの遊び方
javaのインストール
$sudo apt install default-jre

goguiをインスト―ル
$wget http://downloads.sourceforge.net/project/gogui/gogui/1.4.9/gogui-1.4.9.zip

解凍
$unzip gogui-1.4.9.zip

仮想環境の場合以下から、vncserverなどで行ったほうが良いだろう
$cd gogui-1.4.9/bin/

$./gogui

プログラムの登録方法
~/darkforest でpipeを通すところまで行う

Program/NewProgram…をクリック
コマンド(Command)
th cnnPlayerMCTSV2.lua ―pipe_path ~/df_pipe -num_gpu 1 -time_limit 5
time_limitは変更可能

Workingdirectry
~/darkforest/cnnPlayerV2
パスはフルパスで入力

Program/Attachで先ほど登録したものを選択

Game/New Gameをクリックし、対局が始められる

デフォルトではComputerが白番だが、以下から
Game/Computer Colorで手番を決められる

やってみた
gogui.PNG

棋譜は載せませんが、軽く対局してみた感じですと、有段者レベル(1~3d)はあるかなぁという感じでした
悪手と守る手が多くて… ( ´∀` )

学習につきましては、試行錯誤してみましたが、どうもうまくいかないようでした

続きを読む

[Rails]本番環境のデプロイで思わぬところで躓いた

環境

Ruby 2.3.1
Rails 5.0.0
unicorn
AWS/EC2

鍵の設定やらMySQLのインストールやらでちょこちょこ躓きながらも、
ようやくアセットファイルのコンパイルにたどり着きました。

rails assets:precompileとコマンドを打つことでpublic/assets以下にファイルができるのですが、
下記の通りCSSが読み込まれないという現象が。

スクリーンショット 2017-02-12 15.27.16.png

一見するとCSSだけが読み込まれていないように見えますが、
chromeのデベロッパーツールを見るともはやHTMLから違う、、、

EC2サーバーには最新のコードをクローンしているし、vimで中身を見ても問題ない。
とりあえずCSSが効いていないと踏んで検索するとconfig.assets.compileをtrueにしたりキャッシュを消したり色々出てきました。

あれこれ試しましたが、結果は変わらず。
var/wwwからディレクトリを作り直したり、ブラウザのキャッシュを消したりしてもダメ。

しかし結果的にはgemのhaml-railsが開発環境でしかインストールされておらず、
CSSはおろかHTMLも読み込まれていないということでした。

後日検証

 gem haml-railsをしっかりと本番環境にインストールし、再度デプロイ。
案の定CSSは反映されていませんが、前日までとは違い、ちゃんとコード通りのHTMLが表示されている。

スクリーンショット 2017-02-12 17.14.41.png

私の場合は

bundle exec rake assets:precompile RAILS_ENV=production

とするだけで無事読み込まれました。

そしてついにデプロイ。

スクリーンショット 2017-02-12 19.47.52.png

アセットコンパイルが怪しい決め込んでいたことが、泥沼にはまった原因です。猛省します。

続きを読む

【AWS】DBパラメータグループの内容をBacklogのWikiにはっつけるメモ

概要

  • RDSの特定のDBパラメータグループをBacklogのWikiにはっつける
  • 前提
    • AWS CLIが使えること
    • WikiはBacklog形式

メモ

1. データをテーブル形式でひっぱってくる

MySQL 5.7のデフォルトパラメータグループの場合

$ aws rds describe-db-parameters --db-parameter-group-name default.mysql5.7 --query "Parameters[*].[ParameterName, ParameterValue, AllowedValues, ApplyType, Description]" --output table > hoge.txt

2.スペースを削除

vim とかで頑張る

3.Backlogにはっつける

コピペして、ヘッダ行をつける(以下例)
|パラメータ名|デフォルト値|許容される値|適用タイプ|説明|h

続きを読む

RaspberryPi + Athena + Quicksightで可視化<前編>

概要

本コンテンツは、JAWS DAYS 2017IoTハンズオン向けに作成したものです。
温度センサーやAWSの新サービスを活用し、下記のようなデータフローを構築します。
温度センサー -> RaspberryPi -> Kinesis Firehose -> S3 -> Athena -> Quicksight
本格的な分析や可視化にも耐えうる構成となっており、プロダクションへの応用も可能です。また、温度センサーの組み立てでは簡単な電子工作を体験できます。
スクリーンショット 2017-02-06 21.28.43.png

<前半>の範囲

<前半>では、温度センサー -> RaspberryPi -> Kinesis Firehose -> S3 の部分を説明します。成功すれば下記のようなJSONメッセージがS3バケットに格納されます。汎用的なJSONメッセージですので、後工程で様々に活用することが可能(ココ重要!)です。

{"time":2017-02-04 19:14:33, "temperature":25.562}
{"time":2017-02-04 19:14:43, "temperature":25.562}
{"time":2017-02-04 19:14:53, "temperature":25.5}
{"time":2017-02-04 19:15:03, "temperature":19.5}
{"time":2017-02-04 19:15:13, "temperature":15.375}
{"time":2017-02-04 19:15:23, "temperature":14.5}

必要な機材

  • PC
  • RaspberryPi 2または3
  • micro USB電源
  • micro SDカード (8GB以上)
  • 温度センサーセット
    • DS18B20
    • 2.2kΩカーボン抵抗
    • ブレッドボード
    • ジャンパワイヤ オスーメス 3本

前提条件

  • AWSアカウントをもっていること。アカウント開設手順はこちら
  • RaspberryPiにRaspbianがインストール済みで、PCからSSHでログインまたはHDMI接続のモニタでターミナル操作ができること。手順はこちら。Raspbianは[Release date:2017-01-11]で動作確認しています
  • RaspberryPiがインターネットに接続できていること

<前編>で利用するAWSサービス

Amazon Kinesis Firehose

多数のノードからデータを集約する場合、古くはFTPサーバを利用しました。現在であればHTTPベースのAPIサーバといったところでしょうか。数千~数万といった多数のIoTエッジデバイスノードを集約する場合、サーバのスケールや分散処理が大きな課題となります。Kinesisファミリーはストリーミングデータの集約に特化したフルマネージドサービスで、単一のエンドポイントで大量のストリーミングデータを処理することができます。

  • データはクレデンシャルをつけてエンドポイントにHTTPSでPOSTする。CLIやSDKも利用可能
  • 受信したデータはAmazon Kinesis Analytics、Amazon S3、Amazon Redshift、および Amazon Elasticsearch Serviceのどれかに配送される
  • 配送先には設定したインターバル毎にデータが届く。最小のインターバルは60秒
  • データ転送量による課金
  • オートスケールで、1 秒あたり数ギガバイトを超える入力データレートにも対応
  • さらなるリアルタイム性(遅延数秒以内)が求められる場合は、Kinesis Streamsを使う

Amazon S3

一見ただのオブジェクトストレージ(ファイルサーバ)ですが、AWSの根幹をなすサービスと言え、非常に多くの機能をもっており、一言で説明することはできません。今回の利用用途に絞って説明します。

  • API/CLI/SDKでHTTPSによるアクセス
  • ストレージ容量とリクエスト数とデータ転送量(アウトバウンド)による課金。ストレージ容量1GBあたり3円/月程度と非常に低料金
  • バケット毎にリージョンを指定でき、リージョン内で3箇所にコピーが作成される
  • IoTにおけるユースケースとしては、データのハブとしての使い方が有効。S3にデータを集めておけばAWSのさまざまなサービスから利用可能となる。JSON形式のデータが望ましい

[ToDo 0] RaspberryPiの準備

インターネット接続確認

RaspberryPiをインターネットに接続し、PCから有線LANまたはWi-Fi経由でSSH接続します。
RaspberryPiのIPアドレスが分からない場合は、「Raspberry piをインストールして設定してみよう」を参考にUSBシリアル変換ケーブルを使ってログインし、ifconfigコマンドを実行しましょう。下記の場合、192.168.0.209がRaspberryPiのIPアドレス(Wi-Fi)であることが分かります。
wlan0にIPアドレスが割り当てられていない場合は、「Raspberry piをインストールして設定してみよう」を参考にWi-Fiアクセスポイントへの接続設定を行って下さい。

$ ifconfig
wlan0     Link encap:Ethernet  HWaddr cc:e1:d5:4d:59:5e
          inet addr:192.168.0.209  Bcast:192.168.0.255  Mask:255.255.255.0
          inet6 addr: 2001:268:d006:d652::2/128 Scope:Global

wgetコマンドで適当なURLを打ってみればインターネットに接続できているか確認ができます。

 $ wget https://google.co.jp

--2016-03-23 17:53:19--  https://google.co.jp/
Resolving google.co.jp (google.co.jp)... 2404:6800:4003:809::2003, 106.162.198.104, 106.162.198.123, ...
Connecting to google.co.jp (google.co.jp)|2404:6800:4003:809::2003|:443... connected.
HTTP request sent, awaiting response... 301 Moved Permanently
Location: https://www.google.co.jp/ [following]
--2016-03-23 17:53:30--  https://www.google.co.jp/
Resolving www.google.co.jp (www.google.co.jp)... 2404:6800:4007:805::2003, 106.162.192.173, 106.162.192.167, ...
Connecting to www.google.co.jp (www.google.co.jp)|2404:6800:4007:805::2003|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: unspecified [text/html]
Saving to: ‘index.html’

index.html              [ <=>                  ]  18.90K  --.-KB/s   in 0.1s

2016-03-23 17:53:32 (130 KB/s) - ‘index.html’ saved [19358]

アップデートとvimのインストール

インターネット接続が確認できたら、下記を実行します。

$ sudo apt-get update
$ sudo apt-get install -y vim

[ToDo 1] APIアクセス用のKeyを作成する(AWS)

Kinesis FirehoseにAPIでアクセスするために、AWSのIAM Access Keyを作成します。
今回作成するのは下記のユーザーおよびそれにひも付くKeyです。

ユーザー名 アクセス権 使用箇所
firehose-full AmazonKinesisFirehoseFullAccess RaspberryPiからKinesisへの書き込み
  • AWSにサインインする
  • Identity and Access Management(IAM)コンソールを開く
    ※ 英語画面で説明するので日本語表示になっている場合は、画面左下の言語選択を「English」に変更する
    AWS Management Console 2017-02-05 15-21-17.png
  • 「firehose-full」という名前でProgrammatic accessユーザーを作成する
    IAM Management Console 2017-02-05 15-25-26.png

    IAM Management Console 2017-02-05 15-27-19.png

  • [Attach existing policies directly]をクリックし、[firehose]で検索して、[AmazonKinesisFirehoseFullAccess]にチェックを入れる
  • [Next: Review]をクリックする
    IAM Management Console 2017-02-05 15-36-54.png
  • [Create user]をクリックする
    IAM Management Console 2017-02-05 15-38-24.png
  • 認証情報の書かれたCSVをダウンロードし、[Close]をクリックする
    IAM Management Console 2017-02-05 15-41-43.png

[ToDo 2] KinesisFirehoseの設定(AWS)

  • Kinesisコンソールを開く
    AWS Management Console 2017-02-05 15-13-43.png

  • Kinesis Firehoseは2016年10月現在、東京リージョンでは提供されていないので、利用可能な北バージニア(N.Virginia)リージョンを選択する
    k-1.png
  • [Go to Firehose]をクリックする
    スクリーンショット 2016-10-19 13.42.20.png
  • [Create Delivery Stream]をクリックする
    Amazon Kinesis Firehose 2016-10-19 13-43-18.png
  • 配送先に[Amazon S3]を選択する
    スクリーンショット 2016-10-19 13.44.22.png
  • Stream Nameを入力し、ログを格納するS3バケットを選択(ここではともに「iotan-f」としている)する。バケットはここで作成することもできる(S3バケットのリージョンはお好みで)
  • ログのファイル名はデフォルトでStream名+タイムスタンプとなるが、必要であればプレフィックスを指定する
  • [Next]をクリックする
    Amazon Kinesis Firehose 2016-10-19 13-46-27.png
  • Buffer interval(S3への送信間隔)を指定する。最小値は60秒
  • IAM roleで[Firehose Delivery IAM role]を選択する
  • 他はデフォルトでOK
    c6aa8e58-6526-40dd-c9b7-ad890b113caa.png
  • そのまま[Allow]をクリックする
    IAM Management Console 2016-10-19 13-49-20.png
  • そのまま[Next]をクリックする
    Amazon Kinesis Firehose 2016-10-19 13-50-05.png
  • [Create Delivery Stream]をクリックする
    Amazon Kinesis Firehose 2016-10-19 13-51-10.png
  • S3配送ストリームが作成された。[ACTIVE]になったら利用可能
    Amazon Kinesis Firehose 2016-10-19 14-24-14.png

[ToDo 3] AWS CLIをインストールする(RaspberryPi)

AWS CLIのインストール

pipコマンドでAWS CLIをインストールします。

$ sudo pip install awscli
(途中省略)
Successfully installed awscli botocore PyYAML docutils rsa s3transfer jmespath python-dateutil futures
Cleaning up...

AWS CLIの設定

CLIを実行する際のリージョンや認証情報を設定します。認証情報は[ToDo 1]でダウンロードしたCSVを参照します。

項目 入力内容
AWS Access Key ID (CSV参照)
AWS Secret Access Key (CSV参照)
Default region name us-east-1
Default output format json
$ aws configure
AWS Access Key ID [None]: ************                    
AWS Secret Access Key [None]: ************
Default region name [None]: us-east-1
Default output format [None]: json

[ToDo 4] AWS CLIでKinesis Firehoseにput-recordしてみる(RaspberryPi / AWS)

テストデータのput-record

AWS CLIでKinesis Firehoseにテストデータをput-recordしてみます。--delivery-stream-nameのあとの*****には、[ToDo 2]で設定したストリーム名を入力します。
下記のような"RecordId"のレスポンスが表示されたら成功です。データがS3に書き込まれるまで、最大で[ToDo 2]で設定したBuffer intervalの秒数だけかかります。

$ aws firehose put-record --delivery-stream-name ***** --record Data=blob
{
    "RecordId": "PEMfHNCtnMGQ5+bCvkm7TgynKepLxf2awvfD5He1atikTPY3zg8Eo2o3LnGhEqYzlVFUEnbv4xT7z8t/AUOVMI8Fy4EcznypTh1xDLP1RnoBtB7MzXOa1094BJ3WhvSV9vWC0sxaHeTAMcDoe1hkSrDQ1BsVZje3iMGkxOuMpnCKw/wEAhPtFn3V3/safpY7sTm7dr8oGYWbPNGzxN7wc6K6VgTYzV6Z"
}

S3バケットの確認

  • S3コンソールを開く(新UIで説明)
  • [ToDo 2]で設定したバケット -> 年 -> 月 -> 日 -> 時(UTC) の順に開く
  • 1つだけファイルがあるはずなので、ダウンロードする
    S3 Management Console 2017-02-05 18-28-22.png
  • テキストエディタで開くと送信内容が確認できる
  • 確認後はバケットからファイルを削除しておく
    iotan-f-2-2017-02-05-09-07-49-8c4cf761-2815-4bda-8f76-be03d3525ee6 (1) 2017-02-05 18-30-49.png

S3バケットにファイルが書き込まれない場合は・・・

Kinesis Firehoseのコンソール画面からデモデータが送信できるので、S3バケットに書き込まれるか確認することで原因の切り分けができます。
Amazon Kinesis Firehose 2017-02-05 18-21-24.png
iotan-f-2-2017-02-05-09-39-46-02201148-4ccd-4c57-81eb-81f5d7fc8c4e 2017-02-05 18-43-29.png

[ToDo 5] 温度センサー回路を組み立てて温度を計測する(RaspberryPi)

1-wireデジタル温度センサーとは

RaspberryPiのGPIOはデジタル信号しか受け付けませんが、多くのセンサー類はアナログ信号を出力しますので、ADコンバーターを使って信号を変換する必要があります。今回使うデジタル温度センサーDS18B20は、1-wire規格に準拠しており、1本の信号線だけで低速なデジタルデータ転送を行うことができるので、回路が非常にシンプルになります。

温度センサー回路の組み立て

念のため、RaspberryPiをシャットダウンして回路を接続して下さい。
下記のパーツを図の通り配線し、RaspberryPiの電源をONにします。ジャンパワイヤの色は図の通りでなくてかまいません。

  • DS18B20
  • 2.2kΩカーボン抵抗
  • ブレッドボード
  • ジャンパワイヤ オスーメス 3本

DS18B20.png

RaspberryPiの設定

  • 1-wireデバイス用のモジュールを起動時に読み込むようにする
$ sudo vim /etc/modules

(最後尾に下記2行を追記する)
w1-gpio
w1-therm
  • 1-wireデバイスのGPIOに関する設定を記述する
$ sudo vim /boot/config.txt

(最後尾に下記1行を追記する)
dtoverlay=w1-gpio-pullup,gpiopin=4
  • リブート後、再度ログインする
$ sudo reboot

温度を読み出す

DS18B20は温度をファイルに書き込む仕様となっています。再起動すると/sys/bus/w1/devices/28-************/ (28-************は個体により異なるデバイスID)というディレクトリが作成されており、その中にw1_slaveというファイルが見つかるはずです。t=に続く5桁の整数を1000で割った数が温度(摂氏)です。
デバイスID 28-************[ToDo 6]で使うので控えておいてください

$ cd /sys/bus/w1/devices/
$ ls -l
total 0
lrwxrwxrwx 1 root root 0 Feb  6 01:28 28-************ -> ../../../devices/w1_bus_master1/28-************
lrwxrwxrwx 1 root root 0 Feb  6 01:28 w1_bus_master1 -> ../../../devices/w1_bus_master1
$ cd 28-************
$ cat w1_slave 
a7 01 55 00 7f ff 0c 10 22 : crc=22 YES
a7 01 55 00 7f ff 0c 10 22 t=26437

pi@raspberrypi: :sys:bus:w1:devices:28-0214642802ff — ssh pi@192.168.0.3 — 90×11 2017-02-06 01-32-30.png

[ToDo 6] シェルスクリプトで温度データをKinesis Firehoseにput-recordする(RaspberryPi)

シェルスクリプトで一定時間ごとに温度を読み出し、AWS CLIを叩いてKinesis Firehoseにput-recordします。JSONは["]をバックスラッシュでエスケープしなければならないことに注意が必要です。
本スクリプトはエラー処理を考慮していません。プロダクションの場合は、当然エラー処理を考慮する必要がありますので、fluentdなどのミドルウェアを使うとよいでしょう。

$ vim temp-firehose.sh
  • streamname=のあとの*********には、[ToDo 2]で設定したストリーム名を入力する
  • deviceid=のあとの28-************には、[ToDo 5]で控えた28-から始まるDS18B20のデバイスIDを入力する
temp-firehose.sh
#!/bin/bash

# 第1引数がデータの送信間隔となる(デフォルトは15秒)
if [ "$1" = "" ]
then
        interval=15
else
        interval=$1
fi

# Kinesis Firehoseストリーム名をstreamnameにセット
streamname=*********
# DS18B20のデバイスIDをdeviceidにセット
deviceid=28-************

while [ 1 ]
do
        (
                # 日時をtimeにセット
                time=`date '+%F %T'`
                # 温度を読み取り、tempにセット
                temp=$(awk -F= 'END {print $2/1000}' < /sys/bus/w1/devices/$deviceid/w1_slave)
                # 送信するJSON文字列を作る
                payload='{\"time\":\"'$time'\", \"temperature\":'$temp'}'
                # JSONを画面表示して、AWS CLIでput-recordする
                echo $payload
                aws firehose put-record --delivery-stream-name $streamname --record="{\"Data\":\"$payload\n\"}"
        ) &
        sleep $interval
done

シェルスクリプトを実行します。停止する場合はctrl + C。第1引数(下記では20)が送信間隔で、省略した場合は15秒間隔で送信されます。下記のようにJSONと"RecordId"のレスポンスが交互に表示されたら成功です。送信間隔によってはレスポンスは遅れて表示されることもあります。

$ sh temp-firehose.sh 20
{\"time\":\"2017-02-06 01:00:10\", \"temperature\":26.187}
{
    "RecordId": "sqyX8BaKpoWgWlYjJcPdxlp/sIxOIisujyk/cjbgnUO+CbigLVEhNk9UBL9VmmQL7QXtWu5c0Gi0yZF9hYi8rY5YBXoM9anaKsEowTHSfpjQSpx9/zink5b8WQvJYZtAazYolN7GfwQkWzCQHmTbGrUztmLwiZfECorKU4SCiHF0xbDkz5b+1W9F/ylabNHNbV8Fx4gEIJs45pX+LpWz7+zjsMHmZSun"
}
{\"time\":\"2017-02-06 01:00:30\", \"temperature\":26.062}
{
    "RecordId": "sHadXs3jJTUnu9FLwPfwZzVDautdy7Vdy7k7u9dt1tYLlgF5MA4aVTYiNTh6yOtKn8H+OwWOIS3WmJTIO70kA+3snWWWG+a0fHFwQEXkIZTlu1ybXbPfXPS9566mMRkA1nLAxiBQSsXr8Wpv9XO9S6e3tf5CJYwOMaKbVVCKpr+u+uKqueyZVioLAKQz1TrF11jMBxNUdApdyVK8LQFvWu9c3V+WDcPl"
}

[ToDo 7] S3バケットに温度データログファイルが書き込まれていることを確認する(AWS)

[ToDo 4]の手順でS3バケットにファイルが書き込まれていることを確認します。

S3 Management Console 2017-02-06 01-21-10.png
ファイルを開くとJSONが書き込まれているはずです。センサーを保冷剤で冷やすとこんな感じで温度が下がります。

iotan-f-2-2017-02-05-16-04-50-9ee9d802-867e-41bc-b222-6162ec3c2f18 2017-02-06 01-23-02.png

<後編>はこちら

(作成中)

続きを読む

[メモ]yum check-updateで"Could not contact any CDS load balancers:"

はじめに

ただのメモです。
ごくごく単純な話ですが、
どこかしらがどなたかしらの役に立てば幸いです。

実行環境

  • Amazon EC2
  • ap-northeast-1
  • RHEL-7.2_HVM_GA-20151112-x86_64-1-Hourly2-GP2 (ami-0dd8f963)
  • パブリックサブネットに配置
  • 実行ユーザ:ec2-user
  • インターネットへのアウトバウンド制限なし

実行結果1

失敗します。

[ec2-user@ip-172-31-26-175 ~]$ yum check-update
Loaded plugins: amazon-id, rhui-lb, search-disabled-repos
Repo rhui-REGION-client-config-server-7 forced skip_if_unavailable=True due to: /etc/pki/rhui/cdn.redhat.com-chain.crt
Repo rhui-REGION-client-config-server-7 forced skip_if_unavailable=True due to: /etc/pki/rhui/product/rhui-client-config-server-7.crt
Repo rhui-REGION-client-config-server-7 forced skip_if_unavailable=True due to: /etc/pki/rhui/rhui-client-config-server-7.key
Repo rhui-REGION-rhel-server-releases forced skip_if_unavailable=True due to: /etc/pki/rhui/cdn.redhat.com-chain.crt
Repo rhui-REGION-rhel-server-releases forced skip_if_unavailable=True due to: /etc/pki/rhui/product/content-rhel7.crt
Repo rhui-REGION-rhel-server-releases forced skip_if_unavailable=True due to: /etc/pki/rhui/content-rhel7.key
Repo rhui-REGION-rhel-server-rh-common forced skip_if_unavailable=True due to: /etc/pki/rhui/cdn.redhat.com-chain.crt
Repo rhui-REGION-rhel-server-rh-common forced skip_if_unavailable=True due to: /etc/pki/rhui/product/content-rhel7.crt
Repo rhui-REGION-rhel-server-rh-common forced skip_if_unavailable=True due to: /etc/pki/rhui/content-rhel7.key
Could not contact CDS load balancer rhui2-cds01.ap-northeast-1.aws.ce.redhat.com, trying others.


Could not contact any CDS load balancers: rhui2-cds01.ap-northeast-1.aws.ce.redhat.com, rhui2-cds02.ap-northeast-1.aws.ce.redhat.com.

エラーでググる

下記のサイト樣あたりを確認。
レポジトリ周りの設定が関係してくるのか。
と思いつつも、私のケースでは関係なかった。

・yumが使えない・・・!?
http://qiita.com/gooska/items/10b23810e59df213da78
・RHEL7 on EC2 のyum upadteエラー
http://qiita.com/kanemotsu/items/d634a3416807f832fadc
・RHUI クライアントシステムで yum コマンドを実行すると失敗し、Could not contact any CDS load balancers: エラーが発生する
https://access.redhat.com/ja/node/1573453

実行結果2

ただ単にsudoをつけていないだけでした。
成功します。

[ec2-user@ip-172-31-26-175 ~]$ sudo yum check-update
Loaded plugins: amazon-id, rhui-lb, search-disabled-repos

NetworkManager.x86_64                                                  1:1.4.0-14.el7_3                                        rhui-REGION-rhel-server-releases
NetworkManager-config-server.x86_64                                    1:1.4.0-14.el7_3                                        rhui-REGION-rhel-server-releases
NetworkManager-libnm.x86_64                                            1:1.4.0-14.el7_3                                        rhui-REGION-rhel-server-releases
NetworkManager-team.x86_64                                             1:1.4.0-14.el7_3                                        rhui-REGION-rhel-server-releases
NetworkManager-tui.x86_64                                              1:1.4.0-14.el7_3                                        rhui-REGION-rhel-server-releases
bash.x86_64                                                            4.2.46-21.el7_3                                         rhui-REGION-rhel-server-releases
bind-libs-lite.x86_64                                                  32:9.9.4-38.el7_3.1                                     rhui-REGION-rhel-server-releases
bind-license.noarch                                                    32:9.9.4-38.el7_3.1                                     rhui-REGION-rhel-server-releases
chrony.x86_64                                                          2.1.1-4.el7_3                                           rhui-REGION-rhel-server-releases
device-mapper.x86_64                                                   7:1.02.135-1.el7_3.2                                    rhui-REGION-rhel-server-releases
device-mapper-libs.x86_64                                              7:1.02.135-1.el7_3.2                                    rhui-REGION-rhel-server-releases
expat.x86_64                                                           2.1.0-10.el7_3                                          rhui-REGION-rhel-server-releases
glibc.x86_64                                                           2.17-157.el7_3.1                                        rhui-REGION-rhel-server-releases
glibc-common.x86_64                                                    2.17-157.el7_3.1                                        rhui-REGION-rhel-server-releases
kernel.x86_64                                                          3.10.0-514.6.1.el7                                      rhui-REGION-rhel-server-releases
kernel-tools.x86_64                                                    3.10.0-514.6.1.el7                                      rhui-REGION-rhel-server-releases
kernel-tools-libs.x86_64                                               3.10.0-514.6.1.el7                                      rhui-REGION-rhel-server-releases
kpartx.x86_64                                                          0.4.9-99.el7_3.1                                        rhui-REGION-rhel-server-releases
krb5-libs.x86_64                                                       1.14.1-27.el7_3                                         rhui-REGION-rhel-server-releases
libgudev1.x86_64                                                       219-30.el7_3.6                                          rhui-REGION-rhel-server-releases
libnl3.x86_64                                                          3.2.28-3.el7_3                                          rhui-REGION-rhel-server-releases
libnl3-cli.x86_64                                                      3.2.28-3.el7_3                                          rhui-REGION-rhel-server-releases
libsemanage.x86_64                                                     2.5-5.1.el7_3                                           rhui-REGION-rhel-server-releases
libsemanage-python.x86_64                                              2.5-5.1.el7_3                                           rhui-REGION-rhel-server-releases
microcode_ctl.x86_64                                                   2:2.1-16.1.el7_3                                        rhui-REGION-rhel-server-releases
nss.x86_64                                                             3.21.3-2.el7_3                                          rhui-REGION-rhel-server-releases
nss-sysinit.x86_64                                                     3.21.3-2.el7_3                                          rhui-REGION-rhel-server-releases
nss-tools.x86_64                                                       3.21.3-2.el7_3                                          rhui-REGION-rhel-server-releases
nss-util.x86_64                                                        3.21.3-1.1.el7_3                                        rhui-REGION-rhel-server-releases
openssh.x86_64                                                         6.6.1p1-33.el7_3                                        rhui-REGION-rhel-server-releases
openssh-clients.x86_64                                                 6.6.1p1-33.el7_3                                        rhui-REGION-rhel-server-releases
openssh-server.x86_64                                                  6.6.1p1-33.el7_3                                        rhui-REGION-rhel-server-releases
policycoreutils.x86_64                                                 2.5-11.el7_3                                            rhui-REGION-rhel-server-releases
policycoreutils-python.x86_64                                          2.5-11.el7_3                                            rhui-REGION-rhel-server-releases
python-perf.x86_64                                                     3.10.0-514.6.1.el7                                      rhui-REGION-rhel-server-releases
selinux-policy.noarch                                                  3.13.1-102.el7_3.13                                     rhui-REGION-rhel-server-releases
selinux-policy-targeted.noarch                                         3.13.1-102.el7_3.13                                     rhui-REGION-rhel-server-releases
sudo.x86_64                                                            1.8.6p7-21.el7_3                                        rhui-REGION-rhel-server-releases
systemd.x86_64                                                         219-30.el7_3.6                                          rhui-REGION-rhel-server-releases
systemd-libs.x86_64                                                    219-30.el7_3.6                                          rhui-REGION-rhel-server-releases
systemd-sysv.x86_64                                                    219-30.el7_3.6                                          rhui-REGION-rhel-server-releases
tuned.noarch                                                           2.7.1-3.el7_3.1                                         rhui-REGION-rhel-server-releases
tzdata.noarch                                                          2016j-1.el7                                             rhui-REGION-rhel-server-releases
vim-minimal.x86_64                                                     2:7.4.160-1.el7_3.1                                     rhui-REGION-rhel-server-releases
xfsprogs.x86_64                                                        4.5.0-9.el7_3                                           rhui-REGION-rhel-server-releases
[ec2-user@ip-172-31-26-175 ~]$

実行結果3

上記で成功を確認後、セキュリティグループをHTTP(80)のアウトバウンドのみ許可するように編集します。
失敗します。

[ec2-user@ip-172-31-26-175 ~]$ sudo yum check-update
Loaded plugins: amazon-id, rhui-lb, search-disabled-repos
Could not contact CDS load balancer rhui2-cds01.ap-northeast-1.aws.ce.redhat.com, trying others.


Could not contact any CDS load balancers: rhui2-cds01.ap-northeast-1.aws.ce.redhat.com, rhui2-cds02.ap-northeast-1.aws.ce.redhat.com.

実行結果4

セキュリティグループをHTTPS(443)のアウトバウンドのみ許可するように編集します。
成功します。

(実行結果2と同一のため省略)

おわりに

初回の失敗はsudoしていないというごく単純なものでした。
それだけだとあまりにも何なので、アウトバウンドを制限した時の挙動も確認しました。

「EC2インスタンスからHTTPSでアウトバウンドできるかの確認」を実施したい場合、WindowsだったらIEでGoogleのトップページでも開いて確認しちゃいますが、Linuxの場合どうしよう、なんて思っておりました。条件にもよるでしょうが、yum check-updateで代替とできるかな、と考えております。

以上です。

蛇足:rpmパッケージの命名規則

<名前>-<バージョン>-<リリース>-<アーキテクチャ>.rpm
(例)tree-1.5.3-3.el6.x86_64.rpm

(参考)
http://uxmilk.jp/10240

続きを読む