小白服務器編程指北(1)——從零搭建Django服務器(Centos7+Nginx+uWSGI)

前言

最近在學習運用python Django框架開發服務器的相關知識。爲了能夠讓服務器在生產環境中運行,也順便學習了Centos操作系統。總的感覺是,從無到有是最困難的,一個新的環境,新的框架,入了門,接下來就簡單了。當然,從入門到深入,又是另一回事,暫且按下不表。現在就記錄下如何在Centos下安裝Django服務器。

用到的開發配置如下:
虛擬機:

  • VMWare 11.5.1

操作系統:

  • CentOS 7.7.1908

Django開發環境:

  • Python 3.6.8
  • psycopg2 2.8.4
  • Django 3.0.1

數據庫:

  • Postgresql-12
  • Redis 5

服務器環境:

  • uWSGI 2.0.18
  • Nginx 1.16.1

三方服務

  • 阿里雲python core SDK

CentOS配置

我們可以去CentOS官網來下載對應的ISO鏡像,來安裝CentOS系統。

網絡連接

當我們在虛擬機中安裝完Centos系統後,會發現無法連接網絡。我們需要修改其網絡配置。

將虛擬機網絡連接修改爲NAT模式
在這裏插入圖片描述

通過Mac終端查看VMware Fusion的vmnet8目錄下nat.conf文件內容

cat /Library/Preferences/VMware\ Fusion/vmnet8/nat.conf

記錄下nat的網關地址和子網掩碼:
在這裏插入圖片描述

查看dhcpd.conf中的內容:

cat /Library/Preferences/VMware\ Fusion/vmnet8/dhcpd.conf

在這裏插入圖片描述
注意range 這個是虛擬機允許選擇的靜態ip地址範圍,自定義的靜態ip地址必須要在這個範圍內(本文打算使用172.16.9.200爲例介紹)

獲取DNS(在mac系統偏好設置—>網絡—>)

在這裏插入圖片描述

在這裏插入圖片描述

在這裏插入圖片描述

登錄CentOS系統,進入虛擬機的network-scripts目錄,配置網絡:

cd /etc/sysconfig/network-scripts

在這裏插入圖片描述

查找CentOS虛擬機對應的網卡配置文件,找到ifcfg-en開頭的文件,上圖中我的是ifcfg-ens33

想用vim編輯該文件,卻發現vim在CentOS下面默認沒有安裝(WTF???),只能用vi來編輯:
原始內容:
在這裏插入圖片描述

修改後的內容:
在這裏插入圖片描述

重新啓動network服務:

systemctl restart network

現在來試一下ping 百度吧,可以看到,網絡已經通了:
在這裏插入圖片描述

配置YUM 源

在CentOS系統中,我們可以通過YUM(Yellow dog Update Modifie)來管理安裝軟件,但是系統默認的YUM源上的軟件包有限,我們需要安裝擴展的epel源。

默認的yum源:
在這裏插入圖片描述

安裝eple源:
輸入命令

yum -y install epel-release

在這裏插入圖片描述

再次查看已經安裝的YUM源:
在這裏插入圖片描述

ifconfig: command not found

針對CentOS,其最小化安裝已經不會包含ifconfig和netstat相關的網絡工具了。

使用yum進行安裝:

yum install net-tools

安裝vim

yum install vim

安裝gcc/g++

開發時,我們需要gcc/g++編譯器來編譯文件,在CentOS默認最小安裝下是沒有的,我們可以用yum來安裝:

yum install gcc
yum install gcc-c++

Python配置

Python3 update

CentOS 7 中的python默認版本是2.7.5,我們需要將其升級至python3。

yum -y install python36

yum會安裝python3到本機,同時會安裝python的依賴庫pip3 。

這時,在命令行中輸入python3,就會使用python 3。

在這裏插入圖片描述
如果要默認的python命令替換爲python 3執行的話,需要斷開原有的python命令連接,使其指向python 3:

$ sudo rm /bin/python 
$ sudo rm /bin/python3 
$ cd /bin
$ ln -s python3.6 python # Choose the Python 3.x binary here

成功:
在這裏插入圖片描述

NOTE:在安裝python3的時候,會默認安裝依賴庫pip3。但有時候pip3 不能夠正常運行,這時候只要用yum將python3刪除重裝就好。

安裝Python3 開發套件

sudo yum install python-devel   # for python2.x installs
sudo yum install python3-devel  # for python3.x installs

這時候會出現如下錯誤:
在這裏插入圖片描述

這時因爲,yum使用python2來寫的,因此我們將默認的python指向python3,會出現語法錯誤。

這時候可以根據yum的錯誤提示,修改對應的錯誤文件的頭不引用:

!/usr/bin/python 
改成了

!/usr/bin/python2.7 

參考這裏

再次運行yum命令,正常安裝:
在這裏插入圖片描述

當輸入‘y’後,仍會報錯,這時候要依次修改python引用。

PostgreSQL數據庫

PostgreSQL數據庫是一款最高效的面向關係型數據庫。我們可以到他的官網來下載安裝對應的版本。

我們這裏使用12這個版本。

首先安裝postgresql YUM源:

yum install https://download.postgresql.org/pub/repos/yum/reporpms/EL-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm

安裝postgresql及server

yum install postgresql12
yum install postgresql12-server

初始化數據庫,並設置默認開機啓動postgresql服務

/usr/pgsql-12/bin/postgresql-12-setup initdb
systemctl enable postgresql-12
systemctl start postgresql-12

安裝postgresql開發包

yum install postgresql-devel

安裝psycopg2

psycopg2是python的postgresql DB適配器,爲了能夠在python中開發postgresql數據庫,需要安裝該python庫。

注意,這時候我們需要使用pip3來安裝到python3,默認的pip會安裝到python2中。

pip3 install psycopg2-binary==2.8.4

這裏我們選擇用安裝包安裝,而不是本地編譯源代碼安裝。因爲源代碼安裝會發生編譯錯誤,這時因爲psycopg2使用python2編寫的,在python3環境下編譯會出現錯誤。

Redis數據庫

Redis數據庫是一款優秀的非關係型數據庫,利用鍵值對的關係,可以達到內存中的高效存取。我們可以用它來做Server中一些緩存的操作。

yum install http://rpms.remirepo.net/enterprise/remi-release-7.rpm
yum --enablerepo=remi install redis

啓用Redis服務

systemctl enable --now redis

修改Redis配置

vim /etc/redis.conf
Bind 0.0.0.0 (71 line)

重啓Redis

systemctl restart redis

查看Redis狀態

systemctl status redis

在這裏插入圖片描述

測試Redis Server是否可用

[root@linuxhelp ~]# redis-cli
127.0.0.1:6379> ping
PONG
127.0.0.1:6379> exit

安裝Django 及其他Python庫

安裝Django

pip3 install Django==3.0.1

安裝Django restframework

pip3 install djangorestframework==3.11.0

安裝Python 圖像處理庫Pillow

pip3 install pillow==6.2.1

安裝Django JWT認證庫

pip3 install django-rest-auth==0.9.5

pip3 install djangorestframework-jwt==1.11.0

安裝Django的Redis庫

pip3 install django-redis==4.11.0

注意到在安裝django-redis時的時候,會默認安裝依賴的redis 3.3.11版本。但是我們之前安裝啓動了redis 5這個版本,不知道會如何處理。

因爲項目中使用了阿里雲的短信服務,所以需要安裝阿里雲Python SDK:

pip3 install aliyun_python_sdk_core

配置Postgresql

默認情況下,Postgresql數據庫是採用host認證的方式,就是其DB用戶是和Linux用戶綁定的,當用戶登錄了系統後,便可直接訪問Postgresql數據庫。爲了支持密碼認證,我們可以通過修改他的host-based authentication (HBA)配置:

vim /var/lib/pgsql/12/data/pg_hba.conf

在文件最後的位置,找到:

host    all             all             127.0.0.1/32            ident
host    all             all             ::1/128                 ident

修改爲:

host    all             all             127.0.0.1/32            md5
host    all             all             ::1/128                 md5

啓動Postgresql服務

systemctl start postgresql-12
systemctl enable postgresql-12

使用Postgresql role

在Postgresql中,使用‘role’的概念來控制數據庫的訪問權限。所謂的role,可以理解爲角色,他是和CentOS系統中的account是對應的。當我們創建一個role時,默認會與CentOS中同名的account進行關聯(即該account能夠按照role中的規定,訪問Postgresql數據庫)。當我們在CentOS中安裝Postgresql時,安裝包會默認創建一個postgres的用戶,該用戶被分配到Postgresql default role。

postgres用戶類似於CentOS中的root用戶,擁有頂級的數據庫權限。因此,我們可以用postgres用戶創建其他用戶及數據庫。

登錄到postgres用戶

sudo -i -u postgres

進入psql shell

-bash-4.2$ psql
psql (12.1)
Type "help" for help.

postgres=# 

退出psql

postgres=# \q

Create new Postgresql role

利用postgres用戶,我們可以創建新的Postgresql role:

-bash-4.2$ createuser -dP John.tyler
Enter password for new role: 
Enter it again: 

-d : role用戶擁有創建數據庫權限
-P : role用戶需要密碼認證。(我們在前面已經啓用了密碼認證)

Create new CentOS user

上一步我們創建了一個 John.tyler的role,它會默認匹配到到當前CentOS同名用戶中。

如果沒有同名用戶,我們需要創建一個新的John.tyler用戶

adduser John.tyler

爲John.tyler設置CentOS登錄密碼

[root@localhost ~]# passwd John.tyler

讓John.tyler擁有sudo權限:

# 首先切換爲root用戶
su root

# 修改sudoers文件
d

# 找到權限設置,如下
root    ALL=(ALL)       ALL

# 若要給John.tyler用戶增加sudo權限,需要增加如下一行
root    ALL=(ALL)       ALL
John.tyler    ALL=(ALL)       ALL

# 保存退出後John.tyler用戶則擁有了sudo權限

創建數據庫

首先切換到John.tyler

su - John.tyler

創建數據庫

createdb -E utf8 -U John.tyler testDB

測試,運行psql命令,會發現報錯,提示John.tyler數據庫不存在。

[John.tyler@localhost ~]$ psql
psql: error: could not connect to server: FATAL:  database "John.tyler" does not exist

這是因爲psql默認會去嘗試連接與當前role同名的數據庫,我們可以用-d選項,指定連接數據庫名稱爲testDB:

[John.tyler@localhost ~]$ psql -d testDB
psql (12.1)
Type "help" for help.

testDB=> 

輸入\conninfo查看當前的DB連接信息

testDB=> \conninfo
You are connected to database "testDB" as user "John.tyler" via socket in "/var/run/postgresql" at port "5432".

OK,至此,我們創建了一個叫做testDB的數據庫,用戶名稱爲John.tyler,testDB數據庫登錄密碼是我們在createuser中指定的,注意,不是我們CentOS中John.tyler的account 密碼!

爲Django設置不同的環境配置

在實際的項目中,往往需要處理多種環境,其中至少涉及本地開發和產品環境,某些設置可以適用於全部環境,而其他一些則需要針對性的設置。下面就說明如何設置多種環境配置。

在項目的settings.py文件旁創建settings目錄,將settings.py重命名爲base.py,並將其移動到settings目錄下。同時,在settings目錄中生成如下附加文件:

settings/
	__init__.py
	base.py
	local.py
	pro.py

local.py用於本地環境配置,pro.py用於產品環境配置。他們都繼承自base.py:

from .base import *

編輯settings/base.py,找到如下代碼行:

BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

修改爲:

BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(os.path.join(__file__, os.pardir))))

因爲我們已經將配置文件移動到了低一級的目錄,因此需要將BASE_DIR指向其父目錄方可有效,可以使用os.pardir

編輯settings/local.py :

from .base import *

DEBUT = True

DATABASE = {
	'default': {
		'ENGINE': 'django.db.backends.sqlite3',
		'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
	}
}

編輯settings/pro.py:

from .base import *

DEBUG = False

ADMINS = (
    ('John.tyler', '[email protected]'),
)

ALLOWED_HOSTS = ['*']

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'testDB',
        'USER': 'John.tyler',
        'PASSWORD': '******',
    }
}

ADMINS : 因爲此時DEBUG = False,因此對於視圖的異常情況,會以郵件的形式發送到ADMINS。

ALLOWED_HOSTS:Django僅會在指定的服務器上運行,這裏未來可以設置線上服務器的域名或IP地址。*表示可以再任意主機上運行。

DATABASES:產品環境下,我們將數據庫替換爲更強大的Postgresql,注意這裏的參與要與我們創建的數據庫相匹配。

我們已經爲本地和產品環境設置了兩套配置文件,那麼Django如何知道使用哪個配置文件呢?

我們需要設置一個DJANGO_SETTINGS_MODULE環境變量來指定我們使用的setting 文件:

打開Shell並運行以下命令:

export DJANGO_SETTINGS_MODULE=yourprojectname.settings.pro

或者在每次運行管理命令時,添加–settings參數:

python manage.py migrate --settings=yourprojectname.settings.pro

如果不想每次都添加settings變量,我們可以配置Shell的全局環境變量(以bash shell爲例):

/bin/bash
The bash executable
/etc/profile
The systemwide initialization file, executed for login shells
~/.bash_profile
The personal initialization file, executed for login shells
~/.bashrc
The individual per-interactive-shell startup file
~/.bash_logout
The individual login shell cleanup file, executed when a login shell exits
~/.inputrc
Individual readline initialization file|
vim ~/.bash_profile
export DJANGO_SETTINGS_MODULE=yourprojectname.settings.pro

重新加載Shell變量

$ source ~/.bashrc

uWSGI

在本地開發中,我們用Django自帶的服務器做開發測試。但是,在真正的產品環境中,需要使用真實的Web服務器。

對於Python來說,使用uWSGI。

安裝uWSGI

pip3 install uwsgi==2.0.18 

安裝成功後,就可以用uwsgi來運行我們的server了。

爲了讓uWSGI能夠正確的運行我們的server,我們需要爲uWSGI編寫啓動配置文件。

在項目目錄下生成如下文件結構:

config/
	uwsgi.ini

uwsgi.ini內容如下:

[uwsgi]
# variables
projectname = yourprojectname
base = absoult path of your project folder
# configuration
pythonpath = %(base)
chdir = %(base)
env = DJANGO_SETTINGS_MODULE=%(projectname).settings.pro
module = %(projectname).wsgi:application
socket = /tmp/%(projectname).sock
# maximum number of worker processes
processes      = 10
# ... with appropriate permissions - may be needed
chmod-socket = 666
# clear environment on exit
vacuum = true
enable-threads = true

socket :這裏指定與uWSGI通信是使用unxi socket文件(比直接指定ip:端口形式通信效率高),socket文件的路徑爲/tmp/%(projectname).sock。
當uwsgi運行時,會自動在tmp目錄下創建sock文件,無需人工參與。當使用NGINX時,NGINX會通過該sock文件與uWSGI通信。

chmod-socket:sock文件的訪問權限,這裏用666表示所有用戶皆可訪問。這時爲了預防NGINX因爲sock文件權限,而無法與uWSGI通信的問題。

Nginx

我們訪問網絡,獲取的資源,可以整體上分爲靜態資源和動態資源。而在當前流行的服務器架構中,靜態資源和動態資源的獲取可以分爲兩種不同的方式,從而減輕後臺服務器的壓力。

通用的做法是,在真正的服務器之前,設置一個反向代理,通常使用Nginx。Nginx的功能是過濾客戶端的資源請求類型,對於靜態資源,會有Nginx直接返回,並作緩存以備下次使用。而對於真正需要動態返回的動態資源,Nginx纔會去請求後臺服務器(在我們這裏也就是uWGSI),由uWSGI將動態結果返回給Nginx,Nginx再返回給客戶端。對於動態資源,Nginx起到一個代理請求的作用。

這也就是反向代理名稱的由來,正向代理是由客戶端使用的,常見的如VPN。正向代理代理的是客戶端。而反向代理,並不是在客戶端使用,而是在服務器內部作代理,這種服務器自身使用的代理對客戶端來說是透明的,比如我們訪問www.baidu.com,我們並不知道我們請求下來的資源究竟是來自Nginx還是其後臺真正的服務器。

使用了Nginx後,產品環境的架構如下:
在這裏插入圖片描述

在我們的應用中,Nginx和uWSGI還是在同一個服務器上,在更復雜的應用中,我們可以將Nginx和uWSGI部署到不同的服務器中,來減輕同一個服務器的壓力。

安裝Nginx

根據CentOS 7 下 yum 安裝和配置 Nginx這篇文章中的內容,我們可以如下安裝Nginx:
添加Nginx for CentOS7 的YUM源:

$ sudo rpm -ivh http://nginx.org/packages/centos/7/noarch/RPMS/nginx-release-centos-7-0.el7.ngx.noarch.rpm

YUM安裝Nginx:

$ sudo yum install nginx

配置Nginx

爲了Nginx能夠和我們的uWSGI服務器能夠協同工作,我們需要編寫Nginx的配置文件。同樣在config目錄,添加nginx.conf文件:

config/
	nginx.conf
	uwsgi.ini

內容如下:

# the upstream componet nginx needs to connect ot
upstream ai_mei_jia {
    server unix:///tmp/ai_mei_jia.sock;
}

server {
    listen 80;
    # the domain name it will serve for
    server_name 172.16.9.130; # substitute your machine's IP address or FQDN(服務器域名)
    charset     utf-8;

     # Django media
    location /media  {
        alias /home/ai_mei_jia_db_mgr/ai_mei_jia/media;  # your Django project's media files - amend as required
    }

    location /static {
        alias /home/ai_mei_jia_db_mgr/ai_mei_jia/static; # your Django project's static files - amend as required
    }

    # Finally, send all non-media requests to the Django server.
    location / {
        uwsgi_pass  ai_mei_jia;
        include     /etc/nginx/uwsgi_params; # the uwsgi_params file you installed
    }

}

uwsgi_pass : 用來指定Nginx和uWSGI通信的socket地址。通常情況下,可以寫爲:

uwsgi_pass unix:///tmp/ai_mei_jia.sock;

這裏我們使用了upstream ai_mei_jia。這樣寫可以支持多服務器負載均衡,因爲在upstream裏面,我可以指定多個server,Nginx會自動在這些server間做負載均衡,如:

upstream uwsgicluster {
  server unix:///tmp/uwsgi.sock;
  server 192.168.1.235:3031;
  server 10.0.0.17:3017;
}
uwsgi_pass uwsgicluster;

Nginx配置有很多,具體可以參考Nginx支持。我們不做深入研究。

寫好配置文件後,我們就可以將我們整個的Django工程目錄,scp到服務器的對應目錄下了。

最後的配置

當我們用scp命令將Django文件拷貝到CentOS中後,還需要做一些最後的配置。

初始化Django工程

進入到項目根目錄,運行

$ python manage.py migrate

如果一切正常,恭喜,已經成功了一大半了。

然後,我們來創建第一個super user:

$ python manage.py createsuperuser

uWSGI啓動工程

接下來,我們通過uWSG來啓動工程,而不是本地開發的runserver命令。

設置Nginx啓動配置

在前面的步驟中,爲了能夠使Nginx和uWSGI聯合工作,我們需要將之前我們寫的nginx.conf拷貝到Nginx的配置文件目錄中:

cp /home/John.tyler/ai_mei_jia/config/nginx.conf /etc/nginx/conf.d/ai_mei_jia.conf

啓動Nginx

systemctl start nginx

並設置nginx服務開機自啓動

 systemctl enable nginx

嘗試連接服務器:

http://172.16.9.150/admin

可能會出現服務器無法連接錯誤:
在這裏插入圖片描述

這可能是因爲CentOS的防火牆的原因,我們讓防火牆開放http的80端口:

firewall-cmd --zone=public --permanent --add-service=http
sudo firewall-cmd --reload

重新嘗試,會發現服務器已經聯通,但是會有502服務器錯誤:
在這裏插入圖片描述

這可能是因爲CentOS中SELinux導致Nginx無法訪問uWSGI的sock文件導致的。

SELinux(Security-Enhanced Linux)
是美國國家安全局(NSA)對於強制訪問控制的實現,是Linux歷史上最傑出的新安全子系統。
NSA是在Linux社區的幫助下開發了一種訪問控制體系,在這種訪問控制體系的限制下,進程只能訪問那些在他的任務中所需要文件。

這有點像沙盒機制的概念。但是SELinux也是的Nginx無法正常的使用。正統的方式應該是在SELinux中配置相應策略,但是配置起來比較麻煩。我們就根據selinux 不限制nginx

永久關閉selinux,永久關閉需要重啓才能生效
編輯vim /etc/selinux/config

這個文件的註釋寫的非常清楚,直接將selinux設置爲disable後,重啓,即可。

再次嘗試訪問Django服務器,這時候應該可以正常訪問。如果仍然提示502錯誤,可以查看nginx的error log,

cat /var/log/nginx/error.log 

如果error log如下所示:

2019/12/30 18:33:50 [crit] 1454#1454: *1 connect() to unix:///tmp/ai_mei_jia.sock failed (13: Permission denied) while connecting to upstream, client: 172.16.9.1, server: 172.16.9.150, request: "GET /admin HTTP/1.1", upstream: "uwsgi://unix:///tmp/ai_mei_jia.sock:", host: "172.16.9.150"

說明nginx沒有權限訪問ai_mei_jia.sock,這時候要再次檢查一下uwsgi.ini的配置,

chmod-socket = 666

來確保nginx能夠有權限訪問sock文件。

用Nginx項靜態和媒體數據集提供服務

至此,我們的Nginx服務並沒有初始化需要其提供的靜態和媒體數據。

我們到Django工程中,編輯settings/base.py文件:

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

然後運行

python manage.py collectstatic

對應輸出結果類似下面:

160 static files copied to '/ai_mei_jia/static'.

注意上面的路徑要和nginx.conf中的配置一致:

  # Django media
    location /media  {
        alias /home/ai_mei_jia_db_mgr/ai_mei_jia/media;  # your Django project's media files - amend as required
    }

    location /static {
        alias /home/ai_mei_jia_db_mgr/ai_mei_jia/static; # your Django project's static files - amend as required
    }

重新加載nginx服務

systemctl reload nginx

這時候再加載頁面,會發現nginx仍然沒有加載靜態文件(這裏是格式文件css):

查看nginx的error log,會發現是nginx沒有權限訪問css文件:

2019/12/31 11:17:16 [error] 2747#2747: *4 open() "/home/John.tyler/ai_mei_jia/static/admin/css/responsive.css" failed (13: Permission denied), client: 172.16.9.1, server: 172.16.9.150, request: "GET /static/admin/css/responsive.css HTTP/1.1", host: "172.16.9.150", referrer: "http://172.16.9.150/admin/login/?next=/admin/"

再用ll命令查看static文件夾的權限,發現其他用戶明明是有rx權限的,很是奇怪。

嘗試讓nginx以root用戶運行,修改nginx.conf文件:


user  nginx; # 修改nginx爲root
worker_processes  1;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    keepalive_timeout  65;

    #gzip  on;

    include /etc/nginx/conf.d/*.conf;
}

將第一行的nginx user修改爲root,重啓nginx服務,再次加載頁面:
在這裏插入圖片描述

這次css文件終於加載成功了。

總結

查了無數資料,終於將本地的server代碼整到CentOS服務器上運行了。回頭看一下,服務器的環境配置確實很很繁瑣,但是又重複性的勞動。尤其是Nginx和uWSGI之間的通信,涉及到的用戶權限問題,自己也是模模糊糊的用最高權限搞定了,但細想一下,這裏肯定會有很大的安全隱患在裏面。

下一步會繼續研究Docker,來簡化服務器環境的設置。

發佈了89 篇原創文章 · 獲贊 47 · 訪問量 12萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章