WSGIDaemonProcessの謎

mod_wsgiを設定してみた。
環境は FreeBSD11 + Apache24。

ハマったのは、venv のパス。

/usr/local/lib/python3.6/venv

とかに設定してもダメ。encodings というモジュールがないです、と言われてしまう。

どうも大事なのは以下のコマンド。バーチャルホストのホームディレクトリ(あるいはApacheのドキュメントルートのひとつ上あたりのディレクトリ。要するにPythonアプリケーションをインストールしたあたり)で以下を実行する。

$ python -m venv ./venv

したらば、Apacheの設定ファイルに以下のような設定を書く。

<VirtualHost 127.0.0.1:80>
    ServerName www.example.com
    DocumentRoot /path/to/www.example.com/htdocs
    WSGIDaemonProcess www.example.com python-home=/path/to/www.example.com/venv threads=5
    WSGIProcessGroup www.example.com
    WSGIScriptAlias /myapp/ /path/to/www.example.com/myapp/myapp/wsgi.py process-group=www.example.com
    WSGIScriptReloading On
    <Directory /path/to/www.example.com/pyapp/pyapp>
        <Files wsgi.py>
            Require all granted
        </Files>
    </Directory>
</VirtualHost>

python-home=/path/to/www.example.com/venv は、さっき作った venv ディレクトリの絶対パス。

これでなんとか動きました。
が…。
今度は、djangoが認識されない。アプリをウェブから叩いて動かそうとすると、「djangoモジュールが見つからないです」(意訳)とapacheのエラーログに吐き出される。

どうして!? Django公式ドキュメント通りにやってるのに!
と悩むことしばし。

ふと基本に立ち返って「rootじゃダメだよなぁ」と、バーチャルホストのユーザーに切り替えて、バーチャルホストのユーザーディレクトリであれこれコマンドを打ち込んだら、なんとかなった。

わたしがハマった最大の要因は、すべての作業を root で行ったこと。
本来はバーチャルホストのユーザーで行わなくてはいけない。
(わたしはバーチャルホストごとにユーザーを分けています)

そして、バーチャルホストのユーザーディレクトリで完結するように「環境」を整えないといけない。
さすがはPython! PHPとは違う。

$ cd www.example.com
$ python -m venv ./venv
$ ./venv/bin/pip install django
$ ./venv/bin/django-admin startproject myapp
$ ... (以下python公式ドキュメントと同じ。)

こんな感じで、バーチャルホストのディレクトリで作業を完結させる。
すると、「モジュールないですエラー」は出なくなる。

次の問題は…「myappモジュールないですエラー」
わけがわからない…。

Nginx+Apache×2+SSL(+WordPress)

獲得目標は以下の通りです。
●SSLの設定をしたNginxでリバースプロキシをさせて、バックエンドのApacheに渡す。
●Wordpressも動くようにする。

ではさっそく。

Nginx(エンジンエックスと読むそうですね!):

http {
    upstream MidgenasiaServers {
        # バックエンドサーバーの設定をします。
        server 10.0.0.80:8080; # 8080番ポートでhttp通信をListenしているIP 10.0.0.80上のApache 
        server 10.0.0.81:8080; # 8080番ポートでhttp通信をListenしているIP 10.0.0.81上のApache
    }
    upstream OtherServers {
        # こちらもバックエンドサーバーの設定です。上と同じサーバーに格納されている想定です。
        server 10.0.0.80:8081;
        server 10.0.0.81:8081;
    }
    server {
        # http通信はすべてhttpsに301リダイレクトします。
        listen 80;
        return 301 https://$host$request_uri;
    }
    server {
        listen 443 ssl;
        server_name  www.midgen.asia;
        charset utf-8;

        # SSL
        ssl_certificate      path/to/server.crt;
        ssl_certificate_key  path/to/server.key;
        ssl_session_cache  builtin:1000  shared:SSL:10m;
        ssl_protocols  TLSv1 TLSv1.1 TLSv1.2;
        ssl_ciphers HIGH:!aNULL:!eNULL:!EXPORT:!CAMELLIA:!DES:!MD5:!PSK:!RC4;
        ssl_prefer_server_ciphers  on;
        # server.keyにパスフレーズを指定してある場合はたぶん必要。パスフレーズだけ書いておけばOK。
        ssl_password_file path/to/server-password;

        location / {
            # upstreamの場所で指定した名称をここに書きます。
            proxy_pass          http://MidgenasiaServers;
            proxy_redirect      off;
            proxy_set_header    Host    $host;
            proxy_set_header    X-Real-IP    $remote_addr;
            proxy_set_header    X-Forwarded-Proto     https;
            proxy_set_header    X-Forwarded-Host      $host;
            proxy_set_header    X-Forwarded-Server    $host;
            proxy_set_header    X-Forwarded-For    $proxy_add_x_forwarded_for;
        }
    }

    # 2つ目のドメインの設定も続けて書きます。
    server {
        server_name  www.another.domain.com;
        charset utf-8;

        ssl_certificate      path/to/another.domain.com.crt;
        ssl_certificate_key  path/to/another.domain.com.key;
        ssl_session_cache  builtin:1000  shared:SSL:10m;
        ssl_protocols  TLSv1 TLSv1.1 TLSv1.2;
        ssl_ciphers HIGH:!aNULL:!eNULL:!EXPORT:!CAMELLIA:!DES:!MD5:!PSK:!RC4;
        ssl_prefer_server_ciphers  on;
        ssl_password_file path/to/another.domain.com-password;

        location / {
            # 上記と同様に、upstreamの場所で指定した名称をここに書きます。
            proxy_pass    http://OtherServers;
            proxy_redirect      off;
            proxy_set_header    Host    $host;
            proxy_set_header    X-Real-IP    $remote_addr;
            proxy_set_header    X-Forwarded-Proto     https;
            proxy_set_header    X-Forwarded-Host      $host;
            proxy_set_header    X-Forwarded-Server    $host;
            proxy_set_header    X-Forwarded-For    $proxy_add_x_forwarded_for;
        }
    }
}

※要旨に関係がないコメントや設定は削除してあります。

Apache:

Listen 8080
Listen 8081

>VirtualHost 10.0.0.80:8080<
    ServerName www.midgen.asia
    DocumentRoot "/path/to/midgen.asia.htdocs"
>/VirtualHost<

>VirtualHost 10.0.0.80:8081<
    ServerName www.gowest-comewest.net
    DocumentRoot "/path/to/another.domain.com.htdocs"
>/VirtualHost<

ここまででNginx+SSLとラウンドロビンなApacheは完成。
あとは…Wordpress。リバースプロキシ+SSLという環境下では、特殊な記述をwp-config.phpに加える必要があるようです。

WordPress(wp-config.phpの上のほうに書きます):

if (isset($_SERVER['HTTP_X_FORWARDED_PROTO'])
    && $_SERVER['HTTP_X_FORWARDED_PROTO'] === "https") {
  $_SERVER['HTTPS'] = 'on';
}
define('FORCE_SSL_LOGIN', true);
define('FORCE_SSL_ADMIN', true);

参考: WordPressをhttps化してnginx reverse proxy配下で使うには

これでWordpressのCSSやJavaScriptが読み込まれないという状況も打破できます。

できてよかった!

MeCab+python+JavaScriptでサイトにルビを振る

標題の通り。意外と簡単にできたので共有します。


※正規表現で先読みと後読みのアサーションを使っているため、最新のEcmaScriptでないと動作しません…PC版Chromeでしか動作しません…なんとかしたい…。


ではさっそく。

python:


#coding: utf-8

from mod_python import apache, util
import re
import json
import MeCab

def handler(req):
    # テキスト取得
    fs  = util.FieldStorage(req, True)
    q   = fs.getfirst("q", "")

    if q:
        tagger  = MeCab.Tagger("-Oyomi")
        node    = tagger.parseToNode(q)

        wordTable   = []
        a2n_regexp  = re.compile("^[あいうえおかきくけこさしすせそたちつてとなにぬねのはひふへほまみむめもやゆよらりるれろわゐゑをんがぎぐげござじずぜぞだぢづでどばびぶべぼぱぴぷぺぽぁぃぅぇぉっゃゅょアイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワヰヱヲンガギグゲゴザジズゼゾダヂズデドバビブベボパピプペポァィゥェォッャュョヴ]+$")
        while node:
            wordTypes   = node.feature.split(',')
            if node.surface != "" and \
                len(wordTypes) >= 8 and \
                not a2n_regexp.match(node.surface) and \
                wordTypes[7] != node.surface:
                wordTable.append({
                    "y": wordTypes[7] or "*",
                    "k": node.surface or ""
                })
            node    = node.next
        sendJson(req, True, wordTable)

    else:
        sendJson(req, False)
    
    return apache.OK

# JSON形式で返答を返す。
def sendJson(req, result = False, returnValue = []):
    req.content_type    = "application/json; charset=utf-8"
    req.write(json.dumps({"result": result, "words": returnValue}, False, False))



JavaScript:

"use esversion:6";

(function($, d, w, undef) {
    "use strict";

    $(d).on("click", ".furigana-toggler", function(e) {
        if (!$(this).hasClass("on")) {
            $(this).remove();
            furiganaOn();
        }
        e.preventDefault()
    })

    function furiganaOn() {
        $("body").find("*:not([nodeType=1])").each(function() {
            let node = $(this);
            if (node.text() !== "") {
                $.post(
                    "/py/mecab.py/getFurigana",
                    {
                        q : node.text()
                    },
                    function(json) {
                        if (json.result === true && json.words !== undef) {
                            let text  = node.html();
                            let max   = json.words.length;
                            for (let i = 0; i < max; i++) {
                                let regex = new RegExp("(?<!<ruby>[^<>]*)" + json.words[i].k + "(?![^<]*<rt>" + json.words[i].y + "</rt></ruby>)");
                                text = text.replace(
                                    regex,
                                    "<ruby>" + json.words[i].k + "<rp>[</rp><rt>" + json.words[i].y + "</rt><rp>]</rp></ruby>"
                                );
                            }
                            node.html(text);
                        }
                    },
                    "json"
                );
            }
        });
    }
    
    function furiganaOff() {
        $("ruby.furigana, rp.furigana, rt.furigana").unwrap();
    }
})(jQuery, document, window);

pythonの方には何らかの認証機構をつけなければいけませんが、ひとまずこんな感じです。

さくらのレンタルサーバー[スタンダードプラン FreeBSD]にsubversionを入れる方法

FreeBSD環境下におけるsubversionのインストールにはほんのちょっとだけ工夫がいるのでした。

[参考] https://apr.apache.org/compiling_unix.html

“Note that if you are compiling directly from the Subversion repository, you’ll need GNU autoconf and GNU libtool installed, and you’ll need to run ./buildconf before running the commands listed above.”だそうです。わたしは気がついたけれど、気づかないひともおられるかも、ということで、書き残しておきます。

あと、非技術者だけどさくらのレンタルサーバーにsubversionを入れたい、というひとのために。

※ ホームディレクトリが散らかるので、それが嫌なひとは ~/src みたいなディレクトリを作って作業するといいと思います。作業が終わったあとでまとめて消せてラクです。
※ わたしは ~/local というディレクトリを作ってその中にインストールしましたが、作らずに ./configure –prefix=$HOME などとやっても大多数のひとにはたぶん問題ないです。その場合はパスを通す作業は必要ないです。
※ aprとsubversionのバージョンは、特別な理由がない限り、最新のものをそろえたほうがいいと思います。

[準備]
% cd
% fetch http://ftp.jaist.ac.jp/pub/apache/apr/apr-1.5.2.tar.bz2
% fetch http://ftp.jaist.ac.jp/pub/apache/apr/apr-util-1.5.4.tar.bz2
% fetch http://ftp.yz.yamagata-u.ac.jp/pub/network/apache/subversion/subversion-1.9.5.tar.bz2
% tar jxf apr-1.5.2.tar.bz2
% tar jxf apr-util-1.5.4.tar.bz2
% tar jxf subversion-1.9.5.tar.bz2
% mkdir local

[apr]
% cd $HOME/apr-1.5.2/
% ./buildconf
% ./configure --prefix=$HOME/local
% make test
% make
% make install

[apr-util]
% cd $HOME/apr-1.5.4/
% ./buildconf --with-apr=$HOME/apr-1.5.2
% ./configure --prefix=$HOME/local --with-apr=$HOME/apr-1.5.2
% make test
% make
% make install

[subversion]
% cd $HOME/subversion-1.9.5/
% ./configure --prefix=$HOME/local --with-apr=$HOME/apr-1.5.2 --with-apr-util=$HOME/apr-util-1.5.4
% make test
% make
% make install

[パスを通す]
% vi ~/.cshrc
% source ~/.cshrc

set path の最後に $HOME/local/bin をつけ足す。以下のようになります。

set path = (/sbin /bin /usr/sbin /usr/bin /usr/local/sbin /usr/local/bin $HOME/bin $HOME/local/bin)

[動作確認]
% cd
% svnadmin help
>>> 実行結果が表示されれば成功! <<<

[かたづけ]
% cd
% rm -rf apr-* subversion-1.9.5*

以上。

Mysql5.6 on Raspberry pi 2: サーバーを落ちないようにする

mysqldが落ちて落ちてしかたがない! という状況に陥ったわたしが探し当てた解決策。

大したことではないですが、初心者には大事なこと。

http://easyramble.com/example-of-mysql-options.html

上記ウェブサイトで紹介されていた設定を以下のように変えてmy.cnfに単純に追記しただけで、落ちなくなりました。

max_connections = 3
max_user_connections = 1

単純に、接続制限をかけただけです。が、効果はてきめん!
F5攻撃を行っても、単に接続エラーが発生するだけで、落ちなくなりました。

ついでにhttpd(apache24)のmpmの設定でも接続制限をして、ひと安心。