2014年5月15日木曜日

curlを使ってJSONファイルをPOST

偶に必要な時にパッと出てこないので自分用メモ。 

# test.jsonを用意

WikipediaにあるJSONを一部抜き出した次のJSONをサンプルファイルとして使用する。

{
    "firstName": "John",
    "lastName": "Smith",
    "age": 25,
    "address": {
        "state": "NY"
    },
    "phoneNumbers": [
        {
            "type": "home",
            "number": "212 555-1234"
        }
    ]
}

# curlを実行

$ curl -X POST http://localhost:3000 -H 'Content-Type: application/json' -d @test.json

# 受信側がRailsアプリの場合

パラメータとしてJSONは次のように解釈されている。

[1] pry(#<DashboardController>)> params
=> {"firstName"=>"John",
 "lastName"=>"Smith",
 "age"=>25,
 "address"=>{"state"=>"NY"},
 "phoneNumbers"=>[{"type"=>"home", "number"=>"212 555-1234"}],
 "controller"=>"dashboard",
 "action"=>"index",
(余分なものは省略)

コード内では実際に次のようにJSONファイルの中身を取得できる。

[2] pry(#<DashboardController>)> params[:firstName]

=> "John"

これでまた忘れても大丈夫。

2014年1月19日日曜日

Ancestry

親子構造を格納するモデルを用意しようとしてたら、AncestryというGemを見つけた

準備

適当にrailsプロジェクトを作成し、Gemfileにgem 'ancestry'を記述しbundle install


scaffoldを作る

$ rails  g scaffold tenant name:string


Tenantモデルを編集

# app/models/tenant.rb
class Tenant < ActiveRecord::Base
  attr_accessible :name, :parent_id
  attr_accessible :name # 2014/1/21 parent_idは不要でした
  has_ancestry
end


フォーム編集用ビューを編集

# app/views/tenants/_form.html.erb
<div class="field">
  <%= f.label :parent %><br />
  <%= f.select :parent_id, options_from_collection_for_select(Tenant.all, :id, :name) %>
</div>


Tenantモデルへancestryカラムを追加してマイグレーション

# rails g migration add_ancestry_to_tenants ancestry:string
class AddAncestryToTenants < ActiveRecord::Migration
  def change
    add_column :tenants, :ancestry, :string, after: :id
    add_index :tenants, :ancestry
  end
end


indexビューを編集(抜粋)

# app/views/tenants/index.html.erb
    <td><%= tenant.parent ? tenant.parent.name : nil %></td>
    <td><%= tenant.ancestors.map &:name %></td>
    <td><%= tenant.children.map &:name %></td>
    <td><%= tenant.descendants.map &:name %></td>

この状態でモデルの作成を繰り返すと、indexの表示はたとえば次のようになる

この時、tenantsテーブルの中身は次のようになる







2014年1月1日水曜日

Ansible導入(前編)

2013年からの宿題なのか2014年のお年玉なのか不明だが、確定申告用の領収書類を持ち帰るのを忘れたので大晦日に手持ち無沙汰になっていたのでやってた。後編あるかは不明

Ansibleを使ってできること

  • サーバの構成管理。Chefと同じ目的で使うものという認識
  • 設定として記述したパッケージ・ソフトウェアを簡単に導入できる
  • 冪等性を保ちながらサーバの構築を繰り返し行うことができる(使い方によっては当然冪等性を損なうこともできるけど)

インストール

AnsibleWorksのサイトにあるので参照。たとえば次の方法がある
  • GitHubレポジトリからcloneしてインストール
  • pipインストール
  • yum, apt

ローカルMacにpipが既に入っていたので、pipインストール
$ pip install paramiko PyYAML jinja2 httplib2 
$ sudo pip install ansible

Ansibleで管理対象となるホスト設定を作成

# /etc/ansible/hosts
[experiments]
dev ansible_ssh_port=<ssh port> ansible_ssh_host=<your hostname> ansible_ssh_user=<ssh user>
  • experimentsはAnsible管理のホストをグルーピングしてる
  • devはサーバのニックネーム

Ansibleの動作設定を作成

以降のコマンド実行の際にControlPathが長過ぎて怒られるのを予防。デフォルトは control_path=%(directory)s/ansible-ssh-%%h-%%p-%%r らしいがこれだと長過ぎるみたい。
# /etc/ansible/ansible.cfg
[ssh_connection]
control_path=/tmp/ansible-ssh-%%h-%%p-%%r

テスト

$ /usr/local/share/python/ansible all -m ping
dev | success >> {
    "changed": false,
    "ping": "pong"
}

OKそう。さらにテストしてみる
$ /usr/local/share/python/ansible all -a "/bin/echo hello"
dev | success | rc=0 >>
hello

OKですな

幾つかコマンドサンプル

$ /usr/local/share/python/ansible dev -m copy -a "src=/etc/hosts dest=/tmp/hosts"
#=> ローカルの/etc/hostsをdevの/tmp/へコピー

$ /usr/local/share/python/ansible dev -a "/sbin/reboot" —sudo
#=> devを再起動
$ /usr/local/share/python/ansible -m yum -a 'name=mysql-server state=installed’ dev --sudo#=> devでmysql-serverをyumインストール

しかし、いちいちコマンド打つの面倒くさい。そのため、実行する処理(task)をまとめて記述したもの(Playbook)を使う。

# /etc/ansible/builds_server.yml 
- hosts: experiments
  remote_user: ec2-user
  sudo: no
  tasks:
    - name: test connection
      ping:
    - include: tasks/begins_pkg.yml

# /etc/ansible/tasks/begins_pkg.yml
- name: ensure all packages are at the latest version
  yum: name=* state=latest
  sudo: yes
- name: ensure git is installed
  yum: name=git state=latest
  sudo: yes
- name: ensure apache is running
  service: name=httpd state=started
  sudo: yes

上記を用意した上で、次のコマンドを実行する
$ /usr/local/share/python/ansible-playbook /etc/ansible/builds_server.yml

PLAY [experiments] ************************************************************
GATHERING FACTS ***************************************************************
ok: [dev]
TASK: [test connection] *******************************************************
ok: [dev]
TASK: [ensure all packages are at the latest version] *************************
ok: [dev]
TASK: [ensure git is installed] ***********************************************
ok: [dev]
TASK: [ensure apache is running] **********************************************
ok: [dev]
PLAY RECAP ********************************************************************
dev                        : ok=5    changed=0    unreachable=0    failed=0   

こんな感じで実行結果が出力される。言うまでもないが、

builds_server.ymlで次を記述

  • pingテスト
  • begins_pkg.ymlをインクルード

begins_pkg.ymlで次を記述

  • yum -y update
  • 最新のgitパッケージをyumインストール
  • apacheを起動する(パッケージが無ければインストール)

後編では次のものをする予定
  • EC2イメージからのインスタンスローンチ(Elastic IP貼り付け等も併せて)
  • rbenvベースのrubyインストール


2013年12月30日月曜日

fluentdで設定したformat(正規表現)をメモしておく

Fluentularを利用したり@tagomoris氏のページを見ながら格闘したので記録

ざっくりとした設定

<source>
  type config_expander
  <config>
    type tail
    path /some/path/to/log
    pos_file /some/path/to/pos/file
    tag some.tag
    ☆
  </config>
</source>
以下だらだら挙げる各ログ名の下にある斜体の設定が☆の部分に入る

Apache

ssl_access_log

format apache2

[ログ]

xx.xx.xx.xx - - [30/Dec/2013:16:49:34 +0900] "GET / HTTP/1.1" 304 -
xx.xx.xx.xx - - [30/Dec/2013:16:49:49 +0900] "GET /assets/jquery-i18n/messages_ja.properties?_=1388389789290 HTTP/1.1" 200 2589

[fluentd出力]

2013-12-30T07:49:34Z    some.tag {"host":"xx.xx.xx.xx","user":null,"method":"GET","path":"/","code":304,"size":null,"referer":null,"agent":null}
2013-12-30T07:49:49Z    some.tag {"host":"xx.xx.xx.xx","user":null,"method":"GET","path":"/assets/jquery-i18n/messages_ja.properties?_=1388389789290","code":200,"size":2589,"referer":null,"agent":null}

[メモ]
標準で用意されてるapache2を使えたので一番楽できた

ssl_error_log

format /^\[(?<time>[^\]]*)\] \[(?<level>[^\]]*)\] \[client (?<host>[^\]]*)\] (?<message>.*)$/
time_format %a %b %d %H:%M:%S %Y

[ログ]

[Mon Dec 30 17:53:50 2013] [error] [client xx.xx.xx.xx"] Premature end of script headers: hogehoge

[fluentd出力]
2013-12-30T08:53:50Z  some.tag  {"level":"error","host":"xx.xx.xx.xx"","message":"Premature end of script headers: hogehoge"}

[メモ]
host部分を取る方法が泥臭い

ssl_request_log

format /^\[(?<time>[^\]]*)\] (?<host>[^ ]*) (?<ssl_protocol>[^ ]*) (?<ssl_cipher>[^ ]*) "(?<method>\S+)(?: +(?<path>[^ ]*) +\S*)?" (?<size>[^ ]*)$/
time_format %d/%b/%Y:%H:%M:%S %z

[ログ]
[30/Dec/2013:17:53:44 +0900] xx.xx.xx.xx TLSv1.2 DHE-RSA-AES128-GCM-SHA256 "GET /assets/jquery-i18n/messages_ja.properties?_=1388393624678 HTTP/1.1" 2589

[30/Dec/2013:17:53:50 +0900] xx.xx.xx.xx TLSv1.2 DHE-RSA-AES128-GCM-SHA256 "GET /hogehoge HTTP/1.1" 534

[fluentd出力]
2013-12-30T08:53:44Z    some.tag        {"host":"xx.xx.xx.xx","ssl_protocol":"TLSv1.2","ssl_cipher":"DHE-RSA-AES128-GCM-SHA256","method":"GET","path":"/assets/jquery-i18n/messages_ja.properties?_=1388393624678","size":"2589"}

2013-12-30T08:53:50Z    some.tag        {"host":"xx.xx.xx.xx","ssl_protocol":"TLSv1.2","ssl_cipher":"DHE-RSA-AES128-GCM-SHA256","method":"GET","path":"/hogehoge","size":"534"}

[メモ]
こんなもんかな

access_log(combined + %{X-Forwarded-For}i)

format /^(?<host>[^ ]*) (?<remotelog>[^ ]*) (?<user>[^ ]*) \[(?<time>[^\]]*)\] "(?<method>\S+)(?: +(?<path>[^ ]*) +\S*)?" (?<status>[^ ]*) (?<size>[^ ]*) "(?<referer>[^\"]*)" "(?<agent>[^\"]*)" "(?<forwardedfor>[^\"]*)"$/
time_format %d/%b/%Y:%H:%M:%S %z

[ログ]
xx.xx.xx.xx - - [30/Dec/2013:16:49:32 +0900] "GET / HTTP/1.1" 304 - "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36" "-"

xx.xx.xx.xx - - [30/Dec/2013:17:18:19 +0900] "GET /admin/sign_in HTTP/1.1" 200 2038 "http://www.baidu.com" "Mozilla/5.0 (X11; U; Linux i686) Web-Security/1.0(it's for a research study,if you have questions,plz contact me liangw@cs.wisc.edu)" "-"

[fluentd出力]
2013-12-30T07:49:32Z    some.tag     {"host":"xx.xx.xx.xx","remotelog":"-","user":"-","method":"GET","path":"/","status":"304","size":"-","referer":"-","agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36","forwardedfor":"-"}

2013-12-30T08:18:19Z    some.tag     {"host":"xx.xx.xx.xx","remotelog":"-","user":"-","method":"GET","path":"/admin/sign_in","status":"200","size":"2038","referer":"http://www.baidu.com","agent":"Mozilla/5.0 (X11; U; Linux i686) Web-Security/1.0(it's for a research study,if you have questions,plz contact me liangw@cs.wisc.edu)","forwardedfor":"-"}

[メモ]
baiduアク禁にしとくか

error_log

format /^(?<message>.+)$/

[ログ]

[ pid=25565 thr=140417271351328 file=ext/apache2/Hooks.cpp:841 time=2013-12-30 17:53:50.689 ]: The backend application (process 13501) did not send a valid HTTP response; instead, it sent nothing at all. It is possible that it has crashed; please check whether there are crashing bugs in this application.

[fluentd出力]

2013-12-30T08:53:50Z    some.tag      {"message":"[ pid=25565 thr=140417271351328 file=ext/apache2/Hooks.cpp:841 time=2013-12-30 17:53:50.689 ]: The backend application (process 13501) did not send a valid HTTP response; instead, it sent nothing at all. It is possible that it has crashed; please check whether there are crashing bugs in this application."}

[メモ]
スタックトレースがあるので空行を削る以外の加工を諦めた

System

/var/log/messages

format syslog

[ログ]

Dec 30 17:15:01 xx.xx.xx.xx yum[14369]: Updated: 2:tar-1.26-27.21.amzn1.x86_64

[fluentd出力]

2013-12-30T08:15:01Z    some.tag       {"host":"xx.xx.xx.xx","ident":"yum","pid":"14369","message":"Updated: 2:tar-1.26-27.21.amzn1.x86_64"}

[メモ]
標準でsyslogが用意されていたので助かった

/var/log/secure

format /^(?<time>[^ ]* [^ ]* [^ ]*) (?<host>[^ ]*) (?<message>.*)$/
time_format %b %d %H:%M:%S

[ログ]
Dec 30 17:15:53 xx.xx.xx.xx runuser: pam_unix(runuser:session): session opened for user td-agent by ec2-user(uid=0)

Dec 30 17:15:54 xx.xx.xx.xx runuser: pam_unix(runuser:session): session closed for user td-agent

[fluentd出力]
2013-12-30T08:15:53Z    some.tag {"host":"xx.xx.xx.xx","message":"runuser: pam_unix(runuser:session): session opened for user td-agent by ec2-user(uid=0)"}

2013-12-30T08:15:54Z    some.tag {"host":"xx.xx.xx.xx","message":"runuser: pam_unix(runuser:session): session closed for user td-agent"}

[メモ]
スペースで分けられたtimeに初めて対処。ここのsyslogの項に学んだ

/var/log/cron

format /^(?<time>[^\]]*) (?<host>[^ ]*) (?<process>[^\[]*)\[(?<process_id>[0-9]+)\]: (?<message>.*)$/
time_format %b %d %H:%M:%S

[ログ]
Dec 30 17:05:01 xx.xx.xx.xx CROND[14223]: (root) CMD (/usr/sbin/logrotate /etc/logrotate.d/ooo)

Dec 30 17:10:01 xx.xx.xx.xx CROND[14297]: (root) CMD (/usr/lib64/sa/sa1 1 1)

[fluentd出力]
2013-12-30T08:05:01Z    some.tag   {"host":"xx.xx.xx.xx","process":"CROND","process_id":"14223","message":"(root) CMD (/usr/sbin/logrotate /etc/logrotate.d/ooo)"}

2013-12-30T08:10:01Z    some.tag   {"host":"xx.xx.xx.xx","process":"CROND","process_id":"14297","message":"(root) CMD (/usr/lib64/sa/sa1 1 1)"}

[メモ]
サンプル不足

/var/log/yum.log

format /^(?<time>[^\]]*) (?<action>[^:]*): (?<package>[^ ]*)$/
time_format %b %d %H:%M:%S

[ログ]

Dec 30 17:15:01 Updated: 2:tar-1.26-27.21.amzn1.x86_64

[fluentd出力]

2013-12-30T08:15:01Z    some.tag        {"action":"Updated","package":"2:tar-1.26-27.21.amzn1.x86_64"}

[メモ]
とりあえず仕掛けた。サンプル不足

/var/log/maillog

format /^(?<time>[^\]]*) (?<host>[^ ]*) (?<process>[^\[]*)\[(?<process_id>[0-9]+)\]: (?<message>.*)$/
time_format %b %d %H:%M:%S

[ログ]

Dec 30 16:58:42 xx.xx.xx.xx sendmail[13943]: NOQUEUE: SYSERR(ec2-user): can not chdir(/var/spool/mqueue/): Permission denied

[fluentd出力]

2013-12-30T07:58:42Z    some.tag        {"host":"xx.xx.xx.xx","process":"sendmail","process_id":"13943","message":"NOQUEUE: SYSERR(ec2-user): can not chdir(/var/spool/mqueue/): Permission denied"}

[メモ]
これでいいのか。サンプル不足

App(my app)

myapp.log (tab separated)

format tsv
keys user_id, ref_user_id, request_url, params,executed_at
time_key executed_at

[ログ]

28     22     https://xx.xx.xx.xx/18      {"controller"=>"users", "action"=>"detail", "id"=>"18"}       2013-12-30 08:26:57 UTC

[fluentd出力]

2013-12-30T08:26:57Z    some.tag    {"user_id":"28","ref_user_id":"22","request_url":"https://xx.xx.xx.xx/users/18","params":"{\"controller\"=>\"users\", \"action\"=>\"detail\", \"id\"=>\"18\"}"}

[メモ]
初めてのtsv。問題なくtime_keyも解釈できた様子

2013年12月15日日曜日

メタプログラミング本からプロコン本へ

メタプログラミングRubyを終えたので、この本を勉強会で進めている。

 

以前は第一版をRubyやPythonでやっていたのでC++で書いてる。以下は、今日やったところ。

2-1


DFS

  • 部分和問題
  • 水たまりカウント

BFS

  • 迷路問題


相変わらず面白いので今後も進める。

2013年9月29日日曜日

AWSについて少し話した話

某所にてAWSの使い方とか組み合わせ方を少し話してきた。

資料はここにあります。
やったことをかいつまんで記述する。

スキルについて話

よくあるアイスブレイク。参加者がこれまでどのようなことをやってきたか、今回どのようなことを期待してきたかを確認

ハンズオンその1

テーマ: 開発するアプリケーションの基盤としてAWSを使うのに必要なスキルを身につける

EC2, RDS, Route53, VPCあたりをハンズオンで進める。ドメインを持っている人が一人だけだったのでRoute53は説明だけ。あとはいわゆるWEB-DBアプリケーションを動かす基盤をVPC上で構築するところまでをゴールに進めた。

ハンズオンその2

テーマ: ログとかデータがAWSサービス間でどのように流れるかを理解する

S3, DynamoDB, Elastic MapReduceを使い、AWSサービス間でデータをどのように移行するかを話した。ただ、流石にいきなりハンズオンは厳しかったので概念を説明するにとどまった。Elastic MapReduceのサンプルHiveスクリプトやジョブ実行ファイルは用意した方がより分かりやすかったなと反省。DynamoDBにサンプルデータを入れるコードを用意するのが時間的に精一杯だった。

急ごしらえだったが4時間は軽く持ったのでよしとするか。

2013年9月28日土曜日

fluentdのテスト導入



メタプログラミングRuby勉強会で使ってた本(↑)が終わったので、
fluentdを触って遊ぼうみたいな話になった。
今回は手元でS3プラグインの動作確認まで。

fluentdのインストール

公式ページにしたがい入れた

fluent-plugin-s3のインストール

このページを見たりして入れた

S3プラグインを使う為の今の設定

その後、何度か設定を試したりして次の内容になった。

はまった点

fluentdと関係無いのだが、Macのmaxfilesオプションに引っかかってうまく起動しなかった。http://docs.fluentd.org/articles/before-install#increase-max--of-file-descriptorsをMacに読み替えて設定したらOK。設定の途中で間違えて数を減らしてしまいMacを強制再起動する羽目になった