AtlassianのLocalStackを使ってみてなんとなく理解するまでのお話

Atlassianが「LocalStack」なんてとても便利そうなものを出していたけど、なかなか使い方を解説しているページが見つからなかったので、とりあえず使いながらなんとなく中身を理解するまでのお話。

https://github.com/atlassian/localstack
スクリーンショット 2017-04-23 17.53.59.png

起動

いくつかGithubで利用方法が紹介されていますが、今回はdockerでの利用をしてみます。

$ docker run -it -p 4567-4578:4567-4578 -p 8080:8080 atlassianlabs/localstack
2017-04-23 08:50:15,876 INFO supervisord started with pid 1
2017-04-23 08:50:16,879 INFO spawned: 'dashboard' with pid 7
2017-04-23 08:50:16,885 INFO spawned: 'infra' with pid 8
(. .venv/bin/activate; bin/localstack web --port=8080)
. .venv/bin/activate; exec localstack/mock/infra.py
Starting local dev environment. CTRL-C to quit.
 * Running on http://0.0.0.0:8080/ (Press CTRL+C to quit)
 * Restarting with stat
Starting local Elasticsearch (port 4571)...
Starting mock ES service (port 4578)...
Starting mock S3 server (port 4572)...
Starting mock SNS server (port 4575)...
Starting mock SQS server (port 4576)...
Starting mock API Gateway (port 4567)...
Starting mock DynamoDB (port 4569)...
Starting mock DynamoDB Streams (port 4570)...
Starting mock Firehose (port 4573)...
Starting mock Lambda (port 4574)...
Starting mock Kinesis (port 4568)...
Starting mock Redshift server (port 4577)...
 * Debugger is active!
2017-04-23 08:50:18,537 INFO success: dashboard entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
2017-04-23 08:50:18,538 INFO success: infra entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
 * Debugger PIN: 844-652-544
Ready.

とりあえず起動はしたみたい。で、http://localhost:8080/にアクセスしてみたけど、こんな感じで何も表示されず。

スクリーンショット 2017-04-23 17.59.04.png

使ってみてわかりましたが、要は本当に各種サービスが以下のアドレスで利用できるようにしてくれたもののようです。

全部試すのもアレなので、とりあえず馴染み深いDynamoDBとS3を使ってみる。

DynamoDBのテーブル作成

全然関係ないですが、http://localhost:4569/にアクセスすると以下のレスポンスをもらえます。デフォルトはus-east-1で動いている想定のようで。(Githubページにも書いてあった。バインドすればいくつか設定を変更できるようですね。)

healthy: dynamodb.us-east-1.amazonaws.com 

では早速テーブルをCLIから作ってみます。一応作成前にlist-tablesをしてみますが、もちろん何も登録されていません。

$ aws --endpoint-url=http://localhost:4569 dynamodb list-tables
{
    "TableNames": []
}

こちらを参考にさせていただいて、create-tableコマンドを発行します。

$ aws --endpoint-url=http://localhost:4569 dynamodb create-table --table-name test --attribute-definitions AttributeName=testId,AttributeType=S --key-schema AttributeName=testId,KeyType=HASH --provisioned-throughput ReadCapacityUnits=1,WriteCapacityUnits=1
{
    "TableDescription": {
        "TableArn": "arn:aws:dynamodb:us-east-1:000000000000:table/test", 
        "AttributeDefinitions": [
            {
                "AttributeName": "testId", 
                "AttributeType": "S"
            }
        ], 
        "ProvisionedThroughput": {
            "NumberOfDecreasesToday": 0, 
            "WriteCapacityUnits": 1, 
            "ReadCapacityUnits": 1
        }, 
        "TableSizeBytes": 0, 
        "TableName": "test", 
        "TableStatus": "CREATING", 
        "KeySchema": [
            {
                "KeyType": "HASH", 
                "AttributeName": "testId"
            }
        ], 
        "ItemCount": 0, 
        "CreationDateTime": 1492937089.534
    }
}

なんか作られたっぽいですね。もう一度list-tablesをしてみます。

$ aws --endpoint-url=http://localhost:4569 dynamodb list-tables
{
    "TableNames": [
        "test"
    ]
}

出来上がってますね。なるほど、本当にAWS上でやる操作をEndpointURLを変更するだけで操作できてしまうようです。これは思っていたよりも便利かも。

S3のバケットを作成してみる

S3のバケットも作成できるのか試してみます。まずバケットのリストを見てみますが、当然何もありません。

$ aws --endpoint-url=http://localhost:4572 s3 ls
(何も表示されず)

では作成してみます。

$ aws --endpoint-url=http://localhost:4572 s3 mb s3://kojiisd-test/
make_bucket: s3://kojiisd-test/
$ aws --endpoint-url=http://localhost:4572 s3 ls
2006-02-04 01:45:09 kojiisd-test

まじか、これは便利。ただdockerでサービス起動したら停止時に中身が消えてしまうから、できれば作成したものが残るような起動方法の方が色々試そうとしたら適していそうですね。
(作成時間がはちゃめちゃですが、とりあえずそこまで問題にはならないかな)

ちなみにDynamoDBのテーブルとS3のバケットを作成してから気づきましたが、http://localhost:8080/にアクセスしたら、作成したものが表示されていました。なるほど、そのためのDashBoardだったのか。素敵。

スクリーンショット 2017-04-23 18.20.02.png

まとめ

どれくらいどこまで何ができるのかは気になりますが、一般的なことであればだいたいローカルでできるような気がします。しかもEndpointURLを変更するだけで良さそうなので、これはかなり便利かも。今度作成したアプリを全てLocalStackで動かしてみるとかやってみようかな。

とりあえず、もっとこれは知られるべきと、思いました。

続きを読む

AMLのシステム構成

機械学習に関する基本的な内容をまとめてみたものです。機械学習に関する、Web上にすでにある解説コンテンツをまとめたサイトの抜粋です。
AMLのシステム構成

完全マネージドの機械学習サービス

Amazon Machine Learning(AML)は、機械学習を行うためのインフラストラクチャやワークフローなどが不要の完全マネージド型機械学習サービスです。

そのため、
・ハードウェアのプロビジョニング
・コンピュータの負荷の分散やスケーリング
・リソース間の依存関係の管理
などに配慮する必要はありません。

ユーザーは使用するデータを用意するだけで、機械学習を行う事が出来ます。

システム構成

Amazon Machine Learningを使った機械学習システムは、
・データソースなどの入出力をするために使うS3、RedShift、RDS
・学習モデルの構築を構築して予測を行うAmazon Machine Learning
から構成されます。
シンプルなシステム構成となっているので、手軽に利用する事が出来ます。

複数の予測を一度に行うバッチ予測をするために、AWS APIを使ったクライアントアプリケーションを利用する事も可能です。

可用性の高いシステム構成

・モデルのトレーニング、評価、予測などは、Amazonのデータセンターで実行
・AWSのリージョン別に、サービススタックレプリケーションが複数の施設に分散
・メンテナンスなどによるダウンタイムはない

などの工夫がされています。

そのため、サーバーの障害やアベイラビリティーゾーンの機能停止に対しても、万全の対策が施されています。

続きを読む

Amazon Machine Learning

機械学習に関する基本的な内容をまとめてみたものです。機械学習に関する、Web上にすでにある解説コンテンツをまとめたサイトの抜粋です。
Amazon Machine Learning

Amazon Machine Learningとは

Amazon Machine Learningとは、機械学習に関する技術やアルゴリズムを十分に理解していない人でも、簡単に利用出来るサービスです。

Amazon Machine Learningは、
・エンドツーエンドのモデル作成ツール
・モニタリングツール
などの開発環境が全て提供されるマネージドサービスなので、訓練用のデータなどの用意が整っていれば、直ぐに機械学習モデルを構築する事が出来ます。

機械学習システムの構築と維持するために、労力と費用を使う必要がなくなります。

使いやすいAPIが用意されている

Amazon Machine Learningは利用可能なアルゴリズムが限定されているものの、モデル可視化ツールやインタラクティブなデータの遣り取りなどを使う事で、機械学習モデルの構築と調整を効率的に行えます。

構築した学習モデルを使って予測をする場合も、Amazon Machine Learningの予測APIを使うと、即座に予測結果を取得する事が可能です。

APIは、
・多数の予測を一度にリクエスト出来るバッチ予測API
・個別の予測を取得出来るリアルタイムAPI
の2種類が用意されていますので、用途に合った使い方が出来ます。

低コストで機械学習モデルの構築が可能

低コストで機械学習モデルを構築出来る事も、Amazon Machine Learningの特徴です。

・初期費用は不要
・従量制の料金体系
なので、用途に合わせた機械学習モデルを構築出来ますし、必要に応じてスケールを拡大したり縮小する事も簡単です。

続きを読む

簡単に仮想のS3を作成してAWSLambdaとS3サービスの連携をローカル環境でテストする

事前準備

実行する前にEclise用のAWSツールキットを導入しておいてください。導入手順は以下のリンクにご参考をお願いします。

ー>AWS Toolkit導入手順

導入完成したらAWSプロジェクトがプロジェクト新規画面で出てくるはずです。

mavenは導入済みの前提です。

Screenshot from 2017-04-20 17-13-55.png

Lambdaファンクションを書く

  1. まずはAWS Lambda Java ファンクションのプロジェクト作成する.

Screenshot from 2017-04-20 17-19-41.png
- プロジェクト名 :S3EventTutorial
- パッケージ名:com.amazonaws.lambda.s3tutorial
以上のように必須な情報をいれておきまましょう。「完了」を押したらプロジェクトは作成されて一般的なプロジェクトフォルダは以下のようにみれます。
Screenshot from 2017-04-20 17-25-12.png

  1. S3をモックするように「s3mock_2.11」というライブラリをMavenで導入する。pomファイルに依存ライブラリを定義するだけでいいですので下のpomファイルを参考にして自分が作成したプロジェクトのpomを作成してみてください。
<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.amazonaws.lambda</groupId>
    <artifactId>s3tutorial</artifactId>
    <version>4.0.0</version>
    <dependencies>
        <dependency>
            <groupId>com.amazonaws</groupId>
            <artifactId>aws-lambda-java-core</artifactId>
            <version>1.1.0</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>com.amazonaws</groupId>
            <artifactId>aws-lambda-java-events</artifactId>
            <version>1.3.0</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.amazonaws</groupId>
            <artifactId>aws-java-sdk</artifactId>
            <version>1.11.119</version>
            <scope>compile</scope>
        </dependency>

        <!-- https://mvnrepository.com/artifact/com.typesafe.akka/akka-http-experimental_2.11 -->
        <dependency>
            <groupId>com.typesafe.akka</groupId>
            <artifactId>akka-http-experimental_2.11</artifactId>
            <version>2.4.11.1</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/com.typesafe.scala-logging/scala-logging_2.11 -->
        <dependency>
            <groupId>com.typesafe.scala-logging</groupId>
            <artifactId>scala-logging_2.11</artifactId>
            <version>3.5.0</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/io.findify/s3mock_2.11 -->
        <dependency>
            <groupId>io.findify</groupId>
            <artifactId>s3mock_2.11</artifactId>
            <version>0.1.10</version>
            <scope>test</scope>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.mockito/mockito-core -->
        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-core</artifactId>
            <version>2.7.22</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.github.tomakehurst/wiremock -->
        <dependency>
            <groupId>com.github.tomakehurst</groupId>
            <artifactId>wiremock</artifactId>
            <version>2.6.0</version>
        </dependency>


    </dependencies>
</project>

ロカールにおいてあるmavenのリポジトリーにない依存ライブラリがあるかもしれないので一応プロジェクトのrootフォルダで「mvn package」をコマンドラインとして実行してみましう。そしてmavenはpomに定義されていたdependencyをダウンロードしてくれます。

  1. Lambdaファンクションのロジック
    作成してもらったLambdaFunctionHandler.javaを開いてロジックをかいてみましょう。アイデアは凄っく簡単です。

S3からファイルがアップロードされたというイベントがこられたら、イベントの内容を見てアップロードされたファイルをゲットしてコンソールでそのファイルを書き出すという作業です。コードみてみたらすぐ分かると思いますので説明しないですむ。


public class LambdaFunctionHandler implements RequestHandler<S3Event, Object> {

    private AmazonS3 s3Client;

    public LambdaFunctionHandler(AmazonS3 s3Client){
        this.s3Client = s3Client;
    }
    public LambdaFunctionHandler(){
        this.s3Client =  new AmazonS3Client(new ProfileCredentialsProvider());
    }

    private static void storeObject(InputStream input) throws IOException {
        // Read one text line at a time and display.
        BufferedReader reader = new BufferedReader(new InputStreamReader(input));
        while (true) {
            String line = reader.readLine();
            if (line == null)
                break;
            System.out.println("    " + line);
        }
        System.out.println();
    }

    @Override
    public Object handleRequest(S3Event input, Context context) {
        context.getLogger().log("Input: " + input);

        // Simply return the name of the bucket in request
        LambdaLogger lambdaLogger = context.getLogger();
        S3EventNotificationRecord record = input.getRecords().get(0);
        lambdaLogger.log(record.getEventName()); // イベント名

        String bucketName = record.getS3().getBucket().getName();
        String key = record.getS3().getObject().getKey();
        /*
         * Get file to do further operation
         */
        try {
            lambdaLogger.log("Downloading an object");

            S3Object s3object = s3Client.getObject(new GetObjectRequest(bucketName, key));

            lambdaLogger.log("Content-Type: " + s3object.getObjectMetadata().getContentType());

            storeObject(s3object.getObjectContent());

            // Get a range of bytes from an object.

            GetObjectRequest rangeObjectRequest = new GetObjectRequest(bucketName, key);
            rangeObjectRequest.setRange(0, 10);
            S3Object objectPortion = s3Client.getObject(rangeObjectRequest);

            System.out.println("Printing bytes retrieved.");
            storeObject(objectPortion.getObjectContent());

        } catch (AmazonServiceException ase) {
            System.out.println("Caught an AmazonServiceException, which" + " means your request made it "
                    + "to Amazon S3, but was rejected with an error response" + " for some reason.");
            System.out.println("Error Message:    " + ase.getMessage());
            System.out.println("HTTP Status Code: " + ase.getStatusCode());
            System.out.println("AWS Error Code:   " + ase.getErrorCode());
            System.out.println("Error Type:       " + ase.getErrorType());
            System.out.println("Request ID:       " + ase.getRequestId());
        } catch (AmazonClientException ace) {
            System.out.println("Caught an AmazonClientException, which means" + " the client encountered "
                    + "an internal error while trying to " + "communicate with S3, "
                    + "such as not being able to access the network.");
            System.out.println("Error Message: " + ace.getMessage());
        }catch (IOException ioe){
            System.out.println("Caught an IOException, which means" + " the client encountered "
                    + "an internal error while trying to " + "save S3 object, "
                    + "such as not being able to access the network.");
            System.out.println("Error Message: " + ioe.getMessage());
        }
        return record.getS3().getObject().getKey();
    }

}


書いたコードに対してのテストケースを作成しましょう

今回は実装したLambdaコードを注目しますのでLambdaFunctionHandlerTestを開いてテストケース作成します。まずはテストケースのコードに目を通してみましょう。


    private static S3Event input;
    private static AmazonS3Client client;

    @BeforeClass
    public static void createInput() throws IOException {
        input = TestUtils.parse("s3-event.put.json", S3Event.class);

        S3Mock api = S3Mock.create(8999, "/tmp/s3");
        api.start();

        client = new AmazonS3Client(new AnonymousAWSCredentials());
        client.setRegion(Region.getRegion(Regions.AP_NORTHEAST_1));

        // use IP endpoint to override DNS-based bucket addressing
        client.setEndpoint("http://127.0.0.1:8999");

    }

    private Context createContext() {
        TestContext ctx = new TestContext();

        // TODO: customize your context here if needed.
        ctx.setFunctionName("Your Function Name");

        return ctx;
    }

    @Test
    public void testLambdaFunctionHandlerShouldReturnObjectKey() {

        client.createBucket(new CreateBucketRequest("newbucket", "ap-northeast-1"));
        ClassLoader classLoader = this.getClass().getClassLoader();
        File file = new File(classLoader.getResource("file/test.xml").getFile());
        client.putObject(new PutObjectRequest(
                                 "newbucket", "file/name", file));

        LambdaFunctionHandler handler = new LambdaFunctionHandler(client);
        Context ctx = createContext();

        Object output = handler.handleRequest(input, ctx);

        if (output != null) {
            assertEquals("file/name", output.toString());
            System.out.println(output.toString());
        }
    }

テストのため、createInput関数でS3Mockのインスタンスを作成して起動します。このインスタンスはローカル環境の8999番ポートにバイドしてリクエストを待ちます。それに「/temp/s3」というフォルダを作成しておいてS3サービスのストレージを真似する。

一番大事なのはtestLambdaFunctionHandlerShouldReturnObjectKeyという関数の内容です。見るの通り、以下の作業を実装します。
– 「testbucket」を作成する。注意:Regionを指定するのは必須です(Regionの内容は別になでもいいですがなかったらjava.lang.NoSuchMethodError: com.amazonaws.regions.RegionUtils.getRegionByEndpoint(Ljava/lang/String;)Lcom/amazonaws/regions/Region;というErrorが出てきます。これはAWSのバグです)
– プロジェクトのしたにあるresourceフォルダに作成したfile/test.xmlを仮ストレージにアップロードする
– アップロードしたファイルを仮S3からダウンロードして内容をチェックする。

トリガーは「s3-event.put.json」で定義されているイベントの内容なので「s3-event.put.json」の内容にアップロードされたファイルの情報を反映しなければなりません


{
  "Records": [
    {
      "eventVersion": "2.0",
      "eventSource": "aws:s3",
      "awsRegion": "us-east-1",
      "eventTime": "1970-01-01T00:00:00.000Z",
      "eventName": "ObjectCreated:Put",
      "userIdentity": {
        "principalId": "EXAMPLE"
      },
      "requestParameters": {
        "sourceIPAddress": "127.0.0.1"
      },
      "responseElements": {
        "x-amz-request-id": "C3D13FE58DE4C810",
        "x-amz-id-2": "FMyUVURIY8/IgAtTv8xRjskZQpcIZ9KG4V5Wp6S7S/JRWeUWerMUE5JgHvANOjpD"
      },
      "s3": {
        "s3SchemaVersion": "1.0",
        "configurationId": "testConfigRule",
        "bucket": {
          "name": "testbucket",
          "ownerIdentity": {
            "principalId": "EXAMPLE"
          },
          "arn": "arn:aws:s3:::mybucket"
        },
        "object": {
          "key": "file/name",
          "size": 1024,
          "eTag": "d41d8cd98f00b204e9800998ecf8427e"
        }
      }
    }
  ]
}

注意:bucket名とobjectのキーは一番大事です。見た内容の通りファイルはtestbuckにfile/nameというキーでアップロードされましたので応じてjsonの内容はそ言うことを表現される。

#終わり

ドラフトに説明しましたが不明なところがありましたらご相談をお願いします

続きを読む

Amazon Dash ButtonをHackするのはJust do itでしょ、と思って手を出したら落とし穴にどハマった件

Exective Summary

そもそもやりたかったこと:

Amazon Dash Buttonを押して、AWS上の全EC2を停止する。
(利用料が個人レベルでない上に、お小遣いが少ないもので…)

前提・制約:

(甲) 我が家にはWindows10マシンしかない(Linuxマシンはない、という意味)。
(乙) 一方、AWS Dash ButoonをHackするツールとしては、Linux/OS X向けNode.js系ツールが主流らしい([1],[3],[5],[9])。
⇒Amazon Dash Buttonは、ボタンを押すとARPリクエストを送出(ブロードキャスト)する。それを同じLANで待ち受けて検知できるのがDasher。その検知をトリガーにして任意の処理(WebAPIを呼び出す、等)を起動できる。
(丙) 我が家にはAWS Dash Buttonが2つある。それは、購入商品の指定/設定を、
  (A) 意図的に途中でやめてあるモノ
  (B) 無邪気に最期まで完了した(知らずに済ませてしまった)モノ
  の2種類だ。
(丁) 呼び出されたら「全EC2を停止する」というAPIは、AWS IoT/API Gateway/Lambdaのサーバレスアーキで作成済み。
⇒世間の情報も豊富だし、小1時間程度でできるっしょーとたかをくくる。

俺的に考え得た・試した選択肢

①Windows10+Dasher・・・×
②Windows10+Bash on Windows+Dasher・・・×
③Windows10+Cygwin+Dasher・・・×
④Windows10+win-node-dash-button・・・×
⑤Windows10+Vagrant+VirtualBox+Ubuntu+Dasher・・・○
⑥Windows10+Docker-Machine+VirtualBox+Boot2Docker+Dasher・・・×
⑦Windows10+Docker-Machine+VirtualBox+Boot2Docker+Docker+Ubuntu+Dasher・・・未

紆余曲折の結果が…コレだ…ワン・ツー・スリー…

⑤Windows10+Vagrant+VirtualBox+Ubuntu+Dasher

学べる(学べた)コト

・Git
・node.js/npm/node-gyp/node_pcap
・python/pip
・Bash on Windows/Cygwin/Ubuntu
・Docker-Machine/Vagrant/VirtualBox
・Wireshark/tcpdump
・NW/ARP/DNS

嵐の後の所感

やってみた結果として、Amazon Dash Buttonは幅広く技術を体験するのに良いテーマだったという実感。
(「フェルマーの最終定理」と同じ構図?定理の意味の理解はトリビアルだが、その証明を理解するには色々な数学的知識が必要!)
次は『Dash Button(丙)-(B)』のHackを試みる…(方向性としては『DNSサーバをポジティブに偽装する』)
…まあ、AWS IoT Buttonが日本でも発売されたら、それを使うけどね…

俺が落ちた落とし穴の各論

以下、詳細:(細々と続く)

参考情報

[1] Amazon Dash ButtonをただのIoTボタンとして使う
[2] node-dash-button@GitHub
[3] Amazon Dashボタンのハックにおける期待と落胆(Wi-Fi編)
[4] dasher@GitHub
[5] Amazon Dash Buttonを(正しくない方向で)使ってみた
[6] Dash Button for Node@GitHub
[7] node_pcap
[8] node_pcapのWindows7対応Folk
[9] win-node-dash-button
[10] Windowsでnpm installしてnode-gypでつまずいた時対処方法
[11] これはすごい!分解とパケット解析で分かったAmazon Dash Buttonの「本気度」
[12] これはすごい!Amazon Dash Buttonをプレゼンに使う
[13] これはすごい!Amazon Dash Buttonでツイートできた

続きを読む

インフラチームによくある極小サーバーやバッチジョブをECSで集約したい

すごい。この記事には文字しかない。

話の発端

ほとんどアクセスが無く、あっても昼間しかなく、しかし消すわけにはいかない社内サービスのようなデモサービスのようなサーバーが豪勢にもEC2単独インスタンスを持っていると、CPUを使わなすぎてリソースがもったいなかったり、たまったCPUクレジットを捨ててしまったりしてもったいない。日時バッチサーバーみたいなものも似たような状況で、バッチ稼働時以外は上と同じ無駄を今も発生させている。

では、全部Elastic Container Serviceでコンテナにしてインスタンスを集約して、昼間ためたクレジットを夜中の日時バッチで使うのはどうか、という手を考えた。外国リージョンではAWS Batchってやつが使えるようになっているので、そいつが東京リージョンに来たときにうまいことECSから移行できると最高に素敵だ。

各ミニサービスのECSへの集約

上述ミニサーバー群はまがいなりにもサーバーで、しかもSSLなので、IPを固定したい。

が、コンテナにEIPを付けることはできず、Global IPを持たせたい場合は、かわりにELBを使うのが定石1

なのだが、さして重要でもないのにELBを使うのはオーバースペック、というかお金の無駄なので、Unmanagedなコンテナインスタンスを作成し、その中でnginxを動かして、コンテナにリバースプロキシすればーーー、ということを考えていた。コンテナインスタンス障害時にはまるごと作り直せばいいようなサービスレベルだし、それが簡単にできちゃうのがコンテナの長所だ。

が、じゃあそもそもnginxもコンテナでよくね、っていうことに気付いた。コンテナ間でリバースプロキシしちゃえばいいじゃん。

ELB無しでSSLできるコンテナインスタンスの作成

つまりコンテナインスタンスにEIPをつけたい、ということ。ECSのウィザードからクラスタを作成する場合、コンテナインスタンスのカスタマイズ可能項目は少ない。カスタム可能 (つまりコンテナインスタンス作成時に指定可能) なものは

  • インスタンスタイプ
  • ディスク容量
  • VPC
  • Security Group

だけで、Public IPとPrivate IPは自動的に割り当てられるため、コンテナインスタンス作成時にIPやEIPの指定はできない。なので、このあとに、作成されたEC2インスタンスに対して

  1. EIPを作成
  2. EIPをコンテナインスタンスのインターフェースに割り当て

ってやるとコンテナインスタンスのIPを安全に固定できる。

実際にEIPを付けた直後からコンテナに疎通可能になっていて、しかもECSから見えるコンテナインスタンス情報も自動で更新され、Global IPは新しいEIPになっていることが確認できる。便利な世の中になったもんだ。

コンテナ間リンク

nginxもコンテナなので、リバースプロキシ先はコンテナのリンクでOK、と思っていたのだが、なんとコンテナのリンクは同じタスク定義に属したコンテナ間のみで可能とのこと2。つまりexternal_linkは書けない。でも今回のユースケースだと、コンテナごとにタスク定義を変えるタイミング (Docker imageの更新とか) は異なるので、コンテナごとにタスク定義を作ることになる。

ってことは、コンテナごとに80:10080443:10443みたいなPort Mappingを書き、nginxはこのポートとコンテナインスタンスのローカルアドレスで、各コンテナへリバースプロキシする。まぁPort Mappingは複数コンテナインスタンスにまたがってコンテナを配置をしたい場合とかに結局必要だよね、たぶん。

Unmanaged Container Instanceを作りたい場合

ちなみに、どうしてもオレオレカスタムなUnmanagedコンテナインスタンスを作りたいときは

  1. 自力でEC2インスタンスを作成する

    • AWSが用意しているECS用のAMIを使う
    • か、それ以外を使う場合は自分でAgentのインストールなどが必要
    • めんどくさいけどEC2でインスタンスをいろいろいじれる
  2. ECSクラスタ作成時に空のクラスタを作る
  3. さっきのインスタンスをクラスタに追加する

という手順で可能らしい3

でもnginxもコンテナにしちゃうなら、ECSウィザード経由でデフォルトのAMI使う方が再作成と運用が楽な気がする。できるだけ外の世界へ出て行こうとしないのがパブリッククラウドの鉄則。

AWS Batch

ドキュメントを†熟読† (3分) した感じ、ECSにジョブキューを付けて、順番やリトライ、キューごとの計算環境の割り当て、実行時間やリソースの制限、優先度とかを定義できるようにしたものっぽい。†熟読†して思ったことは、

  • 要はPBS4サービスみたいなもんじゃね? PBSのインストールと運用は意外にめんどくさいらしい。というか需要がニッチすぎて活発には開発されてないらしい。通常SQSとかAMQPが実装された何かを使うんじゃないかしら。
  • GPUインスタンスが使えたりとか、AWSをスパコン的に使いたい人にはグッとくるのかもしれない5が、いるのかそんな人?並列性能上げると青天井でお金かかるし、1週間かかる機械学習だとやっぱり青天井でお金かかるし、そういう人は京とかどっかのスパコン借りた方が安いんじゃ……ぶつぶつ……

というわけで、AWS Lambdaで時間足りない人向け、という位置づけが現実的。

ECSからAWS Batchへの移行を考える

東京リージョンで今でも使えるECSから、今後東京リージョンにも展開されるであろうAWS Batchへの移行を考えると、既存のECSクラスタをAWS BatchのCompute Environment (CE) に変換できるとうれしいぞ。ドキュメントを読んでみると

By default, AWS Batch managed compute environments use the latest Amazon ECS-optimized AMI for compute resources.6

どうやらCEはECSのAMIをそのまま使うだけっぽいので、ECSクラスタを作ったあとにCEに追加できるのでは?と思ったが、 (画面上からは) ECSクラスタをCEに変換することはできなさそう。逆はできる、つまりCEをECSクラスタとして扱うことはできる。というか、CEを作った時点で空のECSクラスタが作成されている。末尾にUUIDとかついた、とてもゴチャったECSクラスタが作成される。

AWSというサービスの哲学として、Manageされるもの (e.g. ECSクラスタ) はManageするもの (e.g. CE) にはできない。これは言われれば分かるけど、気付かないと「不便だ」と不要な精神の不衛生を招く。

というわけで、ECSからそのままBatchに移行はできなさそうなので、今回のように特定のEIPを使い続けたいような場合、ECSコンテナインスタンスのEIPを、CEから生まれたECSコンテナインスタンスに移行時に付け替えることになる。

API使っても瞬断が出るが、まぁそもそもそこまでサービスレベルは高くない。はずだ。

でもこれって、ECSクラスタ間でコンテナインスタンスの移動ができれば解決するんじゃ?

と思って調べてみると

各コンテナインスタンスには、それぞれに固有の状態情報がコンテナインスタンスにローカルで保存され、Amazon ECS にも保存されているため、コンテナインスタンスを 1 つのクラスターから登録解除して別のクラスターに再登録しないでください。コンテナインスタンスリソースを再配置するには、1 つのクラスターからコンテナインスタンスを終了し、新しいクラスター内の Amazon ECS に最適化された最新の AMI で、新しいコンテナインスタンスを起動することをお勧めします。7

ぐはっ……。

サーバー系はECSに残し、バッチ系だけAWS Batchに、という住み分けが清潔なのかもしれないけど、それじゃ集約にならないなぁ。

Refs.

続きを読む

CRM(F-Revo)をLightsailで導入した

はじめに

F-RevoCRMとは(引用 https://f-revocrm.jp/)

F-RevoCRM は、世界中で活用されている vTigerCRM をベースに、日本の企業文化に合わせてシンキングリードが独自のカスタマイズを行った、フリー&オープンな高機能CRMアプリケーションです。

このCRMをインストールしてから運用するために実施した内容をまとめておきます。
インストール手順は公式ページを参照ください。

環境

サーバ
 AWS Lightsail(RAM:1GB, SSD:30GB, 転送量:2TB)
 リージョン:バージニア

ミドルウェア
 Apache2.4
 MySQL5.6.35
 PHP5.6
※最初PHP7でインストールを実施したところ、バージョン対応していないらしくインストール時点でコケたためPHP5.6で導入しました。

前提

CRMのユーザは社員であり、社内・社外からアクセスする

セキュリティ設定

  • 常時HTTPS接続
  • 社外IPからのアクセス時はダイジェスト認証
  • サーバ証明書(Let’s encrypt)
  • iptablesでファイアウォール設定
     ※LightsailのGUIコンソールでネットワーク設定もできますが高度な設定ができないためにiptablesを使用しました

バックアップ設定

  • Lightsailで使用できるスナップショットを利用
  • データベースはmysqldumpを定時実行

運用設定

  • F-Revoログ出力設定
  • 証明書自動更新設定

懸念点(特にLightsail)

Lightsailのスペックに限界がきた場合、スケールアップする可能性があります。
2017年4月段階で、LightsailのスケールアップはAPIからしか実施できないのが注意点です。
さらに、DBをインスタンスから切り離したいとなると、VPC Peeringを経由させる必要があったりするので若干複雑化します。
また、スナップショットでサーバを復旧させた場合(復旧と言っても新規インスタンスとして立ち上がる)、Public IPが変わります。Static IPを設定するかDNS Zoneを設定する必要が出てきますね(IPもDNSもLightsailで設定可能です)。

おわりに

今のところ特に問題は発生してないです。F-Revoの推奨スペックはRAM4GBと記載がありますが、そこも特に問題はないです。
ただし、Lightsailのリージョンがバージニアのため、コマンド入力に微妙にタイムラグがあってストレスが溜まります。。。

続きを読む

api gatewayのcustom authorizersでip制限するのはやっぱりできなかった話

結論:なんでできなかったか

https://gyazo.com/dd58ad4bfc8bba9a08818f7a5611f8b4

なんでip制限したかったか

api gatewayへのアクセスを制約する方法としてapi keyで認証させるケースをよく見るけど、結局エンドポイントとapikeyが盗まれると叩けてしまうよなぁと思っていて、

  • 会社が小さくて誰でもaws触れる
  • 誰か会社をやめる可能性がある
  • 金になるAPIである

って条件が揃った時、やっぱりIP制限するのが一番安心できると思ったのでした。
cloud frontとWAFを挟んでIP制限している例もみるのですが、結局api gatewayのエンドポイントとapikeyが盗まれたら一緒なのでは?と思っている。
間違ってたら指摘頂けると幸せです。

やってみたこと

カスタム認証のIdentity token source

api gatewayにカスタム認証(認可)を付ける方法があって、これに習って、特定のIPのみを認可するように作りたかったのでした。

Identity token sourceでサポートされるコンテキスト変数

Identity token source にIPが渡ってくるように書ければ認可用のlambdaにIP渡せそう!!このへん
この method.request.header.Auth をいい感じに書き換えれば、と思ってこのへんをみてみると
https://gyazo.com/c0b855ca9597ca5050e50adfcebe5ec6
きたっぽい!
サポートされるコンテキスト変数をみてみる。
https://gyazo.com/fc0ee3e78c3ad0f05a08f30994abcbaa
あったー!コレ設定したら勝ちだ!

設定してみる

https://gyazo.com/dd58ad4bfc8bba9a08818f7a5611f8b4
ぴゃーーーーーーー!!!!
まぁ Identity token source のとこにも書いてあったんですけどね。

https://gyazo.com/511574d47ca885d42cbb8c872a4e33ed

代替案

この方がやってるとおりlambdaにてipチェックはできそう。でもlambdaごとにipチェック挟むの書かなきゃではあるのかなぁ。。。
プロキシしてくれるlambdaに一旦引き入れてそこでチェックして、各lambdaにinvokeすれば良いのかも(未検証)

続きを読む

純粋なシェルスクリプトだけでAWSを操りたい!〜POSIX原理主義でクラウドも侵略〜

この記事は、POSIX原理主義とシェルショッカー日本支部に感化されちゃって、なんとか実践しようとしている、なんちゃって雑魚戦闘員がお送りします。POSIX原理主義でもクラウドを侵略できますよ、というお話です。これで、POSIX原理主義がますます強力になれるんじゃないかと勝手に思っています。

まずコマンドの紹介から、

axs - UNIX哲学を守ったつもりのsimpleなawsコマンド

A simple ‘aws’ command ‘axs’ written in POSIX sh. axs(access) to aws(amazon web services) with posixism.

このコマンドは、AmazonWebServicesにアクセスし、数々のwebサービスを利用したり、アプリを構築したりするために作られた、POSIX原理主義に基づく、なんちゃってawsコマンドです。

https://github.com/BRAVEMAN-L-BRID/axs

作った経緯

AWS APIツールは「いつでも、どこでも、すぐ」使えるわけではない。

バージョン依存や環境依存の大きい、AWS APIツールはいつでもどこでもすぐに使えるわけではありません。それに加えて、SDKやCLIも、それらをインストールできない環境だと、そもそも役に立ちません。意外なこと使えない環境を目にする機会も多いです。

しかしながら、環境やバージョンアップに悩まされずにAWSのサービスを利用したい、というニーズも時にはあるのではないでしょうか?
浅くネットサーフィンした限りでは、そのように認識しています。

このaxsコマンドは、そのニーズに応えるために作りました。

POSIX shに準拠しているつもりなので、Windows, Mac, Linuxなどで、それこそコピーしてくるだけで「いつでも、どこでも、すぐ」動きます。

大量のオプション、引数からの解放

作った経緯2です。awsコマンドの大量の引数やオプション、サービスごとに異なる数々のサブコマンドにヘキヘキした覚えはありませんか?私はあります。
あれはUNIX哲学を守っていません。コマンドとクラウドへ渡すパラメーターがぐちゃぐちゃに混ざったシェルスクリプトを書くのは、もう、こりごりです。

このコマンドでは、その煩わしさから解放されます(嘘、かもしれません)。また、UNIX哲学を守ったつもりで、RESTfulの概念も大事にしています。直感的な操作が可能かもしれません。

スクリプトを使った自動化も楽になる可能性があります。

使い方

ダウンロードしたら、このリポジトリの/binディレクトリにPATHを通してください。

0. 準備

~/.aws/credentialsファイルに以下のようにアクセスキーIDとシークレットアクセスキーを記述してください。パーミッションには気を付けてください。

[default]
aws_access_key_id = hogehoge
aws_secret_access_key = mogemoge

1. 基本

使い方は簡単です。設定ファイル(データ形式は後述)をaxsコマンドで読み込むだけです。

例えば、catコマンドを使って

$cat config_file | axs

とできますし、もしくは、引数に設定ファイルを置くだけです

$axs config_file

2. 設定ファイルのデータ形式について

AWSはREST APIを用いています。そこで、今回は、正しいのかどうかは横に置いておいて、HTTPにちなんだデータ形式の設定ファイルを記述します。

以下のような形式をとることにしました。

METHOD URI                    (←リクエストライン)
Key Value                   (←キーバリュー形式に分解したクエリ)
key Value                   (←キーバリュー形式に分解したクエリ)
Key Value                     (←キーバリュー形式に分解したクエリ)
Host: ec2.ap-northeast-1.amazonaws.com  (←必須ヘッダー)
Content-Type: application/hoghoge     (←場合によっては必須のヘッダー)
X-Amz-moge: hogehogehoge                (←設定などの追加のヘッダー)

(bodyの部分コンテンツの中身)
xmlとかjsonとかバイナリデータ         (←コンテンツの中身)

基本的に、Host,Content-Typeヘッダのみが必須だと考えてもらっていいです。

AWS APIを利用するので、body部には、基本的にxmlやjsonを記述します。

ただし、場合によっては画像ファイルや音声ファイルなどのバイナリデータをアップロードしなければならない時もあります。また、xml,jsonが長く煩雑な時は、リクエストライン、クエリ、ヘッダまでの部分とbody部を分離したいと思う時もあるでしょう。

そのような時に、axsコマンドの-fオプションを使います。

3. -fオプション

body部を分離して別ファイル(body.txtなど)にしてaxsコマンドを使う場合には以下のようにします。
※ここではヒアドキュメントを使用しています。

$cat<<END | axs -f moon.jpg
PUT /image.jpg
Host: Bucket.s3.amazonaws.com (東京リージョンの場合はbucket.s3-ap-northeast-1.amazonaws.com)
Content-Type: image/jpeg
END

*この例では、ローカルのmoon.jpgという画像ファイルをs3のBucketバケットにimage.jpgという名前で保存しています。

4. -qオプション

 これはAPIアクセス後に返ってくる、レスポンスのレスポンスヘッダーを表示するかいなかを決定するオプションです。
 デフォルトではレスポンスヘッダを表示します。AWSのREST APIがヘッダ情報をよく扱うので、汎用性を高めるためにそうのようにしてあります。

例えば、以下のような違いになります。

$cat config_file | axs 

HTTP 200  OK
hoge: hogehoge
hage: hagehoge
hoga: hogレスポンスヘッダー

<xml ......>
<DescribeInstances>...........</...>
.......

-qオプションを利用した場合は、レスポンスヘッダが省略されxmlやjsonやバイナリが直に帰ってきます。ですので、パイプでつないで加工したりするのに便利です。


cat config_file | axs -q > polly.mp3
               (pollyに喋ってもらう)


cat config_file | axs -q | parsrx.sh(POSIX原理主義製xmlパーサー)
               (xmlが返ってくる)


cat config_file | axs -q | parsrj.sh(POSIX原理主義製jsonパーサー)
               (jsonが返ってくる)

TIPS

  • 設定ファイルの書き方は、AWS API referenceなどを参照してください。設定ファイルの記述はクエリ部分以外はHTTPと同じです。
    深く悩まずに記述できることでしょう。

  • Content-Lengthヘッダ,x-amz-content-shaナンチャラヘッダ,Autorizationヘッダは自動生成されるので、考慮する必要はありません。

  • 私も仲間に加えてもらった秘密結社シェルショッカー日本支部のPOSIX原理主義製の他コマンドと相性がいいです。
    これを機に秘密結社シェルショッカー日本支部よりダウンロードしてくることをお勧めします。
    中でもmojihameコマンドとの相性は抜群です。https://github.com/ShellShoccar-jpn/installer

例えば、クエリAPIとmojihameコマンド

テンプレファイルを用意します、template

GET /
QUERY
%1 %2
QUERY
Version 2016-11-15
Host: ec2.ap-northeast-1.amazonaws.com
Content-Type: application/x-www-form-urlencoded

設定の用意、config.txt

Action Hogehoge
AAAAA dededed
hogemoge ahahaha

いざアクセス

cat config.txt | mojihame -lQUERY template - | axs -q | parsrx.sh | 加工

素晴らしいparsrsのコマンドを用いれば、無駄に多くのコマンドを用いずにレスポンスの解析もできます

ちなみに

cat config.txt | mojihame -lQUERY template -

までの結果だけ抜き出すと以下のようになっています。

GET /
Action Hogehoge
AAAAA dededed
hogemoge ahahaha
Version 2016-11-15
Host: ec2.ap-northeast-1.amazonaws.com
Content-Type: application/x-www-form-urlencoded

実際にAWSにaxs(アクセス)してみよう!

以下の内容ではシェルショッカー日本支部の開発しているコマンドを多く使うかもしれません。mojihameコマンドとParsrsは必須です。また、記述の簡略化のために、ヒアドキュメントを多用します。

S3にアクセスして、東京リージョンで静的webサイトのホスティングをしてみよう!

0.好きな名前のbucketをおく(PUTする)

cat <<END | axs
PUT /
Host: BucketName.s3-ap-northeast-1.amazonaws.com
Content-Type: application/xml

<CreateBucketConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/"> 
  <LocationConstraint>ap-northeast-1</LocationConstraint> 
</CreateBucketConfiguration >
END

1.bucketの中に公開するオブジェクトindex.htmlをおく(PUTする)

公開するものなので、誰でも読み込み可能にするために、設定として追加のヘッダx-amz-acl: public-readを付与しています。

cat <<-END | axs -f index.html     (←ローカルファイル)
PUT /index.html
Host: BucketName.s3-ap-northeast-1.amazonaws.com
Content-Type: text/html
x-amz-acl: public-read
END

2.bucketの中に公開するオブジェクトerror.htmlをおく(PUTする)

これは、エラーページ用のhtmlです。

cat <<END | axs -f error.html
PUT /Error.html 
Host: BucketName.s3-ap-northeast-1.amazonaws.com
Content-Type: text/html
x-amz-acl: public-read
END

3.bucketの中に公開するオブジェクトimage.jpgをおいて(PUTする)、やっぱり消す(DELETE)

index.htmlに載せる画像をbucketにputしましたが、気に入らなかったのでやっぱりDELETEすることにしました。

cat <<END | axs -f moon.jpg
PUT /image.jpg
Host: BucketName.s3-ap-northeast-1.amazonaws.com
Content-Type: text/html
x-amz-acl: public-read
END

やっぱり気に入らないので消す

cat <<END | axs
DELETE /image.jpg
Host: BucketName.s3-ap-northeast-1.amazonaws.com
END

4.バケットをウェブサイト用に修正する(PUTする)

s3を静的webサイトとして活用します。

s3のwebサイトホスティングでは、ルーティングルールを使用して、特定のHTTPエラーコードをチェックする条件を指定できます。ルールを追加すれば、リクエストエラーが発生した場合、リクエストを再ルーティングしたりできます。つまりwebサイトの機能を持たせることができるのです。

たとえば、リクエストエラーを処理し、別のホストにルーティングしてみましょう。次のルーティングルールは、HTTPエラー404のイベントでEC2インスタンスにリダイレクトします。

ExamplePage.htmlページをリクエストしてHTTP 404エラーが発生したとします。すると、そのリクエストは、指定されたEC2インスタンスのreport-404/testPage.htmlに再ルーティングされます。ルーティングルールが存在しない状態で、HTTPエラーが発生した場合には、Error.htmlを返します。

cat <<END | axs
PUT /
website
Host: BucketName.s3-ap-northeast-1.amazonaws.com
Content-Type: application/xml

<WebsiteConfiguration xmlns='http://s3.amazonaws.com/doc/2006-03-01/'>
  <IndexDocument>
    <Suffix>index.html</Suffix>
  </IndexDocument>
  <ErrorDocument>
    <Key>Error.html</Key>
  </ErrorDocument>

  <RoutingRules>
    <RoutingRule>
    <Condition>
      <HttpErrorCodeReturnedEquals>404</HttpErrorCodeReturnedEquals >
    </Condition>
    <Redirect>
      <HostName>ec2-11-22-333-44.compute-1.amazonaws.com</HostName>
      <ReplaceKeyPrefixWith>report-404/</ReplaceKeyPrefixWith>
    </Redirect>
    </RoutingRule>
  </RoutingRules>
</WebsiteConfiguration>

5.TIPS:独自ドメインで静的webサイトを公開する場合

上記までの設定を用いて、s3で静的webサイトのホスティングを行うと、webサイトの公開アドレスは、AWS側で決定されたendpoitとなります。例えばs3-website.s3.amazonaws.comのようなものです。

しかし、自分のドメインでwebサイトを公開したいと思う時もあるでしょう。

その場合は、バケット名を独自ドメイン名にして、Route53で設定してあげれば可能です。

例えば、あなたの所得したドメインがwebsite.comだった場合。バケット名をwebsite.comにします。あとは、Route53で設定してあげると、website.comでwebサイトを公開できます。

6.TIPS:独自ドメインのバケットを用いるときの注意

独自ドメインでサイトを公開しない通常の時は、以下のようにホストヘッダにバケットの名前を含めることができます。そしてこちらの方がレファレンス的には正しいです。

cat <<END | axs
PUT /
website
Host: BucketName.s3-ap-northeast-1.amazonaws.com
END

なおこの場合のwebサイトのアドレス以下のようになります。

http://BucketName.s3-website-ap-northeast-1.amazonaws.com

しかし、独自ドメインをバケット名に使用し、ホストヘッダに含める場合は、axsコマンドでは、エンドポイントの名前解決ができません。したがってドメイン名のバケットを作る場合には、ターゲットuriにバケット名を含めます。

cat <<END | axs -f index.html
PUT /website.com/index.html
Host: s3.amazonaws.com
Contetnt-Type: text/html
END

この操作では、us-east-1リージョンにあるwebsite.comバケットに、index.htmlをPUTしています。なおAWS APIで用いるendpointについては、レファレンスを参照してください。サービスとリージョンごとに形式が異なる場合が多いです。
http://docs.aws.amazon.com/ja_jp/general/latest/gr/rande.html

7.TIPS:ヒアドキュメントで記述した内容を設定ファイルに残したい

そんな時はteeコマンドを使います 。ヒアドキュメントに記述した内容がconfig_fileに保存されます。

cat <<END | tee config_file | axs
GET /
Host: s3.amazonaws.com
END

Amazon Pollyにアクセスして音読させてみよう!

Amazon Pollyはテキストの音声変換サービスです。誰でも非常に簡単に利用できるサービスで、AWSの中でも敷居は低いです。とりあえずやってみましょう。何かのアプリ開発に役に立つかもしれません。

また、AWSのフルマネージドサービスなので利用者がインフラ構成やコーディングについて心配する必要は微塵もありません。東京リージョンでは利用できないので注意してください。

なお、ここではmojihameコマンドを使います

1.テンプレートファイル speech.templateを用意します。

お馴染みのHTTP形式の設定ファイルのテンプレです。使い回しが可能なので、メンテナンス効率は上がるかもしれません。aws cliのようにパラメータとコマンドをごちゃまぜにしたスクリプトを書く必要がなくなります。

POST /v1/speech
Host: polly.us-east-1.amazonaws.com
Content-Type: application/json

{
  "OutputFormat": "mp3",
  "Text": "%1",
  "VoiceId": "Mizuki"
}

2.pollyにアクセスしておしゃべりしよう!

ここまできたら簡単、あとはAmazon Pollyにアクセスしておしゃべりするだけです。

$echo "こんにちはQiita" | mojihame polly.template - | axs -q > result.mp3

ちなみにaxsコマンドの前までを抜き出すと以下のようになっています。

POST /v1/speech
Host: polly.us-east-1.amazonaws.com
Content-Type: application/json

{
  "OutputFormat": "mp3",
  "Text": "こんにちはQiita",
  "VoiceId": "Mizuki"
}

3.長文の小説もPollyに喋ってもらうことができます。

「小説家になろう」というサイトから小説をもらってきて、pollyに音読させましょう。pollyには文字数制限がありますので、何文字かに区切ってリクエストを出し、返ってきたレスポンスを対象ファイルに上書きしていきましょう。

下記のcurl先のurlのncode下の番号は小説とページによって異なります。

$curl http://ncode.syosetu.com/txtdownload/dlstart/ncode/XXXXXXXX/ | 
tr -d 'n' | 
sed 's/.{250}/&@/g' | 
tr '@' 'n' | 
while read LINE; do 
echo $LINE | mojihame polly - | axs -q ; 
done >> polly.mp3

EC2にアクセスしてインスタンスを立ててみよう!

このセクションではmojihameコマンドとparsrx.shを使います。

0.テンプレートファイルec2.templateの用意

以下のファイルはmojihameコマンドで使います。

GET /
QUERY
%1 %2
QUERY
Version 2016-11-15
Host: ec2.ap-northeast-1.amazonaws.com
Content-Type: x-www-form-urlencoded

1.VPCを作ろう

cat <<END                       | 
Action CreateVpc
CidrBlock 10.0.0.0/16
END
mojihame -lQUERY ec2.template - | 
axs -q                          |
parsrx.sh                       | 
grep 'vpcId'                    | 
awk '{print $2}'

(結果としてvpcIdが表示されます)

ヒアドキュメントではなく、config_file1に設定項目であるAction CreateVpcとCidrBlock 10.0.0.0/16を記述したとしたら以下のようになワンライナーになります。

 cat config_file1 | mojihame -lQUERY ec2.template - | axs -q | parser.sh | grep 'vpcId' | awk '{print $2}'

2.puclic-subnetを作ろう

作成したvpcにinternet gatewayをアタッチし、パブリックサブネットを作成します。以下省略

3.ec2インスタンスを立てよう

作成したpublic-subnet内にec2インスタンスを作成します。以下省略

今回は面倒臭かったので色々省略しましたが、1、2、3の流れは全て簡潔で似通っています。次回があれば、AWS上に3-tierアーキテクチャを構成する完全自動化スクリプトを組んでみたいと思います。もちろんaxsコマンドを使って。

Amazon Rekognitionにアクセスして画像解析してみよう!

Amazon Rekognitionは、深層学習により、画像解析を行なってくれるAWSのフルマネージドサービスです。ユーザーがコーディングの心配をすることなく、簡単にすぐに、どこでも利用することができます。非常に利便性が高く、将来必要不可欠になるであろうサービスの一つです。

1.S3にバケットを配置して、顔を含んだjpeg画像を置いてみる

リージョンはusを選択してください。他リージョンではまだサービスを開始していません。

  • バケットを作る
cat <<END | axs
PUT /
Host: s3-to-rekognition.s3.amazonaws.com
  • 画像ファイルを配置する
cat <<END | axs -f my-face.jpg
PUT /my-face.jpg
Host: s3-to-rekognition.s3.amazonaws.com
Content-Type: image/jpeg

2.Rekognitionで画像解析して、要素を抽出してみる

これにはDetectLabels APIを使用します。

DetectLabelsは、入力として提供される画像(JPEGまたはPNG)内における実世界の要素を検出して、ラベルを付与します。花、木、テーブルなどのオブジェクトは当然含まれ、さらには、結婚式、卒業、誕生日パーティーのようなイベントシーン、風景、夕方、自然などの概念までが含まれます。

結果は整形されていないjsonで帰ってきます。これでは読み取りづらいので、ヘッダ情報を排して、jsonパーサーにかけてしまいます。もちろん、POSIX原理主義jsonパーサーparsrj.shを使って。

cat <<END | axs -q | parsj.sh
POST /
Host: rekognition.us-east-1.amazonaws.com
X-Amz-Target: RekognitionService.DetectLabels
Content-Type: application/x-amz-json-1.1

{
   "Image":{
      "S3Object":{
         "Bucket":"s3-to-rekognition",
         "Name":"my-face.jpg"
      }
   }
}
END

私の顔画像をアップロードして解析した結果は以下のようになりました。

$.Labels[0].Confidence 99.2739486694336
$.Labels[0].Name Human
$.Labels[1].Confidence 99.27888488769531
$.Labels[1].Name People
$.Labels[2].Confidence 99.27890014648438
$.Labels[2].Name Person
$.Labels[3].Confidence 82.6961669921875
$.Labels[3].Name Dimples
$.Labels[4].Confidence 82.6961669921875
$.Labels[4].Name Face
$.Labels[5].Confidence 82.6961669921875
$.Labels[5].Name Smile
$.Labels[6].Confidence 54.255836486816406
$.Labels[6].Name Selfie
$.Labels[7].Confidence 50.784881591796875
$.Labels[7].Name Female
$.Labels[8].Confidence 50.784881591796875
$.Labels[8].Name Girl
$.Labels[9].Confidence 50.784881591796875
$.Labels[9].Name Woman
$.Labels[10].Confidence 50.74814987182617
$.Labels[10].Name Glasses
$.Labels[11].Confidence 50.74814987182617
$.Labels[11].Name Goggles

多分、私は二重で目が大きいので上のような結果になってしまったのだと思います。サングラスとか眼鏡の可能性が指摘されてます。ちなみに顔アップの自撮り写真をあげたのですが、それも認識されているようですね。えくぼとスマイルまで認識されています。rekognitionには女顔として認識されたようでした。

以上のようにかなり詳しく要素を検出してくれます。別の例ではテーブルクロスなども検出していました。他にもAmazon Rekognitionでは顔の判別や比較、リストができます。ですので、例えば、普段見ない顔を検出した時にアラートを送信するようなアプリケーションを作れば、自作警備システムなんかも作成できるかもしれませんね!東京オリンピックに備えて笑

まとめ

さて、これでクラウドの力を借りれば、POSIX原理主義でも、様々なアプリケーションを開発できることがわかりましたね!さらには、機械学習や深層学習の力を借りた強力なものまでも、比較的簡単にできちゃうことがわかったと思います!しかも環境やバージョンに悩まされることなしで!

わーい!POSIX原理主義シェルスクリプトは楽しいですね!

※ここまでの流れで、AWS自体がPOSIX原理主義と対極じゃんて声が聞こえてきそうですが、そこはあまり関係ありません。AWSは道具というよりサービスです。ほらAmazon Web Servicesって言います。それにフルマネージドなサービスを多く活用するようにすれば、バージョンアップによる不具合とは比較的無縁です。

ということで、私は、実は、POSIX原理主義は、クラウドととても相性がいいのではないかと勝手に思っています(素人考え)。

「必要なもの」POSIX準拠コマンドと交換可能性を担保したコマンド

  • openssl ver1.0.0以上
  • utconv (同梱、秘密結社シェルショッカー日本支部)
  • urlencode (同梱、秘密結社シェルショッカー日本支部)
  • cat
  • awk
  • sed
  • printf
  • echo
  • grep
  • curl
  • wget
  • eval

感想

意味がるのか知りません。あるかもしれないし、ないかもしれません。ただ楽しかったですとだけ付け加えておきます。

「シェルかっこいい、クラウドかっこいい」
→「なんか作りたい」
→「POSIX原理主義すげー」
→「POSIX原理主義でもクラウドwebサービスの力を借りたい!」
→「一番サービスが充実していそうなAWSを操りたい」
→「awsコマンド的なものをPOSIX原理主義で開発しよう!」
という動機で始めました。作者は初Qiita,教育学部卒業したてのずぶの素人で数ヶ月前までIT系とは無縁でした。技術的な誤りを多分に含んでいる可能性があります。その時は指摘してくださると助かります。

続きを読む