読者です 読者をやめる 読者になる 読者になる

FIELD NOTES: 書を持って街へ出よう

合同会社フィールドワークス プログラマ兼代表のブログ

CentOSにnginx+web2pyをインストール

さくらVPS上にnginx+web2pyの構成でWEBサーバを設定する手順をまとめました。 CentOSであれば,概ね同様の手順でインストールできると思います。

前提として,web2pyのファイルは /var/www/web2py に置いているものとします。

nginxのインストール

nginxの公式サイトに書いてあるとおり,yumリポジトリを追加してyumでインストールしてみました。

$ sudo vim /etc/yum.repos.d/nginx.repo

[nginx]
name=nginx repo
baseurl=http://nginx.org/packages/centos/$releasever/$basearch/
gpgcheck=0
enabled=1

$ sudo yum install nginx

uWSGIのインストール

uWSGIは,pipを使ってインストールしました。

$ sudo yum update
$ sudo yum install python-devel
$ sudo yum install libxml2-devel
$ sudo yum install python-pip

$ sudo pip-python install uwsgi

nginxの設定

web2pyに添付されているスクリプト setup-web2py-nginx-uwsgi-ubuntu.sh の中身を切り出して,以下の設定ファイルを作成しました。

$ sudo vim /etc/nginx/conf.d/web2py.conf

server {
        listen          80;
        server_name     $hostname;
        location ~* /(\w+)/static/ {
           root /var/www/web2py/applications/;
        }
        location / {
                uwsgi_pass      127.0.0.1:9001;
                include         uwsgi_params;
                uwsgi_param     UWSGI_SCHEME $scheme;
                uwsgi_param     SERVER_SOFTWARE    nginx/$nginx_version;
        }
}

server {
        listen          443;
        server_name     $hostname;
        ssl                     on;
        ssl_certificate         /etc/nginx/ssl/web2py.crt;
        ssl_certificate_key     /etc/nginx/ssl/web2py.key;
        location / {
                uwsgi_pass      127.0.0.1:9001;
                include         uwsgi_params;
                uwsgi_param     UWSGI_SCHEME $scheme;
                uwsgi_param     SERVER_SOFTWARE    nginx/$nginx_version;
        }

}

HTTPS接続を行う場合は,サーバ証明書と秘密鍵を用意して /etc/ngin/ssl 配下に置きます。

uWSGIの設定

uWSGIの設定は,以下の記事を流用させて頂きました。

まず,uWSGIの設定ファイルを作成します。

$ sudo vim /etc/uwsgi.yaml

uwsgi:
  socket: 127.0.0.1:9001
  pythonpath: /var/www/web2py
  module: wsgihandler
  processes: 2
  daemonize: /var/log/uwsgi-web2py.log
  touch-reload: /tmp/reload.txt

最後のtouch-reloadで指定したファイルをtouchコマンドで更新してやると, ソースの修正が反映される仕組みになっています。 uwsgiとDjangoとリロードに, 自動的に反映させる方法が書いてありましたが,こちらは試していません。

次に,uWSGIの起動スクリプトを作成します。

$ sudo vim /etc/sysconfig/uwsgi

# Configuration file for the uwsgi service.

UWSGI=/usr/bin/uwsgi
CONFFILE=/etc/uwsgi.yaml
LOCKFILE=/var/lock/subsys/uwsgi

$ sudo vim /etc/init.d/uwsgi

#!/bin/sh
#
# uwsgi - this script starts and stops the uwsgi daemon
#
# chkconfig: - 85 15
# processname: uwsgi
# config: /etc/uwsgi.yaml
# config: /etc/sysconfig/uwsgi
# pidfile: /var/run/uwsgi.pid
# description: uwsgi is a WSGI server
#

# Source function library.
. /etc/rc.d/init.d/functions

CONFFILE="/etc/uwsgi.yaml"

if [ -f /etc/sysconfig/uwsgi ]; then
        . /etc/sysconfig/uwsgi
fi

prog=uwsgi
uwsgi=${NGINX-/usr/bin/uwsgi}
conffile=${CONFFILE-/etc/uwsgi.yaml}
lockfile=${LOCKFILE-/var/lock/subsys/uwsgi}
pidfile=${PIDFILE-/var/run/uwsgi.pid}
RETVAL=0

start() {
    echo -n $"Starting $prog: "

    #daemon --pidfile=${pidfile} ${uwsgi} --yaml ${conffile}
    daemon ${uwsgi} --yaml ${conffile} --pidfile ${pidfile}
    RETVAL=$?
    echo
    [ $RETVAL = 0 ] && touch ${lockfile}
    return $RETVAL
}

stop() {
    echo -n $"Stopping $prog: "
    killproc -p ${pidfile} ${prog} -INT
    RETVAL=$?
    echo
    [ $RETVAL = 0 ] && rm -f ${lockfile} ${pidfile}
}

# See how we were called.
case "$1" in
  start)
     start
     ;;
  stop)
     stop
     ;;
  status)
        status -p ${pidfile} ${uwsgi}
     RETVAL=$?
     ;;
  restart)
     stop
     start
     ;;
  *)
     echo $"Usage: $prog {start|stop|restart|status}"
     RETVAL=2
esac

exit $RETVAL

$ sudo chmod +x /etc/init.d/uwsgi

作成した起動スクリプトをサービスとして登録します。

$ sudo chkconfig --add uwsgi
$ sudo chkconfig on

サービスを開始

最後に,uWSGIとnginxのサービスを開始させます。

$ sudo service uwsgi start
$ sudo service nginx start

参照サイト

はがきの宛名印刷用PDF作成サービスを公開します

弊社製品のPDF帳票開発ツール Field Reportsを使って, はがき宛名面の印刷用PDFを生成するサービスを作成しました。

当初は単に製品のデモとして作り始めたのですが, 意外と実用的なものができたので, Webサービスとして一般に公開したいと思います。

サービスの概要

URLは, http://labs.field-works.co.jp/labs/atena/form です。

フォームに宛名と差出人の住所・氏名を入力して「帳票出力」ボタンを押すと,PDFを生成します。 プリンターで生成されたPDFをはがきに印刷すれば,宛名面の印刷ができます。

本サービスはフォームから入力して利用するだけでないく,Web API として機能を呼び出すこともできます。 Web API を介しての利用方法については,次回説明します。

入力フォーム

宛名(個別入力)

「宛名指定方法」欄の「個別入力」ラジオボタンを選択すると, 宛名をフォームで一件ごとに入力するモードになります。

このモードで作成できるのは,1ページのPDFのみです。

宛名(一括入力)

「宛名指定方法」欄の「一括入力」ラジオボタンを選択すると, CSVファイルで一括して宛名を指定するモードになります。

CSVファイルとCSVファイルの文字コードを指定してください。 CSVファイルの書式については,後述します。

差出人

差出人の郵便番号と住所を入力します。

レイアウト

横書きと縦書きの2種類のテンプレートを用意しています。

横書き 縦書き

位置の調整

プリンターの特性により印刷位置がずれる場合に調節してください。

CSVファイルの書式

カンマ区切りのCSVファイルとします(Excel形式のCSVファイルを想定しています)。

1行目にフィールド名,2行目以降にフィールドに対応するデータを記述してください。 使用できるフィールド名は以下のとおりです。

フィールド名 説明
post1 郵便番号上3桁
post2 郵便番号下4桁
address1 住所1行目
address2 住所2行名
company 会社名・団体名
title 肩書き
name 氏名

データ数は最大100件です。 101件目以降のデータは無視されます。

記述例

post1,post2,address1,address2,company,title,name
163,8001,東京都新宿区西新宿二丁目8番1号,,東京都庁,知事,石原 慎太郎 様
460,8501,愛知県名古屋市中区三の丸三丁目1番2号,,愛知県庁,知事,大村 秀章 様

OCamlでWindows用DLLを作成する方法

同一のコードから複数のプラットフォーム用の共有ライブラリ(Unix系OSなら.so,Mac OS Xなら.dylib,Windowsなら*.DLL)を作りたい場合,どのプログラミング言語を選ぶべきでしょうか?

まず思い浮かぶのがC/C++だと思いますが,もっと「モダン」なプログラミング言語は使えないものでしょうか?

「モダン」なプログラミング言語とは

私の考えるモダンなプログラミング言語の要件は以下のとおりです。

  • オブジェクト指向または関数プログラミングを取り入れていること。
  • リスト,辞書などのデータ構造が言語仕様に組み込まれていること。
  • GCが搭載されていること。
  • segmentation fault が原理的に発生しないこと。

2年ほど前にこれらの要件満たし,かつネイティブ・オブジェクトを生成可能なプログラミング言語を探したところ, Lisp/Scheme系の言語と関数プログラミング言語の中からいくつか見つけることができました。

弊社ではこの中からOCamlを選択し,PDF帳票開発ツール Field Reportsを開発しました。 ネイティブなライブラリが必要だった理由は,Python, Ruby, PHP, Perlなど多くのLL言語から利用可能な拡張ライブラリを作るためでした。

OCamlで各LL言語用の拡張ライブラリを構築する手順については過去に何回か書いてきましたが, Unix系のOSでの話が中心でした。

Windowsでの構築手順を書いていなかったので,今回はOCamlのコードからDLLを作成するまでの手順を説明したいと思います。


らくだ / 31o5

用意するもの

Windows用OCaml処理系には,MinGWでビルドしたものとVisual C++でビルドしたものがあります。 今回は,MinGWでビルドしたバイナリを選択しました。

MinGW版のOCamlを使用するので,当然MinGW開発環境も必要になります。 ただ,標準のMinGW環境をベースに不足しているライブラリやコマンドを足していくのは結構手間が掛かります。 裏技的ですが,Ruby用の開発環境DevKitをベースにすると, 最初から必要な物が揃っているのでいくらか楽ができます。

ラッパー・プログラムの記述

OCamlがネイティブオブジェクトを出力できるといっても直接呼び出すことはできません。 薄いラッパーの層が必要になります。

以下の説明では,OCamlで記述したFooモジュールからビルドしたライブラリ foo.cmxa が既に存在しているものとし, Fooモジュールの中のbar関数を外部に公開するものとします。

OCamlコールバック関数の登録

まず,外部に公開するOCaml関数をコールバック関数として登録します。 以下のようなOCamlプログラムを作成して,foo_register.ml という名前で保存します。

let _ =
  Callback.register "bar" Foo.bar;

OCamlコールバック関数を呼び出すC関数

次に,OCamlのコールバック関数を呼び出すC関数を記述します。 これを foo_callback.c という名前で保存します。

#include <caml/callback.h>
void bar()
{
    static value * f = NULL;
    if (f == NULL) {
        f = caml_named_value("bar");
    }
    caml_callback(*f, Val_unit);
}

ビルド手順

(1) foo_register.mlのコンパイル

最初に foo_register.ml をコンパイルします。 -output-obj オプションを付けることにより,C形式のオブジェクトが出力されます。 この時,Fooモジュールから参照しているOCamlライブラリを含めると, ライブラリのコードも一つのオブジェクトファイルにまとめることができます。

$ ocamlopt -cclib -fPIC -output-obj -o foo_register.o -I <ライブラリ・パス> \
    <OCamlライブラリ> foo.cmxa foo_register.ml

(2) foo_callback.c のコンパイル

次にCソースをコンパイルします。

$ ocamlc -ccopt -I <インクルード・パス>  -c foo_callback.c

(3) DLLの作成

最後に(1), (2)とその他必要なライブラリ をすべてリンクしてDLLとしてまとめます。

$ flexlink -L<ライブラリパス> -maindll -show-imports -chain mingw \
      -o foo.dll foo_register.o foo_callback.o -lm -lstr -lunix -lasmrun

-lasmrunは必須です。 -lstr-lunixは,Strライブラリ,Unixライブラリを使用している場合に必要になります。 その他,-lmのようなライブラリを必要に応じてリンクします。

(4) インポートライブラリの作成

インポートライブラリがあると,暗黙的リンクが可能になります。 必要に応じてインポートライブラリを作成します。

$ dlltool -k -l libreports.a -d libreports.dll -d libreports.def

まとめ

OCamlでDLLを作成する手順が確立できました。 これで,Linux, Mac OS X, Windows用の共有ライブラリが作成できるようになりました。

残課題として以下があります。

64ビット版DLLがビルドできなかった。

64ビット版MinGWも存在するようなので,今後挑戦したいと思います。

Visual Studio からは,dlltoolで作成したインポートライブラリを利用できなかった。

今回は明示的リンクで回避しましたが,使用するAPIが多くなると大変かもしれません。 Visual C++版のバイナリを使用すれば問題なかったかもしれませんが, 特に必要を感じていないので試す予定はありません。

flexlinkでリンクする時にオブジェクトモジュールが大きくなるとエラーが発生する

flexlinkのバグなのかどうは判然としませんが,3.11ではflexlinkが必須になっていました を参考にパッチをあててエラーが出ないようにしてしまいました。 今のところ問題は出ていません。

参照サイト

ブログ移転のお知らせ

2012年3月より,本ブログは下記URLへ引越します(Tumblrの独自ドメイン機能を利用しています)。

http://blog.field-works.co.jp/

今後ともどうぞよろしくおねがいいたします。

2012/11/17

はてなブログがMarkdown記法に対応しました。

これを機会に,はてなダイヤリーの過去記事をはてなブログに移行すると共に,しばらくはTumblerとはてなブログを併用していきたいと思います。