IDEA Note

  • embulk に環境変数で接続情報を渡す

    embulk に環境変数で接続情報を渡す

    tl;dr;

    embulk で環境変数を処理することができる.liquid

    について紹介。これを使うことで、docker image を複数環境で起動することができるようになる。

    公式ドキュメント

    以下のような記載がある。

    https://www.embulk.org/docs/built-in.html

    試してみた

    準備

    以下のようなディレクトリ構成

    “`
    ├── Dockerfile
    └── config.yml.liquid

    “`

    それぞれ中身は以下の通り

    FROM java:8
    MAINTAINER sumito.tsukada "tsukada@sumito.jp"
     
    ENV EMBULK_VERSION 0.9.23
     
    RUN curl -L https://bintray.com/artifact/download/embulk/maven/embulk-${EMBULK_VERSION}.jar -o /opt/embulk.jar
    RUN java -jar /opt/embulk.jar gem install embulk-input-mysql
    RUN java -jar /opt/embulk.jar gem install embulk-output-bigquery
    
    COPY config.yml.liquid /work/
     
    WORKDIR /work
    ENTRYPOINT ["java", "-jar", "/opt/embulk.jar", "run", "/work/config.yml.liquid"]

    受け取る側には {{env.}} を接頭辞として付ける。

    in:
      type: mysql
      user: {{env.DB_USER}}
      password: {{env.DB_PASS}}
      database: {{env.DB_NAME}}
      host: {{env.DB_HOST}}
      query: {{env.DB_QUERY}}
    out: {type: stdout}

    build

    docker build コマンドでビルドする。

    $ docker build -t tsukada/embulk .
    Sending build context to Docker daemon  3.072kB
    Step 1/9 : FROM java:8
     ---> d23bdf5b1b1b
    Step 2/9 : MAINTAINER sumito.tsukada "tsukada@sumito.jp"
     ---> Using cache
     ---> 5fc85815e7bd
    Step 3/9 : ENV EMBULK_VERSION 0.9.23
     ---> Using cache
     ---> d449016ef749
    Step 4/9 : RUN curl -L https://bintray.com/artifact/download/embulk/maven/embulk-${EMBULK_VERSION}.jar -o /opt/embulk.jar
     ---> Using cache
     ---> ea1d918c1c64
    Step 5/9 : RUN java -jar /opt/embulk.jar gem install embulk-input-mysql
     ---> Using cache
     ---> e7057cd4cefe
    Step 6/9 : RUN java -jar /opt/embulk.jar gem install embulk-output-bigquery
     ---> Using cache
     ---> 01d70f5dc598
    Step 7/9 : COPY config.yml.liquid /work/
     ---> Using cache
     ---> 8f7529947df8
    Step 8/9 : WORKDIR /work
     ---> Using cache
     ---> 7d4b1d4ea599
    Step 9/9 : ENTRYPOINT ["java", "-jar", "/opt/embulk.jar", "run", "/work/config.yml.liquid"]
     ---> Using cache
     ---> 8161e0f70fde
    Successfully built 8161e0f70fde
    Successfully tagged tsukada/embulk:latest

    使い方

    環境変数に接続先情報や、クエリの情報を渡す。

    $ docker run -e DB_USER=tsukada -e DB_PASS=password -e DB_NAME=database -e DB_HOST=hogehoge.ap-northeast-1.rds.amazonaws.com  -e DB_QUERY="select * from sample" --rm -it tsukada/embulk
    2020-02-18 03:43:44.882 +0000: Embulk v0.9.23
    (中略)
    2020-02-18 03:43:52.822 +0000 [INFO] (0015:task-0000): > 0.00 seconds
    1,tsukada,hoge,,2020-02-14 09:14:31,2020-02-14 09:14:32,84,004,2020-02-14 09:14:31,2020-02-14 09:14:31
    2020-02-18 03:43:52.913 +0000 [INFO] (0001:transaction): {done:  1 / 1, running: 0}
    2020-02-18 03:43:52.917 +0000 [INFO] (main): Committed.
    2020-02-18 03:43:52.917 +0000 [INFO] (main): Next config diff: {"in":{},"out":{}}

    .liquid ファイル内では env. で受け取るが、docker 起動する際に渡す環境変数には、env を付けづに実行する。

    無事データを取得することができた。

    所感

    これを使えば、コンテナ内にcredential情報を保持しなくて済むようになるため、1つのcontainer imageで、複数環境で動かすことができそうだ。

  • org/embulk/cli/Main : Unsupported major.minor version 52.0

    org/embulk/cli/Main : Unsupported major.minor version 52.0

    tl;dr;

    embulk をbuild しようとすると ` Unsupported major.minor version 52.0` というエラーが出るようになった。原因と対処について紹介。

    元にした作業手順(2018年だから、もう2年も前だ。。)

    https://tsukada.sumito.jp/2018/09/18/mysql%e3%81%ae%e3%83%87%e3%83%bc%e3%82%bf%e3%82%92embulk%e3%81%a7bigquery%e3%81%b8%e5%85%a5%e3%82%8credash%e3%81%a7%e7%a2%ba%e8%aa%8d%e3%81%99%e3%82%8b/

    エラー詳細

    Exception in thread "main" java.lang.UnsupportedClassVersionError: org/embulk/cli/Main : Unsupported major.minor version 52.0
    	at java.lang.ClassLoader.defineClass1(Native Method)
    	at java.lang.ClassLoader.defineClass(ClassLoader.java:803)
    	at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
    	at java.net.URLClassLoader.defineClass(URLClassLoader.java:449)
    	at java.net.URLClassLoader.access$100(URLClassLoader.java:71)
    	at java.net.URLClassLoader$1.run(URLClassLoader.java:361)
    	at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
    	at java.security.AccessController.doPrivileged(Native Method)
    	at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
    	at java.lang.ClassLoader.loadClass(ClassLoader.java:425)
    	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
    	at java.lang.ClassLoader.loadClass(ClassLoader.java:358)
    	at sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:482)

    原因

    embulk でjava 7のサポートが切れた。build する際にプラグインの組み込み段階でエラーになる。

    対処

    embulk のベースの docker image を

    `FROM java:7` から

    `FROM java:8` に変更する。

    サンプル

    FROM java:8
    MAINTAINER sumito.tsukada "tsukada@hoge.jp"
     
    ENV EMBULK_VERSION 0.9.23
     
    RUN curl -L https://bintray.com/artifact/download/embulk/maven/embulk-${EMBULK_VERSION}.jar -o /opt/embulk.jar
    RUN java -jar /opt/embulk.jar gem install embulk-input-mysql
    RUN java -jar /opt/embulk.jar gem install embulk-output-bigquery
     
    WORKDIR /work
    ENTRYPOINT ["java", "-jar", "/opt/embulk.jar"]
    CMD ["--help"]
    

     

     

     

  • シーケンス図をブラウザだけで書く

    シーケンス図をブラウザだけで書く

    tl;dr;

    シーケンス図などを作ろうとすると、GUIのツールを使わなければならない時代が長く続いたが、今はシーケンス図ですらコードで管理することができ、ブラウザで描写することが可能な時代になった。

    残念ながら現時点で日本語されていないが、 `Web Sequence Diagrams`  というサービスが便利だったので紹介。

    https://www.websequencediagrams.com/

     どのように書くか

    このように書けば

    title schemaspy-with-sql
    
    PC(MAC)->schemaspy: boot
    PC(MAC)->db: boot
    schemaspy->+db: connection check
    db->db: create table
    db-->-schemaspy: OK
    
    schemaspy->db: scan
    schemaspy->PC(MAC): make output files

    以下のようなシーケンス図が生成される。

    非常にシンプル。コードもなんとなくわかる気がする。

    しかし、無料で使う限りだと非常にシンプルな図しか作れない。月々数十ドル課金することで表現の幅は広がるようだ。

    https://www.websequencediagrams.com/

    編集状況

    Web Sequence Diagrams

     

  • SchemaSpy で SQLファイル からスキーマ情報を出せるようにした

    SchemaSpy で SQLファイル からスキーマ情報を出せるようにした

    tl;dr;

    ER図などを自動生成してくれる SchemaSpy というツールがある。
    このツールは接続先のDBを指定する事でそのDBサーバのスキーマ情報を読み取って解析してくれる作りだ。
    しかし、DBに接続させたくない場合は多い。手元にcreate table文などを盛り込んだDDL文の塊を用意する事で対応できるようにした。

    対処した事

    と言っても SchemaSpy 自体をいじったわけではなく、SQLファイルを手元に用意して、DBのコンテナに読み込ませ、そのコンテナに対して SchemaSpy を叩かせるようにしただけ。

    見えてきた未来

    DBをダイレクトに繋ぎに行かずにすむ事で、ブランチごとにDB構成を管理することができるようになり、CIに組み込めばスキーマのレビューもしやすくなる。

    考えなければならなかったところ

    SchemaSpy が動く前にDBコンテナに適切にデータが作られてないといけない。

    もしテーブルが作られていないと SchemaSpy が空振りしてしまい、期待している結果に至らないからだ。そのため、DBコンテナにテーブルが作られていることを確認してから SchemaSpy が動かす必要がある。

    しかし、docker-compose では、それ自体で細かな起動順番を指定することができない。

    docker 公式ドキュメントでも以下のような記載がある。

    Compose はコンテナの準備が「整う」まで待ちません(つまり、特定のアプリケーションが利用可能になるまで待ちません)。単に起動するだけです。

    http://docs.docker.jp/compose/startup-order.html

    自分で制御するしかないようだ。起動したい順番をシーケンス図でかくとこのような感じ。

    SchemaSpyコンテナ側での制御はシンプルにシェルで行っており、mysqlに繋がったら CREATE TABLE 含むDB側の準備は完了とみなし、SchemaSpy側の実際の処理をするようにした。

    #!/bin/sh
    
    set -e
    echo $@
    
    host=$1
    shift
    user=$1
    shift
    password=$1
    shift
    cmd="java -jar schemaspy.jar"
    
    echo "Waiting for mysql"
    until mysql -h"$host" -u"$user" -p"$password" &> /dev/null
    do
            >$2 echo -n "."
            sleep 1
    done
    
    >&2 echo "MySQL is up - executing command"
    exec $cmd

    https://github.com/GitSumito/schemaspy-with-sql/blob/master/docker/schemaspy/bin/wait.sh

    MySQLに繋がったらtableも作られていると解釈するのは危険ではないかと感じるかもしれない。

    これは  mysql のコンテナの設定を用いてもう少し説明したい。

    version: '3'
    
    services:
      nginx:
        image: nginx:latest
        container_name: schemaspy_nginx
        volumes:
          - ./schemaspy:/var/www/html:ro
          - ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
        ports:
          - "80:80"
        environment:
          - LANG=ja_JP.UTF-8
          - TZ=Asia/Tokyo
    
      schemaspy:
        build: ./docker/schemaspy
        image: treetips/schemaspy-mysql
        container_name: schemaspy
        volumes:
          - ./schemaspy:/app/html:rw
          - ./docker/schemaspy/config/schemaspy.properties:/app/schemaspy.properties:ro
        environment:
          - LANG=ja_JP.UTF-8
          - TZ=Asia/Tokyo
        working_dir: "/app"
        depends_on:
          - mysql
        command: "sh -x /app/bin/wait.sh mysql docker docker employee java -jar schemaspy.jar"
    # also modify db info docker/schemaspy/config/schemaspy.properties
    
      mysql:
        image: mysql:5.7
        container_name: mysql_host
        environment:
          MYSQL_ROOT_PASSWORD: root
          MYSQL_DATABASE: employee
          MYSQL_USER: docker
          MYSQL_PASSWORD: docker
          TZ: 'Asia/Tokyo'
          MYSQL_ALLOW_EMPTY_PASSWORD: "yes"
        command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
        volumes:
          - ./docker/mysql/sql:/docker-entrypoint-initdb.d
        ports:
          - 3306:3306

    https://github.com/GitSumito/schemaspy-with-sql/blob/master/docker-compose.yml

    mysqlのvolumesでは

    `./docker/mysql/sql` を`/docker-entrypoint-initdb.d`へマウントするよう設定している。

    これはMySQLコンテナの仕様で、デフォルトでこのディレクトリの`.sh`ファイル、`.sql`ファイル、 `.sql.gz`ファイル があれば初期化段階で読み込み、MYSQL_DATABASEに指定したデータベースに作られる。

    Initializing a fresh instance

    When a container is started for the first time, a new database with the specified name will be created and initialized with the provided configuration variables. Furthermore, it will execute files with extensions .sh.sql and .sql.gz that are found in /docker-entrypoint-initdb.d. Files will be executed in alphabetical order. You can easily populate your mysql services by mounting a SQL dump into that directory and provide custom images with contributed data. SQL files will be imported by default to the database specified by the MYSQL_DATABASE variable.

    https://hub.docker.com/_/mysql

    念のためdocker-compose をupしてログを確認してみると、`mysqld: ready for connections.`になる前に、実行されている。

    mysql_host   | 2020-01-19 23:12:16+09:00 [Note] [Entrypoint]: Creating database employee
    mysql_host   | 2020-01-19 23:12:16+09:00 [Note] [Entrypoint]: Creating user docker
    mysql_host   | 2020-01-19 23:12:16+09:00 [Note] [Entrypoint]: Giving user docker access to schema employee
    mysql_host   | 
    mysql_host   | 2020-01-19 23:12:17+09:00 [Note] [Entrypoint]: /usr/local/bin/docker-entrypoint.sh: running /docker-entrypoint-initdb.d/001-create-tables.sql
    (中略)
    mysql_host   | 2020-01-19T14:12:20.391989Z 0 [Note] mysqld: ready for connections.
    mysql_host   | Version: '5.7.29'  socket: '/var/run/mysqld/mysqld.sock'  port: 3306  MySQL Community Server (GPL)

    使ってみる

    githubに置いておいたので適宜clone

    git clone https://github.com/GitSumito/schemaspy-with-sql.git
    make build
    make up
    

     その後、自分のPCにhttp接続するとSchemaSpyが作ったアウトプットを見ることができる。schemaspy-1

    ER図はこのような感じ。非常に見やすい。

    なお、今回はMySQLで公開されているemployeeテーブルから一部情報を使って描写した。

    https://github.com/datacharmer/test_db/blob/master/employees.sql

    手取り早く手元のDDLに置き換えたい場合は、

    schemaspy-with-sql/blob/master/docker/mysql/sql/001-create-tables.sql

    ファイルの中身を変更してdocker-composeを起動するだけでSchemaSpyを起動させることができるようにしてある。

    最後に

    Migraiton ファイルをそのまま読み込ませることができればもっと便利になると思うので、次回はその対処を行いたいと思う。

  • Numpy

    Numpy

    はじめに

    `numpy`を使うと複雑な計算ができるようになるが、機会学習でよくつかう計算についてまとめた。

    x転置x

    .dotの後に、掛けるものを2つ置けばよい。

    XtX= np.dot(X.T,X)
    print(XtX)

    逆行列

    linear algebra(リニアアルジェブラ): 線形代数

    numpyの場合は、linalg.invを使って描写する。

    XtX_inv = np.linalg.inv(XtX)
    print(XtX_inv)
  • AWS SQS互換アプリを使ってDockerで検証する

    AWS SQS互換アプリを使ってDockerで検証する

    tl;dr;

    AWS SQSを使ったシステムを作る際、ローカル環境で手軽にSQSがいじれれば非常に便利だ。今回、Dockerを用いてSQS互換の環境を作った。
    docker imageの作り方、localでの起動方法までまとめている。

    docker image作成

    SQSのinterfaceに適合したアプリケーションがある。

    https://github.com/softwaremill/elasticmq

    これをそのままDockerの中に入れ、ポートを開いて`ENTRYPOINT`を起動すれば、SQS互換のdocker imageを作成することができる。

    FROM java:8
    
    ADD https://s3-eu-west-1.amazonaws.com/softwaremill-public/elasticmq-server-0.15.3.jar /elasticmq/elasticmq-server-0.15.3.jar
    EXPOSE 9324
    ENTRYPOINT ["java","-jar","/elasticmq/elasticmq-server-0.15.3.jar"]

    ビルド方法はこちら。

    `docker build -t tsukada/elasticmq:0.15.3 .`

    そのimageを用いて起動する。ローカルの9324 portがコンテナ内の9324 portにそのままマッピングさせた。

    `docker run -d –name=elasticmq -p 9324:9324 tsukada/elasticmq:0.15.3`

    起動確認

    % docker ps
    CONTAINER ID        IMAGE                      COMMAND                  CREATED             STATUS              PORTS                    NAMES
    cb1023027390        tsukada/elasticmq:0.15.3   "java -jar /elasticm…"   3 seconds ago       Up 2 seconds        0.0.0.0:9324->9324/tcp   elasticmq

    起動成功。localhost の 9324 portとのマッピングも成功した。

    使い方

    キューの作成

    aws sqs create-queue --queue-name test --endpoint-url http://localhost:9324
    
    {
        "QueueUrl": "http://localhost:9324/queue/test"
    }

    ここで表示される`QueueUrl`が、メッセージの送受信などで必須情報となる。

    メッセージ送信

    作成したキューにメッセージを送信する。

    `–queue-url` オプションが必要になる。ここではキューを作成した際に作られた`QueueUrl`を入力する。

    aws sqs send-message --queue-url http://localhost:9324/queue/test --message-body 'tsukada hoge. Hello world' --endpoint-url http://localhost:9324
    
    
    {
        "MD5OfMessageBody": "c5ec7b6e06ad8b6faf949efb02e2f406", 
        "MessageId": "83a36e5f-1d40-4da8-84eb-b37a8c864541"
    }

    送信が成功すると、メッセージ本文の MD5 ダイジェストとメッセージ ID が表示される。

    メッセージを受信する

    aws sqs receive-message --queue-url http://localhost:9324/queue/test --endpoint-url http://localhost:9324
    
    {
        "Messages": [
            {
                "Body": "tsukada hoge. Hello world ", 
                "ReceiptHandle": "83a36e5f-1d40-4da8-84eb-b37a8c864541#58efa4b9-8fe9-4324-aded-08b72253be1c", 
                "MD5OfBody": "c5ec7b6e06ad8b6faf949efb02e2f406", 
                "MessageId": "83a36e5f-1d40-4da8-84eb-b37a8c864541"
            }
        ]
    }

    メッセージで送信された

    tsukada hoge. Hello world

    を確認できた。
    ちなみに、このコマンドを複数回実行しても、毎回メッセージが表示されるわけではない。

    SQSには可視性タイムアウトという考えがあり、デフォルトで1度キューを表示したら、30秒間は表示されない。

    この仕組みで、重複してキューが実行されるのを防いでいる。

    そのような仕様だと、「見えているキュー、見えてないキュー」の件数を確認したくなる。

    見ているキュー、見えていないキューの件数を確認したい

     見えているキュー(キューから取得可能なメッセージのおおよその数)

    `ApproximateNumberOfMessages` が見えている件数

    aws sqs get-queue-attributes --endpoint-url http://localhost:9324 --queue-url http://localhost:9324/queue/test --attribute-names ApproximateNumberOfMessages
    {
        "Attributes": {
            "ApproximateNumberOfMessages": "1"
        }
    }

    見えてないキュー(処理されたメッセージ)

    `ApproximateNumberOfMessagesNotVisible`を `–attribute-names`に指定することで見えなくなっているキューの件数を表示することができる。

    aws sqs get-queue-attributes --endpoint-url http://localhost:9324 --queue-url http://localhost:9324/queue/test --attribute-names ApproximateNumberOfMessagesNotVisible
    {
        "Attributes": {
            "ApproximateNumberOfMessagesNotVisible": "0"
        }
    }

    これらのオプションについては公式ドキュメントで綺麗に整理されている。

    https://docs.aws.amazon.com/ja_jp/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-resources-required-process-messages.html

    メッセージを削除する

    不要になったメッセージは削除した方が良い。削除コマンドは以下の通り。

    aws sqs delete-message  --endpoint-url http://localhost:9324 --queue-url http://localhost:9324/queue/test --receipt-handle "****"

    `–receipt-handle` には、メッセージを受信した際に記載されている`ReceiptHandle`を指定する。

    receive-message (メッセージを受信)した際、した際に確認できる ReceiptHandle はこのような感じに表示される。`.jq` コマンドなどを利用すれば簡単にシェル化できそうだ。

    QueueUrlsを確認する

    QueueUrlsを確認できるのはもちろんキュー作成時だけではなく、確認用のコマンドも用意されている。

    aws sqs list-queues --endpoint-url http://localhost:9324
    {
        "QueueUrls": [
            "http://localhost:9324/queue/test"
        ]
    }

    参考情報

    https://qiita.com/tsukapah/items/0ba64ac734e885f6e8e8

    https://qiita.com/tcsh/items/7781fe238df82fc030d2

    参考情報

    AWSについて体系的に学ぶことができるのでおすすめ。

  • laravelでシンプルなformを作りMySQLに保存した

    laravelでシンプルなformを作りMySQLに保存した

    tl;dr;

    極限まで削り、ただtextを受け取りDBに格納するだけの処理。
    laravelでシンプルなformを作りMySQLに保存した。(ただの備忘録)

    view(入力画面)

     ソースは以下の通り。

    <head>
      <meta charset="utf-8">
      <!-- css をあてる際は以下のようにcssを読み込む-->
      <link rel="stylesheet" href="/css/style.css">
    </head>
    
    <form method="post" action="{{ url('/add') }}">
        {{ csrf_field() }}
        <p>
          <input type="text" name="title" placeholder="enter title">
        </p>
        <p>
          <textarea name="body" placeholder="enter body"></textarea>
        </p>
        <p>
          <input type="submit" value="Add">
        </p>
      </form>

    読み込むcss。今回は以下のようにした。

      input[type="text"], textarea {
        box-sizing: border-box;
        width: 500px;
        padding: 5px;
      }
      
      textarea {
        height: 100px;
      }

    router

    view(入力画面)と、そこからsubmitする宛先(post)を以下の通り設定した。

    両方とも PostsController  クラスを呼び、それぞれのメソッドを紐付けるようにした。

    <?php
    Route::get('/information', 'PostsController@create');
    Route::post('/add', 'PostsController@add');
    

     

    Model

    データベースに保存するハコ。

    `protected fillable`  として、DBへ保存できるようにした。

    <?php
    
    namespace App;
    
    use Illuminate\Database\Eloquent\Model;
    
    class Post extends Model
    {
        //
        protected $fillable = ['title', 'body'];
    }
    

    Controllers

    クライアントからの Post  は request 型で送られてくる。
    そのため、それぞれ適宜抜き出し、Modelに入れ、`save()`  することでDBへ保存されるようにした。

    <?php
    
    namespace App\Http\Controllers;
    use Illuminate\Http\Request;
    
    use App\Post;
    
    
    class PostsController extends Controller
    {
        public function create() {
            return view('information.create');
          }
    
          // request型で送られてくる
        public function add(Request $request) {
    
            $post = new Post();
            $post->title = $request->title;
            $post->body = $request->body;
            $post->save();
    
            return redirect('/information');
        }
    
    }

    以上。

    動作確認

    無事登録が完了

     

    laravel の実践向け書籍

  • 手動で作った lambda を code として管理する

    手動で作った lambda を code として管理する

    tl;dr;

    既存の lambda をダウンロードし、コンソールを使わず sam を用いて管理する方法をまとめた。deploy 方法から注意事項までを記載。

    既存 lambda をダウンロード

     lambda をダウンロードすると大きく2つに分けてダウンロードすることができる。

    sam のドキュメントについてはこちら

    関数のエクスポートを押下すると、

     

    • AWS SAM ファイルのダウンロード
    • デプロイパッケージのダウンロード

    が選択できる。

    端的に言うと

    • lambda の設定ファイル
    • プログラムのコード

    だ。両方ダウンロードする。

    sam のセットアップ

    公式ドキュメントの手順を準拠すると、 sam のインストールは以下の通りとなる。

    brew tap aws/tap
    brew install aws-sam-cli

    deploy ディレクトリの初期化

    今回は python3.8 を利用するので以下のように設定した。
    ダウンロードした zipファイル は unzipコマンドなどで解凍しておく。

    sam init
    
    
    	SAM CLI now collects telemetry to better understand customer needs.
    
    	You can OPT OUT and disable telemetry collection by setting the
    	environment variable SAM_CLI_TELEMETRY=0 in your shell.
    	Thanks for your help!
    
    	Learn More: https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-telemetry.html
    
    Which template source would you like to use?
    	1 - AWS Quick Start Templates
    	2 - Custom Template Location
    Choice: 1
    
    Which runtime would you like to use?
    	1 - nodejs12.x
    	2 - python3.8
    	3 - ruby2.5
    	4 - go1.x
    	5 - java11
    	6 - dotnetcore2.1
    	7 - nodejs10.x
    	8 - nodejs8.10
    	9 - python3.7
    	10 - python3.6
    	11 - python2.7
    	12 - java8
    	13 - dotnetcore2.0
    	14 - dotnetcore1.0
    Runtime: 2
    
    Project name [sam-app]: 
    
    Cloning app templates from https://github.com/awslabs/aws-sam-cli-app-templates.git
    
    AWS quick start application templates:
    	1 - Hello World Example
    	2 - EventBridge Hello World
    	3 - EventBridge App from scratch (100+ Event Schemas)
    Template selection: 1
    
    -----------------------
    Generating application:
    -----------------------
    Name: sam-app
    Runtime: python3.8
    Dependency Manager: pip
    Application Template: hello-world
    Output Directory: .
    
    Next steps can be found in the README file at ./sam-app/README.md
        
    coco@darkenagy sam % 

    ダウンロードした yamlファイル を、 template.yml と言うファイル名に変更する。

    mv test-imagemagick.yaml template.yml 

    requirements.txt も必要になるので、作成する。使わない場合でも空のファイルを用意する必要があるので、 touch するなり作成する。

    touch requirements.txt

    以下のように表示されれば成功。

    sam build
    Building resource 'testimagemagick'
    Running PythonPipBuilder:ResolveDependencies
    Running PythonPipBuilder:CopySource
    
    Build Succeeded
    
    Built Artifacts  : .aws-sam/build
    Built Template   : .aws-sam/build/template.yaml
    
    Commands you can use next
    =========================
    [*] Invoke Function: sam local invoke
    [*] Deploy: sam deploy --guided
        

    ちなみに、sam コマンドでも、.aws 配下の profile を読み込むことも可能。

    `sam build –profile cloudformation` 

    エラーが発生した場合は debugモード で確認することができる。

    sam build --debug

    あとは invoke するなり、deploy するなりすればよい。

    deploy

    `sam deploy –guided`コマンドを利用することで deploy が可能。

    % sam deploy --guided
    
    Configuring SAM deploy
    ======================
    
    	Looking for samconfig.toml :  Not found
    
    	Setting default arguments for 'sam deploy'
    	=========================================
    	Stack Name [sam-app]: 
    	AWS Region [us-east-1]: ap-northeast-1
    	#Shows you resources changes to be deployed and require a 'Y' to initiate deploy
    	Confirm changes before deploy [y/N]: y
    	#SAM needs permission to be able to create roles to connect to the resources in your template
    	Allow SAM CLI IAM role creation [Y/n]: y
    	Save arguments to samconfig.toml [Y/n]: y
    
    	Looking for resources needed for deployment: Found!
    
    		Managed S3 bucket: aws-sam-cli-managed-default-samclisourcebucket-1dc492sz2bqu7
    		A different default S3 bucket can be set in samconfig.toml
    
    	Saved arguments to config file
    	Running 'sam deploy' for future deployments will use the parameters saved above.
    	The above parameters can be changed by modifying samconfig.toml
    	Learn more about samconfig.toml syntax at 
    	https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-config.html
    
    	Deploying with following values
    	===============================
    	Stack name                 : sam-app
    	Region                     : ap-northeast-1
    	Confirm changeset          : True
    	Deployment s3 bucket       : aws-sam-cli-managed-default-samclisourcebucket-1dc492sz2bqu7
    	Capabilities               : ["CAPABILITY_IAM"]
    	Parameter overrides        : {}
    
    Initiating deployment
    =====================
    Uploading to sam-app/9a97ece5eb5fac3dd9dff84a97110722  9611 / 9611.0  (100.00%)
    Uploading to sam-app/cac8e2252620a466ff8e4becb2d4a8ed.template  638 / 638.0  (100.00%)
    
    Waiting for changeset to be created..
    
    CloudFormation stack changeset
    ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
    Operation                                                           LogicalResourceId                                                   ResourceType                                                      
    ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
    + Add                                                               testimagemagick                                                     AWS::Lambda::Function                                             
    ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
    
    Changeset created successfully. arn:aws:cloudformation:ap-northeast-1:821594579130:changeSet/samcli-deploy1578156109/01d374f5-800c-438e-b4cb-4f25d8d0bc4d
    
    
    Previewing CloudFormation changeset before deployment
    ======================================================
    Deploy this changeset? [y/N]:

    問題なければyを押す。

    2020-01-05 01:42:01 - Waiting for stack create/update to complete
    
    CloudFormation events from changeset
    ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
    ResourceStatus                                     ResourceType                                       LogicalResourceId                                  ResourceStatusReason                             
    ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
    CREATE_IN_PROGRESS                                 AWS::Lambda::Function                              testimagemagick                                    -                                                
    CREATE_IN_PROGRESS                                 AWS::Lambda::Function                              testimagemagick                                    Resource creation Initiated                      
    CREATE_COMPLETE                                    AWS::Lambda::Function                              testimagemagick                                    -                                                
    CREATE_COMPLETE                                    AWS::CloudFormation::Stack                         sam-app                                            -                                                
    ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
    
    Successfully created/updated stack - sam-app in ap-northeast-1
    

    確認

    成功すれば以下の通り作成される。

    function name が、意図した名前と違う場合、明示的に指定することができる。

    AWSTemplateFormatVersion: '2010-09-09'
    Transform: 'AWS::Serverless-2016-10-31'
    Description: An AWS Serverless Specification template describing your function.
    Resources:
      testimagemagick:
        Type: 'AWS::Serverless::Function'
        Properties:
          FunctionName: test-imagemagick-123
          Handler: lambda_function.lambda_handler
          Runtime: python3.8
          CodeUri: .
          Description: ''
          MemorySize: 128
          Timeout: 3
          Role: 'arn:aws:iam::123:role/lambdaExecution'
          Layers:
            - 'arn:aws:lambda:ap-northeast-1:123:layer:image-magick:2'

    template.yml で`FunctionName`を指定する。

    再度`sam build`、 `sam deploy` を行うと、新しい lambda function に置き換わる。

    注意点

    手動で作り上げた lambda を sam を用いて上書いて管理することはできないのか。
    結論、そのままはできない。

    現在自分が動かしているものについては、手動で作った lambda は削除し、その後 sam を用いたdepoloy に切替えるなどして対応している。

     

    https://dev.classmethod.jp/cloud/aws/cloudformation-import-existing-resources/

     

    しかし、CloudForomation で手動で作成したリソースをインポートできるようになったので、これを使えばできるかも。(未確認)

    試したことある人いたら twitter でもご一報ください。

     

  • AWS lambda(Python 3.8)でimagemagickを使う

    AWS lambda(Python 3.8)でimagemagickを使う

    tl;dr;

    AWS lambda(Python 3.8)で画像処理ツールのimagemagickを動かした。

    install方法からlambda内でimagemagickの動かす際のコードまでを紹介。

    imagemagickとは

    imagemagick は動的に画像を編集する古典的なツール。こちらの記事によると、なんと1987年から開発されている。wikipediaを確認する限り、Photoshopのリリースが1990年だから、その古さに驚かされる。

    しかしこのツール、度重なる脆弱性は報告されているものの、画像編集の機能としてまだまだ現役として使っているプロジェクトも存在する。

    今回はそのimagemagickをlambdaで。Python 3.8のランタイムから使う方法を紹介する。

    Serverless.Pub

    ありがたい事にServerlessを支援し積極的に情報発信をしてくれている団体がいる。

    彼らが作ったlambdaの`binally layer`をCloudFormationでdeployする機能も提供してくれている。

    https://serverless.pub/lambda-utility-layers/

    GitHubはこちら

    https://github.com/serverlesspub/imagemagick-aws-lambda-2

    こちらを`git clone`して、`make all` すればimagemagickのlambdaの`binally layer`ができる。

    その方法を使ってもよいのだが、
    実は以下のページからDeployボタンを押せば、それだけで自動でCloudFormationが自分のAWSで動き、lambdaの中に`binally layer`が作成されることもできる。

    https://serverlessrepo.aws.amazon.com/applications/arn:aws:serverlessrepo:us-east-1:145266761615:applications~image-magick-lambda-layer

    しかし、注意すべきはここの部分。

    要約すると、nodejs10.xランタイムのようなAmazon Linux 2で使えるよ。とのこと。PythonのPの字も見当たらない。
    対象外かと思い当初諦めていた。

    そんな中、最近、`Python 3.8`ランタイムがlambdaで使えるようになった事を知り、公式ドキュメントを読んでみると、今までのPythonで使っていたOSとは異なり、`Amazon linux2`がベースとなっていることがわかった。

    node.jsに絞っているのはAmazonLinux2がベースのように見受けられたため、これはlambdaの中でPythonでもimagemagickを動かせるかもと思い今回試す事にした。

    やってみた

    https://serverlessrepo.aws.amazon.com/applications/arn:aws:serverlessrepo:us-east-1:145266761615:applications~image-magick-lambda-layer

    を開きDeployボタンを押す。
    しかし、この時点でいきなりDeployされるわけではない。

    DeployはCloudFormationによって行われる。CloudFormationのスタック名確定させたら(デフォルトでもよい)デプロイを押下。

    しばらくすると、AWS LambdaのLayersに新たに`image-magick`が登録される。

    Lambda関数から、Layersを選択する

    レイヤーの追加ボタンを押し、imagemagickを選択する。

    レイヤーに表示されない場合は、以下のようにARNを直接入力する。

    これで下準備は完了

    AWS lambda(Python 3.8)でimagemagickを使う

     ここからがPythonからimagemagickを使えるかの確認。

    今回はシンプルにversionを表示させることで使える事にしたい。

    コードはこちら。

    import json
    import subprocess
    
    def lambda_handler(event, context):
        subprocess.check_call(['convert', '-version'])
      
        return {
            'body': json.dumps('Hello from Lambda!')
        }
    

    subprocessを使って、Pythonから、lambdaが動いているOS側のコマンドを実行する。

    imagemagickをinstallすると使えるようになる`convert`コマンドを叩く。

     

    結果は以下の通り

    無事imagemagickのversionを確認できた。

    最後に

    私が実施した際はレイヤーの追加ボタンを押しても選択肢としてimagemagickが表示されなかった。

    これは、CloudFormationによって作られるimagemagickのレイヤーがnode.jsに限定しているため。不便なのでpull reqを出してmergeしてもらった。

    https://github.com/serverlesspub/imagemagick-aws-lambda-2/pull/18/files

    今はレイヤー選択画面から互換性のあるレイヤーとして選択できるはずだ。

     

  • Cloud functionsを使ってCloud Storageに画像がuploadされるとサムネイルを作成する

    Cloud functionsを使ってCloud Storageに画像がuploadされるとサムネイルを作成する

    tl;dr;

    Cloud Storageにuploadされると、イベントフックでCloud functionsが実行されサムネイル作成処理が走る処理を作った。

    事前準備

    gcloudコマンドのインストール。既に設定済ならスキップを。

    https://cloud.google.com/storage/docs/gsutil_install?hl=ja

    バケットの作成。今回は変換対象、変換後の格納先を作成。

    export YOUR_INPUT_BUCKET_NAME=tsukada-input
    gsutil mb gs://$YOUR_INPUT_BUCKET_NAME
    
    export YOUR_OUTPUT_BUCKET_NAME=tsukada-output
    gsutil mb gs://$YOUR_OUTPUT_BUCKET_NAME

    deploy

    sample productをダウンロード。deploy。

    mkdir project
    cd project
    git clone https://github.com/GitSumito/cloudfunctions-imagemagick-on-gcp
    cd cloudfunctions-imagemagick-on-gcp
    
    # deploy
    gcloud functions deploy ImageConvert --runtime go111 --trigger-bucket $YOUR_INPUT_BUCKET_NAME --set-env-vars THUMBNAILED=$YOUR_OUTPUT_BUCKET_NAME

    `gcloud functions deploy`の後ろは、実行する関数名を入力する。

    また、引数として `(ctx context.Context, e GCSEvent)` を受け付ける必要があるので注意。

    Cloud Functionsのコマンドでは予め`–trigger-bucket`というオプションが用意されていて、任意のバケットを指定すれば簡単にイベント処理を紐付けることができる。便利。

    deployコマンドを実行すると

    ` Deploying function (may take a while – up to 2 minutes)…⠼     `

     と表示され、しばらく待つ。

    result

    左がアップロードしたオリジナルの画像。

    右側がEventを検知して、Cloud Functionsが実行され、サムネイル作成された画像。

    使ってみると非常に簡単にイベント駆動処理を作ることができた。使い方次第では活躍しそう。