h2oでmrubyを有効にしてビルドするにはruby >= 1.9が必要

h2o/1.5.2です。

h2oでmrubyを有効にするには、

cmake -DWITH_MRUBY=ON .

とすればよいのですが、ビルドを完遂するにはruby >= 1.9が必要です。

なぜなら、deps/mruby-onig-regexp/mrbgem.rake にこういう処理

    def run_command(env, command)
      STDOUT.sync = true
      Open3.popen2e(env, command) do |stdin, stdout, thread|
        print stdout.read
        fail "#{command} failed" if thread.value != 0
      end
    end

があるのですが、Open3#popen2e が使えるのは 1.9 からだからです。

マルチスレッドでgetaddrinfo(3)するとたまに Temporary failure in name resolution (EAI_AGAIN) で失敗する件

マルチスレッドでgetaddrinfo(3)するとたまにTemporary failure in name resolution (EAI_AGAIN) で失敗します。

自分は↓な環境で確認しました。

こういうコードで再現します。(ruby 2.1.4で確認)

なお、便宜的にrubyで再現コードを書いていますが、該当環境であれば言語問わずマルチスレッド+getaddrinfo(3)の組み合わせで発現する問題です。



対処法は、

rpmCentOS 6 Updatesにあるバージョン 2.12-1.149.el6_6.9 のに上げればOKです。


ちなみに自分は、Chef実行時にたまにberksのAPIサーバーの名前が引けなくてコケてたのがこれで直りました。

h2o試してみました、もしくはとりあえずサクッと既存のサイトをHTTP/2化する方法

先日、HTTP/2が正式な仕様として承認されると同時に、その実装であるH2Oのv1.0.0もリリースされました。

HTTP/2の情報はちょいちょいウォッチはしていたのですが、今までHTTP/2なサーバーを動かしたことはなく、いい機会なので自分のサイトをH2Oを使ってHTTP/2対応してみました。



大したことはやってないのですが、Apacheでサービスしており、認証やアクセス制限、ごにょごにょ黒魔術、CGI(!)やSSI(!!)なども動いているので、ApacheをH2Oにリプレースするのは無理でした。

そこで、H2Oをリバプーとして前段に置き、Apacheを後段に置く構成にしました。

設定ファイルはこんな感じです。

# -*- mode: yaml; -*-

user: www-data

max-connections: 1024
num-threads: 4

listen:
  host: 10.6.25.29
  port: 80
listen:
  host: 10.6.25.29
  port: 443
  ssl:
    certificate-file: /usr/irori/etc/ssl/cert_web/irori.org-cert.pem
    key-file: /usr/irori/etc/ssl/priv_web/irori.org-priv.pem
    cipher-suite: AES128-SHA:ECDHE-RSA-AES128-SHA256:AES128-GCM-SHA256:HIGH:MEDIUM:!aNULL:!MD5:!RC4

hosts:
  "*":
    access-log: /dev/stdout
    paths:
      /:
        proxy.reverse.url: http://10.6.25.29:9080
        proxy.preserve-host: ON

"www.irori.org"のグローバルIPアドレスの80と443宛てのはルーターで10.6.25.29にNATされるので、それをlistenします。

443の方は、SSLの設定をします。

証明書は StartSSL で無料で発行したもらったものです。

certificate-file には発行されたサーバー証明書を指定しますが、中間CAの証明書も必要な場合は、cat でくっつけたファイルを指定します。

くっつける際には、くっつける順序に注意してください。最初にサーバー証明書、つづいて中間CA証明書やクロスルート証明書という風に、信頼チェーンの順序にしましょう。


今回はまるっとApacheに投げたいので、hostsは1つだけです。

H2Oは、リクエストのHostヘッダに合致するものがなければ最初の項目の設定に従うので、便宜的に「"*"」として、そこでぜんぶひっかけてApacheに丸投げしています。


以上、HTTP/0.9ってのもあったよね〜〜と感慨にふけっている老害がお送りしました。

『Serverspec』を読みました

『Serverspec』を読んだ(ご恵贈ありがとうございました!)ので感想とかを書いてみたいと思います。

ちなみに、本書は1/17発売なので既に書店に並んでいると思いますし、

Serverspec

Serverspec

電子書籍版もあるのでお急ぎの向きはそちらを購入するのもいいかと思います。

Serverspec, Specinfra を拡張したい、内部を知りたい、使い倒したい人

Serverespec, Specinfraの生みの親であり本書の著者でもある mizzy ([twitter:@gosukenator]) さんが自ら、

その代わり、開発に至る経緯や開発する上での哲学、動作仕様や内部のアーキテクチャソースコードレベルでの拡張方法など、表から見ただけではわからないようなことをふんだんに盛り込んでいます。


Serverspec について詳しく解説するということは、必然的に Specinfra のことも詳しく解説することになるわけですが、ドキュメントがまったくない Specinfra についても、ソースコードレベルで詳しく解説していますので、Specinfra をベースに Serverspec や Itamae とは違う何かをつくってみたい、という方にもお勧めです。

http://mizzy.org/blog/2014/12/25/1/

と言ってるのでまちがいないです。さっさと購入して熟読しましょう。

自分も以前、ある機能を追加しようと思いざっとコード読んだのですが、ちょっと把握するのに時間がかかりそうだったんで mizzy さんに相談したら実装してくれた経験があるのですが、本書を読んだ今なら自分でできそうな気がしています!!

Serverspecを使えればいい、またはServerspecには特に関心がないインフラエンジニアの人

使うだけならネットに情報が豊富にあるのでググれば事足ります。本書でもそういった「使い方」の情報は最小限にとどめているので、そういった目的では本書はおすすめできません。


そういった目的では」です。


「Infrastructure as Code」とは何か?改めて説明するまでもないと思いますが、インフラエンジニアならもうこの潮流は避けて通れないでしょう。

ServerspecはこのInfrastructure as Codeの重要なピースの一つである「テスト」を担う実装であり、本書の第一章にはServerspecの生まれた経緯や必要性、利用目的が書かれています。こういった内容は、コードを読んだだけでは伝わらないのでとても貴重だと思います。

つまり、本書を読むことでInfrastructure as Codeにおけるテストの重要性や戦略をServerspecというプロダクトを媒介として知ることができます。たとえServerspecに興味がなかったとしても、Infrastructure as Codeを知る上でこれはとても有益であるでしょう。

まとめると

すべてのインフラエンジニアにおすすめです!!!

MySQL 5.5以降のutf8mb4とPerlのDBD::mysqlのmysql_enable_utf8のワナ

結論から言うと、ひろせが望ましいと思う順にこうすればいいんじゃないの?ってのを列挙します。

  1. my.cnfに[libmysqlclient]グループを追加しそこにdefault-character-set = utf8mb4と書き、DBD::mysqlでは mysql_read_default_file=/etc/my.cnf;mysql_read_default_group=libmysqlclient と指定する
    • mysql_enable_utf8を使いたい場合はSET NAMES指定も必要
  2. my.cnfの[client]グループにdefault-character-set = utf8mb4と書き、DBD::mysqlでは mysql_read_default_file=/etc/my.cnf と指定する。mysqlbinlogなど--default-character-setオプションを解せずエラーになるものは--no-defaultsをつけて実行する
    • mysql_enable_utf8を使いたい場合はSET NAMES指定も必要
  3. DBD::mysqlで接続後に SET NAMES utf8mb4 を実行する

オハマリポイントは

  • DBD::mysql (libmysqlclientなクライアント全般?) は loose-default-character-set の設定値を認識しない
  • select結果をPerlの内部表現文字列(UTF8フラグ)にしようと DBD::mysqlmysql_enable_utf8 を使うと、ついでにクライアント側も文字コード設定を(utf8mb4ではなく)utf8にしてしまう

です。

DBD::mysql (libmysqlclientなクライアント全般?) は loose-default-character-set の設定値を認識しない

my.cnfの[client]グループに書いた設定はクライアント全般に適用されるので、ここにdefault-character-set = utf8mb4 と書ければいいのですが、[client]グループに書くとmysqlbinlogなどこのオプションに対応していないクライアントがエラー終了してしまいます。

my.cnfの設定項目には loose- というprefixをつけることができて、対応していないクライアントはそれを無視することができます。

なので、[client]グループに loose-default-character-set = utf8mb4 と書いておけば、シアワセになれると思っていたのですが、loose- だとDBD::mysql (libmysqlcientを使うクライアント全般かもしれません)が認識しくれず、libmysqlclientのデフォルト文字コード(特に変えてなければ latin1)になってしまいます(ガーン)。

仕方ないので、[libmysqlclient]といったグループを新規で追加してそこにdefault-character-setを書き、DBD::mysqlにはそのグループを読むように指示するか、mysqlbinlogがエラーになるのは --no-defaults オプションをつけて回避するか、の2択になります。

SET NAMESよりdefault-character-setを推す理由は、エスケープ処理に関連するからです。詳しくはこのへんを参照してください。

select結果をPerlの内部表現文字列(UTF8フラグ)にしようと DBD::mysqlmysql_enable_utf8 を使うと、ついでにクライアント側も文字コード設定を(utf8mb4ではなく)utf8にしてしまう

の通りですが、DBD::mysqlmysql_enable_utf8 はPerlの文字列の内部表現化(sv_utf8_decode)するだけでなく、mysql_options(MYSQL_SET_CHARSET_NAME) も実行します。

  • mysql_enable_utf8 が 1
  • mysql_enable_utf8 が 0
  • mysql_enable_utf8を指定しない
    • mysql_options(MYSQL_SET_CHARSET_NAME) は呼ばない

なにが問題になるかというと、mysql_enable_utf8 を 1 にしてネコ (Unicode: U+1F639, UTF-8: \xF0\x9F\x98\xB9) をutf8mb4なテーブルにinsertしても、utf8mb4でなく MYSQL_SET_CHARSET_NAME=utf8 になっているので DBD::mysql::st execute failed: Incorrect string value というエラーでinsertできません。

また、mysql_enable_utf8 を 0 にした場合(MYSQL_SET_CHARSET_NAME=latin1 を実行している)やmysql_enable_utf8を指定しなかった場合(libmysqlclientのデフォルトであるlatin1が使われる)、my.cnfの適切なグループにdefault-character-set = utf8mb4と書いてあれば、character_set_clientや_connectionはutf8mb4になりますが、libmysqlclientのエスケープ処理は(多分)latin1で行われるのでもしかしたら問題があるかもしれません。

エスケープ処理が本当にlatin1で行われるのかと、その場合具体的にどういう問題があるのか、はちょっと調べきれてないので情報あったら教えてください><)

エスケープの問題を別にすれば、mysql_enable_utf8を有効にしてもしなくてもいずれにせよ、MySQLに接続した直後にSET NAMES utf8mb4を実行すれば、ネコもちゃんとハンドリングできます。

生のDBD::mysqlなら、Callbacksのconnectedでやるといいと思います。

my $dbh = DBI->connect($dsn, $user, $password, {
    ...
    Callbacks => {
        connected => sub {
            $_[0]->do('SET NAMES utf8mb4');
            return;
        },
    },
});

bashでsplitを書いてみた

空白絡むとどうにも配列で返せなかったんで、裏変数(_split)経由で結果渡すようにしてるのがイマイチ。。。

#!/bin/bash

set -u
set -e
export LANG="C"

split() {
  sep=$1
  str=$2

  _split=()
  if [[ $str =~ $sep ]]; then
    while IFS= read -r e; do
      _split+=("$e")
    done < <(echo "${str//$sep/$'\n'}")
  fi

  # declare -p _split >&2
}

split ':' 'foo:bar:baz'
declare -p _split
echo ">${_split[0]}<"

split ':' 'f o o:b a r:baz'
declare -p _split
echo ">${_split[0]}<"

split ':' ':'
declare -p _split
echo ">${_split[0]}<"

exit

結果:

declare -a _split='([0]="foo" [1]="bar" [2]="baz")'
>foo<
declare -a _split='([0]="f o o" [1]="b a r" [2]="baz")'
>f o o<
declare -a _split='([0]="" [1]="")'
><

PerlのDBD::mysqlをlibmysqlclient.aとstatic linkしたい話

static linkするにあたっての動機、諸注意(ハメがあるので必読)は [twitter:@sonots] さんの

を参照してください。

ここではDBD::mysqlをビルドする際のオプションのみ記します。

http://dev.mysql.com/downloads/mysql/ からダウンロードできるrpm

の場合、

$ ldconfig -p | grep libmysqlclient
        libmysqlclient.so.18 (libc6,x86-64) => /usr/lib64/libmysqlclient.so.18
        libmysqlclient.so (libc6,x86-64) => /usr/lib64/libmysqlclient.so

$ rpm -ql MySQL-devel | grep libmysqlclient.a
/usr/lib64/mysql/libmysqlclient.a

$ mysql_config --libs
-L/usr/lib64 -lmysqlclient -lpthread -lm -lrt -ldl

こういう感じの構成になっているので、

  • /usr/lib64の代わりにlibmysqlclient.aがある/usr/lib64/mysqlにライブラリパスを通す
  • libstdc++もリンクする

するように、このように

$ mysql_config --libs | sed -e 's@-L/usr/lib64@-L/usr/lib64/mysql@' -e 's@$@ -lstdc++@'
-L/usr/lib64/mysql -lmysqlclient -lpthread -lm -lrt -ldl -lstdc++

します。


ソースを展開してMakefile.PLを使ってビルドする場合は、--libsにこれを指定すればよいです。

$ perl Makefile.PL --libs="$(mysql_config --libs | sed -e 's@-L/usr/lib64@-L/usr/lib64/mysql@' -e 's@$@ -lstdc++@')"
$ make

$ ldd blib/arch/auto/DBD/mysql/mysql.so
        linux-vdso.so.1 =>  (0x00007fff9a52a000)
        libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f3434572000)
        libm.so.6 => /lib64/libm.so.6 (0x00007f34342ee000)
        librt.so.1 => /lib64/librt.so.1 (0x00007f34340e5000)
        libdl.so.2 => /lib64/libdl.so.2 (0x00007f3433ee1000)
        libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007f3433ccb000)
        libc.so.6 => /lib64/libc.so.6 (0x00007f3433936000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f3434d84000)
# ↑libmysqlclient.so を dynamic linkしていない

$ perl -Iblib/lib -Iblib/arch -MDBD::mysql -e 1
# ↑ちゃんとモジュールをロードできる

$ sudo make install

ちなみに -lstdc++ していない場合はこのようにモジュールのロードで失敗します。

$ perl -Iblib/lib -Iblib/arch -MDBD::mysql -e 1
Can't load 'blib/arch/auto/DBD/mysql/mysql.so' for module DBD::mysql: blib/arch/auto/DBD/mysql/mysql.so: undefined symbol: __cxa_pure_virtual at /usr/lib64/perl5/DynaLoader.pm line 200.
 at - line 0
Compilation failed in require.
BEGIN failed--compilation aborted.


cpanmでインストールする場合は、--configure-args="--libs=..." に指定すればよいです。

$ mylib=$(mysql_config --libs | sed -e 's@-L/usr/lib64@-L/usr/lib64/mysql@' -e 's@$@ -lstdc++@')
$ cpanm -n DBD::mysql --configure-args="--libs='${mylib}'"


Oracle謹製のrpmには先程書いた http://dev.mysql.com/downloads/mysql/ で配布しているものの他に、yumレポジトリで配布しているものもあります。

なぜか両者ではライブラリファイルの配置が異なっているので気をつけてください。

このように、

$ ldconfig -p | grep libmysqlclient
        libmysqlclient.so.18 (libc6,x86-64) => /usr/lib64/mysql/libmysqlclient.so.18
        libmysqlclient.so (libc6,x86-64) => /usr/lib64/mysql/libmysqlclient.so

$ rpm -ql mysql-community-devel |  grep libmysqlclient.a
/usr/lib64/mysql/libmysqlclient.a

$ mysql_config --libs
-L/usr/lib64/mysql -lmysqlclient -lpthread -lm -lrt -ldl

yumで配布しているrpmのはlibmysqlclient.soも.aも同じディレクトリ/usr/lib64/mysqlにあります。

このままではstatic linkできないので、*.aを別のディレクトリにコピーして、そこにライブラリパスを通してビルドしないといけません。

$ mkdir /tmp/oreno-lib
$ cp /usr/lib64/mysql/*.a /tmp/oreno-lib/

$ mysql_config --libs
-L/usr/lib64/mysql -lmysqlclient -lpthread -lm -lrt -ldl
$ mylib="-L/tmp/oreno-lib -lmysqlclient -lpthread -lm -lrt -ldl -lstdc++"

$ perl Makefile.PL --libs="${mylib}"
  OR
$ cpanm -n DBD::mysql --configure-args="--libs='${mylib}'"