Quantcast
Channel: A Day In The Boy's Life
Viewing all 287 articles
Browse latest View live

Apache Solrのschema.xmlを読み解く

$
0
0

前回の「Apache Solrのデモ環境を作ってみる 」にて動く環境が作れたので、今回はもう少し突っ込んだApache Solrの設定ファイル周りについて書いていきたいと思います。



Apache Solrの設定ファイルの中身


Apache Solrの環境設定で触るファイルは大きく2つあります。

1つは、Solrの動作自体を定義するsolrconfig.xmlファイル。

そしてもう1つは、Solrに取り込むデータのスキーマ情報を定義するschema.xmlファイルです。

何れも、


/path/to/solr/example/solr/collection1/conf

に、存在します。

先のエントリでも書きましたがcollection1はSolrのコアディレクトリとなるため、コアを追加したらそのコアごとに設定ファイルが存在します。


順に説明したいところではありますが、solrconfig.xmlファイル(Solrの管理メニューのConfigメニューで見える内容もこのファイル)はSolrの動作を定義したもので、Solrが利用するjarファイルのパスやディレクトリの定義をしたり、検索クエリの細かな仕様やキャッシュ周りの設定などができるのですが、そのままでも取あえずは動くため一旦割愛します。


今回は、前回のデモ環境で作った住所録データベースのデータインポート作業をしたときに編集した、schema.xmlファイルについて少しだけ触れてみます。

下記が、前回のデモ環境用の作ったschema.xmlの全体です(一部コメントなどを割愛しています)。


<?xml version="1.0" encoding="UTF-8" ?>
<schema name="example" version="1.1">
    <types>
        <fieldType name="string" class="solr.StrField" sortMissingLast="true" omitNorms="true"/>
        <fieldType name="date" class="solr.DateField" sortMissingLast="true" omitNorms="true" />
        <fieldType name="long" class="solr.LongField" omitNorms="true" />
        <fieldType name="textTight" class="solr.TextField" positionIncrementGap="100" >
            <analyzer>
                <tokenizer class="solr.WhitespaceTokenizerFactory" />
                <filter class="solr.SynonymFilterFactory" synonyms="synonyms.txt" ignoreCase="true" expand="false" />
                <filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt" />
                <filter class="solr.WordDelimiterFilterFactory" generateWordParts="0" generateNumberParts="0" catenateWords="1" catenateNumbers="1" catenateAll="0" />
                <filter class="solr.LowerCaseFilterFactory" />
                <filter class="solr.RemoveDuplicatesTokenFilterFactory" />
            </analyzer>
        </fieldType>

        <!-- N-gram analyzed type using CJKAnalyzer -->
        <fieldType name="text_cjk" class="solr.TextField">
            <analyzer class="org.apache.lucene.analysis.cjk.CJKAnalyzer" />
        </fieldType>

        <!-- Morphologically analyzed type using JapaneseAnalyzer -->
        <fieldType name="text_ja" class="solr.TextField">
            <analyzer class="org.apache.lucene.analysis.ja.JapaneseAnalyzer" />
        </fieldType>
    </types>

    <fields>
        <field name="id"           type="long"      indexed="true" stored="true" required="true" />
        <field name="organization" type="textTight" indexed="true" stored="true" />
        <field name="zip-old"      type="textTight" indexed="true" stored="true" />
        <field name="zip"          type="textTight" indexed="true" stored="true" />
        <field name="prefecture"   type="text_ja"   indexed="true" stored="true" termVectors="true" termPositions="true" />
        <field name="district"     type="text_cjk"  indexed="true" stored="true" termVectors="true" termPositions="true" />
        <field name="town"         type="text_ja"   indexed="true" stored="true" termVectors="true" termPositions="true" />
        <field name="prefecture-undevided" type="string" indexed="true" stored="true" termVectors="true" termPositions="true" />
        <field name="full_address" type="text_ja" indexed="true" stored="true" termVectors="true" termPositions="true" multiValued="true" />
        <field name="timestamp" type="date" indexed="true" stored="true" default="NOW" multiValued="false" />
        <field name="_version_" type="long" indexed="true" stored="true"/>
    </fields>

    <uniqueKey>id</uniqueKey>

    <copyField source="prefecture" dest="prefecture-undevided" />
    <copyField source="prefecture" dest="full_address" />
    <copyField source="district"     dest="full_address" />
    <copyField source="town"             dest="full_address" />

    <defaultSearchField>full_address</defaultSearchField>

    <solrQueryParser defaultOperator="OR" />
</schema>


schmea.xmlは大きく3つの領域に分けて定義します。

1つ目がtypesタグで括られた利用するフィールドタイプやトークナイザーやフィルタを定義する箇所、2つ目がfieldsタグで括られた実際にSolrへ取り込む際のデータフィールドを定義する箇所、そして3つ目がその他のSolrの設定や取り込む際のデータ加工を指示する箇所です。



Solrで利用するfieldTypeを定義する


設定する1つ目の項目にあるtypesタグ内に定義するのは、フィールドタイプやトークナイザーなどを定義する箇所です。

ここでは、Solrへ取り込む際のデータの型を決めたり、取り込む際の解析に利用するフィルターや単語を解析するためのトークナイザーを定義します。

トークナイザーが何をするものなのかは、下記の記事が参考になるかと思います。


検索エンジンの常識をApache Solrで身につける (1/4) @IT


フィールドタイプは、後のfieldsタグの項目と連動します。

例えば、


<fieldType name="long" class="solr.LongField" omitNorms="true" />

という記述でlong型のフィールドを定義していますが、


<field name="id" type="long" indexed="true" stored="true" required="true" />

の中で、そのlong型を利用することを宣言しています。

要は、データをSolrに取り込んだり検索をする際にそのデータをどう取り込めば(扱えば)よいのかを決めているわけです。


トークナイザーは、データの中から意味ある単語をどう抽出するのかというものですが、形態素解析とN-gramの2種類があり、上記の設定ファイルの中では


<!-- N-gram analyzed type using CJKAnalyzer -->
<fieldType name="text_cjk" class="solr.TextField">
<analyzer class="org.apache.lucene.analysis.cjk.CJKAnalyzer" />
</fieldType>

<!-- Morphologically analyzed type using JapaneseAnalyzer -->
<fieldType name="text_ja" class="solr.TextField">
<analyzer class="org.apache.lucene.analysis.ja.JapaneseAnalyzer" />
</fieldType>

のように、「text_cjk」(N-gram用)と「text_ja」(形態素解析用)にて定義しています。

そして、実際の住所録データの中では、


<field name="district" type="text_cjk" indexed="true" stored="true" termVectors="true" termPositions="true" />
<field name="town" type="text_ja" indexed="true" stored="true" termVectors="true" termPositions="true" />

の中で使われており、このデータの中では市や区のデータが入った「district」には「text_cjk」(N-gram)が、町村などのデータが入った「town」には「text_ja」(形態素解析)が利用されています(何故使い分けているのかはわかりませんが)。


その他に、Solrで用意されている幾つかのトークナイザやフィルタを組み合わせたオリジナルのフィールドタイプを作ることもできます。


<fieldType name="textTight" class="solr.TextField" positionIncrementGap="100" >
<analyzer>
<tokenizer class="solr.WhitespaceTokenizerFactory" />
<filter class="solr.SynonymFilterFactory" synonyms="synonyms.txt" ignoreCase="true" expand="false" />
<filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt" />
<filter class="solr.WordDelimiterFilterFactory" generateWordParts="0" generateNumberParts="0" catenateWords="1" catenateNumbers="1" catenateAll="0" />
<filter class="solr.LowerCaseFilterFactory" />
<filter class="solr.RemoveDuplicatesTokenFilterFactory" />
</analyzer>
</fieldType>

上記では、トークナイザーに「WhitespaceTokenizerFactory」が利用されています。

これは、単純にスペースの区切りで単語を分解するトークナイザです。

その他に、フィルターの中で定義している「SynonymFilterFactory」は、単語の別名を定義するものであったり、「StopFilterFactory」ではNGワードを定義したりもできます。

利用できるトークナイザーやフィルタは、マニュアル にも記載しているのでそちらもあわせて確認してください。



Solrで利用するfieldsを定義する


設定する2つ目の項目のfieldsタグでは、Solrに取り込む各フィールドのデータ型や取り込む際のオプションを定義します。

データ型の話は、先ほど書いたとおりでtypesの中で定義したデータ型を各フィールドにて定義していきます。

fieldsタグの中には幾つかの属性を設定でき、データの扱いを制御できます。

今回のschema.xmlで追加されている属性とその意味は下記の通りです。


属性 設定できる値 意味
indexed true | false フィールドをインデックス(検索対象)するかどうか
stored true | false フィールドを検索結果に含めるかどうか(インデックスはするけど検索結果には含めたくないというようなことができる)
multiValued true | false 複数の検索フィールドを追加することを許可するかどうか
required true | false 必須項目にするかどうか
omitNorms true | false 検索スコアに関連する項目でそのキーワードのスコアの平均点を保持するかどうか(検索スコアに関連しない項目などの場合、falseに設定することでメモリ使用量を抑えることができるらしい)
termVectors true | false 検索キーワードが含まれる数やキーワードの開始、終了位置などを結果に含めるかどうか(termPositionsとtermOffsetsをtrueに設定した場合、termVectorsもtrue扱いとなる)
termPositions true | false 検索キーワードの含まれる位置を返すかどうか
termOffsets true | false 検索キーワードが含まれるオフセットを返すかどうか
default デフォルトに設定したい文字列 そのフィールドのデフォルト値


また、この定義ファイルの中では書かれていませんが、ダイナミックフィールドといってアスタリスクを使って、フィールド名(name)に特定の文字が含まれるものを一括して特定のデータ型や属性を指定するというやり方もできたりします。

属性に設定できる値も含めて、マニュアル を参考にしてみてください。



schema.xmlで定義するその他の設定


3つ目の設定項目では、データフィールドを加工したり検索の挙動を定義するオプションを書いたりします。
今回の設定ファイルの中で、最初に登場するのが


<uniqueKey>id</uniqueKey>

というものですが、これは見ての通りデータの中のユニークキーが何なのかを定義しています。

SolrのRESTなAPIでは、データに対して更新や削除をする際にIDを指定して行うため一意なキーが必要となります。


次に、


<copyField source="prefecture" dest="prefecture-undevided" />
<copyField source="prefecture" dest="full_address" />
<copyField source="district"     dest="full_address" />
<copyField source="town"             dest="full_address" />

という設定ですが、こちらは既に定義済みのフィールドを別のフィールドにコピーするという設定です。

上記の場合、都道府県から市区町村までのデータをコピー(source)してfull_addressという新しいフィールドを作って(dest)います。

検索は通常どこか特定のフィールドに対して行うため、このfull_addressのように複数のデータを連結したフィールドを作っておけば検索にヒットさせやすくなったりします。


続いては、


<defaultSearchField>full_address</defaultSearchField>

という設定項目は、先ほど作ったfull_addressのフィールドをデフォルトの検索フィールドに指定しています。

そして、最後に


<solrQueryParser defaultOperator="OR" />

検索オプションとして、AND検索にするのかOR検索にするのかを指定しています。


このように、schema.xmlではデータの型を定義したり取り込みデータのフィールドやその加工、そして検索のオプションなどが指定できます。

schema.xmlはデータ取り込みの際に必ず必要となりますので、自分の環境に合わせて参考にしてみてください。






PR: 今までにない部屋探しサイト「Nomad.(ノマド)」

$
0
0
理想の部屋を登録して物件を紹介してもらえる、新しい部屋探しサイト「Nomad.」

エンジニアの情熱と暴走モード

$
0
0

若いエンジニアに仕事を頼むと、時にやりすぎなところがあったりして悩むときがあったりします。

それはあらぬ方向に突っ走られるというよりは、頼んだ以上のことを実装したりするといったシーンです。

それって別にいいことなんじゃないの?って思われるときもあるのですが、プロジェクトを管理しているとそこに時間を割くのではなく他に手を回したい、でも作ったものはそれはそれで素敵な機能なんで叱るに叱れないといった状況に悩んだりするわけです。



うまく暴走させてあげるのに大事なレール


特に経験の浅いエンジニアは作る楽しさを覚えて間もなかったりもしますから、予想以上に工数かかっているなって確認してみると想像で突っ走ったりしてとんでもないものを作ろうとしていることはしばしばあります。

ちゃんと仕様を伝えていないこちらも悪いところがあるのですが、あまり細かく管理していくのもエンジニア自身の成長の妨げになりますし、自分もそうやって自由に作ることでスキルを磨いてこれたりしたものですから、自分の言うものだけを作れというのはあまりよくないなと考えていたりします。


視点が狭いというかどうしても技術よりな観点になってしまうのもうなずけるのですが、何を作るのかというよりはどう実装するかという観点に考えがよりがちです。

ドアを作ってくれと頼むと自動ドアを作ろうとしていたりするのですが、こっちとしてはそもそも人を通すための出入り口があればよいわけで最悪不恰好な穴でもあけてくれればいいわけですが、職人気質が働くのか立派なドアを一生懸命に作ろうとするわけです。

何でここにドアを作らないといけないのかという視点ではなく、とにかくドアを作らないといけない、どうやってどんなドアを作ろうかということに偏向してしまっているのをよく見かけたりします。

こういったことは自分自身にも当然経験があるわけで、視点を広げるというのはある程度の経験を経てじゃないとなかなか難しいことではありますから、上がきちんと間違った方向に進んでいないかコントロールしてあげる必要はあるなと感じています。


もう一つありがちなのが、与えた仕事から飛びついてしまうという点です。

今、しかかり中のものがあるにもかかわらず「Aくん、この件ちょっと相談したいんだけどクライアントからの要望でこういう機能を作ってくれない?」というと、言われたことだからすぐにやらないといけないというバイアスが働くのか、優先度の付け方がうまくできずにLIFO形式にタスクをこなしてしまったりします。

ですので、こういった事にならないようにあえて情報を渡さないというような管理も必要になってきたりします。

そんなに優先度の高くない仕事であれば、たとえ内容がまとまっていても本人に伝えずに温存しておくというか。

優先度の管理というものを引き取ってしまった方が本人的にも目の前のことに集中できますし、こちらとしても複数のタスクを同時並行的に見る必要も無いのでスケジュールの進捗が見えやすかったりします。


ただ、先に書いたようにこと細かい管理というのはエンジニア自身のモチベーションを下げることにもなりますし、こちらもそんな細かく管理するだけの余力もありません。

きちんとゴール設定をしてあげ、マイルストンごとに成果物を確認していくようにしてベクトルが違う方向にむき出したら都度修正していくというような形で踏み外さないレールを敷いておくというのは必要かなと思うわけです。



暴走の果ての成果を無駄にしない


エンジニアの情熱というのは、そのシステムを組み上げる原動力の1つです。

ですから、例え突っ走ってしまって自分の想像の斜め上をいくものができたとしても頭ごなしに否定して叱るというのはよくないと思います。

むしろ、その成果物をどう使うかという方が本人のモチベーションを下げないですし、自動ドアができたらできたで使い道は全然あるわけです。


もし、通常は使わないような機能が実装されてしまったとしても、オプションとして扱ってしまえば便利機能の一つということで片付けられたりもしますから成果を無駄にせずにうまく活かす方法を探した方が賢明です。

そういった成果は独りよがりで作ってしまったものだったりもしますので、機能の目的として論点がずれてたりするものが多かったりもするのですが、要らないと捨てるよりもいっそのこと、その機能をより拡張していく方向に進めてしまえば結果的に正しい方向に突っ走ったことにしてしまうこともできます。


こういったエンジニアの暴走を目の当たりにしてしまうと「何でそんなことに時間を割いているんだ」という気分にもなってしまうこともあるのですが、よくよく考えてみると管理する立場の人がその人の能力であったり、プロジェクトのスケジュールやタスクの優先度を鑑みて設定した仕様であり、その仕様を超えるような成果であれば怒る理由もありません。

意外とそこまでの機能は要らないと思っているのは自分だけであったりして、クライアントに見せると喜ばれたりもします。

まぁ、元々単なるドアを作る想定で工数を積んでいたものが、その期間で自動ドアができたんなら万々歳って気分になるのはわかるんですが、管理する立場としてその暴走を見て気が気じゃないのは単に自分の決めた枠にはまらず行動しているというエゴなのかもしれません。


コミュニケーションミスで全く逆方向のものを作り上げられたらそれは大問題だったりもしますが、とりあえず前に進んでいるのであれば、こういったエンジニアの情熱をうまく活かして正しい暴走をさせてあげるのが重要なんだなと思います。

暴走するほどの情熱を削ぐのは管理者として正しくないでしょうし、そのパワーって他に変えがたく与えようとしてもなかなか与えられないわけですから、右往左往しながらも前へ前へ推し進めていくマネジメントが必要なんだなと感じています。





システム部の失敗

$
0
0

華やかに始まったプロジェクトも時の流れとともに衰退し、今となってはあの時の栄光と熱意はどこに?といったチームの惨状を見ることはしばしばあります。

ユーザーに提供するサービスの場合は、そのユーザー離れにより衰退していく様を利用する立場としてよく目にしますが、社内情報システム部門のそれは構築・運用チームは存在しており、その会社のシステム基盤として維持・管理はしていかないといけないんだけど、チームとしての士気は下がりきっていてどうしようもない状況というのはより悲惨な光景かもしれません。

何でそんな状況にいたったんでしょうか?



1. 外部リソースの総動員


プロジェクトの始まりは極めて華やかだったりします。

予算が確保され、タスクフォース的にその道のプロが要員計画に組みこれます。

そこには下請けやコンサルタントなども含めて大量の外部リソースが流入してきます。


プロジェクト運営において、外部リソースを使うのはスケジュールであったり、そもそも自社にそのスキルやノウハウが無いという状況であれば外部から取り入れるというのは至極当たり前のことではあるのですが、現場のエンジニアから見たらあまり外部のリソースに頼る状況うれしいものではありません。

自分たちだけではできないのか、という信頼されていないような疑念を持ったりしますし、自分たちの畑を荒らされるのが嫌だという気分にもなってきます


自社の文化と合わない人も来たりして、プロジェクト期間中は職場の雰囲気がだいぶ変わることもあり、ホームなのにアウェイ感が出て仕事のやりにくさを感じ出すエンジニアもいます。

特に、プロジェクトによって招き入れた人がプロジェクトマネージャやオーナーのつてだったりした場合は最悪で、職場内での立場が逆転することさえあります。

こうなってくるとますますチームや組織への不満や信頼がなくなってくるわけです。



2. 残らないスキル


プロジェクトは当然紆余曲折ありながらもゴールへと着地します。

しかし、社内情報システムの場合はここがスタートラインです。

長い長い運用フェーズの始まりです。

リリース後の1年近くはシステムが安定しなかったり、プロジェクトの制約により延期された機能の実装、ユーザー部門からのフィードバックを受けてブラッシュアップの期間が続きます。

そして、システムは安定期に入ります。


こうなってくるとプロジェクト開始当初にいた大量のリソースは当然不要になりますので、1人また1人とチームを去っていきます。

残るのはアップデートされていない大量のドキュメントと口伝によるノウハウです。

そして、その情報はますます陳腐化していきもはや誰も真実の仕様をしらないか、一部の生き残りが生きる伝説として重宝されだしたりします。


プロジェクト期間中のコアメンバーが外部リソースで塗り固められていた場合、社内にはそのスキルを持っている人がいないので、もはや末路は火を見るより明らかで触らぬ神になんとやらと言うように触れないシステムが不良資産として残るわけです。



3. 持てない愛着と誇り


不良資産を抱えたチームは当然それを保守・運用するモチベーションを見出しにくくなります。

自分が作り出したものであれば何時までも愛着によって維持できるかもしれませんが、他人が作ったものとなるとなお更かもしれません。


なんせ自分でもよくわからないシステムなわけです。

現状維持することに精一杯になっていますし、これは自分が作ったものだ!自分がこのサービスを動かしているのだ!なんて誇りを持てないわけですから、それをよりよい方向にしようなんて考えを誰も持たなくなってきます

取りあえず動いているサービスを維持することが仕事となり、そのサービスがどう思われようと関係ない気分になってきます。



4. 存在しない危険因子


社内システムの場合、環境はなるべく安定するように意思決定されますので、ライフサイクルが長くなりがちですし、危険因子が少ないのでリスクをとるようなことを避けたりします

なるべく動いている今の環境を活かそうとしますし、外部サービスのように別のサービスによって客を持っていかれたり食いつぶされるというリスクがありません。

これによってシステムは安定するかもしれませんが、誰もそこに作る楽しさや運営する喜びを見出すのが難しくなってきます。


「システム部を変えよう!」なんてスローガンが常套句のように毎年発表されますが、危険因子がない分、自分たちにさえ変え方がわからなかったりします。

当然社内からの圧力や不満というのは肌で感じることがあるので、変わらなきゃいけないという焦りを持つことはあるのですが、変わることでのメリットより変えることでのリスクを嫌います。


安定した環境でのエンジニアは新たに得るものが無いのでスキルを磨いたり、多少火を噴いたりしつつも収束に向かって結束して乗り切ったときの達成感を得ることがないわけです。

変化が無いところにモチベーションは見出させません



5. 主導権を握れない


モチベーションがあがらない仕事に対して自ら先導するような行動を取ることは難しいでしょう。

あえて行動するとすれば、自分たちに降りかかるリスクをなるべく回避しようとするマイナスのモチベーションです。

「いや、この時期は忙しいから無理だ」と自分たちを守るために機能改修の依頼を断り、「業務が回らなくなるからこういった機能を付けてくれないと困る」という部分最適化した依頼がユーザー部門から来たりしてその応戦だけに多くの時間を費やしているシーンをよく見かけます。


ユーザー部門もシステムのことはわからず、自分たちの業務をよりよくするためという名目であれやこれや注文を伝えてくるわけですが、当然業務的には正しくともシステム運営的に正しいとは限りません

システム部門が面倒が無いように行動をすることで、やがて当初の綺麗に設計されたコードは崩れ落ち、受身のシステム部門は何時までも主導権を握れずにいます。

言われるままに追加したいくつもの使わないボタンが画面を埋め尽くし、めったに使わない機能に多くの工数が費やされそして消えていきます。


自ら主導権を握れず握ろうともしないので、システム部としての存在意義を発揮できないでいるわけです。



まとめ


見てきた中での一例として書いていきましたが、後手後手に回る計画がますます身動きを取れない組織を作り出していきますし、今を乗り切るための安易な施策が未来の組織をどんどん壊していきます。


やはりシステム部というからにはその中心にはエンジニアがいないと成り立たないと思いますし、そのエンジニアのモチベーションを上げるためには、自分たちがその業務やサービスをコントロールしているのだという、中の人としての意識を芽生えさせてあげるということにあるのではないかと感じています。





PR: 豪華夏の特別号受付中★今が入会のチャンス!/ベネッセ

Apache Solrをマルチコアに対応させる

$
0
0

前回「Apache Solrのデモ環境を作ってみる 」にてApache Solrへのデータの取り込みをしてみましたが、Apache Solrはマルチコアに対応しており、複数のデータベースを作ることが可能です。

例えば、本番用とテスト用のコアを作って別々に管理したり、検索対象のアイテムによってコアを分けて検索結果を別々にするということが可能になります。



Apache Solrにコアを追加する


コアの追加方法は管理サイトから簡単に可能そうなのですが、この機能だけではどうもうまく動作してくれません。

下図のように「Core Admin」メニューの「Add Core」から追加しようとすると


Error CREATEing SolrCore 'new_core': Unable to create core: new_core

とエラーがでて作成できません。


A Day In The Boy&#39;s Life-ApacheSolrのコアの追加


そもそもの仕様ではありそうなのですが、一部の操作をサーバー上で手動で行う必要があります。

まず、Solrのコア用のディレクトリに移動し、既存でうまく動作しているコアディレクトリをコピーします。


$ cd /path/to/solr/example/solr/
$ cp -r collection1 new_core/

次に、追加したコアディレクトリ(new_core)の設定ファイル(conf/schema.xml)や辞書ファイル(conf/lang/userdict_ja.txtやconf/synonyms.txtなど)を編集します(これはコアの動作確認が取れた後でも問題ありませんが)


次に、コピーしたコアディレクトリにデータが入っている場合は、データをクリアしておきます。

Apache Solrのデータの削除(クリア)方法は、dataディレクトリの中身を削除するだけです。


$ cd /path/to/solr/example/solr/new_core/data
$ rm -rf ./*

これで再度Apache Solr管理画面から「Add Core」でコアを追加すればうまく登録できます。

登録後は、Apache Solrを再起動させます。


実は、ここまでやったらApache Solr管理サイトを使わずとも全て手動で作業もできたりもします。

コアディレクトリをコピーした後(データファイルの削除後でもいいですが)、solr.xmlを編集して新しいコアの情報を追加することでもコアの登録が行えます。


# cd /path/to/solr/example/solr
# vi solr.xml

以下のように新しいコアの情報(new_coreの箇所)を追加します。


<?xml version="1.0" encoding="UTF-8" ?>
<solr persistent="true">
  <cores defaultCoreName="collection1" adminPath="/admin/cores" zkClientTimeout="${zkClientTimeout:15000}" hostPort="8983" hostContext="solr">
    <core schema="schema.xml" loadOnStartup="true" instanceDir="collection1/" transient="false" name="collection1" config="solrconfig.xml" dataDir="data"/>
    <core schema="schema.xml" loadOnStartup="true" instanceDir="new_core/" transient="false" name="new_core" config="solrconfig.xml" dataDir="data"/>
  </cores>
</solr>

solr.xmlファイルの編集後にApache Solrを再起動させCore Adminメニューを見てみると新しいコアが追加されているはずです。



Apache Solrのコアを削除する


コアの追加をするんだったら、コアの削除方法も覚えておかなくてはなりません。

コアの削除は、追加手順を逆順で実施するだけで問題ありません。


まず、管理サイト上のCore Adminメニューから該当のコア名を選択し、Unloadボタンをクリックします。
そのまま「OK」ボタンをクリックすると該当のコア情報が削除されます。

ただし、これもsolr.xmlファイルからこのコアの情報が削られるだけなのでデータファイルなどのコアディレクトリは残ったままです。

あとは、サーバー上でコアディレクトリを削除します。


$ cd /path/to/solr/example/solr/
$ rm -rf ./new_core

順番を間違えて先にコアディレクトリを削除してしまうと、下記のように管理サイト上でエラーが出てしまいます。


new_core: org.apache.solr.common.SolrException:org.apache.solr.common.SolrException: Could not load config for solrconfig.xml

この回避方法は、solr.xmlファイル上に残っている該当のコア情報を手動で削除するか、または再度追加してSolrに正しく認識させることです。

あとは、手順を正しくリトライしましょう。





PR: 豪華夏の特別号受付中★今が入会のチャンス!/ベネッセ

変わらない情報システム部としての使いどころ

$
0
0

情報システム部門またはそれに類する部門というのはどこの企業にも存在するかと思いますが、その存在意義については各所で危機感を煽る記事が掲載されています。

旧態依然のままの情報システム部門というのは、これまでの社内で募った不満もあいまってクラウドなど変化が早い外部サービスなどに取って変わられるのではないかという懸念です。

ただ、変わらない存在というものが企業内に存在するというのは、サービスの運営面ではよい場合というのもあるのではないかと感じることがあります。



抜け殻となったサービスとその衰退


私は、社内で多くのシステム開発を担当してきましたが、最初は意気揚々と要件を売り込んでくる担当者の熱にほだされ大々的にサービスインしたシステムが、その担当者がいなくなった途端に一気に衰退するという様を何度も見てきました。

運営という立場をしっかり担う人がいなくなっては、如何に軌道にのりまくっているサービスであっても辿る道は下降線でしかありません


軌道に乗ったんだから誰にでも運営できるだろうと考える人が多く、その担当者の能力も買われたりして引き剥がされると、その人の力によって維持していた揚力が失われて失速することになります。

立案した人はある程度、そのサービスに対しての熱意を持っているので維持するように努めますが、引き継いだ人は海のものとも山のものともわからないそのサービスに対してそこまで思い入れがありません。

適当に仕事をしているわけではありませんし、そのサービスの品質を落としている意識もないわけですが、徐々にそのサービスは下降の一途を辿るのはよく見てきた光景です。


これは担当者だけの問題でもなく、その運営を担うチームであったり組織であったりもするわけで、どちらかというと会社の中ではこの問題の方が大きかったりします。

というのも、組織は毎年のようにその役割によって変化していくわけで、その度にサービスを維持していた運営チームは解体の危機にさらされます

こういった企業としての変化の波に比較的影響を受けにくいのが人事であったり総務であったり広報であったり、そして情報システム部門であったりもします。

中の人は変わるにしろ、役割というのは維持されるわけです。


こういったことを逆手に取られてか、運用を維持できないサービス部門のお荷物が情報システム部門にまわされるというのはよくあることではありますが・・・。



変化に取り残されるシステム


サービス部門は、その企業としての方向性など経営指示によって体制がどんどん変化していきます。

その変化に伴って生み出される課題を解決するための仕組みづくりを情報システム部門として協力するわけですが、その変化のために取り残されていくシステムというのが多々あります


先ほど書いたように、それを運営する組織がなくなることで衰退し、また数年後に同じ課題を解決するための仕組みづくりのPJが立ち上がるという具合に。

まぁ、良いか悪いかはおいといてそういったスクラップ&ビルドを繰り返すのに割り切ってしまっているところもあるので、課題は次に持ち越すとしてその場を解決できればいいやというのもありなのかもしれませんが、やはり全体最適化を目指したいという情報システム部門の立場から見るとコストがかかるやり方であったり、数年後とに同じ要件をヒアリングして「その課題前にも聞いたんだけどな」とか「また業務改革とかいってるよ」とか「えー何度目のSFAなんでしょうか?」とかいう心の声を押し殺して対応していっているわけです。


確かに現場手動で、現在巷に溢れるクラウドなど外部サービスを使えば、手軽に課題解決をするソリューションは見つかりそうです。

しかし、それを運営する組織がなくなり維持できなくなった場合に屍となって累々と積み重なるサービスを見て果たしてそれでよかったのか?という疑問も出てきます。

一時の課題の解決にはなったでしょうが、組織は変われどそこに人とその課題は残ったままでしょう。
結局、解決した課題が新しい組織の元で新しい課題として作り直す作業を何度となく繰り返す必要がでてきます。



サービスを動かすも止めるも人の力


ということで、何も情報システム部門が変わらないことが正義というわけではなく、その役割を担い続けることができる組織があるというのは結構重要で貴重なんだな、といいたかっただけです。


こういった視点はエンドユーザーとなるその他の組織の人間にはなかなか見えない観点ではあります。

自分たちが困っているからどうにかしたいという思いはあり、それを解決する方法も知っているんだけど、それって部分最適になりがちで作っては捨てての繰り返しになるという課題を組織として変わらない情報システム部門がうまくサポートしてあげれば全体最適化されてよい方向にむけることができるのではないかということです。


きっと、その課題の渦中にいる人たちも「なんであのシステムが終わるんだろう」とか「なんでもっと改善できないんだろうか?」というような疑問や苛立ちはたくさん持っているのでしょう。

ただ、やはり重要なのはそれを支える中の人の存在で、その人がいない・いなくなったではサービスを支え続けるのは難しいことになります。

こういった人は、特にシステムに詳しいという人が必要なのではなく、どっちかというとその業務を支える役割にある人を巻き込んだ方が活気が出ます

営業管理部門や営業支援部門などその業務を支える役割の部門とかもあったりしますから、そういった人たちが現場での課題を取りまとめてこういった対策を取りたい、そしてそれを維持するための運用というのも自分たちが見るのはかまわないという意気込みがないと長続きはしません。


当然、サービスというのはその時のニーズに沿って変化していくべきでしょうし、社内の状況も刻一刻と変わっていくわけですから、情報システム部門もそれに応える柔軟な体制とサービス維持が必要でしょう。

結局はサービスとはいえ、それを支えるのは人であり、それを良くするにも悪くするにも、それを運営する人の想いであったり、熱意であるということは事実だと思います。






CakePHPのエラーページをカスタマイズする

$
0
0

404(Not Found)や500(Internal Server Error)などページが無かったり内部エラーが発生した場合や、処理の過程で発生した例外エラーなど、何か問題があったときに表示するエラーページをカスタマイズしたいという要件はよくあります。


CakePHPでは、そういったカスタムエラーページを所定の手続きにそって行えば簡単に作ることができます。



404や500エラーが発生したページのカスタマイズ


CakePHPではデフォルトでHTTPステータスコードが404や500が発生した場合に出力するテンプレートを持っています。


/path/to/cakephp/app/View/Errors

この中にあるerror400.ctpがHTTPステータスコード404が発生した場合のエラーページ、error500.ctpが500用のエラーページです。

これを好きにカスタマイズすることでオリジナルのエラーページを作ることができます。


エラーページもLayoutの影響を受けるため、エラーページ独自でのレイアウトを適用したい場合は、上記のエラーページ内で


<?php $this->layout = "error"; ?>

として、独自のLayoutを準備して適用するか、または


<?php $this->layout = FALSE; ?>

のようにして、レイアウトを適用しないようにしておきましょう。


また、エラーページは開発環境(設定ファイル(Config/core.php)でdebugを1以上に設定)の場合、これ以外に細かなエラー画面が用意されています。

例えば、存在しないコントローラーにアクセスした場合にMissing Controllerという例外が発生するのですが、その例外エラー用に下記のようなエラーページが用意されています。


A Day In The Boy&#39;s Life-cakephp-missing_controller


これをカスタマイズしたい場合、メッセージにあるようにErrorsディレクトリ内に独自のmissing_controller.ctpを用意しておきます。


ただし、本番環境(debugを0に設定)の場合は、これらの例外エラーは全てerror400.ctpまたはerror500.ctpにまとめられます

ですので、開発環境で細かくエラー内容をトレースしたいという場合は個別にエラー画面を用意しておけばよいですが、本番環境の場合はこの2つのエラーページを準備しておくだけでよいわけです。



例外をコントロールする


このようにエラーページはCakePHPの内部で例外が発生したときに呼び出されるわけですが、その例外はCakePHP任せでなく、独自にコントロールすることも可能です。


if (!empty($fuga)) {
// 通常の処理
} else {
// 404用の例外処理を投げる
    throw new NotFoundException();
}

上記のように必要なパラメータが無ければ404用の例外を投げ、それを受けてerror400.ctpが呼び出されて出力させるということができます。

500エラーの例外を投げたければ


throw new InternalErrorException();

と書けば例外を受けてerror500.ctpが呼び出されます。


最後に、独自の例外処理を作る方法ですが、例えば変数が見つからなかった場合に呼び出すMissingVarExceptionという例外を作ってみます。

例外クラスは、CakeExceptionのサブクラスとして以下のような内容のものを用意します。


<?php
class MissingVarException extends CakeException {
    protected $_messageTemplate = '%s が見つかりません。';
}

これを、app/Libディレクトリ以下にMissingVarException.phpとして保存します。

後は、Controller側でこの例外クラスをロードするのと、利用する処理のところで適宜呼び出します。


<?php
App::uses('MissingVarException', 'Lib');

class FugaController extends AppController {
    public function index() {
        if (!empty($fuga)) {
// 通常の処理
        } else {
            throw new MissingVarException(array('var' => 'fugafuga'));
        }
    }
}

実際に例外を発生させて見るとログファイルには


2013-07-28 01:55:15 Error: [MissingVarException] fugafuga が見つかりません。

のようなエラーメッセージが出力されます。


例外のコントロール方法の詳細は、マニュアル の方も参照してみてください。





Linux OSユーザーの色々なリソースを制限する

$
0
0

Linux上のユーザーのリソースの使用状況であったり、運用のポリシーを下に様々な制限を加えたいという場合があります。

例えば、利用するディスクスペースや生成するプロセス数の制限であったり、ログイン数を限定するという具合です。


ということで、その制限方法をまとめていきたいと思いますが、環境としてはRedHat5.9をベースに書いています。



/etc/security/limits.confに制限内容を書く


ユーザーのリソース制限の設定はこのファイルに内容を書いていきます。
例えば、あるユーザーが生成するプロセス数を限定したいという場合


foo    soft    nproc   20
foo    hard    nproc   100

のように書いておきます。


書式としては


ユーザー名(または@グループ名または*で全ユーザー) soft | hard 識別子 設定値

となります。

nprocが最大プロセス数を定義するための識別子となるので、今回の場合はfooユーザーに対して20のプロセス数までしか起動できない設定となります。


softとhardの設定の違いは、softが一般ユーザが変更できる限界値で、hardはrootが変更できる限界値となります。

この状態で、プロセスを大量に生成してみたいと思います。

簡易的にプロセスを大量に作るShellプログラムとして下記のようなものを用意します。

(とはいってもsleepプロセスをバックグラウンドで20個作るだけですけど)


#!/bin/bash
for i in `seq 1 20`
    do sleep 10 & done

実行直後にpsコマンドで見てみると


$ ps aux | grep sleep
--- snip ---
foo      3162  0.0  0.0  58924   524 pts/3    S    11:52   0:00 sleep 10
foo      3163  0.0  0.0  58924   524 pts/3    S    11:52   0:00 sleep 10
foo      3164  0.0  0.0  58924   524 pts/3    S    11:52   0:00 sleep 10
foo      3165  0.0  0.0  58924   524 pts/3    S    11:52   0:00 sleep 10
foo      3166  0.0  0.0  58920   524 pts/3    S    11:52   0:00 sleep 10
foo      3168  0.0  0.0  65416   828 pts/3    S+   11:52   0:00 grep sleep

と、プロセスが作られています。

この場合は、限界値が20なのでこのShellスクリプト実行直後にエラーがでます。

確認する場合は、別のユーザーまたはrootで確認する必要があります。


$ ./proc.sh
./proc.sh: fork: リソースが一時的に利用できません

この状態で何かコマンドを打ってみたり、fooユーザーでログインすると同様に


-bash: fork: リソースが一時的に利用できません

というようなエラーが出てリソース利用の制限に引っかかっていることがわかります。
syslogのほうもsecureログに近い内容のものが出力されます。


Jul 31 11:58:48 localhost sshd[9341]: Accepted password for fuga from 192.168.100.1 port 57033 ssh2
Jul 31 11:58:48 localhost sshd[9341]: pam_unix(sshd:session): session opened for user foo by (uid=0)
Jul 31 11:58:48 localhost sshd[9343]: Disconnecting: fork failed: Resource temporarily unavailable
Jul 31 11:58:48 localhost sshd[9341]: pam_unix(sshd:session): session closed for user foo

最後から2つ目のメッセージに書いてあるように、リソースが一時的に利用できないというメッセージが残されています。

ちなみに、この設定は設定後にログインしたユーザーに対して有効になります。

現在の状況を確認したければ、


$ ulimit -u
20

で、確認できます。
動的に変更したい場合は、


$ ulimit -u 30
-bash: ulimit: max user processes: cannot modify limit: 許可されていない操作です

にて変更できます。

上記の場合は、一般ユーザーがnprocを20までしか変更できないのに対して、30に設定しようとしたのでエラーがでています。


もう1つ、特定ユーザーのログイン数を制限してみたいと思います。


foo    -       maxlogins       5

この状態でターミナルを5つ以上開いてログインしようとすると、拒否されます。

secureログの方にも


Jul 31 09:38:34 localhost sshd[32519]: Accepted password for foo from 192.168.1.100 port 63460 ssh2
Jul 31 09:38:34 localhost sshd[32519]: pam_limits(sshd:session): Too many logins (max 3) for foo
Jul 31 09:38:34 localhost sshd[32519]: pam_unix(sshd:session): session opened for user foo by (uid=0)
Jul 31 09:38:34 localhost sshd[32519]: error: PAM: pam_open_session(): Permission denied
Jul 31 09:38:34 localhost sshd[32521]: Received disconnect from 192.168.1.100: 2: disconnected by server request

接続をサーバー側で拒否したことがわかります。

その他にlimit.confやulimitコマンドで制限できるものには下記のようなものがあります。


項目 リソース名
core 吐き出すcoreファイルのサイズのリミット
data 最大データサイズ
fsize 最大ファイルサイズ
nofile オープンできる最大ファイル数
rss 実行プロセスのメモリサイズ
stack 最大スタック数
cpu CPU割り当て時間
maxlogins 最大ログイン数
maxlogins 最大ログイン数

その他の項目や設定方法はlimits.conf内にコメントがありますので、そちらも参照してください。



limits.confの制約と制限


limits.confに書くことでユーザーのリソースが制限されるわけですが、全てにおいて適用されるわけではありません。

元々、このlimits.confはpam_limits.soという共有ライブラリで提供されており、pam.dディレクトリにあるsystem-authファイルなんかで読み込まれています。


session     required      pam_limits.so

下記のサイトなんかを参考にすると、sessionというタイプの指定はユーザー認証前後に実行するということなので、この時にリソースの使用状況が制限内かチェックしているようです。


PAMによる認証の仕組みを調べてみたAdd Star @ GeekFactory


その他にも、


# grep -l "pam_limits.so" ./*
./atd
./runuser
./sudo
./sudo-i
./system-auth
./system-auth-ac

なんかのファイルにてpam_limits.soが読み込まれているので、それぞれのコマンド実行時にリソースの制限がかかるわけですが、これ以外の場合やそもそもpamを利用していないアクションをした場合は制限されません。

自動起動スクリプトなんかから実行しているユーザーにも制限を加えたいという場合は、その自動起動スクリプトにulimitコマンドを書いて制限を加えるか、下記のようにOS起動時のスクリプトに書いてしまうという方法があるようです。


ulimitが効かない不安を無くす設定 @ 外道父の匠


まぁ、自動起動スクリプトの中でsuしてたりrunuserしてたりする場合は、pamを通るので制限がかかるかと思いますが。

現在の設定内容の確認および設定時のオプションは、下記のコマンドを実行すればわかります。


$ ulimit -a
core file size          (blocks, -c) 0
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 38911
max locked memory       (kbytes, -l) 32
max memory size         (kbytes, -m) unlimited
open files                      (-n) 1024
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kbytes, -s) 10240
cpu time               (seconds, -t) unlimited
max user processes              (-u) 38911
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited

先に書いたnproc(-uオプション)の変更のように、上記のそれぞれのオプションを指定して制限を動的に変更しておけば起動時に制限を反映することができます。


プロセスを大量発行するユーザー(例えばApacheユーザーとか)、利用者にサーバーを開放するけど大きなファイルや大量のファイルを扱われたくないというような場合に制限をかけておくと便利でしょう。





「既読無視」に見るリアルとネットの温度感

$
0
0

先日電車に乗っていたら、後ろの方で若い子達が「LINE送って既読になってんのに無視された」って騒いでいました。

ひどいとかぎゃーぎゃー騒いでたんですが、まぁ確かに自分もいい気はしないけど返事を求めてない場合もあるし、求めてたとしても相手の都合もあるだろうし、返事しないならしないで勝手にこっちで進めちまえばいいやーみたいな感覚だったりもするんですが、子供の頃からコミュニケーション手段としてネットが当たり前に使われている世代にとっては、「届いているはずなのに返事しない」って言うのはリアルに会っていて挨拶したのに無視されているような感覚を持っているのかなとも思ったりしました。



届いたという感覚が生み出すジレンマ


情報機器の発達とともに当たり前につながる手段を誰しもが手に入れている状況下では、相手に届けたメッセージが届いているとわかるとともに誰しもがどんな場所でも返答できる環境を作り出しています。

これは利便性を高めるとともにSNS疲れのような弊害も生み出しているわけですが、多くの人がそれを読める環境にいる(またはすぐに読め無くても数時間のうちに読めるだろう)場合、返事をよこさないことに不信感や不満を持つケースというのはあると思います。


「読んだ」という行為ができれば「書く」という行為もできるだろうという感覚を持つことは自然なことかもしれません。

ただ、実際には読むと書くとではコストが全然違ってくるわけで、読むのは何も考えずに少しのアクションで読めるのに対して、書くのは状況を把握したり考えたりした上で多くの文字を打ち込むという行為が必要になります。

会議中にメッセージを読むことはできても書くことは難しかったりもするわけです。


一方で、いくらネットでの情報伝達が容易になったとはいえ、私たちが住むリアルな世界でも様々なことが起きているわけで、ネットの文章だけを通してみるとそれはネットの中で起きているかのように錯覚してしまうんですけど、きちんと相手がいることなので当然リアルな世界で起きていることばかりなわけです。

データを通してやり取りするデジタルの世界に文章を放り投げるとあたかもそれは相手の頭の中に届くような感覚を持ちますが、リアルで話す言葉よりもずっと弱いものだったりもします。

現実世界での行為というものに時間をとられるだけでなく、その世界にいる人たちとの今のつながりを大事にしている人も多くいるわけで、それを押しのけて差し込んでくるデジタルの世界からのメッセージの優先度ばかりが高となるわけではないわけです。


まぁ、優先度は低くてもいいから時間のあるときに返事が欲しいというのは思うところですが、よっぽど返事が無いのであれば、文章がわかりづらいか、返事しづらい内容なのか、絶え間なくそっちのけで優先度を高いことを生み出している忙しい人か、はたまた嫌われているのか、何れにせよ本当に大事なことであれば電話するか直接会って話すかをした方がよいとは思います。

リアルに会う、話すという手続きを簡略化できるのがメールであったりTwitterやLINEのようなショートメッセージサービスではあるのですが、その分多くの情報にさらされているわけで、届く情報量というのはデータの中にある文章より断然小さくなってしまうのではないかと感じているところです。



情報スピードについていけない現実


先ほど書いたように、LINEとか親しいグループの中でやり取りする情報というのは数が多かったりしますしますので、そこで流れていく情報に自分が送ったメッセージがどんどん押し流されていくことがあったりします。

いろんな人とやり取りしていると返事を求めたメッセージが読み飛ばされたり、後で返事しようと思っていても次々に流れてくる新しい情報に脳内のメモリは埋め尽くされてしまいます。


そんな状況の中で、「じゃあ何時返事くれるの?」って言われても、さっき書いたように書くというコストが高い分なかなか「今でしょ!」っていえない現実があったりして、並列処理としてうまく情報をコントロールできない人は多いと思います。

以前に読んだ「ワーク・シフト ― 孤独と貧困から自由になる働き方の未来図〈2025〉 」という本の中で、これからは今よりももっと多くの情報が飛び交い時間に追われ続ける未来を予想してたりもするのですが、現在の状況でもスピード感についていけないと感じている人は多いのではないでしょうか。


ブログでは長文が流れてきて読む気がしないし、TwitterやFacebookではリアルな今が伝えられて今の自分とのギャップに思い悩み、LINEでは感情を変わりに表現してくれるスタンプの応酬で会話が無いという状況が刻々と進んでは変化していったりするわけです。

その全てを真っ向から受けて制御していくだけでも大変なわけで、これから生まれてくる新たなサービスや流行についていくとなるといつかは自身の頭と体が破綻するでしょう。


自分自身も、Twitterなんかで次から次へと流れてくる情報に面白みを感じていた時期がありましたが、追えば追うほどキリが無いという感覚をあるときから感じ出して、自分に降りかかる情報量をコントロールするようになって来ました。

時間があれば、ひっきりなしに情報を浴びるのも悪くないと思うのですが、常にそんな情報を浴びているのは仕事をしていたり現実の世界の中で両立させるのは無理だと感じているからです。

ネットの世界に生きている人はそれに押し流されること無くその流量の中でコントロールできていたりするのですごいな、と感じるわけですが自分には無理なので「基本的にTwitterは読まずに書くだけにする」とか「常に情報が更新されるSNSよりは好きなときに好きなだけ読めるブログの方がいいよなぁ」とか思ってたりもします。


きっと「既読無視とかひどい」っていってた子もそんな情報を絶え間なくあびる環境にいたら無視することも出てくるでしょうし、無視した相手もそんな世界に疲れているのかもしれませんし、まぁそんなことをつながっていることを理由にいちいち考えなくても神経すり減らすだけなので、もっとゆるく考えて自分にあったコミュニケーション手段と情報量というのをキープしてネットにつながっていたほうが楽しいんじゃないのって思う今日この頃です。





PR: 知ることから はじめよう アイヌのこと-政府ITV

$
0
0
イランカラプテという言葉を通じて、アイヌの文化とその伝承について見てみませんか

Oracleのプロセスが正常かどうか確認するシェルスクリプト

$
0
0

LinuxネタではなくOracleネタではあるのですが。

運用開始後にDBが正常に動作しているか監視したいという要件はよくあったりしますので、サービス監視ネタとしてまとめてみたいと思います。

環境は、Oracle11.2.0(2ノードRAC構成)、RedHat6.2環境をベースに書いています。



Oracleのサービス監視をする


サービス監視の一環として、正常にDBが稼動しているかどうかを動作しているプロセスの個数で監視(いわゆるプロセス監視)するケースはよくありますが、Oracleは動作するプロセスが非常に多かったりしますし、サービスの動作確認用コマンドが用意されていたりするので、それを応用してシェルで自動チェックする仕組みを作ってみます。


利用するコマンドは、下記のcrsctlコマンドでリソースの状況を表示するオプションをつけて実行します。


$ crsctl stat res -t
--------------------------------------------------------------------------------
NAME           TARGET  STATE        SERVER                   STATE_DETAILS
--------------------------------------------------------------------------------
Local Resources
--------------------------------------------------------------------------------
ora.DG1_CTRL.dg
               ONLINE  ONLINE       oradb1
               ONLINE  ONLINE       oradb2
ora.DG2_DATA.dg
               ONLINE  ONLINE       oradb1
               ONLINE  ONLINE       oradb2
ora.DG3_ARCH.dg
               ONLINE  ONLINE       oradb1
               ONLINE  ONLINE       oradb2
ora.LISTENER.lsnr
               ONLINE  ONLINE       oradb1
               ONLINE  ONLINE       oradb2
ora.asm
               ONLINE  ONLINE       oradb1                Started
               ONLINE  ONLINE       oradb2                Started
--- snip ---

--------------------------------------------------------------------------------
Cluster Resources
--------------------------------------------------------------------------------
ora.LISTENER_SCAN1.lsnr
      1        ONLINE  ONLINE       oradb1
ora.LISTENER_SCAN2.lsnr
      1        ONLINE  ONLINE       oradb2
ora.LISTENER_SCAN3.lsnr
      1        ONLINE  ONLINE       oradb2
ora.cvu
      1        ONLINE  ONLINE       oradb2
ora.oradb1.vip
      1        ONLINE  ONLINE       oradb1
ora.oradb2.vip
      1        ONLINE  ONLINE       oradb2
ora.oradb.db
      1        ONLINE  ONLINE       oradb1                Open
      2        ONLINE  ONLINE       oradb2                Open
--- snip ---


上記のように、このコマンドでリソースの状況が確認できるうえに、TARGET(しかるべき状態)とSTATE(現在の状態)が表示できるので、これを比較することでリソースの状況がチェックできそうです。


ただ、これは視覚的にすごく見やすいのですが、プログラムから見れば処理しにくい部分があるため、「-t」オプションを除いたコマンド実行結果を利用します。


$ crsctl stat res
NAME=ora.DG1_CTRL.dg
TYPE=ora.diskgroup.type
TARGET=ONLINE             , ONLINE
STATE=ONLINE on oradb1, ONLINE on oradb2

NAME=ora.DG2_DATA.dg
TYPE=ora.diskgroup.type
TARGET=ONLINE             , ONLINE
STATE=ONLINE on oradb1, ONLINE on oradb2

NAME=ora.DG3_ARCH.dg
TYPE=ora.diskgroup.type
TARGET=ONLINE             , ONLINE
STATE=ONLINE on oradb1, ONLINE on oradb2

NAME=ora.LISTENER.lsnr
TYPE=ora.listener.type
TARGET=ONLINE             , ONLINE
STATE=ONLINE on oradb1, ONLINE on oradb2

NAME=ora.LISTENER_SCAN1.lsnr
TYPE=ora.scan_listener.type
TARGET=ONLINE
STATE=ONLINE on oradb1
--- snip ---


この結果だと、TARGETとSTATEの部分がカンマ区切りで取得できるのでスクリプト的に処理しやすかったりします。



Oracleのリソース状況をチェックするスクリプト


ということで、以下のようなcrsctlコマンド実行結果を解析するシェルスクリプトを書いてみました。


#!/bin/bash

# チェック対象のリソース一覧。SCAN系やVIP、CVUなどのリソースはどちらのノードが持っているか不明なため対象外
RESOURCES=("ora.DG1_CTRL.dg" "ora.DG2_DATA.dg" "ora.DG3_ARCH.dg" "ora.LISTENER.lsnr" "ora.asm" "ora.net1.network" "ora.ons" "ora.oradb.db")

# RAC対象ノード
RACNODES=("oradb1" "oradb2")

# CRSコマンド
CRSCHECK="/opt/app/11.2.0/grid/bin/crsctl check cluster"

# 実行ファイル名
FILENAME="${0##*/}"

# CRSが生きているかチェック
crsstat=`$CRSCHECK | grep "online" | wc -l`
if [ $crsstat -ne 3 ]
then
    logger -p err -t "${FILENAME}" "error: Cannot communicate with Cluster Ready Services"
    exit
fi

# リソースをチェックするコマンド
CRSCMD="/opt/app/11.2.0/grid/bin/crsctl stat res "

# 各リソースをチェック
for res in "${RESOURCES[@]}"
do
# crsctlコマンドを実行しSTATE行を抽出し=で分割して結果を受取る
    result=`$CRSCMD $res | grep "STATE=" | cut -d "=" -f2`
    for node in "${RACNODES[@]}"
    do
# HOST名が存在しないかONLINEでなければそのリソースは停止している可能性がある
        status=`echo $result | grep "$node"`
        if [ -z "$status" ]
        then
            logger -p err -t "${FILENAME}" "error:" $res "on" $node "is missing."
        fi
    done
done


やっていることは、crsctlコマンドの実行結果から監視対象のリソース(RESOURCES配列の中身)のTARGETとSTATEの中身を比較しており、異常があったときにsyslog経由でエラーメッセージを吐き出すというものです。

あとは、Cronとかで数分おきにシェルを実行してチェックするように動作させておけばよいかと思います。


シェルのコメントにも書いていますが、SCAN系やVIPのリソースはどっちのノードが持っているか起動の状況によりばらけたり、1ノードを意図的に落としたときにフェイルオーバーして片ノードによってしまうということがあって監視が難しいので対象外にしています。

Oracle自身もリソースやプロセスに異常があったら自己修復するような機能を備えているので、この辺が完全にアウトになることがあるのか不明ではありますが。


あと、RAC用と書きましたが、対象リソース(RESOURCES変数)や対象ノード(RACNODES変数)を書き換えればSingle環境のOracleでもチェックは可能だと思います。

同様にcrsctlコマンドは利用できるので。


監視としては、syslogにメッセージが吐き出されるので、最終的にはログ監視の中で異常を拾う仕組みにはなっていますが、監視ツールのPandoraとかではスクリプトの実行結果を元に判別できたりもするので、ログに書き出す部分に戻り値を書いてあげてもよいかもしれません。





セキュリティのためのApache Solrアクセス制限あれこれ

$
0
0

Apache Solrをデフォルトのままで利用しようとすると、管理サイトへのアクセスや検索や情報の更新もどこからでもできたりします。

これは、セキュリティ的にまずいので、Solrへのアクセスを制御する方法をまとめてみます。

なお、環境はRedHat5.9にApache Solr4.2を利用した環境です。

Solrは付属のJettyで動かしているので、その他の環境で動かしている場合は設定が異なるかと思います。



Solr(Jetty)がListenするIPアドレスを限定したアクセス制御


管理サイトをつぶしてしまってかまわないのであれば、JettyがListenするIPアドレスを下記のようにローカルホストからのみに変更するのが手っ取り早い方法かもしれません。


<New class="org.eclipse.jetty.server.bio.SocketConnector">
  <Set name="host"><SystemProperty name="jetty.host" /></Set>
  <Set name="port"><SystemProperty name="jetty.port" default="8983"/></Set>


jetty.xmlの54行目あたりにある上記の箇所を


<Set name="host"><SystemProperty name="jetty.host" default="127.0.0.1" /></Set>
または
<Set name="host">127.0.0.1</Set>


※ 編集後は要Solr再起動。


のように設定することで、localhostからしかアクセスできなくなります。

要はListenするサーバーのIPアドレスが、127.0.0.1になるのでサーバーローカルからしかアクセスできなくなります。

もちろんそのサーバーにGUIでアクセスできる環境があるのであれば、そこからは管理サイトへアクセス可能です。

そして、検索結果の取得やSolr内の情報の更新もlocalhostからの接続に限定できます。


もしサーバーに複数のN/Wの足が出ていて、サービス用(ユーザーがアクセスする)とマネジメント用(管理者がアクセスする)のN/Wに接続されているようなサーバーであればマネジメント用のIPアドレスをListenさせることで、Solrへのアクセスを限定できたりします。


こういった制限を加えた状態で、Solrのサンプルでついているpost.shを利用してUPDATEしようとしてみると、

$ ./post.sh foo.xml
Posting file foo.xml to http://192.168.0.1:8983/solr/collection1/update
curl: (7) couldn't connect to host


というように、接続できないことがわかります。



Solr(Jetty)へ接続するIPアドレスを限定したアクセス制御


もう1つのやり方は、接続元のIPアドレスを限定するやり方です。

このIPアドレスを持つクライアントからしか接続させない、というやり方です。


まず、先ほど書いたListenするIPアドレスはデフォルトの状態(全てで受付ける)に戻しておきます。

次に、jetty.xmlの108行目あたりを以下のように編集します。


<Array type="org.eclipse.jetty.server.Handler">
  <Item>
<!--
             <New id="Contexts" class="org.eclipse.jetty.server.handler.ContextHandlerCollection"/>
-->
    <New class="org.eclipse.jetty.server.handler.IPAccessHandler">
      <Call name="addWhite">
        <Arg>192.168.0.1</Arg>
      </Call>
      <Set name="handler">
        <New id="Contexts" class="org.eclipse.jetty.server.handler.ContextHandlerCollection"/>
      </Set>
    </New>
  </Item>

※ 編集後は要Solr再起動。


コメントアウトしている箇所は、元々設定ファイルに書かれていた内容ですので、削除してもかまいません。

「addWhite」属性のArgタグに書かれているのが許可したい接続もとのIPアドレスです。

N/Wで制限したい場合は、


<Call name="addWhite">
  <Arg>192.</Arg>
</Call>


という風に書いておきます。
これで、許可していないIPから接続しようとすると


$ ./post.sh foo.xml
Posting file foo.xml to http://localhost:8983/solr/collection1/update

<html>
<head>
<meta

 http-equiv="Content-Type" content="text/html;charset=ISO-8859-1"/>
<title>Error 403 Forbidden</title>
</head>
<body>
<h2>HTTP ERROR: 403</h2>
<p>Problem accessing /solr/collection1/update. Reason:
<pre>    Forbidden</pre></p>
<hr /><i><small>Powered by Jetty://</small></i>
</body>
</html>


というようにHTTPステータスコード403が返ってきて拒否されます。



Solrの管理サイトやUPDATE実行時にBasic認証をかける


N/W制限に加えて管理サイトやSolrの情報をUPDATEする箇所にパスワードをかけられたらよりセキュリティは高まるでしょう。

ただし、この方法はBasic認証を使うやり方なので、それほど堅牢ではありませんが。

まず、jetty.xmlの設定ファイル内の適当な箇所(一番最後とか)に下記の設定を埋め込みます。


<Call name="addBean">
  <Arg>
    <New class="org.eclipse.jetty.security.HashLoginService">
      <Set name="name">Admin Realm</Set>
      <Set name="config"><SystemProperty name="jetty.home" default="."/>/etc/realm.properties</Set>
      <Set name="refreshInterval">0</Set>
    </New>
  </Arg>
</Call>


続いて、同フォルダ内にあるwebdefault.xmlの適当な箇所に下記の情報を追加します。


<login-config>
  <realm-name>AdminAuth</realm-name>
</login-config>
<security-constraint>
  <web-resource-collection>
    <web-resource-name>Admin</web-resource-name>
    <url-pattern>/*</url-pattern>
  </web-resource-collection>
  <auth-constraint>
    <role-name>admin-role</role-name>
  </auth-constraint>
</security-constraint>


最後に、jetty.xmlで指定したユーザー用の設定ファイル(realm.properties)を同じくetcディレクトリ直下に作成します。


admin:admin, admin-role

書式としては、「ユーザー名:パスワード, ロール名」です。

これで、Apache Solrを再起動し管理サイトにアクセスしてみるとBasic認証がかかっているはずです。


A Day In The Boy&#39;s Life-Solr-Basic認証


ちなみに、この状態だと検索やUPDATEもBasic認証がかけられるため、今まで使っていたプログラムも変更する必要があります。

そのため、付属のpost.shでUPDATEするシェルスクリプトも下記のように変更しておきます。


FILES=$*
URL=http://localhost:8983/solr/collection1/update

for f in $FILES; do
  echo Posting file $f to $URL
  curl $URL -u admin:admin --data-binary @$f -H 'Content-type:application/xml'
  echo
done

curl -u admin:admin "$URL?softCommit=true"
echo


変更箇所は、curlの実行オプションに「-u」をつけ、Basic認証用のユーザー/パスワードを指定しているだけです。

データの送信とコミットは分けて実行しているので2箇所とも変更する必要があります。
(1箇所しか変更しないでBasic認証が通らずにはまりました・・・)


Basic認証に失敗すると下記のようにHTTPステータスコード401が返ってきます。


$ ./post.sh foo.xml
Posting file foo.xml to http://localhost:8983/solr/collection1/update
<html>
<head>
<meta
 http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"/>
<title>Error 401 Unauthorized</title>
</head>
<body><h2>HTTP ERROR 401</h2>
<p>Problem accessing /solr/collection1/update. Reason:
<pre>    Unauthorized</pre></p><hr /><i><small>Powered by Jetty://</small></i><br/>
</body>
</html>


もし、管理サイトとUPDATEのBasic認証用のユーザーを分けたい、もしくはSolrからの検索はBasic認証なしに通したい、という場合は下記のように項目を分けて設定することもできます。


<login-config>
  <realm-name>AdminAuth</realm-name>
</login-config>
<security-constraint>
  <web-resource-collection>
    <web-resource-name>Admin</web-resource-name>
    <url-pattern>/admin/*</url-pattern>
  </web-resource-collection>
  <auth-constraint>
    <role-name>admin-role</role-name>
  </auth-constraint>
</security-constraint>

<security-constraint>
  <web-resource-collection>
    <web-resource-name>FileUpload</web-resource-name>
    <url-pattern>/collection1/update/*</url-pattern>
  </web-resource-collection>
  <auth-constraint>
    <role-name>guest-role</role-name>
  </auth-constraint>
</security-constraint>


前半分は、管理サイト用のURL(/admin/*)に、後半部分がUPDATEをする際のURL(/collection1/update/*)に対してBasic認証をかけています。


パスワードファイルも、下記のようにユーザーを追加し、ロールを分けておきます。


guest:guest, guest-role
admin:admin, admin-role

これで、guestユーザーはUPDATE時にだけ使え、adminユーザーは管理サイトへのアクセスできるユーザーとして登録できます。



Solr(Jetty)の起動・停止時のポート番号を変更する


Apache Solrはデフォルトでポート8983を使うので、何も制限をかけてない状態でそのまま利用しておくと管理サイトや情報検索・更新が外部から可能になってしまいます。

ということで、他で利用していないオリジナルのポート番号に変更することで、変にアクセスされるリスクを幾分か低下させることができます。


まず、起動時のポート番号の変更ですが、jetty.xmlを編集します。
今回は、8080番ポートを利用するように変更してみます。


<Call name="addConnector">
  <Arg>
      <New class="org.eclipse.jetty.server.bio.SocketConnector">
        <Set name="host"><SystemProperty name="jetty.host" /></Set>
        <Set name="port"><SystemProperty name="jetty.port" default="8080"/></Set>
        <Set name="maxIdleTime">50000</Set>
        <Set name="lowResourceMaxIdleTime">1500</Set>
        <Set name="statsOn">false</Set>
      </New>
  </Arg>
</Call>

port属性のdefault値を8080変更します。
続いて、solr.xmlを編集します。


<?xml version="1.0" encoding="UTF-8" ?>
<solr persistent="true">
  <cores defaultCoreName="collection1" adminPath="/admin/cores" zkClientTimeout="${zkClientTimeout:15000}" hostPort="8080" hostContext="solr">
    <core schema="schema.xml" loadOnStartup="true" instanceDir="collection1/" transient="false" name="collection1" config="solrconfig.xml" dataDir="data"/>
</solr>


編集箇所は、hostPort値の箇所を8080に変更しています。
これで、起動時のSolrの利用するポートを変更できます。


続いて、停止時のポート番号ですが、「Apache Solrを利用して本格的な検索エンジンを導入する 」にて紹介した起動スクリプトを見るとわかりますが、Solrを起動する際に「DSTOP.PORT」というポート番号(8079)を指定しています。

これは、Jettyの仕様で停止コマンドを受付けるポートを起動時に指定できるというもので、「DSTOP.KEY」で指定したキーの値がパスワード代わりとなり、セットで揃っていればJettyの停止ができるというものです。


ですので、停止時のポート番号は起動スクリプトを編集するなどして、起動時のオプションを変更することで変える事ができます。


KEY=stopkey
CORE=solr
cd $JETTY_HOME_DIR
start() {
  $JAVA -Dsolr.solr.home=$CORE -DSTOP.PORT=6060 -DSTOP.KEY=$KEY -jar start.jar >> $LOG_FILE 2>&1 &
  echo "Solr started!"
}
 
stop() {
  $JAVA -DSTOP.PORT=6060 -DSTOP.KEY=$KEY -jar start.jar --stop
  echo "Solr stopped!"
}


上記の場合は、停止時のポート番号を6060に指定してSolrを起動しています。
ポート番号は適当に空いているものを利用してください。



ここで紹介したことも含め、Solrのマニュアル の方も確認してみてください。





多くの人に受入れられるための「無難」という考え

$
0
0

人それぞれ個性があるが故に、様々な考えを持ってたりスタイルがあったり生き方があるわけですが、多くの人に受入れられる無難さって言うのはそれと反するイメージが強くあまり好まれるように見えません。

ただ、多くの人に受入れられるっていう面ではこの無難さって言うのは結構大事なんだなって思うときがあります。



無難というのは自然に受入れられること


例えばファッションを取ってみても、おしゃれというのは一見して奇抜なように見え、到底自分には真似できないように思えます。

その格好はその人の個性とあいまって価値観を増して輝かせたりもしますが、多くの人が受入れられないのは事実です。


また、自分がおしゃれと思っていても周りの女の子とかに聞いてみると「自分の好みではない」「実際に近くにいたら引くわぁ~」とか本音を言われたりしてギャップを感じることがしばしばですが、無難なファッションというものも存在します。

「格好良くもないんだけど、悪くないよね」「目立たないけどセンスはいい」というような感じで要は見た人が嫌悪感を示さない格好です。


こういう風に、できるだけ多くの人が自然に受入れられる意見であったり行動であったりスタイルを持つというのはすごく難しいことです。

無難にこなすというのは、極端な意見であったり考えであったりスタイルであったりがないので、多くの人にとっては目立たない存在にはなるのですが、これはある意味面白みがないことにもなります。
嫌悪感を示さないわけであって、ひどく納得して受入れられたというのではないわけですから、下手したらどっちでもよいということにも扱われます。


そして、ひどく無難な意見というのはその人の意見ではないように受取られ返って嫌悪感を抱く対象となってしまいます。

「君の意見はないのか」って言うところなんですが、変に白黒つけようとしたがる雰囲気があまり自分は好きではなかったりするので、アンケートにはある「よくわからない」って項目のように受け流すことも必要なんじゃないかなと感じたりもするんですが、日本人はこういう曖昧なところが好きな人も多いので、日本人らしい考え方なのかもしれません。


人それぞれ考え方があるので、100%受入れられる答えなどありません。
無難というのはせいぜい半分かもう少し上の辺りまでの割合で受入れられたら良い方だと思います。

残りもさらに細分化されるので、ある一方的な意見を言ったところでその他の人たちがついてくるわけでもなく、せいぜい数%かもっと極端に少ない割合の人たちが同意するレベルの意見であって、「君の意見はないのか」の解としてそこに響くことを言ってもそれって意味があることなのか、って思ってしまいます。

まぁ、人それぞれ意見があるんだからそれをぶつける事が大切っていう無難な意見は言えるわけですが。



無難に染まるために自分を押し殺せるか


少し開発者よりの話にはなりますが、何故無難さというのが大事なのかって、それを使う人や作る人のできるだけ多くを納得させないといけないからです。

UIの話にしたって「俺はこのUIがいい」「俺はこの色が好みだ」って言ったところで、それが万人受けするわけではありません。

今では、過去の統計から導いたベストプラクティスや人間工学に基づいた情報があり、若い人に受ける流行のインターフェースであったり、年配の人が好む色であったり、そういうのが一般化してきたりしています。

ひどく奇抜なUIを使うのがよいのか、無難に使えるUIがよいのかどちらが多くの人に受入れられるためには良いのかと考えたら自然に後者になるでしょう。

要はそれも多くの人が自然と受入れられるノウハウの集大成であって、変にとんがったり斬新なものではなかったりするわけです。


こうなってくると何が無難なのかと考えるのは、自分のスタイルが無難なのかどうなのかってことにもなりかなり混乱してきます。
多くの人は、「自分は普通だ」って思っているんですが、大体は(自分も)当てはまってないからです
多くの人に受入れられるはずの考えを持っている、服装もしている、食べ物の好みだって普通だし・・・って思っていると意外と当てはまらないことは多かったりします。

一分野で標準だとしても他の分野すべてが一般的な考えを持っているわけではないわけなんですが、物を作るって観点から見ると多種多様な分野を横断的に妥当を当てはめていかなくてはならず、それは一部では自分とのギャップを押し殺しながら進まなくてはならなくなります。


まぁ、先進的なものを作ってそれがスタンダードになるようなものを作るべきだという意見もあるとは思いますが、いきなりそんな先進的なものを投入してもみんなに受入れられるわけではないので、多くの人にとっての普通を先進的なものにするためのギャップを埋める道筋を、その多くの人がついてくるように導く戦略は必要になってくるでしょう。

そこでもまた多くの人に受入れられるための考えというものが求められます。



まとめ


ということで、無難というのは意見がないこととは違って、多くの人に受入れられるための特異な考え方かもしれません。

自分の色を出さずに他の人の色に合わせられるというのは、自分を自由に変化させられる能力が必要になってきます。
八方美人で誰にでも良いように顔色を変えていくということではありません。


母数に対しての中央値をわかってないといけませんし、それと自分がどれほどギャップがあるのかということを分析する必要もありますし、それを埋めるためにある意味自分の好みを捨てる必要も出てきます。

そういったことを経て、案外無難な意見を言うというのは難しいことなんだなと感じています。






iSCSI経由でExt4環境のボリュームをマウントする

$
0
0

最近は少し規模が大きなシステムの構成となると、大容量ストレージを使うケースが多かったりします。

ストレージがNFSを喋れる場合は、NFS経由でマウントするのが楽だったりもしますが、iSCSI経由でも少しの設定で同様にネットワーク上にあるディスクボリュームをローカルディスクのように扱うことができます(iSCSIは排他ロックができないのでNFSとは用途が違うといえば違いますが)。


ということで、リモートにあるディスクiSCSIを使い、Ext4のファイルシステムのディスクボリュームをマウントする方法のまとめです。

環境は、RedHat6.2を利用しています。

なお、ストレージ側(マウントされる側)の設定は省略しています。



iSCSIでターゲットとなるディスクボリュームをマウントする


まずは、iSCSIを利用するためのパッケージが用意されているか確認する必要があります。


# rpm -qa | grep iscsi
iscsi-initiator-utils-6.2.0.872-34.el6.x86_64

上記のように、iSCSIイニシエータのパッケージが入っているか確認します。

存在しない場合は、yum経由とかでインストールしておきましょう。


パッケージが入っているとiscsiadmコマンドが利用できますので、それでホスト(ターゲット)となるストレージを探します。


# iscsiadm -m discovery -t sendtargets -p 192.168.0.1

指定するIPはターゲットなるストレージ側のIPアドレスです。

コマンドと実行後、ストレージ側で公開されているディスクボリュームの情報(IQN)が下記ディレクトリに展開されます。


# ls -la /var/lib/iscsi/nodes
合計 20
drwxr-xr-x. 5 root root 4096  7月 30 16:36 2013 .
drwxr-xr-x. 8 root root 4096  6月  7 15:08 2013 ..
drw-------  3 root root 4096  7月 30 16:35 2013 iqn.2001-05.com.foo:0-1cb196-cc3294a2d-f1f000167e151b82-bar1
drw-------  3 root root 4096  7月 30 16:35 2013 iqn.2001-05.com.foo:0-1cb196-f6a294a2d-c02000167ed51b82-bar2

ただし、不要(マウントしない)なボリュームもあるでしょうから、それらは事前に削除しておきます。

削除は、上記ディレクトリを直接削除しても良いですし、下記のコマンドでIQNを指定して削除することもできます。


# iscsiadm -m node -o delete -T iqn.IQN情報

続いて、iSCSI経由でターゲットにログインします。


iscsiadm -m node --login

ログインできたかどうかは、セッション情報を確認してみるとわかります。


# iscsiadm -m session
tcp: [1] 192.168.0.1:3260,1 iqn.2001-05.com.foo:0-1cb196-cc3294a2d-f1f000167e151b82-bar1

ターゲットへのログインが成功したら、ローカルのデバイスとして認識されているかを確認します。


# ls -l /dev/disk/by-path/
合計 0
lrwxrwxrwx 1 root root  9  8月  9 12:56 2013 ip-192.168.0.1:3260-iscsi-iqn.2001-05.com.foo:0-1cb196-cc3294a2d-f1f000167e151b82-bar1-lun-0 -> ../../sdb
lrwxrwxrwx 1 root root  9  8月  9 12:56 2013 pci-0000:02:00.0-scsi-0:2:0:0 -> ../../sda
lrwxrwxrwx 1 root root 10  8月  9 12:56 2013 pci-0000:02:00.0-scsi-0:2:0:0-part1 -> ../../sda1
lrwxrwxrwx 1 root root 10  8月  9 12:56 2013 pci-0000:02:00.0-scsi-0:2:0:0-part2 -> ../../sda2
lrwxrwxrwx 1 root root 10  8月  9 12:56 2013 pci-0000:02:00.0-scsi-0:2:0:0-part3 -> ../../sda3

上記のように、ターゲット内で見つけたbar1のボリュームがローカルの/dev/sdbにマウントされていることがわかります。


ただし、このデバイス名はOSを再起動したりすると変わる可能性があるため、デバイス名を固定するための設定をしておきます。

固定する方法は、特定のSCSI IDであったら固定のデバイス名にするというものです。

そのため、まずはSCSI IDを調べます。


# scsi_id -g /dev/sdb
36019cbd1a29432cc821b157e1600f0f1

上記のSCSI IDのデバイスを固定するためのルールを設定ファイルに記述します。

ファイルが存在しない場合は、新規作成します。


KERNEL=="sd*", BUS=="scsi", PROGRAM="/lib/udev/scsi_id -g /dev/%k", RESULT=="36019cbd1a29432cc821b157e1600f0f1", NAME="bar1", OWNER:="fuga", GROUP:="hoge", MODE:="0660" 

「RESULT==」とかかれたところに、先ほどのSCSI IDを設定しておきます。

NAMEがデバイス名となり、OWNERやGROUPはボリュームをマウントしたときの所有者とグループ名になります。



ターゲットボリュームのファイルシステムをExt4へフォーマットする


ボリュームがサーバーから認識できるようになったら、その領域のファイルシステムをExt4へフォーマットします。


# fdisk /dev/sdb
デバイスは正常な DOS 領域テーブルも、Sun, SGI や OSF ディスクラベルも
含んでいません
新たに DOS ディスクラベルをディスク識別子 0xb1eeb347 で作成します。
あなたが書き込みを決定するまで、変更はメモリ内だけに残します。
その後はもちろん以前の内容は修復不可能になります。
警告: 領域テーブル 4 の不正なフラグ 0x0000 は w(書き込み)によって
正常になります

警告: DOS互換モードは廃止予定です。このモード (コマンド 'c') を止めることを
      強く推奨します。 and change display units to
         sectors (command 'u').

コマンド (m でヘルプ): m
コマンドの動作
   a   ブート可能フラグをつける
   b   bsd ディスクラベルを編集する
   c   dos 互換フラグをつける
   d   領域を削除する
   l   既知の領域タイプをリスト表示する
   m   このメニューを表示する
   n   新たに領域を作成する
   o   新たに空の DOS 領域テーブルを作成する
   p   領域テーブルを表示する
   q   変更を保存せずに終了する
   s   空の Sun ディスクラベルを作成する
   t   領域のシステム ID を変更する
   u   表示/項目ユニットを変更する
   v   領域テーブルを照合する
   w   テーブルをディスクに書き込み、終了する
   x   特別な機能 (エキスパート専用)

コマンド (m でヘルプ): n
コマンドアクション
   e   拡張
   p   基本パーティション (1-4)
p
パーティション番号 (1-4): 1
最初 シリンダ (1-65272, 初期値 1):
初期値 1 を使います
Last シリンダ, +シリンダ数 or +size{K,M,G} (1-65272, 初期値 65272):
初期値 65272 を使います

コマンド (m でヘルプ): p

ディスク /dev/sdb: 536.9 GB, 536881397760 バイト
ヘッド 255, セクタ 63, シリンダ 65272
Units = シリンダ数 of 16065 * 512 = 8225280 バイト
セクタサイズ (論理 / 物理): 512 バイト / 512 バイト
I/O size (minimum/optimal): 512 bytes / 512 bytes
ディスク識別子: 0xb1eeb347

デバイス ブート      始点        終点     ブロック   Id  システム
/dev/sdb1               1       65272   524297308+  83  Linux

コマンド (m でヘルプ): w
パーティションテーブルは変更されました!

ioctl() を呼び出してパーティションテーブルを再読込みします。
ディスクを同期しています。


やっていることは、fdiskコマンドを使って基本パーティションを作成するというものです。

パーティションの作成が終ったら、その領域をExt4でフォーマットしてあげます。


# mkfs.ext4 /dev/sdb1
mke2fs 1.41.12 (17-May-2010)
Discarding device blocks: done
Filesystem label=
OS type: Linux
Block size=4096 (log=2)
Fragment size=4096 (log=2)
Stride=0 blocks, Stripe width=0 blocks
32776192 inodes, 131074327 blocks
6553716 blocks (5.00%) reserved for the super user
First data block=0
Maximum filesystem blocks=4294967296
4001 block groups
32768 blocks per group, 32768 fragments per group
8192 inodes per group
Superblock backups stored on blocks:
        32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632, 2654208,
        4096000, 7962624, 11239424, 20480000, 23887872, 71663616, 78675968,
        102400000

Writing inode tables: done
Creating journal (32768 blocks): done
Writing superblocks and filesystem accounting information: done

This filesystem will be automatically checked every 38 mounts or
180 days, whichever comes first.  Use tune2fs -c or -i to override.


フォーマット完了後は、サーバーからマウントして見ましょう。


# mount -t ext4 /dev/sdb1 /export

 df -k
Filesystem           1K-ブロック    使用   使用可 使用% マウント位置
/dev/sda3             15000964   3383136  10855820  24% /
tmpfs                   961900         0    961900   0% /dev/shm
/dev/sda1               495844     36945    433299   8% /boot
/dev/sdb1            516068876    202520 489651492   1% /export


これで、/exportとしてサーバーからマウントできていますので、後はread/writeのテストをしてみれば良いかと思います。

問題なければ、永続的にサーバーからマウントされるようにfstabに情報を追加しておきます。


/dev/sdb1             /export              ext4    _netdev        1 1

これで、iSCSI経由でリモートにあるディスクボリュームをローカルにあるかのように使うことができます。





chkconfigに登録されていないデーモンのランレベルをいきなり変えてはいけない

$
0
0

いまさらなネタでもあるんですが、RedHatとかのデーモンの自動起動・自動停止の管理はchkconfigを使うというのが一般的ですが、後で追加したパッケージの起動のランレベルをいきなり変えるとサーバーの再起動や停止時にちゃんと自動停止してくれないかもよ、ってお話です。



chkconfigの仕組み


この辺は、ググれば情報はたくさん出てくるので簡単にしか書きませんが、init.dの下に配置されている各デーモン用のスクリプトのヘッダ部分で起動するランレベルが定義されています。


#!/bin/bash
#
# httpd        Startup script for the Apache HTTP Server
#
# chkconfig: 345 85 15
# description: Apache is a World Wide Web server.  It is used to serve \
#          HTML files and CGI.
# processname: httpd


「chkconfig:」って書かれた箇所が該当のところで、「起動するランレベル」「起動時のプライオリティ」「停止時のプライオリティ」の順番で書いていきます。
ランレベル345の時に起動させ、起動する際のプライオリティは85、停止時のプライオリティは15としています。

この設定でchkconfigに登録されていれば、


# chkconfig --list httpd
httpd           0:off   1:off   2:off   3:on    4:on    5:on    6:off

って状態になり、rc3.d~rc5.dの中にシンボリックリンクが作成されます。


# ls -la /etc/rc3.d/S85httpd
lrwxrwxrwx 1 root root 15  8月 22 09:51 /etc/rc3.d/S85httpd -> ../init.d/httpd

停止の場合はランレベル0なのでrc0.dですね。


# ls -la /etc/rc0.d/K15httpd
lrwxrwxrwx 1 root root 15  8月 22 09:51 /etc/rc0.d/K15httpd -> ../init.d/httpd

同様に再起動した場合はランレベル6なのでrc6.dの下に自動停止用のスクリプト(シンボリックリンク)が作成されます。

chkconfigの起動するデーモンの管理は、rcX.dの下にシンボリックリンクが存在しているかどうかで管理しています。
コマンド実行時に毎回見に行っているようです。
ですので、このシンボリックリンクを消すことでも起動するランレベルを変更することができます。


# rm S85httpd
rm: remove シンボリックリンク `S85httpd'? y
# chkconfig --list httpd
httpd           0:off   1:off   2:off   3:off   4:on    5:on    6:off

は、

# chkconfig --level 3 httpd off

と、同義になります。



chkconfigに登録していないデーモンのランレベルをいきなり変えると自動停止してくれない


で今回の問題ですが、新しく追加したパッケージなどをデーモンとして自動起動させたいような場合、chkconfigに登録されていない状況で起動するランレベルをいきなり変更すると、自動起動はするようになるのですが自動停止はしてくれません。


# chkconfig --del httpd
# chkconfig --list httpd
サービス httpd は chkconfig をサポートしますが実行レベルで参照されていません (run 'chkconfig --add httpd')
# chkconfig --level 345 httpd on
# cd /etc/rc3.d/
# ls -la S85httpd
lrwxrwxrwx 1 root root 15  8月 22 10:13 S85httpd -> ../init.d/httpd
# chkconfig --list httpd
httpd           0:off   1:off   2:off   3:on    4:on    5:on    6:off


上記のように自動起動はちゃんと行えていますが、rc0.dの下を覗いてみると自動停止のスクリプトが配置されていません。


# ls -la /etc/rc0.d/S15httpd
ls: /etc/rc0.d/S15httpd: そのようなファイルやディレクトリはありません

ってことで、Apacheの自動停止が正常に行われません。
まぁ、最終的にはkillallでプロセスが殺されますけどお行儀のいいやりかたではありませんし、場合によっては障害となる可能性もあります。

で、正式なやり方としてはちゃんとchkconfigにaddしてから起動するランレベル変えましょうねってやり方になります。


# chkconfig --add httpd
# ls -la /etc/rc0.d/K15httpd
lrwxrwxrwx 1 root root 15  8月 22 10:30 /etc/rc0.d/K15httpd -> ../init.d/httpd

または、resetオプションを実行しても問題ありません。


# chkconfig --del httpd
# chkconfig --level 345 httpd on
# ls -la /etc/rc0.d/K15httpd
ls: /etc/rc0.d/K15httpd: そのようなファイルやディレクトリはありません
# chkconfig httpd reset
# ls -la /etc/rc0.d/K15httpd
lrwxrwxrwx 1 root root 15  8月 22 10:34 /etc/rc0.d/K15httpd -> ../init.d/httpd


resetオプションを実行するとinitスクリプトのヘッダを読み直してその通りに設定しなおしてくれます


こう考えると、よくサーバー立てたら不要なデーモンは動かさないようにしましょうとか言われますけど、chkconfigコマンドで指定のレベルでOn/Offするよりは、initスクリプトのヘッダで本当に必要なレベルで動かすように定義しておいた方がadd/del/resetで管理できるので確実なのかもしれません。
まぁ、スクリプトの数は多いので手間といえば手間なのですが。


不安があるのであれば一旦各スクリプトをresetさせるのも良いかと思います。





PR: 想像から始める防災・減災-政府ネットTV

$
0
0
災害に対する事前の備えやその対処法等、来るべき大災害に生き残るための方法をご紹介

ブログを7年間書き続けて変わった5つのこと

$
0
0

今月でこのブログを始めてから7年間が経過しました。

よく飽きもせずに続けられたものだと我ながら感心したりもするのですが、よくよく思い返してみるとブログを始める前と始めた後では自分の考え方であったりライフスタイルも随分と変わってきたな、と感じたりしています。

先にまとめてしまうと、その変化は今の自分にとってよい方向性に導いてくれたものが多かったと思いますし、ブログを書かなかったら普通のおじさんとして返ってネットには疎く、ここまでエンジニア志望として仕事もしていなかったように思えます。



1. 他人のブログに興味を持ち始めた


それまで自分自身、エンジニアという仕事をしながらあまりネットに興味を持つことがありませんでした。

どちらかというとネットは調べ物をするところという感じで、それ以上でも以下でもない使い方をしていたと思います。

ネットから拾う情報というのはひどく自分にとって偏りがあったので、他人の考えなんてものにはあまり興味がありませんでしたし、今ほど多種多様な考え方が毎日どころか毎分埋もれるほど発信されるということも無かったので、他人の発現に目を留めること自体が少なかったと思います。


ただ、ブログを書くようになってからはひどく他人の意見というものが気になりだしました

単に人の目を気にするという以上に、自分の考え方とのずれというのが気になりだしたというか、もちろんブログのネタとして情報を探していたというのもあるのですが、他人のブログを見ることでこういう事を言ってもいいんだ、言うのは止めた方がいい、そんなこと考えている人もいるんだ、このアイデアは誰でも思いつくことなんだな、と色々気づく点も多かったりします。


最初の頃はわき目も降らずにひたすら書いていたりもしましたが、そのうち毎月書くエントリより全然多い本数の他人の記事を読み出すようになりました。

ブログは書くものですが読むためのものでもあるわけです。

当たり前ですけど人様のブログを読んでいるとそんなことがはっきりわかるわけで、じゃあブログの書き方ってなんだろうという視点に回帰していきます。


こういうことって、他人に興味を持つというところで、仕事でも私生活でも相手の真意を気にすることで会話がうまくいったり、自然と気を回せたりというところにつながってくるかなとか思ってたりもします。



2. 35歳を過ぎてもエンジニアとしての情熱を持っていられた


プログラマ定年説の年齢を超えても技術的なことに興味を持ち続けられたのは、ブログのネタとしてでもプログラミングであったりツールの使い方であったりと言うことを発信し続けたからというのが大きいと思っています。


多分、ブログ書いてなかったら元々そこまで深追いするエンジニアでもないのでそのうち頭の片隅に追いやられていって技術力も無く、どちらかというと社内でどううまく立ち振る舞うかみたいなところばかり気にしていたのかもしれません。

情報システム部門ということで、得る情報はよりいっそう少ないですしレガシー化した環境に囲まれて「俺のコードはちゃんと動くしこれでいいんだ」って辺に納得する井の中の蛙になってたような気がします。


自分の記憶のまとめという役割でもこのブログに集約されることで、「前にこれやったな」ってことを引き出すことも容易になりましたし、過去の情報に触れることで興味が薄れていくことを断ち切れますし、ブログにまとめるついでにもう一歩踏み込んでみるというようなことでより新しい知識も得られますし、その過程で読んだ他人のブログとかで「この人スゲー」という憧れを得てそれがモチベーションにつながることもありますし、よりいっそうの興味も拡大していったりもしますし、ここまでまとめておけば自分のエンジニアとしての履歴書のようなものが簡単に示せたりもします。


元々子供の頃からプログラマになりたいなと漠然と思ってたけど大学とかまで大して情熱を持てずにいた自分をここまでずるずるとでも引っ張ってこれたのはこのブログの影響が大きいと感じています。



3. 本を読むようになった


それまで活字嫌いだったりして、子供の頃はおろか社会人に出るまでろくに本を読んだことが無かった自分ですが、ブログを書き始めてからその苦手意識のようなものはなくなりました。

これだけ文章を書いていたら書く楽しさというものにどっぷりつかる様になりましたし、他人のブログへの興味同様に本も読んでみようかと自然と考えるようになって、少ないながらも手にする機会が増えました


本はどちらかというと技術よりな話よりも考え方であったりビジネス本が多かったりするんですが、エンジニアとしてキャリアを考える一方で、違った側面も伸ばしていきたいという考え方もあって、昔親からよく言われた「本を読め!ってことわかるわー」なんて漠然と思ったりしてます。

まぁ、先に書いたように活字嫌いが克服されなかったらそんな本なんて読まなかったでしょうし、自分の中にある不安や疑問というものを何時までも抱え込んで「まぁどうにかなるでしょう」という楽観主義のひどくレベルの高いところで生活していたのかもしれません。


それが本を読むことで全て解消されるなんてことまで言えるほど読んでも無いですし、実際そうではないのでしょうけど自分には無い新たな知識を頭に入れることでそれをどう自分なりに昇華させようか考えてみるというのは視野を広げたりするきっかけになるかと思います。



4. 面倒くさがり屋から抜け出した


結構めんどくさがり屋だったんですよ。飽きっぽいですし。
ですが、流石にここまでブログ続けてればそういった敷居がかなり低くなったように思えます。


ブログ書くこと自体とても手間なわけです。
技術ネタとか書き出すと1エントリで早くても2、3時間かかったりしますし、凝り出すと丸1日ぐらいの時間を費やしたりしてます。
まぁ、それで大きな反響があるかといわれるとあまりそうでもなかったりするのでがっかりすることも多いのですが、自分の知識を集約する延長上にはあるのでその辺は置いとくとしても、過去のエントリを保守したりコメントがあった間違いの指摘を自分で再検証して修正したりと細々としたところで手間もかかりますから、そういったことを続けることでそんなぐらいの手間であれば仕事であれなんであれあまり面倒だと思わずに作業できたりもします。


特に文章でまとめるというのは活字に慣れたということも相まって仕事でも全然気にならなくなったりします。
が、弊害としてはメールなり長文になりがちだというのはあるかもしれませんが。
あとは、このブログ自体、アクセスアップだ!このブログで食っていくんだ!みたいなガツガツした感じは本人的に持っていないので、平穏無事をモットーとして長く続けることで忍耐力というのも鍛えられたかなと思います。



5. 趣味ができた


それまで趣味らしい趣味というのがなかったんですが、日常的にブログを書くようになってからは、空いた時間でブログ書くかというように趣味として確立ができました。
毎日毎日書いたりしているわけではないのですが、仕事をしてたり日常の中でも「このネタを書いてみるかな」って思ったりしてEvernoteとかにメモったりしています。


自分の中に根付いたものなので自然とそういう考えになってしまいましたし、他人に「ブログ書くことが趣味です」とか言うと「じゃあ読みたい!」とか言われるのが面倒くさいので口外してませんけど、人に言える趣味かどうかなんてものはそんなに重要ではなく、取りあえず自分の中での時間を忘れさせてくれることとしてブログを書くというのが根付いたりしています。


趣味ができるというのは要は何か大事なものが他にできるということだったりします。
どんな趣味であれ、それをすることが楽しくライフスタイルの中に当たり前にある存在を見つけられたというのはラッキーだったなと感じています。



おわりのまとめ


先に書いたようにブログを始めなかったらここに書いたことは変わらなかったと思いますし、自分の性格上ではそれは良い方向に転がったのではないかなと思っています。


だからみんなブログ書こうぜ!見たいなことは当然その人それぞれのライフスタイルにあった書き方があるわけですが、少なからず長く続けることで自分の中に変化をもたらすのは当然あると思います。

その変化がよいものになるかどうかブログとの付き合い方によっても変わってくるわけですけど、あまりガツガツせずにただ長く付き合うという中でも色々な考えや行動がそれ以前と以後では違いが出てくるので変わるきっかけでもあったよな、と今改めて感じたりします。





PR: 想像から始める防災・減災-政府ネットTV

$
0
0
災害に対する事前の備えやその対処法等、来るべき大災害に生き残るための方法をご紹介
Viewing all 287 articles
Browse latest View live