puppet-mode.el のパッチ
Puppet で同じタイプのリソースを大量に記述する時には、
package { sudo:
ensure => latest
}
package { ntp:
ensure => latest
}
package { ssh:
ensure => latest
}
なんて書くのはだるいので、
package {
sudo:
ensure => latest;
ntp:
ensure => latest;
ssh:
ensure => latest;
}
と書きたいのですが、Emacs + puppet-mode.el では、後者みたいな書き方だと、インデントが意図した通りにならなくて、イラっときてパッチ書いた。
=== puppet-mode.el
==================================================================
--- puppet-mode.el (revision 1800)
+++ puppet-mode.el (local)
@@ -106,7 +106,9 @@
(progn
(setq cur-indent (- (current-indentation) 2))
(setq not-indented nil))
- (setq cur-indent (- (current-indentation) 2))))
+ (setq cur-indent (- (current-indentation) 2)))
+ (if (looking-at "^.*;")
+ (setq cur-indent (- cur-indent 2))))
(if (< cur-indent 0) ; We can't indent past the left margin
(setq cur-indent 0)))
(save-excursion
@@ -125,6 +127,18 @@
(setq cur-indent (+ (current-indentation) 2))
(setq not-indented nil))
(if (bobp)
+ (setq not-indented nil)))
+ (if (looking-at "^.*:\s*$")
+ (progn
+ (setq cur-indent (+ (current-indentation) 2))
+ (setq not-indented nil))
+ (if (bobp)
+ (setq not-indented nil)))
+ (if (looking-at "^.*;")
+ (progn
+ (setq cur-indent (- (current-indentation) 2))
+ (setq not-indented nil))
+ (if (bobp)
(setq not-indented nil)))))))
(if cur-indent
(indent-line-to cur-indent)
他にもインデントまわりの動作で微妙なところはあるけど、ちょっとはましになった。インデントまわりの処理で参考になる emacs-lisp って何かないかな?
Trac の SearchHyperEstraierPlugin を Trac 0.10.x で動かすパッチ
SearchHyperEstraierPlugin が、Trac 0.10.x というか、うちの環境で動かないので、パッチ書いた。初 Python プログラミング。(というほどのものじゃないけど。)
=== searchhyperestraier/searchhyperestraier.py
==================================================================
--- searchhyperestraier/searchhyperestraier.py (revision 892)
+++ searchhyperestraier/searchhyperestraier.py (local)
@@ -2,8 +2,9 @@
# SearchRepositoryWithNamazu plugin
from trac.core import *
-from trac.Search import ISearchSource, query_to_sql, shorten_result
+from trac.Search import ISearchSource, shorten_result
from trac.util import NaivePopen
+from trac.util.text import to_unicode
from StringIO import StringIO
import urllib
import time
@@ -44,9 +45,9 @@
browse_trac = self.env.config.get('searchhyperestraier', 'browse_trac','enabled')
- cmdline = "%s %s %s %s" % (estcmd_path,estcmd_arg,index_path,unicode(query,'utf-8').encode(estcmd_encode))
+ cmdline = "%s %s %s %s" % (estcmd_path,estcmd_arg,index_path,' '.join(query))
self.log.debug('SearchHyperEstraier:%s' % cmdline)
- np = NaivePopen(cmdline)
+ np = NaivePopen( cmdline.encode(estcmd_encode) )
#self.log.debug('Result:%s' % unicode(np.out,'utf-8').encode('mbcs'))
#self.log.debug('Result:%s' % np.out)
if np.errorlevel or np.err:
@@ -74,7 +75,7 @@
attrelem_array = element.getElementsByTagName("attribute")
for attrelem in attrelem_array:
attr_name = attrelem.getAttribute("name")
- attr_value = unicode(attrelem.getAttribute("value")).encode('utf-8')
+ attr_value = to_unicode(attrelem.getAttribute("value"))
#URLとタイトルを生成
if attr_name == "_lreal":
attr_value=attr_value[len(replace_left):].replace("\\","/")
エラーが出るたびに、適当にいじってたら動いた、という感じ。なので、こんなんでいいのかはよく分からない。
Pushmi つかってます & 技術者募集中 at 福岡 3
弊社 は東京と福岡にオフィスがあり、それぞれの拠点に Subversion + Trac 環境を構築し、OpenVPN により VPN 接続してお互いの開発状況を公開しています。サービス絡みの開発は基本的に、東京は東京、福岡は福岡で完結しているので、この方式で問題はないのですが、サーバ管理関連のスクリプトなんかは、東京と福岡で共通するものが多いため、別々の SVN リポジトリで管理されてると不便なんですよね。かといって、どちらかの拠点だけしかリポジトリがないと、VPN の障害発生時に、リポジトリのない拠点からはまったくアクセスができない、という困ったことになってしまいます。
そこで、SVN リポジトリレプリケーションツール Pushmi を導入してみました。詳細は YAPC::Asia での 作者 Cl Kao によるスピーチ動画 を見て頂くとして、ここでは行った設定についてメモを残しておきます。ほとんど perldoc Pushmi の内容と同じですが。
まず当然 Pushmi のインストールが必要です。これの手順は省略。また、より良いアトミックロック実現のために memcached を利用している、ということなのでインストールしておきます。これも手順は省略。
memcached を起動します。
$ sudo /usr/bin/memcached -p 8123 -dP /var/run/memcached.pid -u nobody
/etc/pushmi.conf を設定します。弊社の環境では memcached のポートのみ指定してます。
authproxy_port: 8123
ミラーリポジトリを作成します。
$ pushmi mirror /var/db/my-local-mirror http://master.repository/svn
Mirror initialized.
ミラーリポジトリとマスターリポジトリを同期します。
$ pushmi sync /var/db/my-local-mirror
Retrieving log information from 1 to 62
ミラーリポジトリは、svnadmin create で作成するのと同様なディレクトリ、ファイル構造になってますが、Pushmi 用の pre-commit スクリプトと post-commit スクリプトが置かれている、というところが異なります。
pre-commit では以下の様なコマンドが実行され、ミラーリポジトリにコミットされた内容を、マスターリポジトリにコミットしに行きます。
/usr/bin/pushmi runhook $1 --txnname $2
post-commit では以下の様なコマンドが実行され、ミラーとマスターの整合性を確認しています。
/usr/bin/pushmi verify $1 --revision $2
Apache + WebDAV で SVN リポジトリにアクセスするために、以下の様な設定を Apache で行います。
<Location /svn/server>
PerlSetVar SVNPath /var/db/my-local-mirror
PerlSetVar Pushmi /usr/bin/pushmi
PerlSetVar PushmiConfig /etc/pushmi.conf
<LimitExcept GET PROPFIND OPTIONS REPORT>
AuthName "mirrored private area"
AuthType Basic
Require valid-user
AuthLDAPURL ldap://localhost:389/ou=people,o=paperboy?uid?sub?(objectclass=*)
PerlAuthenHandler Pushmi::Apache::AuthCommit
</LimitExcept>
</Location>
設定見て分かると思いますが、mod_perl を利用していて、Apche2 + mod_perl の環境が必要です。Apache は 2.0 系でも 2.2 系でも大丈夫なようです。弊社では 2.0 系を利用しています。
特にポイントとなるのは、
PerlAuthenHandler Pushmi::Apache::AuthCommit
の部分で、認証で渡されたユーザ名、パスワードをこのモジュールで memcached にキャッシュしておき、マスタリポジトリへのコミット時の認証に利用します。Apache 2.2 系の場合には、以下の様に設定します。
AuthBasicProvider Pushmi::Apache::RelayProvider
この状態では、ミラーへのコミットは即マスターに反映されますが、マスターへのコミットはミラーに反映されませんので、以下の様な cron 設定を行い、5分おきにマスターとミラーを同期するようにします。
*/5 * * * * /usr/bin/pushmi sync --nowait /var/db/my-local-mirror
今のところこれで問題なく動いています。
話変わりまして、弊社福岡支社では、プログラマ と サーバエンジニア を募集しています。Pushmi を実戦で使ってみたい!という方はぜひ。メガネ女子プログラマもいますよ。(東京本社にもいます。人妻ですが。)
mod_dosdetector を Apache 2.0 系で動かすパッチ 1
mod_dosdetecter 0.2 を Apache 2.0 系で動かすパッチを書いてみた。修正ポイントは、
- /usr/local/apache/include/pcreposix.h:41: error: redeclaration of enumerator `REG_BADBR’ といったエラーが大量に出てくるが、これは /usr/include/regex.h と宣言が重複しているため。なので、#include <regex.h> は削除。
- ap_regmatch_t 構造体と ap_regex_t 構造体は 2.2 系にのみ存在し、2.0 系には存在しない。なのでそれぞれ、regmatch_t, regex_t に置き換える。
- apr_shm_remove は 2.2系 でしか使えないので、そのための修正を mod_fcgid のソース からパクる。
- set_ignore_contenttype_config() がセグフォるので修正
=== mod_dosdetector.c
==================================================================
--- mod_dosdetector.c (revision 804)
+++ mod_dosdetector.c (local)
@@ -28,7 +28,6 @@
#include <arpa/inet.h>
//#include <netinet/in.h>
#include <time.h>
-#include <regex.h>
#include "httpd.h"
#include "http_config.h"
#include "http_request.h"
@@ -41,6 +40,7 @@
#include "apr_strings.h"
#include "apr_shm.h"
#include "apr_thread_mutex.h"
+#include "apr_version.h"
//#define _DEBUG
@@ -102,7 +102,90 @@
static apr_global_mutex_t *lock = NULL;
static apr_shm_t *shm = NULL;
+/* apr version 0.x not support apr_shm_remove, I have to copy it from apr version 1.x */
+#if (APR_MAJOR_VERSION < 1)
+#ifdef HAVE_SYS_MMAN_H
+#include <sys/mman.h>
+#endif
+#ifdef HAVE_SYS_IPC_H
+#include <sys/ipc.h>
+#endif
+#ifdef HAVE_SYS_MUTEX_H
+#include <sys/mutex.h>
+#endif
+#ifdef HAVE_SYS_SHM_H
+#include <sys/shm.h>
+#endif
+#if !defined(SHM_R)
+#define SHM_R 0400
+#endif
+#if !defined(SHM_W)
+#define SHM_W 0200
+#endif
+#ifdef HAVE_SYS_FILE_H
+#include <sys/file.h>
+#endif
+static apr_status_t apr_shm_remove(const char *filename, apr_pool_t * pool)
+{
+#if APR_USE_SHMEM_SHMGET
+ apr_status_t status;
+ apr_file_t *file;
+ key_t shmkey;
+ int shmid;
+#endif
+
+#if APR_USE_SHMEM_MMAP_TMP
+ return apr_file_remove(filename, pool);
+#endif
+#if APR_USE_SHMEM_MMAP_SHM
+ if (shm_unlink(filename) == -1) {
+ return errno;
+ }
+ return APR_SUCCESS;
+#endif
+#if APR_USE_SHMEM_SHMGET
+ /* Presume that the file already exists; just open for writing */
+ status = apr_file_open(&file, filename, APR_WRITE,
+ APR_OS_DEFAULT, pool);
+ if (status) {
+ return status;
+ }
+
+ /* ftok() (on solaris at least) requires that the file actually
+ * exist before calling ftok(). */
+ shmkey = ftok(filename, 1);
+ if (shmkey == (key_t) - 1) {
+ goto shm_remove_failed;
+ }
+
+ apr_file_close(file);
+
+ if ((shmid = shmget(shmkey, 0, SHM_R | SHM_W)) < 0) {
+ goto shm_remove_failed;
+ }
+
+ /* Indicate that the segment is to be destroyed as soon
+ * as all processes have detached. This also disallows any
+ * new attachments to the segment. */
+ if (shmctl(shmid, IPC_RMID, NULL) == -1) {
+ goto shm_remove_failed;
+ }
+ return apr_file_remove(filename, pool);
+
+ shm_remove_failed:
+ status = errno;
+ /* ensure the file has been removed anyway. */
+ apr_file_remove(filename, pool);
+ return status;
+#endif
+
+ /* No support for anonymous shm */
+ return APR_ENOTIMPL;
+}
+#endif /* APR_MAJOR_VERSION<1 */
+
+
static apr_status_t cleanup_shm(void *not_used)
{
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, "Notice: cleaning up shared memory");
@@ -261,8 +344,8 @@
address = r->connection->remote_ip;
- ap_regmatch_t regmatch[AP_MAX_REG_MATCH];
- ap_regex_t **contenttype_regexp = (ap_regex_t **) cfg->contenttype_regexp->elts;
+ regmatch_t regmatch[AP_MAX_REG_MATCH];
+ regex_t **contenttype_regexp = (regex_t **) cfg->contenttype_regexp->elts;
for (i = 0; i < cfg->contenttype_regexp->nelts; i++) {
if(!ap_regexec(contenttype_regexp[i], content_type, AP_MAX_REG_MATCH, regmatch, 0)){
//ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, 0, "ignoring content-type: %s", content_type);
@@ -390,15 +473,13 @@
const char *arg)
{
dosdetector_dir_config *cfg = (dosdetector_dir_config *) mconfig;
- char **ignore_contenttype = (char **) cfg->ignore_contenttype->elts;
+ regex_t *regexp;
+ char *type;
- *(char **) apr_array_push(cfg->ignore_contenttype) = apr_pstrdup(parms->pool, arg);
-
- int i;
- regex_t *regexp;
- for (i = 0; i < cfg->ignore_contenttype->nelts; i++) {
- regexp = (regex_t *)ap_pregcomp(parms->pool, (char *)ignore_contenttype[i], REG_EXTENDED|REG_ICASE);
- *(regex_t **)apr_array_push(cfg->contenttype_regexp) = regexp;
+ while (*arg) {
+ type = ap_getword_conf(parms->pool, &arg);
+ regexp = ap_pregcomp(parms->pool, type, REG_EXTENDED|REG_ICASE);
+ *(regex_t **)apr_array_push(cfg->contenttype_regexp) = regexp;
}
return NULL;
2.0 系でも 2.2 系でもどっちでも動くように修正した方がいいんだろうけど、それはまた今度。
Puppet の連載はじめました
gihyo.jp で Puppet に関する連載をはじめました。
第1回目は、システム管理の自動化がなぜ必要なのか、ということと、Puppet がどういったツールなのか、その概要について書いています。
この連載を一通り読めば、Puppet をシステム管理の現場で実践するための十分な知識が得られる、といったものを目指しています。
分かりにくいなどご意見ございましたら、ぜひお知らせください。
3週間毎に更新予定です。
write はユーザプロセスを待たせない?の答え 2
前回のエントリ について、naoya さんから僕の期待以上の素晴らしい回答を頂きました。感謝感謝です。
詳細は上記エントリをじっくり読んで頂きたいのですが、僕の疑問への直接の答えとなるのが、まとめにある
- write はプロセスを待たせない
- ただし明示的に sync する場合は待たせる
ですね。「write はプロセスを待たせない」が正しい、というのは naoya さんの解説を読んで納得しました。プロセスが write するということと、それが実際にディスクに書き出されることを、区別しないで考えていたのが僕の敗因ですね。
また、こちらの環境で kjournald や qmail-queue プロセスが待たされていたのは、sync してるから、ということでした。kjournald は確かめていないのですが、qmail-queue のソースの中では確かに fsync() が実行されていました。考えてみれば当然ですよね。ジャーナルのメタデータやメールキューなど、大事なデータを write して sync しないなんてことはあり得ないでしょうし。
なので、id:hirose31さんからのコメント「fsync(2)のせい?」がずばり回答になっていました。ありがとうございます。今度カレーおごらせてください。
というわけで、「write はユーザプロセスを待たせない?」は「待たせない」が答え、ということで完結です。
あと、
ということですが free の結果では、プロセスに割り当てるためのメモリが足りてるかどうかはわかりますが、ページキャッシュ用にメモリが足りてるかどうかはわかりません。
実際そのサーバーが主な仕事で使っているデータは、合計でどのぐらいのサイズでしょう。おそらく、キューに溜まったメールその他 OS 上で必要なデータのサイズをあわせると 800MB を超えるんではないかなあと思います。外してたらごめん。
もおっしゃるとおりですね。プロセスに必要なメモリ容量と、キャッシュに必要なメモリ容量をちゃんと区別していませんでした。
となると、メモリを増やせば書き込み性能が向上する可能性があるのでは、と思ったのですが、今回のケースではおそらく向上することはなさそうです。というのも、iostat の値を見てると、avgqu-sz が 1 日平均でおよそ 5、短時間で見ると 10 とか 20 あるいはそれ以上ということがざらにあります。avgqu-sz はキューに入っていて待ちとなっている I/O リクエストの数なので、常に処理待ちとなっている I/O リクエストが存在する、ということなります。なので、ページキャッシュの容量の問題ではなく、ディスクの性能がボトルネックになっている、と言えそうです。(これも自分の勘違いなら恥ずかしいなー…メモリを簡単に増やせるなら、試してみたいんですけどね。)
naoya さん、詳細な解説ありがとうございました。大変勉強になりました。僕のために時間を割いて長文エントリ書いてくれた、と思うとちょっと萌えました。(べっ、別にあんたのために…、とか言われそうですが。)
write はユーザプロセスを待たせない? 6
naoyaグループ - naoyaの日記 - I/O, iowait にちょっと反応してみます。
write はページに dirty フラグを立てるだけなので決してユーザープロセスを待たせない
って、本当にそうなんでしょうか?(否定しているわけではなく、純粋な疑問です。)
最近、書き込みの多いメールサーバのディスク I/O 周りを調査していて、実際にどのプロセスの書き込みが多いのかを調べる方法がわからなかったため、I/O 待ちになっているプロセスをカウントして、そこから類推してみようと、まずは
while [ 1 ]
do
ps -eo comm,state|grep D|grep -v COMMAND >> ps.txt
sleep 1
done
な感じで STAT が D のプロセスを記録するスクリプトをしばらく走らせておいて、
sort ps.txt | uniq -c | sort -gr| more
でカウントしてみると、
9129 kjournald D
6576 qmail-queue D
2017 multilog D
1897 pdflush D
1373 qmail-send D
1094 procmail D
1032 syslogd D
な感じで、kjournald と qmail-queue が圧倒的に多い。これってどっちも書き込みがメインの仕事のはず。
man ps で見ても、
D Uninterruptible sleep (usually IO)
なので、書き込みでプロセスが待たされてるのだと思うのですが、何か間違ってますでしょうか?
ちなみに、書き込みが多いというのはどれぐらいかというと、iostat での r/s と w/s の比率が 1:7 ぐらいです。
また、free の結果はこんな感じなので、メモリに余裕はあるみたいです。
# free
total used free shared buffers cached
Mem: 2043756 2011160 32596 0 351016 847776
-/+ buffers/cache: 812368 1231388
Swap: 1052248
ここ数日 I/O まわりを追いかけていたので、naoya さんのエントリはタイムリーでとてもためになります。ついでに色々教えてもらおうという甘い考えで、疑問に感じたことを書いてみました。
追記
もしかして「write はユーザプロセスを待たせない」というのは、非同期 I/O の話?であれば納得はいく。
FizzBuzz アセンブラ版 for x86/Linux
竹迫さん、Yappo さん に触発されて、FizzBuzz アセンブラ版 for x86/Linux をつくってみた。
20年ほど前に Z80 でアセンブラをちょっとかじった程度の知識しかないので、ベストには程遠いコードだと思います。だれかもっといいコードを教えてください。
最初竹迫さんのコードと同じように書けるかな、と思ったのですが、Windows とちがって、画面に表示するだけで EAX, EBX, ECX, EDX レジスタ使うので、竹迫さんのように BX レジスタを見張り役に、CX レジスタをカウンタに、ってことができませんでした。
また、とりあえず書いただけで疲れたので、コードゴルフにチャレンジする気力はありません。
global _start
_start:
mov si, 0
mawasu:
call space
inc si
mov ax, si
mov di, 3
xor edx, edx
div di
cmp dx, 0
je pfizz
mov ax, si
mov di, 5
xor edx, edx
div di
cmp dx, 0
je pbuzz
call num
cmp si, 100
jb mawasu
pfizz:
call fizz
mov ax, si
mov di, 5
xor edx, edx
div di
cmp dx, 0
je pbuzz
jmp mawasu
pbuzz:
call buzz
jmp mawasu
num:
mov ax, si
mov di, 100
cmp ax, di
jnge num2
jmp end
num2:
mov di, 10
cmp ax, di
jnge num3
xor edx, edx
div di
add ax, 48
call print
num3:
mov ax, si
mov di, 10
xor edx, edx
div di
mov al, dl
add al, 48
call print
ret
fizz:
mov al, 0x46
call print
mov al, 0x69
call print
mov al, 0x7a
call print
call print
ret
buzz:
mov al, 0x42
call print
mov al, 0x75
call print
mov al, 0x7a
call print
call print
ret
space:
mov al, 0x20
call print
ret
print:
push eax
mov eax, 4
mov ebx, 1
mov ecx, esp
mov edx, 1
int 0x80
pop eax
ret
end:
mov al, 0x0a
call print
mov al, 1
mov bl, 0
int 0x80
nasm を使ってこんな感じで動かすことができます。
$ nasm -f elf fizzbuzz.asm
$ ld -s -o fizzbuzz fizzbuzz.o
$ ./fizzbuzz

書いていてアセンブラの TMTOWTDI っぷりは Perl の比じゃないと思った。
puppetrun を試すならやっぱ trunk 版の方がよさげ
puppet 今試すなら バージョン 0.22.1 を使え、そして gem で入れるな というエントリを書いたわけですが、puppetrun で特に exec を使って puppet クライアントで何かコマンドを実行させたい、ということであれば、今は trunk 版を使ったほうがいいかも。
理由。まず以下の様な単純なコマンドを実行させる site.pp をつくる。
exec { 'touch':
path => '/bin',
command => 'touch /tmp/puppet',
}
で、puppetmasterd を puppet サーバで起動。
$ sudo puppetmasterd
次に puppet クライアント側で puppetd を起動。
$ sudo puppetd --listen --no-client --server kenny.southpark -v
そして、puppet サーバ側で puppetrun を起動して、設定した exec をクライアントで実行させる。
$ sudo puppetrun --host cartman.southpark
すると、0.22.1 では、クライアント側の puppetd がハングした状態になります。strace で見ると、以下の記述が延々と表示されるという状態に。
rt_sigprocmask(SIG_SETMASK, [], ULL, 8) = 0 rt_sigprocmask(SIG_SETMASK, [], ULL, 8) = 0 rt_sigprocmask(SIG_SETMASK, [], ULL, 8) = 0 rt_sigprocmask(SIG_SETMASK, [], ULL, 8) = 0 ... (以下繰り返し)
0.22.1 と 0.22.2 の間で、lib/puppet/type/exec.rb にかなり修正が入ってるので、おそらく 0.22.2 では大丈夫だろうけど、前エントリ で書いたように、0.22.2 ではそもそも puppetd がエラーで起動できない、という問題がある。なので、trunk 版を使うのがいい、という結論になりました。trunk 版だと問題なく実行できることを確認済み。
ただ、これも 前エントリ で書いたように、puppetrun に以下の一行を追加しないと動かないので注意。 fix された。自分の修正内容と違うけど。
require 'puppet/network/client/runner'
まだ実戦投入はやめておいた方がよさそうかな…。まだ検証段階なのでいいのですが。