2012年10月16日

Ubuntu Server 12.04 LTS に nginx を Install したメモ

概要

Ubuntu Server 12.04 LTS に apt-get で nginx をインストールしようとしたら、nginx のバージョンが古かったのでコンパイルしてインストールした。とりあえずメモしておく。

fabfile

Fabric」で設定を書いてコンパイルした。
この fabfile だとインストール先は「/opt」の下にしてある。
ユーザは nginx ユーザを想定して作成している。
あまり綺麗な fabfile ではないが、そこまで綺麗に書いても意味ないかも。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from fabric.decorators import task
from fabric.api import (
    sudo,
    run,
    cd,
    put,
    env,
)


@task
def install():
    """
    nginx のインストール
    """

    # 依存ファイルのインストール
    sudo('apt-get install -y gcc')
    sudo('apt-get install -y make')
    sudo('apt-get install -y libpcre3-dev')
    sudo('apt-get install -y libssl-dev')

    # nginx
    with cd(env.path):
        etc_dict = {
            'urlpath': 'http://nginx.org/download/',
            'ver': '1.2.4',
            'data_dir': '/opt/nginx',
            'user': 'nginx',
            'group': 'nginx',
        }

        # ディレクトリ用意
        sudo('mkdir -p %(data_dir)s/conf' % etc_dict)
        sudo('mkdir -p %(data_dir)s/module' % etc_dict)
        sudo('mkdir -p %(data_dir)s/var' % etc_dict)
        sudo('mkdir -p %(data_dir)s/var/log' % etc_dict)
        sudo('mkdir -p %(data_dir)s/var/run' % etc_dict)
        sudo('mkdir -p %(data_dir)s/var/lock' % etc_dict)
        sudo('mkdir -p %(data_dir)s/var/tmp' % etc_dict)

        # ユーザ作成
        sudo('useradd -s /bin/false nginx -d /opt/nginx')

        # nginx コンパイル
        nginx = 'nginx-{ver}'.format(**etc_dict)
        run('wget {urlpath}/{nginx}.tar.gz'.format(nginx=nginx, **etc_dict))
        run('tar xvfz {nginx}.tar.gz'.format(nginx=nginx))
        with cd('{nginx}'.format(nginx=nginx)):
            run('./configure '
                '--prefix=%(data_dir)s '
                '--conf-path=%(data_dir)s/conf/nginx.conf '
                '--error-log-path=%(data_dir)s/var/log/nginx-error.log '
                '--http-log-path=%(data_dir)s/var/log/nginx-access.log '
                '--pid-path=%(data_dir)s/var/run/nginx.pid '
                '--lock-path=%(data_dir)s/var/lock/nginx.lock '
                '--user=%(user)s '
                '--group=%(group)s '
                '--with-debug '
                '--with-poll_module '
                '--with-http_gzip_static_module '
                '--with-http_realip_module '
                '--with-http_ssl_module '
                '--with-http_stub_status_module '
                % etc_dict)
            run('make')
            sudo('make install')

        # パーミッションの設定
        sudo('chown -R nginx:nginx /opt/nginx')


@task
def setup_nginx():
    """
    nginx 設定
    """
    # 設定ファイルのアップロード
    conf_path = '/opt/nginx/conf/nginx.conf'
    put('./fabfile/{conf_path}'.format(conf_path=conf_path),
        '{conf_path}'.format(conf_path=conf_path), use_sudo=True)
    # パーミッションの設定
    sudo('chown -R nginx:nginx {conf_path}'.format(conf_path=conf_path))

nginxの設定ファイル

「nginx.conf」の中身はいいかげんだが以下な感じ。

daemon on;
worker_processes  5;

events {
    worker_connections  1024;
}

http {
    include       mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';
    error_log /opt/nginx/var/log/nginx-error.log debug;

    sendfile        on;

    keepalive_timeout  65;

    gzip  on;

    gzip_comp_level 2;
    gzip_proxied    any;
    gzip_types      text/plain text/css application/x-javascript text/xml application/xml application/xml+rss text/javascript;

    server {
        listen       80;
        server_name  localhost;

        charset  utf-8;

        location / {
            root   /opt/www/html;
            index  index.html index.htm index.php;
        }

        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }

        # nginx status
        location ^~ /nginx_status {
            stub_status on;
            access_log  off;
        }

    }
}

コンパイルとインストール

fabfileの実行は以下のようにする。

fab -H ホスト install
fab -H ホスト setup_nginx

起動

nginx の起動は以下

sudo su /opt/nginx/sbin/nginx -c /opt/nginx/conf/nginx.conf

このままだと root ユーザでの起動になってしまう。本当は nginx ユーザで起動したいので、後で設定する。

動作確認

FW設定している場合はポートを開放する。

sudo ufw allow 80/tcp

「http://hostip/」でアクセスして、表示されたら成功。

2012年10月15日

システム構築、デプロイツール Fabric について

概要

サーバ構築等のシステム構築は多数のコマンドを打鍵することになる。そうしたコマンドをメモの形で残しておいても、コピペで実行していればミスは発生するし、安全性が低い。
プログラマーなら、手順全てをプログラムしておくのが良い。
それを可能にするツールが「Fabric」。

インストール

Fabric」は Python で作成されている。インストールは easy_install か pip 等で実施する。

pip install fabric

インストールで「fab」というコマンドがインストールされるので、PATHを通しておく。

事前準備

サーバに対して実行する場合は、サーバにログインできる状態にしておく必要がある。

簡単な使い方

以下のようなファイルを「fabfile.py」の名前で作成。

from fabric.api import run

def host_uname():
    run('uname -s')

「fabfile.py」の存在するディレクトリで、ホストを指定して、「fab」コマンドを実行する。

fab -H 192.0.2.178 host_uname

この例だと「192.0.2.178」のサーバにログインして「uname -s」を実行する。ログインする時に通常のログイン処理が実行される。

もっと自動化したい場合は、「evn」を設定することで、自動的にログインすることも可能。

from fabric.api import env

# SSH のキーのパスを設定
env.key_filename = [os.path.join(os.path.dirname(__file__), 'keys/id_rsa')]
# ログイン先の IP
env.hosts = [
    '192.0.2.178',
]
# ログインユーザ名
env.user = 'username'
# sudo する際のパスワード
env.password = 'password'

マニュアル

マニュアルは「Fabric」に存在する。 Operationsの章とかは理解しやすいかもしれない。ほとんどの事が実行できる。 あとは「fabfile.py」で検索するとサンプルが沢山あるのでそれを見るのが速い。

fabfileの分割

通常は「fabfile.py」を作成して実行するが、システム構築等を実行するとすぐに「fabfile.py」が肥大化する。
「fabfile.py」を分割したい場合は「fabfile」というディレクトリを作成し、その中に「__init__.py」ファイルを作成。
同じディレクトリに Python ファイルを用意して、「__init__.py」で import すれば良い。

たとえば以下のようなディレクトリ構成にする。

.
├── fabfile
    ├── __init__.py
    ├── commons.py
    ├── etc
    │   └── ufw
    │       ├── before.rules
    │       └── before6.rules
    ├── ufw.py

「__init__.py」の中身は以下のような感じ。

import commons
import ufw

実行は「ufw.py」の中身が以下のようだとすると。「fab ufw.status」で実行できる。

from fabric.decorators import task
from fabric.api import sudo

@task
def status():
    """
    Firewall の状態確認
    """
    sudo('ufw status')

あとは API を使いこなせば、ほとんど何でも自動実行可能。事前に構築環境と同じOSなどを用意して、十分に動作を確認してから実行できるので、非常に便利。

まとめ

Fabric」に関しては利用してみないと便利さが良くわからないかもしれないが、かなり便利なツール。環境構築を汎用的に実施したい人には特にお勧め。

2012年10月14日

Ubuntu の Firewall ツール UFW メモ

概要

Ubuntuでは通常Firewallの設定をする場合、「UFW(Uncomplicated Firewall)」を利用した方が簡単。
UFW は iptables のラッパで、基本的な事は ufw コマンドから設定することができる。

マニュアル

基本は「man ufw」と「UFW(Uncomplicated Firewall)」を参照すると良い。

設定ファイル

Ubuntuのバージョンによって多少設定ファイルのディレクトリが異なる。
ufwコマンドにて設定された値は「/lib/ufw」もしくは「/var/lib/ufw」以下の「user.rules」に保存される。
ufwコマンドで設定できない項目は「/etc/ufw」以下のファイルで設定する。

ufwコマンドで設定可能な項目

ufwコマンドでは「allow」、「deny」、「reject」、「limit」しか設定できない。「hashlimit」等を設定したい場合は、「/etc/ufw/before.rules」等に設定する。

ufwコマンドの使い方

「man ufw」と本家のマニュアル「UFW(Uncomplicated Firewall)」でほとんどわかる。
注意点は ufw で設定した物は設定順に読まれるという事。例えば「deny」して「allow」すると、「allow」が有効にならない。「ufw status numbered」にて順番を確認すること。
順番が意図と異なる場合は一度削除して、設定しなおす必要がある。
ufw を再有効(ufw enable)しないかぎり、すぐに反映されるわけではない。

ufwコマンドのサンプル

例えば SSH の設定は以下のようににすると、IPを特定からのアクセスに制限し、その他を拒否する。

sudo ufw allow from 192.0.2.178 to any port ssh
sudo ufw deny 22/tcp

DNS の port 53 を空けておかないと名前解決できないかも。

sudo ufw allow 53/tcp
sudo ufw allow 53/udp

「/etc/ufw/after.rules 」の「COMMIT」の前に以下を記述しとかないと、DNS解決できない。

-A ufw-after-input -m state --state ESTABLISHED,RELATED -j ACCEPT

設定内容の確認

以下のコマンドで確認できる。

# 概要
sudo ufw status

# iptables 形式
sudo ufw show raw

参考サイト

ufw コマンドの使い方は本家マニュアル参照。before とかの書き方は iptables の書き方なので、別途学習が必要。

2012年10月13日

Ubuntu で ufw を利用して ping を拒否する設定

概要

Ubuntu で ufw を利用して ping を拒否する機会があったのでメモ。
Ubuntu では ファイアウォールの設定は ufw を利用した方が良い。
ping のプロトコルは ICMP になる。

設定(IPv4)

「/etc/ufw/before.rules」に以下の設定をする。

-A ufw-before-input -p icmp --icmp-type destination-unreachable -j DROP
-A ufw-before-input -p icmp --icmp-type source-quench -j DROP
-A ufw-before-input -p icmp --icmp-type time-exceeded -j DROP
-A ufw-before-input -p icmp --icmp-type parameter-problem -j DROP
-A ufw-before-input -p icmp --icmp-type echo-request -j DROP

当然 ufw を有効にしておく必要がある。

sudo ufw enable

ファイルのパーミッションが不正だと警告が出るので、確認。

chown root:root /etc/ufw/before.rules
chmod 640 /etc/ufw/before.rules

設定(IPv6)

IPv6も設定するなら、「/etc/ufw/before6.rules」にも設定しないといけない模様。

ufwの有効化

当然 ufw を有効にしておく必要がある。

sudo ufw enable

ファイルのパーミッションが不正だと警告が出るので、確認。

chown root:root /etc/ufw/before6.rules
chmod 640 /etc/ufw/before6.rules

まとめ

ufw のコマンド経由では設定できないので、設定ファイルを編集する必要がある。

2012/10/13公開後追記:

Ubuntu 12.04 でこの設定をすると再起動時に「ERROR: problem running ufw-init」エラーが発生して、ufw が有効にならない様子。
公式説明にある、「Enable PING」を再度参考にして設定したが、変化しない。
「/lib/ufw/ufw-init restart」すると、rules のエラーが表示される。

以下でも同じような現象が発生している。
UFW ERROR: problem running ufw-init

2012/10/13夜追加

上記のエラーは単にfilterの記述忘れだった。

*filter
:ufw-before-input - [0:0]
:ufw-before-output - [0:0]
:ufw-before-forward - [0:0]

-A ufw-before-input -p icmp --icmp-type destination-unreachable -j DROP
-A ufw-before-input -p icmp --icmp-type source-quench -j DROP
-A ufw-before-input -p icmp --icmp-type time-exceeded -j DROP
-A ufw-before-input -p icmp --icmp-type parameter-problem -j DROP
-A ufw-before-input -p icmp --icmp-type echo-request -j DROP
COMMIT

2012年10月12日

RRDtool を Python から使う

概要

RRDtool」に関して調査しているが、Shell スクリプトだと個人的には面倒なので、Python から利用する方法を調査してみる。

モジュール

ちゃんとメンテナンスされているのは「python-rrdtool」と「PyRRD」がある。
python-rrdtool」は本家で解説(rrdpython)されているモジュールで、Cモジュールから RRDtool を利用する。ほとんど RRDtool そのまま利用するモジュール。
PyRRD」は、RRDtoolを結構ちゃんとラップしようとしている。Python 的に利用しようとすると便利だが、RRDtoolの一部機能が上手く使えない模様。

どちらを利用するか悩んだが、PyRRD の方が書き易そうだが、結局 RRDtool のマニュアル読むことになって、どのオブジェクト使うか調べるのが面倒。
素直に書ける方がRRDtoolのマニュアルとの対応が取りやすいので、「python-rrdtool」の方を使うことにした。

インストール

pip でインストールする。

pip install python-rrdtool

ただし、Macでインストールすると、Mac の ls が GNU ls でないため「error: ls -t appears to fail. Make sure there is not a broken」のエラーが出るかもしれない。
エラーになる場合は、GNU ls(coreutils)を別途インストールするなりして対処するか、「python-rrdtool」をダウンロードしてきて、「configure」ファイルで「ls」の出力チェックをしている場所をコメントアウトするとコンパイルが通る。

RRDtool を MacPorts でインストールしている場合はソースをダウンロードしてきて、以下でインストールする。

env CPPFLAGS=-I/opt/local/include LDFLAGS=-L/opt/local/lib python setup.py install

使い方

rrdtoolコマンドがわかっていれば、それほど難しくはない。
例えば Shell で以下のように書く場合。

rrdtool create load.rrd \
    --step 300 \
    DS:load1:GAUGE:600:0:U \
    DS:load5:GAUGE:600:0:U \
    DS:load15:GAUGE:600:0:U \
    RRA:AVERAGE:0.5:1:600 \
    RRA:AVERAGE:0.5:6:700 \
    RRA:AVERAGE:0.5:24:775 \
    RRA:AVERAGE:0.5:288:797 \
    RRA:MAX:0.5:1:600 \
    RRA:MAX:0.5:6:700 \
    RRA:MAX:0.5:24:775 \
    RRA:MAX:0.5:288:797 \
    RRA:MIN:0.5:1:600 \
    RRA:MIN:0.5:6:700 \
    RRA:MIN:0.5:24:775 \
    RRA:MIN:0.5:288:797

python-rrdtoolでは以下のように書ける。このサンプルはほとんど変数とか利用してないが、もうすこし Python らしく書くことも可能。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import rrdtool


def create():
    data_sources = [
        'DS:load1:GAUGE:600:0:U',
        'DS:load5:GAUGE:600:0:U',
        'DS:load15:GAUGE:600:0:U',
    ]
    rras = [
        'RRA:AVERAGE:0.5:1:600',
        'RRA:AVERAGE:0.5:6:700',
        'RRA:AVERAGE:0.5:24:775',
        'RRA:AVERAGE:0.5:288:797',
        'RRA:MAX:0.5:1:600',
        'RRA:MAX:0.5:6:700',
        'RRA:MAX:0.5:24:775',
        'RRA:MAX:0.5:288:797',
        'RRA:MIN:0.5:1:600',
        'RRA:MIN:0.5:6:700',
        'RRA:MIN:0.5:24:775',
        'RRA:MIN:0.5:288:797',
    ]

    rrdtool.create('pyload.rrd',
                   '--step', '300',
                   data_sources,
                   rras)


def main():
    create()

if __name__ == '__main__':
    main()

まとめ

個人的には Shell で書くよりはいろいろと応用が効くので楽。

2012年10月11日

RRDtool の使い方

概要

RRDtool」は時系列データを高速にロギングしたり、グラフ化するためのオープンソースのツール。
Munin」等のツール経由で利用する機会が多い。
ほとんどコマンドラインから利用しないので使い方をちゃんと調べた事がなかったのだけど、Munin で高度なグラフが必要になる機会が増えてきたので、ちゃんと調査してみることにした。

インストール

Mac であれば MacPorts なり homebrew なりでインストールするのが楽。この記事を書いている時点では「1.4.7」がインストールされる。

# MacPorts
port install rrdtool
# homebrew
brew install rrdtool

記録用ファイル作成

最初に記録用のファイルであるRRD(Round Robin Database)を作成する。
「rrdtool」コマンドを利用する。ファイルの作成は「rrdcreate」を参照すると詳細が記述されている。

rrdtool create filename \
        [--start|-b start time] \
        [--step|-s step] \
        [--no-overwrite] \
        [DS:ds-name:DST:dst arguments] \
        [RRA:CF:cf arguments]

コマンドの引数は以下。

  • filename:ファイルの名前。通常拡張子を「rrd」にするが、特に拡張子は関係ない。
  • --start:記録開始とする時刻。指定しないと、RRDファイル作成時間の10秒前に設定される。
  • --step:RRDファイルへの記録間隔。指定しないと300秒(5分)に設定される。
  • --no-overwrite:同じファイル名があったら上書きしない。
  • DS:データソース(data sources)の定義。複数定義でき、一定義がグラフ画像を生成する時の一つの線になる。
    • ds-name:参照用のデータソース名。1文字以上19文字以下で、[a-zA-Z0-9_]で定義する
    • DST:データソースのタイプ(Data Source Type)定義。(DS:ds-name:GAUGE | COUNTER | DERIVE | ABSOLUTE:heartbeat:min:max もしくは DS:ds-name:COMPUTE:rpn-expression)
      • GAUGE:取得した値をそのまま記録
      • COUNTER:前回の記録に加算していく。減算は考慮されていない。そのため、いつかオーバーフローするが、適切に処理される。
      • DERIVE:前回との差を記録。オーバフローは考慮されない。マイナス値もありえる。
      • ABSOLUTE:読み取るごとに0になる、もしくは0にしたい場合で、マイナスにならない物を記録。
      • COMPUTE:計算式を記述して、その結果を記録
    • arguments(heartbeat:min:max もしくは rpn-expression)
      • heartbeat:指定秒数を越えてデータが取得できない場合、UNKNOWNと記録する
      • min or max:必須ではない。最大値、最小値を定義。不明な場合は「U」を記述。最大最小が判明している時は明記すること。
      • rpn-expression:逆ポーランド記法の数式。COMPUTEの時のみ利用可能。
  • RRA:RRDへのRRA(round robin archives)の保存定義。(RRA:AVERAGE | MIN | MAX | LAST:xff:steps:rows)
    • CF:統合関数(consolidation function)
      • AVERAGE:平均
      • MIN:最小
      • MAX:最大
      • LAST:最後
    • xff:統合関数の結果、UNKNOWNがどの程度だったら、UNKNOWNにするかを定義。0から1の間の数を指定。0.5だと半分以上がUNKNOWNだと結果をUNKNOWNにする。
    • steps:統合関数にて統合するステップ数
    • rows:記録するレコード数

コマンドの引数は、実際の例を見た方が速いかもしれない。サンプルとして「load average」を取得してみる。

rrdtool create load.rrd \
    --step 300 \
    DS:load1:GAUGE:600:0:U \
    DS:load5:GAUGE:600:0:U \
    DS:load15:GAUGE:600:0:U \
    RRA:AVERAGE:0.5:1:600 \
    RRA:AVERAGE:0.5:6:700 \
    RRA:AVERAGE:0.5:24:775 \
    RRA:AVERAGE:0.5:288:797 \
    RRA:MAX:0.5:1:600 \
    RRA:MAX:0.5:6:700 \
    RRA:MAX:0.5:24:775 \
    RRA:MAX:0.5:288:797 \
    RRA:MIN:0.5:1:600 \
    RRA:MIN:0.5:6:700 \
    RRA:MIN:0.5:24:775 \
    RRA:MIN:0.5:288:797

この例だと以下のようになる。

  • 「load.rrd」というファイル名
  • 1ステップ5分
  • 「load average」は1分、5分、15分の3つの値があるので、DSをその数定義。
  • Daily用に、1ステップを600個、つまり5分×600個なので、50時間(2日2時間)の平均、最小、最大を保存
  • Weekly用に、6ステップを700個、つまり5分×6×700個なので、350時間(2日2時間×7)の平均、最小、最大を保存
  • Monthly用に、24ステップを775個、つまり5分×24×775個なので、1550時間(2日2時間×31)の平均、最小、最大を保存
  • Yearly用に、288ステップを797個、つまり5分×288×797個なので、18600時間(2日2時間×372=31×12)の平均、最小、最大を保存

Dailyは前々日ぐらいまでと比較したいので、50時間にしてある。48時間ぴったりだと同時刻が上手く比較できないため2時間を加算。
この50時間を単位として、週、月、年を定義してある。
週は7倍、月は31倍、年は31×12倍になるように定義。この定義で2年間はデータが記録可能ということになる。

データの記録

実際にデータを記録する。データ記録は「rrdupdate」を参照すると詳細が記述されている。

今回は「load average」を取得してみる。Mac で load average を取得する場合は以下のコマンドで可能。

uptime | awk -F\  '{print $(NF - 2)":"$(NF - 1)":"$NF}'

rrdtoolで記録する場合は時刻と同時に記録するように作成。

rrdtool update load.rrd \
    --template load1:load5:load15 \
    `date +%s`:`uptime | awk -F\  '{print $(NF - 2)":"$(NF - 1)":"$NF}'`

cron 等でしばらく動作させておく。step が 300 に指定してあるが、それより短い時間で記録してもそれなりに処理する。

記録内容の確認

内容確認は「fetch」を利用する。「rrdfetch」を参照すると詳細が記述されている。

rrdtool fetch load.rrd AVERAGE

グラフ画像の生成

グラフ生成は「graph」を利用する。「rrdgraph」を参照すると詳細が記述されている。
グラフの色は自動ではないので、自分で考える必要がある。

rrdtool graph load.png \
    --title "Load Average" \
    --vertical-label "Load Average" \
    --width 500 \
    --upper-limit 10.0 \
    --lower-limit 0 \
    --rigid \
    --imgformat PNG \
    DEF:load1=load.rrd:load1:AVERAGE \
    DEF:load5=load.rrd:load5:AVERAGE \
    DEF:load15=load.rrd:load15:AVERAGE \
    LINE1:load1#00FF00:"CPU Usage 1" \
    GPRINT:load1:LAST:"cur\: %2.2lf /" \
    GPRINT:load1:AVERAGE:"ave\: %2.2lf /" \
    GPRINT:load1:MAX:"max\: %2.2lf /" \
    GPRINT:load1:MIN:"min\: %2.2lf\n" \
    LINE2:load5#0000FF:"CPU Usage 5" \
    GPRINT:load5:LAST:"cur\: %2.2lf /" \
    GPRINT:load5:AVERAGE:"ave\: %2.2lf /" \
    GPRINT:load5:MAX:"max\: %2.2lf /" \
    GPRINT:load5:MIN:"min\: %2.2lf\n" \
    LINE3:load15#FF0000:"CPU Usage 15" \
    GPRINT:load15:LAST:"cur\: %2.2lf /" \
    GPRINT:load15:AVERAGE:"ave\: %2.2lf /" \
    GPRINT:load15:MAX:"max\: %2.2lf /" \
    GPRINT:load15:MIN:"min\: %2.2lf\n"

まとめ

とりあえず、グラフまで生成できるようになったが、オプションが一部の動作が良くわかっていないので、さらに細かい事項は使いながら調べることにする。

参考資料

いくつかの資料を参照した。本家のチュートリアルはそれなりに説明されているが、わかりずらい気がする。

2012年10月10日

Python の ORM 調査:結論

概要

ここ数日Python の ORM を調査した結論。

結論

peewee」お勧め。よほど複雑なことをしないならこれで十分。
peewee」で機能が不足するレベルだと、「SQLAlchemy」以外の選択肢は無い。

「SQLObject」と「storm」は「peewee」の出きが良いので、調査する気力がなくなった。

2012/10/16追記:

この記事は Python のORM 調査の記事の一部となる。以下が関連記事。