8

แก้ปัญหาเล็กๆ น้อยๆ กับ Pylons + mod_wsgi + virtualenv

เดิมที่ผมมีปัญหาว่า app ที่ใช้ pylons เขียนใช้ได้บ้างไม่ได้บ้าง ชนิดที่ว่าเข้าครั้งแรกไม่ได้พอ reload ก็ใช้ได้ เป็นแบบนี้ไปเรื่อยๆ โดยไม่รู้ว่าเกิดจากอะไรกันแน่. แต่เท่าที่ดู error log ของ Apache พอว่าเกิดจากหา package ไปเจอ. ผมก็ลองแก้ไปแบบชนิดที่ว่าคิดอะไรออกก็ลองดูหมด จนผ่านไปหลายชั่วโมง. สิ่งที่ทำคือ แทนที่จะเพิ่ม path เข้าไปใน app ที่วางไว้เฉยๆ โดยไม่ได้ install เช่น ผมเอา app ชื่อ basa ไปวางไว้ใน home ก็จะอยู่ที่ /home/vee/basa ผมก็เพิ่ม /home/vee เข้าไปใน python path เลย โดย site.addsitedir(‘/home/vee’) แต่ก่อนหน้านั้นก็ site.addsitedir(‘/home/vee/env/PYLONS/lib/python2.5/site-packages’) ไปแล้ว. ตอนนี้เลยเปลี่ยนไปเรียก python setup.py build และ python setup.py install ก่อนก็จะทำให้ basa นี้ไปอยู่ใน path อย่าง /home/vee/env/PYLONS/lib/python2.5/site-packages เป็นต้น ก็ทำให้ add แค่ path เดียวคือ /home/vee/env/PYLONS/lib/python2.5/site-packages ก็พอ. ผลคือตอนนี้ยังไม่พบปัญหาอะไร app ใช้งานได้ปกติดี.

ปกติชาวบ้านเขาอาจจะ install กันแบบนี้อยู่แล้ว แต่ผมดันทำอะไรประหลาดเองหรือเปล่าก็ไม่รู้ … และไม่อยากรู้เท่าไหร่ ขอให้ app ทำงานเนียนๆ ก็พึงพอใจมากแล้ว ^_^.

0

What I did for pylons app deployment.

I try to install my application created on Pylons on the Debian server that has already Apache2 + mod_wsgi + virtualenv + userdir.

What I did:

  1. creating prod.ini by copying development.ini.
  2. Change sqlalchemy.url
  3. set debug = false
  4. write a script in ~/public_html/wsgi-script as follow:
    import os
    import site
    os.environ['PYTHON_EGG_CACHE'] = "/home/myhome/egg"
    site.addsitedir("/home/myhome/wsgiapp/basa2")
    site.addsitedir("/home/myhome/pyenv/PYLONS-1/lib/python2.5/site-packages")
    
    from paste.deploy import loadapp
    APP_CONFIG = "/home/myhome/wsgiapp/basa2/prod.ini"
    application = loadapp("config:" + APP_CONFIG)
    
    
  5. I also modify this file: /home/vee/pydev/PYLONS-1/lib/python2.5/site-packages/Pylons-0.9.7rc3-py2.5.egg/pylons/wsgiapp.py by checking before the command:
    del  environ['pylons.pylons']

    following this changeset. (I use Pylons 0.9.7rc3)

The procedures above are just conclusion. I did a lot of trial and error. I also restart Apache web server (I must not have to do). By the ways, it works now.

0

xmlrpc ด้วย python บน apache

xmlrpc ด้วย python บน apache ผ่าน mod_wsgi. ใช้ xmlrpc บน python เขียนง่ายมากเลย ทั้ง client และ server ดูตัวอย่างจาก http://docs.python.org/library/simplexmlrpcserver.html ได้

แต่ว่า simple server นี้เป็น standalone server แล้วผมก็เจอปัญหาอีกว่า พอเรียกใช้จากเครื่องอื่น ที่ไม่ใช่เครื่องเดียวกันแล้วมั้นช้าๆ ชอบกล เลยเปลี่ยนไปใช้ wsgi-xmlrpc แทน มีคนเขียนไว้ให้แล้วด้วยดีจัง ^_^.

ทีแรกผมก็ลงโปรแกรมจาก easy_install เลย ใช้ easy_install wsgi_xmlrpc อะไรทำนองนี้ก็ลงได้ แต่เล่นไปเล่นมาเจอปัญหาภาษาไทย ก็เลยเอา source code จาก svn trunk ที่ google code มาใช้แทน ถึงตอนนี้ก็ยังใช้ได้ดีอยู่. (วิธีใช้ mod_wsgi กับ userdir เข้าไปดูที่ http://blog.vee-u.com/2008/06/18/mod_wsgi_userdir_ubuntu/ ได้
และ http://blog.vee-u.com/tag/wsgi/ ได้)

เพื่อที่ผมจะได้เอาไว้ดูเองในภายหลังด้วยจึงต้อง post ตัวอย่างสักหน่อย สมมุติ เขียน client แบบนี้

# -*- coding: UTF-8 -*-
import sys
reload(sys)
sys.setdefaultencoding('utf-8')

from xmlrpclib import ServerProxy
server = ServerProxy("http://localhost/test_service.wsgi", encoding="UTF-8")
print server.test_func("ดีจ๊ะ")

พอโปรแกรมทำงานแล้วได้ผลแบบนี้

ดีจ๊ะ ^_^

เวลาเขียน server ก็เขียนประมาณนี้ใน test_service.wsgi

import site
import os

os.environ['PYTHON_EGG_CACHE'] = "/somewhere/egg"
site.addsitedir("/home/vee/pyenv/WSGI-1/lib/python2.5/site-packages")

from wsgiref import simple_server
import wsgi_xmlrpc

def test_func(txt):
   return txt + " ^_^"

application = wsgi_xmlrpc.WSGIXMLRPCApplication(methods=[test_func])

^_^

0

แยก python environment: Pylons + virtualenv + mod_wsgi

ในเครื่องแม่ข่ายเครื่องหนึ่งอาจจะเป็นไปได้ว่ามีคนใช้ Pylons, Django, Turbogears แถมยังอาจจะใช้ package ของ Python ที่แตกต่างๆ กัน. ถ้าลงทุก package ที่ทุกคนต้องการไว้ในส่วนกลางหมดก็เป็นไปได้ว่าอาจจะมีบาง package ขัดแย้งกัน โดยเฉพาะโปรแกรมเดียวกันที่ต่าง version กัน. มากไปกว่านั้นก็อาจจะต้องกวนให้ sys admin ช่วย install ให้บ่อยๆ ด้วย หรือจะให้ทุกคนลง package ในส่วนกลางได้เองหมด ก็อาจจะกระทบกับเสถียรภาพโดยรวมของระบบ. อย่ากระนั้นเลยใช้ virtualenv มาแยก Python environment ของแต่ละคนเลยดีกว่า.

ผมก็เลยลองลง virtualenv ใช้กับ Apache + mod_wsgi + mod_userdir บน Ubuntu 8.04 บนเครื่องส่วนตัวของผมเองดู. ใน entry นี้ผมสมมุติว่าใช้ Apache + mod_wsgi + mod_userdir ได้อยู่แล้วนะครับ (และ มี setuptools ไว้แล้ว) แต่ถ้าสนใจเรื่องการติดตั้งระบบที่ใช้ mod_wsgi ผมก็เขียนเก็บไว้บ้างเหมือนกันที http://blog.vee-u.com/tag/mod_wsgi/

ลง virtualenv ง่ายมากเพียงสั่ง

$ sudo easy_install virtualenv

ก็เสร็จแล้ว

จากนั้นก็สร้าง directory ไว้ลง package ส่วนตัว โดยสั่ง

$ mkdir $HOME/pyenv

จากนั้นก็เข้าไปจัดการสร้าง enviroment โดย

$ cd $HOME/pyenv
$ virtualenv –no-site-packages BASELINE

แล้วก็สร้าง environment สำหรับลง Pylons
$ virtualenv –no-site-packages PYLONS-1

ก่อนลง Pylons ก็ activate environment นั้นก่อน
$ source PYLONS-1/bin/activate

พอ activate แล้ว prompt จะเปลี่นนเป็น (PYLONS-1)$

จากนั้นก็ลง Pylons 0.9.7 (รุ่นทดลอง), SQLAlchemy 0.4.8 และ PasteDeploy (ไม่ต้อง sudo ด้วย)
(PYLONS-1)$ easy install pylons==0.9.7rc2
(PYLONS-1)$ easy install sqlalchemy==0.4.8
(PYLONS-1)$ easy install PasteDeploy

แล้วก็ไฟล์สำหรับเรียก Pylons จาก wsgi ประมาณนี้

APP_CONFIG = "/home/veer/Develop/pylons/ex1/production.ini"
import os
os.environ['PYTHON_EGG_CACHE'] = "/home/veer/egg"
import sys

# บอกให้ python ไปหา package ที่ที่เราเตรียมไว้
sys.path = []
sys.path.append("/home/veer/pyenv/BASELINE/lib/python2.5/site-packages")
sys.path.append("/home/veer/pyenv/PYLONS-1/lib/python2.5/site-packages")
sys.path.append("/home/veer/Develop/pylons/ex1")

from paste.deploy import loadapp
application = loadapp("config:" + APP_CONFIG)

อะไรที่เขียน /home/veer ต่างๆ นี้คือสิ่งที่ใช้ในเครื่องผม เครื่องอื่นๆ ก็คงใช้ต่างๆ กันไป

เท่านี้เว็บของแต่ละคนก็มี environment ของตัวเองแล้ว อย่างในกรณีนี้ package ต่างๆ ก็มาอยู่ใน $HOME ของแต่ละ user แทนที่จะไปอยู่รวมๆ กันใน /usr/lib ผู้ใช้แต่ละคนก็แก้ได้ สะดวกสบาย

อ้างอิง http://code.google.com/p/modwsgi/wiki/VirtualEnvironments

2

Pylons + mod_wsgi + mod_userdir

ต่อจาก ติดตั้ง Django ก็ต่อด้วย Pylons. ก่อนหน้าจะพยายามลง Pylons ผมก็ลองพวก WSGIDaemonProcess ไปแล้ว แต่ว่าไม่สำเร็จก็เลยข้ามๆ ไป. ใช้ user เป็น www-data ก็ได้ :-P.

Pylons ก็มา pattern เดิมๆ ที่อ่านมาจาก http://code.google.com/p/modwsgi/wiki/IntegrationWithPylons เริ่มด้วยสร้าง pylons.wsgi ใน /home/veer/public_html/wsgi-scripts ตามนี้

APP_CONFIG = "/home/veer/Develop/pylons/mydb4/production.ini"
import os
os.environ['PYTHON_EGG_CACHE'] = "/home/veer/egg"
import sys
sys.path.append("/home/veer/Develop/pylons/mydb4")
from paste.deploy import loadapp
application = loadapp("config:" + APP_CONFIG)

แล้วก็สร้าง /home/veer/egg เปลี่ยน permission ( mkdir /home/veer/egg; chmod 777 /home/veer/egg )

แล้วก็แก้สร้าง production.ini โดยการ copy มาจาก development.ini แล้วก็แก้ให้
full_stack = false
และ
debug = false

เท่านี้ก็น่าจะพอใช้ได้แล้ว

ลืมไปผมสร้าง app ไว้ใน /home/veer/Develop/mydb4 นะครับ พวก development.ini ก็อยู่ในนี้ล่ะ

1

Django + userdir + mod_wsgi

แน่นอนเป็นภาคต่อมาจาก Turbogears + userdir + mod_wsgi. Django ก็ใช้ท่าคล้ายๆ Turbogears เปลี่ยน prefix นิดๆ หน่อยก็ใช้ได้แล้ว

หลังจากศึกษาวิธีใช้จาก http://code.google.com/p/modwsgi/wiki/IntegrationWithDjango ผมสร้าง django.wsgi ไว้ใน /home/veer/public_html/wsgi-scrips อีกเช่นเคย

import os, sys
sys.path.append('/home/veer/Develop/django/mysite') # site ของผมเก็บไว้ที่นี้
sys.path.append('/home/veer/Develop/django')
os.environ['DJANGO_SETTINGS_MODULE'] = 'mysite.settings'

import django.core.handlers.wsgi

application = django.core.handlers.wsgi.WSGIHandler()

ที่ลำบากนิดหน่อยคือพวก media ทั้งหลาย ผมก็ copy มาไว้ใน home เลย (ขนาดประมาณ 300KiB กว่าๆ)

cp -r /usr/share/python-support/python-django/django/contrib/admin/media/ /home/veer/public_html/django_admin_media

แล้วก็ไปแก้ settings.py ใน /home/veer/Develop/django/mysite นิดหน่อย แก้ ADMIN_MEDIA_PREFIX จากเท่ากับ ” เป็น ADMIN_MEDIA_PREFIX = ‘/~veer/django_admin_media/’

เท่านี้ก็ไปเปิดเว็บดูก็ใช้ได้ (ถ้าผมไม่ได้ลืมเขียนอะไรไปนะ)

พรุ่งนี้น่าจะได้ลอง Pylons กับ WSGIDaemonProcess username user=username อะไรทำนองนี้.

2

mod_wsgi + userdir + Turbogears

อาจจะดูคลั่งๆ หน่อยแต่ mod_wsgi + userdir + Turbogears ก็ใช้ได้แล้ว. ด้วยพื้นฐานจากการใช้ mod_wsgi+userdir , การใช้ mod_wsgi กับ Turbogears และ การใช้ mod_wsgi กับ Turbogears แบบ non-root-mount. ก็แทบจะไม่ต้องทำอะไรแล้ว :-P.

แค่ copy ไฟล์ tg.wsgi ที่เคยอยู่ใน /home/veer/tg/tgapp/apache ไปไว้ใน /home/veer/public_html/wsgi-scripts/ แล้วก็แก้ prod.cfg ใน /home/veer/tg/tgapp นิดหน่อยที่ server.webpath ให้เปลี่ยนเป็น server.webpath=”/~veer/wsgi-scripts/tg.wsgi” เท่านี้ก็ใช้ซะงั้น.

สำหรับ Turbogears แล้วสำคัญมาที่ต้องใช้ tg.url เวลาเขียน URL ไม่ใส่ URL เอาเองตรงๆ (แบบที่ผมเคยทำ) … ไม่งั้นเวลามา deploy แบบนี้ก็แก้กันอ้วก :-P.

0

ทำให้ Turbogears + mod_wsgi ใช้งานกับ non-root mounted app

วิธีทำจริงๆ ก็อ่านจาก http://www.lucasmanual.com/mywiki/TurboGears#head-57ee578707aa057229eb1171a8e4aeb553bd6d3a มาเกือบหมด. แต่ว่าเขียนเสียหน่อยเพื่อความต่อเนื่องจากที่เขียนไว้เดิม. เริ่มจากแก้ /home/veer/tg/tgapp/apache/tg.wsgi:

import sys
sys.path.append('/home/veer/tg/tgapp')
sys.stdout = sys.stderr

import os
os.environ['PYTHON_EGG_CACHE'] = '/home/veer/tg/python-eggs'

import atexit
import cherrypy
import cherrypy._cpwsgi
import turbogears

# ข้างล่างเปลี่ยนไปใช้ fullpath แทนที่จะเขียน prod.cfg หรือ dev.cfg เฉยๆ
turbogears.update_config(configfile="/home/veer/tg/tgapp/prod.cfg", modulename="tgapp.config")
turbogears.config.update({'global': {'server.environment': 'production'}})
turbogears.config.update({'global': {'autoreload.on': False}})
turbogears.config.update({'global': {'server.log_to_screen': False}})

import tgapp.controllers

cherrypy.root = tgapp.controllers.Root()

if cherrypy.server.state == 0:
    atexit.register(cherrypy.server.stop)
    cherrypy.server.start(init_only=True, server_class=None)

application = cherrypy._cpwsgi.wsgiApp

จริงๆ แล้วก็แก้บรรทัดเดียว :-P … แต่ตอนลองทำงงเหมือนกัน

แล้วก็ไปสร้าง server.log ใน /home/veer/tg/tgapp/server.log อันนี้มีก็ทำแบบชั่วคราวๆ :-P (อีกแล้ว)

(อยู่ใน /home/veer/tg/tgapp)
touch server.log
chmod 777 server.log

ตามด้วยแก้ /home/veer/tg/tgapp/prod.cfg ทีแรกมันไม่มีให้แก้ก็เลยต้อง copy sample-prod.cfg มา

เพิ่ม server.webpath=”/first” เข้าไป (จริงมี # server.webpath=”” อยู่แล้ว ก็แค่เอา shape ออกแล้วใส่ /first ลงไป) แก้ path ของ server.log แบบนี้ args=”(‘/home/veer/tg/tgapp/server.log’,)” จากที่เดิมเขียนไว้ว่า args=”(‘server.log’,)”

ตามด้วยแก้ /etc/apache2/mod-enabled/mod-wsgi.conf เดิมเขียนไว้ว่า
WSGIScriptAlias / /home/veer/tg/tgapp/apache/tg.wsgi
ก็แก้เป็น
WSGIScriptAlias /first /home/veer/tg/tgapp/apache/tg.wsgi
เพื่อเปลี่ยนที่ mount

แล้วก็ restart apache ( /etc/init.d/apache restart ) จากนั้นพอเข้าไปดูใน localhost/first ก็ได้ผลตามต้องการ

… ต่อไปก็เป็น userdir T_T (เหมือนหา doc ไม่ค่อยเจอด้วย :-P)

4

mod_wsgi + turbogears (แบบง่ายๆ ก่อน)

วิธีลงอ่านมาจาก http://code.google.com/p/modwsgi/wiki/IntegrationWithTurboGears. ที่เขียนนี้ผมยังไม่ได้ลองใช้ userdir เลย คิดว่าน่าจะซับซ้อนขึ้นมาก. จริงๆ แล้วแค่เปลี่ยน url จาก localhost ไปเป็น localhost/tg ก็เจ๊งแล้ว :-P.

เริ่มจากแก้ไฟล์ /etc/apache2/mod_enabled/mod-wsgi.conf:

<IfModule mod_wsgi.c>
    WSGIScriptAlias / /home/veer/tg/tgapp/apache/tg.wsgi

    <Directory /home/veer/tg/tgapp/apache>
        Order deny,allow
        Allow from all
    </Directory>
</IfModule>

เสร็จแล้วก็สร้าง folder ชื่อ /home/vee/tg (mkdir /home/veer/tg) แล้วก็สร้าง python-eggs ใน /home/veer/tg ไว้เป็น cache แล้วผมก็เปลี่ยน permission ของ python-eggs เป็น 777 เลย เพราะว่า user id ของ folder กับ apache ต่างกัน … จริงๆน่าจะมีวิธีแก้ที่ดีกว่านี้ แต่ผมคิดว่าเอาง่ายให้ได้ก่อนดีกว่า

จากนั้นใน /home/veer/tg ก็เรียก tg-admin quickstart tgapp ตอบๆ อะไรไปหน่อยก็จะได้ folder ชื่อ /home/veer/tg/tgapp ขึ้นมา ในนั้นก็สร้าง /home/veer/tg/tgppp/apache/tg.wsgi ขึ้นมา ข้างในก็ลองมาตามตัวอย่างเลย แต่ก็แอบแก้ path กับชื่อ project นิดหน่อย

import sys
sys.path.append('/home/veer/tg/tgapp')
sys.stdout = sys.stderr

import os
os.environ['PYTHON_EGG_CACHE'] = '/home/veer/tg/python-eggs'

import atexit
import cherrypy
import cherrypy._cpwsgi
import turbogears

turbogears.update_config(configfile="dev.cfg", modulename="tgapp.config")
turbogears.config.update({'global': {'server.environment': 'production'}})
turbogears.config.update({'global': {'autoreload.on': False}})
turbogears.config.update({'global': {'server.log_to_screen': False}})

import tgapp.controllers

cherrypy.root = tgapp.controllers.Root()

if cherrypy.server.state == 0:
    atexit.register(cherrypy.server.stop)
    cherrypy.server.start(init_only=True, server_class=None)

application = cherrypy._cpwsgi.wsgiApp

เสร็จแล้วก็ restart apache ( sudo /etc/init.d/apache2 restart ) แล้วก็เปิดเว็บดูที่ localhost ก็ดูได้ตามรูป

แต่ว่าก็ยังเหลืออะไรที่ต้องแก้อีกมากมาย พื้นก็เปลี่ยน url จาก localhost ไปเป็น localhost/tgapp ผมก็ยังทำไม่ได้เลย :-P

3

mod_wsgi + userdir บน ubuntu 8.04

ผมอ่านเรื่องเปิดเทียบ web app tech ต่างๆ มา. เห้น mod_wsgi น่าสนใจดีก็เลยจะลองเล่นบ้าง (เอาแบบให้ใช้ได้หลายๆ user ด้วย น่าจะเหมาะกับ lab ดี) ก็เริ่มจากไปอ่าน http://code.google.com/p/modwsgi/wiki/ConfigurationGuidelines

แต่ก่อนจะทำตามตำรา ผมก็ลง mod_wsgi ก่อนเลย

 sudo aptitude install libapache2-mod-wsgi

ตามด้วย enable มันซะ

sudo a2enmod mod-wsgi

แปลกนะมีคำว่า mod ติดมาด้วย หาตั้งนาน :-P ปกติเวลาจะใช้ mod_userdir ก็แค่

sudo a2enmod userdir

(ไม่ใช่เหรอ? …. รวมถึง mod_rewrite และอื่นๆ ด้วย)

ด้วยความพิเศษของ Ubuntu/Debian นิดหน่อยๆ ไฟล์ที่ผมเข้าไปแก้คือ /etc/apache2/mods-enabled/userdir.conf ก็ดัดแปลงตามตัวอย่างอะนะแก้แล้วเป็นแบบนี้

<IfModule mod_userdir.c>
        UserDir public_html
        UserDir disabled root

        <Directory /home/*/public_html/>
                AllowOverride FileInfo AuthConfig Limit
                Options MultiViews Indexes SymLinksIfOwnerMatch IncludesNoExec
        </Directory>

        RewriteEngine On
        RewriteCond %{REQUEST_URI} ^/~([^/]+)
        RewriteRule . - [E=APPLICATION_GROUP:~%1]

        <Directory /home/*/public_html/wsgi-scripts/>
                Options ExecCGI
                SetHandler wsgi-script
                WSGIApplicationGroup %{ENV:APPLICATION_GROUP}
        </Directory>
</IfModule>

เสร็จแล้วผมก็เข้าไปสร้าง folder ที่ /home/veer/public_html/wsgi-scripts พร้องเปลี่ยน permission เป็น 755 ตามด้วยเอา myapp.wsgi ตามตัวอย่างไปวางในนั้น

def application(environ, start_response):
    status = '200 OK'
    output = 'Hello World!'
    response_headers = [('Content-type', 'text/plain'),
                        ('Content-Length', str(len(output)))]
    start_response(status, response_headers)
    return [output]

จากนั้นก็ไปที่ web browser เรียก http://127.0.0.1/~vee/wsgi-scripts/myapp.wsgi (ตามรูป)

ขั้นตอนถัดไปก็จะลองลง Pylons และ Django ดู