2012/05/29

WinRM+HTTPS+Basic認証

拙作パソコンオン/オフを実装するに当たり、WinRMをサポートした時にハマったのでメモ。

1. レガシープラットホームは、事前準備が必要!
まず、WinRMのインストール状況を確認する。
Windows XPなどのレガシー環境には、デフォルトでインストールされていないので、
こちらをインストールする。


2. HTTPSには、専用鍵が必要!
OpenSSLなどでよくやるSSL鍵生成プロセスで作成したものは、対応していない。
EKU ( Enhanced KeyUsage )が必要となる。

WinRMでよく取り上げられる鍵生成は、
makecert -sk "test.net" -ss My -sr localMachine -r -n "CN=test.net, O=hoge, L=foobar" -a sha1 -eku "1.3.6.1.5.5.7.3.1"
などとWindows上で実行する方法がある。

OpenSSLでやる場合は、configファイルのv3_caセクションに、
extendedKeyUsage       = 1.3.6.1.5.5.7.3.1
と追記する。

3. 自己署名SSL鍵インポートが面倒くさい!
正確にインポートするには、mmcを開く→証明書スナップインを追加→ルート証明書+ドメイン証明書それぞれをインポートといった手順になる。
これを複数の環境に実施するは、面倒くさいのでバッチ処理するといい。
例えば、
using System.Security.Cryptography.X509Certificates;

class Program
{
  static void importCert(string certFile,StoreName storeName,string pswd){
    X509Certificate2 cert = null;
    if( pswd == null ){
      cert = new X509Certificate2(  GetAppPath() + "\\" + certFile );
    }else{
      cert = new X509Certificate2(  GetAppPath() + "\\" + certFile, pswd , X509KeyStorageFlags.PersistKeySet);
    }

    X509Store store = new X509Store(storeName, StoreLocation.LocalMachine);

    store.Open(OpenFlags.ReadWrite);
    store.Add(cert);
    store.Close();
  }

  public static string GetAppPath()
  {
    return System.IO.Path.GetDirectoryName(
        System.Reflection.Assembly.GetExecutingAssembly().Location);
  }

  static void Main()
  {
        importCert( "ca.crt", StoreName.Root,null);
        importCert( "ca.pfx", StoreName.My,"");
  }
}
といったimport.csなどを作成し、ca.crtとca.pfx( パスワード無し )をそれぞれ用意する。

後は、
C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\csc.exe import.cs
import
として実行する。
.NET2.0のcsc.exeを利用しているのは、VistaやWin7では初期導入されている為。
WinXPで、未だに2.0すら導入していない環境なんて皆無だろう...きっと。


4. HTTPSリクエスト用ポートを追加するのが、一番面倒!
面倒というか、一番ハマった。
Vista以降に導入されているWinRM2.0は、ポート作成に失敗すると、
再起動するまで挙動不審な状態に陥った。
一度ハマったら、証明書をmmc経由で登録しないと正常にポート追加できなかった。
環境依存かもしれない。
ポートを追加するには、
winrm create winrm/config/listener?Address=*+Transport=HTTPS @{Hostname="test.net";CertificateThumbprint="680b3b27620317079c2c5fecbc05d8d10395abd3";Port="5986"}
などと実行する。

5. Basic認証に対応するには、Client/Serverを分けて設定する!
WinRMが、面倒かつ混乱しやすいところだろうClient/Server認証。
非Windows環境からWinRMへリクエストを送信するだけの場合は、Server上で、
winrm set winrm/config/service/Auth @{Basic="true"}
と実行する。

Windows環境からの場合は、上記およびClient上で、
winrm set winrm/config/client/Auth @{Basic="true"}
と実行する。

CentOS4をメンテした

久しぶりにCentOS4をメンテしようと、yumってみたらどのリポジトリもCentOS5以降に移行してて参った話。
とりあえず、ググッてみると
http://vault.centos.org/4.9/
がリポジトリ最新版?らしい。
これを/etc/yum.repos.d/CentOS-Base.repoに適用すれば、リポジトリ操作を正常に行えるようになった。
...IE6同様に負の遺産は、早めにご退場願いたい。

2012/01/22

NetBeansでCoffeeScript

Railsに乗ってから一層有名になったCoffeeScript。
NetBeansでも対応しているので、試してみた。
CoffeeScriptプラグイン(記事投稿時v0.96)を利用すると、
  • 文法チェック
  • node.js不要で自動的にCoffeeScript->JavaScript変換
をしてくれる。
プラグインのインストールは、NetBeansのプラグインメニューから行える。
それでは、さっそくサンプルを書いてみる。
文法エラーがあると、JavasSript同様にエラー表示してくれる
文法エラーがなくなると、自動的に同名.jsファイルが生成される
ただし、コード補完やナビゲータなどが利用できない。
コンパイルされたJavaScriptファイルは、通常のJavaScriptファイルとして編集できる。
CoffeeScript言語仕様から抜粋
YAML形式オブジェクト記述しても、インデント対応していない。
ただし、インデントがズレていても文法エラーは検出されない
このように、JavaScriptの言語サポートには程遠いものの、とりあえず動いた。
ただし、YAML形式オブジェクトやIF~ELSEなどのインデントが一切サポートされていない。
この辺りは、コードテンプレート機能を利用すれば良さそう。
※コードテンプレートは、「テンプレート展開時」を「何もしない」に設定しないとインデントが破綻した。

CoffeeScript用エディタとしての品質は、Emacs + CoffeeScript Modeの方がよっぽどいい。

2012/01/19

Linuxユーザーに送る便利な10コマンド(訳)

Linuxユーザーに送る便利な10コマンド(訳)なる記事を眺めてたら、
確かに使う機会があれば便利そうだった。
とりあえず、意訳しつつ一部抜粋。

1. manページをpdf出力
man -t apt | ps2pdf - apt.pdf
別にmanページでなくてもいいけど、テキストファイルをPDF化するのに便利そう。

2. 他の同じディストロマシンへパッケージインストールを同期する
CentOSなどのRedHat系向け
ssh root@remote.centos "rpm -qa" | xargs yum -y install
UbuntuなどのDebian系向け(未検証)
ssh root@remote.debian "dpkg-query --showformat='${Package}\t${Status}\n' --show | grep 'install ok installed' | sed 's/install ok installed//g'" | xargs apt-get -y install
稼働している仮想ゲストOSのミドルウェアの都合でスナップショットを作成できないけど、
とりあえずパッケージとコンフィグを同期して同等環境を構築したい場合に使えそう。

3. PDFの各ページの先頭に指定テキストをスタンプ出力する
echo "ほげほげ" | enscript -B -f Courier-Bold16 -o- | ps2pdf - | pdftk input.pdf stamp - output output.pdf
ウォーターマーク的なものを挿し込みたい場合に使えそう。
pdftkを使い込めば、他にも色々できそう。

4. MySQLのコネクション数を出力する
mysql -u root -p -BNe "SELECT host,count(host) FROM processlist GROUP BY host;" information_schema
開発時や運用監視で使いそう。
MySQLなどのDBMSには、こうした1linerを使いたいシーンが多い。
ツールセットとして覚えておきたい。

5. リモートホストでTAR圧縮した内容をローカルホストに保存する1liner
ssh user@host "tar -zcf - /path/to/dir" > dir.tar.gz
考えれば当たり前のコトだけど、意外にやらない方法。
リモートホストを汚さずに済むし、転送も圧縮しながらされるなどのメリットがある。

9. 強度の高いパスワードを生成する(16文字)
dd if=/dev/urandom bs=1 count=32 2>/dev/null | md5sum | base64 | cut -c -16
本家内容では動作しないので、/dev/urandomを入力ソース指定した。

2012/01/18

DebianでPython + RabbitMQ( 2 )

前回、基本的な構築を終えたので、可用性と冗長性を加える。
RabbitMQ( 2.7.1 )には、Active/Activeクラスタリング機能が搭載されている。
これを利用すると、RabbitMQ間でキュー内容をミラーリングすることができる。
但し、キュー保存形式にDisk指定したクラスタを複数構成しても、
複製クラスタでの永続化は保証されていない。
プライマリークラスタにエンキューした内容は、複製クラスタ上のRAMへ即座に反映されるが、
Disk出力完了を待たずに同期完了として扱われる。
尚、この他に多重化モードとして、WAN間向けの「Federation」や「Shovel」がある。

クラスタリングの設定は、rabbitmqctlコマンドで行う。
複製クラスタを構築する(1Primary1Cluster構成時)
#cookie同期を全クラスタリングサーバーで実施
echo 'hogehoge' > /var/lib/rabbitmq/.erlang.cookie

#**以降、全てCluster**
#サービス初期化
rabbitmqctl stop_app
rabbitmqctl reset

#Clusterノード連携登録
#パターン1,2の何れかを実施
#
#パターン1: Primary(Disk)<->Cluster(RAM)構成時
rabbitmqctl cluster rabbit@primarymq

#パターン2: Primary(Disk)<->Cluster(Disk)構成時
rabbitmqctl cluster rabbit@primarymq rabbit@clustermq

#Cluster側RabbitMQサービス開始
rabbitmqctl start_app
以上を実行した後、
前回作成したConsumer/Producerをそれぞれプライマリとクラスタ上で実行して、
相互にキュー取得をできればクラスタリング設定は完了。

次に、縮退運転を検証する。
縮退運転の生じる状況では、
  1. Cluster上でRabbitMQが落ちたものの、他クラスタへアクセス出来る
    RabbitMQ自体の障害などで生じるケース。
    アプリ側で真っ当なコネクション管理を実装していれば、継続運転できる可能性が高い

  2. 他クラスタへアクセス出来ないが、キューが残っている
    他クラスタ側での項目1状態やネットワーク障害、ファイヤーウォールなどの設定誤り等で生じるケース。
    とりあえず残留キューを処理する。
    後は、順次他クラスタを復旧させる。
などがスプリットブレインと共に生じていると思われる。
スプリットブレインを発生させて、復元してみる。
#非同期、RabbitMQダウン前にエンキュー
python producer.py

#NICをダウンさせる
ifdown eth0

#nodedownを確認
rabbitmqctl cluster_status

#NICを復帰させる
#ダウンタイムが短い場合は、クラスタ復元処理をせずともNIC復帰後に再同期が一瞬に行われた
ifup eth0

#node_runningsを確認
rabbitmqctl cluster_status

#クラスタ再設定
rabbitmqctl stop_app
rabbitmqctl reset
rabbitmqctl cluster rabbit@primarymq
rabbitmqctl start_app

#RabbiMQダウン前に自身でエンキューした内容の受信
python consumer.py
などを試した結果、RabbitMQでスプリットブレインが生じると...
  • 非同期中にエンキューされた内容は、同期クラスタ間ないしエンキューしたクラスタから復旧したクラスタへ伝播しない
  • ネットワーク瞬断時は動作が安定せず、一瞬同期が行われるものの、
    継続した正常な同期は見込めない
となる。
要するに、スプリットブレインが生じたら一刻も早く復旧すべしということかと。
次回は、RabbitMQコネクション管理とprocmail周りを実装する。

DebianでPython + RabbitMQ( 1 )

Postfix + procmail + PHPで実装していたサービスを拡張することになった。
現状の問題点としては、
  • procmail駆動の為、受信ごとにPHPプロセスが立ち上がる
  • 冗長性や高負荷を考慮したメール処理の多重化が困難
  • PHPでバッチ処理って...
など色々とある。
そこで、procmail + Python( Queue + xmlrpclibXMLRPCServer )を検証実装していた。
とはいえ、単一サーバーのprocmail処理をプールする程度で、
本質的に冗長性や可用性を確保するには至らなかった。
そんな訳で、Postfix + Python + RabbitMQで実装することにした。

まずは、RabbitMQをインストールする
echo 'deb http://www.rabbitmq.com/debian/ testing main' >> /etc/apt/source.lst
wget -q http://www.rabbitmq.com/rabbitmq-signing-key-public.asc -O - | apt-key add -
apt-get update
apt-get install rabbitmq-server
次に、Python用モジュール(pika)をインストールする
apt-get install python-setuptools
easy_install pika
pikaのサンプルソースを実行してみる。
from pika.adapters import SelectConnection

# Create our connection object
connection = SelectConnection()

try:
    # Loop so we can communicate with RabbitMQ
    connection.ioloop.start()
except KeyboardInterrupt:
    # Gracefully close the connection
    connection.close()
    # Loop until we're fully closed, will stop on its own
    connection.ioloop.start()

これを実行して、 「AttributeError: 'ProtocolHeader' object has no attribute 'channel_number'」 といったエラーが出力される場合は、RabbitMQが2.0未満の可能性が高い。
稼働ディストロに応じた本家リポジトリなどから再インストールすると解決できる。

次にRabbitMQに掲載されている単純なProducer/Consumerを試す。
Producer( 「deliver_mode=2」でDisk出力 )[ producer.py ]
#!/usr/bin/env python
import pika
import sys

connection = pika.BlockingConnection(pika.ConnectionParameters(
        host='localhost'))
channel = connection.channel()

channel.queue_declare(queue='task_queue', durable=True)

message = ' '.join(sys.argv[1:]) or "Hello World!"
channel.basic_publish(exchange='',
                      routing_key='task_queue',
                      body=message,
                      properties=pika.BasicProperties(
                         delivery_mode = 2, # make message persistent
                      ))
print " [x] Sent %r" % (message,)
connection.close()

Consumer( 「prefetch_count=1」でマルチキャスト。指定しないとブロードキャスト )[ consumer.py ]
#!/usr/bin/env python
import pika
import time

connection = pika.BlockingConnection(pika.ConnectionParameters(
        host='localhost'))
channel = connection.channel()

channel.queue_declare(queue='task_queue', durable=True)
print ' [*] Waiting for messages. To exit press CTRL+C'

def callback(ch, method, properties, body):
    print " [x] Received %r" % (body,)
    time.sleep( body.count('.') )
    print " [x] Done"
    ch.basic_ack(delivery_tag = method.delivery_tag)

channel.basic_qos(prefetch_count=1)
channel.basic_consume(callback,
                      queue='task_queue')

channel.start_consuming()
おおまかには、この構成で実装する。

但し実戦投入するには、
  • クラスタリング構成時に自動的な縮退運転への切り替え
  • スプリットブレインなどの障害時のキュー復元
などの課題がある。
この辺りを次回検証する。

2012/01/08

Linuxにおけるファイル同期とクラスタリングファイルシステム:incron+Unison

rsyncほどファイルシステム同期で話に上がらないUnison
*nixのみならず、Windowsでも走ったりと優秀なんだけどなぁ..
ちなみに2台以上で構成する場合は、スター型( 更新系1 × 参照系1~N )が推奨されている。

けれど、スター型が保証できるコンテンツ/インフラであれば、別にスター型でなくてもいい気がする。
さっそくインストールするが、Debianなら、
apt-get install unison
と簡単。 ローカルでの利用では、
unison origin sync_dest
などとする。 リモート2拠点間では、
unison origin ssh://192.168.1.1/sync_dest
などとSSH経由が推奨されているが、
#@remote host(192.168.1.1)
unison -socket 5963

#sync from another
unison origin socket://192.168.1.1:5963/sync_dest
などとしてSOCKETモードなるデーモンモードもある。
ただし、こちらの場合はセキュリティなどの都合で非推奨。

上記手順は、あくまでも任意で同期する場合の手順で、PULL型同期となる。
このままでは実運用には耐えないので、inotifyイベント発生時に同期するようincron(inotifyイベントのcron的な代物)を仕組む。
Debianなら、
apt-get install incron
echo 'root' > /etc/incron.allow
incrontab -e
 # origin IN_MODIFY,IN_ATTRIB,IN_CREATE,IN_DELETE,IN_CLOSE_WRITE,IN_MOVE unison -batch origin sync_dest
となどとする。
但し、inotifyイベントの指定はザックリなので、稼働状況に応じて調整する必要有り。

ちなみに、
cat /proc/sys/fs/inotify/max_user_watches
とし、出力される数値が監視対象ディレクトリ数となる。
必ず、対象ディレクトリ数を上回らないように調整すること。
これを怠ると、超過時にincronが正常動作せず同期処理が不完全に行われることがある。

これを踏まえるとリモート2拠点間構成の場合は、
incrontab -e
 # origin IN_MODIFY,IN_ATTRIB,IN_CREATE,IN_DELETE,IN_CLOSE_WRITE,IN_MOVE unison -batch origin socket://192.168.1.1:5963/sync_dest
とするといい。
incron経由では環境変数などが設定されずに実行されるので、
正常動作しない場合は、
vi sync.sh
#!/bin/sh
. /etc/profile
. /etc/bash.bashrc
unison -batch origin socket://192.168.1.1:5963/sync_dest
といったスクリプトを経由すると回避できる。

但し、この手法はいわゆるPUSH型同期となるので、3拠点以上になると更新サーバーへの負担が線形的に増加する。
この場合は、参照サーバー間でPULL型とPUSH型を併用するなどして回避できるけれど、
この簡易的な同期手法ではそもそもお門違いなので別真っ当なクラスタリング手法を検討したほうがいい。

最後に、split-brainが生じた場合は以下の手順で復旧する
  1. 衝突と変更内容を確認する
  2. 更新サーバーのincronを停止する
  3. 手動で同期する
  4. 更新サーバーのincronを復旧する
尚、Windows環境ではincron実装がないので、.NetのFileSystemWatcherクラスをPowerShellなどで利用して代替実装をする必要がある。

[パフォーマンス測定結果]
ディレクトリ数 2878
ファイル数 31281
ファイルサイズ合計 4591789950 byte (4.4GB)
同期開始遅延 28秒
同期速度 8.2780MB (8分49秒)

[テスト環境bonnie++実行結果]
bonnie -d /root/ -n 0 -u root -b
Version  1.96       ------Sequential Output------ --Sequential Input- --Random-
Concurrency   1     -Per Chr- --Block-- -Rewrite- -Per Chr- --Block-- --Seeks--
Machine        Size K/sec %CP K/sec %CP K/sec %CP K/sec %CP K/sec %CP  /sec %CP
testhost        512M   892  94 35214   4 28256   4  4735  92 +++++ +++ 128.0   2
Latency             15100us    4294ms     530ms    4539us    3900us     696ms
[所感]
仮想環境間で試験した為、思うようなIOパフォーマンスが通常転送でもでなかった。
その最中とはいえ、通常運用では生じないような大量ファイル同期をシナリオとして扱った。
やはり大量同期は不得手のようだったものの、マスタIO書き込み完了はGlusteFSより速い。
ちなみに、初回同期はunison側で同期カタログを作成するため、
通常同期以上にCPU負荷と同期開始遅延が生じる。

他に気になった点としては、
  • vimなどの書き込み一時ファイルが同期対象に含まれると同期エラーが生じる
  • ファイル新規作成時は、同期遅延が生じやすい
導入環境は、
  • 静的画像などで低頻度の更新が生じるコンテンツの簡易クラスタリング
  • バックアップなどのアーカイブサーバー間の簡易クラスタリング
  • *nix<->Windows間の簡易クラスタリング
  • 多少の同期遅延が許容されるDR環境
が適していそう。

2012/01/04

Linuxにおけるファイル同期とクラスタリングファイルシステム

Linux環境でファイル同期を実現するのに、数多ある手法からいくつか雑感と共にまとめてみた。 今後、それぞれ検証してみる。
プロダクト名 incon +
Unison
lsyncd + rsyncd GlusterFS DRBD Lustre OCFS2 GFS2
ストレージ ディレクトリ,ファイル ディレクトリ,ファイル ディレクトリ(Fuse) ブロックデバイス ブロックデバイス ブロックデバイス ブロックデバイス
プロトコル inotify
+ rsync?
inotify + rsync 独自 独自 独自 独自 独自
C/S構成 C x N + S x N C x N + S x N C x N + S x Y C + S,
(C + S) + C
MetaData (1 ~ 2)
+ C x N + S x Y
(C + S) x N (C + S) x N
レプリケーション 1世代xN 1世代xN 1世代xN 1世代xN N世代xN 1世代xN 1世代xN
マルチマスター △(別途FS実装)
制限,課題 inotifyリソース制限有り。
Windows環境では
デッドロックする場合があるらしい
inotifyリソース制限有り CPU負荷が高い 結局はシングルマスタ メタサーバー多重化が厄介? コミュニティーサポートが手薄 RedHat系以外での導入は面倒
IO雑感と予想 読み取り速い.同期遅い。
但し、rsyncよりエコな同期?
読み取り速い.同期遅い 全体的にやや遅い シングルマスタなら
IO共に速い
C/S増加の影響は限定的。
読み取り速い
C/S増加と共に逓減 C/S増加と共に逓減