-
Notifications
You must be signed in to change notification settings - Fork 93
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
263 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,222 @@ | ||
先說好,雖然 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/)。 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
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' |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
[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.production_ubuntu |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
from .base import * # noqa | ||
|
||
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') |