2015年11月19日木曜日

IoTpack(SORACOM)でProxy(Squid)


"cloudpack"には"SORACOM"さんのサービスを利用した「IoTpack」という商品があります。


この商品は簡単にいえば、SORACOMのVPCと自分のVPCを"VPC Peering"で接続し、
"SORACOM Air"のSIMを利用したデバイスからの通信を、インターネットに出ることなく
自分のVPC上のサーバで処理することができるものとなっています。


ということで下記のようにIoTpackを利用して"Air SIM"のiPhoneのHTTP(S)通信が
自分のVPC上のProxyサーバ(Squid)経由でインターネットに出て行くようにしてみました。


EC2上には下記のようにSquidを用意しておきます。
("no_cache deny all"でキャッシュしないようにしています)
$ sudo yum -y install squid
...

$ sudo cat /etc/squid/squid.conf
http_port         3128
visible_hostname  cloudpack
http_access allow all
no_cache deny     all

$ sudo service squid start
Starting squid:                                            [  OK  ]

環境はIoTpackとしてもらったので、
いきなりiPhoneからの上記のProxy用のEC2の"Private IP"にPingできてました。


さらに上述のProxyサーバを利用するには、
iPhoneのプロファイルを調整する必要があります。

プロファイルは下記のツールで「構成プロファイル」として作成することができます。
iPhone 構成ユーティリティ 2.2 (Mac)
今回の構成プロファイルは次のように作成しています。



「アクセスポイント」に関してはSORACOMの情報を入力し、
さらに「プロキシサーバとポート」で上記のProxyサーバの情報を入れています。

このプロファイルはMacにiPhoneをUSBでつなぎ、
上記ツールからインストールすることができます。(既存のプロファイルは削除しています)


この状態で、iPhoneからSafariで適当なURL(http://cloudpack.jp)にアクセスすると、
Squidのログより、iPhoneのHTTP(S)の通信がProxy経由になっていることが確認できます。
1447883093.478    173 100.64.128.67 TCP_MISS/200 34431 GET http://cloudpack.jp/ - DIRECT/54.239.194.249 text/html
1447883093.622      9 xxx.xxx.xxx.xxx TCP_MISS/200 2037 GET http://cloudpack.jp/js/jquery.browser.min.js - DIRECT/54.239.194.249 application/x-javascript
1447883093.630     17 xxx.xxx.xxx.xxx TCP_MISS/200 19145 GET http://cloudpack.jp/css/common/layout.css - DIRECT/54.239.194.249 text/css
1447883093.631     18 xxx.xxx.xxx.xxx TCP_MISS/200 16280 GET http://cloudpack.jp/css/index.css - DIRECT/54.239.194.249 text/css
1447883093.634     13 xxx.xxx.xxx.xxx TCP_MISS/200 4054 GET http://cloudpack.jp/js/jquery.easing-1.3.min.js - DIRECT/54.239.194.249 application/x-javascript
1447883093.634     17 xxx.xxx.xxx.xxx TCP_MISS/200 22487 GET http://cloudpack.jp/js/common.js - DIRECT/54.239.194.249 application/x-javascript
1447883093.665     52 xxx.xxx.xxx.xxx TCP_MISS/200 34120 GET http://cloudpack.jp/css/common/general.css - DIRECT/54.239.194.249 text/css
...

さらに、ユーザー認証(Basic)もかけてみます。Squidの設定は下記の通りです。
$ sudo cat /etc/squid/squid.conf
http_port         3128
visible_hostname  cloudpack
no_cache deny     all
auth_param basic  program /usr/lib64/squid/ncsa_auth /etc/squid/squid.htpasswd
auth_param basic  children 5
auth_param basic  credentialsttl 1 hours
acl               password proxy_auth REQUIRED
http_access allow password
http_access deny  all

$ sudo service squid restart
Stopping squid: ................                           [  OK  ]
Starting squid: .                                          [  OK  ]

ちゃんと認証画面も表示されました。


ログを見ると、今度は、そのユーザーがどこに接続したかまで、わかるようになります。
1447890752.703     13 xxx.xxx.xxx.xxx TCP_MISS/200 601 GET http://cloudpack.jp/img/index/visual_bg.gif suzuki DIRECT/54.239.194.50 image/gif
1447890752.717     12 xxx.xxx.xxx.xxx TCP_MISS/200 651 GET http://cloudpack.jp/img/common/bg_01.png suzuki DIRECT/54.239.194.50 image/png
1447890752.730     10 xxx.xxx.xxx.xxx TCP_MISS/200 2454 GET http://cloudpack.jp/img/index/btn_bg.gif suzuki DIRECT/54.239.194.50 image/gif
1447890752.730      9 xxx.xxx.xxx.xxx TCP_MISS/200 816 GET http://cloudpack.jp/img/common/line02.gif suzuki DIRECT/54.239.194.50 image/gif
1447890752.739     14 xxx.xxx.xxx.xxx TCP_MISS/200 1858 GET http://cloudpack.jp/img/index/attention_bg.gif suzuki DIRECT/54.239.194.50 image/gif
...

今回は典型的な例としてProxyを試してみましたが、
このようにiPhoneからの(SIMでの)通信も閉域網内の通信として扱え、
いろいろと(社内サーバで処理)できそうなことがわかりました。

夢が広がります。

2015年11月7日土曜日

本ブログのLambda関係の記事をまとめてみた


"AWS re:Invent 2015"の下記Lambda関係の発表を機に書きだしましたが、
【AWS発表】AWS Lambdaのアップデート – Python, VPC, 実行時間の延長, スケジュールなど
何を書いたか、自分でもわからなくなってきたので、一旦まとめてみました。
Cognitoがない...

2015年11月6日金曜日

Lambda(Python)からKinesisにPut(API)してLambda(Python)でGet(Event)する


こんな感じです。


API(Boto3)でPutするのは、まあ当たり前ですが、Getに関しては、
Lambdaの"event source"にKinesisというものがあり、それを設定することにより
イベントドリブンでKinesisのデータをLambdaで処理することができます。

ということで、作ってみます。

Kinesisの作成


AWSマネジメントコンソールより下記のように作成します。


IAMの設定


Lambdaに設定するIAMロール(lambda_basic_exection)にKinesisが扱える
マネージドポリシー(AmazonKinesisFullAccess)をアタッチします。

 

KinesisへPutするLambdaファンクション


コードは、こんな感じです。
import logging
import boto3

def lambda_handler(event, context):

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

    try:
        logger.info(event)
        response = boto3.client('kinesis').put_record(
            StreamName = "test",
            Data = "test",
            PartitionKey = "test",
        )
        logger.info(response)
        return response

    except Exception as e:
        logger.error(e)
        raise e

実行すると、レスポンスは次のように返ってきます。
{
  "ShardId": "shardId-000000000000",
  "ResponseMetadata": {
    "HTTPStatusCode": 200,
    "RequestId": "eef4ccde-840d-11e5-9d26-6b863e58e437"
  },
  "SequenceNumber": "49556079134761214959115455796946952254907110183804076034"
}

Kinesisのモニタリングで確認しても、
ちゃんとPutレコードがカウントされていることがわかります。


KinesisからGetするLambdaファンクション


コードは、こんな感じです。
import logging
import base64

def lambda_handler(event, context):

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

    try:

        logger.info(event)

        payloads = []
        for record in event['Records']:
            payload = base64.b64decode(record["kinesis"]["data"])
            payloads.append(payload)
        logger.info(payloads)

        return payloads

    except Exception as e:
        logger.error(e)
        raise e

そして、Event Sourceとして、Kinesisを追加します。


CloudWatch Logsを確認すると、ちゃんとKinesisのEvent Sourceを設定した、
Lambdaファンクションが実行されログが出力されていることがわかります。


CloudWatchのMetricsでもGetリクエストで確認することができます。


DynamoDBのテーブルからランダムにレコードを取得する(全件とってからの処理なので良い実装ではないけど...)


全件とってからの処理なので良い実装ではないけど、場合によっては使えるので、
TIPSを貯める目的として...

DynamoDBのテーブルは、こんなシンプルな感じで、

そこからランダムな100件のリストを作成したいと思います。
要件は下記とします。
  • レコード数はそれほど多くない(全件取得してもLambda内で処理できる)
  • レコード数が100件未満の場合もあるため、リストの値は重複OK
で、Python(Lambda)で楽にできないかなー、とググってたら、いい記事見つけました。
Python Tips:リストの中から要素をランダムにピックアップしたい
"random.choice(リスト)"っていうのを使うと、リスト中の値から、
ランダムに一件を取得できるようです。

ということで、コード(Lambda)は下記のとおりです。
(余計な処理も入ってますが...)
import logging
import random
import boto3

image_url = "http://xxxxxxxxxxxxxx.cloudfront.net/"

def lambda_handler(event, context):

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

    try:

        logger.info(event)
        table = boto3.resource("dynamodb").Table("image")

        response = table.scan()
        logger.info(response)

        items = []
        if response["Count"] != 0:
            for i in range(0, 100):
                id = random.choice(response["Items"])["id"]
                items.append({ "id": id, "url": image_url + id + ".png"})
        logger.info(items)

        return items

    except Exception as e:
        logger.error(e)
        raise e

上記を実行すると、次のようなDynamoDBのテーブルのレコードから、
100件ランダムに取得した(重複あり)リストを作成することができました。
[
    {
        "url": "http://xxxxxxxxxxxxxx.cloudfront.net/fa4ee1f2838b11e59f9436cb5329c05c.png",
        "id": "fa4ee1f2838b11e59f9436cb5329c05c"
    },
    {
        "url": "http://xxxxxxxxxxxxxx.cloudfront.net/436f0c8c838b11e59f9436cb5329c05c.png",
        "id": "436f0c8c838b11e59f9436cb5329c05c"
    },
    ...
]

2015年11月2日月曜日

"Amazon Linux"にRundeckをインストール


下記を見ればインストール方法は、だいたいわかると思います。
http://rundeck.org/downloads.html

そして、実際にインストールしてみます。

まずはJavaが利用できることの確認です。
$ java -version
java version "1.7.0_75"
OpenJDK Runtime Environment (amzn-2.5.4.0.53.amzn1-x86_64 u75-b13)
OpenJDK 64-Bit Server VM (build 24.75-b04, mixed mode)

次にRPMでYumのリポジトリ(Rundeck用)をインストールします。
$ sudo rpm -Uvh http://repo.rundeck.org/latest.rpm
http://repo.rundeck.org/latest.rpm を取得中
警告: /var/tmp/rpm-tmp.dQklaS: ヘッダー V4 RSA/SHA1 Signature、鍵 ID e2d1065b: NOKEY
準備しています...              ################################# [100%]
更新中 / インストール中...
   1:rundeck-repo-4-0                 ################################# [100%]

最後にYumでRundeckをインストールします。
$ sudo yum install rundeck
...
インストール:
  rundeck.noarch 0:2.6.1-1.12.GA

依存性関連をインストールしました:
  rundeck-config.noarch 0:2.6.1-1.12.GA

完了しました!

これで、インストールは終わりです。

次は設定ファイルを調整してプラウザでRundeckの管理ページに
アクセスできるようにします。

調整するといっても、下記のファイルの、
$ cat /etc/rundeck/rundeck-config.properties
#loglevel.default is the default log level for jobs: ERROR,WARN,INFO,VERBOSE,DEBUG
loglevel.default=INFO
rdeck.base=/var/lib/rundeck

#rss.enabled if set to true enables RSS feeds that are public (non-authenticated)
rss.enabled=false
# change hostname here
grails.serverURL=http://localhost:4440
dataSource.dbCreate = update
dataSource.url = jdbc:h2:file:/var/lib/rundeck/data/rundeckdb;MVCC=true;TRACE_LEVEL_FILE=4
この部分を
grails.serverURL=http://localhost:4440
次のように
grails.serverURL=http://xxx.xxx.xxx.xxx:4440
実際にアクセスするIPアドレスに変更するだけです。

そして最後に"rundeckd"サービスを起動します。
$ sudo service rundeckd start
Starting rundeckd:                                         [  OK  ]

すると下記のように管理画面にアクセスでき(当然セキュリティグループは調整済み)


「Username: admin / Password: admin」でログインすることができます。


LambdaのVPNサポートがきたら、必要なくなるケースが増えるかも...
【AWS発表】AWS Lambdaのアップデート – Python, VPC, 実行時間の延長, スケジュールなど

Lambdaの"API endpoint"を作成してみた


下記ではAPI Gatewayからバックエンドの設定にLambdaを指定して、
LambdaをHTTP(S)から実行できるようにしました。
"API Gateway"のバックエンドを"Lambda"にしてJSONデータをエコーさせる
しかし、Lambda側でも"API endpoint"というものを作成して、
HTTP(S)から実行できるようにできます。

といっても結局API Gatewayと連携させてるだけなので、結果としては上述した方法と
同じことをしていることになります。

具体的な方法は下記となります。


"API endpoint type"に"API Gateway"を選択します。(それしかありませんが...)
("Security"はデフォルトがIAMでしたが、要件的にOpenにしています)


作成すると"API endpoint URL"(API Gateway)が発行されます。


実際に、API Gatewayの方でも確認してみると、上記で作成したものが表示されています。


ちなみにLambda側の"API endpoint"を削除しても、API Gateway側は残ってました。

で、じゃあ、どっちの方法で行うべきか?って話になると思いますが、
自分的には、Lambdaの"API endpoint"の方で行なった方がいいかなー、と思ってます。

なぜなら、API Gatewayの方で行うと、Lambdaの"API endpoint"には何も表示されず、
そのLambdaがどこから実行されるかが、わかりにくくなってしまうからです。

まあ、API GatewayのリソースがLambdaごとに必ず別れてしまうっていデメリット(?)
もありますが...