いかにして私は心配するのをやめてクラウドを使うことにしたか

これまではxreaを使っていました。なんといっても安い。(My|Postgre)SQLがバックエンドにデフォルトで用意されていて、テキトーに小さなサーバーがほぼDedicatedでvirtualhostか何かで共有されているのでもよいと思っていました。
でも許せなかったんです。手元ではPython 2.6, PostgreSQL 9.0.1という割と新しい環境なのに、サーバー側はPython 2.4, PostgreSQL 8.4何とかとかいう古い環境。既に手元でDjangoの開発終わってしまいマシタヨー。いくら私にはbuildoutがついているッ!!といったところでこの二つばかりはbuildoutでもどうにもなりません。
以前自宅にサーバーがあった頃は、引越しでサーバー止めないといけないとかそういう緊急事態にAWSでサーバー起こして1週間だけ運用とかやったんですよ。でも高い。xreaがこみこみで月500円くらいなのに、AWSと来たらなんだかんだで7000円くらいとりやがる。そりゃあmicro instanceもなかった頃だし、海の向こうのDCだし*1、これで商売しようとする人間には初期投資がないだけでも十分うれしい価格(メンテだけでこんくらい飛んでいくもんねー)だわさ。でもインターネットは基本無料だと思っている私です。接続会社とプロバイダ以外にお金を払う気がないです(ぉ
そこでxreaやめてさくらのVPS使ってみることにしたんです。月1000円くらい。環境については

  • Pros
    • ブラウザからコンソールが使える
    • 起動とか再インストールとか簡単
    • DedicatedなOSが使える!!
    • コントロールパネルかっこいい
  • Cons
    • ブラウザのJavaを切ってるけど、Debianをインストールしたかった…OSのインストールもCUIで何とかならないかな。ISOアップロードもしたい。
    • デフォルトのCentOSは古い…Pythonは当然2.4だしyumで入りそうなPostgreSQLも8.0....えっ?

といったところで、とてもよいと思います。sshで入っても長時間走ってるプロセスはkillされるとかいうのよりはるかにすばらしい*2

CentOSが古いので、昔のようにサーバーを手作りしないといけません。PostgreSQL 9.0.1をソースからとってきて何も考えずに

$ tar xvzf postgreql-9.0.1.tar.gz -C /usr/src/mines
$ cd /usr/src/postgresql-9.0.1
$ ./configure
$ make
# make install

そうすると/usr/local/pgsqlイカに一式入るんだけど、binがPATHに入らないわlibがコンパイラとかリンカに入らないわまー大変。/etc/ld.so.conf.d/postgresql.confに/usr/local/pgsql/libと一行かいて# ldconfig とかやるんです。いまどきこんなことやるなんて思わなかった。あ、その前にpsycopg2のインストールに苦戦したりとかね。libpq-fe.hがねーよとか言われるんです。でドキュメントとか読んでられないのでググッた−

# python setup.py build_ext --pg-config /path/to/pg_config build
# python setup.py install

Pythonは、djangoが動くから2.4のままでいっかぁーと。ふつうにインストールしました。ホントはgunicornとか試せるとカッコいいなぁーとか思ったが、そんなに頑張るところでもないのでapache htttpd+mod_pythonで済ませました。/etc/httpd/httpd.confとかひさしぶりにさわったよ。あとapache httpdをちゃんとprefork MPMで動かすには…とかそういうことも調べました。preforkなはずなのになんかモッサリしてるんだよなぁ。なんでだろう。preforkでもmod_pythonでプロセス動かしたりしているのか、それとも純粋にサーバースペックが足りなくてダメなのかな。手間をかけないことを重視したのでmemcachedもそういえば入れてない。

おまけ

xreaだったころはdjango動かすためにdjango.cgiとか入れてた。毎回プロセスたちあげてたから遅かったわー

#!/usr/bin/env python
# encoding: utf-8
"""
django.cgi

A simple cgi script which uses the django WSGI to serve requests.

Code copy/pasted from PEP-0333 and then tweaked to serve django.
http://www.python.org/dev/peps/pep-0333/#the-server-gateway-side

This script assumes django is on your sys.path, and that your site code is at
/home/mycode/mysite. Copy this script into your cgi-bin directory (or do
whatever you need to to make a cgi script executable on your system), and then
update the paths at the bottom of this file to suit your site.

This is probably the slowest way to serve django pages, as the python
interpreter, the django code-base and your site code has to be loaded every
time a request is served. FCGI and mod_python solve this problem, use them if
you can.

In order to speed things up it may be worth experimenting with running
uncompressed zips on the sys.path for django and the site code, as this can be
(theorectically) faster. See PEP-0273 (specifically Benchmarks).
http://www.python.org/dev/peps/pep-0273/

Make sure all python files are compiled in your code base. See
http://docs.python.org/lib/module-compileall.html

"""

import os, sys 
sys.path.append('/virtual/kuenishi/python/lib/python')
# insert a sys.path.append("whatever") in here if django is not
# on your sys.path.
import django.core.handlers.wsgi

def run_with_cgi(application):
    
    environ                      = dict(os.environ.items())
    environ['PATH_INFO']         = environ.get('PATH_INFO', "/")
    environ['wsgi.input']        = sys.stdin
    environ['wsgi.errors']       = sys.stderr
    environ['wsgi.version']      = (1,0)
    environ['wsgi.multithread']  = False
    environ['wsgi.multiprocess'] = True
    environ['wsgi.run_once']     = True
    
    if environ.get('HTTPS','off') in ('on','1'):
        environ['wsgi.url_scheme'] = 'https'
    else:
        environ['wsgi.url_scheme'] = 'http'
    
    headers_set  = []
    headers_sent = []
    
    def write(data):
        if not headers_set:
             raise AssertionError("write() before start_response()")
        
        elif not headers_sent:
             # Before the first output, send the stored headers
             status, response_headers = headers_sent[:] = headers_set
             sys.stdout.write('Status: %s\r\n' % status)
             for header in response_headers:
                 sys.stdout.write('%s: %s\r\n' % header)
             sys.stdout.write('\r\n')
        
        sys.stdout.write(data)
        sys.stdout.flush()
    
    def start_response(status,response_headers,exc_info=None):
        if exc_info:
            try:
                if headers_sent:
                    # Re-raise original exception if headers sent
                    raise exc_info[0], exc_info[1], exc_info[2]
            finally:
                exc_info = None     # avoid dangling circular ref
        elif headers_set:
            raise AssertionError("Headers already set!")
        
        headers_set[:] = [status,response_headers]
        return write
    
    result = application(environ, start_response)
    try:
        for data in result:
            if data:    # don't send headers until body appears
                write(data)
        if not headers_sent:
            write('')   # send headers now if body was empty
    finally:
        if hasattr(result,'close'):
            result.close()

# Change this to the directory above your site code.
sys.path.append("/virtual/kuenishi/python/lib/python2.4/site-packages")
# Change mysite to the name of your site package
os.environ['DJANGO_SETTINGS_MODULE'] = 'mywebapp.settings'
run_with_cgi(django.core.handlers.wsgi.WSGIHandler())

*1:この際距離とかロケーションは関係ないのかな

*2:これってセキュリティリスクにもなるんだけど