Django -- 從平凡到超凡

第11章    部署專案

(1) 雲端服務

∗ 雲端服務的基本型態:

▸ 設備即服務 (Infrastructure as a Service, IaaS)

✶ 提供機器、儲存空間、與網路資源
✶ 使用者需自行安裝作業系統、資料庫管理系統、網路伺服器、及其他應用程式
✶ 範例:Amazon EC2, Google Compute Engine, Rackspace, GoGrid, Microsoft, HP, AT&T, OpSource, ...

▸ 平台即服務 (Platform as a Service, PaaS)

✶ 提供使用者可以開發 Web 系統的平台
✶ 包括硬體(伺服器與硬碟)、作業系統、開發工具、及管理工具
✶ 使用者僅需專注自己所開發的應用程式,其餘有關伺服器、作業系統、資料庫、 或網路等基礎設施問題均交由雲端供應商負責
✶ 範例:Heroku, Google App Engine, Windows Azure, Force.com, Cloud Foundry, PythonAnyWhere, ...

▸ 軟體即服務 (Software as a Service, SaaS)

✶ 一個完整的軟體應用程式,使用者透過瀏覽器即可使用應用程式
✶ 優點:
# 簡單:只需瀏覽器,無需安裝其他軟體
# 成本低:無需購置額外設備
# 可伸縮:用多少付多少
✶ 範例:Salesforce.com, Google Suite, TurboTax, Zoho, QuickBooks, Clarizen, ...

▸ 儲存即服務 (Storage as a Service, StaaS)

✶ 一般型:提供資料儲存服務,使用者可上載、下載、備份,有些提供線上編輯
# 例如:Google drive, Dropbox, Box.net, SugarSync, Mozy, Zmanda, ...
✶ 程式型:提供資料儲存服務,使用者可透過應用程式存取資料
# 例如:Amazon Simple Storage Service (S3), Google cloud storage (GS)

∗ Heroku 的 PaaS 服務

Heroku 於2007 年 創立並提供 PaaS 服務, 於 2010 年被 Salesforce 併購

▸ 架構在 Amazon EC2 的 IaaS 服務上

▸ 提供各種應用程式架構:Python, Node.js, Ruby, Java, PHP, Go, Scala and Play, Clojure

▸ 提供各種資料庫:Postgres, SQLite3, MySQL (ClearDB)

▸ 可直接利用 Github 將專案部署到 Heroku:按下按鍵就部署完畢

(2) Heroku 的相關設定

∗ 在虛擬環境安裝 django-toolbelt

(blogVenv)$ pip install django-toolbelt

∗ 在專案根目錄建立以下三個檔案

1. Procfile:指定 Web 伺服器為 gunicorn , 並使用 blog.wsgi 模組為 Python 與伺服器的溝通介面

web: gunicorn blog.wsgi --log-file -

2. requirements.txt:指定需安裝的套件清單 (內容來自於 (blogVenv)$ pip freeze 指令)

appdirs==1.4.3
dj-database-url==0.4.2
dj-static==0.0.6
Django==1.11
django-toolbelt==0.0.1
gunicorn==19.7.1
packaging==16.8
psycopg2==2.7.1
pyparsing==2.2.0
pytz==2017.2
six==1.10.0
✶ 註:如果永遠指定最新版本,requirements.txt 的內容也可以如下, 但要注意新版本的套件可能與舊程式不相容
django
psycopg2
django-toolbelt
requirements.txt 裡一定要有 django 項目,如此 Heroku 才能確定是 Django 專案

3. runtime.txt:指定應用程式的執行環境

python-3.5.2
✶ 檢查專案所使用的 Python 版本:(blogVenv)$ python -V
✶ 檢查 Heroku 支援的 Python 版本:https://devcenter.heroku.com/articles/python-runtimes

▸ 專案根目錄內容如下:

blog/
   article/ 
   blog/
   main/
   populate/  
   manage.py
   Procfile
   requirements.txt
   runtime.txt

(3) 設定 settings.py 與 wsgi.py

▸ 修改 blog/settings.py

...

if 'DYNO' in os.environ:    # Running on Heroku
    DEBUG = False
else:
    DEBUG = True
...

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

if DEBUG:   # Running on the development environment
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.postgresql_psycopg2',
            'NAME': 'blogDB',
            'USER': 'blog',
            'PASSWORD': 'blog',
            'HOST': 'localhost',
            'PORT': '',     # Set to empty string for default.
        }
    }
else:   # Running on Heroku
    # Parse database configuration from $DATABASE_URL
    import dj_database_url
    DATABASES = {'default':dj_database_url.config()}
    # Honor the 'X-Forwarded-Proto' header for request.is_secure()
    SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')

...
STATIC_URL = '/static/'

# For Heroku deployment
STATIC_ROOT = 'staticfiles'
if 'DYNO' ...DYNO 是 Heroku 的特有環境變數,藉此判斷專案是在 Heroku 上執行或是在本機端執行,若是在 Heroku 環境則將 DEBUG 設為 False,否則設為 True
# DEBUG = False:任何錯誤訊息均不會在使用者瀏覽器顯示,以確保系統安全
if DEBUG: ... else: ...:如果在 Heroku 上執行,則改用 Heroku 的資料庫
✶ 最後設定 STATIC_ROOT = 'staticfiles' ,讓 Heroku 在部署時可順利執行 collectstatic 指令,將所有靜態檔案複製到專案下的 staticfiles 目錄

▸ 修改 blog/wsgi.py

...
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "blog.settings")


if 'DYNO' in os.environ:    # Running on Heroku
    from dj_static import Cling
    application = Cling(get_wsgi_application())
else:
    application = get_wsgi_application()
✶ WSGI (Web Server Gateway Interface):網站伺服器閘道介面,是 Python 網頁程式和伺服器溝通的介面
✶ 如果是在 Heroku 執行,則將 Heroku 的反向代理伺服器 Cling 架在前面,負責處理所有請求:如果是靜態檔案請求, 就直接從 staticfiles 目錄裡送出,如果是動態網頁請求,就轉給應用程式伺服器 Gunicorn 處理

▸ 在本機安裝 Heroku toolbelt,之後就可執行 Heroku 所提供的 CLI 指令

$ wget -qO- https://toolbelt.heroku.com/install-ubuntu.sh | sh

(4) 將資料庫遷移檔案納入版本控制

▸ 刪除 .gitignore 裡的 00*.py 項目:部署時, 資料庫遷移檔案必須上載至 Heroku ,如此才能在 Heroku 建立或修改資料庫

.gitignore
*~
__pycahe__
*.pyc
00*.py

▸ 上推至 Github (包含上述新創的 3 個檔案,以及資料庫遷移檔案)

(5) 部署至 Heroku

∗ 註冊 Heroku 帳號:

▸ 至 Heroku 網站註冊 --> Pick your development language: Python --> 驗證電子信箱

∗ 建立 Heroku 專案:

▸ 登入 Heroku 並進入 Dashboard 網頁

主控台

▸ 建立新 App

✶ 點右上方「New」按鈕 --> Create new app --> App name: <appName>, Region: United States (or Europe) --> Create App
✶ 註:Heroku 的 app name space 是所有使用者共用,因此不可重複,blog 名稱可能早已被註冊,因此需改用其他名稱

∗ 設定專案連結到 Github 帳號並部署

▸ 點 App

部署

▸ 點「Deploy」 --> Deployment method: Github, Connect to Github --> Connect to Github --> 登入 Github --> Search for a repository to connect to: blog --> Search --> Connect --> Deploy Branch

-----> Python app detected
-----> Installing python-x.x.x
       $ pip install -r requirements.txt
       Collecting Django==x.x.x (from -r requirements.txt (line 1))
         Downloading Django-x.x.x-py2.py3-none-any.whl (x.xMB)
         ...
       Successfully installed Django-x.x.x ...
       $ python manage.py collectstatic --noinput
       xx static files copied to '/app/staticfiles'.
-----> Discovering process types
       Procfile declares types -> web
-----> Compressing...
       Done: xxM
-----> Launching...
       Released 
       https://<appName>.herokuapp.com/ deployed to Heroku
✶ 以上訊息說明 Heroku 在部署時會執行 collectstatic, 將所有靜態檔案全部複製一份放在專案目錄下的 staticfiles 子目錄, 可執行 Heroku bash 連線至 Heroku 觀察部署的結果
$ heroku login
Email: xxx@xxx.xxx
Password (typing will be hidden):
$ heroku run bash --app <appName>
Running bash on ⬢ <appName>... up, run.3349 (Free)
$ ls -l
drwx------ 6 u13281 dyno 4096 Oct 23 14:47 article
drwx------ 3 u13281 dyno 4096 Oct 23 14:47 blog
drwx------ 6 u13281 dyno 4096 Oct 23 14:47 main
-rwx------ 1 u13281 dyno  802 Oct 23 14:43 manage.py
drwx------ 2 u13281 dyno 4096 Oct 23 14:43 populate
-rw------- 1 u13281 dyno   36 Oct 23 14:43 Procfile
-rw------- 1 u13281 dyno  125 Oct 23 14:43 requirements.txt
-rw------- 1 u13281 dyno   12 Oct 23 14:43 runtime.txt
drwx------ 5 u13281 dyno 4096 Oct 23 14:47 staticfiles
$ cd staticfiles
$ ls -l
# 其結構如下:
staticfiles/
   admin/
      css/
      fonts/
      img/
      js/
   article/
      css/
   main/
      css/
      img/
      js/
--> 也就是說,collectstatic 將所有 app 的 static/ 目錄裡的內容全部複製到 staticfiles 目錄裡, 這也就是我們在 <app>/static/ 底下還要再建立一個 <app> 目錄的原因
# 登出 Heroku:
$ exit

∗ 進行雲端資料庫遷移

▸ 部署完畢,下一個步驟就是進行資料庫遷移:利用遷移檔案 (00*.py) 在雲端建立資料表

$ heroku run python manage.py migrate --app <appName>

Running python manage.py migrate on ⬢ <appName>... up, run.1865 (Free)
Operations to perform:
  Apply all migrations: admin, article, auth, contenttypes, sessions
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying article.0001_initial... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying auth.0008_alter_user_username_max_length... OK
  Applying sessions.0001_initial... OK

∗ 啟動 Heroku DYNO

$ heroku ps:scale web=1 --app <appName>

Scaling dynos... done, now running web at 1:Free

--> 測試:http://<appName>.herokuapp.com/

∗ 接下來執行填充程式:

$ heroku run python -m populate.test --app <appName>

Running python -m populate.test on ⬢ <appName>... up, run.4701 (Free)

Create admin account...done

Populating Article and Comment...done

∗ 檢查執行記錄:

$ heroku logs --app <appName>

∗ 轉址設定

▸ 如果已有網域,可以將該網域導至 Heroku app

▸ 需要先輸入信用卡資料,然後點「Settings」 --> Domains --> Add domain --> Domain name: xxx.xxx.xxx --> Save changes

∗ 加入插件:Heroku提供許多插件功能,有些免費、有些需付費

--> 在 Resources 頁籤 --> FIND MORE ADD-ONS ,常用的 add-on:

▸ 系統效能監控 (Monitoring): New Relic APM

▸ 執行記錄 (Logging): Papertrail

▸ 錯誤報告 (Errors and Exceptions): Rollbar

∗ 後續部署,系統上線後 (Production stage):

▸ 若程式有修改,則先 Push 至 Github ,再部署至 Heroku 以更新程式

▸ 所有資料庫遷移檔案 (00*.py) 都需要上載到 Github 與 Heroku , 如此 Heroku 才能依據遷移資訊更新資料庫

▸ 若需要修改 Model,程序如下:

✶ 確認只有一個開發者進行 Model 修改
✶ 修改後在本機端執行 Makemigrations
✶ Push 至 Github (包含所有遷移檔案 00*.py )
✶ 部署至 Heroku
✶ 在 Heroku 執行 Migrate
✶ 其他開發者 Pull from Github (如果發生資料衝突,需將本機端所有專案資料清除,再重新 Pull )

∗ 註:清除資料庫裡的所有資料 (危險!)

$ heroku pg:reset DATABASE_URL --app <appName>

DYNO 型態與資料庫方案

∗ DYNO 型態

▸ Heroku 提供許多 DYNO 型態

▸ 預設免費 DYNO,規格如下:

✶ 1 個 CPU (分享),512 MB RAM
✶ 30 分鐘內未收到 Request 就開始睡覺,之後收到 Request 後需要花十幾秒鐘時間甦醒
✶ 總 DYNO 時數
- 沒有信用卡:一個帳戶全月 550 小時 (所有 app 加總)
- 有綁定信用卡:1,000 小時

▸ DYNO型態:

https://devcenter.heroku.com/articles/dyno-types#available-dyno-types
Dyno Type Sleeps Professional
Features
Memory (RAM) CPU Share Dedicated Compute Price (USD)
free yes no 512MB 1x no 1x-4x 0
hobby no no 512MB 1x no 1x-4x 7
standard-1x no yes 512MB 1x no 1x-4x 25
standard-2x no yes 1024MB 2x no 4x-8x 50
performance-m no yes 2.5GB 100% yes 11x 250
performance-l no yes 14GB 100% yes 46x 500

∗ 選擇 Heroku database 方案

https://elements.heroku.com/addons/heroku-postgresql#dev

https://devcenter.heroku.com/articles/heroku-postgres-plans

Heroku
Postgres tier
Downtime
Tolerance
Fork Follow Rollback HA Disk
Encryption
SLA
Hobby < 4 hr downtime per mo. No No No No No No
Standard < 1 hr downtime per mo. Yes Yes 4 days No No No
Premium < 15 min downtime per mo. Yes Yes 1 week Yes Yes No
Enterprise < 15 min downtime per mo. Yes Yes 1 month Yes Yes Yes

▸ Hobby-dev:免費,至多 10 萬筆資料

▸ Hobby-basic:每月 9 美元,至多 1,000 萬筆資料

▸ 其餘方案筆數不限

Plan Name Provisioning Name Cache Size Storage Limit Connection Limit Monthly Price
Standard0 heroku-postgresql:standard-0 1 GB 64 GB 120 $50
Standard2 heroku-postgresql:standard-2 3.5 GB 256 GB 400 $200
Standard4 heroku-postgresql:standard-4 15 GB 512 GB 500 $750
Standard6 heroku-postgresql:standard-6 60 GB 1 TB 500 $2000
Standard7 heroku-postgresql:standard-7 120 GB 1 TB 500 $3500

Plan Name Provisioning Name Cache Size Storage Limit Connection Limit Monthly Price
Standard0 heroku-postgresql:premium-0 1 GB 64 GB 120 $200
Standard2 heroku-postgresql:premium-2 3.5 GB 256 GB 400 $350
Standard4 heroku-postgresql:premium-4 15 GB 512 GB 500 $1200
Standard6 heroku-postgresql:premium-6 60 GB 1 TB 500 $3500
Standard7 heroku-postgresql:premium-7 120 GB 1 TB 500 $6000

(6) 小結

∗ 上推專案到 Github

▸ Right click project --> Team --> Commit --> Commit message: Chapter 11 finished --> Commit and Push

∗ 練習

▸ 將 bookstore 專案部署到雲端

上一章       下一章