iT邦幫忙

DAY 25
0

為程式人寫的 Django Tutorial系列 第 25

Django Tutorial for Programmers: 25. Deploy to Ubuntu Server

內容diff

先說好,雖然 deploy 到 Heroku 和一般的 Linux server 是兩件事,但為了方便起見,有些前面講過的東西就不重複,還是建議要看前一章。我們這裡會用 Ubuntu Server 14.04 來示範,如果你習慣使用其他發行版就麻煩自己舉一反三一下。其他版本中最麻煩的可能是要自己想辦法安裝 Python 3.4(預設可能都只有 2.7),這關過了後面應該都還算差不多。

由於這裡可以玩的花樣很多,本章會用 nginx + Gunicorn + Supervisor + PostgreSQL 示範。上面的所有元件都可以替換,例如

* nginx 可以換成 Apache。
* Gunicorn 可以換成 Waitress。
* uWSGI 可以取代 Gunicorn 與 Supervisor。
* 如果你用 Apache,可以用 mod_wsgi 取代 Gunicorn 與 Supervisor。
* PostgreSQL 可以換成 MySQL 或 MariaDB 甚至 Oracle Database。

不過實在是沒辦法全部講完,所以只好先麻煩舉一反三了。或許之後有機會吧。

首先我們建立一個設定檔:

```python
# lunch/settings/production_ubuntu.py

from .base import *

DEBUG = False

TEMPLATE_DEBUG = False

SECRET_KEY = get_env_var('DJANGO_LUNCH_SECRET_KEY')

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': 'lunch',
        'USER': get_env_var('DJANGO_LUNCH_DATABASE_DEFAULT_USER'),
        'PASSWORD': get_env_var('DJANGO_LUNCH_DATABASE_DEFAULT_PASSWORD'),
        'HOST': 'localhost',
        'PORT': '',
    },
}

ALLOWED_HOSTS = ['*']

STATIC_ROOT = os.path.join(os.path.dirname(BASE_DIR), 'static')

MEDIA_ROOT = os.path.join(os.path.dirname(BASE_DIR), 'media')
```

這裡列出了 PostgreSQL 的所有設定。其中 `NAME` 是資料庫名稱,你可以自己換,`USER` 與 `PASSWORD` 顯然必須有讀寫該資料庫的權限,請自行設定。[註 1] `PORT` 留白代表使用預設 port,如果你有其他需求也可以自行修改。注意有些值我們放在環境變數中,而不直接寫在專案裡,以增加安全性。

`MEDIA_ROOT` 的作用與 `STATIC_ROOT` 類似,只是它是用來告訴 Django 當使用者上傳檔案時,應該把檔案放在哪裡。這裡我們單純就只是把它放在外面一層的 `media` 目錄裡,但你可以設定 NFS 或者各式各樣的服務來用,只要 Django 找得到就行了。

接著我們要安裝一些系統元件:

```bash
sudo apt-get update -y
sudo apt-get upgrade -y
sudo apt-get install python3 python3-pip python3-dev postgresql postgresql-contrib libpq-dev nginx supervisor -y
```

其中前兩個是必須,接下來三個是 PostgreSQL 相關,最後兩個則是字面上的意義。可以依照你的需求增減要安裝的東西。我們會用 PIP 安裝 Gunicorn,所以這裡沒有包含。[註 1]

接著用下面的指令修正 Ubuntu 的 Python 沒有包含 `ensurepip` 模組的問題:

```bash
wget -qO - http://d.pr/f/mbQy+ | sudo python3
```

然後就可以開始部署專案。首先進入你想放專案的地方(以下稱 `/project`),建立 venv:

```bash
python3 -m venv venv/lunch
```

然後把你的專案弄上去。隨便你要用什麼方法都行,當然 Git 或許是最方便的。假設你弄上去後,現在 `/project` 的結構應該會和本機端一樣。

把專案有用到的套件裝上去:

```bash
. venv/lunch/bin/activate
pip install -r lunch/requirements.txt
```

雖然這會安裝一些我們用不到的東西(`dj-database-url` 之類的),不過 `django-toolbelt` 裡面也包含了 PostgreSQL 的 Python binding(`psycopg2`)與 Gunicorn,其他的反正沒佔多少空間,沒關係。

先把一些環境變數設起來:

```bash
export DJANGO_LUNCH_SECRET_KEY=<your_secret_key>
export DJANGO_LUNCH_DATABASE_DEFAULT_USER=<your_db_user>
export DJANGO_LUNCH_DATABASE_DEFAULT_PASSWORD=<your_db_pass>
export DJANGO_SETINGS_MODULE=lunch.settings.deploy_ubuntu
```

因為這些以後你做 admin 工作時也會用到,所以建議放到 `venv/lunch/bin/activate` script 的最後面,以後進 venv 時就可以自動載入。

還是得記得 migrate 資料庫:

```bash
python manage.py migrate
```

接著我們要把 static 收集起來,才能讓 static file server 找到它們。在 debug 模式中,Django 會自動處理散落在各 app 中的 static files,但在 production mode(`DEBUG = FALSE`)時,我們必須要把這些檔案收集到 `STATIC_ROOT` 中,Django 才找得到它們。

```bash
python manage.py collectstatic
```

確認一下收集的目標是否正確(應該會是 `/project/static`),如果沒問題就按 y 開始收集吧!應該會有一堆,因為 Django 內建就有許多靜態檔(主要是 admin 會用到)。

設定完成!先把 dev server 跑起來,看到目前為止的設定對不對。

```bash
python manage.py runserver 0.0.0.0:8000
```

如果都正確,你應該可以用 <http://server-ip:8000/> 看到首頁。

接著是 Gunicorn。在 `/project` 建立 `gunicorn.conf.py`:

```python
import os

bind = '127.0.0.1:8080'
worders = (os.sysconf('SC_NPROCESSORS_ONLN') * 2) + 1
loglevel = 'error'
command = '/project/venv/lunch/bin/gunicorn'
pythonpath = '/project/lunch'
```

這會讓 Gunicorn 使用你的 CPU 數乘二加一個 processes,然後把網頁 serve 在 `127.0.0.1:8080`,並把錯誤記錄下來。具體需要幾個 processes 效能比較好要視機器而定,所以你可能必須自己試試看其他組合,不過這個設定在多數情況都還 OK。

接著是 nginx。在 `/etc/nginx/sites-available` 裡建立一個設定檔:

```
upstream lunch {
    server 127.0.0.1:8080;
}

server {
    listen 80 default_server;
    listen 443 default ssl;
    server_name <your_server_name>;
    client_max_body_size 10M;
    keepalive_timeout    15;

    location /static/ {
        alias           /project/static/;
    }

    location /media/ {
        alias           /project/media/;
    }

    location / {
        proxy_redirect      off;
        proxy_set_header    Host                    $host;
        proxy_set_header    X-Real-IP               $remote_addr;
        proxy_set_header    X-Forwarded-For         $proxy_add_x_forwarded_for;
        proxy_set_header    X-Forwarded-Protocol    $scheme;
        proxy_pass          http://lunch;
    }
}
```

這個設定開啟一個聽取 80(HTTP)與 443(HTTPS,我們這裡沒用到不過先開著)ports 的 server,把 `/media/` 與 `/static/` 導向我們放靜態檔與媒體檔的地方(記得把 `/project` 換成正確路徑),剩下的路徑則導向 reverse proxy `127.0.0.1:8080`,也就是上面 Gunicorn 的位置。

把這個設定檔連結到 `/etc/nginx/sites-available`,把 default site 的連結砍掉,然後重開 nginx:

```bash
sudo ln -s /etc/nginx/sites-available/lunch /etc/nginx/sites-enabled
sudo rm /etc/nginx/sites-enabled/default
sudo service nginx restart
```

如果一切正確,你現在應該可以在 `/project` 把 Gunicorn 跑起來:

```bash
gunicorn -c gunicorn.conf.py lunch.wsgi
```

然後用 server IP 看到網站了!

但是這樣我們要手動把 Gunicorn 跑起來才看得到網站,如果用 Ctrl-C 關掉,網站就掛了。最後一個步驟:設定 Supervisor。

Supervisor 是一個

建立 `/etc/supervisor/conf.d/lunch.conf`:

```
[group:lunch]
programs=site

[program:site]
directory=/project
command=/project/venv/bin/gunicorn -c /project/gunicorn.conf.py -p gunicorn.pod lunch.wsgi
autostart=true
autorestart=true
stdout_logfile=/project/supervisor.log
environment=DJANGO_LUNCH_SECRET_KEY=<your_secret_key>,DJANGO_LUNCH_DATABASE_DEFAULT_USER=<your_db_user>,DJANGO_LUNCH_DATABASE_DEFAULT_PASSWORD=<your_db_pass>,DJANGO_SETINGS_MODULE=lunch.settings.deploy_ubuntu
```

這個設定檔定義了一個群組,裡面有一個程式 `site`。這個程式會自動在 Supervisor 啟動時自動開始執行,切換掉 `/poject` 目錄執行適當的 Gunicorn 指令。`environment` 設定指名了需要的環境變數。

現在我們讓 Supervisor 讀進這個設定:

```bash
sudo supervisorctl reread
```

這應該會顯示有一個新設定 `lunch`。重新啟動 Supervisor:

```bash
sudo supervisorctl reload
```

看看它有沒有跟著起來:

```bash
sudo supervisorctl status
```

如果你有看到 `lunch:site` 處於 `RUNNING`,恭喜你!現在你應該可以正常使用網站了。部署成功!

---

註 1:如果你不知道怎麼做,可以參考[這篇](http://www.cyberciti.biz/faq/howto-add-postgresql-user-account/)。

上一篇
Django Tutorial for Programmers: 24. Deploy to PaaS (以 Heroku 為例)
下一篇
Django Tutorial for Programmers: 26. REST API
系列文
為程式人寫的 Django Tutorial30

尚未有邦友留言

立即登入留言