ようへいの日々精進XP

よかろうもん

シェルスクリプトも頑張りたいオプスが正月三ヶ日で解った気になる Rspec と Travis CI

新年はげましておめでとうございます。

はじめに

  • ずっとやらなきゃって思って引きずってきた Rspec をやってみる
  • ちゃんとうんちくを押さえておきたいけど、まずは写経したりサンプルを弄ったりしながら
  • 合わせて RspecTravis CI で実行させてみる

以下、色々と試しながら書いているので間違い等があると思われるので、適宜、アップデートしていく。


参考

qiita.com

qiita.com

qiita.com

morizyun.github.io

上記記事がとても参考になった。有難うございます。今後の為にもリンクを貼っておく。


Hello Rspec

何はともあれ

うんちくは後回しにしてサンプルスクリプトを使って Rspec をざっくりと体験してみる。

サンプル Ruby スクリプト

以下のようなスクリプトを zura.rb という名前で用意する。

class Zura
  def hage(str)
    str
  end
end

watashi = Zura.new()
p watashi.hage("私はズラではない")

実行すると以下のような結果が得られる。

$ ruby zura.rb
"私はズラではない"

本当にズラではありません。

テスト

テストと称してして zura_spec.rb を以下のように作成。

require_relative 'zura'

describe Zura, "のテスト" do

  before do
    @watashi = Zura.new()
  end

  context '#hage の引数が "私はズラ" の場合' do
    before do
      @arg = "私はズラ"
    end
    it '私はズラ' do
      expect(@watashi.hage(@arg)).to eq '私はズラ'
    end
  end

  context '#hage の引数が指定されていない場合' do
    it '引数エラーとなる' do
      expect{@watashi.hage}.to raise_error(ArgumentError)
    end
  end

end

テスト実行

以下のようにテストを実行する。

$ rspec --color -fd zura_spec.rb 

Zura のテスト
  #hage の引数が "私はズラ" の場合
    "私はズラ" が返ってくる
  #hage の引数が指定されていない場合
    引数エラーとなる

Finished in 0.00176 seconds (files took 0.08805 seconds to load)
2 examples, 0 failures

(追記)Travis CI でズラ

f:id:inokara:20160103231625p:plain


うんちく

Rspec とは

こちらより抜粋。

RSpecとは、Rubyで書かれたプログラムの動作を確認するための、テストコードを記述・実行するためのフレームワークのことです。実際に動くプログラムの動作のことを振る舞い(ビヘイビア)と呼ぶことから、それまでのテスト駆動開発のやり方をビヘイビア駆動開発(BDD)と呼ぶようになっています。

Ruby のテスティングフレームワーク

用語 / 命令のおさらい

前述のサンプルスクリプトからピックアップ。詳細については参考にさせて頂いた記事より抜粋させて頂いた。重ねて有難うございます。

用語 / 命令 詳細 備考
describe テストのグループ化を宣言する
context テストを条件別にグルーピングする
it テストを example という単位にまとめる
before example 実行前に呼ばれる before do ... end
expect expect(foo).to eq bar で記述して「foobar であることを期待する」という意味になる 「エクスペクテーション」と呼ばれる
マッチャ マッチャ(matcher)は「期待値と実際の値を比較して、一致した(もしくは一致しなかった)という結果を返すオブジェクト」 こちらより抜粋、マッチャの一覧はこちらが参考になる
モック 「あるオブジェクトのメソッドが[引数]で呼ばれることを期待して、呼び出された時には[戻り値]を返すようにしたい場合」に利用する こちらより抜粋
スタブ 「あるオブジェクトのメソッドが呼び出された時に、特定の戻り値を返すようにしたい場合」に利用する こちらより抜粋

その他にも用語や命令がありそうだけど...取り急ぎ。

前述のサンプルを再掲。

# Zura Class のテストを宣言
describe Zura, "のテスト" do

  #  example 実行前に Zura Class から @watashi インスタンスを生成する
  before do
    @watashi = Zura.new()
  end

  # 条件「#hage の引数が "私はズラ" の場合」
  context '#hage の引数が "私はズラ" の場合' do

    # example 実行前に @arg に "私はズラ" を代入
    before do
      @arg = "私はズラ"
    end

    # hage メソッドの返り値として "私はズラ" を期待する example を実行
    it '私はズラ' do
      expect(@watashi.hage(@arg)).to eq '私はズラ'
    end
  end

(snip)

end

も少し踏み込んで

教材

github.com

こちらスクリプトから一部を抜粋したもの。

スクリプト

LXD でコンテナ一覧を取得する部分。

require "net_http_unix"

class Client
  def initialize(uri)
    @uri = uri
  end

  def client
    NetX::HTTPUnix.new(@uri)
  end

  def list_containers
    req = Net::HTTP::Get.new("/1.0/containers")
    resp = client.request(req)
    return resp.body
  end
end

hoge.rb という名前で保存しておく。

テストスクリプト

以下のようなテストスクリプトを作成。

require 'hoge'

describe Client do

  describe '#initialize' do
    context '引数 uri が設定されている場合' do
      before do
        @uri = 'unix:///var/lib/lxd/unix.socket'
      end
      it '引数は String である' do
        expect(@uri).to be_a_kind_of(String)
      end
    end
    context '引数 uri が設定されていない場合' do
      it '引数エラーとなる' do
        expect{Client.new}.to raise_error(ArgumentError)
      end
    end
  end

  describe '#client' do
    context '引数 uri が設定されている場合' do
      before do
        @uri = 'unix:///var/lib/lxd/unix.socket'
      end
      it 'object が生成される' do
        c = Client.new(@uri)
        expect(c.client).to be_a_kind_of(Object)
      end
    end
  end

  describe '#list_containers' do
    context '引数 uri が正しく設定されている場合' do
      before do
        uri = 'unix:///var/lib/lxd/unix.socket'
        @res = '{"type":"sync","status":"Success","status_code":200,"metadata":["/1.0/containers/test01"],"operation":""}'
        @c = Client.new(uri)
        allow(@c).to receive(:list_containers).and_return(@res)
      end
      it 'コンテナ一覧が String で取得出来る' do
        expect(@c.list_containers).to be_a_kind_of(String)
      end
      it 'コンテナ一覧が取得出来る' do
        response = @c.list_containers
        expect(response).to eq @res
      end
    end
  end
end

hoge_spec.rb というファイル名で保存しておく。

ローカルホストでテストを実行する

$ rake spec
/usr/local/bin/ruby -I/usr/local/lib/ruby/gems/2.1.0/gems/rspec-support-3.4.1/lib:/usr/local/lib/ruby/gems/2.1.0/gems/rspec-core-3.4.1/lib /usr/local/lib/ruby/gems/2.1.0/gems/rspec-core-3.4.1/exe/rspec --pattern spec/\*\*/\*_spec.rb --color -fd

Client
  #initialize
    引数 uri が設定されている場合
      引数は String である
    引数 uri が設定されていない場合
      引数エラーとなる
  #client
    引数 uri が設定されている場合
      object が生成される
  #list_containers
    引数 uri が正しく設定されている場合
      コンテナ一覧が String で取得出来る
      コンテナ一覧が取得出来る

Finished in 0.00738 seconds (files took 0.12878 seconds to load)
5 examples, 0 failures

Travis CI でテストを実行する

テストを Travis CI で実行させたいので、事前に Travis CI と GitHubリポジトリを連携させておく。

上図のように連携を ON にしておくだけ。

f:id:inokara:20160103225021p:plain

リポジトリに push すると以下のようにテストが実行されて結果が出力される。

f:id:inokara:20160103224831p:plain

おお。


ということで

引き続き

  • マッチャ、モック、スタブ等についてメモっていきたい
  • テストを書くというのはセンスが必要な気がしているが、まずは基礎をちゃんとやりたい
  • どんな風に Rspec を勉強していいのかすら解らないのが辛い
  • Travis CI 楽しい

シェルスクリプト

こんな風にテストフレームワークが存在するんだろうか。その前にまともにシェルスクリプトも書けるようになりたい。

gem 初め(LXD REST API Client for Ruby と test-kitchen driver for LXD REST API をリリースした)

tl;dr

書き初めのように gem 初めをしたのでメモ。


一筆入魂

何を作ったのか?

  • LXD が提供している REST API のクライアント for Ruby
  • 上記を利用した test-kitchen ドライバ

具体的な内容については以下の記事を...

inokara.hateblo.jp

色々と足りてない

  • エラー処理
  • 特にテスト、テスト、テスト、徹底的にテスト(テストが何かというところから始めたい)
  • API クライアントでは全ての API がサポートされているわけではない

ということで

本年も宜しくお願い致します。

超メモで走り切る 2015 年(5) iMac Mid 2011 を外付け Thunderbolt SSD で起動するようにした

tl;dr

VirtualBoxradiko.jp 再生、iTunes 母艦等に利用していた iMac Mid 2011 がもっさりしてきて少しストレスを感じていたので、手元にあった SSD を外付け Thunderbolt HDD ケースに入れて、そちらから OS を起動することでストレスレスになったのでメモ。

f:id:inokara:20151230194054p:plain

プラスドライバーだけで簡単増設。


メモ

外付け Thunderbolt HDD ケース

秋葉館で購入。

www.akibakan.com

ディスクのケースでこんな値段がするのか...と驚いた。高価である。 あと、追加で Thunderbolt ケーブルを購入。こやつもいい値段する。

SSD

三年位前に購入した IntelSSD を流用。

www.amazon.co.jp

120GB なのでなんでもインストールするわけにはいかない。

SSD の組み込み

SSD をケースに設置して Thunderbolt ケーブルで繋ぐだけで Mac には認識された。

超簡単。

OS のインストール

  1. Command + R キーで iMac を起動する
  2. 「ディスクユーティティ」を使って Mac OS拡張(ジャーナリング)+ GUID パーティションテーブル で SSD を初期化
  3. OS X を再インストール」より Mac OS X のインストールを進める(インストール先として SSD を選択する)
  4. しばし待つ...
  5. trimforce を使って SSD の Trim を有効化(sudo trimforce enable
  6. OS インストールが完了したら AppStore から OS のアップデートを行う

あとは必要なツール類を吟味した上でインストールした。


どのくらいストレスが軽減されたか

oreno iMac

f:id:inokara:20151230193218p:plain

こちらが仕様。メモリは 16GB まで増設出来るけど、USB が 2.0 止まりで Thunderbolt ポートが一つしか無いという...若干中途半端な仕様。

OS の起動時間

ログイン画面から再起動を実施後、再びログイン画面が現れるまでの時間をざっくりと計測。

SSD 前夜(ハードディスク) SSD 対比
Mac OS X の起動 58 秒 21 秒 2.7 倍

3 倍近く速くなったのは嬉しいが、基本的に起動しっぱなしなのであまりメリットを感じる機会は少なそう。

VirtualBox 上の仮想マシンの起動時間

VirtualBox 上の Ubuntu 14.04 の起動時間を比較してみる。これは、仮想マシンは「電源オフ」の状態から「通常起動」を選択してログインプロンプトが表示されるまでをざっくりと計測。

SSD 前夜(ハードディスク) SSD 対比
Ubuntu 14.04(on VirtualBox 5.0) の起動 2 分 10 秒 56 秒 2.32 倍

2.5 倍程起動が速くなった。嬉しい。ただ、こちらも起動しっぱなしなのであまりメリットを感じていないのが正直なところであるが、実際にサーバーにログインして操作する感じだと体感レスポンスは俄然良くなった感じ。


ということで

数値では測れない...

当然、数値で結果が出ているので、追加のメリットはあったと思う。また、数値では測れない体感速度も期待以上に向上しているので、暫く iMac Mid 2011 は利用出来そうな気がする。

なんで外付け?

換装や増設のスキルや勇気が無かった。

以上。

(ショロカレ 24 日目)LXD を試した && test-kitchen + kitchen-lxd_cli + Shell-Verifier で Ansible の Playbook を Serverspec でテストする

「初老丸の独り Advent calendar 2015」の二十四日目の記事です。

tl;dr

test-kitchen で Cookbook や Playbook を適用する仮想マシンとして LXC が利用出来る kitchen-lxc が手元の環境で動作しなくなっていたので、LXD なら...と思って、まずは LXD を試してみることにした。

以下のドキュメントを参考に進める。

続いて、test-kitchen と kitchen-lxd_cli + Shell-Verifier を組み合わせて Ansible の Playbook を Serverspec でやってみることにした。


LXD とは

参考

まずはうんちくから

こちら のドキュメントを抜粋。

コンテナのハイパーバイザ

以下の三つのコンポーネント

lxd デーモンは Rest API を提供する。

主な機能

以下のような機能を提供する

  • セキュアなデザイン (非特権コンテナ、リソース制限、その他)
  • 拡張性 (あなたのラップトップ機から数千のコンピュートノードまで)
  • 直感的 (シンプルでクリアな API、きびきびしたコマンドライン体験)
  • イメージをベースにしている (ディストリビューションテンプレートは不要で、良い信頼できるイメージのみ使用)
  • ライブマイグレーション

完全にドキュメントのパクリで恐縮だが...

  • 非特権コンテナを動かすことが出来る
  • 信頼出来るイメージのみを使用する
  • ライブマイグレーション

等が個人的には興味あり〼。


試す

試す環境

Virtualbox + Vagrant で用意した。

$ cat /etc/lsb-release 
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=14.04
DISTRIB_CODENAME=trusty
DISTRIB_DESCRIPTION="Ubuntu 14.04.2 LTS"

LXD のインストール

Ubuntu14.04 の場合には PPA でインストールする必要がある。

$ sudo add-apt-repository ppa:ubuntu-lxc/lxd-git-master && sudo apt-get update
$ sudo apt-get install lxd

一般ユーザーでも lxd にアクセス出来るようにする

以下のように一般ユーザーを lxd グループに追加する。

$ sudo usermod -aG lxd  vagrant

一旦、ログアウトを忘れずに...。

利用可能なイメージ一覧を確認

$ lxc image list images:
+--------------------------------+--------------+--------+-------------------------+---------+----------+-------------------------------+
|             ALIAS              | FINGERPRINT  | PUBLIC |       DESCRIPTION       |  ARCH   |   SIZE   |          UPLOAD DATE          |
+--------------------------------+--------------+--------+-------------------------+---------+----------+-------------------------------+
| centos/6/amd64 (1 more)        | 5d673faf89bd | yes    | Centos 6 (amd64)        | x86_64  | 49.93MB  | Dec 22, 2015 at 12:17pm (JST) |
| centos/6/i386 (1 more)         | 7f3f871f1900 | yes    | Centos 6 (i386)         | i686    | 49.85MB  | Dec 22, 2015 at 12:19pm (JST) |
| centos/7/amd64 (1 more)        | c9856d99aa27 | yes    | Centos 7 (amd64)        | x86_64  | 58.94MB  | Dec 22, 2015 at 12:22pm (JST) |

(snip)

|                                | 57b38724e1c6 | yes    | Oracle 6.5 (i386)       | i686    | 141.56MB | Dec 21, 2015 at 9:23pm (JST)  |
|                                | c5a7845ec81e | yes    | Plamo 5.x (amd64)       | x86_64  | 243.76MB | Dec 21, 2015 at 7:24am (JST)  |
|                                | 164c45e79912 | yes    | Plamo 5.x (amd64)       | x86_64  | 248.15MB | Dec 22, 2015 at 7:24am (JST)  |
|                                | 228b34412c4a | yes    | Plamo 5.x amd64 (mini)  | x86_64  | 495.06MB | Dec 21, 2015 at 7:51am (JST)  |
+--------------------------------+--------------+--------+-------------------------+---------+----------+-------------------------------+

リモートのイメージサーバーを登録する

lxc remote add images images.linuxcontainers.org

images.linuxcontainers.org からコンテナイメージを取得するように指定する。

コンテナの起動

$ lxc launch images:ubuntu/trusty/amd64 oreno-ubuntu

images オプションを付けるとリモートのイメージサーバーから取得するように設定される。

尚、lxc launch オプションは以下の通り。

lxc launch [remote:]<image> [remote:][<name>] [--ephemeral|-e] [--profile|-p <profile>...] [--config|-c <key=value>...]

コンテナの一覧を確認

$ lxc list
+------------------+---------+-------------------+------+-----------+-----------+
|       NAME       |  STATE  |       IPV4        | IPV6 | EPHEMERAL | SNAPSHOTS |
+------------------+---------+-------------------+------+-----------+-----------+
| default-ubuntu01 | RUNNING | 10.x.x.249 (eth0) |      | NO        |         0 |
+------------------+---------+-------------------+------+-----------+-----------+
| oreno-ubuntu     | RUNNING | 10.x.x.250 (eth0) |      | NO        |         0 |
+------------------+---------+-------------------+------+-----------+-----------+

コンテナにアクセスする

コンテナを起動後、以下のように lxc exec コマンドを利用してコンテナにアクセスする。

$ lxc exec oreno-ubuntu -- /bin/bash

あらかじめ SSH をインストールしたコンテナイメージから起動すれば SSH でアクセスすることも可能。

コンテナを停止

コンテナの停止は以下のように。

$ lxc stop oreno-ubuntu

コンテナの停止を確認。

$ lxc list
+------------------+---------+-------------------+------+-----------+-----------+
|       NAME       |  STATE  |       IPV4        | IPV6 | EPHEMERAL | SNAPSHOTS |
+------------------+---------+-------------------+------+-----------+-----------+
| default-ubuntu01 | RUNNING | 10.x.x.249 (eth0) |      | NO        |         0 |
+------------------+---------+-------------------+------+-----------+-----------+
| oreno-ubuntu     | STOPPED |                   |      | NO        |         0 |
+------------------+---------+-------------------+------+-----------+-----------+

コンテナを任意の名前でイメージとして保存する

こちらを参考にした。

#
# oreno-ubuntu コンテナで openssh-serve をインストール
#
$ lxc exec oreno-ubuntu -- apt-get -y install openssh-server

#
# oreno-ubuntu コンテナを停止
#
$ lxc stop oreno-ubuntu

#
# lxc publish コマンドを利用してイメージを保存
#
$ lxc publish oreno-ubuntu --alias=oreno-ubuntu-image

#
# lxc image list コマンドでイメージ一覧を確認
#
$ lxc image list
+--------------------+--------------+--------+-----------------------+--------+----------+-------------------------------+
|       ALIAS        | FINGERPRINT  | PUBLIC |      DESCRIPTION      |  ARCH  |   SIZE   |          UPLOAD DATE          |
+--------------------+--------------+--------+-----------------------+--------+----------+-------------------------------+
| oreno-ubuntu-image | b04af838736b | no     |                       | x86_64 | 126.77MB | Dec 23, 2015 at 1:03pm (JST)  |
|                    | 13a58482578d | no     |                       | x86_64 | 126.74MB | Dec 23, 2015 at 12:44pm (JST) |
|                    | 5a477c368646 | no     | Ubuntu trusty (amd64) | x86_64 | 64.46MB  | Dec 23, 2015 at 12:03pm (JST) |
+--------------------+--------------+--------+-----------------------+--------+----------+-------------------------------+

おお。


test-kitchen + kitchen-lxd_cli + Shell-Verifier で Ansible の Playbook を Serverspec でテストする

こんな感じで

f:id:inokara:20151224000551p:plain

※上図、うっかり vagrant って書いてあるけど Virtualbox のことデス。

kitchen-lxd_cli

あてにしていた kitchen-lxc が動かなくて、代替を探していたら kitchen-lxd_cli というツールを発見したのでこちらを利用させていただく。

github.com

ざっくり拝見すると、その名の通り lxc コマンドを叩いてイメージからコンテナを作成したりしていて、ソースコードを追えばどんな事をしているのか把握しやすかったりする。

尚、現時点では kitchen-lxd_cli で利用できるコンテナイメージの OS は UbuntuDebian 系の OS に限定されているので注意が必要。

インストールは Gemfile に追加するか、gem install で。

$ cat Gemfile
source "https://rubygems.org"

gem 'test-kitchen'
gem 'kitchen-ansible'
gem "serverspec"
gem "kitchen-vagrant"
gem "kitchen-verifier-shell"
gem 'kitchen-lxc'
gem 'kitchen-lxd_cli'

教材

教材は引き続き、以下の教材を利用。

github.com

コンテナイメージを作成する

事前にコンテナイメージを作成する。

$ lxc exec oreno-ubuntu -- apt-get -y install openssh-server && mkdir /root/.ssh
$ lxc stop oreno-ubuntu
$ lxc publish oreno-ubuntu --alias=oreno-ubuntu-image

作成されたイメージを確認。

$ lxc image list
+--------------------+--------------+--------+-----------------------+--------+----------+-------------------------------+
|       ALIAS        | FINGERPRINT  | PUBLIC |      DESCRIPTION      |  ARCH  |   SIZE   |          UPLOAD DATE          |
+--------------------+--------------+--------+-----------------------+--------+----------+-------------------------------+
| oreno-ubuntu-image | b04af838736b | no     |                       | x86_64 | 126.77MB | Dec 23, 2015 at 1:03pm (JST)  |
|                    | 13a58482578d | no     |                       | x86_64 | 126.74MB | Dec 23, 2015 at 12:44pm (JST) |
|                    | 5a477c368646 | no     | Ubuntu trusty (amd64) | x86_64 | 64.46MB  | Dec 23, 2015 at 12:03pm (JST) |
+--------------------+--------------+--------+-----------------------+--------+----------+-------------------------------+

.kitchen.local.yal

以下のように .kitchen.local.yml を作成する。

---
driver:
  name: lxd_cli
  image_name: oreno-ubuntu-image
  enable_wait_for_ssh_login: true

provisioner:
  name: ansible_playbook
  roles_path: roles

platforms:
  - name: ubuntu01

suites:
  - name: default
    provisioner:
      playbook: default.yml
      hosts: default

verifier:
  name: shell
  command: export KITCHEN_USERNAME=root && rspec -c -f d -I serverspec serverspec/common_spec.rb

verifiercommandKITCHEN_USERNAME=root を指定しているのは苦肉の策(root ユーザーで createconverge が実施される前提になっている為...→一応、ユーザー指定が出来るようにプルリクエストを送らせて頂いた。)

調理、試食

  • kitchen create
$ kitchen create
-----> Starting Kitchen (v1.4.2)
-----> Creating <default-ubuntu01>...
       Initializing container default-ubuntu01
       Starting container default-ubuntu01
       Waiting for network to become ready
       Setting up public key /home/vagrant/.ssh/id_rsa.pub on default-ubuntu01
       Check /root/.ssh on default-ubuntu01
       Finished creating <default-ubuntu01> (0m8.14s).
-----> Kitchen is finished. (0m8.16s)

この時点で LXD コンテナを確認してみる。

$ lxc list
+------------------+---------+-------------------+------+-----------+-----------+
|       NAME       |  STATE  |       IPV4        | IPV6 | EPHEMERAL | SNAPSHOTS |
+------------------+---------+-------------------+------+-----------+-----------+
| default-ubuntu01 | RUNNING | 10.x.x.249 (eth0) |      | NO        |         0 |
+------------------+---------+-------------------+------+-----------+-----------+

コンテナが作成されている。

  • kitchen converge

Ansible の Playbook を流してみる。

$ kitchen converge

(snip)

       PLAY [all] ********************************************************************



       GATHERING FACTS ***************************************************************
       ok: [localhost]

       TASK: [common | file path=/tmp/sample.txt state=touch mode=0644] **************
       changed: [localhost]

       PLAY RECAP ********************************************************************
       localhost                  : ok=2    changed=1    unreachable=0    failed=0

       Finished converging <default-ubuntu01> (2m26.22s).
-----> Kitchen is finished. (2m26.30s)
zlib(finalizer): the stream was freed prematurely.
  • kitchen verify

Shell-Verifier に Serverspec のテストを叩かせる。Shell-Verifier に Serverspec を実行させるのが快適なので手放せなくなりそう。

$ kitchen verify
-----> Starting Kitchen (v1.4.2)
-----> Setting up <default-ubuntu01>...
       Finished setting up <default-ubuntu01> (0m0.00s).
-----> Verifying <default-ubuntu01>...
       [Shell] Verify on instance=#<Kitchen::Instance:0x007f3d0802d4c8> with state={:hostname=>"10.x.x.249", :username=>"root", :last_action=>"setup"}


File "/tmp/sample.txt"
  should be file

Finished in 0.4627 seconds (files took 0.71709 seconds to load)
1 example, 0 failures

       Finished verifying <default-ubuntu01> (0m1.31s).
-----> Kitchen is finished. (0m1.37s)

ひとまず調理、試食までは滞りなく。


ということで...

LXD について

入門レベルでどうこう言える身分では無いけど、Docker のように起動が速いし、一般ユーザーでも手軽にコンテナ環境を利用出来るのは嬉しい。仮想マシン上でも動くというのも嬉しい。API も提供されているとのことで引き続き触っていきたい。

kitchen-lxd_cli について

作者のモチベーションが githubREADME に記載されていたので転載。

I started the project because I really like the idea of developing containers, but kitchen-lxc wouldn't work with my version. I also tried docker but preferred how lxd is closer to a hypervisor virtual machine. For instance kitchen-docker my recipes that had worked on virtual machies for mongodb, the service would not start when using docker. I was able to get the service to start but liked the concept of system containers more than application containers. Ultimately I was interested in LXD and there wasn't anything out there. I was quickly able to get my mongodb recipe working. I figured I'd clean things up, and some features and publish it. Since then I've added numerous features, mainly with a focus on speeding up development of cookbooks, and exploring LXD.

超ざっくり意訳。

  • 俺(作者)が作った Mongodb の Chef レシピをテストしようとしたよ
  • kitchen-lxc が動かなかったぜ
  • kitchen-docker でも思ったような結果が得られなかった
  • 俺(作者)は LXD に興味があったので Mongodb のレシピを LXD で試してみた
  • 満足出来るような結果が得られたのでこのツールを公開しようと考えた

なるほど。

先述の通り、現時点では UbuntuDebian のコンテナのみ対応していたり、開発途上ではあるものの kitchen-lxc の代わりとして引き続き注目していきたい。

以上

LXD は Docker 周辺のエコシステムが充実している訳でも無さそうだけど、Docker のようにサクッとコンテナ環境が起動するし、従来の lxc と遜色の無い操作で扱えるので、Docker 以上 Vagrant 未満な仮想環境を求める(意外に多いのでは...)方々は是非、試したいツールだと思う。

(ショロカレ 22 日目)LXC を一般ユーザー権限で利用する超メモ

「初老丸の独り Advent calendar 2015」の二十二日目の記事です。

tl;dr

test-kitchen の Driver の一つ kitchen-lxc を動かしてみたくなって、まずは以下の記事を参考に LXC を一般ユーザー権限で動かしてみた。

gihyo.jp

上記の記事は LXC を一般ユーザー権限で動かす為の基礎知識等を含めて詳しく記載されているので、上記の記事だけで簡単に動かすことが出来た。


memo

注意

すいません...上記の記事を参考に写経しているだけなので詳しい用語等については追記していく予定。

ホスト環境

vagrant@vagrant-ubuntu-trusty-64:~$ cat /etc/lsb-release 
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=14.04
DISTRIB_CODENAME=trusty
DISTRIB_DESCRIPTION="Ubuntu 14.04.2 LTS"

VirtualBox で起動した Ubuntu 14.04 を利用する。

サブ ID の確認

サブ ID とは...以下を参考に。

以下の通り、Ubuntu の場合には確認するだけ。

$ cat /etc/subuid /etc/subgid                                                                                            
vagrant:100000:65536
ubuntu:165536:65536
consul:231072:65536
vagrant:100000:65536
ubuntu:165536:65536
consul:231072:65536

ネットワークインターフェースの設定

仮想インターフェース(veth)を操作することが出来るコマンドを確認。

$ ls -l /usr/lib/x86_64-linux-gnu/lxc/lxc-user-nic
-rwsr-xr-x 1 root root 34888 Nov  4 00:39 /usr/lib/x86_64-linux-gnu/lxc/lxc-user-nic

ユーザーが作成できるネットワークインターフェースの数や作成したインターフェースをどのホストにブリッジさせるかを指定できるファイルを以下のように作成した。

$ cat /etc/lxc/lxc-usernet
# USERNAME TYPE BRIDGE COUNT
vagrant veth lxcbr0 10

LXC 設定ファイルを配置するディレクトリ作成

$ mkdir -p ~/.config/lxc

LXC コンテナのデフォルト設定ファイルをコピーする

$ cp /etc/lxc/default.conf ~/.config/lxc/default.conf

ファイルの中身を確認。

$ cat  ~/.config/lxc/default.conf
lxc.network.type = veth
lxc.network.link = lxcbr0
lxc.network.flags = up
lxc.network.hwaddr = 00:16:3e:xx:xx:xx
#
lxc.id_map = u 0 100000 65536
lxc.id_map = g 0 100000 65536

コンテナ作成

$ lxc-create -n oreno-lxc-centos -t download -- -d centos
Setting up the GPG keyring
Downloading the image index

---
DIST    RELEASE ARCH    VARIANT BUILD
---
centos  6       amd64   default 20151222_02:16
centos  6       i386    default 20151222_02:16
---

Release: 6
Architecture: amd64

Downloading the image index
Downloading the rootfs
Downloading the metadata
The image cache is now ready
Unpacking the rootfs

---
You just created a CentOS container (release=6, arch=amd64, variant=default)

To enable sshd, run: yum install openssh-server

For security reason, container images ship without user accounts
and without a root password.

Use lxc-attach or chroot directly into the rootfs to set a root password
or create user accounts.

上記の lxc-create オプションは以下の通り。

  • -n コンテナ名
  • -t テンプレートを指定(一般ユーザーの場合には download を利用する)
  • -- テンプレートオプションのオプションを指定する際に利用
  • -d テンプレートのディストリビューションを指定

今回は指定していないが、テンプレートオプションには以下のオプションも利用可能。

コンテナスタート

コンテナは作成済み。念のために確認。

$ lxc-ls --fancy
NAME              STATE    IPV4       IPV6  AUTOSTART
-----------------------------------------------------
oreno-lxc-centos  STOPPED  -          -     NO

コンテナスタート。

$ lxc-start --name oreno-lxc-centos --daemon
$ lxc-ls --fancy
NAME              STATE    IPV4        IPV6  AUTOSTART
------------------------------------------------------
oreno-lxc-centos  RUNNING  10.0.3.227  -     NO

コンテナにログイン

コンテナの root パスワードは設定されておらず、ユーザーも作成されていないので lxc-attach でコンテナにアクセスして root パスワードの設定、またはユーザーを作成する。

$ lxc-attach --name oreno-lxc-centos
bash-4.1#
bash-4.1# passwd root
Changing password for user root.
New password:
Retype new password:
passwd: all authentication tokens updated successfully.

一旦、exit して lxc-console でログインする。

$ lxc-console --name oreno-lxc-centos

Connected to tty 1
Type <Ctrl+a q> to exit the console, <Ctrl+a Ctrl+a> to enter Ctrl+a itself


CentOS release 6.7 (Final)
Kernel 3.13.0-55-generic on an x86_64

oreno-lxc-centos login: root
Password:
Last login: Tue Dec 22 08:25:42 on tty1
[root@oreno-lxc-centos ~]#

コンソールから抜ける場合には Ctrl+a q を押下する。

Apache をインストールしてみる

Apache をインストールしてコンテナホストからインストールしてみる。

$ lxc-console --name oreno-lxc-centos
Connected to tty 1
Type <Ctrl+a q> to exit the console, <Ctrl+a Ctrl+a> to enter Ctrl+a itself


CentOS release 6.7 (Final)
Kernel 3.13.0-55-generic on an x86_64

oreno-lxc-centos login: root
Password:
Last login: Tue Dec 22 08:28:00 on tty1
[root@oreno-lxc-centos ~]# yum -y install httpd

(snip)

[root@oreno-lxc-centos ~]# echo "hello LXC" > /var/www/html/index.html
[root@oreno-lxc-centos ~]# service httpd start
                                                           [  OK  ]
[root@oreno-lxc-centos ~]#

# Ctrl+a q コンテナを抜ける

$ curl 10.0.3.227
hello LXC

おっけ。


以上

LXC を一般ユーザーでコンテナを作成して起動するメモでした。参考にさせていただいた記事だけでここまで出来た。嬉しい。

次は kitchen-lxc を動かしてみたいと企んでいる。

(ショロカレ 14 日目)EC2 の詳細モニタリングが適用されていることを確認するメモ

遅延していますが...これは「初老丸の独り Advent calendar 2015」の十四日目の記事です。

tl;dr

EC2 で詳細モニタリングが適用されていることをマネジメントコンソール以外から確認する方法のメモ(ドキュメントをちゃんと読もう事案)。


ドキュメントをちゃんと読もう事案

ドキュメント

docs.aws.amazon.com

詳細モニタリングとは

CloudWatch の詳細モニタリングを有効にすると 1 分ごとの CloudWatch 収集データを確認することが出来るようになる。

詳細は以下のドキュメントを。

docs.aws.amazon.com

(この記事を書く前にやっていた)詳細モニタリング設定

マネジメントコンソールから。

f:id:inokara:20151215081525p:plain

ポチ。

f:id:inokara:20151215081536p:plain

ポチと。

f:id:inokara:20151215081550p:plain

簡単に出来た。

さて、マネジメントコンソール以外から適用を確認する

aws-cli を利用する。

$ aws --version
aws-cli/1.9.8 Python/2.7.6 Linux/3.13.0-55-generic botocore/1.3.8

以下のように monitor-instances を使えばよさそう。というか、monitor-instances を利用することで詳細モニタリングが有効に出来るとのこと。

$ aws ec2 monitor-instances --instance-ids i-xxxxxxx

以下のように出力される。

{
    "InstanceMonitorings": [
        {
            "InstanceId": "i-xxxxxxx",
            "Monitoring": {
                "State": "enabled"
            }
        }
    ]
}

なるほど、なるほど。すでに有効になっているので Stateenabled になっている。

ちなみに、無効にする場合には以下のように unmonitor-instances を利用する。

$ aws ec2 monitor-instances --instance-ids i-xxxxxxx

以下のように出力される。

{
    "InstanceMonitorings": [
        {
            "InstanceId": "i-xxxxxxx",
            "Monitoring": {
                "State": "disabling"
            }
        }
    ]
}

無効中という感じなのかな...disabling となっている。もう一度、たたくと...

{
    "InstanceMonitorings": [
        {
            "InstanceId": "i-xxxxxxx",
            "Monitoring": {
                "State": "disabled"
            }
        }
    ]
}

念のためにマネジメントコンソールから確認すると以下のように...。

f:id:inokara:20151215082222p:plain

改めて有効にしてみる。

$ aws ec2 monitor-instances --instance-ids i-xxxxxxx

以下のように出力される。

{
    "InstanceMonitorings": [
        {
            "InstanceId": "i-xxxxxxx",
            "Monitoring": {
                "State": "pending"
            }
        }
    ]
}

設定中ということなのかな...pending となっている。もう一度、たたくと...

$ aws ec2 monitor-instances --instance-ids i-xxxxxxx

以下のように出力される。

{
    "InstanceMonitorings": [
        {
            "InstanceId": "i-xxxxxxx",
            "Monitoring": {
                "State": "enabled"
            }
        }
    ]
}

有効(詳細)になっている。念のためにマネジメントコンソールでも...

f:id:inokara:20151215082757p:plain

ほうほう。


ということで

詳細モニタリングってなんぼ?

こちらが詳しい。

おまけに基本のモニタリングと詳細モニタリングについてこちらより抜粋。

モニタリング 特徴
基本モニタリング 事前に選択した 7 種類のメトリックス(5 分間隔)と、3 種類の状態チェックメトリックス(1 分間隔)。追加料金なし
詳細モニタリング 基本モニタリングで利用できるすべてのメトリックスが 1 分間隔で更新される。追加料金が必要。詳細モニタリングが有効にされているインスタンスでは、Amazon EC2 AMI ID とインスタンスタイプごとにデータを集計できる。

なるほど、なるほど。

ecs-cli で起動するコンテナインスタンスって

デフォルトで詳細メトリクスが有効になっていて驚いた。

以上

詳細モニタリングを始めて触ることになったし、ドキュメントをちゃんと読みましょう事案でした。

(ショロカレ 13 日目)Elasticsearch 及び Amazon ES で Bulk API を試すメモ

遅延していますが...これは「初老丸の独り Advent calendar 2015」の十三日目の記事です。

tl;dr

先日の勉強会で Amazon ES に Web 上から取得したデータを解析して Amazon ES に放り込むデモをやったら Amazon ES にデータを放り込む前にタイムオーバーしてしまって残念だったのでメモ。


原因と対策

原因

原因というか大量のデータを放り込む時には Bulk Insert を利用する方が良さそう。

対策

上記の通り、Bulk API を使うように修正する。今回は Ruby の elasticsearch-ruby を利用する。

github.com

elasticsearch-ruby で Bulk API を利用する場合には bulk メソッドを利用する。

def bulk_post(es_endpoint, data)
  c = Elasticsearch::Client.new log: true, url: es_endpoint
  c.bulk(body: data)
end

data は配列で指定する。

ということで、以下のような感じで soramame-es.rb を書き換えた。

github.com

def processing_soramame_content(uri, check_date_time, check_time)
  index_date = check_date_time.split(' ')
  html = NKF.nkf("--utf8", open(uri).read)
  # Parse content and post to Elasticsearch
  header = ['CHECK_DATE_TIME','CHECK_TIME','mon_st_code','town_name', 'mon_st_name', 'SO2','NO','NO2','NOX','CO','OX','NMHC','CH4','THC','SPM','PM2_5','SP','WD','WS','TEMP','HUM','mon_st_kind']
  doc = Nokogiri::HTML.parse(html, nil, nil)
  bulk = []
  num = 1
  doc.xpath('//tr[td]').each do |tr|
    row = tr.xpath('td').map { |td| td.content.gsub(/[\u00A0\n]|\-\-\-/,'NA') }
    row.unshift(check_time)
    row.unshift(check_date_time)
    ary = [header,row].transpose
    h = Hash[*ary.flatten]
    record_index = {
      'index' => {
        '_index' => "pm25_test_" + $d.strftime("%Y-%m-%d"),
        '_type' => 'kyushu',
      }
    }
    record_data = {
      'CHECK_DATE_TIME' => h['CHECK_DATE_TIME'],
      'CHECK_TIME' => h['CHECK_TIME'],
      'town_name' => h['town_name'],
      'mon_st_name' => h['mon_st_name'],
      'PM2_5' => h['PM2_5'].include?('NA') ? h['PM2_5'] = '' : h['PM2_5'],
      'TEMP' => h['TEMP'].include?('NA') ? h['TEMP'] = '' : h['TEMP']
    }
    bulk << record_index
    bulk << record_data
  end
  bulk_post($url, bulk)
end

上記のようにすることで bulk の中身は以下となり Bulk API で放り込まれる。

(snip)

{"index":{"_index":"pm25_test_2015-12-13","_type":"kyushu"}}
{"CHECK_DATE_TIME":"2015-12-13 01:00:00","CHECK_TIME":"01","town_name":"志布志市","mon_st_name":"志布志","PM2_5":"","TEMP":""}
{"index":{"_index":"pm25_test_2015-12-13","_type":"kyushu"}}
{"CHECK_DATE_TIME":"2015-12-13 01:00:00","CHECK_TIME":"01","town_name":"肝属郡東串良町","mon_st_name":"東串良","PM2_5":"","TEMP":""}

比較

Bulk API を利用した場合と通常のドキュメント追加を利用した場合の速度をザクっと比較。

  • レコード数は 205 レコード
  • ファイルに書き出すと 36KB 弱

以下のような結果となった。

  • Elasticsearch で比較
#
# Bulk API
#
$ time ruby soramame-es.rb hb201512130108.html

(snip)

real    0m0.349s
user    0m0.224s
sys     0m0.063s

#
# 通常のドキュメント追加
#
$ time ruby soramame-es.rb hb201512130108.html

(snip)

real    0m0.643s
user    0m0.309s
sys     0m0.085s
#
# Bulk API
#
$ time ruby soramame-es.rb hb201512130108.html

(snip)

real    0m0.981s
user    0m0.254s
sys     0m0.051s

#
# 通常のドキュメント追加
#
$ time ruby soramame-es.rb hb201512130108.html

(snip)

real    0m44.970s
user    0m0.416s
sys     0m0.635s

Bulk API を使う方が速い。Amazon ES の場合がその差が顕著。Bulk API は速い分だけトレードオフはきっとあるだろうから引き続き調べる。


ということで...

How big is Too big?

ドキュメントより抜粋して意訳。

The entire bulk request needs to be loaded into memory by the node that receives our request, so the bigger the request, the less memory available for other requests. There is an optimal size of bulk request. Above that size, performance no longer improves and may even drop off. The optimal size, however, is not a fixed number. It depends entirely on your hardware, your document size and complexity, and your indexing and search load.

  • Bulk リクエスト(Bulk API)は Elasticsearch ノードのメモリにロードして処理する
  • ということは Elasticsearch ノードのメモリサイズによって Bulk API の性能は変化する
  • Bulk API のリクエストサイズによってはパフォーマンスが劣化する可能性がある
  • Bulk API のリクエストの最適値は Elasticsearch ノードのハードウェア性能、リクエストのドキュメントサイズや内容等によって変わる

なるほど、なるほど。

Amazon ES に対する要望

  • マネジメントコンソールからインデックスを追加、削除出来るとちょっとうれしい(API からでもそれほど難しくないけど)

以上

取り急ぎ、メモでした。(後ほど Bulk API についてもう少し書く)

(ショロカレ 11 日目)Keynote Remote が個人的に未来感半端無かったのでメモ

遅くなりましたが、これは「初老丸の独り Advent calendar 2015」の十一日目の記事です。

tl;dr

Keynote で作った資料を人前で発表する機会を頂いたので以前から気になっていた Keynote Remote を使ってみることにした。


Keynote Remote とは

support.apple.com

iPhoneiPod touch / iPad を利用して Mac 上の Keynote を操作出来る機能で、MaciPhone / iPod touch / iPad に最新の Keynote をインストールしておけば良いとのことなので早速試してみた。


セットアップ

iPod touch

最新版の Keynote を起動して上部のアイコンをタップする。

f:id:inokara:20151212230401p:plain

Mac 上の Keynote との接続を開始する。

f:id:inokara:20151212230432p:plain

Mac

[Preference...] から [Remotes] タブから [Enable] にチェックすると iPod touch の名前が表示されるので [Link] をクリックする。

f:id:inokara:20151212230544p:plain

パスコードが表示されるので iPod touch 側と同じコードになっていることを確認して [Confirm] をクリックする。

f:id:inokara:20151212230648p:plain

以下のようにリンクされた状態になる。

f:id:inokara:20151212230705p:plain

再度 iPod touch

以下のように Mac 上のスライドが操作出来る状態になっている。

f:id:inokara:20151212231645p:plain

再生をタップすると資料の一枚目を見ることが出来る。

f:id:inokara:20151212231720p:plain

さらにフリップすると資料のページをめくることが出来る。

f:id:inokara:20151212231807p:plain

当然、Mac 側の Keynote では全画面のプレゼンモードでスライドが表示されている。

f:id:inokara:20151212232722p:plain

おお、これは!未来感半端ない!

話の内容なんかそっちのけで感動してしまった。


でも

Keynote Remote を使っているからといって良い発表とは限らないのが辛いところ。これからも精進したい。

(ショロカレ 10 日目)S3 Event notification → Lamda → Amazon ES に挫折したので SQS → Amazon ES にしたメモ

ということで、これは「初老丸の独り Advent calendar 2015」の十日目の記事です。


tl;dr

S3 で動かしている静的サイトの access_log を S3 Event notification → SQS → Amazon ES という流れで可視化してみようと思ったのでメモ。本当は SQS ではなくて Lambda で挑戦したかったんだけど Lambda から IAM role で制御されている Amazon ES にポストするのに挫折してしまって SQS に頼ることにした。(何に挫折したかにも触れる)


メモ

構成

f:id:inokara:20151210214909p:plain

作ったもの

github.com

  • ログを正規表現で解析するのがつらかった(最終的にググったら同じことをされている方がいらっしゃったので拝借させていただいた)
  • Elasticsearch の Date Format の調整に苦労した(結局は ISO Format で放り込めた)

デモ

すでに上記のリポジトリを取得している前提で...

  • Mapping Template を放り込む
$ cd oreno-s3-access_log/es

#
# Amazon ES の Endpoint を修正する
#
$ vim regist-mapping-template.rb

#
# Amazon ES に Mapping Template を放り込む
#
$ ruby regist-mapping-template.rb
code: 200
msg: OK
body: {"acknowledged":true}
  • アプリケーションを Docker build する
$ cd oreno-s3-access_log/sqs

#
# Amazon ES のエンドポイント等を修正
#
$ vim Makefile
(snip)
DOCKER_RUN := docker run -d --name s3-access-log \
              --env ES_ENDPOINT="http://YOUR-ES-ENDPOINT:9200" \
              --env ES_PREFIX="YOUR_INDEX_PREFIX" \
              --env SQS_QUEUE_NAME="YOUR_QUEUE_NAME" \
              --env AWS_ACCESS_KEY_ID="AKxxxxxxxxxxxxxxxxxxxxxxxx" \
              --env AWS_SECRET_ACCESS_KEY="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
              --env AWS_REGION="YOUR_REGION" \
              -v /etc/localtime:/etc/localtime:ro s3-access-log
(snip)

#
# アプリケーションのデプロイ
#
$ make build

今回は ES_ENDPOINT には Amazon ES のエンドポイントを設定することになる。

  • アプリケーションを起動する
#
# docker run をラップしている
#
$ make run

#
# コンテナの軌道を確認
#
$ docker ps
CONTAINER ID        IMAGE                COMMAND                  CREATED             STATUS              PORTS                    NAMES
5915f8932f0f        s3-access-log        "/usr/local/bin/super"   About an hour ago   Up About an hour                             s3-access-log
  • しばらく待つ
#
# Supervisord でアプリケーションを起動しているのでログを以下のように確認できる(キューを定期的にポーリングしている図)
#
$ docker exec s3-access-log /usr/local/bin/supervisorctl -c /app/supervisord.conf tail -f app
==> Press Ctrl-C to exit <==
21:56:34,261 INFO Calling sqs:receive_message with {u'QueueUrl': 'https://ap-northeast-1.queue.amazonaws.com/xxxxxxxxxxxx/s3-notification', 'MessageAttributeNames': ['*']}
2015-12-10 21:56:34,345 INFO Event does not exists...
2015-12-10 21:56:34,346 INFO Start polling...
2015-12-10 21:57:34,402 INFO Calling sqs:get_queue_url with {'QueueName': 's3-notification'}
2015-12-10 21:57:34,444 INFO Calling sqs:receive_message with {u'QueueUrl': 'https://ap-northeast-1.queue.amazonaws.com/xxxxxxxxxxxx/s3-notification', 'MessageAttributeNames': ['*']}
2015-12-10 21:57:34,525 INFO Event does not exists...
2015-12-10 21:57:34,526 INFO Start polling...
2015-12-10 21:58:34,585 INFO Calling sqs:get_queue_url with {'QueueName': 's3-notification'}
2015-12-10 21:58:34,626 INFO Calling sqs:receive_message with {u'QueueUrl': 'https://ap-northeast-1.queue.amazonaws.com/xxxxxxxxxxxx/s3-notification', 'MessageAttributeNames': ['*']}
2015-12-10 21:58:34,707 INFO Event does not exists...
2015-12-10 21:58:34,708 INFO Start polling...
  • Kibana を見てみる

f:id:inokara:20151210221603p:plain

ひとまず、Kibana 3 で見てみる。

Lambda → Amazon ES について何に挫折したのか

Lambda から Amazon ES にデータをポストしようとしたら以下のようなログが出た。

AuthorizationException: TransportError(403, u'{"Message":"User: anonymous is not authorized to perform: es:ESHttpPost on resource: arn:aws:es:ap-northeast-1:xxxxxxxxxxx:domain/oreno-es/s3_log-2015-12-08/s3_log"}')

なんや、権限が不足しとーとや、ほんじゃ、Lambda Function につけている IAM role に Amazon ES へのアクセスポリシーを付与すればよかろーもんって以下のようなポリシーを付与。

(snip)
    {
      "Effect": "Allow",
      "Action": "es:ESHttpPost",
      "Resource": "arn:aws:es:*:*:*"
    }
(snip)

付与しても状況は変わらず...。悩んで CloudWatch Logs の Amazon ES へのストリーム処理に利用する Node.js のコードを見てみると何やら Amazon ES のデータをポストする際に以下のような処理を行っていることが判明。

(snip)

function buildRequest(endpoint, body) {
    var endpointParts = endpoint.match(/^([^\.]+)\.?([^\.]*)\.?([^\.]*)\.amazonaws\.com$/);
    var region = endpointParts[2];
    var service = endpointParts[3];
    var datetime = (new Date()).toISOString().replace(/[:\-]|\.\d{3}/g, '');
    var date = datetime.substr(0, 8);
    var kDate = hmac('AWS4' + process.env.AWS_SECRET_ACCESS_KEY, date);
    var kRegion = hmac(kDate, region);
    var kService = hmac(kRegion, service);
    var kSigning = hmac(kService, 'aws4_request');
    
    var request = {
        host: endpoint,
        method: 'POST',
        path: '/_bulk',
        body: body,
        headers: { 
            'Content-Type': 'application/json',
            'Host': endpoint,
            'Content-Length': Buffer.byteLength(body),
            'X-Amz-Security-Token': process.env.AWS_SESSION_TOKEN,
            'X-Amz-Date': datetime
        }
    };

    var canonicalHeaders = Object.keys(request.headers)
        .sort(function(a, b) { return a.toLowerCase() < b.toLowerCase() ? -1 : 1; })
        .map(function(k) { return k.toLowerCase() + ':' + request.headers[k]; })
        .join('\n');

    var signedHeaders = Object.keys(request.headers)
        .map(function(k) { return k.toLowerCase(); })
        .sort()
        .join(';');

    var canonicalString = [
        request.method,
        request.path, '',
        canonicalHeaders, '',
        signedHeaders,
        hash(request.body, 'hex'),
    ].join('\n');

    var credentialString = [ date, region, service, 'aws4_request' ].join('/');

    var stringToSign = [
        'AWS4-HMAC-SHA256',
        datetime,
        credentialString,
        hash(canonicalString, 'hex')
    ] .join('\n');

    request.headers.Authorization = [
        'AWS4-HMAC-SHA256 Credential=' + process.env.AWS_ACCESS_KEY_ID + '/' + credentialString,
        'SignedHeaders=' + signedHeaders,
        'Signature=' + hmac(kSigning, stringToSign, 'hex')
    ].join(', ');

    return request;
}

(snip)

うう、これは...よく解らないということで挫折。

なお、Python から同じことを行う際には以下の記事が参考になることが判明。(有難うございます!)

qiita.com

どうやら、API リクエストを投げるのに Credential な情報を利用して署名を行ったリクエストヘッダが必要となるようで、以下ののドキュメントについて詳細に書かれている。

docs.aws.amazon.com

また、以下の記事も参考になった。(有難うございます!)

qiita.com

今まで CLISDK を何も考えずに使っていたのを反省するとともに次回の宿題にしたい。


以上

Amazon ES を触るというか、データを放り込んだりする方に時間を取られてしまっている...という位に Amazon ES は簡単に Elasticsearch を扱えるのは良いと思う。(ただし、ちゃんと運用しようとすると気を付けなければいけない点があると思う)

ということで、やっと、十日目に追いついた。

以上。

(ショロカレ 9 日目)Docker コンテナ内でちょっとしたアプリを立ち上げるのに Supervisord を使う

だいぶん体調が戻ってきたのでショロカレを続ける...ということで、これは「初老丸の独り Advent calendar 2015」の九日目の記事です。

tl;dr

Docker コンテナ内でちょっとしたアプリ(スクリプト)を立ちあげたかったので、以下の記事を参考に Supervisord を使ったのでメモ。

docs.docker.com


memo

作ったもの

github.com

S3 の Event 通知が SQS に入るように設定しているので、その通知を解析して S3 に保存されているログを取得して Elasticsearch にポストするスクリプト。詳細については別の記事で。

Dockerfile

$ cat Dockerfile
FROM python:2
MAINTAINER inokappa
RUN mkdir /app
ADD requirements.txt /app
ADD app.py /app
ADD supervisord.conf /app
RUN pip install -r /app/requirements.txt
CMD [ "/usr/local/bin/supervisord", "-c", "/app/supervisord.conf" ]

supervisord.conf

以下を参考に...

以下のよに作成。

$ cat supervisord.conf
[inet_http_server]
port=127.0.0.1:9001

[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface

[supervisorctl]
serverurl=http://127.0.0.1:9001

[supervisord]
nodaemon=true

[program:app]
command=/usr/local/bin/python /app/app.py
stdout_logfile=/app/app.log
stdout_logfile_maxbytes=1MB
stdout_logfile_backups=5
stdout_capture_maxbytes=1MB
redirect_stderr=true

requirements.txt

必要なモジュールは requirements.txt に書いておくと良いことを最近知った。

$ cat requirements.txt
boto3
elasticsearch
pytz
urllib3
supervisor

docker run すると...

docker run にオプションをたくさんくっつける時には Makefile にまとめておくのが最近の個人的なトレンド。

$ make run

でコンテナが起動する(ようにしている)。

$ docker ps
CONTAINER ID        IMAGE                COMMAND                  CREATED             STATUS              PORTS                    NAMES
ecf226637831        s3-access-log        "/usr/local/bin/super"   4 hours ago         Up 4 hours                                   s3-access-log

supervisorctl で確認する。

$ docker exec s3-access-log /usr/local/bin/supervisorctl -c /app/supervisord.conf status
app                              RUNNING   pid 8, uptime 0:02:01

標準出力の内容を確認する。

$ docker exec s3-access-log /usr/local/bin/supervisorctl -c /app/supervisord.conf tail app
-12-10 20:41:19,290 INFO Event does not exists...
2015-12-10 20:41:19,291 INFO Start polling...
2015-12-10 20:42:19,347 INFO Calling sqs:get_queue_url with {'QueueName': 's3-notification'}
2015-12-10 20:42:19,460 INFO Calling sqs:receive_message with {u'QueueUrl': 'https://ap-northeast-1.queue.amazonaws.com/xxxxxxxxxxxxx/s3-notification', 'MessageAttributeNames': ['*']}
2015-12-10 20:42:19,542 INFO Event does not exists...
2015-12-10 20:42:19,542 INFO Start polling...
2015-12-10 20:43:19,595 INFO Calling sqs:get_queue_url with {'QueueName': 's3-notification'}
2015-12-10 20:43:19,637 INFO Calling sqs:receive_message with {u'QueueUrl': 'https://ap-northeast-1.queue.amazonaws.com/xxxxxxxxxxxxx/s3-notification', 'MessageAttributeNames': ['*']}
2015-12-10 20:43:19,718 INFO Event does not exists...
2015-12-10 20:43:19,718 INFO Start polling...
2015-12-10 20:44:19,780 INFO Calling sqs:get_queue_url with {'QueueName': 's3-notification'}
2015-12-10 20:44:19,824 INFO Calling sqs:receive_message with {u'QueueUrl': 'https://ap-northeast-1.queue.amazonaws.com/xxxxxxxxxxxxx/s3-notification', 'MessageAttributeNames': ['*']}
2015-12-10 20:44:19,905 INFO Event does not exists...
2015-12-10 20:44:19,905 INFO Start polling...
2015-12-10 20:45:19,966 INFO Calling sqs:get_queue_url with {'QueueName': 's3-notification'}
2015-12-10 20:45:20,000 INFO Calling sqs:receive_message with {u'QueueUrl': 'https://ap-northeast-1.queue.amazonaws.com/xxxxxxxxxxxxx/s3-notification', 'MessageAttributeNames': ['*']}
2015-12-10 20:45:20,084 INFO Event does not exists...
2015-12-10 20:45:20,084 INFO Start polling...

おお、いい感じ。


ということで

Supervisord を引き続き、便利に使っていきたい。