在 OpenShift 上部署 Django Todolist 应用


一、初始化 Openshift Django 应用

首先去 Openshift 上创建一个 Django 应用, 关于怎么创建 Openshift 应用参考我另一篇笔记

我这里创建了一个 todolist-smallcpp.rhcloud.com, 然后, 利用另一篇笔记的知识用 SecureCRT 连上我们的服务器.

补充一下用 putty.exe 连接的方法.

首先, 和另一篇笔记中一样, 通过 Git 简明手册 拿到了两个密钥文件: id_rsa(私钥) 和 id_rsa.pub(公钥), 将 id_rsa.pub(公钥) 部署到刚创建的 Django 应用上.

putty.exe 不能识别直接从服务器拷贝来的私钥, 需要使用 puttygen.exe 进行格式转换.

  • 开 puttygen.exe --> Conversions --> Import Key
  • 选择私钥文件 id_rsa
  • Save private key -> id_rsa.ppk (保存私钥)

最后, 打开 putty.exe

  • Session --> Host Name (填写服务器地址或者域名)
  • Connection --> SSH --> Auth (点 Browse 选择刚生成的 id_rsa.ppk)
  • Connection --> Data (填写 Auto-login username)
  • open

成功连接后出现下面这样的提示:

login as: your login user name
Authenticating with public key "imported-openssh-key"
...
[todolist-smallcpp.rhcloud.com your login user name]\>

二、配置默认的 Django

用 Openshift 创建的 Django 应用默认有一个 Hello World (你懂的…), 但此时我们直接访问 https://todolist-smallcpp.rhcloud.com 是会报错的…需要配置下.

我们先用 git 把 Openshift 上的 Django 项目 pull 回本地 (git 地址在创建应用时网站上会提示), 该项目目录如下:

里面有个 Readme.md, 阅读后发现下面这些信息:

  • Django project name 是 myproject (在 wsgi 目录下)
  • 数据库保存在 $OPENSHIFT_DATA_DIR/db.sqlite3 ($OPENSHIFT_DATA_DIR 是一个 Link, 指向 app-root/data)
  • 初次使用, 必须创建/修改 admin 的密码 + python $OPENSHIFT_REPO_DIR/wsgi/myproject/manage.py createsuperuser ($OPENSHIFT_REPO_DIR 是一个 Link, 指向 app-root/runtime/repo) + 登录地址: https://todolist-smallcpp.rhcloud.com/admin (已被修改为 https://todolist-smallcpp.rhcloud.com/todos)
  • 要注意使用 python $OPENSHIFT_REPO_DIR/wsgi/myproject/manage.py syncdb 同步数据库 + 由于 syncdb 的尿性, 如果修改了数据库结构, 需要先执行 rm -f $OPENSHIFT_DATA_DIR/db.sqlite3 + 关于 syncdb 的说明参考我另一篇笔记 44. Django migration vs syncdb

三、安装 todolist 项目

todolist 我弄了个开源的源码, clone 到本地, 将目录更名为 todolist 并放到步骤二 pull 回本地的 Django 项目根目录的 wsgi 目录下, 并删除自带的 myproject 目录 (注意, 备份下 myproject 里的 settings.py, 后面有用).

然后有几个关键地方要配置:

首先是 setup.py 文件, 其中有一项 install_requires 很重要, 它表示项目中用到的第三方 python 库, 填在里面的项 push 到 openshift 后, openshift 会自动调用 easy_install 进行安装, 例如:

install_requires = ['Django<=1.8', 'django-markdown', 'django-markdown-deux==1.0.5', 'markdown2==2.3.0', 'markdown']

然后打开 \todolist\.openshift\action_hooks 目录下的 deploy 文件, 这里面保存的是当 push 到 openshift 后执行的动作, 我们把里面的 myproject 改为 todolist.

#!/bin/bash
# This deploy hook gets executed after dependencies are resolved and the
# build hook has been run but before the application has been started back
# up again.  This script gets executed directly, so it could be python, php,
# ruby, etc.

if [ ! -f "$OPENSHIFT_DATA_DIR"secrets.json ]; then
    echo "Generating $OPENSHIFT_DATA_DIR/secrets.json"
    python "$OPENSHIFT_REPO_DIR"libs/secrets.py > "$OPENSHIFT_DATA_DIR"secrets.json
fi

# GETTING-STARTED: change 'todolist' to your project name:
echo "Executing 'python $OPENSHIFT_REPO_DIR/wsgi/todolist/manage.py migrate --noinput'"
# GETTING-STARTED: change 'todolist' to your project name:
python "$OPENSHIFT_REPO_DIR"wsgi/todolist/manage.py migrate --noinput

# GETTING-STARTED: change 'todolist' to your project name:
echo "Executing 'python $OPENSHIFT_REPO_DIR/wsgi/todolist/manage.py collectstatic --noinput'"
# GETTING-STARTED: change 'todolist' to your project name:
python "$OPENSHIFT_REPO_DIR"wsgi/todolist/manage.py collectstatic --noinput

再进入 wsgi 文件夹, 里面有个 application 的文件

#!/usr/bin/env python
import os
import sys
## GETTING-STARTED: make sure the next line points to your settings.py:
os.environ['DJANGO_SETTINGS_MODULE'] = 'openshift.settings'
## GETTING-STARTED: make sure the next line points to your django project dir:
sys.path.append(os.path.join(os.environ['OPENSHIFT_REPO_DIR'], 'wsgi', 'openshift'))
virtenv = os.environ['APPDIR'] + '/virtenv/'
## GETTING-STARTED: make sure the next line has the right python version:
os.environ['PYTHON_EGG_CACHE'] = os.path.join(virtenv, 'lib/python2.7/site-packages')
virtualenv = os.path.join(virtenv, 'bin/activate_this.py')
try:
    execfile(virtualenv, dict(__file__=virtualenv))
except:
    pass
import django.core.wsgi
application = django.core.wsgi.get_wsgi_application()

注意这两行:

os.environ['DJANGO_SETTINGS_MODULE'] = 'myproject.settings'
sys.path.append(os.path.join(os.environ['OPENSHIFT_REPO_DIR'], 'wsgi', 'myproject'))

改为:

os.environ['DJANGO_SETTINGS_MODULE'] = 'simple_todo.settings'
sys.path.append(os.path.join(os.environ['OPENSHIFT_REPO_DIR'], 'wsgi', 'todolist'))

注意, 别改错了, 第一个是 simple_todo.settings, 别改成 todolist.settings 了.

最后, 进入 \todolist\wsgi\todolist\simple_todo 目录, 用之前从 myproject 里备份的 settings.py 替换掉 simple_todo 里的 settings.py.

但要做下修改, 把 settings.py 里的 myproject 替换成 simple_todo, 然后在 INSTALLED_APPS 里添加一项 todo, 再把 Debug 属性改为 False (如果部署到 openshift 后发现有问题, 可以改为 True 后进行调试, 调试完毕再改为 False), 改完后的 settings.py 如下:

"""
Django settings for simple_todo project.

For more information on this file, see
https://docs.djangoproject.com/en/1.8/topics/settings/

For the full list of settings and their values, see
https://docs.djangoproject.com/en/1.8/ref/settings/
"""

# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
import os
DJ_PROJECT_DIR = os.path.dirname(__file__)
BASE_DIR = os.path.dirname(DJ_PROJECT_DIR)
WSGI_DIR = os.path.dirname(BASE_DIR)
REPO_DIR = os.path.dirname(WSGI_DIR)
DATA_DIR = os.environ.get('OPENSHIFT_DATA_DIR', BASE_DIR)

import sys
sys.path.append(os.path.join(REPO_DIR, 'libs'))
import secrets
SECRETS = secrets.getter(os.path.join(DATA_DIR, 'secrets.json'))

# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/1.8/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = SECRETS['secret_key']

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = False
TEMPLATE_DEBUG = DEBUG

from socket import gethostname
ALLOWED_HOSTS = [
    gethostname(), # For internal OpenShift load balancer security purposes.
    os.environ.get('OPENSHIFT_APP_DNS'), # Dynamically map to the OpenShift gear name.
    #'example.com', # First DNS alias (set up in the app)
    #'www.example.com', # Second DNS alias (set up in the app)
]

# Application definition

INSTALLED_APPS = (
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'todo',
)

MIDDLEWARE_CLASSES = (
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
)

# GETTING-STARTED: change 'simple_todo' to your project name:
ROOT_URLCONF = 'simple_todo.urls'

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

WSGI_APPLICATION = 'simple_todo.wsgi.application'

# Database
# https://docs.djangoproject.com/en/1.8/ref/settings/#databases

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        # GETTING-STARTED: change 'db.sqlite3' to your sqlite3 database:
        'NAME': os.path.join(DATA_DIR, 'db.sqlite3'),
    }
}

# Internationalization
# https://docs.djangoproject.com/en/1.8/topics/i18n/

LANGUAGE_CODE = 'en-us'

TIME_ZONE = 'UTC'

USE_I18N = True

USE_L10N = True

USE_TZ = True

# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.8/howto/static-files/

STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(WSGI_DIR, 'static')

静态文件的处理

比如 CSS、图片等等, 一般 Django 应用的静态文件的目录是在 settings.py 中配置的, 但是 OpenShift 使用了不一样的方式, 所有的静态文件都将被放置在 wsgi/static 目录下.

貌似在 openshift 中, css 文件中的 url 属性不生效, 我现在把 css 中的 url 属性都移到 html 标签中嵌套了…

四、DIY todolist

上面的开源项目基于实现了 todolist 的功能, 不好的是暂时缺少用户管理系统 (所有人访问都是同一个 todolist), 所以准备 DIY 一下, 加一个用户系统.

五、最后

重要的事再说一次:

由于 syncdb 的尿性, 如果修改了数据库结构, 需要先执行 rm -f $OPENSHIFT\_DATA\_DIR/db.sqlite3, 再执行次 python $OPENSHIFT_REPO_DIR/wsgi/myproject/manage.py syncdb.

修改后的项目, 我放在 git@oschina 上了, 可以参考参考…