ようへいの日々精進XP

よかろうもん

2017 年 06 月 21 日(水)

ジョギング

  • 香椎浜 x 2 周
  • ずーっと、右太ももから股関節にかけて違和感、体固い

2017 年 06 月 20 日(火)

ジョギング

  • 香椎浜 x 2 周
  • 引き続き、右太ももから股関節にかけて違和感、体固い

日課

  • 今日は無し

仕事で

諸々ミスが目立った一日だった。

気を引き締めんといかんばい。

2017 年 06 月 19 日(月)

ジョギング

  • 香椎浜 x 2 周
  • 右太ももから股関節にかけて違和感、体固い

日課

  • (腕立て x 30 + 腹筋 x 30) x 3

一気に暑くなって

辛い。

2017 年 06 月 18 日(日)

抜けるような

青空。

ジョギング

日課

  • (腕立て x 30 + 腹筋 x 30) x 3

AWS Batch が

東京リージョンで使えるようになった…

ので、従来 EC2 で行っていたような cron をトリガとした夜間バッチの置き換えになるのではと思って資料を読んだりしてみた。

www.slideshare.net

aws.typepad.com

夜間バッチ

夜間バッチの置き換えになるのではという期待を込めた視点では…

  • 現時点では残念ながら cron をトリガとするような夜間バッチの置き換えにはならない
  • AWS Batch はユーザが任意に投入したジョブをキューイングして、 リソース状況に合わせて、効率良く実行するシステム

ということで、夜間バッチの置き換えは意図したサービスではないことが判明。Data Pipeline の方が良さそう(今のところ)。

その他

  • コンピュート環境(実際に処理を行う部分)には Docker イメージを利用するとのことなので、Docker イメージ内に必要なツールやスクリプトや設定等を予め組み込んでおく必要がある

奥さんと

博多デート。

たまたま二人の意見が一致して夕飯は手羽先を食べる。

お店の名前は忘れてしまったけど、塩焼きがとても美味しかった。

2017 年 06 月 17 日(土)

ジョギング

日課

  • (腕立て x 30 + 腹筋 x 30) x 3

義父の四十九日法要

石原裕次郎の「わが人生に悔いなし」を聴きながら。

f:id:inokara:20170618083159j:plain

四十九日の意味についてググってみたら…

四十九日は、初七日から七日ごとに受けたお裁きにより来世の行き先が決まるもっとも重要な日で、「満中陰(まんちゅういん)」と呼ばれます。

故人にとっても、家族にとっても重要な日になるらしい。

そんな日に我々夫婦は小さな写真立てに義父の写真を飾り、オレンジ色のバラの花を一輪挿しに飾り、自宅から手を合わせた。

きっと、義父のことだから閻魔大王にも見初められて極楽浄土に途中下車無しでたどり着かれんたんではないかと思う。

合掌。

夕飯

自宅近くの回っていないお寿司屋さんに奥さんと義父も一緒に。

f:id:inokara:20170618083102j:plain

義父が大好きだったという「あげ巻貝」とビールや日本酒を頂く。

たまたまカウンターの横に座ったさんちゃんとゆーこさん、大将も交えて色々と人生について語る。

大将が慰めてくれるように言った、

葬式や仏事ってさ、生きてる人間の自己満足なんだよ、だから形なんてどーでもいいんだよ

って言いながら義父にお酒を頂いたのがすごく嬉しくて心が救われた気がした。

初めて暖簾をくぐったお店だったのに、義父が導いてくれたのかとても楽しく記憶に残る一日になった。

Elixir と Maru を使って REST API アプリケーションを丸っと 10 分くらいで実装する(写経する)

ちょっと

REST API で操作出来たら便利になりそうだなあと思った部分があったので、maru で実装してみようと思って、Maru ドキュメントの Basic Usage を写経してみたメモです。

◯ (Maru)

github.com

先日の fukuoka.ex で教えて頂きました。

www.slideshare.net

特徴については、上記資料の 6 ページに簡潔に纏まっていますので転載させて頂きます。

https://image.slidesharecdn.com/maru-170611034513/95/elixir-maru-rest-api-6-638.jpg?cb=1497152754

有難うございます。

Rubygrape という REST API ライクな API を生成するフレームワークインスパイヤされているらしい。grape 自体初耳でした。

Basic Usage 写経

写経した環境

$ sw_vers
ProductName:    Mac OS X
ProductVersion: 10.11.6
BuildVersion:   15G1421

$ iex -v
Erlang/OTP 19 [erts-8.3] [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]

IEx 1.4.4

新規アプリケーションの作成

$ mix new oreno_app

以下のように出力されます。

$ mix new oreno_app
* creating README.md
* creating .gitignore
* creating mix.exs
* creating config
* creating config/config.exs
* creating lib
* creating lib/oreno_app.ex
* creating test
* creating test/test_helper.exs
* creating test/oreno_app_test.exs

Your Mix project was created successfully.
You can use "mix" to compile it, test it, and more:

    cd oreno_app
    mix test

Run "mix help" for more commands.

mix.exs に依存するモジュール名を定義

mix.exs に以下を追記する。

defp deps do
  [ {:maru, "~> 0.11"} ]
end

アプリケーションの実装

lib/oreno_app.ex に以下のように実装します。

defmodule OrenoApp.Router.Homepage do
  use Maru.Router

  get do
    json(conn, %{ hello: :maru })
  end
end

defmodule OrenoApp.API do
  use Maru.Router

  plug Plug.Parsers,
    pass: ["*/*"],
    json_decoder: Poison,
    parsers: [:urlencoded, :json, :multipart]
  
  mount OrenoApp.Router.Homepage

  rescue_from :all do
    conn
    |> put_status(500)
    |> text("Server Error")
  end
end

アプリケーションを起動させる為の設定

config/config.exs に以下を追記します。

config :maru, OrenoApp.API,
  http: [ip: {0, 0, 0, 0}, port: 8881]

ip: {0, 0, 0, 0} を追加しない場合、デフォルトでは 127.0.0.1 でのみ Listen するので Docker で動かす場合等は注意しませう。

アプリケーションの起動

依存するモジュールの導入 (maru の導入)

$ mix deps.get

以下のように出力されます。

$ mix deps.get
Running dependency resolution...
Dependency resolution completed:
  cowboy 1.1.2
  cowlib 1.0.2
  maru 0.11.4
  mime 1.1.0
  plug 1.3.5
  poison 3.1.0
  ranch 1.3.2
* Getting maru (Hex package)
  Checking package (https://repo.hex.pm/tarballs/maru-0.11.4.tar)
...

サーバーの起動

$ iex -S mix

以下のように出力され、コンパイルが走ります。

$ iex -S mix
Erlang/OTP 19 [erts-8.3] [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]

==> poison
Compiling 4 files (.ex)
Generated poison app
===> Compiling cowlib
src/cow_multipart.erl:392: Warning: crypto:rand_bytes/1 is deprecated and will be removed in a future release; use crypto:strong_rand_bytes/1
...

09:40:54.442 [info]  Starting Elixir.OrenoApp.API with Cowboy on http://0.0.0.0:8881
Interactive Elixir (1.4.4) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)>

Cowboy という Web サーバーが起動します。

Hello Maru

Web サーバーが起動したら API にアクセスしてみる。

$ curl http://127.0.0.1:8881 -w '\n' -i
HTTP/1.1 200 OK
server: Cowboy
date: Sat, 17 Jun 2017 01:02:15 GMT
content-length: 16
cache-control: max-age=0, private, must-revalidate
content-type: application/json; charset=utf-8

{"hello":"maru"}

やったー。

Basic Usage + アルファ

プラスアルファ

  • Docker で動かしたい

Dockerfile

mix deps.get 等がそのまま利用出来ないので焦ったものの、以下のような Dockerfile で一通り動かせることを確認しました。

ROM elixir
ADD ./ /app
WORKDIR /app
RUN mix local.hex --force && mix deps.get
EXPOSE 8881
CMD ["mix", "run", "--no-halt"]

あと、フォアグラウンドで Web サーバーを動かす方法についても悩みましたが、mix run --no-halt でいけるようです。

docker-compose.yml

version: '2'

services:
  hello_maru:
    build: ./
    volumes:
      - ./:/app
    ports:
      - "8881:8881"

Docker 環境でも Hello Maru

ビルドして…

$ docker-compose build
Building hello_maru
Step 1/6 : FROM elixir
 ---> 066128183b87
Step 2/6 : ADD ./ /app
 ---> f5e8affa64c7
Removing intermediate container 6cde27f8f51f
Step 3/6 : WORKDIR /app
 ---> 9c269ced9d4f

...

Removing intermediate container 01de153aa0ac
Successfully built 8cadb37872f7

Run すると…

$ docker-compose up -d
Creating network "orenoapp_default" with the default driver
Creating orenoapp_hello_maru_1

$ docker ps
CONTAINER ID        IMAGE                     COMMAND                  CREATED             STATUS              PORTS                                            NAMES
0ecfa2a6d96b        orenoapp_hello_maru       "mix run --no-halt"      11 seconds ago      Up 9 seconds        0.0.0.0:8881->8881/tcp                           orenoapp_hello_maru_1

Hello Maru.

$ curl http://127.0.0.1:8881 -w '\n' -i
HTTP/1.1 200 OK
server: Cowboy
date: Sat, 17 Jun 2017 01:02:45 GMT
content-length: 16
cache-control: max-age=0, private, must-revalidate
content-type: application/json; charset=utf-8

{"hello":"maru"}

本当に

丸っと 10 分くらいで Maru を体験することが出来ました。

こちらの写経については、進捗が芳しくありません。ボチボチやっていきます。

小ネタ道場一本勝負 〜 logstash の Ruby filter メモ 〜

tl;dr

logstash を使っていて、filter でちょっと凝ったことをしたいと思って調べていたら Ruby のコードが直接書けるようなので導入してみた。

www.elastic.co

今回やりたかったこと

  • input で入ってきた event オブジェクトの中にある date 型フィールドデータのフォーマットを整形したデータを追加したい
  • 具体的には ISO8601 で入ってきた Datetime データを HH:MM:SSHH:MM に整形してそれぞれ個別のフィールドに追加したい

サンプル実装

logstash.conf

logstash 5.x とそれ以前のバージョンで書き方が若干異なるので注意。

  • logstash 5.x の場合
input {
  tcp {
    port => 5000
  }
}

filter {
  ruby {
    code => "
      event.set('datetime_hour_min_sec', DateTime.parse(event.get('message')).strftime('%H:%M:%S'));
      event.set('datetime_hour_min', DateTime.parse(event.get('message')).strftime('%H:%M'));
    "
  }
}

output {
  stdout {
    codec => "rubydebug"
  }
}
  • logstash 5.x 以前
input {
  tcp {
    port => 5000
  }
}

filter {
  ruby {
    code => "
      event['datetime_hour_min_sec'] = DateTime.parse(event['message']).strftime('%H:%M:%S');
      event['datetime_hour_min'] = DateTime.parse(event['message']).strftime('%H:%M');
    "
  }
}

output {
  stdout {
    codec => "rubydebug"
  }
}

詳細については、以下の注意点にて。

ELK Stack の用意

ちょっと脱線して Elasticsearch + Logstash + Kibana 環境については以下の docker-compose 用の YAML を利用した。

github.com

超簡単に ELK Stack を作成することが出来た。

$ git clone https://github.com/deviantony/docker-elk.git
$ cd docker-elk
$ docker-compose up -d
$ docker ps
CONTAINER ID        IMAGE                     COMMAND                  CREATED             STATUS              PORTS                                            NAMES
1ff0e05da5ab        dockerelk_logstash        "/usr/local/bin/do..."   15 minutes ago      Up 12 minutes       5044/tcp, 0.0.0.0:5000->5000/tcp, 9600/tcp       dockerelk_logstash_1
3099200a271e        dockerelk_kibana          "/bin/sh -c /usr/l..."   8 hours ago         Up 8 hours          0.0.0.0:5601->5601/tcp                           dockerelk_kibana_1
24b575c350b7        dockerelk_elasticsearch   "/bin/bash bin/es-..."   8 hours ago         Up 8 hours          0.0.0.0:9200->9200/tcp, 0.0.0.0:9300->9300/tcp   dockerelk_elasticsearch_1

以下のように logstash にメッセージを投げることが出来る。

$ echo "hello" | nc localhost 5000

デモ

  • MacOS X 上で以下のように logstash にメッセージを投げる
$ echo $(date +%FT%T.$(gdate +%3N)Z) | nc localhost 5000
  • logstash では以下のように出力される
$ docker-compose logs -f logstash
...
logstash_1       | {
logstash_1       |                "@timestamp" => 2017-06-15T14:49:44.455Z,
logstash_1       |         "datetime_hour_min" => "23:49",
logstash_1       |                      "port" => 58782,
logstash_1       |     "datetime_hour_min_sec" => "23:49:44",
logstash_1       |                  "@version" => "1",
logstash_1       |                      "host" => "172.18.0.1",
logstash_1       |                   "message" => "2017-06-15T23:49:44.426Z"
logstash_1       | }

datetime_hour_mindatetime_hour_min_sec というキーが追加され、意図した通りに時分秒、時分が値として設定されている。

event オブジェクトへのアクセス方法

Event API

logstash 5.0 で再実装された Event API に合わせて event オブジェクトへのアクセス方法が変更されている。

www.elastic.co

以下、ドキュメントの抜粋。

In 5.0, we’ve re-implemented the Event class and its supporting classes in pure Java. Since Event is a critical component in data processing, a rewrite in Java improves performance and provides efficient serialization when storing data on disk.

Pure Jave で書き換えることで、データ保存時のシリアライズのパフォーマンス向上を図った(と読める)ようだ。

GitHub issue

github.com

github.com

logstash 5.0 から event オブジェクトへのアクセス方法は従来の hash like なアクセス方法を廃止して、getter と setter でアクセスするように変更されたというのが概要。

5.x 以前

hash にアクセスするような書き方でアクセス出来る。

filter {
  ruby {
    code => "
      event['datetime_hour_min_sec'] = DateTime.parse(event['message']).strftime('%H:%M:%S');
      event['datetime_hour_min'] = DateTime.parse(event['message']).strftime('%H:%M');
    "
  }
}

5.x 移行

GetAPI と SetAPI を利用してアクセスする。

filter {
  ruby {
    code => "
      event.set('datetime_hour_min_sec', DateTime.parse(event.get('message')).strftime('%H:%M:%S'));
      event.set('datetime_hour_min', DateTime.parse(event.get('message')).strftime('%H:%M'));
    "
  }
}

影響範囲

きっと、ちゃんとフォローされている環境であれば、何てこと無いんだろうけど、うっかりバージョンアップしたり、ググった設定をそのまま利用しようとするとハマりそう(実際にハマった)。

その他の filter

mutate

Ruby filter を利用する前に導入を検討していた。

www.elastic.co

add_field で任意のキーと値を追加したり…

filter {
  mutate {
    add_field => { "foo_%{somefield}" => "Hello world, from %{host}" }
  }
}

split で任意のキーの値を分割したり出来る。

filter {
  mutate {
     split => { "fieldname" => "," }
  }
}

全てを網羅出来ていないが、ドキュメントを見る限りではかなり柔軟に mutate 出来る。

grok

logstash に適当に入ってきたレコードを一定のルールに従って構造化してくれるスゴイやつ(という認識)。

www.elastic.co

こちら の例を見ると解りやすいので、そのまま写経。

入ってくるログレコードは Web サーバーのアクセスログっぽい。

55.3.244.1 GET /index.html 15824 0.043

Grok フィルタを以下のように定義する。

filter {
  grok {
    match => { "message" => "%{IP:client} %{WORD:method} %{URIPATHPARAM:request} %{NUMBER:bytes} %{NUMBER:duration}" }
  }
}

出力は stdout プラグインの codec は rubydebug を利用して確認。

$ docker-compose logs -f logstash
...
logstash_1       | {
logstash_1       |       "duration" => "0.043",
logstash_1       |        "request" => "/index.html",
logstash_1       |     "@timestamp" => 2017-06-16T23:49:13.404Z,
logstash_1       |         "method" => "GET",
logstash_1       |           "port" => 36390,
logstash_1       |          "bytes" => "15824",
logstash_1       |       "@version" => "1",
logstash_1       |           "host" => "172.18.0.1",
logstash_1       |         "client" => "55.3.244.1",
logstash_1       |        "message" => "55.3.244.1 GET /index.html 15824 0.043"
logstash_1       | }

上記のように clientmethod 等、よしなに解析してくれている。

以上

logstash 奥深い。

2017 年 06 月 16 日(金)

ジョギング

日課

  • (腕立て x 30 + 腹筋 x 30) x 3

夕飯

昨日、JAYA のお兄ちゃんが作っていた作り方を参考にしつつ、乳化に気を使いながら作った。

唐辛子を入れすぎて激辛パスタになって奥さんに詰められた。

明日は

先月亡くなった義父の 49 日法要。

2017 年 06 月 15 日(木)

ジョギング

日課

  • (腕立て x 30 + 腹筋 x 30) x 3

夕飯

奥さんが忙しくて遅くなるというので JAYA でパスタを食べる。

JAYA のパスタはいつ食べても美味しいと思う。

小ネタ道場一本勝負 〜 date コマンドの出力を ISO 8601 フォーマットにしたい 〜

date コマンドで

ISO 8601 フォーマットで出力したい時がたまにあるのでメモっておく。

Mac だと

date +%FT%T%z

実行。

$ date +%FT%T%z
2017-06-15T19:26:43+0900

Linux だと

date --iso-8601=seconds

実行。

$ date --iso-8601=seconds
2017-06-13T19:06:52+0900

Ruby だと

irb(main):008:0> require 'time'
=> true
irb(main):009:0> Time.now.iso8601
=> "2017-06-15T19:39:15+09:00"

Python だと

>>> import datetime
>>> from pytz import timezone
>>> datetime.datetime.now(timezone('Asia/Tokyo')).strftime('%Y-%m-%dT%H:%M:%S%z')
'2017-06-15T19:57:48+0900'

以上

メモでした。