Hatena::ブログ(Diary)

(ひ)メモ このページをアンテナに追加 RSSフィード

2014-07-14 (Mon)

github:eで管理しているChefのクックブックを、Berkshelf APIサーバーを立てていい感じに依存解決する方法

ちょっと前にリリースされた Berkshelf 3から、Berkshelf APIというインデックスサーバーからクックブックの情報を得るようになりました。

Berkshelf APIを使うと、外部に公開していないクックブックをBerksfileで指定する際の記述が簡潔になります。

まだ日が浅いせいか、Berkshelf API + github:e の情報が少ないので、備忘録も兼ねて残しておきます。

環境は以下のとおりです。

  • berkshelf (3.1.3)
  • berkshelf-api (2.0.0)

Berkshelf APIが必要な理由

Berksfile で cookbook キーワードに git: を添えることで、github:e からクックブックを持ってくることができます。

source 'https://api.berkshelf.com'

cookbook 'oreno-apache', git: 'git@github.oreno:cookbooks/oreno-apache.git'

さて、クックブック oreno-apache が oreno-iptables に依存している(oreno-apache/metadata.rbで depends 'oreno-iptables' している)とします。

この状態で berks を実行すると、oreno-iptables が見つけられなくてエラーになります。

$ berks vendor ./cookbooks
Resolving cookbook dependencies...
Fetching 'oreno-apache' from git@github.oreno:cookbooks/oreno-apache.git (at master)
Fetching cookbook index from https://api.berkshelf.com...
Unable to satisfy constraints on package oreno-iptables, which does not exist, due to solution constraint (oreno-apache = 0.1.0). Solution constraints that may result in a constraint on oreno-iptables: [(oreno-apache = 0.1.0) -> (oreno-iptables >= 0.0.0)]
Missing artifacts: oreno-iptables
Demand that cannot be met: (oreno-apache = 0.1.0)
Unable to find a solution for demands: oreno-apache (0.1.0)

原因は、内部の github:e にしかない oreno-iptables の情報をBerkshelf API (https://api.berkshelf.com) から得たインデックス情報から探そうとして、当然そこには情報がないためです。

なので、以下のようにBerksfileに oreno-iptables の情報を書けば、うまくいきます。

source 'https://api.berkshelf.com'

cookbook 'oreno-apache', git: 'git@github.oreno:cookbooks/oreno-apache.git'
cookbook 'oreno-iptables, git: 'git@github.oreno:cookbooks/oreno-iptables.git'

しかしこれでは使いたいクックブックが依存しているクックブックまでいちいち意識してトップレベルのBerksfileに書かないといけません。ひとつやふたつならまだいいですが、増えてくると面倒このうえないでしょう。

そこで内部のクックブックの情報を返すBerkshelf APIを立てることで、このようなBerksfileを書けばよいようになります。

source 'http://berks-api.oreno:26200'
source 'https://api.berkshelf.com'

cookbook 'oreno-apache'

簡潔ですよね?

最初のsourceを参照してクックブックが見つからなかった場合は、次のsourceを参照するので、これで内部クックブックとコミュニティクックブックの両方の情報を取得できるようになるわけです。

github:e の構成

Berkshelf APIサーバーから参照する場合、github:e 上のクックブックの管理は以下のような構成になっている必要があります。

  • organizationの下にクックブックのレポジトリを置く
    • ユーザーの下ではダメです (少なくとも berkshelf-api 2.0の実装では)
    • クックブック以外のレポジトリがあっても無視されますが、クックブック専用のorganizationを用意した方がいいでしょう
  • metadata.rbのversionをgit tagする
    • 「version '1.2.3'」なら「v1.2.3」というタグをつける必要があります
    • タグがついていないとクックブックと認識されなくてハマります

metadata.rb の書き方

Berkshelf APIサーバーは、github:e から当該クックブックの metadata.rb の「内容」を得て、それをもって instance_eval で評価します。

したがって、metadata.rb に外部ファイルを参照するようなコードを書いていると、例外が発生してスキップされクックブックとみなされなくなってしまいます。

例えば knife cookbook create で作ったクックブックの metadata.rb には、

long_description IO.read(File.join(File.dirname(__FILE__), 'README.md'))

と書かれているので修正する必要がります。(berks cookbookで作ったものは大丈夫です)

自分は ../recipes/*.rb を開いて「include_recipe」しているクックブックを全部 depends するRubyコードを書いていたのでハマりました。

Berkshelf APIサーバーを上げる

berkshelf-api のインストール方法は https://github.com/berkshelf/berkshelf-api を参照してください。

github:e にあるクックブックの情報を返すために、次のような設定ファイルを作って、

{
  "endpoints": [
    {
      "type": "github",
      "options": {
        "organization": "cookbooks",
        "access_token": "YOUR_ACCESS_TOKEN",
        "api_endpoint": "https://github.oreno/api/v3",
        "web_endpoint": "https://github.oreno",
        "ssl_verify": true
      }
    }
  ]
}

起動します。

berks-api -c /path/to/config.json

うまくいけば、26200番ポートの /universe にアクセスすればクックブックの情報が返ってきます。

$ curl http://127.0.0.1:26200/universe | jq .
{
  "oreno-apache": {
    "0.1.2": {
      "location_path": "https://github.oreno/cookbooks/oreno-apache",
      "location_type": "github",
      "dependencies": {
        "oreno-iptables": ">= 0.0.0"
      },
...

berks-api はかなり短い周期で github:e にアクセスしているようで、push された情報はほぼすぐに反映されるようです。

berks の設定をする

これで適切な URL からクックブックをダウンロードできる情報は揃ったのですが、github:e から実際にダウンロードするには berks の設定が必要です。

~/.berkshelf/config.json に次のように書きます。

{
  "github": [
    {
      "access_token": "YOUR_ACCESS_TOKEN",
      "api_endpoint": "https://github.oreno/api/v3",
      "web_endpoint": "https://github.oreno",
      "ssl_verify": true
    }
  ]
}

これで berks vendor で自家製とコミュニティの両方のクックブックが取得できるようになったはずです!

2014-06-12 (Thu)

bashでもpercolをイイ感じに使う方法

@(mooz)さん作のpercolはシェルとは独立しているのですが、ググるzshと共に使っている人が多いようです。これはzshの強力な行編集機能を提供するzleに因るところが大きいためだと思います。

bashでもpercolを使っている人もいるのですが、

「絞り込んだパスへのcdを実行する」ことはできても、「絞り込んだパスを現在のコマンドライン行に挿入する」ことは実現できていない、つまりコマンド実行はできるけど行編集はできていないようでした。

そこで今回ちょっと調べてみたところ、bashでも(zleには及ばないにしても)変数READLINE_LINEとREADLINE_POINTで行編集できることがわかったので紹介します。ただし、bash 4以上が必要です。

READLINE_LINEとREADLINE_POINT

READLINE_LINEには現在のコマンド行の内容が、READLINE_POINTにはカーソルの位置が格納されます。

例えば、

_readline_test() {
  {
    echo "LINE=$READLINE_LINE"
    echo "POINT=$READLINE_POINT"
  } >&2
}

bind -x '"\C-xx":_readline_test'

とし、対話シェル上で C-x x を打つとこのような結果になります。「■」の位置がカーソルの位置を示します。

$ ■
LINE=
POINT=0

$ foo bar baz■
LINE=foo bar baz
POINT=11

$ foo bar■baz
LINE=foo bar baz
POINT=7

percol --query に $READLINE_LINE 全体もしくは $READLINE_POINT までの部分文字列を渡せば、検索文字列が入った状態で percol を呼ぶことができそうです。


またこの READLINE_LINE と READLINE_POINT は変更することもできます。変更した場合、対話シェル上のコマンドライン行をその内容とカーソルの位置に変更できます。

例えば、

_readline_test() {
  READLINE_LINE="$READLINE_LINE hok"
  READLINE_POINT=${#READLINE_LINE}
}

bind -x '"\C-xx":_readline_test'

とした場合、C-x x を押す度に「 hok」が追加されカーソルが行末に移動するはずです。


続いては実例です。

コマンドヒストリー

C-r を percol を使うものに置き換えています。念の為、C-r だった reverse-search-history を C-x r に当てています。

ヒストリーはpercolの結果で完全に置き換えて欲しいので、READLINE_LINEにはそのまま絞り込み結果を代入し、READLINE_POINTにはその長さを代入しています。

_replace_by_history() {
  local l=$(HISTTIMEFORMAT= history | tac | sed -e 's/^\s*[0-9]\+\s\+//' | percol --query "$READLINE_LINE")
  READLINE_LINE="$l"
  READLINE_POINT=${#l}
}
bind -x '"\C-r": _replace_by_history'
bind    '"\C-xr": reverse-search-history'

gitのディレクトリ{ にcd | を挿入 }

自分は git clone したディレクトリは ~/repos の直下もしくは1階層サブディレクトリを掘った下に置いているので、percolでそれを絞り込むシェル関数はこのようになります。

_select_gitdir() {
  find ~/repos -maxdepth 3 -type d -a -name '.git' | \
    sed -e 's@/.git@/@' | \
    percol
}

そこへ移動したい場合は、単純にcdすればよいです。

_cd_gitdir() {
  cd "$(_select_gitdir)"
}
bind '"\C-xg": "_cd_gitdir\n"'

次に、コマンドライン行の現在のカーソル位置に、絞り込んだ git ディレクトリのパスを挿入する場合はこのようになります。

現在の行の内容のカーソル位置のところに絞り込んだ結果を挟んで、カーソル位置を挟んだ末尾に移動しています。

_insert_gitdir() {
  local l=$(_select_gitdir)
  READLINE_LINE="${READLINE_LINE:0:$READLINE_POINT}${l}${READLINE_LINE:$READLINE_POINT}"
  READLINE_POINT=$(($READLINE_POINT + ${#l}))
}
bind -x '"\C-x\C-g": _insert_gitdir'

最近移動したディレクトリの履歴

自分は昔から Bash の小枝集 で紹介されている cdhist.sh を使っています。これを使っていると配列変数 CDHIST_CDQ に最近移動したディレクトリが記録されているので、percolから利用してみます。

_cd_cdhist() {
  cd "$(for i in "${CDHIST_CDQ[@]}"; do echo $i; done | percol)"
}
bind '"\C-xj": "_cd_cdhist\n"'

screenのアクティブwindowを選択する

screen 4.1以上なら -Q windows でリストが取れるようなのでそれでできそうです。

が、諸般の事情で tmux に乗り換え中です><

tmux の例は https://github.com/mooz/percol#tmux をどうぞ!

peco

goで書かれた peco というのもあるようです。

バイナリだけコピーすれば使えるので、いろんな環境で使いたい場合はいいかもしれませんね!

2014-05-19 (Mon)

Apacheのmod_statusの各状態はどういう意味か?

Apacheのmod_statusを使えば各プロセスの状態を知ることができます。CloudForecastやCacti等でそれを元に視覚化している人も多いと思います。

mod_statusで確認できる状態には以下の11種類があるのですが、

状態名mod_stautsでの記号説明
SERVER_DEAD 0.Open slot with no current process
SERVER_STARTING 1SServer Starting up
SERVER_READY 2_Waiting for connection (or accept() lock)
SERVER_BUSY_READ 3RReading a client request
SERVER_BUSY_WRITE 4WProcessing a client request
SERVER_BUSY_KEEPALIVE5KWaiting for more requests via keepalive
SERVER_BUSY_LOG 6LLogging the request
SERVER_BUSY_DNS 7DLooking up a hostname
SERVER_CLOSING 8CClosing the connection
SERVER_GRACEFUL 9Gserver is gracefully finishing request
SERVER_IDLE_KILL 10IServer is cleaning up idle children

年に3回ぐらい「これって具体的にどういう状態なんですか?」と聞かれてテケトーに答えてたので、httpdが一生のうちどういう状態を変遷するのかと、いくつかの状態についてはソースも読みながら確認してみたのでそのメモです。

httpdの一生

Apache 2.2.27で、prefork MPMでのhttpdの一生はこんな感じでした。

  • 起動
    • DEAD -> READY
    • READY -> READY
  • リクエスト/レスポンス処理 (非 keepalive)
    • READY -> BUSY_READ
    • BUSY_READ -> BUSY_READ
    • BUSY_READ -> BUSY_WRITE
    • BUSY_WRITE -> BUSY_LOG
    • BUSY_LOG -> CLOSING
    • CLOSING -> READY
  • リクエスト/レスポンス処理 (keepalive)
    • READY -> BUSY_READ
    • BUSY_READ -> BUSY_READ
    • BUSY_READ -> BUSY_WRITE
    • BUSY_WRITE -> BUSY_LOG
    • BUSY_LOG -> BUSY_KEEPALIVE
    • BUSY_KEEPALIVE -> CLOSING
    • CLOSING -> READY

まずはDEADから始まり、もろもろ初期化を済ませると READY に遷移し、続いて生きてる限り回るループの中で(再度)READY に遷移し accept(2) してリクエストを待ちます。

リクエストが着弾してaccept(2)が抜けると、ap_read_requestによりHTTPリクエストを処理します。このap_read_requestの前にBUSY_READに遷移し、ap_read_requestが終わるとBUSY_WRITEに遷移します。

ここで留意すべきは、HTTPリクエスト処理を終えた時点でBUSY_WRITEに遷移するという点です。

低速な回線のクライアントへのレスポンスの送信の場合にBUSY_WRITE状態で滞留するのはもちろんですが、reverse proxy先のバックエンドサーバーの処理が遅い場合もBUSY_WRITEで滞留します

つまりBUSY_WRITEだけでは回線起因でレスポンス送信に時間がかかっているのか、バックエンドの処理が詰まっているのか判別できません。

バックエンドの処理が詰まっているかどうかは、その処理時間をバックエンドサーバーのログやレスポンスヘッダに入れる(で、フロントのApacheのアクセスログで記録してクライアントへのレスポンスからは当該ヘッダを消す)ようにしておけばよいと思います。

さて、無事にレスポンスを返し終えると、BUSY_LOGに遷移しap_run_log_transactionでログの記憶を行います。ちなみに、異常系で終了する場合も同じくBUSY_LOGに遷移してログを書き出します。

あとはCLOSING→READYと遷移し最初に戻ります。HTTP keepaliveが有効の場合は、BUSY_KEEPALIVE→CLOSING→READYという遷移になります。


ちなみにですが、少なくとも今回確認した Apache 2.2.27, prefork MPM, Linuxでは、

  • BUSY_DNS
  • IDLE_KILL

は使われていないようでした。

2003 | 11 | 12 |
2004 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 10 | 11 | 12 |
2005 | 01 | 02 | 03 | 05 | 08 | 09 | 10 | 11 | 12 |
2006 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2007 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2008 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2009 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2010 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2011 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 12 |
2012 | 01 | 02 | 03 | 06 | 08 | 10 | 11 | 12 |
2013 | 01 | 02 | 03 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2014 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 |