投稿者: sumito.tsukada

  • FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed – JavaScript heap out of memory

    FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed – JavaScript heap out of memory

    はじめに

    redash v8 のリポジトリを clone して コンテナを build しようとすると、

    ` FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed – JavaScript heap out of memory `

    というエラーになった。

    FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory
    
    Writing Node.js report to file: report.20200305.111353.33.0.001.json
    Node.js report completed
     1: 0x9dab70 node::Abort() [node]
     2: 0x9dbd26 node::OnFatalError(char const*, char const*) [node]
     3: 0xb3b18e v8::Utils::ReportOOMFailure(v8::internal::Isolate*, char const*, bool) [node]
     4: 0xb3b509 v8::internal::V8::FatalProcessOutOfMemory(v8::internal::Isolate*, char const*, bool) [node]
     5: 0xce68a5  [node]
     6: 0xce6f36 v8::internal::Heap::RecomputeLimits(v8::internal::GarbageCollector) [node]
     7: 0xcf2dca v8::internal::Heap::PerformGarbageCollection(v8::internal::GarbageCollector, v8::GCCallbackFlags) [node]
     8: 0xcf3cd5 v8::internal::Heap::CollectGarbage(v8::internal::AllocationSpace, v8::internal::GarbageCollectionReason, v8::GCCallbackFlags) [node]
     9: 0xcf66e8 v8::internal::Heap::AllocateRawWithRetryOrFail(int, v8::internal::AllocationType, v8::internal::AllocationAlignment) [node]
    10: 0xcbd017 v8::internal::Factory::NewFillerObject(int, bool, v8::internal::AllocationType) [node]
    11: 0xff307b v8::internal::Runtime_AllocateInYoungGeneration(int, unsigned long*, v8::internal::Isolate*) [node]
    12: 0x1376259  [node]
    Aborted (core dumped)
    npm ERR! code ELIFECYCLE
    npm ERR! errno 134
    npm ERR! redash-client@9.0.0-alpha build: `npm run clean && NODE_ENV=production webpack`
    npm ERR! Exit status 134
    npm ERR! 
    npm ERR! Failed at the redash-client@9.0.0-alpha build script.
    npm ERR! This is probably not a problem with npm. There is likely additional logging output above.
    ERROR: Service 'server' failed to build: The command '/bin/sh -c npm run build' returned a non-zero code: 134

    原因

    マシンのメモリーが足りない。

    https://redash.io/help/open-source/dev-guide/docker

    v8 のイメージをbuild するには最低限 4G のメモリーが必須となるようだ。

    ビルドするマシンのメモリーを増やすことによって解決できた。

     

     

     

  • ERROR: (gcloud.auth.application-default.print-access-token) The Application Default Credentials are not available.

    ERROR: (gcloud.auth.application-default.print-access-token) The Application Default Credentials are not available.

    はじめに

    gcloud コマンドで key を取得しようとした際、The Application Default Credentials are not available.と出た時の対処法についてまとめます。

    原因と対処

    default のkeyが読み込めていないことが濃厚です。
    対処法としては、環境変数でgcp の key の場所を定義するだけでパスすることができます。

    詳細

    % curl -X POST \
    -H "Authorization: Bearer "$(gcloud auth application-default print-access-token) \
    -H "Content-Type: application/json; charset=utf-8" \
    -d @request.json \
    https://translation.googleapis.com/language/translate/v2
    ERROR: (gcloud.auth.application-default.print-access-token) The Application Default Credentials are not available. They are available if running in Google Compute Engine. Otherwise, the environment variable GOOGLE_APPLICATION_CREDENTIALS must be defined pointing to a file defining the credentials. See https://developers.google.com/accounts/docs/application-default-credentials for more information.
    {
      "error": {
        "code": 403,
        "message": "The request is missing a valid API key.",
        "errors": [
          {
            "message": "The request is missing a valid API key.",
            "domain": "global",
            "reason": "forbidden"
          }
        ],
        "status": "PERMISSION_DENIED"
      }
    }

    `gcloud auth application-default print-access-token`  がうまく動いていないようですね。

    gcloud auth application-default print-access-token

    ERROR: (gcloud.auth.application-default.print-access-token) The Application Default Credentials are not available. They are available if running in Google Compute Engine. Otherwise, the environment variable GOOGLE_APPLICATION_CREDENTIALS must be defined pointing to a file defining the credentials. See https://developers.google.com/accounts/docs/application-default-credentials for more information.

    環境変数 `GOOGLE_APPLICATION_CREDENTIALS` に gcp の key を登録します。

    export GOOGLE_APPLICATION_CREDENTIALS=~/.ssh/gcp.json

     再度 `gcloud auth application-default print-access-token` を叩くとtoken を取得することができます。

     

  • redash API で パラメータを動的に変えてクエリを受け付ける

    redash API で パラメータを動的に変えてクエリを受け付ける

    tl;dr;

    redash API で パラメータを動的に変えてクエリを受け付ける方法を紹介します。

    サードパーティを使わず行います。今回の version は redash v6ですがおそらくそれ以降でも可能かと思います。

    手順をご紹介する前の前提条件

    私の環境では検証環境としてすぐ起動できる redash の環境を利用します。

    # git clone
    git clone https://github.com/GitSumito/redash-blue.git
    cd redash-blue
    
    # DB 初期化
    docker-compose run --rm server create_db docker-compose up
    
    # 起動
    docker-compose up -d  

    http://127.0.0.1/ へ接続し、データソースの設定などを適宜行います。

    DBのcredential 情報などは下記をご覧ください。

    https://github.com/GitSumito/redash-blue/blob/master/README.md

     

    動的クエリを作成する

    その後、 Create -> Query に移動し、変数を受け付けるカラムを書きます。

    この場合はクエリを code という引数で受け取ります。

     code に JPN といれると、redash が where ContryCode = "JPN"と組み立ててくれる仕組みです。

    これをブラウザからではなく、RestFull API で行うようにするというのが今回の趣旨です。

    作成した Query は publish してください、

    事前準備

    ユーザーのAPI keyを確認します。

    http://127.0.0.1/users/1

    を開けばAPI keyを確認することができます。

    このAPI keyを確認します。

    ジョブを登録する

    引数は p_xxxx というパラメータ名で受け付けるようです。
    code という引数を設定したので、今回は p_code というパラメータ名になりました。

    これを POST で送ります。curl コマンドで行うとやりやすいかと思います。

    curl --request POST \
    --url 'http://127.0.0.1/api/queries/1/refresh?p_code=IND' \
    --header 'Authorization: Key ajxxxxxxxxxxxxxxx'

    受け付けられると、 以下のようなレスポンスが返ってきます。

    ID を控えてください。今回登録したジョブを、以下のIDを用いて確認することができます。

    query_result_id を確認する

    ジョブのIDをパスパラメータとして渡します。

    また、api_keyを後ろにつけてください。

    curl "http://127.0.0.1/api/jobs/93452aa6-8569-43fc-b816-cb441ce80e03?api_key=ajxxxxxxxxxxxxxxx"

    query_result_id が返ってきます。こちらを用いて結果を確認します。

    query_result_id を用いて結果を確認する

     

    curl -s -H 'Authorization:Key ajxxxxxxxxxxxxxxx' -H 'Accept:application/json' -H 'Content-Type:application/json' http://127.0.0.1/api/query_results/8
    

    以下の通り、ContryCode `IND`の情報を取得することができました。

    今回の version は redash v6ですがおそらくそれ以降でも可能かと思います。

  • ECS Scheduling tasks の FailedInvocations を対処する

    ECS Scheduling tasks の FailedInvocations を対処する

    tl;dr;

    ECS のタスクスケジューリングで

    FailedInvocations が発生するのは、CloudWatch eventsでの実行ロールに権限が足りないから。その対処方法をまとめる。

    IAM ロール

    I AM ロールを作成する。

    今回は ecsEventsRole という名前にした。

    作成したロールの信頼関係を編集。

    cloudwatch events から実行できるよう権限を付与。

    {
      "Version": "2012-10-17",
      "Statement": [
        {
          "Sid": "",
          "Effect": "Allow",
          "Principal": {
            "Service": "events.amazonaws.com"
          },
          "Action": "sts:AssumeRole"
        }
      ]
    }

    以下の通り貼り付ける。

    作成したroleに`AmazonEC2ContainerServiceEventsRole` ポリシーをアタッチする。

    ECS task scheduleで作成したロールをアタッチ

    今回作成した ecsEventsRole を割り当てる。

    動作確認

    CloudWatch メトリクスを確認すると、

    FailedInvocations が解消し Invocations になった。

     

  • 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について体系的に学ぶことができるのでおすすめ。